Skip to content

Commit

Permalink
Prettify errors and warnings for Webpack 2 (facebook#2113)
Browse files Browse the repository at this point in the history
* Prettify errors and warnings for Webpack 2

* Update formatWebpackMessages.js
  • Loading branch information
gaearon authored and romaindso committed Jul 10, 2017
1 parent e91ba24 commit ac0f615
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 43 deletions.
118 changes: 77 additions & 41 deletions packages/react-dev-utils/formatWebpackMessages.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,14 @@ function isLikelyASyntaxError(message) {
}

// Cleans up webpack error messages.
function formatMessage(message) {
function formatMessage(message, isError) {
var lines = message.split('\n');

if (lines.length > 2 && lines[1] === '') {
// Remove extra newline.
lines.splice(1, 1);
}

// Remove webpack-specific loader notation from filename.
// Before:
// ./~/css-loader!./~/postcss-loader!./src/App.css
Expand All @@ -35,6 +40,15 @@ function formatMessage(message) {
lines[0] = lines[0].substr(lines[0].lastIndexOf('!') + 1);
}

lines = lines.filter(function(line) {
// Webpack adds a list of entry points to warning messages:
// @ ./src/index.js
// @ multi react-scripts/~/react-dev-utils/webpackHotDevClient.js ...
// It is misleading (and unrelated to the warnings) so we clean it up.
// It is only useful for syntax errors but we have beautiful frames for them.
return line.indexOf(' @ ') !== 0;
});

// line #0 is filename
// line #1 is the main error message
if (!lines[0] || !lines[1]) {
Expand All @@ -50,54 +64,76 @@ function formatMessage(message) {
.replace("Cannot resolve 'file' or 'directory' ", '')
.replace('Cannot resolve module ', '')
.replace('Error: ', ''),
// Skip all irrelevant lines.
// (For some reason they only appear on the client in browser.)
'',
lines[lines.length - 1], // error location is the last line
];
}

// Cleans up syntax error messages.
if (lines[1].indexOf('Module build failed: ') === 0) {
// For some reason, on the client messages appear duplicated:
// https://github.com/webpack/webpack/issues/3008
// This won't happen in Node but since we share this helpers,
// we will dedupe them right here. We will ignore all lines
// after the original error message text is repeated the second time.
var errorText = lines[1].substr('Module build failed: '.length);
var cleanedLines = [];
var hasReachedDuplicateMessage = false;
// Gather lines until we reach the beginning of duplicate message.
lines.forEach(function(line, index) {
if (
// First time it occurs is fine.
index !== 1 &&
// line.endsWith(errorText)
line.length >= errorText.length &&
line.indexOf(errorText) === line.length - errorText.length
) {
// We see the same error message for the second time!
// Filter out repeated error message and everything after it.
hasReachedDuplicateMessage = true;
}
if (
!hasReachedDuplicateMessage ||
// Print last line anyway because it contains the source location
index === lines.length - 1
) {
// This line is OK to appear in the output.
cleanedLines.push(line);
}
});
// We are clean now!
lines = cleanedLines;
// Finally, brush up the error message a little.
lines[1] = lines[1].replace(
'Module build failed: SyntaxError:',
friendlySyntaxErrorLabel
);
}

// Clean up export errors.
// TODO: we should really send a PR to Webpack for this.
var exportError = /\s*(.+?)\s*(")?export '(.+?)' was not found in '(.+?)'/;
if (lines[1].match(exportError)) {
lines[1] = lines[1].replace(
exportError,
"$1 '$4' does not contain an export named '$3'."
);
}

// TODO: Ideally we should write a custom ESLint formatter instead.

// If the second line already includes a filename, and it's a warning,
// this is likely coming from ESLint. Skip it because Webpack also prints it.
// Let's omit that in this case.
var BEGIN_ESLINT_FILENAME = String.fromCharCode(27) + '[4m';
// Also filter out ESLint summaries for each file
var BEGIN_ESLINT_WARNING_SUMMARY = String.fromCharCode(27) +
'[33m' +
String.fromCharCode(27) +
'[1m' +
String.fromCharCode(10006);
var BEGIN_ESLINT_ERROR_SUMMARY = String.fromCharCode(27) +
'[31m' +
String.fromCharCode(27) +
'[1m' +
String.fromCharCode(10006);
// ESLint puts separators like this between groups. We don't need them:
var ESLINT_EMPTY_SEPARATOR = String.fromCharCode(27) +
'[22m' +
String.fromCharCode(27) +
'[39m';
// Go!
lines = lines.filter(function(line) {
if (line === ESLINT_EMPTY_SEPARATOR) {
return false;
}
if (
line.indexOf(BEGIN_ESLINT_FILENAME) === 0 ||
line.indexOf(BEGIN_ESLINT_WARNING_SUMMARY) === 0 ||
line.indexOf(BEGIN_ESLINT_ERROR_SUMMARY) === 0
) {
return false;
}
return true;
});

// Prepend filename with an explanation.
lines[0] =
// Underline
String.fromCharCode(27) +
'[4m' +
// Filename
lines[0] +
// End underline
String.fromCharCode(27) +
'[24m' +
(isError ? ' contains errors.' : ' contains warnings.');

// Reassemble the message.
message = lines.join('\n');
// Internal stacks are generally useless so we strip them... with the
Expand All @@ -109,15 +145,15 @@ function formatMessage(message) {
''
); // at ... ...:x:y

return message;
return message.trim();
}

function formatWebpackMessages(json) {
var formattedErrors = json.errors.map(function(message) {
return 'Error in ' + formatMessage(message);
return formatMessage(message, true);
});
var formattedWarnings = json.warnings.map(function(message) {
return 'Warning in ' + formatMessage(message);
return formatMessage(message, false);
});
var result = {
errors: formattedErrors,
Expand Down
9 changes: 7 additions & 2 deletions packages/react-dev-utils/webpackHotDevClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,13 @@ function handleWarnings(warnings) {

function printWarnings() {
// Print warnings to the console.
for (var i = 0; i < warnings.length; i++) {
console.warn(stripAnsi(warnings[i]));
var formatted = formatWebpackMessages({
warnings: warnings,
errors: [],
});

for (var i = 0; i < formatted.warnings.length; i++) {
console.warn(stripAnsi(formatted.warnings[i]));
}
}

Expand Down

0 comments on commit ac0f615

Please sign in to comment.