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

Long term caching and split app into main.js and vendors.js #3145

Closed
wants to merge 30 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
5932a67
Split vendor and app into separate files
Sep 17, 2017
cbfd216
Entries in array and not in object
Sep 17, 2017
cfb54cf
remove trailing commas
Sep 17, 2017
85b0ae0
Add appVendors in every paths object in paths.js
Sep 17, 2017
7690dfa
Add vendors.js in the kitchensink folder
Sep 17, 2017
51206b7
Refactor how vendors are imported in webpack
Sep 18, 2017
005369c
Add CommonsChunkPlugin for runtime
Sep 18, 2017
ff447f8
Rename app to main
Sep 18, 2017
1517fbe
Name every chunks and if a chunk doesn't have any name, use the name …
Sep 18, 2017
72f35db
Handle deprecated chunk.modules
Sep 18, 2017
7d7ff19
Use only chunk.mapModules from webpack 3 and use path.basename instea…
Sep 18, 2017
f8e4682
Add Name All Modules Plugin
Sep 19, 2017
9e648d4
Only add the vendors if the file "src/vendors.js" exists
Sep 19, 2017
00f160c
Ensure that major entries are loaded before the rest
Sep 19, 2017
186f289
evert "Ensure that major entries are loaded before the rest"
Sep 21, 2017
75cf8c9
Pin the version of name-all-modules-plugin
Sep 21, 2017
3ef3617
Add comments in vendors.js
Sep 21, 2017
f4a889d
Change comments in vendors.js
Sep 21, 2017
88dd88e
Add newline at the end of package.json
Sep 21, 2017
49ffbcb
Desactivate webpack.optimize.CommonsChunkPlugin(vendors) in vendors.j…
Sep 21, 2017
6778d82
Change the way the optimize pluggin for the vendors is set or removed
Sep 22, 2017
f37f51d
Change the way unamed chunks are named
Sep 23, 2017
6277cd3
Change commentaries
Sep 23, 2017
779faee
Check if every vendors are defined in the package.json
Sep 29, 2017
910a5f7
Add devDeps and peerDeps
Sep 29, 2017
0d3b82c
Update comments
Sep 29, 2017
50782f3
Export code in a detectMissingVendors function of react-dev-utils
Jan 18, 2018
7629f7a
Use json files instead of js files for vendors
Jan 18, 2018
b314c99
merge with :next
Jan 18, 2018
0fdaac1
Add detectMissingVendors in files of react-dev-utils/package.json
Jan 18, 2018
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
34 changes: 34 additions & 0 deletions packages/react-dev-utils/detectMissingVendors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// @remove-on-eject-begin
/**
* Copyright (c) 2015-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// @remove-on-eject-end
'use strict';

const fs = require('fs');
const path = require('path');
const chalk = require('chalk');

function detectMissingVendors(pathToPackageJson, pathToVendors) {
const packageJson = require(pathToPackageJson);
const dependencies = Object.keys(packageJson.dependencies || {})
.concat(Object.keys(packageJson.devDependencies || {}))
.concat(Object.keys(packageJson.peerDependencies || {}));
const vendors = fs.existsSync(pathToVendors) ? require(pathToVendors) : [];
const missingVendors = vendors.filter(
vendor => dependencies.indexOf(vendor) === -1
);
if (missingVendors.length > 0) {
throw new Error(
'Error: Unknown vendors: ' +
chalk.yellow(missingVendors) +
" should be listed in the project's dependencies.\n" +
`(Vendors defined in '${path.resolve(pathToVendors)}')`
);
}
}

module.exports = detectMissingVendors;
1 change: 1 addition & 0 deletions packages/react-dev-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"clearConsole.js",
"crashOverlay.js",
"crossSpawn.js",
"detectMissingVendors.js",
"eslintFormatter.js",
"errorOverlayMiddleware.js",
"FileSizeReporter.js",
Expand Down
3 changes: 3 additions & 0 deletions packages/react-scripts/config/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ module.exports = {
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveApp('src/index.js'),
appVendors: resolveApp('src/vendors.json'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
yarnLockFile: resolveApp('yarn.lock'),
Expand All @@ -74,6 +75,7 @@ module.exports = {
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
appIndexJs: resolveApp('src/index.js'),
appVendors: resolveApp('src/vendors.json'),
appPackageJson: resolveApp('package.json'),
appSrc: resolveApp('src'),
yarnLockFile: resolveApp('yarn.lock'),
Expand Down Expand Up @@ -104,6 +106,7 @@ if (
appPublic: resolveOwn('template/public'),
appHtml: resolveOwn('template/public/index.html'),
appIndexJs: resolveOwn('template/src/index.js'),
appVendors: resolveOwn('template/src/vendors.json'),
appPackageJson: resolveOwn('package.json'),
appSrc: resolveOwn('template/src'),
yarnLockFile: resolveOwn('template/yarn.lock'),
Expand Down
65 changes: 62 additions & 3 deletions packages/react-scripts/config/webpack.config.prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ 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 NameAllModulesPlugin = require('name-all-modules-plugin');

const fs = require('fs');
const paths = require('./paths');
const getClientEnvironment = require('./env');

Expand Down Expand Up @@ -79,8 +82,20 @@ module.exports = {
// We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment.
devtool: shouldUseSourceMap ? 'source-map' : false,
// In production, we only want to load the polyfills and the app code.
entry: [require.resolve('./polyfills'), paths.appIndexJs],
// In production, we only want to load the polyfills, the app code and the vendors.
entry: Object.assign(
{
// Load the app and all its dependencies
main: paths.appIndexJs,
// Add the polyfills
polyfills: require.resolve('./polyfills'),
},
// Only add the vendors if the file "src/vendors.js" exists
fs.existsSync(paths.appVendors)
? // List of all the node modules that should be excluded from the app
{ vendors: require(paths.appVendors) }
: {}
),
output: {
// The build folder.
path: paths.appBuild,
Expand Down Expand Up @@ -455,7 +470,51 @@ module.exports = {
// 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$/),
],

// For some reason, Webpack adds some ids of all the modules that exist to our vendor chunk
// Instead of using numerical ids it uses a unique path to map our request to a module.
// Thanks to this change the vendor hash will now always stay the same
new webpack.NamedModulesPlugin(),

// Ensure that every chunks have an actual name and not an id
// If the chunk has a name, this name is used
// otherwise the name of the file is used
new webpack.NamedChunksPlugin(chunk => {
if (chunk.name) {
return chunk.name;
}
const chunkNames = chunk.mapModules(m => m);
// Sort the chunks by their depths
// The chunk with the lower depth is the imported one
// The others are its dependencies
chunkNames.sort((chunkA, chunkB) => chunkA.depth - chunkB.depth);
// Get the absolute path of the file
const fileName = chunkNames[0].resource;
// Return the name of the file without the extension
return path.basename(fileName, path.extname(fileName));
}),

// Avoid having the vendors in the rest of the app
// Only execute if the vendors file exists

fs.existsSync(paths.appVendors)
? new webpack.optimize.CommonsChunkPlugin({
name: 'vendors',
minChunks: Infinity,
})
: null,
// The runtime is the part of Webpack that resolves modules
// at runtime and handles async loading and more
new webpack.optimize.CommonsChunkPlugin({
name: 'runtime',
}),

// https://medium.com/webpack/predictable-long-term-caching-with-webpack-d3eee1d3fa31
// Name the modules that were not named by the previous plugins
new NameAllModulesPlugin(),
]
// Remove null elements
.filter(Boolean),
// 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.
node: {
Expand Down
6 changes: 3 additions & 3 deletions packages/react-scripts/fixtures/kitchensink/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ class App extends Component {
);
break;
case 'css-modules-inclusion':
import(
'./features/webpack/CssModulesInclusion'
).then(f => this.setFeature(f.default));
import('./features/webpack/CssModulesInclusion').then(f =>
this.setFeature(f.default)
);
break;
case 'custom-interpolation':
import('./features/syntax/CustomInterpolation').then(f =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["react", "react-dom"]
16 changes: 3 additions & 13 deletions packages/react-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,7 @@
"bugs": {
"url": "https://github.com/facebookincubator/create-react-app/issues"
},
"files": [
"bin",
"config",
"scripts",
"template",
"utils"
],
"files": ["bin", "config", "scripts", "template", "utils"],
"bin": {
"react-scripts": "./bin/react-scripts.js"
},
Expand Down Expand Up @@ -47,6 +41,7 @@
"html-webpack-plugin": "2.30.1",
"identity-obj-proxy": "3.0.0",
"jest": "22.1.2",
"name-all-modules-plugin": "1.0.1",
"object-assign": "4.1.1",
"postcss-flexbugs-fixes": "3.2.0",
"postcss-loader": "2.0.10",
Expand Down Expand Up @@ -77,11 +72,6 @@
"last 2 firefox versions",
"last 2 edge versions"
],
"production": [
">1%",
"last 4 versions",
"Firefox ESR",
"not ie < 11"
]
"production": [">1%", "last 4 versions", "Firefox ESR", "not ie < 11"]
}
}
9 changes: 9 additions & 0 deletions packages/react-scripts/scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const webpack = require('webpack');
const config = require('../config/webpack.config.prod');
const paths = require('../config/paths');
const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
const detectMissingVendors = require('react-dev-utils/detectMissingVendors');
const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
Expand Down Expand Up @@ -71,6 +72,14 @@ checkBrowsers(paths.appPath)
fs.emptyDirSync(paths.appBuild);
// Merge with the public folder
copyPublicFolder();

// Check if every vendors are defined in the package.json
// @remove-on-eject-begin
// The devDepencencies shouldn't be available for vendors
// But otherwise the tests fail.
// @remove-on-eject-end
detectMissingVendors(paths.appPackageJson, paths.appVendors);

// Start the webpack build
return build(previousFileSizes);
})
Expand Down
1 change: 1 addition & 0 deletions packages/react-scripts/template/src/vendors.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
["react", "react-dom"]