From 8e1629578f32d6ad676a80af6435cfdb0d0bbf37 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Thu, 4 Aug 2016 20:56:52 +0100 Subject: [PATCH] Document configuration and build process (#362) --- config/babel.dev.js | 27 +++++++++----- config/babel.prod.js | 30 ++++++++++------ config/polyfills.js | 1 + config/webpack.config.dev.js | 67 ++++++++++++++++++++++++++++++++++- config/webpack.config.prod.js | 64 ++++++++++++++++++++++++++++++--- scripts/build.js | 8 ++++- scripts/start.js | 46 ++++++++++++++++++------ 7 files changed, 205 insertions(+), 38 deletions(-) diff --git a/config/babel.dev.js b/config/babel.dev.js index 64545127..04af8606 100644 --- a/config/babel.dev.js +++ b/config/babel.dev.js @@ -8,22 +8,31 @@ */ module.exports = { + // Don't try to find .babelrc because we want to force this configuration. babelrc: false, + // This is a feature of `babel-loader` for webpack (not Babel itself). + // It enables caching results in OS temporary directory for faster rebuilds. cacheDirectory: true, presets: [ - 'babel-preset-es2015', - 'babel-preset-es2016', - 'babel-preset-react' - ].map(require.resolve), + // let, const, destructuring, classes, modules + require.resolve('babel-preset-es2015'), + // exponentiation + require.resolve('babel-preset-es2016'), + // JSX, Flow + require.resolve('babel-preset-react') + ], plugins: [ - 'babel-plugin-syntax-trailing-function-commas', - 'babel-plugin-transform-class-properties', - 'babel-plugin-transform-object-rest-spread' - ].map(require.resolve).concat([ + // function x(a, b, c,) { } + require.resolve('babel-plugin-syntax-trailing-function-commas'), + // class { handleClick = () => { } } + require.resolve('babel-plugin-transform-class-properties'), + // { ...todo, completed: true } + require.resolve('babel-plugin-transform-object-rest-spread'), + // Polyfills the runtime needed for generators [require.resolve('babel-plugin-transform-runtime'), { helpers: false, polyfill: false, regenerator: true }] - ]) + ] }; diff --git a/config/babel.prod.js b/config/babel.prod.js index f5446012..ae5ea038 100644 --- a/config/babel.prod.js +++ b/config/babel.prod.js @@ -8,22 +8,30 @@ */ module.exports = { + // Don't try to find .babelrc because we want to force this configuration. babelrc: false, presets: [ - 'babel-preset-es2015', - 'babel-preset-es2016', - 'babel-preset-react' - ].map(require.resolve), + // let, const, destructuring, classes, modules + require.resolve('babel-preset-es2015'), + // exponentiation + require.resolve('babel-preset-es2016'), + // JSX, Flow + require.resolve('babel-preset-react') + ], plugins: [ - 'babel-plugin-syntax-trailing-function-commas', - 'babel-plugin-transform-class-properties', - 'babel-plugin-transform-object-rest-spread', - 'babel-plugin-transform-react-constant-elements', - ].map(require.resolve).concat([ + // function x(a, b, c,) { } + require.resolve('babel-plugin-syntax-trailing-function-commas'), + // class { handleClick = () => { } } + require.resolve('babel-plugin-transform-class-properties'), + // { ...todo, completed: true } + require.resolve('babel-plugin-transform-object-rest-spread'), + // Polyfills the runtime needed for generators [require.resolve('babel-plugin-transform-runtime'), { helpers: false, polyfill: false, regenerator: true - }] - ]) + }], + // Optimization: hoist JSX that never changes out of render() + require.resolve('babel-plugin-transform-react-constant-elements') + ], }; diff --git a/config/polyfills.js b/config/polyfills.js index 0a0bd950..1f71e4ac 100644 --- a/config/polyfills.js +++ b/config/polyfills.js @@ -6,4 +6,5 @@ if (typeof Promise === 'undefined') { window.Promise = require('promise/lib/es6-extensions.js'); } +// fetch() polyfill for making API calls. require('whatwg-fetch'); diff --git a/config/webpack.config.dev.js b/config/webpack.config.dev.js index 26e60af3..7893f09c 100644 --- a/config/webpack.config.dev.js +++ b/config/webpack.config.dev.js @@ -15,22 +15,56 @@ var CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); var WatchMissingNodeModulesPlugin = require('../scripts/utils/WatchMissingNodeModulesPlugin'); var paths = require('./paths'); +// This is the development configuration. +// It is focused on developer experience and fast rebuilds. +// The production configuration is different and lives in a separate file. module.exports = { + // This makes the bundle appear split into separate modules in the devtools. + // We don't use source maps here because they can be confusing: + // https://github.com/facebookincubator/create-react-app/issues/343#issuecomment-237241875 + // You may want 'cheap-module-source-map' instead if you prefer source maps. devtool: 'eval', + // These are the "entry points" to our application. + // This means they will be the "root" imports that are included in JS bundle. + // The first two entry points enable "hot" CSS and auto-refreshes for JS. entry: [ + // Include WebpackDevServer client. It connects to WebpackDevServer via + // sockets and waits for recompile notifications. When WebpackDevServer + // recompiles, it sends a message to the client by socket. If only CSS + // was changed, the app reload just the CSS. Otherwise, it will refresh. + // The "?/" bit at the end tells the client to look for the socket at + // the root path, i.e. /sockjs-node/. Otherwise visiting a client-side + // route like /todos/42 would make it wrongly request /todos/42/sockjs-node. + // The socket server is a part of WebpackDevServer which we are using. + // The /sockjs-node/ path I'm referring to is hardcoded in WebpackDevServer. require.resolve('webpack-dev-server/client') + '?/', + // Include Webpack hot module replacement runtime. Webpack is pretty + // low-level so we need to put all the pieces together. The runtime listens + // to the events received by the client above, and applies updates (such as + // new CSS) to the running application. require.resolve('webpack/hot/dev-server'), + // We ship a few polyfills by default. require.resolve('./polyfills'), + // Finally, this is your app's code: path.join(paths.appSrc, 'index') + // We include the app code last so that if there is a runtime error during + // initialization, it doesn't blow up the WebpackDevServer client, and + // changing JS code would still trigger a refresh. ], output: { // Next line is not used in dev but WebpackDevServer crashes without it: path: paths.appBuild, + // Add /* filename */ comments to generated require()s in the output. pathinfo: true, + // This does not produce a real file. It's just the virtual path that is + // served by WebpackDevServer in development. This is the JS bundle + // containing code from all our entry points, and the Webpack runtime. filename: 'static/js/bundle.js', + // In development, we always serve from the root. This makes config easier. publicPath: '/' }, resolve: { + // These are the reasonable defaults supported by the Node ecosystem. extensions: ['.js', '.json', ''], alias: { // This `alias` section can be safely removed after ejection. @@ -44,11 +78,16 @@ module.exports = { 'babel-runtime/regenerator': require.resolve('babel-runtime/regenerator') } }, + // Resolve loaders (webpack plugins for CSS, images, transpilation) from the + // directory of `react-scripts` itself rather than the project directory. + // You can remove this after ejecting. resolveLoader: { root: paths.ownNodeModules, moduleTemplates: ['*-loader'] }, module: { + // First, run the linter. + // It's important to do this before Babel processes the JS. preLoaders: [ { test: /\.js$/, @@ -57,22 +96,33 @@ module.exports = { } ], loaders: [ + // Process JS with Babel. { test: /\.js$/, include: paths.appSrc, loader: 'babel', query: require('./babel.dev') }, + // "postcss" loader applies autoprefixer to our CSS. + // "css" loader resolves paths in CSS and adds assets as dependencies. + // "style" loader turns CSS into JS modules that inject