Skip to content
This repository was archived by the owner on Mar 25, 2026. It is now read-only.
Open
6 changes: 0 additions & 6 deletions packages/cra-template-typescript/template/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,5 @@ import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, 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();
6 changes: 0 additions & 6 deletions packages/cra-template/template/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,5 @@ import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

ReactDOM.render(<App />, 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();
6 changes: 1 addition & 5 deletions packages/react-dev-utils/FileSizeReporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
83 changes: 52 additions & 31 deletions packages/react-dev-utils/WebpackDevServerUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -128,47 +128,61 @@ 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();
}
console.log('Compiling...');
});

let isFirstCompile = true;
let tsMessagesPromise;
let tsMessagesResolver;
let tsMessagesPromises = [];

if (useTypeScript) {
compiler.hooks.beforeCompile.tap('beforeCompile', () => {
tsMessagesPromise = new Promise(resolve => {
tsMessagesResolver = msgs => resolve(msgs);
});
});
masterCompiler.compilers.forEach((compiler, compilerIndex) => {
let tsMessagesResolver;

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),
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),
});
});
}
});

// "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.
Expand All @@ -189,7 +203,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);
Expand Down Expand Up @@ -269,12 +290,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 {
Expand All @@ -283,7 +304,7 @@ function createCompiler({
});
}

return compiler;
return masterCompiler;
}

function resolveLoopback(proxy) {
Expand Down
4 changes: 2 additions & 2 deletions packages/react-dev-utils/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-dev-utils",
"version": "10.0.0",
"name": "@ouihelp/react-dev-utils",
"version": "10000000.0.0",
"description": "Webpack utilities used by Create React App",
"repository": {
"type": "git",
Expand Down
6 changes: 6 additions & 0 deletions packages/react-scripts/config/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,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'),
Expand All @@ -104,6 +105,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'),
Expand Down Expand Up @@ -140,6 +142,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`),
Expand Down
77 changes: 57 additions & 20 deletions packages/react-scripts/config/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -146,7 +145,7 @@ module.exports = function(webpackEnv) {
return loaders;
};

return {
const originalConfig = {
mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
// Stop compilation early in production
bail: isEnvProduction,
Expand Down Expand Up @@ -253,6 +252,7 @@ module.exports = function(webpackEnv) {
},
},
sourceMap: shouldUseSourceMap,
parallel: false,
}),
// This is only used in production mode
new OptimizeCSSAssetsPlugin({
Expand Down Expand Up @@ -674,24 +674,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: publicUrl + '/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({
Expand Down Expand Up @@ -736,4 +718,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];
};
5 changes: 3 additions & 2 deletions packages/react-scripts/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-scripts",
"version": "3.3.0",
"name": "@ouihelp/react-scripts",
"version": "3003000.0.1",
"description": "Configuration and scripts for Create React App.",
"repository": {
"type": "git",
Expand Down Expand Up @@ -29,6 +29,7 @@
"types": "./lib/react-app.d.ts",
"dependencies": {
"@babel/core": "7.7.4",
"@ouihelp/react-dev-utils": "10000000.0.0",
"@svgr/webpack": "4.3.3",
"@typescript-eslint/eslint-plugin": "^2.8.0",
"@typescript-eslint/parser": "^2.8.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/react-scripts/scripts/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -111,7 +111,7 @@ checkBrowsers(paths.appPath, isInteractive)

const appPackage = require(paths.appPackageJson);
const publicUrl = paths.publicUrl;
const publicPath = config.output.publicPath;
const publicPath = config[0].output.publicPath;
const buildFolder = path.relative(process.cwd(), paths.appBuild);
printHostingInstructions(
appPackage,
Expand Down
2 changes: 1 addition & 1 deletion packages/react-scripts/scripts/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ function verifyTypeScriptSetup() {
if (!fs.existsSync(paths.appTypeDeclarations)) {
fs.writeFileSync(
paths.appTypeDeclarations,
`/// <reference types="react-scripts" />${os.EOL}`
`/// <reference types="@ouihelp/react-scripts" />${os.EOL}`
);
}
}
Expand Down