diff --git a/package.json b/package.json
index 92a9291ecb3..affd65b0461 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"packages/*"
],
"scripts": {
- "build": "cd packages/react-scripts && node bin/react-scripts.js build",
+ "build": "cd packages/react-error-overlay/ && yarn build:prod",
"changelog": "lerna-changelog",
"create-react-app": "node tasks/cra.js",
"e2e": "tasks/e2e-simple.sh",
@@ -24,13 +24,13 @@
"@testing-library/react": "^9.3.0",
"@testing-library/user-event": "^7.1.2",
"alex": "^8.0.0",
- "eslint": "^6.1.0",
+ "eslint": "^7.11.0",
"execa": "1.0.0",
"fs-extra": "^7.0.1",
"get-port": "^4.2.0",
"globby": "^9.1.0",
"husky": "^1.3.1",
- "jest": "24.9.0",
+ "jest": "26.6.0",
"lerna": "3.19.0",
"lerna-changelog": "~0.8.2",
"lint-staged": "^8.0.4",
diff --git a/packages/react-error-overlay/package.json b/packages/react-error-overlay/package.json
index d3455b42347..83ca9ae5328 100644
--- a/packages/react-error-overlay/package.json
+++ b/packages/react-error-overlay/package.json
@@ -38,13 +38,13 @@
"@babel/core": "7.9.0",
"anser": "1.4.9",
"babel-eslint": "10.1.0",
- "babel-jest": "^24.9.0",
+ "babel-jest": "^26.6.0",
"babel-loader": "8.1.0",
"babel-preset-react-app": "^9.1.2",
"chalk": "2.4.2",
"chokidar": "^3.3.0",
"cross-env": "6.0.3",
- "eslint": "^6.1.0",
+ "eslint": "^7.11.0",
"eslint-config-react-app": "^5.2.1",
"eslint-plugin-flowtype": "4.5.2",
"eslint-plugin-import": "2.20.1",
@@ -52,7 +52,7 @@
"eslint-plugin-react": "7.19.0",
"flow-bin": "^0.116.0",
"html-entities": "1.2.1",
- "jest": "24.9.0",
+ "jest": "26.6.0",
"jest-fetch-mock": "2.1.2",
"object-assign": "4.1.1",
"promise": "8.0.3",
diff --git a/packages/react-scripts/bin/react-scripts.js b/packages/react-scripts/bin/react-scripts.js
index 7e6e290251a..09604f6a03f 100755
--- a/packages/react-scripts/bin/react-scripts.js
+++ b/packages/react-scripts/bin/react-scripts.js
@@ -26,7 +26,7 @@ const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : [];
if (['build', 'eject', 'start', 'test'].includes(script)) {
const result = spawn.sync(
- 'node',
+ process.execPath,
nodeArgs
.concat(require.resolve('../scripts/' + script))
.concat(args.slice(scriptIndex + 1)),
diff --git a/packages/react-scripts/config/env.js b/packages/react-scripts/config/env.js
index d5ad88fefb2..17b9cdb09db 100644
--- a/packages/react-scripts/config/env.js
+++ b/packages/react-scripts/config/env.js
@@ -25,11 +25,11 @@ if (!NODE_ENV) {
// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
const dotenvFiles = [
`${paths.dotenv}.${NODE_ENV}.local`,
- `${paths.dotenv}.${NODE_ENV}`,
// Don't include `.env.local` for `test` environment
// since normally you expect tests to produce the same
// results for everyone
NODE_ENV !== 'test' && `${paths.dotenv}.local`,
+ `${paths.dotenv}.${NODE_ENV}`,
paths.dotenv,
].filter(Boolean);
@@ -93,6 +93,11 @@ function getClientEnvironment(publicUrl) {
WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
+ // Whether or not react-refresh is enabled.
+ // react-refresh is not 100% stable at this time,
+ // which is why it's disabled by default.
+ // It is defined here so it is available in the webpackHotDevClient.
+ FAST_REFRESH: process.env.FAST_REFRESH !== 'false',
}
);
// Stringify all values so we can feed into webpack DefinePlugin
diff --git a/packages/react-scripts/config/jest/babelTransform.js b/packages/react-scripts/config/jest/babelTransform.js
index 7feed94c59a..c5830153e80 100644
--- a/packages/react-scripts/config/jest/babelTransform.js
+++ b/packages/react-scripts/config/jest/babelTransform.js
@@ -1,16 +1,37 @@
-// @remove-file-on-eject
+// @remove-on-eject-begin
/**
* Copyright (c) 2014-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
+// @remove-on-eject-end
'use strict';
const babelJest = require('babel-jest');
+const hasJsxRuntime = (() => {
+ if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
+ return false;
+ }
+
+ try {
+ require.resolve('react/jsx-runtime');
+ return true;
+ } catch (e) {
+ return false;
+ }
+})();
+
module.exports = babelJest.createTransformer({
- presets: [require.resolve('babel-preset-react-app')],
+ presets: [
+ [
+ require.resolve('babel-preset-react-app'),
+ {
+ runtime: hasJsxRuntime ? 'automatic' : 'classic',
+ },
+ ],
+ ],
babelrc: false,
configFile: false,
});
diff --git a/packages/react-scripts/config/modules.js b/packages/react-scripts/config/modules.js
index 38c95b91e44..22820993a25 100644
--- a/packages/react-scripts/config/modules.js
+++ b/packages/react-scripts/config/modules.js
@@ -22,15 +22,8 @@ const resolve = require('resolve');
function getAdditionalModulePaths(options = {}) {
const baseUrl = options.baseUrl;
- // We need to explicitly check for null and undefined (and not a falsy value) because
- // TypeScript treats an empty string as `.`.
- if (baseUrl == null) {
- // If there's no baseUrl set we respect NODE_PATH
- // Note that NODE_PATH is deprecated and will be removed
- // in the next major release of create-react-app.
-
- const nodePath = process.env.NODE_PATH || '';
- return nodePath.split(path.delimiter).filter(Boolean);
+ if (!baseUrl) {
+ return '';
}
const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
diff --git a/packages/react-scripts/config/paths.js b/packages/react-scripts/config/paths.js
index 230b35070f9..6a03de5b4f9 100644
--- a/packages/react-scripts/config/paths.js
+++ b/packages/react-scripts/config/paths.js
@@ -29,6 +29,8 @@ const publicUrlOrPath = getPublicUrlOrPath(
process.env.PUBLIC_URL
);
+const buildPath = process.env.BUILD_PATH || 'build';
+
const moduleFileExtensions = [
'web.mjs',
'mjs',
@@ -60,7 +62,7 @@ const resolveModule = (resolveFn, filePath) => {
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
- appBuild: resolveApp('build'),
+ appBuild: resolveApp(buildPath),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
entryHtml: resolveApp(`public/${process.env.SECOND_ENTRY}.html`),
@@ -74,6 +76,7 @@ module.exports = {
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
+ swSrc: resolveModule(resolveApp, 'src/service-worker'),
publicUrlOrPath,
};
@@ -84,7 +87,7 @@ const resolveOwn = relativePath => path.resolve(__dirname, '..', relativePath);
module.exports = {
dotenv: resolveApp('.env'),
appPath: resolveApp('.'),
- appBuild: resolveApp('build'),
+ appBuild: resolveApp(buildPath),
appPublic: resolveApp('public'),
appHtml: resolveApp('public/index.html'),
entryHtml: resolveApp(`public/${process.env.SECOND_ENTRY}.html`),
@@ -98,6 +101,7 @@ module.exports = {
testsSetup: resolveModule(resolveApp, 'src/setupTests'),
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
+ swSrc: resolveModule(resolveApp, 'src/service-worker'),
publicUrlOrPath,
// These properties only exist before ejecting:
ownPath: resolveOwn('.'),
@@ -121,7 +125,7 @@ if (
module.exports = {
dotenv: resolveOwn(`${templatePath}/.env`),
appPath: resolveApp('.'),
- appBuild: resolveOwn('../../build'),
+ appBuild: resolveOwn(path.join('../..', buildPath)),
appPublic: resolveOwn(`${templatePath}/public`),
appHtml: resolveOwn(`${templatePath}/public/index.html`),
appIndexJs: resolveModule(resolveOwn, `${templatePath}/src/index`),
@@ -133,6 +137,7 @@ if (
testsSetup: resolveModule(resolveOwn, `${templatePath}/src/setupTests`),
proxySetup: resolveOwn(`${templatePath}/src/setupProxy.js`),
appNodeModules: resolveOwn('node_modules'),
+ swSrc: resolveModule(resolveOwn, `${templatePath}/src/service-worker`),
publicUrlOrPath,
// These properties only exist before ejecting:
ownPath: resolveOwn('.'),
diff --git a/packages/react-scripts/config/webpack.config.js b/packages/react-scripts/config/webpack.config.js
index 4240c7ef869..50216b84c2c 100644
--- a/packages/react-scripts/config/webpack.config.js
+++ b/packages/react-scripts/config/webpack.config.js
@@ -27,12 +27,14 @@ 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');
+const ESLintPlugin = require('eslint-webpack-plugin');
const paths = require('./paths');
const modules = require('./modules');
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 ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
// @remove-on-eject-begin
const getCacheIdentifier = require('react-dev-utils/getCacheIdentifier');
// @remove-on-eject-end
@@ -42,11 +44,20 @@ const appPackageJson = require(paths.appPackageJson);
// Source maps are resource heavy and can cause out of memory issue for large source files.
const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false';
+
+const webpackDevClientEntry = require.resolve(
+ 'react-dev-utils/webpackHotDevClient'
+);
+const reactRefreshOverlayEntry = require.resolve(
+ 'react-dev-utils/refreshOverlayInterop'
+);
+
// Some apps do not need the benefits of saving a web request, so not inlining the chunk
// makes for a smoother build process.
const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false';
-const isExtendingEslintConfig = process.env.EXTEND_ESLINT === 'true';
+const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === 'true';
+const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === 'true';
const imageInlineSizeLimit = parseInt(
process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
@@ -55,12 +66,27 @@ const imageInlineSizeLimit = parseInt(
// Check if TypeScript is setup
const useTypeScript = fs.existsSync(paths.appTsConfig);
+// Get the path to the uncompiled service worker (if it exists).
+const swSrc = paths.swSrc;
+
// style files regexes
const cssRegex = /\.css$/;
const cssModuleRegex = /\.module\.css$/;
const sassRegex = /\.(scss|sass)$/;
const sassModuleRegex = /\.module\.(scss|sass)$/;
+const hasJsxRuntime = (() => {
+ if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
+ return false;
+ }
+
+ try {
+ require.resolve('react/jsx-runtime');
+ return true;
+ } catch (e) {
+ return false;
+ }
+})();
const toCamel = str => str.replace(/-([a-z])/g, g => g[1].toUpperCase());
// Just to add a second entry module
@@ -73,7 +99,7 @@ const isCI =
// 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) {
const isEnvDevelopment = webpackEnv === 'development';
const isEnvProduction = webpackEnv === 'production';
@@ -88,6 +114,8 @@ module.exports = function(webpackEnv) {
// Get environment variables to inject into our app.
const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
+ const shouldUseReactRefresh = env.raw.FAST_REFRESH;
+
// common function to get style loaders
const getStyleLoaders = (cssOptions, preProcessor) => {
const loaders = [
@@ -126,7 +154,7 @@ module.exports = function(webpackEnv) {
// which in turn let's users customize the target behavior as per their needs.
postcssNormalize(),
],
- sourceMap: isEnvProduction && shouldUseSourceMap,
+ sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
},
},
].filter(Boolean);
@@ -135,7 +163,8 @@ module.exports = function(webpackEnv) {
{
loader: require.resolve('resolve-url-loader'),
options: {
- sourceMap: isEnvProduction && shouldUseSourceMap,
+ sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
+ root: paths.appSrc,
},
},
{
@@ -162,36 +191,19 @@ module.exports = function(webpackEnv) {
// This means they will be the "root" imports that are included in JS bundle.
entry: secondEntryName
? {
- app: [
- isEnvDevelopment &&
- require.resolve('react-dev-utils/webpackHotDevClient'),
- paths.appIndexJs,
- ].filter(Boolean),
- [secondEntryName]: [
- isEnvDevelopment &&
- require.resolve('react-dev-utils/webpackHotDevClient'),
- paths.entryIndexJS,
- ].filter(Boolean),
- }
- : [
- // Include an alternative client for WebpackDevServer. A client's job is to
- // connect to WebpackDevServer by a socket and get notified about changes.
- // When you save a file, the client will either apply hot updates (in case
- // of CSS changes), or refresh the page (in case of JS changes). When you
- // make a syntax error, this client will display a syntax error overlay.
- // Note: instead of the default WebpackDevServer client, we use a custom one
- // to bring better experience for Create React App users. You can replace
- // the line below with these two lines if you prefer the stock client:
- // require.resolve('webpack-dev-server/client') + '?/',
- // require.resolve('webpack/hot/dev-server'),
- isEnvDevelopment &&
- require.resolve('react-dev-utils/webpackHotDevClient'),
- // Finally, this is your app's code:
+ app: isEnvDevelopment && !shouldUseReactRefresh ? [
+ webpackDevClientEntry,
+ paths.appIndexJs,
+ ] : paths.appIndexJs,
+ [secondEntryName]: isEnvDevelopment && !shouldUseReactRefresh ? [
+ webpackDevClientEntry,
paths.appIndexJs,
- // 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.
- ].filter(Boolean),
+ ] : paths.appIndexJs,
+ }
+ : isEnvDevelopment && !shouldUseReactRefresh ? [
+ webpackDevClientEntry,
+ paths.appIndexJs,
+ ] : paths.appIndexJs,
output: {
// The build folder.
path: isEnvProduction ? paths.appBuild : undefined,
@@ -216,11 +228,11 @@ module.exports = function(webpackEnv) {
// Point sourcemap entries to original disk location (format as URL on Windows)
devtoolModuleFilenameTemplate: isEnvProduction
? info =>
- path
- .relative(paths.appSrc, info.absoluteResourcePath)
- .replace(/\\/g, '/')
+ path
+ .relative(paths.appSrc, info.absoluteResourcePath)
+ .replace(/\\/g, '/')
: isEnvDevelopment &&
- (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
+ (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
// Prevents conflicts when multiple webpack runtimes (from different apps)
// are used on the same page.
jsonpFunction: `webpackJsonp${appPackageJson.name}`,
@@ -280,13 +292,13 @@ module.exports = function(webpackEnv) {
parser: safePostCssParser,
map: shouldUseSourceMap
? {
- // `inline: false` forces the sourcemap to be output into a
- // separate file
- inline: false,
- // `annotation: true` appends the sourceMappingURL to the end of
- // the css file, helping the browser find the sourcemap
- annotation: true,
- }
+ // `inline: false` forces the sourcemap to be output into a
+ // separate file
+ inline: false,
+ // `annotation: true` appends the sourceMappingURL to the end of
+ // the css file, helping the browser find the sourcemap
+ annotation: true,
+ }
: false,
},
cssProcessorPluginOptions: {
@@ -299,7 +311,7 @@ module.exports = function(webpackEnv) {
// https://medium.com/webpack/webpack-4-code-splitting-chunk-graph-and-the-splitchunks-optimization-be739a861366
splitChunks: {
chunks: 'all',
- name: false,
+ name: isEnvDevelopment,
},
// Keep the runtime chunk separated to enable long term caching
// https://twitter.com/wSokra/status/969679223278505985
@@ -345,7 +357,10 @@ module.exports = function(webpackEnv) {
// To fix this, we prevent you from importing files out of src/ -- if you'd like to,
// please link the files into your node_modules/ and let module-resolution kick in.
// Make sure your source files are compiled, as they will not be processed in any way.
- new ModuleScopePlugin(paths.appSrc, [paths.appPackageJson]),
+ new ModuleScopePlugin(paths.appSrc, [
+ paths.appPackageJson,
+ reactRefreshOverlayEntry,
+ ]),
],
},
resolveLoader: {
@@ -360,39 +375,22 @@ module.exports = function(webpackEnv) {
rules: [
// Disable require.ensure as it's not a standard language feature.
{ parser: { requireEnsure: false } },
-
- // First, run the linter.
- // It's important to do this before Babel processes the JS.
- {
- test: /\.(js|mjs|jsx|ts|tsx)$/,
- enforce: 'pre',
- use: [
- {
- options: {
- cache: true,
- formatter: require.resolve('react-dev-utils/eslintFormatter'),
- eslintPath: require.resolve('eslint'),
- resolvePluginsRelativeTo: __dirname,
- // @remove-on-eject-begin
- ignore: isExtendingEslintConfig,
- baseConfig: isExtendingEslintConfig
- ? undefined
- : {
- extends: [require.resolve('eslint-config-react-app')],
- },
- useEslintrc: isExtendingEslintConfig,
- // @remove-on-eject-end
- },
- loader: require.resolve('eslint-loader'),
- },
- ],
- include: paths.appSrc,
- },
{
// "oneOf" will traverse all following loaders until one will
// match the requirements. When no loader matches it will fall
// back to the "file" loader at the end of the loader list.
oneOf: [
+ // TODO: Merge this config once `image/avif` is in the mime-db
+ // https://github.com/jshttp/mime-db
+ {
+ test: [/\.avif$/],
+ loader: require.resolve('url-loader'),
+ options: {
+ limit: imageInlineSizeLimit,
+ mimetype: 'image/avif',
+ name: 'static/media/[name].[hash:8].[ext]',
+ },
+ },
// "url" loader works like "file" loader except that it embeds assets
// smaller than specified limit in bytes as data URLs to avoid requests.
// A missing `test` is equivalent to a match.
@@ -471,10 +469,17 @@ module.exports = function(webpackEnv) {
customize: require.resolve(
'babel-preset-react-app/webpack-overrides'
),
+ presets: [
+ [
+ require.resolve('babel-preset-react-app'),
+ {
+ runtime: hasJsxRuntime ? 'automatic' : 'classic',
+ },
+ ],
+ ],
// @remove-on-eject-begin
babelrc: false,
configFile: false,
- presets: [require.resolve('babel-preset-react-app')],
// Make sure we have a unique cache identifier, erring on the
// side of caution.
// We remove this when the user ejects because the default
@@ -504,7 +509,10 @@ module.exports = function(webpackEnv) {
},
},
],
- ],
+ isEnvDevelopment &&
+ shouldUseReactRefresh &&
+ require.resolve('react-refresh/babel'),
+ ].filter(Boolean),
// 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.
@@ -521,6 +529,8 @@ module.exports = function(webpackEnv) {
exclude: /@babel(?:\/|\\{1,2})runtime/,
loader: require.resolve('babel-loader'),
options: {
+ // https://docs.mapbox.com/mapbox-gl-js/api/#transpiling-v2
+ ignore: ['./node_modules/mapbox-gl/dist/mapbox-gl.js'],
babelrc: false,
configFile: false,
compact: false,
@@ -565,7 +575,9 @@ module.exports = function(webpackEnv) {
exclude: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
- sourceMap: isEnvProduction && shouldUseSourceMap,
+ sourceMap: isEnvProduction
+ ? shouldUseSourceMap
+ : isEnvDevelopment,
}),
// Don't consider CSS imports dead code even if the
// containing package claims to have no side effects.
@@ -579,7 +591,9 @@ module.exports = function(webpackEnv) {
test: cssModuleRegex,
use: getStyleLoaders({
importLoaders: 1,
- sourceMap: isEnvProduction && shouldUseSourceMap,
+ sourceMap: isEnvProduction
+ ? shouldUseSourceMap
+ : isEnvDevelopment,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
@@ -594,7 +608,9 @@ module.exports = function(webpackEnv) {
use: getStyleLoaders(
{
importLoaders: 3,
- sourceMap: isEnvProduction && shouldUseSourceMap,
+ sourceMap: isEnvProduction
+ ? shouldUseSourceMap
+ : isEnvDevelopment,
},
'sass-loader'
),
@@ -611,7 +627,9 @@ module.exports = function(webpackEnv) {
use: getStyleLoaders(
{
importLoaders: 3,
- sourceMap: isEnvProduction && shouldUseSourceMap,
+ sourceMap: isEnvProduction
+ ? shouldUseSourceMap
+ : isEnvDevelopment,
modules: {
getLocalIdent: getCSSModuleLocalIdent,
},
@@ -652,16 +670,16 @@ module.exports = function(webpackEnv) {
{},
length > 1
? {
- chunks: [_i === 0 ? 'app' : `${secondEntryName}`],
- excludeChunks: [_i === 0 ? `${secondEntryName}` : 'app'],
- }
+ chunks: [_i === 0 ? 'app' : `${secondEntryName}`],
+ excludeChunks: [_i === 0 ? `${secondEntryName}` : 'app'],
+ }
: {},
_i == 1
? {
- filename: htmlTemplate.split('/')[
- htmlTemplate.split('/').length - 1
- ],
- }
+ filename: htmlTemplate.split('/')[
+ htmlTemplate.split('/').length - 1
+ ],
+ }
: {},
{
inject: true,
@@ -669,19 +687,19 @@ module.exports = function(webpackEnv) {
},
isEnvProduction
? {
- minify: {
- removeComments: true,
- collapseWhitespace: true,
- removeRedundantAttributes: true,
- useShortDoctype: true,
- removeEmptyAttributes: true,
- removeStyleLinkTypeAttributes: true,
- keepClosingSlash: true,
- minifyJS: true,
- minifyCSS: true,
- minifyURLs: true,
- },
- }
+ minify: {
+ removeComments: true,
+ collapseWhitespace: true,
+ removeRedundantAttributes: true,
+ useShortDoctype: true,
+ removeEmptyAttributes: true,
+ removeStyleLinkTypeAttributes: true,
+ keepClosingSlash: true,
+ minifyJS: true,
+ minifyCSS: true,
+ minifyURLs: true,
+ },
+ }
: undefined
)
)
@@ -690,8 +708,8 @@ module.exports = function(webpackEnv) {
// a network request.
// https://github.com/facebook/create-react-app/issues/5358
isEnvProduction &&
- shouldInlineRuntimeChunk &&
- new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
+ shouldInlineRuntimeChunk &&
+ new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
// Makes some environment variables available in index.html.
// The public URL is available as %PUBLIC_URL% in index.html, e.g.:
//
@@ -707,8 +725,23 @@ module.exports = function(webpackEnv) {
// during a production build.
// Otherwise React will be compiled in the very slow development mode.
new webpack.DefinePlugin(env.stringified),
- // This is necessary to emit hot updates (currently CSS only):
+ // This is necessary to emit hot updates (CSS and Fast Refresh):
isEnvDevelopment && new webpack.HotModuleReplacementPlugin(),
+ // Experimental hot reloading for React .
+ // https://github.com/facebook/react/tree/master/packages/react-refresh
+ isEnvDevelopment &&
+ shouldUseReactRefresh &&
+ new ReactRefreshWebpackPlugin({
+ overlay: {
+ entry: webpackDevClientEntry,
+ // The expected exports are slightly different from what the overlay exports,
+ // so an interop is included here to enable feedback on module-level errors.
+ module: reactRefreshOverlayEntry,
+ // Since we ship a custom dev client and overlay integration,
+ // the bundled socket handling logic can be eliminated.
+ sockIntegration: false,
+ },
+ }),
// Watcher doesn't work well if you mistype casing in a path so we use
// a plugin that prints an error when you attempt to do this.
// See https://github.com/facebook/create-react-app/issues/240
@@ -718,14 +751,14 @@ module.exports = function(webpackEnv) {
// makes the discovery automatic so you don't have to restart.
// See https://github.com/facebook/create-react-app/issues/186
isEnvDevelopment &&
- new WatchMissingNodeModulesPlugin(paths.appNodeModules),
+ new WatchMissingNodeModulesPlugin(paths.appNodeModules),
isEnvProduction &&
- new MiniCssExtractPlugin({
- // Options similar to the same options in webpackOptions.output
- // both options are optional
- filename: 'static/css/[name].[contenthash:8].css',
- chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
- }),
+ new MiniCssExtractPlugin({
+ // Options similar to the same options in webpackOptions.output
+ // both options are optional
+ filename: 'static/css/[name].[contenthash:8].css',
+ chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
+ }),
// Generate an asset manifest file with the following content:
// - "files" key: Mapping of all asset filenames to their corresponding
// output file so that tools can pick it up without having to parse
@@ -754,13 +787,13 @@ module.exports = function(webpackEnv) {
}),
// Generate gzip files
isEnvProduction &&
- new CompressionPlugin({
- filename: '[path].gz[query]',
- algorithm: 'gzip',
- test: /\.js$|\.css$|\.html$|\.eot?.+$|\.ttf?.+$|\.woff?.+$|\.svg?.+$/,
- threshold: 10240,
- minRatio: 0.8,
- }),
+ new CompressionPlugin({
+ filename: '[path].gz[query]',
+ algorithm: 'gzip',
+ test: /\.js$|\.css$|\.html$|\.eot?.+$|\.ttf?.+$|\.woff?.+$|\.svg?.+$/,
+ threshold: 10240,
+ minRatio: 0.8,
+ }),
// Moment.js is an extremely popular library that bundles large locale files
// by default due to how webpack interprets its code. This is a practical
// solution that requires the user to opt into importing specific locales.
@@ -770,48 +803,72 @@ module.exports = function(webpackEnv) {
// 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('/[^/?]+\\.[^/]+$'),
- ],
+ fs.existsSync(swSrc) &&
+ new WorkboxWebpackPlugin.InjectManifest({
+ swSrc,
+ dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
+ exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
+ // Bump up the default maximum size (2mb) that's precached,
+ // to make lazy-loading failure scenarios less likely.
+ // See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
+ maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
}),
// TypeScript type checking
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.*',
- ],
- silent: true,
- // The formatter is invoked directly in WebpackDevServerUtils during development
- formatter: isEnvProduction ? typescriptFormatter : undefined,
+ new ForkTsCheckerWebpackPlugin({
+ typescript: resolve.sync('typescript', {
+ basedir: paths.appNodeModules,
}),
+ async: isEnvDevelopment,
+ checkSyntacticErrors: true,
+ resolveModuleNameModule: process.versions.pnp
+ ? `${__dirname}/pnpTs.js`
+ : undefined,
+ resolveTypeReferenceDirectiveModule: process.versions.pnp
+ ? `${__dirname}/pnpTs.js`
+ : undefined,
+ tsconfig: paths.appTsConfig,
+ reportFiles: [
+ // This one is specifically to match during CI tests,
+ // as micromatch doesn't match
+ // '../cra-template-typescript/template/src/App.tsx'
+ // otherwise.
+ '../**/src/**/*.{ts,tsx}',
+ '**/src/**/*.{ts,tsx}',
+ '!**/src/**/__tests__/**',
+ '!**/src/**/?(*.)(spec|test).*',
+ '!**/src/setupProxy.*',
+ '!**/src/setupTests.*',
+ ],
+ silent: true,
+ // The formatter is invoked directly in WebpackDevServerUtils during development
+ formatter: isEnvProduction ? typescriptFormatter : undefined,
+ }),
+ !disableESLintPlugin &&
+ new ESLintPlugin({
+ // Plugin options
+ extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
+ formatter: require.resolve('react-dev-utils/eslintFormatter'),
+ eslintPath: require.resolve('eslint'),
+ failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
+ context: paths.appSrc,
+ cache: true,
+ cacheLocation: path.resolve(
+ paths.appNodeModules,
+ '.cache/.eslintcache'
+ ),
+ // ESLint class options
+ cwd: paths.appPath,
+ resolvePluginsRelativeTo: __dirname,
+ baseConfig: {
+ extends: [require.resolve('eslint-config-react-app/base')],
+ rules: {
+ ...(!hasJsxRuntime && {
+ 'react/react-in-jsx-scope': 'error',
+ }),
+ },
+ },
+ }),
].filter(Boolean),
// Some libraries import Node modules but don't use them in the browser.
// Tell webpack to provide empty mocks for them so importing them works.
diff --git a/packages/react-scripts/config/webpackDevServer.config.js b/packages/react-scripts/config/webpackDevServer.config.js
index a6b9d66b96b..252a55fa641 100644
--- a/packages/react-scripts/config/webpackDevServer.config.js
+++ b/packages/react-scripts/config/webpackDevServer.config.js
@@ -23,7 +23,7 @@ const sockPath = process.env.WDS_SOCKET_PATH; // default: '/sockjs-node'
const sockPort = process.env.WDS_SOCKET_PORT;
const secondEntry = process.env.SECOND_ENTRY || '';
-module.exports = function(proxy, allowedHost) {
+module.exports = function (proxy, allowedHost) {
return {
// WebpackDevServer 2.4.3 introduced a security fix that prevents remote
// websites from potentially accessing local content through DNS rebinding:
diff --git a/packages/react-scripts/lib/react-app.d.ts b/packages/react-scripts/lib/react-app.d.ts
index 981cd73472c..624c875ec80 100644
--- a/packages/react-scripts/lib/react-app.d.ts
+++ b/packages/react-scripts/lib/react-app.d.ts
@@ -9,6 +9,11 @@ declare namespace NodeJS {
}
}
+declare module '*.avif' {
+ const src: string;
+ export default src;
+}
+
declare module '*.bmp' {
const src: string;
export default src;
diff --git a/packages/react-scripts/package.json b/packages/react-scripts/package.json
index 803e7bef92a..559544be072 100644
--- a/packages/react-scripts/package.json
+++ b/packages/react-scripts/package.json
@@ -1,11 +1,11 @@
{
- "name": "@onsmart/react-scripts",
- "version": "0.4.3",
- "description": "(3.4.1) Beakyn Configuration and scripts for Create React App.",
+ "name": "@outfront/react-scripts",
+ "version": "0.4.1",
+ "description": "(4.0.3) Beakyn Configuration and scripts for Create React App.",
"repository": "https://github.com/beakyn/create-react-app",
"license": "MIT",
"engines": {
- "node": ">=8.10"
+ "node": "^10.12.0 || >=12.0.0"
},
"bugs": {
"url": "https://github.com/beakyn/create-react-app/issues"
@@ -24,70 +24,77 @@
},
"types": "./lib/react-app.d.ts",
"dependencies": {
- "@babel/core": "7.9.0",
- "@svgr/webpack": "4.3.3",
- "@typescript-eslint/eslint-plugin": "^2.10.0",
- "@typescript-eslint/parser": "^2.10.0",
- "babel-eslint": "10.1.0",
- "babel-jest": "^24.9.0",
+ "@babel/core": "7.12.3",
+ "@pmmmwh/react-refresh-webpack-plugin": "0.4.3",
+ "@svgr/webpack": "5.5.0",
+ "@typescript-eslint/eslint-plugin": "^4.5.0",
+ "@typescript-eslint/parser": "^4.5.0",
+ "babel-eslint": "^10.1.0",
+ "babel-jest": "^26.6.0",
"babel-loader": "8.1.0",
- "babel-plugin-named-asset-import": "^0.3.6",
- "babel-preset-react-app": "^9.1.2",
- "camelcase": "^5.3.1",
+ "babel-plugin-named-asset-import": "^0.3.7",
+ "babel-preset-react-app": "^10.0.0",
+ "bfj": "^7.0.2",
+ "camelcase": "^6.1.0",
"case-sensitive-paths-webpack-plugin": "2.3.0",
- "css-loader": "3.4.2",
+ "css-loader": "4.3.0",
"dotenv": "8.2.0",
"dotenv-expand": "5.1.0",
- "eslint": "^6.6.0",
- "eslint-config-react-app": "^5.2.1",
- "eslint-loader": "3.0.3",
- "eslint-plugin-flowtype": "4.6.0",
- "eslint-plugin-import": "2.20.1",
- "eslint-plugin-jsx-a11y": "6.2.3",
- "eslint-plugin-react": "7.19.0",
- "eslint-plugin-react-hooks": "^1.6.1",
- "file-loader": "4.3.0",
- "fs-extra": "^8.1.0",
- "html-webpack-plugin": "4.0.0-beta.11",
+ "eslint": "^7.11.0",
+ "eslint-config-react-app": "^6.0.0",
+ "eslint-plugin-flowtype": "^5.2.0",
+ "eslint-plugin-import": "^2.22.1",
+ "eslint-plugin-jest": "^24.1.0",
+ "eslint-plugin-jsx-a11y": "^6.3.1",
+ "eslint-plugin-react": "^7.21.5",
+ "eslint-plugin-react-hooks": "^4.2.0",
+ "eslint-plugin-testing-library": "^3.9.2",
+ "eslint-webpack-plugin": "^2.5.2",
+ "file-loader": "6.1.1",
+ "fs-extra": "^9.0.1",
+ "html-webpack-plugin": "4.5.0",
"identity-obj-proxy": "3.0.0",
- "jest": "24.9.0",
- "jest-environment-jsdom-fourteen": "1.0.1",
- "jest-resolve": "24.9.0",
- "jest-watch-typeahead": "0.4.2",
- "mini-css-extract-plugin": "0.9.0",
- "optimize-css-assets-webpack-plugin": "5.0.3",
+ "jest": "26.6.0",
+ "jest-circus": "26.6.0",
+ "jest-resolve": "26.6.0",
+ "jest-watch-typeahead": "0.6.1",
+ "mini-css-extract-plugin": "0.11.3",
+ "optimize-css-assets-webpack-plugin": "5.0.4",
"pnp-webpack-plugin": "1.6.4",
- "postcss-flexbugs-fixes": "4.1.0",
+ "postcss-flexbugs-fixes": "4.2.1",
"postcss-loader": "3.0.0",
"postcss-normalize": "8.0.1",
"postcss-preset-env": "6.7.0",
- "postcss-safe-parser": "4.0.1",
- "react-app-polyfill": "^1.0.6",
- "react-dev-utils": "^10.2.1",
- "resolve": "1.15.0",
- "resolve-url-loader": "3.1.1",
- "sass-loader": "8.0.2",
- "semver": "6.3.0",
- "style-loader": "0.23.1",
- "terser-webpack-plugin": "2.3.5",
- "ts-pnp": "1.1.6",
- "url-loader": "2.3.0",
- "webpack": "4.42.0",
- "webpack-dev-server": "3.10.3",
+ "postcss-safe-parser": "5.0.2",
+ "prompts": "2.4.0",
+ "react-app-polyfill": "^2.0.0",
+ "react-dev-utils": "^11.0.3",
+ "react-refresh": "^0.8.3",
+ "resolve": "1.18.1",
+ "resolve-url-loader": "^3.1.2",
+ "sass-loader": "^10.0.5",
+ "semver": "7.3.2",
+ "style-loader": "1.3.0",
+ "terser-webpack-plugin": "4.2.3",
+ "ts-pnp": "1.2.0",
+ "url-loader": "4.1.1",
+ "webpack": "4.44.2",
+ "webpack-dev-server": "3.11.1",
"webpack-manifest-plugin": "2.2.0",
- "workbox-webpack-plugin": "4.3.1",
+ "workbox-webpack-plugin": "5.1.4",
"compression-webpack-plugin": "3.0.0",
"worker-loader": "2.0.0"
},
"devDependencies": {
- "react": "^16.12.0",
- "react-dom": "^16.12.0"
+ "react": "^17.0.1",
+ "react-dom": "^17.0.1"
},
"optionalDependencies": {
- "fsevents": "2.1.2"
+ "fsevents": "^2.1.3"
},
"peerDependencies": {
- "typescript": "^3.2.1"
+ "react": ">= 16",
+ "typescript": "^3.2.1 || ^4"
},
"peerDependenciesMeta": {
"typescript": {
diff --git a/packages/react-scripts/scripts/build.js b/packages/react-scripts/scripts/build.js
index fa30fb09a3f..de2ff505eb3 100644
--- a/packages/react-scripts/scripts/build.js
+++ b/packages/react-scripts/scripts/build.js
@@ -34,6 +34,7 @@ verifyTypeScriptSetup();
const path = require('path');
const chalk = require('react-dev-utils/chalk');
const fs = require('fs-extra');
+const bfj = require('bfj');
const webpack = require('webpack');
const configFactory = require('../config/webpack.config');
const paths = require('../config/paths');
@@ -59,6 +60,9 @@ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
process.exit(1);
}
+const argv = process.argv.slice(2);
+const writeStatsJson = argv.indexOf('--stats') !== -1;
+
// Generate configuration
const config = configFactory('production');
@@ -146,18 +150,6 @@ checkBrowsers(paths.appPath, isInteractive)
// Create the production build and print the deployment instructions.
function build(previousFileSizes) {
- // We used to support resolving modules according to `NODE_PATH`.
- // This now has been deprecated in favor of jsconfig/tsconfig.json
- // This lets you use absolute paths in imports inside large monorepos:
- if (process.env.NODE_PATH) {
- console.log(
- chalk.yellow(
- 'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
- )
- );
- console.log();
- }
-
console.log('Creating an optimized production build...');
const compiler = webpack(config);
@@ -210,11 +202,20 @@ function build(previousFileSizes) {
return reject(new Error(messages.warnings.join('\n\n')));
}
- return resolve({
+ const resolveArgs = {
stats,
previousFileSizes,
warnings: messages.warnings,
- });
+ };
+
+ if (writeStatsJson) {
+ return bfj
+ .write(paths.appBuild + '/bundle-stats.json', stats.toJson())
+ .then(() => resolve(resolveArgs))
+ .catch(error => reject(new Error(error)));
+ }
+
+ return resolve(resolveArgs);
});
});
}
diff --git a/packages/react-scripts/scripts/eject.js b/packages/react-scripts/scripts/eject.js
index 2a494ffe24f..0972d33384a 100644
--- a/packages/react-scripts/scripts/eject.js
+++ b/packages/react-scripts/scripts/eject.js
@@ -16,11 +16,11 @@ process.on('unhandledRejection', err => {
const fs = require('fs-extra');
const path = require('path');
+const prompts = require('prompts');
const execSync = require('child_process').execSync;
const chalk = require('react-dev-utils/chalk');
const paths = require('../config/paths');
const createJestConfig = require('./utils/createJestConfig');
-const inquirer = require('react-dev-utils/inquirer');
const spawnSync = require('react-dev-utils/crossSpawn').sync;
const os = require('os');
@@ -62,283 +62,279 @@ console.log(
);
console.log();
-inquirer
- .prompt({
- type: 'confirm',
- name: 'shouldEject',
- message: 'Are you sure you want to eject? This action is permanent.',
- default: false,
- })
- .then(answer => {
- if (!answer.shouldEject) {
- console.log(cyan('Close one! Eject aborted.'));
- return;
- }
+prompts({
+ type: 'confirm',
+ name: 'shouldEject',
+ message: 'Are you sure you want to eject? This action is permanent.',
+ initial: false,
+}).then(answer => {
+ if (!answer.shouldEject) {
+ console.log(cyan('Close one! Eject aborted.'));
+ return;
+ }
- const gitStatus = getGitStatus();
- if (gitStatus) {
- console.error(
+ const gitStatus = getGitStatus();
+ if (gitStatus) {
+ console.error(
+ chalk.red(
+ 'This git repository has untracked files or uncommitted changes:'
+ ) +
+ '\n\n' +
+ gitStatus
+ .split('\n')
+ .map(line => line.match(/ .*/g)[0].trim())
+ .join('\n') +
+ '\n\n' +
chalk.red(
- 'This git repository has untracked files or uncommitted changes:'
- ) +
- '\n\n' +
- gitStatus
- .split('\n')
- .map(line => line.match(/ .*/g)[0].trim())
- .join('\n') +
- '\n\n' +
- chalk.red(
- 'Remove untracked files, stash or commit any changes, and try again.'
- )
- );
- process.exit(1);
- }
+ 'Remove untracked files, stash or commit any changes, and try again.'
+ )
+ );
+ process.exit(1);
+ }
- console.log('Ejecting...');
+ console.log('Ejecting...');
- const ownPath = paths.ownPath;
- const appPath = paths.appPath;
+ const ownPath = paths.ownPath;
+ const appPath = paths.appPath;
- function verifyAbsent(file) {
- if (fs.existsSync(path.join(appPath, file))) {
- console.error(
- `\`${file}\` already exists in your app folder. We cannot ` +
- 'continue as you would lose all the changes in that file or directory. ' +
- 'Please move or delete it (maybe make a copy for backup) and run this ' +
- 'command again.'
- );
- process.exit(1);
- }
+ function verifyAbsent(file) {
+ if (fs.existsSync(path.join(appPath, file))) {
+ console.error(
+ `\`${file}\` already exists in your app folder. We cannot ` +
+ 'continue as you would lose all the changes in that file or directory. ' +
+ 'Please move or delete it (maybe make a copy for backup) and run this ' +
+ 'command again.'
+ );
+ process.exit(1);
}
+ }
- const folders = ['config', 'config/jest', 'scripts'];
-
- // Make shallow array of files paths
- const files = folders.reduce((files, folder) => {
- return files.concat(
- fs
- .readdirSync(path.join(ownPath, folder))
- // set full path
- .map(file => path.join(ownPath, folder, file))
- // omit dirs from file list
- .filter(file => fs.lstatSync(file).isFile())
- );
- }, []);
+ const folders = ['config', 'config/jest', 'scripts'];
+
+ // Make shallow array of files paths
+ const files = folders.reduce((files, folder) => {
+ return files.concat(
+ fs
+ .readdirSync(path.join(ownPath, folder))
+ // set full path
+ .map(file => path.join(ownPath, folder, file))
+ // omit dirs from file list
+ .filter(file => fs.lstatSync(file).isFile())
+ );
+ }, []);
- // Ensure that the app folder is clean and we won't override any files
- folders.forEach(verifyAbsent);
- files.forEach(verifyAbsent);
+ // Ensure that the app folder is clean and we won't override any files
+ folders.forEach(verifyAbsent);
+ files.forEach(verifyAbsent);
- // Prepare Jest config early in case it throws
- const jestConfig = createJestConfig(
- filePath => path.posix.join('', filePath),
- null,
- true
- );
+ // Prepare Jest config early in case it throws
+ const jestConfig = createJestConfig(
+ filePath => path.posix.join('', filePath),
+ null,
+ true
+ );
- console.log();
- console.log(cyan(`Copying files into ${appPath}`));
+ console.log();
+ console.log(cyan(`Copying files into ${appPath}`));
- folders.forEach(folder => {
- fs.mkdirSync(path.join(appPath, folder));
- });
+ folders.forEach(folder => {
+ fs.mkdirSync(path.join(appPath, folder));
+ });
- files.forEach(file => {
- let content = fs.readFileSync(file, 'utf8');
+ files.forEach(file => {
+ let content = fs.readFileSync(file, 'utf8');
- // Skip flagged files
- if (content.match(/\/\/ @remove-file-on-eject/)) {
+ // Skip flagged files
+ if (content.match(/\/\/ @remove-file-on-eject/)) {
+ return;
+ }
+ content =
+ content
+ // Remove dead code from .js files on eject
+ .replace(
+ /\/\/ @remove-on-eject-begin([\s\S]*?)\/\/ @remove-on-eject-end/gm,
+ ''
+ )
+ // Remove dead code from .applescript files on eject
+ .replace(
+ /-- @remove-on-eject-begin([\s\S]*?)-- @remove-on-eject-end/gm,
+ ''
+ )
+ .trim() + '\n';
+ console.log(` Adding ${cyan(file.replace(ownPath, ''))} to the project`);
+ fs.writeFileSync(file.replace(ownPath, appPath), content);
+ });
+ console.log();
+
+ const ownPackage = require(path.join(ownPath, 'package.json'));
+ const appPackage = require(path.join(appPath, 'package.json'));
+
+ console.log(cyan('Updating the dependencies'));
+ const ownPackageName = ownPackage.name;
+ if (appPackage.devDependencies) {
+ // We used to put react-scripts in devDependencies
+ if (appPackage.devDependencies[ownPackageName]) {
+ console.log(` Removing ${cyan(ownPackageName)} from devDependencies`);
+ delete appPackage.devDependencies[ownPackageName];
+ }
+ }
+ appPackage.dependencies = appPackage.dependencies || {};
+ if (appPackage.dependencies[ownPackageName]) {
+ console.log(` Removing ${cyan(ownPackageName)} from dependencies`);
+ delete appPackage.dependencies[ownPackageName];
+ }
+ Object.keys(ownPackage.dependencies).forEach(key => {
+ // For some reason optionalDependencies end up in dependencies after install
+ if (
+ ownPackage.optionalDependencies &&
+ ownPackage.optionalDependencies[key]
+ ) {
+ return;
+ }
+ console.log(` Adding ${cyan(key)} to dependencies`);
+ appPackage.dependencies[key] = ownPackage.dependencies[key];
+ });
+ // Sort the deps
+ const unsortedDependencies = appPackage.dependencies;
+ appPackage.dependencies = {};
+ Object.keys(unsortedDependencies)
+ .sort()
+ .forEach(key => {
+ appPackage.dependencies[key] = unsortedDependencies[key];
+ });
+ console.log();
+
+ console.log(cyan('Updating the scripts'));
+ delete appPackage.scripts['eject'];
+ Object.keys(appPackage.scripts).forEach(key => {
+ Object.keys(ownPackage.bin).forEach(binKey => {
+ const regex = new RegExp(binKey + ' (\\w+)', 'g');
+ if (!regex.test(appPackage.scripts[key])) {
return;
}
+ appPackage.scripts[key] = appPackage.scripts[key].replace(
+ regex,
+ 'node scripts/$1.js'
+ );
+ console.log(
+ ` Replacing ${cyan(`"${binKey} ${key}"`)} with ${cyan(
+ `"node scripts/${key}.js"`
+ )}`
+ );
+ });
+ });
+
+ console.log();
+ console.log(cyan('Configuring package.json'));
+ // Add Jest config
+ console.log(` Adding ${cyan('Jest')} configuration`);
+ appPackage.jest = jestConfig;
+
+ // Add Babel config
+ console.log(` Adding ${cyan('Babel')} preset`);
+ appPackage.babel = {
+ presets: ['react-app'],
+ };
+
+ // Add ESlint config
+ if (!appPackage.eslintConfig) {
+ console.log(` Adding ${cyan('ESLint')} configuration`);
+ appPackage.eslintConfig = {
+ extends: 'react-app',
+ };
+ }
+
+ fs.writeFileSync(
+ path.join(appPath, 'package.json'),
+ JSON.stringify(appPackage, null, 2) + os.EOL
+ );
+ console.log();
+
+ if (fs.existsSync(paths.appTypeDeclarations)) {
+ try {
+ // Read app declarations file
+ let content = fs.readFileSync(paths.appTypeDeclarations, 'utf8');
+ const ownContent =
+ fs.readFileSync(paths.ownTypeDeclarations, 'utf8').trim() + os.EOL;
+
+ // Remove react-scripts reference since they're getting a copy of the types in their project
content =
content
- // Remove dead code from .js files on eject
+ // Remove react-scripts types
.replace(
- /\/\/ @remove-on-eject-begin([\s\S]*?)\/\/ @remove-on-eject-end/gm,
+ /^\s*\/\/\/\s*.*(?:\n|$)/gm,
''
)
- // Remove dead code from .applescript files on eject
- .replace(
- /-- @remove-on-eject-begin([\s\S]*?)-- @remove-on-eject-end/gm,
- ''
- )
- .trim() + '\n';
- console.log(` Adding ${cyan(file.replace(ownPath, ''))} to the project`);
- fs.writeFileSync(file.replace(ownPath, appPath), content);
- });
- console.log();
-
- const ownPackage = require(path.join(ownPath, 'package.json'));
- const appPackage = require(path.join(appPath, 'package.json'));
+ .trim() + os.EOL;
- console.log(cyan('Updating the dependencies'));
- const ownPackageName = ownPackage.name;
- if (appPackage.devDependencies) {
- // We used to put react-scripts in devDependencies
- if (appPackage.devDependencies[ownPackageName]) {
- console.log(` Removing ${cyan(ownPackageName)} from devDependencies`);
- delete appPackage.devDependencies[ownPackageName];
- }
- }
- appPackage.dependencies = appPackage.dependencies || {};
- if (appPackage.dependencies[ownPackageName]) {
- console.log(` Removing ${cyan(ownPackageName)} from dependencies`);
- delete appPackage.dependencies[ownPackageName];
+ fs.writeFileSync(
+ paths.appTypeDeclarations,
+ (ownContent + os.EOL + content).trim() + os.EOL
+ );
+ } catch (e) {
+ // It's not essential that this succeeds, the TypeScript user should
+ // be able to re-create these types with ease.
}
- Object.keys(ownPackage.dependencies).forEach(key => {
- // For some reason optionalDependencies end up in dependencies after install
- if (
- ownPackage.optionalDependencies &&
- ownPackage.optionalDependencies[key]
- ) {
- return;
- }
- console.log(` Adding ${cyan(key)} to dependencies`);
- appPackage.dependencies[key] = ownPackage.dependencies[key];
- });
- // Sort the deps
- const unsortedDependencies = appPackage.dependencies;
- appPackage.dependencies = {};
- Object.keys(unsortedDependencies)
- .sort()
- .forEach(key => {
- appPackage.dependencies[key] = unsortedDependencies[key];
- });
- console.log();
+ }
- console.log(cyan('Updating the scripts'));
- delete appPackage.scripts['eject'];
- Object.keys(appPackage.scripts).forEach(key => {
+ // "Don't destroy what isn't ours"
+ if (ownPath.indexOf(appPath) === 0) {
+ try {
+ // remove react-scripts and react-scripts binaries from app node_modules
Object.keys(ownPackage.bin).forEach(binKey => {
- const regex = new RegExp(binKey + ' (\\w+)', 'g');
- if (!regex.test(appPackage.scripts[key])) {
- return;
- }
- appPackage.scripts[key] = appPackage.scripts[key].replace(
- regex,
- 'node scripts/$1.js'
- );
- console.log(
- ` Replacing ${cyan(`"${binKey} ${key}"`)} with ${cyan(
- `"node scripts/${key}.js"`
- )}`
- );
+ fs.removeSync(path.join(appPath, 'node_modules', '.bin', binKey));
});
- });
-
- console.log();
- console.log(cyan('Configuring package.json'));
- // Add Jest config
- console.log(` Adding ${cyan('Jest')} configuration`);
- appPackage.jest = jestConfig;
-
- // Add Babel config
- console.log(` Adding ${cyan('Babel')} preset`);
- appPackage.babel = {
- presets: ['react-app'],
- };
-
- // Add ESlint config
- if (!appPackage.eslintConfig) {
- console.log(` Adding ${cyan('ESLint')} configuration`);
- appPackage.eslintConfig = {
- extends: 'react-app',
- };
+ fs.removeSync(ownPath);
+ } catch (e) {
+ // It's not essential that this succeeds
}
+ }
- fs.writeFileSync(
- path.join(appPath, 'package.json'),
- JSON.stringify(appPackage, null, 2) + os.EOL
+ if (fs.existsSync(paths.yarnLockFile)) {
+ const windowsCmdFilePath = path.join(
+ appPath,
+ 'node_modules',
+ '.bin',
+ 'react-scripts.cmd'
);
- console.log();
-
- if (fs.existsSync(paths.appTypeDeclarations)) {
+ let windowsCmdFileContent;
+ if (process.platform === 'win32') {
+ // https://github.com/facebook/create-react-app/pull/3806#issuecomment-357781035
+ // Yarn is diligent about cleaning up after itself, but this causes the react-scripts.cmd file
+ // to be deleted while it is running. This trips Windows up after the eject completes.
+ // We'll read the batch file and later "write it back" to match npm behavior.
try {
- // Read app declarations file
- let content = fs.readFileSync(paths.appTypeDeclarations, 'utf8');
- const ownContent =
- fs.readFileSync(paths.ownTypeDeclarations, 'utf8').trim() + os.EOL;
-
- // Remove react-scripts reference since they're getting a copy of the types in their project
- content =
- content
- // Remove react-scripts types
- .replace(
- /^\s*\/\/\/\s*.*(?:\n|$)/gm,
- ''
- )
- .trim() + os.EOL;
-
- fs.writeFileSync(
- paths.appTypeDeclarations,
- (ownContent + os.EOL + content).trim() + os.EOL
- );
- } catch (e) {
- // It's not essential that this succeeds, the TypeScript user should
- // be able to re-create these types with ease.
+ windowsCmdFileContent = fs.readFileSync(windowsCmdFilePath);
+ } catch (err) {
+ // If this fails we're not worse off than if we didn't try to fix it.
}
}
- // "Don't destroy what isn't ours"
- if (ownPath.indexOf(appPath) === 0) {
+ console.log(cyan('Running yarn...'));
+ spawnSync('yarnpkg', ['--cwd', process.cwd()], { stdio: 'inherit' });
+
+ if (windowsCmdFileContent && !fs.existsSync(windowsCmdFilePath)) {
try {
- // remove react-scripts and react-scripts binaries from app node_modules
- Object.keys(ownPackage.bin).forEach(binKey => {
- fs.removeSync(path.join(appPath, 'node_modules', '.bin', binKey));
- });
- fs.removeSync(ownPath);
- } catch (e) {
- // It's not essential that this succeeds
+ fs.writeFileSync(windowsCmdFilePath, windowsCmdFileContent);
+ } catch (err) {
+ // If this fails we're not worse off than if we didn't try to fix it.
}
}
+ } else {
+ console.log(cyan('Running npm install...'));
+ spawnSync('npm', ['install', '--loglevel', 'error'], {
+ stdio: 'inherit',
+ });
+ }
+ console.log(green('Ejected successfully!'));
+ console.log();
- if (fs.existsSync(paths.yarnLockFile)) {
- const windowsCmdFilePath = path.join(
- appPath,
- 'node_modules',
- '.bin',
- 'react-scripts.cmd'
- );
- let windowsCmdFileContent;
- if (process.platform === 'win32') {
- // https://github.com/facebook/create-react-app/pull/3806#issuecomment-357781035
- // Yarn is diligent about cleaning up after itself, but this causes the react-scripts.cmd file
- // to be deleted while it is running. This trips Windows up after the eject completes.
- // We'll read the batch file and later "write it back" to match npm behavior.
- try {
- windowsCmdFileContent = fs.readFileSync(windowsCmdFilePath);
- } catch (err) {
- // If this fails we're not worse off than if we didn't try to fix it.
- }
- }
-
- console.log(cyan('Running yarn...'));
- spawnSync('yarnpkg', ['--cwd', process.cwd()], { stdio: 'inherit' });
-
- if (windowsCmdFileContent && !fs.existsSync(windowsCmdFilePath)) {
- try {
- fs.writeFileSync(windowsCmdFilePath, windowsCmdFileContent);
- } catch (err) {
- // If this fails we're not worse off than if we didn't try to fix it.
- }
- }
- } else {
- console.log(cyan('Running npm install...'));
- spawnSync('npm', ['install', '--loglevel', 'error'], {
- stdio: 'inherit',
- });
- }
- console.log(green('Ejected successfully!'));
+ if (tryGitAdd(appPath)) {
+ console.log(cyan('Staged ejected files for commit.'));
console.log();
+ }
- if (tryGitAdd(appPath)) {
- console.log(cyan('Staged ejected files for commit.'));
- console.log();
- }
-
- console.log(
- green('Please consider sharing why you ejected in this survey:')
- );
- console.log(green(' http://goo.gl/forms/Bi6CZjk1EqsdelXk1'));
- console.log();
- });
+ console.log(green('Please consider sharing why you ejected in this survey:'));
+ console.log(green(' http://goo.gl/forms/Bi6CZjk1EqsdelXk1'));
+ console.log();
+});
diff --git a/packages/react-scripts/scripts/init.js b/packages/react-scripts/scripts/init.js
index fdc4e0ae624..7b9958e8148 100644
--- a/packages/react-scripts/scripts/init.js
+++ b/packages/react-scripts/scripts/init.js
@@ -81,7 +81,7 @@ function tryGitCommit(appPath) {
}
}
-module.exports = function(
+module.exports = function (
appPath,
appName,
verbose,
@@ -103,21 +103,21 @@ module.exports = function(
'create-react-app'
)} are no longer supported.`
);
+ console.error(
+ `You can fix this by running ${chalk.cyan(
+ 'npm uninstall -g create-react-app'
+ )} or ${chalk.cyan(
+ 'yarn global remove create-react-app'
+ )} before using ${chalk.cyan('create-react-app')} again.`
+ );
return;
}
- const templatePath = path.join(
- require.resolve(templateName, { paths: [appPath] }),
- '..'
+ const templatePath = path.dirname(
+ require.resolve(`${templateName}/package.json`, { paths: [appPath] })
);
- let templateJsonPath;
- if (templateName) {
- templateJsonPath = path.join(templatePath, 'template.json');
- } else {
- // TODO: Remove support for this in v4.
- templateJsonPath = path.join(appPath, '.template.dependencies.json');
- }
+ const templateJsonPath = path.join(templatePath, 'template.json');
let templateJson = {};
if (fs.existsSync(templateJsonPath)) {
@@ -126,6 +126,25 @@ module.exports = function(
const templatePackage = templateJson.package || {};
+ // TODO: Deprecate support for root-level `dependencies` and `scripts` in v5.
+ // These should now be set under the `package` key.
+ if (templateJson.dependencies || templateJson.scripts) {
+ console.log();
+ console.log(
+ chalk.yellow(
+ 'Root-level `dependencies` and `scripts` keys in `template.json` are deprecated.\n' +
+ 'This template should be updated to use the new `package` key.'
+ )
+ );
+ console.log('For more information, visit https://cra.link/templates');
+ }
+ if (templateJson.dependencies) {
+ templatePackage.dependencies = templateJson.dependencies;
+ }
+ if (templateJson.scripts) {
+ templatePackage.scripts = templateJson.scripts;
+ }
+
// Keys to ignore in templatePackage
const templatePackageBlacklist = [
'name',
@@ -142,7 +161,6 @@ module.exports = function(
'man',
'directories',
'repository',
- 'devDependencies',
'peerDependencies',
'bundledDependencies',
'optionalDependencies',
@@ -170,8 +188,7 @@ module.exports = function(
appPackage.dependencies = appPackage.dependencies || {};
// Setup the script rules
- // TODO: deprecate 'scripts' key directly on templateJson
- const templateScripts = templatePackage.scripts || templateJson.scripts || {};
+ const templateScripts = templatePackage.scripts || {};
appPackage.scripts = Object.assign(
{
start: 'react-scripts start',
@@ -283,14 +300,15 @@ module.exports = function(
args = ['install', '--save', verbose && '--verbose'].filter(e => e);
}
- // Install additional template dependencies, if present
- // TODO: deprecate 'dependencies' key directly on templateJson
- const templateDependencies =
- templatePackage.dependencies || templateJson.dependencies;
- if (templateDependencies) {
+ // Install additional template dependencies, if present.
+ const dependenciesToInstall = Object.entries({
+ ...templatePackage.dependencies,
+ ...templatePackage.devDependencies,
+ });
+ if (dependenciesToInstall.length) {
args = args.concat(
- Object.keys(templateDependencies).map(key => {
- return `${key}@${templateDependencies[key]}`;
+ dependenciesToInstall.map(([dependency, version]) => {
+ return `${dependency}@${version}`;
})
);
}
diff --git a/packages/react-scripts/scripts/start.js b/packages/react-scripts/scripts/start.js
index 2568ab36db1..ffbb15d1204 100644
--- a/packages/react-scripts/scripts/start.js
+++ b/packages/react-scripts/scripts/start.js
@@ -44,10 +44,14 @@ const {
prepareUrls,
} = require('react-dev-utils/WebpackDevServerUtils');
const openBrowser = require('react-dev-utils/openBrowser');
+const semver = require('semver');
const paths = require('../config/paths');
const configFactory = require('../config/webpack.config');
const createDevServerConfig = require('../config/webpackDevServer.config');
+const getClientEnvironment = require('../config/env');
+const react = require(require.resolve('react', { paths: [paths.appPath] }));
+const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
const useYarn = fs.existsSync(paths.yarnLockFile);
const isInteractive = process.stdout.isTTY;
@@ -72,7 +76,7 @@ if (process.env.HOST) {
`If this was unintentional, check that you haven't mistakenly set it in your shell.`
);
console.log(
- `Learn more here: ${chalk.yellow('https://bit.ly/CRA-advanced-config')}`
+ `Learn more here: ${chalk.yellow('https://cra.link/advanced-config')}`
);
console.log();
}
@@ -95,6 +99,7 @@ checkBrowsers(paths.appPath, isInteractive)
const config = configFactory('development');
const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
const appName = require(paths.appPackageJson).name;
+
const useTypeScript = fs.existsSync(paths.appTsConfig);
const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
const urls = prepareUrls(
@@ -142,36 +147,31 @@ checkBrowsers(paths.appPath, isInteractive)
clearConsole();
}
- // We used to support resolving modules according to `NODE_PATH`.
- // This now has been deprecated in favor of jsconfig/tsconfig.json
- // This lets you use absolute paths in imports inside large monorepos:
- if (process.env.NODE_PATH) {
+ if (env.raw.FAST_REFRESH && semver.lt(react.version, '16.10.0')) {
console.log(
chalk.yellow(
- 'Setting NODE_PATH to resolve modules absolutely has been deprecated in favor of setting baseUrl in jsconfig.json (or tsconfig.json if you are using TypeScript) and will be removed in a future major release of create-react-app.'
+ `Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`
)
);
- console.log();
}
console.log(chalk.cyan('Starting the development server...\n'));
openBrowser(urls.localUrlForBrowser);
});
- ['SIGINT', 'SIGTERM'].forEach(function(sig) {
- process.on(sig, function() {
+ ['SIGINT', 'SIGTERM'].forEach(function (sig) {
+ process.on(sig, function () {
devServer.close();
process.exit();
});
});
- if (isInteractive || process.env.CI !== 'true') {
+ if (process.env.CI !== 'true') {
// Gracefully exit when stdin ends
- process.stdin.on('end', function() {
+ process.stdin.on('end', function () {
devServer.close();
process.exit();
});
- process.stdin.resume();
}
})
.catch(err => {
diff --git a/packages/react-scripts/scripts/utils/createJestConfig.js b/packages/react-scripts/scripts/utils/createJestConfig.js
index 5c17ad69a0e..ca316aab5c5 100644
--- a/packages/react-scripts/scripts/utils/createJestConfig.js
+++ b/packages/react-scripts/scripts/utils/createJestConfig.js
@@ -38,18 +38,19 @@ module.exports = (resolve, rootDir, isEjecting) => {
'/src/**/__tests__/**/*.{js,jsx,ts,tsx}',
'/src/**/*.{spec,test}.{js,jsx,ts,tsx}',
],
- testEnvironment: 'jest-environment-jsdom-fourteen',
+ testEnvironment: 'jsdom',
+ testRunner: require.resolve('jest-circus/runner'),
transform: {
- '^.+\\.(js|jsx|ts|tsx)$': isEjecting
- ? '/node_modules/babel-jest'
- : resolve('config/jest/babelTransform.js'),
+ '^.+\\.(js|jsx|mjs|cjs|ts|tsx)$': resolve(
+ 'config/jest/babelTransform.js'
+ ),
'^.+\\.css$': resolve('config/jest/cssTransform.js'),
- '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': resolve(
+ '^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)': resolve(
'config/jest/fileTransform.js'
),
},
transformIgnorePatterns: [
- '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$',
+ '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$',
'^.+\\.module\\.(css|sass|scss)$',
],
modulePaths: modules.additionalModulePaths || [],
@@ -65,6 +66,7 @@ module.exports = (resolve, rootDir, isEjecting) => {
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
],
+ resetMocks: true,
};
if (rootDir) {
config.rootDir = rootDir;
@@ -85,6 +87,7 @@ module.exports = (resolve, rootDir, isEjecting) => {
'resetModules',
'restoreMocks',
'snapshotSerializers',
+ 'testMatch',
'transform',
'transformIgnorePatterns',
'watchPathIgnorePatterns',
diff --git a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js
index ebd93d7b667..949f34ab7d2 100644
--- a/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js
+++ b/packages/react-scripts/scripts/utils/verifyTypeScriptSetup.js
@@ -14,9 +14,23 @@ const resolve = require('resolve');
const path = require('path');
const paths = require('../../config/paths');
const os = require('os');
+const semver = require('semver');
const immer = require('react-dev-utils/immer').produce;
const globby = require('react-dev-utils/globby').sync;
+const hasJsxRuntime = (() => {
+ if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
+ return false;
+ }
+
+ try {
+ require.resolve('react/jsx-runtime', { paths: [paths.appPath] });
+ return true;
+ } catch (e) {
+ return false;
+ }
+})();
+
function writeJson(fileName, object) {
fs.writeFileSync(
fileName,
@@ -59,9 +73,17 @@ function verifyTypeScriptSetup() {
// Ensure typescript is installed
let ts;
try {
+ // TODO: Remove this hack once `globalThis` issue is resolved
+ // https://github.com/jsdom/jsdom/issues/2961
+ const globalThisWasDefined = !!global.globalThis;
+
ts = require(resolve.sync('typescript', {
basedir: paths.appNodeModules,
}));
+
+ if (!globalThisWasDefined && !!global.globalThis) {
+ delete global.globalThis;
+ }
} catch (_) {
console.error(
chalk.bold.red(
@@ -106,8 +128,7 @@ function verifyTypeScriptSetup() {
allowSyntheticDefaultImports: { suggested: true },
strict: { suggested: true },
forceConsistentCasingInFileNames: { suggested: true },
- // TODO: Enable for v4.0 (#6936)
- // noFallthroughCasesInSwitch: { suggested: true },
+ noFallthroughCasesInSwitch: { suggested: true },
// These values are required and cannot be changed by the user
// Keep this in sync with the webpack config
@@ -125,8 +146,15 @@ function verifyTypeScriptSetup() {
isolatedModules: { value: true, reason: 'implementation limitation' },
noEmit: { value: true },
jsx: {
- parsedValue: ts.JsxEmit.React,
- suggested: 'react',
+ parsedValue:
+ hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta')
+ ? ts.JsxEmit.ReactJSX
+ : ts.JsxEmit.React,
+ value:
+ hasJsxRuntime && semver.gte(ts.version, '4.1.0-beta')
+ ? 'react-jsx'
+ : 'react',
+ reason: 'to support the new JSX transform in React 17',
},
paths: { value: undefined, reason: 'aliased imports are not supported' },
};
@@ -190,7 +218,7 @@ function verifyTypeScriptSetup() {
if (appTsConfig.compilerOptions == null) {
appTsConfig.compilerOptions = {};
firstTimeSetup = true;
- }
+ }
for (const option of Object.keys(compilerOptions)) {
const { parsedValue, value, suggested, reason } = compilerOptions[option];
@@ -200,7 +228,9 @@ function verifyTypeScriptSetup() {
if (suggested != null) {
if (parsedCompilerOptions[option] === undefined) {
- appTsConfig.compilerOptions[option] = suggested;
+ appTsConfig = immer(appTsConfig, config => {
+ config.compilerOptions[option] = suggested;
+ });
messages.push(
`${coloredOption} to be ${chalk.bold(
'suggested'
@@ -208,7 +238,9 @@ function verifyTypeScriptSetup() {
);
}
} else if (parsedCompilerOptions[option] !== valueToCheck) {
- appTsConfig.compilerOptions[option] = value;
+ appTsConfig = immer(appTsConfig, config => {
+ config.compilerOptions[option] = value;
+ });
messages.push(
`${coloredOption} ${chalk.bold(
valueToCheck == null ? 'must not' : 'must'
@@ -220,7 +252,9 @@ function verifyTypeScriptSetup() {
// tsconfig will have the merged "include" and "exclude" by this point
if (parsedTsConfig.include == null) {
- appTsConfig.include = ['src'];
+ appTsConfig = immer(appTsConfig, config => {
+ config.include = ['src'];
+ });
messages.push(
`${chalk.cyan('include')} should be ${chalk.cyan.bold('src')}`
);
diff --git a/packages/react-scripts/template/README.md b/packages/react-scripts/template/README.md
index 03ced3cd297..1269cf81993 100644
--- a/packages/react-scripts/template/README.md
+++ b/packages/react-scripts/template/README.md
@@ -1 +1 @@
-Learn about making a Progressive Web App [here](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
+This file has moved [here](https://github.com/facebook/create-react-app/blob/master/packages/cra-template/template/README.md)