From 493e5a699631d5f4daa4aaa70a82dd00a1cae9b4 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 16 May 2017 23:35:14 +0100 Subject: [PATCH 01/12] Tweak lint rules (#2186) --- packages/eslint-config-react-app/index.js | 2 ++ packages/react-dev-utils/eslintFormatter.js | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/eslint-config-react-app/index.js b/packages/eslint-config-react-app/index.js index 9d876042c85..42aa9db2cb0 100644 --- a/packages/eslint-config-react-app/index.js +++ b/packages/eslint-config-react-app/index.js @@ -235,6 +235,8 @@ module.exports = { ], // https://github.com/benmosher/eslint-plugin-import/tree/master/docs/rules + 'import/first': 'error', + 'import/no-amd': 'error', 'import/no-webpack-loader-syntax': 'error', // https://github.com/yannickcr/eslint-plugin-react/tree/master/docs/rules diff --git a/packages/react-dev-utils/eslintFormatter.js b/packages/react-dev-utils/eslintFormatter.js index a051b9c5b0e..674f2f06dec 100644 --- a/packages/react-dev-utils/eslintFormatter.js +++ b/packages/react-dev-utils/eslintFormatter.js @@ -12,6 +12,7 @@ function isError(message) { function formatter(results) { let output = '\n'; + let hasErrors = false; results.forEach(result => { let messages = result.messages; @@ -19,7 +20,6 @@ function formatter(results) { return; } - let hasErrors = false; messages = messages.map(message => { let messageType; if (isError(message)) { @@ -61,6 +61,19 @@ function formatter(results) { output += `${outputTable}\n\n`; }); + if (hasErrors) { + // Unlike with warnings, we have to do it here. + // We have similar code in react-scripts for warnings, + // but warnings can appear in multiple files so we only + // print it once at the end. For errors, however, we print + // it here because we always show at most one error, and + // we can only be sure it's an ESLint error before exiting + // this function. + output += 'Search for the ' + + chalk.underline(chalk.red('rule keywords')) + + ' to learn more about each error.'; + } + return output; } From 65ff5481d9cfe55b042c4b645403cb2d810766a2 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 16 May 2017 23:59:48 +0100 Subject: [PATCH 02/12] Ignore Moment.js locales by default (#2187) * Ignore Moment.js locales by default * Update README.md --- .../config/webpack.config.dev.js | 6 +++++ .../config/webpack.config.prod.js | 6 +++++ packages/react-scripts/template/README.md | 27 +++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 98e57ea9e5b..f90ca6bd09e 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -252,6 +252,12 @@ module.exports = { // makes the discovery automatic so you don't have to restart. // See https://github.com/facebookincubator/create-react-app/issues/186 new WatchMissingNodeModulesPlugin(paths.appNodeModules), + // Moment.js is an extremely popular library that bundles large locale files + // by default due to how Webpack interprets its code. This is a practical + // solution that requires the user to opt into importing specific locales. + // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack + // You can remove this if you don't use Moment.js: + new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), ], // Some libraries import Node modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 7615894a755..182105c756a 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -286,6 +286,12 @@ module.exports = { new ManifestPlugin({ fileName: 'asset-manifest.json', }), + // Moment.js is an extremely popular library that bundles large locale files + // by default due to how Webpack interprets its code. This is a practical + // solution that requires the user to opt into importing specific locales. + // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack + // You can remove this if you don't use Moment.js: + new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/), ], // Some libraries import Node modules but don't use them in the browser. // Tell Webpack to provide empty mocks for them so importing them works. diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md index c178e573816..b25f723c97b 100644 --- a/packages/react-scripts/template/README.md +++ b/packages/react-scripts/template/README.md @@ -81,6 +81,7 @@ You can find the most recent version of this guide [here](https://github.com/fac - [`npm test` hangs on macOS Sierra](#npm-test-hangs-on-macos-sierra) - [`npm run build` silently fails](#npm-run-build-silently-fails) - [`npm run build` fails on Heroku](#npm-run-build-fails-on-heroku) + - [Moment.js locales are missing](#momentjs-locales-are-missing) - [Something Missing?](#something-missing) ## Updating to New Releases @@ -1616,6 +1617,32 @@ It is reported that `npm run build` can fail on machines with no swap space, whi This may be a problem with case sensitive filenames. Please refer to [this section](#resolving-heroku-deployment-errors). +### Moment.js locales are missing + +If you use a [Moment.js](https://momentjs.com/), you might notice that only the English locale is available by default. This is because the locale files are large, and you probably only need a subset of [all the locales provided by Moment.js](https://momentjs.com/#multiple-locale-support). + +To add a specific Moment.js locale to your bundle, you need to import it explicitly.
+For example: + +```js +import moment from 'moment'; +import 'moment/locale/fr'; +``` + +If import multiple locales this way, you can later switch between them by calling `moment.locale()` with the locale name: + +```js +import moment from 'moment'; +import 'moment/locale/fr'; +import 'moment/locale/es'; + +// ... + +moment.locale('fr'); +``` + +This will only work for locales that have been explicitly imported before. + ## Something Missing? If you have ideas for more “How To” recipes that should be on this page, [let us know](https://github.com/facebookincubator/create-react-app/issues) or [contribute some!](https://github.com/facebookincubator/create-react-app/edit/master/packages/react-scripts/template/README.md) From e1f22b592f73e7ceb41b5b08842c8c66f5664fd9 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Wed, 17 May 2017 11:47:39 +0100 Subject: [PATCH 03/12] Disable Yarn on CI temporarily --- .travis.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 99edd919888..2c60bc2674b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,5 +24,7 @@ matrix: include: - node_js: 0.10 env: TEST_SUITE=simple - - node_js: 6 - env: USE_YARN=yes TEST_SUITE=simple +# There's a weird Yarn/Lerna bug related to prerelease versions. +# TODO: reenable after we ship 1.0. +# - node_js: 6 +# env: USE_YARN=yes TEST_SUITE=simple From a61be9c619c46d07c5f0b191286a818cc3dd1090 Mon Sep 17 00:00:00 2001 From: Joe Haddad Date: Wed, 17 May 2017 06:19:52 -0500 Subject: [PATCH 04/12] Add module scope plugin (#2189) * Add module scope plugin * Oops * Add comments * Check windows seps too * More descriptive error * Document it --- packages/react-dev-utils/ModuleScopePlugin.js | 68 +++++++++++++++++++ packages/react-dev-utils/README.md | 20 ++++++ packages/react-dev-utils/package.json | 1 + .../config/webpack.config.dev.js | 9 +++ .../config/webpack.config.prod.js | 9 +++ 5 files changed, 107 insertions(+) create mode 100644 packages/react-dev-utils/ModuleScopePlugin.js diff --git a/packages/react-dev-utils/ModuleScopePlugin.js b/packages/react-dev-utils/ModuleScopePlugin.js new file mode 100644 index 00000000000..fd70a2f408a --- /dev/null +++ b/packages/react-dev-utils/ModuleScopePlugin.js @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +const chalk = require('chalk'); +const path = require('path'); + +class ModuleScopePlugin { + constructor(appSrc) { + this.appSrc = appSrc; + } + + apply(resolver) { + const { appSrc } = this; + resolver.plugin('file', (request, callback) => { + // Unknown issuer, probably webpack internals + if (!request.context.issuer) { + return callback(); + } + if ( + // If this resolves to a node_module, we don't care what happens next + request.descriptionFileRoot.indexOf('/node_modules/') !== -1 || + request.descriptionFileRoot.indexOf('\\node_modules\\') !== -1 || + // Make sure this request was manual + !request.__innerRequest_request + ) { + return callback(); + } + // Resolve the issuer from our appSrc and make sure it's one of our files + // Maybe an indexOf === 0 would be better? + const relative = path.relative(appSrc, request.context.issuer); + // If it's not in src/ or a subdirectory, not our request! + if (relative[0] === '.') { + return callback(); + } + // Find path from src to the requested file + const requestRelative = path.relative( + appSrc, + path.resolve( + path.dirname(request.context.issuer), + request.__innerRequest_request + ) + ); + // Error if in a parent directory of src/ + if (requestRelative[0] === '.') { + callback( + new Error( + `You attempted to import ${chalk.cyan(request.__innerRequest_request)} which falls outside of the project ${chalk.cyan('src/')} directory. ` + + `Relative imports outside of ${chalk.cyan('src/')} are not supported. ` + + `You can either move it inside ${chalk.cyan('src/')}, or add a symlink to it from project's ${chalk.cyan('node_modules/')}.` + ), + request + ); + } else { + callback(); + } + }); + } +} + +module.exports = ModuleScopePlugin; diff --git a/packages/react-dev-utils/README.md b/packages/react-dev-utils/README.md index ef720449356..4a7effe217a 100644 --- a/packages/react-dev-utils/README.md +++ b/packages/react-dev-utils/README.md @@ -56,6 +56,26 @@ module.exports = { } ``` + +#### `new ModuleScopePlugin(appSrc: string)` + +This Webpack plugin ensures that relative imports from app's source directory don't reach outside of it. + +```js +var path = require('path'); +var ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); + + +module.exports = { + // ... + plugins: [ + new ModuleScopePlugin(paths.appSrc), + // ... + ], + // ... +} +``` + #### `new WatchMissingNodeModulesPlugin(nodeModulesPath: string)` This Webpack plugin ensures `npm install ` forces a project rebuild.
diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index d52fe30fd86..0a22499ca01 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -21,6 +21,7 @@ "getProcessForPort.js", "InterpolateHtmlPlugin.js", "launchEditor.js", + "ModuleScopePlugin.js", "openBrowser.js", "openChrome.applescript", "prepareProxy.js", diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index f90ca6bd09e..3d198d28a34 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -18,6 +18,7 @@ const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin'); const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); const eslintFormatter = require('react-dev-utils/eslintFormatter'); +const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const getClientEnvironment = require('./env'); const paths = require('./paths'); @@ -106,6 +107,14 @@ module.exports = { // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ 'react-native': 'react-native-web', }, + plugins: [ + // Prevents users from importing files from outside of src/ (or node_modules/). + // This often causes confusion because we only process files within src/ with babel. + // To fix this, we prevent you from importing files out of src/ -- if you'd like to, + // please link the files into your node_modules/ and let module-resolution kick in. + // Make sure your source files are compiled, as they will not be processed in any way. + new ModuleScopePlugin(paths.appSrc), + ], }, module: { strictExportPresence: true, diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 182105c756a..9742e7ff50a 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -18,6 +18,7 @@ const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ManifestPlugin = require('webpack-manifest-plugin'); const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); const eslintFormatter = require('react-dev-utils/eslintFormatter'); +const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const paths = require('./paths'); const getClientEnvironment = require('./env'); @@ -103,6 +104,14 @@ module.exports = { // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/ 'react-native': 'react-native-web', }, + plugins: [ + // Prevents users from importing files from outside of src/ (or node_modules/). + // This often causes confusion because we only process files within src/ with babel. + // To fix this, we prevent you from importing files out of src/ -- if you'd like to, + // please link the files into your node_modules/ and let module-resolution kick in. + // Make sure your source files are compiled, as they will not be processed in any way. + new ModuleScopePlugin(paths.appSrc), + ], }, module: { strictExportPresence: true, From e7c113eb158a27284fa2fae67b9ab4c6d8ea5cce Mon Sep 17 00:00:00 2001 From: Kant Date: Wed, 17 May 2017 15:30:07 +0200 Subject: [PATCH 05/12] Webpack config typo (#2193) * Webpack config typo * Webpack config more typo --- packages/react-scripts/config/webpack.config.dev.js | 6 +++--- packages/react-scripts/config/webpack.config.prod.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/react-scripts/config/webpack.config.dev.js b/packages/react-scripts/config/webpack.config.dev.js index 3d198d28a34..540d54abf5c 100644 --- a/packages/react-scripts/config/webpack.config.dev.js +++ b/packages/react-scripts/config/webpack.config.dev.js @@ -146,10 +146,10 @@ module.exports = { include: paths.appSrc, }, // ** ADDING/UPDATING LOADERS ** - // The "url" loader handles all assets unless explicitly excluded. + // The "file" loader handles all assets unless explicitly excluded. // The `exclude` list *must* be updated with every change to loader extensions. // When adding a new loader, you must add its `test` - // as a new entry in the `exclude` list for "url" loader. + // as a new entry in the `exclude` list for "file" loader. // "file" loader makes sure those assets get served by WebpackDevServer. // When you `import` an asset, you get its (virtual) filename. @@ -233,7 +233,7 @@ module.exports = { ], }, // ** STOP ** Are you adding a new loader? - // Remember to add the new extension(s) to the "url" loader exclusion list. + // Remember to add the new extension(s) to the "file" loader exclusion list. ], }, plugins: [ diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 9742e7ff50a..7df2af07baf 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -145,10 +145,10 @@ module.exports = { include: paths.appSrc, }, // ** ADDING/UPDATING LOADERS ** - // The "url" loader handles all assets unless explicitly excluded. + // The "file" loader handles all assets unless explicitly excluded. // The `exclude` list *must* be updated with every change to loader extensions. // When adding a new loader, you must add its `test` - // as a new entry in the `exclude` list in the "url" loader. + // as a new entry in the `exclude` list in the "file" loader. // "file" loader makes sure those assets end up in the `build` folder. // When you `import` an asset, you get its filename. @@ -243,7 +243,7 @@ module.exports = { // Note: this won't work without `new ExtractTextPlugin()` in `plugins`. }, // ** STOP ** Are you adding a new loader? - // Remember to add the new extension(s) to the "url" loader exclusion list. + // Remember to add the new extension(s) to the "file" loader exclusion list. ], }, plugins: [ From c7c3a521e2255939786c27f58c019786697bd820 Mon Sep 17 00:00:00 2001 From: Jeffrey Posnick Date: Wed, 17 May 2017 11:28:27 -0700 Subject: [PATCH 06/12] PWA-ification, via SWPrecacheWebpackPlugin + manifest.json (#1728) * sw-precache-webpack-plugin, SW registration, manifest.json * Documentation + a few SW tweaks. * Added an unregister method, too, just in case. * More info for the READMEs. * Add minify to SWPrecacheWebpackPlugin config * Fix SWPrecacheWebpackPlugin typo * Fix file references in READMEmd * Add instructions for testing service-worker locally * Review feedback * Some additional PWA metadata * Use sw-precache-webpack-plugin v0.9.1 for node >=4.0.0 support * Review feedback. * Add manifest.json context in a comment. * Fix typo * Downgrade to sw-precache-webpack-plugin 0.9.1 * Hide changes in README until 1.0 * Hide changes in User Guide until 1.0 * Hide more docs --- README.md | 6 + .../config/webpack.config.prod.js | 14 +++ packages/react-scripts/package.json | 1 + packages/react-scripts/template/README.md | 114 ++++++++++++++++++ .../react-scripts/template/public/index.html | 8 +- .../template/public/manifest.json | 15 +++ packages/react-scripts/template/src/index.js | 3 + .../src/service-worker-registration.js | 38 ++++++ 8 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 packages/react-scripts/template/public/manifest.json create mode 100644 packages/react-scripts/template/src/service-worker-registration.js diff --git a/README.md b/README.md index 4a0eeab567a..e9658d8585c 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,9 @@ It correctly bundles React in production mode and optimizes the build for the be The build is minified and the filenames include the hashes.
Your app is ready to be deployed! + ## User Guide @@ -162,6 +165,9 @@ Please refer to the [User Guide](https://github.com/facebookincubator/create-rea * Import CSS and image files directly from JavaScript. * Autoprefixed CSS, so you don’t need `-webkit` or other prefixes. * A `build` script to bundle JS, CSS, and images for production, with sourcemaps. + **The feature set is intentionally limited**. It doesn’t support advanced features such as server rendering or CSS modules. The tool is also **non-configurable** because it is hard to provide a cohesive experience and easy updates across a set of tools when the user can tweak anything. diff --git a/packages/react-scripts/config/webpack.config.prod.js b/packages/react-scripts/config/webpack.config.prod.js index 7df2af07baf..34848a972a3 100644 --- a/packages/react-scripts/config/webpack.config.prod.js +++ b/packages/react-scripts/config/webpack.config.prod.js @@ -17,6 +17,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); const ExtractTextPlugin = require('extract-text-webpack-plugin'); const ManifestPlugin = require('webpack-manifest-plugin'); const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); +const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin'); const eslintFormatter = require('react-dev-utils/eslintFormatter'); const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const paths = require('./paths'); @@ -295,6 +296,19 @@ module.exports = { new ManifestPlugin({ fileName: 'asset-manifest.json', }), + // Generate a service worker script that will precache, and keep up to date, + // the HTML & assets that are part of the Webpack build. + new SWPrecacheWebpackPlugin({ + // By default, a cache-busting query parameter is appended to requests + // used to populate the caches, to ensure the responses are fresh. + // If a URL is already hashed by Webpack, then there is no concern + // about it being stale, and the cache-busting can be skipped. + dontCacheBustUrlsMatching: /\.\w{8}\./, + filename: 'service-worker.js', + minify: true, + navigateFallback: publicUrl + '/index.html', + staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/], + }), // Moment.js is an extremely popular library that bundles large locale files // by default due to how Webpack interprets its code. This is a practical // solution that requires the user to opt into importing specific locales. diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index eaab334d4c3..939e40d809f 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -56,6 +56,7 @@ "react-dev-utils": "^1.0.0", "react-error-overlay": "^1.0.0", "style-loader": "0.17.0", + "sw-precache-webpack-plugin": "0.9.1", "url-loader": "0.5.8", "webpack": "2.5.1", "webpack-dev-server": "2.4.5", diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md index b25f723c97b..50bbf7b694a 100644 --- a/packages/react-scripts/template/README.md +++ b/packages/react-scripts/template/README.md @@ -61,6 +61,10 @@ You can find the most recent version of this guide [here](https://github.com/fac - [Editor Integration](#editor-integration) - [Developing Components in Isolation](#developing-components-in-isolation) - [Making a Progressive Web App](#making-a-progressive-web-app) + - [Deployment](#deployment) - [Static Server](#static-server) - [Other Solutions](#other-solutions) @@ -1216,6 +1220,103 @@ Learn more about React Storybook: You can turn your React app into a [Progressive Web App](https://developers.google.com/web/progressive-web-apps/) by following the steps in [this repository](https://github.com/jeffposnick/create-react-pwa). + + ## Deployment `npm run build` creates a `build` directory with a production build of your app. Set up your favourite HTTP server so that a visitor to your site is served `index.html`, and requests to static paths like `/static/js/main..js` are served with the contents of the `/static/js/main..js` file. @@ -1291,6 +1392,19 @@ It will get copied to the `build` folder when you run `npm run build`. Now requests to `/todos/42` will be handled correctly both in development and in production. + + ### Building for Relative Paths By default, Create React App produces a build assuming your app is hosted at the server root.
diff --git a/packages/react-scripts/template/public/index.html b/packages/react-scripts/template/public/index.html index 7f3e83f4e43..fc8689a2a29 100644 --- a/packages/react-scripts/template/public/index.html +++ b/packages/react-scripts/template/public/index.html @@ -3,9 +3,15 @@ + + +