eslintFormatter.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. /**
  2. * Copyright (c) 2015-present, Facebook, Inc.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. 'use strict';
  8. const path = require('path');
  9. const chalk = require('chalk');
  10. const stripAnsi = require('strip-ansi');
  11. const table = require('text-table');
  12. const cwd = process.cwd();
  13. const emitErrorsAsWarnings =
  14. process.env.NODE_ENV === 'development' &&
  15. process.env.ESLINT_NO_DEV_ERRORS === 'true';
  16. function isError(message) {
  17. if (message.fatal || message.severity === 2) {
  18. return true;
  19. }
  20. return false;
  21. }
  22. function getRelativePath(filePath) {
  23. return path.relative(cwd, filePath);
  24. }
  25. function formatter(results) {
  26. let output = '\n';
  27. let hasErrors = false;
  28. let reportContainsErrorRuleIDs = false;
  29. results.forEach(result => {
  30. let messages = result.messages;
  31. if (messages.length === 0) {
  32. return;
  33. }
  34. messages = messages.map(message => {
  35. let messageType;
  36. if (isError(message) && !emitErrorsAsWarnings) {
  37. messageType = 'error';
  38. hasErrors = true;
  39. if (message.ruleId) {
  40. reportContainsErrorRuleIDs = true;
  41. }
  42. } else {
  43. messageType = 'warn';
  44. }
  45. let line = message.line || 0;
  46. if (message.column) {
  47. line += ':' + message.column;
  48. }
  49. let position = chalk.bold('Line ' + line + ':');
  50. return [
  51. '',
  52. position,
  53. messageType,
  54. message.message.replace(/\.$/, ''),
  55. chalk.underline(message.ruleId || ''),
  56. ];
  57. });
  58. // if there are error messages, we want to show only errors
  59. if (hasErrors) {
  60. messages = messages.filter(m => m[2] === 'error');
  61. }
  62. // add color to rule keywords
  63. messages.forEach(m => {
  64. m[4] = m[2] === 'error' ? chalk.red(m[4]) : chalk.yellow(m[4]);
  65. m.splice(2, 1);
  66. });
  67. let outputTable = table(messages, {
  68. align: ['l', 'l', 'l'],
  69. stringLength(str) {
  70. return stripAnsi(str).length;
  71. },
  72. });
  73. // print the filename and relative path
  74. output += `${getRelativePath(result.filePath)}\n`;
  75. // print the errors
  76. output += `${outputTable}\n\n`;
  77. });
  78. if (reportContainsErrorRuleIDs) {
  79. // Unlike with warnings, we have to do it here.
  80. // We have similar code in react-scripts for warnings,
  81. // but warnings can appear in multiple files so we only
  82. // print it once at the end. For errors, however, we print
  83. // it here because we always show at most one error, and
  84. // we can only be sure it's an ESLint error before exiting
  85. // this function.
  86. output +=
  87. 'Search for the ' +
  88. chalk.underline(chalk.red('keywords')) +
  89. ' to learn more about each error.';
  90. }
  91. return output;
  92. }
  93. module.exports = formatter;