diff --git a/packages/cra-template-typescript/template/src/index.tsx b/packages/cra-template-typescript/template/src/index.tsx index f5185c1ec7a..6832e7832bb 100644 --- a/packages/cra-template-typescript/template/src/index.tsx +++ b/packages/cra-template-typescript/template/src/index.tsx @@ -2,7 +2,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; -import * as serviceWorker from './serviceWorker'; ReactDOM.render( @@ -10,8 +9,3 @@ ReactDOM.render( , document.getElementById('root') ); - -// If you want your app to work offline and load faster, you can change -// unregister() to register() below. Note this comes with some pitfalls. -// Learn more about service workers: https://bit.ly/CRA-PWA -serviceWorker.unregister(); diff --git a/packages/cra-template/template/src/index.js b/packages/cra-template/template/src/index.js index f5185c1ec7a..6832e7832bb 100644 --- a/packages/cra-template/template/src/index.js +++ b/packages/cra-template/template/src/index.js @@ -2,7 +2,6 @@ import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; -import * as serviceWorker from './serviceWorker'; ReactDOM.render( @@ -10,8 +9,3 @@ ReactDOM.render( , document.getElementById('root') ); - -// If you want your app to work offline and load faster, you can change -// unregister() to register() below. Note this comes with some pitfalls. -// Learn more about service workers: https://bit.ly/CRA-PWA -serviceWorker.unregister(); diff --git a/packages/react-dev-utils/FileSizeReporter.js b/packages/react-dev-utils/FileSizeReporter.js index b2b4cc6904d..80a1b4e7244 100644 --- a/packages/react-dev-utils/FileSizeReporter.js +++ b/packages/react-dev-utils/FileSizeReporter.js @@ -16,11 +16,7 @@ var stripAnsi = require('strip-ansi'); var gzipSize = require('gzip-size').sync; function canReadAsset(asset) { - return ( - /\.(js|css)$/.test(asset) && - !/service-worker\.js/.test(asset) && - !/precache-manifest\.[0-9a-f]+\.js/.test(asset) - ); + return /\.(js|css)$/.test(asset); } // Prints a detailed summary of build files. diff --git a/packages/react-dev-utils/WebpackDevServerUtils.js b/packages/react-dev-utils/WebpackDevServerUtils.js index 07a1ac873ee..8b5952355ce 100644 --- a/packages/react-dev-utils/WebpackDevServerUtils.js +++ b/packages/react-dev-utils/WebpackDevServerUtils.js @@ -113,9 +113,9 @@ function createCompiler({ }) { // "Compiler" is a low-level interface to webpack. // It lets us listen to some events and provide our own custom messages. - let compiler; + let masterCompiler; try { - compiler = webpack(config); + masterCompiler = webpack(config); } catch (err) { console.log(chalk.red('Failed to compile.')); console.log(); @@ -128,7 +128,7 @@ function createCompiler({ // recompiling a bundle. WebpackDevServer takes care to pause serving the // bundle, so if you refresh, it'll wait instead of serving the old one. // "invalid" is short for "bundle invalidated", it doesn't imply any errors. - compiler.hooks.invalid.tap('invalid', () => { + masterCompiler.hooks.invalid.tap('invalid', () => { if (isInteractive) { clearConsole(); } @@ -136,39 +136,52 @@ function createCompiler({ }); let isFirstCompile = true; - let tsMessagesPromise; - let tsMessagesResolver; - - if (useTypeScript) { - compiler.hooks.beforeCompile.tap('beforeCompile', () => { - tsMessagesPromise = new Promise(resolve => { - tsMessagesResolver = msgs => resolve(msgs); + let tsMessagesPromises = []; + + masterCompiler.compilers.forEach((compiler, compilerIndex) => { + let tsMessagesResolver; + if (useTypeScript) { + compiler.hooks.beforeCompile.tap('beforeCompile', () => { + tsMessagesPromises[compilerIndex] = new Promise(resolve => { + tsMessagesResolver = msgs => resolve(msgs); + }); }); - }); - forkTsCheckerWebpackPlugin - .getCompilerHooks(compiler) - .receive.tap('afterTypeScriptCheck', (diagnostics, lints) => { - const allMsgs = [...diagnostics, ...lints]; - const format = message => - `${message.file}\n${typescriptFormatter(message, true)}`; - - tsMessagesResolver({ - errors: allMsgs.filter(msg => msg.severity === 'error').map(format), - warnings: allMsgs - .filter(msg => msg.severity === 'warning') - .map(format), + forkTsCheckerWebpackPlugin + .getCompilerHooks(compiler) + .receive.tap('afterTypeScriptCheck', (diagnostics, lints) => { + const allMsgs = [...diagnostics, ...lints]; + const format = message => + `${message.file}\n${typescriptFormatter(message, true)}`; + + tsMessagesResolver({ + errors: allMsgs.filter(msg => msg.severity === 'error').map(format), + warnings: allMsgs + .filter(msg => msg.severity === 'warning') + .map(format), + }); }); - }); - } + } + }); // "done" event fires when webpack has finished recompiling the bundle. // Whether or not you have warnings or errors, you will get this event. - compiler.hooks.done.tap('done', async stats => { + masterCompiler.hooks.done.tap('done', async stats => { if (isInteractive) { clearConsole(); } + stats.compilation = { + errors: stats.stats.reduce( + (previousErrors, s) => [...previousErrors, ...s.compilation.errors], + [] + ), + warnings: stats.stats.reduce( + (previousErrors, s) => [...previousErrors, ...s.compilation.warnings], + [] + ), + }; + // We have switched off the default webpack output in WebpackDevServer // options so we are going to "massage" the warnings and errors and present // them in a readable focused way. @@ -189,7 +202,14 @@ function createCompiler({ ); }, 100); - const messages = await tsMessagesPromise; + const masterMessages = await Promise.all(tsMessagesPromises); + const messages = masterMessages.reduce( + (previousMessages, currentMessages) => ({ + errors: [...previousMessages.errors, ...currentMessages.errors], + warnings: [...previousMessages.warnings, ...currentMessages.warnings], + }), + { errors: [], warnings: [] } + ); clearTimeout(delayedMsg); if (tscCompileOnError) { statsData.warnings.push(...messages.errors); @@ -269,12 +289,12 @@ function createCompiler({ arg => arg.indexOf('--smoke-test') > -1 ); if (isSmokeTest) { - compiler.hooks.failed.tap('smokeTest', async () => { - await tsMessagesPromise; + masterCompiler.hooks.failed.tap('smokeTest', async () => { + await Promise.all(tsMessagesPromises); process.exit(1); }); - compiler.hooks.done.tap('smokeTest', async stats => { - await tsMessagesPromise; + masterCompiler.hooks.done.tap('smokeTest', async stats => { + await Promise.all(tsMessagesPromises); if (stats.hasErrors() || stats.hasWarnings()) { process.exit(1); } else { @@ -283,7 +303,7 @@ function createCompiler({ }); } - return compiler; + return masterCompiler; } function resolveLoopback(proxy) { diff --git a/packages/react-dev-utils/package.json b/packages/react-dev-utils/package.json index 36ea5b34047..12181108bb1 100644 --- a/packages/react-dev-utils/package.json +++ b/packages/react-dev-utils/package.json @@ -1,6 +1,6 @@ { - "name": "react-dev-utils", - "version": "10.2.1", + "name": "@ouihelp/react-dev-utils", + "version": "10020001.0.2", "description": "webpack utilities used by Create React App", "repository": { "type": "git", diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js index 11d81b7f5a7..4fa29ad33a6 100644 --- a/packages/react-scripts/config/paths.js +++ b/packages/react-scripts/config/paths.js @@ -64,6 +64,7 @@ module.exports = { appPublic: resolveApp('public'), appHtml: resolveApp('public/index.html'), appIndexJs: resolveModule(resolveApp, 'src/index'), + appServiceWorkerJs: resolveModule(resolveApp, 'src/service-worker.entry'), appPackageJson: resolveApp('package.json'), appSrc: resolveApp('src'), appTsConfig: resolveApp('tsconfig.json'), @@ -86,6 +87,7 @@ module.exports = { appPublic: resolveApp('public'), appHtml: resolveApp('public/index.html'), appIndexJs: resolveModule(resolveApp, 'src/index'), + appServiceWorkerJs: resolveModule(resolveApp, 'src/service-worker.entry'), appPackageJson: resolveApp('package.json'), appSrc: resolveApp('src'), appTsConfig: resolveApp('tsconfig.json'), @@ -121,6 +123,10 @@ if ( appPublic: resolveOwn(`${templatePath}/public`), appHtml: resolveOwn(`${templatePath}/public/index.html`), appIndexJs: resolveModule(resolveOwn, `${templatePath}/src/index`), + appServiceWorkerJs: resolveModule( + resolveOwn, + `${templatePath}/src/service-worker.entry` + ), appPackageJson: resolveOwn('package.json'), appSrc: resolveOwn(`${templatePath}/src`), appTsConfig: resolveOwn(`${templatePath}/tsconfig.json`), diff --git a/packages/react-scripts/config/webpack.config.js b/packages/react-scripts/config/webpack.config.js index 25840d91148..b6153d06212 100644 --- a/packages/react-scripts/config/webpack.config.js +++ b/packages/react-scripts/config/webpack.config.js @@ -22,7 +22,6 @@ const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const safePostCssParser = require('postcss-safe-parser'); const ManifestPlugin = require('webpack-manifest-plugin'); const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin'); -const WorkboxWebpackPlugin = require('workbox-webpack-plugin'); const WatchMissingNodeModulesPlugin = require('react-dev-utils/WatchMissingNodeModulesPlugin'); const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin'); const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent'); @@ -138,7 +137,7 @@ module.exports = function(webpackEnv) { return loaders; }; - return { + const originalConfig = { mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development', // Stop compilation early in production bail: isEnvProduction, @@ -246,6 +245,9 @@ module.exports = function(webpackEnv) { }, }, sourceMap: shouldUseSourceMap, + // We cheat on this one to avoid a bug on CI providers advertising + // overly high number of CPUs. + parallel: 4, }), // This is only used in production mode new OptimizeCSSAssetsPlugin({ @@ -654,24 +656,6 @@ module.exports = function(webpackEnv) { // 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$/), - // Generate a service worker script that will precache, and keep up to date, - // the HTML & assets that are part of the webpack build. - isEnvProduction && - new WorkboxWebpackPlugin.GenerateSW({ - clientsClaim: true, - exclude: [/\.map$/, /asset-manifest\.json$/], - importWorkboxFrom: 'cdn', - navigateFallback: paths.publicUrlOrPath + 'index.html', - navigateFallbackBlacklist: [ - // Exclude URLs starting with /_, as they're likely an API call - new RegExp('^/_'), - // Exclude any URLs whose last part seems to be a file extension - // as they're likely a resource and not a SPA route. - // URLs containing a "?" character won't be blacklisted as they're likely - // a route with query params (e.g. auth callbacks). - new RegExp('/[^/?]+\\.[^/]+$'), - ], - }), // TypeScript type checking useTypeScript && new ForkTsCheckerWebpackPlugin({ @@ -716,4 +700,59 @@ module.exports = function(webpackEnv) { // our own hints via the FileSizeReporter performance: false, }; + + const serviceWorkerConfig = { + target: 'webworker', + mode: originalConfig.mode, + bail: originalConfig.bail, + devtool: originalConfig.devtool, + entry: { 'service-worker': paths.appServiceWorkerJs }, + output: { + path: originalConfig.output.path, + pathinfo: originalConfig.output.pathinfo, + filename: 'sw.js', + publicPath: originalConfig.output.publicPath, + devtoolModuleFilenameTemplate: + originalConfig.output.devtoolModuleFilenameTemplate, + }, + optimization: { + minimize: originalConfig.optimization.minimize, + minimizer: originalConfig.optimization.minimizer, + }, + resolve: originalConfig.resolve, + resolveLoader: originalConfig.resolveLoader, + module: originalConfig.module, + plugins: [ + new webpack.DefinePlugin(env.stringified), + useTypeScript && + new ForkTsCheckerWebpackPlugin({ + typescript: resolve.sync('typescript', { + basedir: paths.appNodeModules, + }), + async: isEnvDevelopment, + useTypescriptIncrementalApi: true, + checkSyntacticErrors: true, + resolveModuleNameModule: process.versions.pnp + ? `${__dirname}/pnpTs.js` + : undefined, + resolveTypeReferenceDirectiveModule: process.versions.pnp + ? `${__dirname}/pnpTs.js` + : undefined, + tsconfig: paths.appTsConfig, + reportFiles: [ + '**', + '!**/__tests__/**', + '!**/?(*.)(spec|test).*', + '!**/src/setupProxy.*', + '!**/src/setupTests.*', + ], + watch: paths.appSrc, + silent: true, + // The formatter is invoked directly in WebpackDevServerUtils during development + formatter: isEnvProduction ? typescriptFormatter : undefined, + }), + ].filter(Boolean), + }; + + return [originalConfig, serviceWorkerConfig]; }; diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json index 63e7faf390c..7a265754804 100644 --- a/packages/react-scripts/package.json +++ b/packages/react-scripts/package.json @@ -1,6 +1,6 @@ { - "name": "react-scripts", - "version": "3.4.1", + "name": "@ouihelp/react-scripts", + "version": "3004001.0.4", "description": "Configuration and scripts for Create React App.", "repository": { "type": "git", @@ -29,6 +29,7 @@ "types": "./lib/react-app.d.ts", "dependencies": { "@babel/core": "7.9.0", + "@ouihelp/react-dev-utils": "10020001.0.2", "@svgr/webpack": "4.3.3", "@typescript-eslint/eslint-plugin": "^2.10.0", "@typescript-eslint/parser": "^2.10.0", diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js index fa30fb09a3f..27aa23e54fc 100644 --- a/packages/react-scripts/scripts/build.js +++ b/packages/react-scripts/scripts/build.js @@ -40,7 +40,7 @@ const paths = require('../config/paths'); const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles'); const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages'); const printHostingInstructions = require('react-dev-utils/printHostingInstructions'); -const FileSizeReporter = require('react-dev-utils/FileSizeReporter'); +const FileSizeReporter = require('@ouihelp/react-dev-utils/FileSizeReporter'); const printBuildError = require('react-dev-utils/printBuildError'); const measureFileSizesBeforeBuild = @@ -111,7 +111,7 @@ checkBrowsers(paths.appPath, isInteractive) const appPackage = require(paths.appPackageJson); const publicUrl = paths.publicUrlOrPath; - const publicPath = config.output.publicPath; + const publicPath = config[0].output.publicPath; const buildFolder = path.relative(process.cwd(), paths.appBuild); printHostingInstructions( appPackage, diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js index 2568ab36db1..86d6253c544 100644 --- a/packages/react-scripts/scripts/start.js +++ b/packages/react-scripts/scripts/start.js @@ -42,7 +42,7 @@ const { createCompiler, prepareProxy, prepareUrls, -} = require('react-dev-utils/WebpackDevServerUtils'); +} = require('@ouihelp/react-dev-utils/WebpackDevServerUtils'); const openBrowser = require('react-dev-utils/openBrowser'); const paths = require('../config/paths'); const configFactory = require('../config/webpack.config'); diff --git a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js index ebd93d7b667..b072e5a3e57 100644 --- a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js +++ b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js @@ -256,7 +256,7 @@ function verifyTypeScriptSetup() { if (!fs.existsSync(paths.appTypeDeclarations)) { fs.writeFileSync( paths.appTypeDeclarations, - `/// ${os.EOL}` + `/// ${os.EOL}` ); } }