├── .editorconfig ├── .gitignore ├── index.d.ts ├── license ├── package.json ├── readme.md └── src └── index.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_size = 2 6 | indent_style = tab 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.{json,yml,md}] 13 | indent_style = space 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | *-lock.* 4 | *.lock 5 | *.log 6 | 7 | /index.mjs 8 | /index.js 9 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | export interface Messages { 2 | errors: string[]; 3 | warnings: string[]; 4 | } 5 | 6 | export function formatMessage(message: any): string; 7 | 8 | export function formatMessages(stats: any): Messages; 9 | export default formatMessages; 10 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Luke Edwards (lukeed.com) 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "3.0.1", 3 | "name": "webpack-format-messages", 4 | "repository": "lukeed/webpack-format-messages", 5 | "description": "Beautiful formatting for Webpack messages; ported from Create React App!", 6 | "module": "index.mjs", 7 | "main": "index.js", 8 | "types": "index.d.ts", 9 | "license": "MIT", 10 | "author": { 11 | "name": "Luke Edwards", 12 | "email": "luke.edwards05@gmail.com", 13 | "url": "https://lukeed.com" 14 | }, 15 | "engines": { 16 | "node": ">=6" 17 | }, 18 | "exports": { 19 | ".": { 20 | "import": "./index.mjs", 21 | "require": "./index.js" 22 | }, 23 | "./package.json": "./package.json" 24 | }, 25 | "files": [ 26 | "index.js", 27 | "index.mjs", 28 | "index.d.ts" 29 | ], 30 | "scripts": { 31 | "build": "bundt" 32 | }, 33 | "dependencies": { 34 | "kleur": "^4.0.0" 35 | }, 36 | "devDependencies": { 37 | "bundt": "1.1.2" 38 | }, 39 | "keywords": [ 40 | "create-react-app", 41 | "console", 42 | "messages", 43 | "webpack", 44 | "pretty", 45 | "format", 46 | "stats", 47 | "output" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # webpack-format-messages 2 | 3 | > Beautiful formatting for Webpack messages; ported from [Create React App](https://github.com/facebookincubator/create-react-app)! 4 | 5 | The console output from CRA is very well-done! Unfortunately, the only way to use it is to install _all_ of `react-dev-utils`, which is quite a module-rich tree. While there is **nothing** wrong with this, many times I'd prefer a quick install for my custom Webpack configs (which aren't always React-related). 6 | 7 | If you are already using `react-dev-utils`, you do not need this module and should do this instead: 8 | 9 | ```js 10 | const formatMessages = require('react-dev-utils/formatWebpackMessages'); 11 | ``` 12 | 13 | #### Differences 14 | 15 | The source code of this module is (nearly) a direct copy-paste of the original file. Only two modifications have been made: 16 | 17 | 1. The code has been tailored to run in a Node-specific environment --- the original can also be run in the browser. 18 | 2. The module input expects a Webpack `stats` object --- the original required a `toJson()` transformation 19 | 20 | 21 | ## Install 22 | 23 | ``` 24 | $ npm install webpack-format-messages --save-dev 25 | ``` 26 | 27 | 28 | ## Usage 29 | 30 | ```js 31 | const webpack = require('webpack'); 32 | const formatMessages = require('webpack-format-messages'); 33 | // or 34 | const { formatMessages } = require('webpack-format-messages'); 35 | 36 | const compiler = webpack(/* config */); 37 | 38 | compiler.hooks.invalid.tap('invalid', function() { 39 | console.log('Compiling...'); 40 | }); 41 | 42 | compiler.hooks.done.tap('done', (stats) => { 43 | const messages = formatMessages(stats); 44 | 45 | if (!messages.errors.length && !messages.warnings.length) { 46 | console.log('Compiled successfully!'); 47 | } 48 | 49 | if (messages.errors.length) { 50 | console.log('Failed to compile.'); 51 | messages.errors.forEach(e => console.log(e)); 52 | return; 53 | } 54 | 55 | if (messages.warnings.length) { 56 | console.log('Compiled with warnings.'); 57 | messages.warnings.forEach(w => console.log(w)); 58 | } 59 | }); 60 | ``` 61 | 62 | 63 | ## API 64 | 65 | ### formatMessages(stats) 66 | Returns: `{ errors: string[], warnings: string[] }` 67 | 68 | Extracts & prettifies warning and error messages from Webpack. 69 | 70 | > **Note:** This is also the `default` export. 71 | 72 | #### stats 73 | 74 | Type: `Object` 75 | 76 | A Webpack [`stats`](https://github.com/webpack/docs/wiki/node.js-api#stats) object. 77 | 78 | 79 | ### formatMessage(message) 80 | Returns: `string` 81 | 82 | Transforms an individual webpack `stats` message object into a string. 83 | 84 | > **Note:** You probably don't want to use this directly! It's used by `formatMessages` for you. 85 | 86 | ## Related 87 | 88 | * [`webpack-messages`](https://github.com/lukeed/webpack-messages) -- Wraps this module as a Webpack plugin, with lifecycle hooks 89 | 90 | 91 | ## Credits 92 | 93 | This module is pulled directly from [`react-dev-utils`](https://github.com/facebookincubator/create-react-app/tree/master/packages/react-dev-utils), provided by Facebook Incubator. 94 | 95 | This package exists solely as a standalone install~! 96 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This has been adapted from `create-react-app`, authored by Facebook, Inc. 3 | * see: https://github.com/facebookincubator/create-react-app/tree/master/packages/react-dev-utils 4 | */ 5 | 6 | import { inverse } from 'kleur/colors'; 7 | 8 | const errorLabel = 'Syntax error:'; 9 | const isLikelySyntaxError = str => str.includes(errorLabel); 10 | 11 | const exportRegex = /\s*(.+?)\s*(")?export '(.+?)' was not found in '(.+?)'/; 12 | const stackRegex = /^\s*at\s((?!webpack:).)*:\d+:\d+[\s\)]*(\n|$)/gm; 13 | 14 | export function formatMessage(message) { 15 | // Workaround to accommodate Webpack v5 16 | // It gives us an Object now, not a string... 17 | // Objects not identical; details > stack > message 18 | if (typeof message === 'object') { 19 | message = message.details || message.stack || message.message; 20 | } 21 | 22 | let lines = message.split('\n'); 23 | 24 | if (lines.length > 2 && lines[1] === '') { 25 | lines.splice(1, 1); // Remove extra newline. 26 | } 27 | 28 | // Remove loader notation from filenames: 29 | // `./~/css-loader!./src/App.css` ~~> `./src/App.css` 30 | if (lines[0].lastIndexOf('!') !== -1) { 31 | lines[0] = lines[0].substr(lines[0].lastIndexOf('!') + 1); 32 | } 33 | 34 | // Remove useless `entry` filename stack details 35 | lines = lines.filter(line => line.indexOf(' @ ') !== 0); 36 | 37 | // 0 ~> filename; 1 ~> main err msg 38 | if (!lines[0] || !lines[1]) { 39 | return lines.join('\n'); 40 | } 41 | 42 | // Cleans up verbose "module not found" messages for files and packages. 43 | if (lines[1].startsWith('Module not found: ')) { 44 | lines = [ 45 | lines[0], 46 | lines[1] // "Module not found: " is enough detail 47 | .replace("Cannot resolve 'file' or 'directory' ", '') 48 | .replace('Cannot resolve module ', '') 49 | .replace('Error: ', '') 50 | .replace('[CaseSensitivePathsPlugin] ', '') 51 | ]; 52 | } 53 | 54 | // Cleans up syntax error messages. 55 | if (lines[1].startsWith('Module build failed: ')) { 56 | lines[1] = lines[1].replace('Module build failed: SyntaxError:', errorLabel); 57 | } 58 | 59 | if (lines[1].match(exportRegex)) { 60 | lines[1] = lines[1].replace(exportRegex, "$1 '$4' does not contain an export named '$3'."); 61 | } 62 | 63 | lines[0] = inverse(lines[0]); 64 | 65 | // Reassemble & Strip internal tracing, except `webpack:` -- (create-react-app/pull/1050) 66 | return lines.join('\n').replace(stackRegex, '').trim(); 67 | } 68 | 69 | export function formatMessages(stats) { 70 | const { errors, warnings } = stats.toJson({}, true); 71 | 72 | const result = { 73 | errors: errors.map(formatMessage), 74 | warnings: warnings.map(formatMessage), 75 | }; 76 | 77 | // Only show syntax errors if we have them 78 | if (result.errors.some(isLikelySyntaxError)) { 79 | result.errors = result.errors.filter(isLikelySyntaxError); 80 | } 81 | 82 | // First error is usually it; others usually the same 83 | if (result.errors.length > 1) { 84 | result.errors.length = 1; 85 | } 86 | 87 | return result; 88 | } 89 | 90 | export default formatMessages; 91 | --------------------------------------------------------------------------------