Skip to content

Multicompilation of indexXX.js files to indexXX.html outputs #2520

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

Closed
wants to merge 1 commit 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
31 changes: 25 additions & 6 deletions packages/react-scripts/config/webpack.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ const env = getClientEnvironment(publicUrl);
// 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 = {
const configure = indexName => ({
// Name in compilation output log when multiple entry files are used.
name: `${indexName}.html`,
// You may want 'eval' instead if you prefer to see the compiled output in DevTools.
// See the discussion in https://github.com/facebookincubator/create-react-app/issues/343.
devtool: 'cheap-module-source-map',
Expand All @@ -58,8 +60,8 @@ module.exports = {
require.resolve('./polyfills'),
// Errors should be considered fatal in development
require.resolve('react-error-overlay'),
// Finally, this is your app's code:
paths.appIndexJs,
// Finally, this is your app's code, depending on entry index file:
paths.appIndexJs.replace('index', indexName),
// 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.
Expand All @@ -72,9 +74,13 @@ module.exports = {
// 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',
filename: indexName === 'index'
? 'static/js/bundle.js'
: `static/js/${indexName}-bundle.js`,
// There are also additional JS chunk files if you use code splitting.
chunkFilename: 'static/js/[name].chunk.js',
chunkFilename: indexName === 'index'
? 'static/js/[name].chunk.js'
: `static/js/${indexName}-[name].chunk.js`,
// This is the URL that app is served from. We use "/" in development.
publicPath: publicPath,
// Point sourcemap entries to original disk location
Expand Down Expand Up @@ -246,6 +252,7 @@ module.exports = {
new InterpolateHtmlPlugin(env.raw),
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
filename: `${indexName}.html`,
inject: true,
template: paths.appHtml,
}),
Expand Down Expand Up @@ -285,4 +292,16 @@ module.exports = {
performance: {
hints: false,
},
};
});

// Read potential indexXX.js candidates from src/ at this point.
const fs = require('fs');
const indexFiles = fs.readdirSync(paths.appSrc).filter(file => {
// Takes all indexXX.js files. Does not check validity at this point, could be a directory and crash.
return path.basename(file).startsWith('index') &&
path.extname(file) === '.js';
});

// Returns an array of configurations to trigger webpack multicompilation
module.exports = indexFiles.map(indexFile =>
configure(path.parse(indexFile).name));
34 changes: 28 additions & 6 deletions packages/react-scripts/config/webpack.config.prod.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,19 @@ const extractTextPluginOptions = shouldUseRelativeAssetPaths
// 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.
module.exports = {
const configure = indexName => ({
// Name in compilation output log when multiple entry files are used.
name: `${indexName}.html`,
// Don't attempt to continue if there are any errors.
bail: true,
// We generate sourcemaps in production. This is slow but gives good results.
// You can exclude the *.map files from the build during deployment.
devtool: 'source-map',
// In production, we only want to load the polyfills and the app code.
entry: [require.resolve('./polyfills'), paths.appIndexJs],
entry: [
require.resolve('./polyfills'),
paths.appIndexJs.replace('index', indexName),
],
output: {
// The build folder.
path: paths.appBuild,
Expand Down Expand Up @@ -258,6 +263,7 @@ module.exports = {
new InterpolateHtmlPlugin(env.raw),
// Generates an `index.html` file with the <script> injected.
new HtmlWebpackPlugin({
filename: `${indexName}.html`,
inject: true,
template: paths.appHtml,
minify: {
Expand Down Expand Up @@ -301,7 +307,9 @@ module.exports = {
// to their corresponding output file so that tools can pick it up without
// having to parse `index.html`.
new ManifestPlugin({
fileName: 'asset-manifest.json',
fileName: indexName === 'index'
? 'asset-manifest.json'
: `${indexName}-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.
Expand All @@ -311,7 +319,9 @@ module.exports = {
// 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',
fileName: indexName === 'index'
? 'service-worker.js'
: `${indexName}-service-worker.js`,
logger(message) {
if (message.indexOf('Total precache size is') === 0) {
// This message occurs for every build and is a bit too noisy.
Expand All @@ -321,7 +331,7 @@ module.exports = {
},
minify: true,
// For unknown URLs, fallback to the index page
navigateFallback: publicUrl + '/index.html',
navigateFallback: `${publicUrl}/${indexName}.html`,
// Ignores URLs starting from /__ (useful for Firebase):
// https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
navigateFallbackWhitelist: [/^(?!\/__).*/],
Expand All @@ -345,4 +355,16 @@ module.exports = {
net: 'empty',
tls: 'empty',
},
};
});

// Read potential indexXX.js candidates from src/ at this point.
const fs = require('fs');
const indexFiles = fs.readdirSync(paths.appSrc).filter(file => {
// Takes all indexXX.js files. Does not check validity at this point, could be a directory and crash.
return path.basename(file).startsWith('index') &&
path.extname(file) === '.js';
});

// Returns an array of configurations to trigger webpack multicompilation
module.exports = indexFiles.map(indexFile =>
configure(path.parse(indexFile).name));
2 changes: 1 addition & 1 deletion packages/react-scripts/config/webpackDevServer.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ module.exports = function(proxy, allowedHost) {
hot: true,
// It is important to tell WebpackDevServer to use the same "root" path
// as we specified in the config. In development, we always serve from /.
publicPath: config.output.publicPath,
publicPath: (config[0] || config).output.publicPath,
// WebpackDevServer is noisy by default so we emit custom message instead
// by listening to the compiler events with `compiler.plugin` calls above.
quiet: true,
Expand Down
8 changes: 6 additions & 2 deletions packages/react-scripts/scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,16 @@ measureFileSizesBeforeBuild(paths.appBuild)
}

console.log('File sizes after gzip:\n');
printFileSizesAfterBuild(stats, previousFileSizes, paths.appBuild);
// Webpack multicompilation produces an array of stats
(stats.stats || [stats]).forEach(stats => {
console.log(` ${stats.compilation.name}:`);
printFileSizesAfterBuild(stats, previousFileSizes, paths.appBuild);
});
console.log();

const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrl;
const publicPath = config.output.publicPath;
const publicPath = (config[0] || config).output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
appPackage,
Expand Down