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 AutoDllPlugin, cache-loader, and WebpackUglifyParallel #2786

Closed
wants to merge 4 commits into from
Closed
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
77 changes: 77 additions & 0 deletions packages/react-dev-utils/createHashFromPaths.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
'use strict';
const { execSync } = require('child_process');
const crypto = require('crypto');
const path = require('path');
const fs = require('fs');

const getSources = (paths = [], sourceMethod = function() {}, exclude = []) => {
const getSource = (somePath = '') => {
try {
if (fs.lstatSync(somePath).isDirectory()) {
if (
exclude.includes(somePath) ||
exclude.find(excluded => somePath.startsWith(excluded))
) {
return somePath;
}
const fileList = fs.readdirSync(somePath);
return getSources(
fileList.map(file => path.join(somePath, file)),
sourceMethod
);
} else {
return sourceMethod(somePath);
}
} catch (ignored) {
return somePath;
}
};
let result = '';
for (let i = paths.length - 1; i >= 0; i--) {
result += getSource(paths[i]);
}
return result;
};

const getSourceMethod = key => {
const cases = {
content: filePath => fs.readFileSync(filePath, 'utf-8'),
mtime: filePath => `${filePath}_${fs.statSync(filePath).mtime}`,
};

const found = cases[key];

if (found) {
return found;
}

console.error(
'Error from createHashFromPaths:\n\n',
'Source method is not recognized, possible values are:\n\n',
Object.keys(cases).map(e => '`' + e + '`').join(', '),
'\n'
);
process.exit(1);
};

const toRelative = filePath => path.relative(process.cwd(), filePath);

const createHashFromPaths = ({ paths, exclude, method = 'mtime' }) => {
try {
const fileList = paths.map(toRelative).join(' ');
const excludedPaths = exclude.map(toRelative).join(' ');
const command = `tar --exclude ${excludedPaths} -cf - ${fileList} | md5`;
return String(execSync(command));
} catch (ignored) {
const hash = crypto.createHash('md5');
hash.update(paths.join(''));
if (Array.isArray(paths)) {
const sourceMethod = getSourceMethod(method);
const sources = getSources(paths, sourceMethod, exclude);
hash.update(sources);
}
return hash.digest('hex');
}
};

module.exports = createHashFromPaths;
1 change: 1 addition & 0 deletions packages/react-dev-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"checkRequiredFiles.js",
"clearConsole.js",
"crashOverlay.js",
"createHashFromPaths.js",
"crossSpawn.js",
"eslintFormatter.js",
"FileSizeReporter.js",
Expand Down
16 changes: 16 additions & 0 deletions packages/react-scripts/config/createCacheLoader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict';

const path = require('path');
module.exports = function(hash, paths) {
return {
loader: require.resolve('cache-loader'),
options: {
cacheDirectory: path.join(
paths.appNodeModules,
'.cache',
'cache-loader',
hash
),
},
};
};
47 changes: 36 additions & 11 deletions packages/react-scripts/config/webpack.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ 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 createHashFromPaths = require('react-dev-utils/createHashFromPaths');
const createCacheLoader = require('./createCacheLoader');
const getClientEnvironment = require('./env');
const paths = require('./paths');
const AutoDllWebpackPlugin = require('autodll-webpack-plugin');

// Webpack uses `publicPath` to determine where the app is being served from.
// In development, we always serve from the root. This makes config easier.
Expand All @@ -32,6 +35,13 @@ const publicUrl = '';
// Get environment variables to inject into our app.
const env = getClientEnvironment(publicUrl);

const hash = createHashFromPaths({
paths: [paths.appNodeModules],
exclude: [path.join(paths.appNodeModules, '.cache')],
});

