logger-diagnostics.js 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. var helpers_1 = require("../util/helpers");
  4. var highlight_1 = require("../highlight/highlight");
  5. var path_1 = require("path");
  6. var logger_1 = require("./logger");
  7. var fs_1 = require("fs");
  8. var chalk = require("chalk");
  9. function printDiagnostics(context, diagnosticsType, diagnostics, consoleLogDiagnostics, writeHtmlDiagnostics) {
  10. if (diagnostics && diagnostics.length) {
  11. if (consoleLogDiagnostics) {
  12. diagnostics.forEach(consoleLogDiagnostic);
  13. }
  14. if (writeHtmlDiagnostics) {
  15. var content = diagnostics.map(generateDiagnosticHtml);
  16. var fileName = getDiagnosticsFileName(context.buildDir, diagnosticsType);
  17. fs_1.writeFileSync(fileName, content.join('\n'), { encoding: 'utf8' });
  18. }
  19. }
  20. }
  21. exports.printDiagnostics = printDiagnostics;
  22. function consoleLogDiagnostic(d) {
  23. if (d.level === 'warn') {
  24. logger_1.Logger.warn(d.header);
  25. }
  26. else {
  27. logger_1.Logger.error(d.header);
  28. }
  29. logger_1.Logger.wordWrap([d.messageText]).forEach(function (m) {
  30. console.log(m);
  31. });
  32. console.log('');
  33. if (d.lines && d.lines.length) {
  34. var lines = prepareLines(d.lines, 'text');
  35. lines.forEach(function (l) {
  36. if (!isMeaningfulLine(l.text)) {
  37. return;
  38. }
  39. var msg = "L" + l.lineNumber + ": ";
  40. while (msg.length < logger_1.Logger.INDENT.length) {
  41. msg = ' ' + msg;
  42. }
  43. var text = l.text;
  44. if (l.errorCharStart > -1) {
  45. text = consoleHighlightError(text, l.errorCharStart, l.errorLength);
  46. }
  47. msg = chalk.dim(msg);
  48. if (d.language === 'javascript') {
  49. msg += jsConsoleSyntaxHighlight(text);
  50. }
  51. else if (d.language === 'scss') {
  52. msg += cssConsoleSyntaxHighlight(text, l.errorCharStart);
  53. }
  54. else {
  55. msg += text;
  56. }
  57. console.log(msg);
  58. });
  59. console.log('');
  60. }
  61. }
  62. function consoleHighlightError(errorLine, errorCharStart, errorLength) {
  63. var rightSideChars = errorLine.length - errorCharStart + errorLength - 1;
  64. while (errorLine.length + logger_1.Logger.INDENT.length > logger_1.Logger.MAX_LEN) {
  65. if (errorCharStart > (errorLine.length - errorCharStart + errorLength) && errorCharStart > 5) {
  66. // larger on left side
  67. errorLine = errorLine.substr(1);
  68. errorCharStart--;
  69. }
  70. else if (rightSideChars > 1) {
  71. // larger on right side
  72. errorLine = errorLine.substr(0, errorLine.length - 1);
  73. rightSideChars--;
  74. }
  75. else {
  76. break;
  77. }
  78. }
  79. var lineChars = [];
  80. var lineLength = Math.max(errorLine.length, errorCharStart + errorLength);
  81. for (var i = 0; i < lineLength; i++) {
  82. var chr = errorLine.charAt(i);
  83. if (i >= errorCharStart && i < errorCharStart + errorLength) {
  84. chr = chalk.bgRed(chr === '' ? ' ' : chr);
  85. }
  86. lineChars.push(chr);
  87. }
  88. return lineChars.join('');
  89. }
  90. var diagnosticsHtmlCache = {};
  91. function clearDiagnosticsCache() {
  92. diagnosticsHtmlCache = {};
  93. }
  94. exports.clearDiagnosticsCache = clearDiagnosticsCache;
  95. function clearDiagnostics(context, type) {
  96. try {
  97. delete diagnosticsHtmlCache[type];
  98. fs_1.unlinkSync(getDiagnosticsFileName(context.buildDir, type));
  99. }
  100. catch (e) { }
  101. }
  102. exports.clearDiagnostics = clearDiagnostics;
  103. function hasDiagnostics(buildDir) {
  104. loadBuildDiagnosticsHtml(buildDir);
  105. var keys = Object.keys(diagnosticsHtmlCache);
  106. for (var i = 0; i < keys.length; i++) {
  107. if (typeof diagnosticsHtmlCache[keys[i]] === 'string') {
  108. return true;
  109. }
  110. }
  111. return false;
  112. }
  113. exports.hasDiagnostics = hasDiagnostics;
  114. function loadBuildDiagnosticsHtml(buildDir) {
  115. try {
  116. if (diagnosticsHtmlCache[exports.DiagnosticsType.TypeScript] === undefined) {
  117. diagnosticsHtmlCache[exports.DiagnosticsType.TypeScript] = fs_1.readFileSync(getDiagnosticsFileName(buildDir, exports.DiagnosticsType.TypeScript), 'utf8');
  118. }
  119. }
  120. catch (e) {
  121. diagnosticsHtmlCache[exports.DiagnosticsType.TypeScript] = false;
  122. }
  123. try {
  124. if (diagnosticsHtmlCache[exports.DiagnosticsType.Sass] === undefined) {
  125. diagnosticsHtmlCache[exports.DiagnosticsType.Sass] = fs_1.readFileSync(getDiagnosticsFileName(buildDir, exports.DiagnosticsType.Sass), 'utf8');
  126. }
  127. }
  128. catch (e) {
  129. diagnosticsHtmlCache[exports.DiagnosticsType.Sass] = false;
  130. }
  131. }
  132. function injectDiagnosticsHtml(buildDir, content) {
  133. if (!hasDiagnostics(buildDir)) {
  134. return content;
  135. }
  136. var contentStr = content.toString();
  137. var c = [];
  138. c.push("<div id=\"ion-diagnostics\">");
  139. // diagnostics content
  140. c.push(getDiagnosticsHtmlContent(buildDir));
  141. c.push("</div>"); // #ion-diagnostics
  142. var match = contentStr.match(/<body>(?![\s\S]*<body>)/i);
  143. if (match) {
  144. contentStr = contentStr.replace(match[0], match[0] + '\n' + c.join('\n'));
  145. }
  146. else {
  147. contentStr = c.join('\n') + contentStr;
  148. }
  149. return contentStr;
  150. }
  151. exports.injectDiagnosticsHtml = injectDiagnosticsHtml;
  152. function getDiagnosticsHtmlContent(buildDir, includeDiagnosticsHtml) {
  153. var c = [];
  154. // diagnostics header
  155. c.push("\n <div class=\"ion-diagnostics-header\">\n <div class=\"ion-diagnostics-header-content\">\n <div class=\"ion-diagnostics-header-inner\">Error</div>\n <div class=\"ion-diagnostics-buttons\">\n <button id=\"ion-diagnostic-close\">Close</button>\n </div>\n </div>\n </div>\n ");
  156. c.push("<div class=\"ion-diagnostics-content\">");
  157. if (includeDiagnosticsHtml) {
  158. c.push(includeDiagnosticsHtml);
  159. }
  160. loadBuildDiagnosticsHtml(buildDir);
  161. var keys = Object.keys(diagnosticsHtmlCache);
  162. for (var i = 0; i < keys.length; i++) {
  163. if (typeof diagnosticsHtmlCache[keys[i]] === 'string') {
  164. c.push(diagnosticsHtmlCache[keys[i]]);
  165. }
  166. }
  167. c.push("</div>");
  168. return c.join('\n');
  169. }
  170. exports.getDiagnosticsHtmlContent = getDiagnosticsHtmlContent;
  171. function generateDiagnosticHtml(d) {
  172. var c = [];
  173. c.push("<div class=\"ion-diagnostic\">");
  174. c.push("<div class=\"ion-diagnostic-masthead\" title=\"" + helpers_1.escapeHtml(d.type) + " error: " + helpers_1.escapeHtml(d.code) + "\">");
  175. var title = helpers_1.titleCase(d.type) + " " + helpers_1.titleCase(d.level);
  176. c.push("<div class=\"ion-diagnostic-title\">" + helpers_1.escapeHtml(title) + "</div>");
  177. c.push("<div class=\"ion-diagnostic-message\" data-error-code=\"" + helpers_1.escapeHtml(d.type) + "-" + helpers_1.escapeHtml(d.code) + "\">" + helpers_1.escapeHtml(d.messageText) + "</div>");
  178. c.push("</div>"); // .ion-diagnostic-masthead
  179. c.push(generateCodeBlock(d));
  180. c.push("</div>"); // .ion-diagnostic
  181. return c.join('\n');
  182. }
  183. exports.generateDiagnosticHtml = generateDiagnosticHtml;
  184. function generateCodeBlock(d) {
  185. var c = [];
  186. c.push("<div class=\"ion-diagnostic-file\">");
  187. c.push("<div class=\"ion-diagnostic-file-header\" title=\"" + helpers_1.escapeHtml(d.absFileName) + "\">" + helpers_1.escapeHtml(d.relFileName) + "</div>");
  188. if (d.lines && d.lines.length) {
  189. c.push("<div class=\"ion-diagnostic-blob\">");
  190. c.push("<table class=\"ion-diagnostic-table\">");
  191. prepareLines(d.lines, 'html').forEach(function (l) {
  192. c.push("<tr" + ((l.errorCharStart > -1) ? ' class="ion-diagnostic-error-line"' : '') + ">");
  193. c.push("<td class=\"ion-diagnostic-blob-num\" data-line-number=\"" + l.lineNumber + "\"></td>");
  194. c.push("<td class=\"ion-diagnostic-blob-code\">" + highlight_1.highlightError(l.html, l.errorCharStart, l.errorLength) + "</td>");
  195. c.push("</tr>");
  196. });
  197. c.push("</table>");
  198. c.push("</div>"); // .ion-diagnostic-blob
  199. }
  200. c.push("</div>"); // .ion-diagnostic-file
  201. return c.join('\n');
  202. }
  203. exports.generateCodeBlock = generateCodeBlock;
  204. function jsConsoleSyntaxHighlight(text) {
  205. if (text.trim().startsWith('//')) {
  206. return chalk.dim(text);
  207. }
  208. var words = text.split(' ').map(function (word) {
  209. if (JS_KEYWORDS.indexOf(word) > -1) {
  210. return chalk.cyan(word);
  211. }
  212. return word;
  213. });
  214. return words.join(' ');
  215. }
  216. function cssConsoleSyntaxHighlight(text, errorCharStart) {
  217. var cssProp = true;
  218. var safeChars = 'abcdefghijklmnopqrstuvwxyz-_';
  219. var notProp = '.#,:}@$[]/*';
  220. var chars = [];
  221. for (var i = 0; i < text.length; i++) {
  222. var c = text.charAt(i);
  223. if (c === ';' || c === '{') {
  224. cssProp = true;
  225. }
  226. else if (notProp.indexOf(c) > -1) {
  227. cssProp = false;
  228. }
  229. if (cssProp && safeChars.indexOf(c.toLowerCase()) > -1) {
  230. chars.push(chalk.cyan(c));
  231. continue;
  232. }
  233. chars.push(c);
  234. }
  235. return chars.join('');
  236. }
  237. function prepareLines(orgLines, code) {
  238. var lines = JSON.parse(JSON.stringify(orgLines));
  239. for (var i = 0; i < 100; i++) {
  240. if (!eachLineHasLeadingWhitespace(lines, code)) {
  241. return lines;
  242. }
  243. for (var i_1 = 0; i_1 < lines.length; i_1++) {
  244. lines[i_1][code] = lines[i_1][code].substr(1);
  245. lines[i_1].errorCharStart--;
  246. if (!lines[i_1][code].length) {
  247. return lines;
  248. }
  249. }
  250. }
  251. return lines;
  252. }
  253. function eachLineHasLeadingWhitespace(lines, code) {
  254. if (!lines.length) {
  255. return false;
  256. }
  257. for (var i = 0; i < lines.length; i++) {
  258. if (!lines[i][code] || lines[i][code].length < 1) {
  259. return false;
  260. }
  261. var firstChar = lines[i][code].charAt(0);
  262. if (firstChar !== ' ' && firstChar !== '\t') {
  263. return false;
  264. }
  265. }
  266. return true;
  267. }
  268. var JS_KEYWORDS = [
  269. 'abstract', 'any', 'as', 'break', 'boolean', 'case', 'catch', 'class',
  270. 'console', 'const', 'continue', 'debugger', 'declare', 'default', 'delete',
  271. 'do', 'else', 'enum', 'export', 'extends', 'false', 'finally', 'for', 'from',
  272. 'function', 'get', 'if', 'import', 'in', 'implements', 'Infinity',
  273. 'instanceof', 'let', 'module', 'namespace', 'NaN', 'new', 'number', 'null',
  274. 'public', 'private', 'protected', 'require', 'return', 'static', 'set',
  275. 'string', 'super', 'switch', 'this', 'throw', 'try', 'true', 'type',
  276. 'typeof', 'undefined', 'var', 'void', 'with', 'while', 'yield',
  277. ];
  278. function getDiagnosticsFileName(buildDir, type) {
  279. return path_1.join(buildDir, ".ion-diagnostic-" + type + ".html");
  280. }
  281. function isMeaningfulLine(line) {
  282. if (line) {
  283. line = line.trim();
  284. if (line.length) {
  285. return (MEH_LINES.indexOf(line) < 0);
  286. }
  287. }
  288. return false;
  289. }
  290. var MEH_LINES = [';', ':', '{', '}', '(', ')', '/**', '/*', '*/', '*', '({', '})'];
  291. exports.DiagnosticsType = {
  292. TypeScript: 'typescript',
  293. Sass: 'sass',
  294. TsLint: 'tslint'
  295. };