From 2adb57624f0f473bc502b0eceb825873c4f0398b Mon Sep 17 00:00:00 2001 From: Jovi De Croock Date: Thu, 7 Mar 2019 15:34:47 +0100 Subject: [PATCH 01/14] basic double dist setup --- packages/babel-preset-react-app/create.js | 16 +++-- .../react-scripts/config/webpack.config.js | 26 +++++-- .../webpack/html-webpack-esmodules-plugin.js | 71 +++++++++++++++++++ packages/react-scripts/scripts/build.js | 15 +++- 4 files changed, 114 insertions(+), 14 deletions(-) create mode 100644 packages/react-scripts/config/webpack/html-webpack-esmodules-plugin.js diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js index a0423f2551..00bb7bd7bd 100644 --- a/packages/babel-preset-react-app/create.js +++ b/packages/babel-preset-react-app/create.js @@ -20,6 +20,9 @@ const validateBoolOption = (name, value, defaultValue) => { return value; }; +const legacyTargets = { ie: 9 }; +const modernTargets = { esmodules: true }; + module.exports = function(api, opts, env) { if (!opts) { opts = {}; @@ -46,6 +49,11 @@ module.exports = function(api, opts, env) { opts.absoluteRuntime, true ); + // TODO: this needs to change. + // At this point we only create one babel config for all builds. + // We should be able to provide a babelconfig for every build. + // Does this mean we should move this to webpack or specify some BABEL_ENV? + var isModern = validateBoolOption('modern', opts.modern, false); var absoluteRuntimePath = undefined; if (useAbsoluteRuntime) { @@ -81,15 +89,13 @@ module.exports = function(api, opts, env) { { // We want Create React App to be IE 9 compatible until React itself // no longer works with IE 9 - targets: { - ie: 9, - }, + targets: isModern ? modernTargets : legacyTargets, // Users cannot override this behavior because this Babel // configuration is highly tuned for ES5 support ignoreBrowserslistConfig: true, // If users import all core-js they're probably not concerned with // bundle size. We shouldn't rely on magic to try and shrink it. - useBuiltIns: false, + useBuiltIns: isModern ? 'entry' : false, // Do not transform modules to CJS modules: false, // Exclude transforms that make all code slower @@ -157,7 +163,7 @@ module.exports = function(api, opts, env) { { corejs: false, helpers: areHelpersEnabled, - regenerator: true, + regenerator: !isModern, // https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules // We should turn this on once the lowest version of Node LTS // supports ES Modules. diff --git a/packages/react-scripts/config/webpack.config.js b/packages/react-scripts/config/webpack.config.js index f6c0e7a237..0133985f1d 100644 --- a/packages/react-scripts/config/webpack.config.js +++ b/packages/react-scripts/config/webpack.config.js @@ -34,6 +34,7 @@ const typescriptFormatter = require('react-dev-utils/typescriptFormatter'); // @remove-on-eject-begin const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier'); // @remove-on-eject-end +const HtmlWebpackEsmodulesPlugin = require('./webpack/html-webpack-esmodules-plugin'); // Source maps are resource heavy and can cause out of memory issue for large source files. const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; @@ -52,7 +53,10 @@ const sassModuleRegex = /\.module\.(scss|sass)$/; // This is the production and development configuration. // It is focused on developer experience, fast rebuilds, and a minimal bundle. -module.exports = function(webpackEnv) { +module.exports = function( + webpackEnv, + { shouldBuildModernAndLegacy, isModernOutput } = {} +) { const isEnvDevelopment = webpackEnv === 'development'; const isEnvProduction = webpackEnv === 'production'; @@ -161,11 +165,15 @@ module.exports = function(webpackEnv) { // There will be one main bundle, and one file per asynchronous chunk. // In development, it does not produce real files. filename: isEnvProduction - ? 'static/js/[name].[contenthash:8].js' + ? `static/js/[name].[contenthash:8]${ + isModernOutput ? '.modern' : '' + }.js` : isEnvDevelopment && 'static/js/bundle.js', // There are also additional JS chunk files if you use code splitting. chunkFilename: isEnvProduction - ? 'static/js/[name].[contenthash:8].chunk.js' + ? `static/js/[name].[contenthash:8]${ + isModernOutput ? '.modern' : '' + }.chunk.js` : isEnvDevelopment && 'static/js/[name].chunk.js', // We inferred the "public path" (such as / or /my-project) from homepage. // We use "/" in development. @@ -194,7 +202,7 @@ module.exports = function(webpackEnv) { ecma: 8, }, compress: { - ecma: 5, + ecma: isModernOutput ? 6 : 5, warnings: false, // Disabled because of an issue with Uglify breaking seemingly valid code: // https://github.com/facebook/create-react-app/issues/2376 @@ -211,7 +219,7 @@ module.exports = function(webpackEnv) { safari10: true, }, output: { - ecma: 5, + ecma: isModernOutput ? 6 : 5, comments: false, // Turned on because emoji and regex is not minified properly using default // https://github.com/facebook/create-react-app/issues/2488 @@ -353,7 +361,12 @@ module.exports = function(webpackEnv) { // @remove-on-eject-begin babelrc: false, configFile: false, - presets: [require.resolve('babel-preset-react-app')], + presets: [ + [ + require.resolve('babel-preset-react-app'), + { modern: isModernOutput }, + ], + ], // Make sure we have a unique cache identifier, erring on the // side of caution. // We remove this when the user ejects because the default @@ -542,6 +555,7 @@ module.exports = function(webpackEnv) { : undefined ) ), + shouldBuildModernAndLegacy && new HtmlWebpackEsmodulesPlugin(), // Inlines the webpack runtime script. This script is too small to warrant // a network request. isEnvProduction && diff --git a/packages/react-scripts/config/webpack/html-webpack-esmodules-plugin.js b/packages/react-scripts/config/webpack/html-webpack-esmodules-plugin.js new file mode 100644 index 0000000000..42473e1cf2 --- /dev/null +++ b/packages/react-scripts/config/webpack/html-webpack-esmodules-plugin.js @@ -0,0 +1,71 @@ +'use strict'; + +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const fs = require('fs-extra'); + +const ID = 'html-webpack-esmodules-plugin'; + +const safariFix = `(function(){var d=document;var c=d.createElement('script');if(!('noModule' in c)&&'onbeforeload' in c){var s=!1;d.addEventListener('beforeload',function(e){if(e.target===c){s=!0}else if(!e.target.hasAttribute('nomodule')||!s){return}e.preventDefault()},!0);c.type='module';c.src='.';d.head.appendChild(c);c.remove()}}())`; + +class HtmlWebpackEsmodulesPlugin { + constructor() {} + + apply(compiler) { + compiler.hooks.compilation.tap(ID, compilation => { + HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync( + ID, + ({ plugin, bodyTags: body }, cb) => { + const targetDir = compiler.options.output.path; + // get stats, write to disk + const htmlName = path.basename(plugin.options.filename); + // Watch out for output files in sub directories + const htmlPath = path.dirname(plugin.options.filename); + const tempFilename = path.join( + targetDir, + htmlPath, + `assets-${htmlName}.json` + ); + + if (!fs.existsSync(tempFilename)) { + fs.mkdirpSync(path.dirname(tempFilename)); + const newBody = body.filter( + a => a.tagName === 'script' && a.attributes + ); + newBody.forEach(a => (a.attributes.nomodule = '')); + fs.writeFileSync(tempFilename, JSON.stringify(newBody)); + return cb(); + } + + const legacyAssets = JSON.parse( + fs.readFileSync(tempFilename, 'utf-8') + ); + // TODO: to discuss, an improvement would be to + // Inject these into the head tag together with the + // Safari script. + body.forEach(tag => { + if (tag.tagName === 'script' && tag.attributes) { + tag.attributes.type = 'module'; + } + }); + + body.push({ + tagName: 'script', + closeTag: true, + innerHTML: safariFix, + }); + + body.push(...legacyAssets); + fs.removeSync(tempFilename); + cb(); + } + ); + + HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tap(ID, data => { + data.html = data.html.replace(/\snomodule="">/g, ' nomodule>'); + }); + }); + } +} + +module.exports = HtmlWebpackEsmodulesPlugin; diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js index 8cd1ad77ee..a795f3e881 100644 --- a/packages/react-scripts/scripts/build.js +++ b/packages/react-scripts/scripts/build.js @@ -63,9 +63,17 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { // Process CLI arguments const argv = process.argv.slice(2); const writeStatsJson = argv.indexOf('--stats') !== -1; +const buildModern = argv.indexOf('--legacy') === -1; // Generate configuration -const config = configFactory('production'); +const modernConfig = configFactory('production', { + shouldBuildModernAndLegacy: buildModern, + isModernOutput: true, +}); +const leagcyConfig = configFactory('production', { + shouldBuildModernAndLegacy: buildModern, + isModernOutput: false, +}); // We require that you explicitly set browsers and do not fall back to // browserslist defaults. @@ -116,7 +124,7 @@ checkBrowsers(paths.appPath, isInteractive) const appPackage = require(paths.appPackageJson); const publicUrl = paths.publicUrl; - const publicPath = config.output.publicPath; + const publicPath = leagcyConfig.output.publicPath; const buildFolder = path.relative(process.cwd(), paths.appBuild); printHostingInstructions( appPackage, @@ -142,8 +150,9 @@ checkBrowsers(paths.appPath, isInteractive) // Create the production build and print the deployment instructions. function build(previousFileSizes) { console.log('Creating an optimized production build...'); + const configs = [buildModern && modernConfig, leagcyConfig].filter(Boolean); - let compiler = webpack(config); + let compiler = webpack(configs); return new Promise((resolve, reject) => { compiler.run((err, stats) => { let messages; From dc22555dec1b0e14e9b56c25a84f88ef5592b911 Mon Sep 17 00:00:00 2001 From: Jovi De Croock Date: Fri, 8 Mar 2019 17:01:51 +0100 Subject: [PATCH 02/14] dev utils --- .../react-dev-utils/htmlEsmodulesPlugin.js | 71 +++++++++++++++++++ packages/react-dev-utils/package.json | 1 + .../react-scripts/config/webpack.config.js | 1 + 3 files changed, 73 insertions(+) create mode 100644 packages/react-dev-utils/htmlEsmodulesPlugin.js diff --git a/packages/react-dev-utils/htmlEsmodulesPlugin.js b/packages/react-dev-utils/htmlEsmodulesPlugin.js new file mode 100644 index 0000000000..42473e1cf2 --- /dev/null +++ b/packages/react-dev-utils/htmlEsmodulesPlugin.js @@ -0,0 +1,71 @@ +'use strict'; + +const path = require('path'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const fs = require('fs-extra'); + +const ID = 'html-webpack-esmodules-plugin'; + +const safariFix = `(function(){var d=document;var c=d.createElement('script');if(!('noModule' in c)&&'onbeforeload' in c){var s=!1;d.addEventListener('beforeload',function(e){if(e.target===c){s=!0}else if(!e.target.hasAttribute('nomodule')||!s){return}e.preventDefault()},!0);c.type='module';c.src='.';d.head.appendChild(c);c.remove()}}())`; + +class HtmlWebpackEsmodulesPlugin { + constructor() {} + + apply(compiler) { + compiler.hooks.compilation.tap(ID, compilation => { + HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync( + ID, + ({ plugin, bodyTags: body }, cb) => { + const targetDir = compiler.options.output.path; + // get stats, write to disk + const htmlName = path.basename(plugin.options.filename); + // Watch out for output files in sub directories + const htmlPath = path.dirname(plugin.options.filename); + const tempFilename = path.join( + targetDir, + htmlPath, + `assets-${htmlName}.json` + ); + + if (!fs.existsSync(tempFilename)) { + fs.mkdirpSync(path.dirname(tempFilename)); + const newBody = body.filter( + a => a.tagName === 'script' && a.attributes + ); + newBody.forEach(a => (a.attributes.nomodule = '')); + fs.writeFileSync(tempFilename, JSON.stringify(newBody)); + return cb(); + } + + const legacyAssets = JSON.parse( + fs.readFileSync(tempFilename, 'utf-8') + ); + // TODO: to discuss, an improvement would be to + // Inject these into the head tag together with the + // Safari script. + body.forEach(tag => { + if (tag.tagName === 'script' && tag.attributes) { + tag.attributes.type = 'module'; + } + }); + + body.push({ + tagName: 'script', + closeTag: true, + innerHTML: safariFix, + }); + + body.push(...legacyAssets); + fs.removeSync(tempFilename); + cb(); + } + ); + + HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tap(ID, data => { + data.html = data.html.replace(/\snomodule="">/g, ' nomodule>'); + }); + }); + } +} + +module.exports = HtmlWebpackEsmodulesPlugin; diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index ca7f00ab22..0db7cdc24b 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -26,6 +26,7 @@ "getCSSModuleLocalIdent.js", "getProcessForPort.js", "globby.js", + "htmlEsmodulesPlugin.js", "ignoredFiles.js", "immer.js", "InlineChunkHtmlPlugin.js", diff --git a/packages/react-scripts/config/webpack.config.js b/packages/react-scripts/config/webpack.config.js index 0133985f1d..148f373611 100644 --- a/packages/react-scripts/config/webpack.config.js +++ b/packages/react-scripts/config/webpack.config.js @@ -31,6 +31,7 @@ const getClientEnvironment = require('./env'); const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin'); const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin'); const typescriptFormatter = require('react-dev-utils/typescriptFormatter'); +// const HtmlWebpackEsmodulesPlugin = require('react-dev-utils/html-webpack-esmodules-plugin'); // @remove-on-eject-begin const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier'); // @remove-on-eject-end From 89aed63ee09ac31bfde70e898c700e16761d2659 Mon Sep 17 00:00:00 2001 From: Jovi De Croock Date: Fri, 15 Mar 2019 12:59:28 +0100 Subject: [PATCH 03/14] chore: updates --- packages/babel-preset-react-app/create.js | 13 +++- packages/create-react-app/createReactApp.js | 1 + .../react-scripts/config/webpack.config.js | 5 +- .../webpack/html-webpack-esmodules-plugin.js | 71 ------------------- packages/react-scripts/scripts/build.js | 2 +- 5 files changed, 15 insertions(+), 77 deletions(-) delete mode 100644 packages/react-scripts/config/webpack/html-webpack-esmodules-plugin.js diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js index 00bb7bd7bd..3a84b5f2f0 100644 --- a/packages/babel-preset-react-app/create.js +++ b/packages/babel-preset-react-app/create.js @@ -54,6 +54,11 @@ module.exports = function(api, opts, env) { // We should be able to provide a babelconfig for every build. // Does this mean we should move this to webpack or specify some BABEL_ENV? var isModern = validateBoolOption('modern', opts.modern, false); + var shouldBuildModern = validateBoolOption( + 'shouldBuildModernAndLegacy', + opts.shouldBuildModernAndLegacy, + true + ); var absoluteRuntimePath = undefined; if (useAbsoluteRuntime) { @@ -89,13 +94,17 @@ module.exports = function(api, opts, env) { { // We want Create React App to be IE 9 compatible until React itself // no longer works with IE 9 - targets: isModern ? modernTargets : legacyTargets, + targets: shouldBuildModern + ? isModern + ? modernTargets + : legacyTargets + : undefined, // Users cannot override this behavior because this Babel // configuration is highly tuned for ES5 support ignoreBrowserslistConfig: true, // If users import all core-js they're probably not concerned with // bundle size. We shouldn't rely on magic to try and shrink it. - useBuiltIns: isModern ? 'entry' : false, + useBuiltIns: !isModern || !shouldBuildModern ? false : 'entry', // Do not transform modules to CJS modules: false, // Exclude transforms that make all code slower diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index 51dac9010f..2343b8ec3c 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -77,6 +77,7 @@ const program = new commander.Command(packageJson.name) ) .option('--use-npm') .option('--use-pnp') + .option('--modern') .option('--typescript') .allowUnknownOption() .on('--help', () => { diff --git a/packages/react-scripts/config/webpack.config.js b/packages/react-scripts/config/webpack.config.js index 148f373611..67cbcf1de3 100644 --- a/packages/react-scripts/config/webpack.config.js +++ b/packages/react-scripts/config/webpack.config.js @@ -31,11 +31,10 @@ const getClientEnvironment = require('./env'); const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin'); const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin'); const typescriptFormatter = require('react-dev-utils/typescriptFormatter'); -// const HtmlWebpackEsmodulesPlugin = require('react-dev-utils/html-webpack-esmodules-plugin'); +const HtmlWebpackEsmodulesPlugin = require('react-dev-utils/html-webpack-esmodules-plugin'); // @remove-on-eject-begin const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier'); // @remove-on-eject-end -const HtmlWebpackEsmodulesPlugin = require('./webpack/html-webpack-esmodules-plugin'); // Source maps are resource heavy and can cause out of memory issue for large source files. const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'; @@ -365,7 +364,7 @@ module.exports = function( presets: [ [ require.resolve('babel-preset-react-app'), - { modern: isModernOutput }, + { modern: isModernOutput, shouldBuildModernAndLegacy }, ], ], // Make sure we have a unique cache identifier, erring on the diff --git a/packages/react-scripts/config/webpack/html-webpack-esmodules-plugin.js b/packages/react-scripts/config/webpack/html-webpack-esmodules-plugin.js deleted file mode 100644 index 42473e1cf2..0000000000 --- a/packages/react-scripts/config/webpack/html-webpack-esmodules-plugin.js +++ /dev/null @@ -1,71 +0,0 @@ -'use strict'; - -const path = require('path'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const fs = require('fs-extra'); - -const ID = 'html-webpack-esmodules-plugin'; - -const safariFix = `(function(){var d=document;var c=d.createElement('script');if(!('noModule' in c)&&'onbeforeload' in c){var s=!1;d.addEventListener('beforeload',function(e){if(e.target===c){s=!0}else if(!e.target.hasAttribute('nomodule')||!s){return}e.preventDefault()},!0);c.type='module';c.src='.';d.head.appendChild(c);c.remove()}}())`; - -class HtmlWebpackEsmodulesPlugin { - constructor() {} - - apply(compiler) { - compiler.hooks.compilation.tap(ID, compilation => { - HtmlWebpackPlugin.getHooks(compilation).alterAssetTagGroups.tapAsync( - ID, - ({ plugin, bodyTags: body }, cb) => { - const targetDir = compiler.options.output.path; - // get stats, write to disk - const htmlName = path.basename(plugin.options.filename); - // Watch out for output files in sub directories - const htmlPath = path.dirname(plugin.options.filename); - const tempFilename = path.join( - targetDir, - htmlPath, - `assets-${htmlName}.json` - ); - - if (!fs.existsSync(tempFilename)) { - fs.mkdirpSync(path.dirname(tempFilename)); - const newBody = body.filter( - a => a.tagName === 'script' && a.attributes - ); - newBody.forEach(a => (a.attributes.nomodule = '')); - fs.writeFileSync(tempFilename, JSON.stringify(newBody)); - return cb(); - } - - const legacyAssets = JSON.parse( - fs.readFileSync(tempFilename, 'utf-8') - ); - // TODO: to discuss, an improvement would be to - // Inject these into the head tag together with the - // Safari script. - body.forEach(tag => { - if (tag.tagName === 'script' && tag.attributes) { - tag.attributes.type = 'module'; - } - }); - - body.push({ - tagName: 'script', - closeTag: true, - innerHTML: safariFix, - }); - - body.push(...legacyAssets); - fs.removeSync(tempFilename); - cb(); - } - ); - - HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tap(ID, data => { - data.html = data.html.replace(/\snomodule="">/g, ' nomodule>'); - }); - }); - } -} - -module.exports = HtmlWebpackEsmodulesPlugin; diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js index a795f3e881..5652e4c6d1 100644 --- a/packages/react-scripts/scripts/build.js +++ b/packages/react-scripts/scripts/build.js @@ -63,7 +63,7 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) { // Process CLI arguments const argv = process.argv.slice(2); const writeStatsJson = argv.indexOf('--stats') !== -1; -const buildModern = argv.indexOf('--legacy') === -1; +const buildModern = argv.indexOf('--modern') !== -1; // Generate configuration const modernConfig = configFactory('production', { From 2fb8c1033365675bdff0de902d92c459a7090690 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Fri, 15 Mar 2019 17:11:38 +0100 Subject: [PATCH 04/14] update babel config --- packages/babel-preset-react-app/create.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js index ffc250b1cc..cb980fc28f 100644 --- a/packages/babel-preset-react-app/create.js +++ b/packages/babel-preset-react-app/create.js @@ -49,10 +49,7 @@ module.exports = function(api, opts, env) { opts.absoluteRuntime, true ); - // TODO: this needs to change. - // At this point we only create one babel config for all builds. - // We should be able to provide a babelconfig for every build. - // Does this mean we should move this to webpack or specify some BABEL_ENV? + var isModern = validateBoolOption('modern', opts.modern, false); var shouldBuildModern = validateBoolOption( 'shouldBuildModernAndLegacy', @@ -99,9 +96,6 @@ module.exports = function(api, opts, env) { ? modernTargets : legacyTargets : undefined, - // Users cannot override this behavior because this Babel - // configuration is highly tuned for ES5 support - ignoreBrowserslistConfig: true, // If users import all core-js they're probably not concerned with // bundle size. We shouldn't rely on magic to try and shrink it. useBuiltIns: 'entry', @@ -191,7 +185,7 @@ module.exports = function(api, opts, env) { { corejs: false, helpers: areHelpersEnabled, - regenerator: !isModern, + regenerator: true, // https://babeljs.io/docs/en/babel-plugin-transform-runtime#useesmodules // We should turn this on once the lowest version of Node LTS // supports ES Modules. From 3f7e53cb026f97a905c24825cfd0e6fd8c6cf620 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Fri, 15 Mar 2019 17:11:51 +0100 Subject: [PATCH 05/14] update naming --- .../{htmlEsmodulesPlugin.js => HtmlWebpackEsModulesPlugin.js} | 0 packages/react-dev-utils/package.json | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/react-dev-utils/{htmlEsmodulesPlugin.js => HtmlWebpackEsModulesPlugin.js} (100%) diff --git a/packages/react-dev-utils/htmlEsmodulesPlugin.js b/packages/react-dev-utils/HtmlWebpackEsModulesPlugin.js similarity index 100% rename from packages/react-dev-utils/htmlEsmodulesPlugin.js rename to packages/react-dev-utils/HtmlWebpackEsModulesPlugin.js diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 2468ba451e..b3e3bbc56c 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -26,7 +26,7 @@ "getCSSModuleLocalIdent.js", "getProcessForPort.js", "globby.js", - "htmlEsmodulesPlugin.js", + "HtmlWebpackEsModulesPlugin.js", "ignoredFiles.js", "immer.js", "InlineChunkHtmlPlugin.js", From c3d2a4632d984df58f0457e97ef36962b87aef84 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Fri, 15 Mar 2019 17:12:03 +0100 Subject: [PATCH 06/14] update webpack config --- packages/react-scripts/config/webpack.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/react-scripts/config/webpack.config.js b/packages/react-scripts/config/webpack.config.js index 8c19640bba..4190f420cb 100644 --- a/packages/react-scripts/config/webpack.config.js +++ b/packages/react-scripts/config/webpack.config.js @@ -31,7 +31,7 @@ const getClientEnvironment = require('./env'); const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin'); const ForkTsCheckerWebpackPlugin = require('react-dev-utils/ForkTsCheckerWebpackPlugin'); const typescriptFormatter = require('react-dev-utils/typescriptFormatter'); -const HtmlWebpackEsmodulesPlugin = require('react-dev-utils/html-webpack-esmodules-plugin'); +const HtmlWebpackEsModulesPlugin = require('react-dev-utils/HtmlWebpackEsModulesPlugin'); // @remove-on-eject-begin const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier'); // @remove-on-eject-end @@ -555,7 +555,7 @@ module.exports = function( : undefined ) ), - shouldBuildModernAndLegacy && new HtmlWebpackEsmodulesPlugin(), + shouldBuildModernAndLegacy && new HtmlWebpackEsModulesPlugin(), // Inlines the webpack runtime script. This script is too small to warrant // a network request. isEnvProduction && From 11d57582e8b95cd610698e60a9567a4dacd0d718 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Fri, 15 Mar 2019 17:26:05 +0100 Subject: [PATCH 07/14] apply pr remarks --- packages/babel-preset-react-app/create.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js index cb980fc28f..d5a9d5ac30 100644 --- a/packages/babel-preset-react-app/create.js +++ b/packages/babel-preset-react-app/create.js @@ -89,15 +89,13 @@ module.exports = function(api, opts, env) { // Latest stable ECMAScript features require('@babel/preset-env').default, { - // We want Create React App to be IE 9 compatible until React itself - // no longer works with IE 9 + // When building normal we take the build we're in being modern or legacy + // If not we respect the users browserslist. targets: shouldBuildModern ? isModern ? modernTargets : legacyTargets : undefined, - // If users import all core-js they're probably not concerned with - // bundle size. We shouldn't rely on magic to try and shrink it. useBuiltIns: 'entry', // Do not transform modules to CJS modules: false, From 718811453ff686a6db02a61749efb19d21447b37 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Fri, 15 Mar 2019 17:56:41 +0100 Subject: [PATCH 08/14] looked over a few defaults --- packages/babel-preset-react-app/create.js | 2 +- packages/create-react-app/createReactApp.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/babel-preset-react-app/create.js b/packages/babel-preset-react-app/create.js index d5a9d5ac30..3a7bcb6b29 100644 --- a/packages/babel-preset-react-app/create.js +++ b/packages/babel-preset-react-app/create.js @@ -54,7 +54,7 @@ module.exports = function(api, opts, env) { var shouldBuildModern = validateBoolOption( 'shouldBuildModernAndLegacy', opts.shouldBuildModernAndLegacy, - true + false ); var absoluteRuntimePath = undefined; diff --git a/packages/create-react-app/createReactApp.js b/packages/create-react-app/createReactApp.js index fde9c2aade..9ea51a87f5 100755 --- a/packages/create-react-app/createReactApp.js +++ b/packages/create-react-app/createReactApp.js @@ -77,7 +77,6 @@ const program = new commander.Command(packageJson.name) ) .option('--use-npm') .option('--use-pnp') - .option('--modern') .option('--typescript') .allowUnknownOption() .on('--help', () => { From 84e8633b66a8c9bc0dc8e26cc1deaa8a441402c9 Mon Sep 17 00:00:00 2001 From: jdecroock Date: Fri, 15 Mar 2019 18:11:13 +0100 Subject: [PATCH 09/14] add documentation --- packages/react-dev-utils/README.md | 35 ++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/react-dev-utils/README.md b/packages/react-dev-utils/README.md index d89dc95f66..d76441574e 100644 --- a/packages/react-dev-utils/README.md +++ b/packages/react-dev-utils/README.md @@ -56,6 +56,41 @@ module.exports = { }; ``` +#### `new HtmlWebpackEsModulesPlugin()` + +This Webpack plugin allows you to build two builds, the first one a legacy build and the +second being a modern one. +This plugin is used to use the first build as a buffer and writes it to a temporairy file, +then when the second build completes it merges the two and adds a shim to support safari. + +This way of working relies on [module-nomodule differential serving](https://jakearchibald.com/2017/es-modules-in-browsers/). + +```js +var path = require('path'); +var HtmlWebpackPlugin = require('html-webpack-plugin'); +var HtmlWebpackEsModulesPlugin = require('react-dev-utils/HtmlWebpackEsModulesPlugin'); + +// Webpack config +var publicUrl = '/my-custom-url'; + +module.exports = { + output: { + // ... + }, + // ... + plugins: [ + // Generates an `index.html` file with the