const cacheLoader = createCacheLoader(hash, 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.
Expand Down Expand Up @@ -170,17 +180,22 @@ module.exports = {
{
test: /\.(js|jsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
// @remove-on-eject-begin
babelrc: false,
presets: [require.resolve('babel-preset-react-app')],
// @remove-on-eject-end
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
},
use: [
cacheLoader,
{
loader: require.resolve('babel-loader'),
options: {
// @remove-on-eject-begin
babelrc: false,
presets: [require.resolve('babel-preset-react-app')],
// @remove-on-eject-end
// This is a feature of `babel-loader` for webpack (not Babel itself).
// It enables caching results in ./node_modules/.cache/babel-loader/
// directory for faster rebuilds.
cacheDirectory: true,
},
},
],
},
// "postcss" loader applies autoprefixer to our CSS.
// "css" loader resolves paths in CSS and adds assets as dependencies.
Expand All @@ -190,6 +205,7 @@ module.exports = {
{
test: /\.css$/,
use: [
cacheLoader,
require.resolve('style-loader'),
{
loader: require.resolve('css-loader'),
Expand Down Expand Up @@ -252,6 +268,15 @@ module.exports = {
inject: true,
template: paths.appHtml,
}),
new AutoDllWebpackPlugin({
env: process.env.NODE_ENV,
additionalHash: hash,
inject: true,
filename: '[name].[hash].js',
entry: {
vendor: ['react', 'react-dom'],
},
}),
// Add module names to factory functions so they appear in browser profiler.
new webpack.NamedModulesPlugin(),
// Makes some environment variables available to the JS code, for example:
Expand Down
43 changes: 34 additions & 9 deletions packages/react-scripts/config/webpack.config.prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,13 @@ 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 createHashFromPaths = require('react-dev-utils/createHashFromPaths');
const AutoDllWebpackPlugin = require('autodll-webpack-plugin');
const WebpackUglifyParallel = require('webpack-uglify-parallel');
const createCacheLoader = require('./createCacheLoader');
const paths = require('./paths');
const getClientEnvironment = require('./env');
const os = require('os');

// Webpack uses `publicPath` to determine where the app is being served from.
// It requires a trailing slash, or the file assets will get an incorrect path.
Expand Down Expand Up @@ -54,6 +59,13 @@ const extractTextPluginOptions = shouldUseRelativeAssetPaths
{ publicPath: Array(cssFilename.split('/').length).join('../') }
: {};

const hash = createHashFromPaths({
paths: [paths.appNodeModules],
exclude: [path.join(paths.appNodeModules, '.cache')],
});

const cacheLoader = createCacheLoader(hash, paths);

// This is the production configuration.
// It compiles slowly and is focused on producing a fast and minimal bundle.
// The development configuration is different and lives in a separate file.
Expand Down Expand Up @@ -171,14 +183,19 @@ module.exports = {
{
test: /\.(js|jsx)$/,
include: paths.appSrc,
loader: require.resolve('babel-loader'),
options: {
// @remove-on-eject-begin
babelrc: false,
presets: [require.resolve('babel-preset-react-app')],
// @remove-on-eject-end
compact: true,
},
use: [
cacheLoader,
{
loader: require.resolve('babel-loader'),
options: {
// @remove-on-eject-begin
babelrc: false,
presets: [require.resolve('babel-preset-react-app')],
// @remove-on-eject-end
compact: true,
},
},
],
},
// The notation here is somewhat confusing.
// "postcss" loader applies autoprefixer to our CSS.
Expand All @@ -199,6 +216,7 @@ module.exports = {
{
fallback: require.resolve('style-loader'),
use: [
cacheLoader,
{
loader: require.resolve('css-loader'),
options: {
Expand Down Expand Up @@ -279,13 +297,20 @@ module.exports = {
minifyURLs: true,
},
}),
new AutoDllWebpackPlugin({
env: process.env.NODE_ENV,
additionalHash: hash,
inject: true,
filename: '[name].[hash].js',
}),
// Makes some environment variables available to the JS code, for example:
// if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
// It is absolutely essential that NODE_ENV was set to production here.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
// Minify the code.
new webpack.optimize.UglifyJsPlugin({
new WebpackUglifyParallel({
workers: os.cpus().length,
compress: {
warnings: false,
// Disabled because of an issue with Uglify breaking seemingly valid code:
Expand Down
3 changes: 3 additions & 0 deletions packages/react-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@
"react-scripts": "./bin/react-scripts.js"
},
"dependencies": {
"autodll-webpack-plugin": "^0.2.1",
"autoprefixer": "7.1.1",
"babel-core": "6.25.0",
"babel-eslint": "7.2.3",
"babel-jest": "20.0.3",
"babel-loader": "7.0.0",
"babel-preset-react-app": "^3.0.1",
"babel-runtime": "6.23.0",
"cache-loader": "^1.0.3",
"case-sensitive-paths-webpack-plugin": "2.1.1",
"chalk": "1.1.3",
"css-loader": "0.28.4",
Expand Down Expand Up @@ -56,6 +58,7 @@
"webpack": "2.6.1",
"webpack-dev-server": "2.5.0",
"webpack-manifest-plugin": "1.1.0",
"webpack-uglify-parallel": "^0.1.3",
"whatwg-fetch": "2.0.3"
},
"devDependencies": {
Expand Down