Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add syntax error overlay in development #744

Merged
merged 3 commits into from
Sep 25, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions packages/react-dev-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,3 +178,25 @@ prompt(
}
});
```

#### `webpackHotDevClient.js`

This is an alternative client for [WebpackDevServer](https://github.com/webpack/webpack-dev-server) that shows a syntax error overlay.

It currently supports only Webpack 1.x.

```js
// Webpack development config
module.exports = {
// ...
entry: [
// You can replace the line below with these two lines if you prefer the
// stock client:
// require.resolve('webpack-dev-server/client') + '?/',
// require.resolve('webpack/hot/dev-server'),
'react-dev-utils/webpackHotDevClient',
'src/index'
],
// ...
}
```
133 changes: 99 additions & 34 deletions packages/react-dev-utils/formatWebpackMessages.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,50 +7,115 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/

// WARNING: this code is untranspiled and is used in browser too.
// Please make sure any changes are in ES5 or contribute a Babel compile step.

// Some custom utilities to prettify Webpack output.
// This is a little hacky.
// It would be easier if webpack provided a rich error object.
// This is quite hacky and hopefully won't be needed when Webpack fixes this.
// https://github.com/webpack/webpack/issues/2878

var friendlySyntaxErrorLabel = 'Syntax error:';

function isLikelyASyntaxError(message) {
return message.indexOf(friendlySyntaxErrorLabel) !== -1;
}

// Cleans up webpack error messages.
function formatMessage(message) {
return message
// Make some common errors shorter:
.replace(
// Babel syntax error
var lines = message.split('\n');

// line #0 is filename
// line #1 is the main error message
if (!lines[0] || !lines[1]) {
return message;
}

// Remove webpack-specific loader notation from filename.
// Before:
// ./~/css-loader!./~/postcss-loader!./src/App.css
// After:
// ./src/App.css
if (lines[0].lastIndexOf('!') !== -1) {
lines[0] = lines[0].substr(lines[0].lastIndexOf('!') + 1);
}

// Cleans up verbose "module not found" messages for files and packages.
if (lines[1].indexOf('Module not found: ') === 0) {
lines = [
lines[0],
// Clean up message because "Module not found: " is descriptive enough.
lines[1].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
)
.replace(
// Webpack file not found error
/Module not found: Error: Cannot resolve 'file' or 'directory'/,
'Module not found:'
)
// Internal stacks are generally useless so we strip them
.replace(/^\s*at\s.*:\d+:\d+[\s\)]*\n/gm, '') // at ... ...:x:y
// Webpack loader names obscure CSS filenames
.replace('./~/css-loader!./~/postcss-loader!', '');
);
}

// Reassemble the message.
message = lines.join('\n');
// Internal stacks are generally useless so we strip them
message = message.replace(
/^\s*at\s.*:\d+:\d+[\s\)]*\n/gm, ''
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are node error message internationalized? PHP ones are and this would break with them

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not as far as I know. At least not yet.

); // at ... ...:x:y

return message;
}

function formatWebpackMessages(stats) {
var hasErrors = stats.hasErrors();
var hasWarnings = stats.hasWarnings();
if (!hasErrors && !hasWarnings) {
return {
errors: [],
warnings: []
};
}
// We use stats.toJson({}, true) to make output more compact and readable:
// https://github.com/facebookincubator/create-react-app/issues/401#issuecomment-238291901
var json = stats.toJson({}, true);
var formattedErrors = json.errors.map(message =>
'Error in ' + formatMessage(message)
);
var formattedWarnings = json.warnings.map(message =>
'Warning in ' + formatMessage(message)
);
function formatWebpackMessages(json) {
var formattedErrors = json.errors.map(function(message) {
return 'Error in ' + formatMessage(message)
});
var formattedWarnings = json.warnings.map(function(message) {
return 'Warning in ' + formatMessage(message)
});
var result = {
errors: formattedErrors,
warnings: formattedWarnings
Expand Down
9 changes: 7 additions & 2 deletions packages/react-dev-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@
"openChrome.applescript",
"openBrowser.js",
"prompt.js",
"WatchMissingNodeModulesPlugin.js"
"WatchMissingNodeModulesPlugin.js",
"webpackHotDevClient.js"
],
"dependencies": {
"ansi-html": "0.0.5",
"chalk": "1.1.3",
"escape-string-regexp": "1.0.5",
"opn": "4.0.2"
"html-entities": "1.2.0",
"opn": "4.0.2",
"sockjs-client": "1.0.3",
"strip-ansi": "3.0.1"
},
"peerDependencies": {
"webpack": "^1.13.2"
Expand Down
Loading