From 0a503954b2b45ca50f82b6eef3f3732263acf4d8 Mon Sep 17 00:00:00 2001
From: Dan Abramov <dan.abramov@gmail.com>
Date: Wed, 10 May 2017 21:49:37 +0100
Subject: [PATCH] Prettify errors and warnings for Webpack 2 (#2113)

* Prettify errors and warnings for Webpack 2

* Update formatWebpackMessages.js
---
 .../react-dev-utils/formatWebpackMessages.js  | 118 ++++++++++++------
 .../react-dev-utils/webpackHotDevClient.js    |   9 +-
 2 files changed, 84 insertions(+), 43 deletions(-)

diff --git a/packages/react-dev-utils/formatWebpackMessages.js b/packages/react-dev-utils/formatWebpackMessages.js
index 098c48657..84d22e526 100644
--- a/packages/react-dev-utils/formatWebpackMessages.js
+++ b/packages/react-dev-utils/formatWebpackMessages.js
@@ -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
@@ -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]) {
@@ -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
@@ -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,
diff --git a/packages/react-dev-utils/webpackHotDevClient.js b/packages/react-dev-utils/webpackHotDevClient.js
index 77cc62be2..4e2d3cf13 100644
--- a/packages/react-dev-utils/webpackHotDevClient.js
+++ b/packages/react-dev-utils/webpackHotDevClient.js
@@ -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]));
     }
   }
 
-- 
GitLab