Skip to content

Commit 17bd920

Browse files
committed
Use friendly-errors-webpack-plugin to display friendlier errors
This also delegates the black magic to a thoroughly tested third party. See https://github.com/geowarin/friendly-errors-webpack-plugin.
1 parent c0ac5c4 commit 17bd920

File tree

3 files changed

+200
-288
lines changed

3 files changed

+200
-288
lines changed

config/webpack.config.dev.js

+194-181
Original file line numberDiff line numberDiff line change
@@ -12,196 +12,209 @@ var autoprefixer = require('autoprefixer');
1212
var webpack = require('webpack');
1313
var HtmlWebpackPlugin = require('html-webpack-plugin');
1414
var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin');
15+
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin');
1516
var WatchMissingNodeModulesPlugin = require('../scripts/utils/WatchMissingNodeModulesPlugin');
1617
var paths = require('./paths');
1718
var env = require('./env');
19+
var chalk = require('chalk');
1820

1921
// This is the development configuration.
2022
// It is focused on developer experience and fast rebuilds.
2123
// The production configuration is different and lives in a separate file.
22-
module.exports = {
23-
// This makes the bundle appear split into separate modules in the devtools.
24-
// We don't use source maps here because they can be confusing:
25-
// https://github.com/facebookincubator/create-react-app/issues/343#issuecomment-237241875
26-
// You may want 'cheap-module-source-map' instead if you prefer source maps.
27-
devtool: 'eval',
28-
// These are the "entry points" to our application.
29-
// This means they will be the "root" imports that are included in JS bundle.
30-
// The first two entry points enable "hot" CSS and auto-refreshes for JS.
31-
entry: [
32-
// Include WebpackDevServer client. It connects to WebpackDevServer via
33-
// sockets and waits for recompile notifications. When WebpackDevServer
34-
// recompiles, it sends a message to the client by socket. If only CSS
35-
// was changed, the app reload just the CSS. Otherwise, it will refresh.
36-
// The "?/" bit at the end tells the client to look for the socket at
37-
// the root path, i.e. /sockjs-node/. Otherwise visiting a client-side
38-
// route like /todos/42 would make it wrongly request /todos/42/sockjs-node.
39-
// The socket server is a part of WebpackDevServer which we are using.
40-
// The /sockjs-node/ path I'm referring to is hardcoded in WebpackDevServer.
41-
require.resolve('webpack-dev-server/client') + '?/',
42-
// Include Webpack hot module replacement runtime. Webpack is pretty
43-
// low-level so we need to put all the pieces together. The runtime listens
44-
// to the events received by the client above, and applies updates (such as
45-
// new CSS) to the running application.
46-
require.resolve('webpack/hot/dev-server'),
47-
// We ship a few polyfills by default.
48-
require.resolve('./polyfills'),
49-
// Finally, this is your app's code:
50-
path.join(paths.appSrc, 'index')
51-
// We include the app code last so that if there is a runtime error during
52-
// initialization, it doesn't blow up the WebpackDevServer client, and
53-
// changing JS code would still trigger a refresh.
54-
],
55-
output: {
56-
// Next line is not used in dev but WebpackDevServer crashes without it:
57-
path: paths.appBuild,
58-
// Add /* filename */ comments to generated require()s in the output.
59-
pathinfo: true,
60-
// This does not produce a real file. It's just the virtual path that is
61-
// served by WebpackDevServer in development. This is the JS bundle
62-
// containing code from all our entry points, and the Webpack runtime.
63-
filename: 'static/js/bundle.js',
64-
// In development, we always serve from the root. This makes config easier.
65-
publicPath: '/'
66-
},
67-
resolve: {
68-
// These are the reasonable defaults supported by the Node ecosystem.
69-
extensions: ['.js', '.json', ''],
70-
alias: {
71-
// This `alias` section can be safely removed after ejection.
72-
// We do this because `babel-runtime` may be inside `react-scripts`,
73-
// so when `babel-plugin-transform-runtime` imports it, it will not be
74-
// available to the app directly. This is a temporary solution that lets
75-
// us ship support for generators. However it is far from ideal, and
76-
// if we don't have a good solution, we should just make `babel-runtime`
77-
// a dependency in generated projects.
78-
// See https://github.com/facebookincubator/create-react-app/issues/255
79-
'babel-runtime/regenerator': require.resolve('babel-runtime/regenerator'),
80-
'react-native': 'react-native-web'
81-
}
82-
},
83-
// Resolve loaders (webpack plugins for CSS, images, transpilation) from the
84-
// directory of `react-scripts` itself rather than the project directory.
85-
// You can remove this after ejecting.
86-
resolveLoader: {
87-
root: paths.ownNodeModules,
88-
moduleTemplates: ['*-loader']
89-
},
90-
module: {
91-
// First, run the linter.
92-
// It's important to do this before Babel processes the JS.
93-
preLoaders: [
94-
{
95-
test: /\.js$/,
96-
loader: 'eslint',
97-
include: paths.appSrc,
98-
}
24+
module.exports = function(serverPort) {
25+
return {
26+
// This makes the bundle appear split into separate modules in the devtools.
27+
// We don't use source maps here because they can be confusing:
28+
// https://github.com/facebookincubator/create-react-app/issues/343#issuecomment-237241875
29+
// You may want 'cheap-module-source-map' instead if you prefer source maps.
30+
devtool: 'eval',
31+
// These are the "entry points" to our application.
32+
// This means they will be the "root" imports that are included in JS bundle.
33+
// The first two entry points enable "hot" CSS and auto-refreshes for JS.
34+
entry: [
35+
// Include WebpackDevServer client. It connects to WebpackDevServer via
36+
// sockets and waits for recompile notifications. When WebpackDevServer
37+
// recompiles, it sends a message to the client by socket. If only CSS
38+
// was changed, the app reload just the CSS. Otherwise, it will refresh.
39+
// The "?/" bit at the end tells the client to look for the socket at
40+
// the root path, i.e. /sockjs-node/. Otherwise visiting a client-side
41+
// route like /todos/42 would make it wrongly request /todos/42/sockjs-node.
42+
// The socket server is a part of WebpackDevServer which we are using.
43+
// The /sockjs-node/ path I'm referring to is hardcoded in WebpackDevServer.
44+
require.resolve('webpack-dev-server/client') + '?/',
45+
// Include Webpack hot module replacement runtime. Webpack is pretty
46+
// low-level so we need to put all the pieces together. The runtime listens
47+
// to the events received by the client above, and applies updates (such as
48+
// new CSS) to the running application.
49+
require.resolve('webpack/hot/dev-server'),
50+
// We ship a few polyfills by default.
51+
require.resolve('./polyfills'),
52+
// Finally, this is your app's code:
53+
path.join(paths.appSrc, 'index')
54+
// We include the app code last so that if there is a runtime error during
55+
// initialization, it doesn't blow up the WebpackDevServer client, and
56+
// changing JS code would still trigger a refresh.
9957
],
100-
loaders: [
101-
// Process JS with Babel.
102-
{
103-
test: /\.js$/,
104-
include: paths.appSrc,
105-
loader: 'babel',
106-
query: require('./babel.dev')
107-
},
108-
// "postcss" loader applies autoprefixer to our CSS.
109-
// "css" loader resolves paths in CSS and adds assets as dependencies.
110-
// "style" loader turns CSS into JS modules that inject <style> tags.
111-
// In production, we use a plugin to extract that CSS to a file, but
112-
// in development "style" loader enables hot editing of CSS.
113-
{
114-
test: /\.css$/,
115-
include: [paths.appSrc, paths.appNodeModules],
116-
loader: 'style!css!postcss'
117-
},
118-
// JSON is not enabled by default in Webpack but both Node and Browserify
119-
// allow it implicitly so we also enable it.
120-
{
121-
test: /\.json$/,
122-
include: [paths.appSrc, paths.appNodeModules],
123-
loader: 'json'
124-
},
125-
// "file" loader makes sure those assets get served by WebpackDevServer.
126-
// When you `import` an asset, you get its (virtual) filename.
127-
// In production, they would get copied to the `build` folder.
128-
{
129-
test: /\.(ico|jpg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
130-
include: [paths.appSrc, paths.appNodeModules],
131-
exclude: /\/favicon.ico$/,
132-
loader: 'file',
133-
query: {
134-
name: 'static/media/[name].[hash:8].[ext]'
135-
}
136-
},
137-
// A special case for favicon.ico to place it into build root directory.
138-
{
139-
test: /\/favicon.ico$/,
140-
include: [paths.appSrc],
141-
loader: 'file',
142-
query: {
143-
name: 'favicon.ico?[hash:8]'
58+
output: {
59+
// Next line is not used in dev but WebpackDevServer crashes without it:
60+
path: paths.appBuild,
61+
// Add /* filename */ comments to generated require()s in the output.
62+
pathinfo: true,
63+
// This does not produce a real file. It's just the virtual path that is
64+
// served by WebpackDevServer in development. This is the JS bundle
65+
// containing code from all our entry points, and the Webpack runtime.
66+
filename: 'static/js/bundle.js',
67+
// In development, we always serve from the root. This makes config easier.
68+
publicPath: '/'
69+
},
70+
resolve: {
71+
// These are the reasonable defaults supported by the Node ecosystem.
72+
extensions: ['.js', '.json', ''],
73+
alias: {
74+
// This `alias` section can be safely removed after ejection.
75+
// We do this because `babel-runtime` may be inside `react-scripts`,
76+
// so when `babel-plugin-transform-runtime` imports it, it will not be
77+
// available to the app directly. This is a temporary solution that lets
78+
// us ship support for generators. However it is far from ideal, and
79+
// if we don't have a good solution, we should just make `babel-runtime`
80+
// a dependency in generated projects.
81+
// See https://github.com/facebookincubator/create-react-app/issues/255
82+
'babel-runtime/regenerator': require.resolve('babel-runtime/regenerator'),
83+
'react-native': 'react-native-web'
84+
}
85+
},
86+
// Resolve loaders (webpack plugins for CSS, images, transpilation) from the
87+
// directory of `react-scripts` itself rather than the project directory.
88+
// You can remove this after ejecting.
89+
resolveLoader: {
90+
root: paths.ownNodeModules,
91+
moduleTemplates: ['*-loader']
92+
},
93+
module: {
94+
// First, run the linter.
95+
// It's important to do this before Babel processes the JS.
96+
preLoaders: [
97+
{
98+
test: /\.js$/,
99+
loader: 'eslint',
100+
include: paths.appSrc,
144101
}
145-
},
146-
// "url" loader works just like "file" loader but it also embeds
147-
// assets smaller than specified size as data URLs to avoid requests.
148-
{
149-
test: /\.(mp4|webm)(\?.*)?$/,
150-
include: [paths.appSrc, paths.appNodeModules],
151-
loader: 'url',
152-
query: {
153-
limit: 10000,
154-
name: 'static/media/[name].[hash:8].[ext]'
102+
],
103+
loaders: [
104+
// Process JS with Babel.
105+
{
106+
test: /\.js$/,
107+
include: paths.appSrc,
108+
loader: 'babel',
109+
query: require('./babel.dev')
110+
},
111+
// "postcss" loader applies autoprefixer to our CSS.
112+
// "css" loader resolves paths in CSS and adds assets as dependencies.
113+
// "style" loader turns CSS into JS modules that inject <style> tags.
114+
// In production, we use a plugin to extract that CSS to a file, but
115+
// in development "style" loader enables hot editing of CSS.
116+
{
117+
test: /\.css$/,
118+
include: [paths.appSrc, paths.appNodeModules],
119+
loader: 'style!css!postcss'
120+
},
121+
// JSON is not enabled by default in Webpack but both Node and Browserify
122+
// allow it implicitly so we also enable it.
123+
{
124+
test: /\.json$/,
125+
include: [paths.appSrc, paths.appNodeModules],
126+
loader: 'json'
127+
},
128+
// "file" loader makes sure those assets get served by WebpackDevServer.
129+
// When you `import` an asset, you get its (virtual) filename.
130+
// In production, they would get copied to the `build` folder.
131+
{
132+
test: /\.(ico|jpg|png|gif|eot|otf|webp|svg|ttf|woff|woff2)(\?.*)?$/,
133+
include: [paths.appSrc, paths.appNodeModules],
134+
exclude: /\/favicon.ico$/,
135+
loader: 'file',
136+
query: {
137+
name: 'static/media/[name].[hash:8].[ext]'
138+
}
139+
},
140+
// A special case for favicon.ico to place it into build root directory.
141+
{
142+
test: /\/favicon.ico$/,
143+
include: [paths.appSrc],
144+
loader: 'file',
145+
query: {
146+
name: 'favicon.ico?[hash:8]'
147+
}
148+
},
149+
// "url" loader works just like "file" loader but it also embeds
150+
// assets smaller than specified size as data URLs to avoid requests.
151+
{
152+
test: /\.(mp4|webm)(\?.*)?$/,
153+
include: [paths.appSrc, paths.appNodeModules],
154+
loader: 'url',
155+
query: {
156+
limit: 10000,
157+
name: 'static/media/[name].[hash:8].[ext]'
158+
}
159+
},
160+
// "html" loader is used to process template page (index.html) to resolve
161+
// resources linked with <link href="./relative/path"> HTML tags.
162+
{
163+
test: /\.html$/,
164+
loader: 'html',
165+
query: {
166+
attrs: ['link:href'],
167+
}
155168
}
156-
},
157-
// "html" loader is used to process template page (index.html) to resolve
158-
// resources linked with <link href="./relative/path"> HTML tags.
159-
{
160-
test: /\.html$/,
161-
loader: 'html',
162-
query: {
163-
attrs: ['link:href'],
169+
]
170+
},
171+
// Point ESLint to our predefined config.
172+
eslint: {
173+
configFile: path.join(__dirname, 'eslint.js'),
174+
useEslintrc: false
175+
},
176+
// We use PostCSS for autoprefixing only.
177+
postcss: function() {
178+
return [
179+
autoprefixer({
180+
browsers: [
181+
'>1%',
182+
'last 4 versions',
183+
'Firefox ESR',
184+
'not ie < 9', // React doesn't support IE8 anyway
185+
]
186+
}),
187+
];
188+
},
189+
plugins: [
190+
// Generates an `index.html` file with the <script> injected.
191+
new HtmlWebpackPlugin({
192+
inject: true,
193+
template: paths.appHtml,
194+
}),
195+
new FriendlyErrorsPlugin({
196+
compilationSuccessInfo: {
197+
messages: ['You application is accessible at ' + chalk.cyan('http://localhost:' + serverPort)],
198+
notes: [
199+
'Note that the development build is not optimized.',
200+
'To create a production build, use ' + chalk.cyan('npm run build') + '.'
201+
]
164202
}
165-
}
166-
]
167-
},
168-
// Point ESLint to our predefined config.
169-
eslint: {
170-
configFile: path.join(__dirname, 'eslint.js'),
171-
useEslintrc: false
172-
},
173-
// We use PostCSS for autoprefixing only.
174-
postcss: function() {
175-
return [
176-
autoprefixer({
177-
browsers: [
178-
'>1%',
179-
'last 4 versions',
180-
'Firefox ESR',
181-
'not ie < 9', // React doesn't support IE8 anyway
182-
]
183203
}),
184-
];
185-
},
186-
plugins: [
187-
// Generates an `index.html` file with the <script> injected.
188-
new HtmlWebpackPlugin({
189-
inject: true,
190-
template: paths.appHtml,
191-
}),
192-
// Makes some environment variables available to the JS code, for example:
193-
// if (process.env.NODE_ENV === 'development') { ... }. See `env.js`.
194-
new webpack.DefinePlugin(env),
195-
// This is necessary to emit hot updates (currently CSS only):
196-
new webpack.HotModuleReplacementPlugin(),
197-
// Watcher doesn't work well if you mistype casing in a path so we use
198-
// a plugin that prints an error when you attempt to do this.
199-
// See https://github.com/facebookincubator/create-react-app/issues/240
200-
new CaseSensitivePathsPlugin(),
201-
// If you require a missing module and then `npm install` it, you still have
202-
// to restart the development server for Webpack to discover it. This plugin
203-
// makes the discovery automatic so you don't have to restart.
204-
// See https://github.com/facebookincubator/create-react-app/issues/186
205-
new WatchMissingNodeModulesPlugin(paths.appNodeModules)
206-
]
207-
};
204+
// Makes some environment variables available to the JS code, for example:
205+
// if (process.env.NODE_ENV === 'development') { ... }. See `env.js`.
206+
new webpack.DefinePlugin(env),
207+
// This is necessary to emit hot updates (currently CSS only):
208+
new webpack.HotModuleReplacementPlugin(),
209+
// Watcher doesn't work well if you mistype casing in a path so we use
210+
// a plugin that prints an error when you attempt to do this.
211+
// See https://github.com/facebookincubator/create-react-app/issues/240
212+
new CaseSensitivePathsPlugin(),
213+
// If you require a missing module and then `npm install` it, you still have
214+
// to restart the development server for Webpack to discover it. This plugin
215+
// makes the discovery automatic so you don't have to restart.
216+
// See https://github.com/facebookincubator/create-react-app/issues/186
217+
new WatchMissingNodeModulesPlugin(paths.appNodeModules)
218+
]
219+
};
220+
}

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
"extract-text-webpack-plugin": "1.0.1",
5757
"file-loader": "0.9.0",
5858
"filesize": "3.3.0",
59+
"friendly-errors-webpack-plugin": "^1.0.0",
5960
"fs-extra": "0.30.0",
6061
"gzip-size": "3.0.0",
6162
"html-loader": "0.4.3",

0 commit comments

Comments
 (0)