1/** 2 * Copyright (c) 2022 Expo, Inc. 3 * Copyright (c) 2015-present, Facebook, Inc. 4 * 5 * This source code is licensed under the MIT license found in the 6 * LICENSE file in the root directory of this source tree. 7 * 8 * Based on https://github.com/facebook/create-react-app/blob/b172b5e/packages/react-dev-utils/ModuleNotFoundPlugin.js 9 * But with Node LTS support. 10 */ 11 12import type { Stats } from 'webpack'; 13 14const friendlySyntaxErrorLabel = 'Syntax error:'; 15 16function isLikelyASyntaxError(message: string): boolean { 17 return message.indexOf(friendlySyntaxErrorLabel) !== -1; 18} 19 20// Cleans up webpack error messages. 21function formatMessage(message: string | { message: string } | { message: string }[]) { 22 let lines: string[] = []; 23 24 if (typeof message === 'string') { 25 lines = message.split('\n'); 26 } else if ('message' in message) { 27 lines = message['message'].split('\n'); 28 } else if (Array.isArray(message)) { 29 message.forEach((message) => { 30 if ('message' in message) { 31 lines = message['message'].split('\n'); 32 } 33 }); 34 } 35 36 // Strip webpack-added headers off errors/warnings 37 // https://github.com/webpack/webpack/blob/master/lib/ModuleError.js 38 lines = lines.filter((line) => !/Module [A-z ]+\(from/.test(line)); 39 40 // Transform parsing error into syntax error 41 // TODO: move this to our ESLint formatter? 42 lines = lines.map((line) => { 43 const parsingError = /Line (\d+):(?:(\d+):)?\s*Parsing error: (.+)$/.exec(line); 44 if (!parsingError) { 45 return line; 46 } 47 const [, errorLine, errorColumn, errorMessage] = parsingError; 48 return `${friendlySyntaxErrorLabel} ${errorMessage} (${errorLine}:${errorColumn})`; 49 }); 50 51 message = lines.join('\n'); 52 // Smoosh syntax errors (commonly found in CSS) 53 message = message.replace( 54 /SyntaxError\s+\((\d+):(\d+)\)\s*(.+?)\n/g, 55 `${friendlySyntaxErrorLabel} $3 ($1:$2)\n` 56 ); 57 // Clean up export errors 58 message = message.replace( 59 /^.*export '(.+?)' was not found in '(.+?)'.*$/gm, 60 `Attempted import error: '$1' is not exported from '$2'.` 61 ); 62 message = message.replace( 63 /^.*export 'default' \(imported as '(.+?)'\) was not found in '(.+?)'.*$/gm, 64 `Attempted import error: '$2' does not contain a default export (imported as '$1').` 65 ); 66 message = message.replace( 67 /^.*export '(.+?)' \(imported as '(.+?)'\) was not found in '(.+?)'.*$/gm, 68 `Attempted import error: '$1' is not exported from '$3' (imported as '$2').` 69 ); 70 lines = message.split('\n'); 71 72 // Remove leading newline 73 if (lines.length > 2 && lines[1].trim() === '') { 74 lines.splice(1, 1); 75 } 76 // Clean up file name 77 lines[0] = lines[0].replace(/^(.*) \d+:\d+-\d+$/, '$1'); 78 79 // Cleans up verbose "module not found" messages for files and packages. 80 if (lines[1] && lines[1].indexOf('Module not found: ') === 0) { 81 lines = [ 82 lines[0], 83 lines[1] 84 .replace('Error: ', '') 85 .replace('Module not found: Cannot find file:', 'Cannot find file:'), 86 ]; 87 } 88 89 // Add helpful message for users trying to use Sass for the first time 90 if (lines[1] && lines[1].match(/Cannot find module.+sass/)) { 91 lines[1] = 'To import Sass files, you first need to install sass.\n'; 92 lines[1] += 'Run `npm install sass` or `yarn add sass` inside your workspace.'; 93 } 94 95 message = lines.join('\n'); 96 // Internal stacks are generally useless so we strip them... with the 97 // exception of stacks containing `webpack:` because they're normally 98 // from user code generated by webpack. For more information see 99 // https://github.com/facebook/create-react-app/pull/1050 100 message = message.replace(/^\s*at\s((?!webpack:).)*:\d+:\d+[\s)]*(\n|$)/gm, ''); // at ... ...:x:y 101 message = message.replace(/^\s*at\s<anonymous>(\n|$)/gm, ''); // at <anonymous> 102 lines = message.split('\n'); 103 104 // Remove duplicated newlines 105 lines = lines.filter( 106 (line, index, arr) => index === 0 || line.trim() !== '' || line.trim() !== arr[index - 1].trim() 107 ); 108 109 // Reassemble the message 110 message = lines.join('\n'); 111 return message.trim(); 112} 113 114export function formatWebpackMessages(json?: Stats.ToJsonOutput) { 115 const formattedErrors = json?.errors?.map(formatMessage); 116 const formattedWarnings = json?.warnings?.map(formatMessage); 117 const result = { errors: formattedErrors, warnings: formattedWarnings }; 118 if (result.errors?.some(isLikelyASyntaxError)) { 119 // If there are any syntax errors, show just them. 120 result.errors = result.errors.filter(isLikelyASyntaxError); 121 } 122 return result; 123} 124