diff --git a/client-src/overlay.js b/client-src/overlay.js index 5c074702ff..1edbb781e0 100644 --- a/client-src/overlay.js +++ b/client-src/overlay.js @@ -3,6 +3,7 @@ import ansiHTML from "ansi-html-community"; import { encode } from "html-entities"; +import formatWebpackMessage from "./utils/format-webpack-message.js"; const colors = { reset: ["transparent", "transparent"], @@ -147,11 +148,8 @@ function hide() { */ function formatProblem(type, item) { let header = type === "warning" ? "WARNING" : "ERROR"; - let body = ""; - if (typeof item === "string") { - body += item; - } else { + if (typeof item === "object") { const file = item.file || ""; // eslint-disable-next-line no-nested-ternary const moduleName = item.moduleName @@ -168,10 +166,9 @@ function formatProblem(type, item) { }${loc ? ` ${loc}` : ""}` : "" }`; - body += item.message || ""; } - return { header, body }; + return { header, body: formatWebpackMessage(item) }; } // Compilation with errors (e.g. syntax error or missing modules). diff --git a/client-src/utils/format-webpack-message.js b/client-src/utils/format-webpack-message.js new file mode 100644 index 0000000000..dccf8f5819 --- /dev/null +++ b/client-src/utils/format-webpack-message.js @@ -0,0 +1,132 @@ +/** +MIT License +Copyright (c) 2015-present, Facebook, Inc. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +import stripAnsi from "../modules/strip-ansi/index.js"; +// This file is based on https://github.com/facebook/create-react-app/blob/7b1a32be6ec9f99a6c9a3c66813f3ac09c4736b9/packages/react-dev-utils/formatWebpackMessages.js +// It's been edited to remove chalk and CRA-specific logic + +const friendlySyntaxErrorLabel = "Syntax error:"; + +// Cleans up webpack error messages. +function formatWebpackMessage(message, verbose) { + if (typeof message === "object" && message.message) { + const filteredModuleTrace = + message.moduleTrace && + message.moduleTrace.filter( + (trace) => + !/next-(middleware|client-pages|flight-(client|server))-loader\.js/.test( + trace.originName + ) + ); + message = + (message.moduleName ? `${stripAnsi(message.moduleName)}\n` : "") + + (message.file ? `${stripAnsi(message.file)}\n` : "") + + message.message + + (message.details && verbose ? `\n${message.details}` : "") + + (filteredModuleTrace && filteredModuleTrace.length && verbose + ? `\n\nImport trace for requested module:${filteredModuleTrace + .map((trace) => `\n${trace.originName}`) + .join("")}` + : "") + + (message.stack && verbose ? `\n${message.stack}` : ""); + } + let lines = message.split("\n"); + + // Strip Webpack-added headers off errors/warnings + // https://github.com/webpack/webpack/blob/master/lib/ModuleError.js + lines = lines.filter((line) => !/Module [A-z ]+\(from/.test(line)); + + // Transform parsing error into syntax error + lines = lines.map((line) => { + const parsingError = /Line (\d+):(?:(\d+):)?\s*Parsing error: (.+)$/.exec( + line + ); + if (!parsingError) { + return line; + } + const [, errorLine, errorColumn, errorMessage] = parsingError; + return `${friendlySyntaxErrorLabel} ${errorMessage} (${errorLine}:${errorColumn})`; + }); + + message = lines.join("\n"); + // Smoosh syntax errors (commonly found in CSS) + message = message.replace( + /SyntaxError\s+\((\d+):(\d+)\)\s*(.+?)\n/g, + `${friendlySyntaxErrorLabel} $3 ($1:$2)\n` + ); + // Clean up export errors + message = message.replace( + /^.*export '(.+?)' was not found in '(.+?)'.*$/gm, + `Attempted import error: '$1' is not exported from '$2'.` + ); + message = message.replace( + /^.*export 'default' \(imported as '(.+?)'\) was not found in '(.+?)'.*$/gm, + `Attempted import error: '$2' does not contain a default export (imported as '$1').` + ); + message = message.replace( + /^.*export '(.+?)' \(imported as '(.+?)'\) was not found in '(.+?)'.*$/gm, + `Attempted import error: '$1' is not exported from '$3' (imported as '$2').` + ); + lines = message.split("\n"); + + // Remove leading newline + if (lines.length > 2 && lines[1].trim() === "") { + lines.splice(1, 1); + } + + // Cleans up verbose "module not found" messages for files and packages. + if (lines[1] && lines[1].indexOf("Module not found: ") === 0) { + lines = [ + lines[0], + lines[1] + .replace("Error: ", "") + .replace("Module not found: Cannot find file:", "Cannot find file:"), + ...lines.slice(2), + ]; + } + + if (!verbose) { + message = lines.join("\n"); + // Internal stacks are generally useless so we strip them... with the + // exception of stacks containing `webpack:` because they're normally + // from user code generated by Webpack. For more information see + // https://github.com/facebook/create-react-app/pull/1050 + message = message.replace( + /^\s*at\s((?!webpack:).)*:\d+:\d+[\s)]*(\n|$)/gm, + "" + // eslint-disable-next-line line-comment-position + ); // at ... ...:x:y + // eslint-disable-next-line line-comment-position + message = message.replace(/^\s*at\s(\n|$)/gm, ""); // at + lines = message.split("\n"); + } + + // Remove duplicated newlines + lines = lines.filter( + (line, index, arr) => + index === 0 || line.trim() !== "" || line.trim() !== arr[index - 1].trim() + ); + + // Reassemble the message + message = lines.join("\n"); + return message.trim(); +} + +export default formatWebpackMessage; diff --git a/test/e2e/__snapshots__/overlay.test.js.snap.webpack5 b/test/e2e/__snapshots__/overlay.test.js.snap.webpack5 index c8a56d4012..20efe665c7 100644 --- a/test/e2e/__snapshots__/overlay.test.js.snap.webpack5 +++ b/test/e2e/__snapshots__/overlay.test.js.snap.webpack5 @@ -70,8 +70,8 @@ exports[`overlay should not show initially, then show on an error and allow to c ERROR in ./foo.js 1:1

- Module parse failed: Unterminated template (1:1) You may need an - appropriate loader to handle this file type, currently no loaders are + ./foo.js Module parse failed: Unterminated template (1:1) You may need + an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders > \`;
@@ -157,8 +157,8 @@ exports[`overlay should not show initially, then show on an error, then hide on ERROR in ./foo.js 1:1

- Module parse failed: Unterminated template (1:1) You may need an - appropriate loader to handle this file type, currently no loaders are + ./foo.js Module parse failed: Unterminated template (1:1) You may need + an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders > \`;
@@ -244,8 +244,8 @@ exports[`overlay should not show initially, then show on an error, then show oth ERROR in ./foo.js 1:1

- Module parse failed: Unterminated template (1:1) You may need an - appropriate loader to handle this file type, currently no loaders are + ./foo.js Module parse failed: Unterminated template (1:1) You may need + an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders > \`;
@@ -294,8 +294,8 @@ exports[`overlay should not show initially, then show on an error, then show oth ERROR in ./foo.js 1:1

- Module parse failed: Unterminated template (1:1) You may need an - appropriate loader to handle this file type, currently no loaders are + ./foo.js Module parse failed: Unterminated template (1:1) You may need + an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders > \`;a