diff --git a/.circleci/config.yml b/.circleci/config.yml
index 7d1eb1e8b3381..8c847fc05f39e 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -209,7 +209,20 @@ jobs:
- run: yarn workspaces info | head -n -1 > workspace_info.txt
- *restore_node_modules
- run: yarn lint-build
- - run: scripts/circleci/check_minified_errors.sh
+
+ check_error_codes:
+ docker: *docker
+ environment: *environment
+ steps:
+ - checkout
+ - attach_workspace: *attach_workspace
+ - run: yarn workspaces info | head -n -1 > workspace_info.txt
+ - *restore_node_modules
+ - run:
+ name: Search build artifacts for unminified errors
+ command: |
+ yarn extract-errors
+ git diff || (echo "Found unminified errors. Either update the error codes map or disable error minification for the affected build, if appropriate." && false)
yarn_test:
docker: *docker
@@ -414,6 +427,9 @@ workflows:
- yarn_lint_build:
requires:
- yarn_build_combined
+ - check_error_codes:
+ requires:
+ - yarn_build_combined
- RELEASE_CHANNEL_stable_yarn_test_dom_fixtures:
requires:
- yarn_build_combined
diff --git a/package.json b/package.json
index e7471a686ce88..e4ea1c83be801 100644
--- a/package.json
+++ b/package.json
@@ -114,7 +114,7 @@
"linc": "node ./scripts/tasks/linc.js",
"lint": "node ./scripts/tasks/eslint.js",
"lint-build": "node ./scripts/rollup/validate/index.js",
- "extract-errors": "yarn build --type=dev --extract-errors",
+ "extract-errors": "node scripts/error-codes/extract-errors.js",
"postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json && node ./scripts/flow/createFlowConfigs.js && node ./scripts/yarn/downloadReactIsForPrettyFormat.js",
"debug-test": "yarn test --deprecated 'yarn test --debug'",
"test": "node ./scripts/jest/jest-cli.js",
diff --git a/packages/shared/__tests__/ReactError-test.internal.js b/packages/shared/__tests__/ReactError-test.internal.js
index c40e62c6f391e..5576c72bf683e 100644
--- a/packages/shared/__tests__/ReactError-test.internal.js
+++ b/packages/shared/__tests__/ReactError-test.internal.js
@@ -37,6 +37,7 @@ describe('ReactError', () => {
});
// @gate build === "production"
+ // @gate !source
it('should error with minified error code', () => {
expect(() => ReactDOM.render('Hi', null)).toThrowError(
'Minified React error #200; visit ' +
diff --git a/scripts/error-codes/README.md b/scripts/error-codes/README.md
index 9933e9903d1ea..38918bd42a52e 100644
--- a/scripts/error-codes/README.md
+++ b/scripts/error-codes/README.md
@@ -9,7 +9,10 @@ provide a better debugging support in production. Check out the blog post
the file will never be changed/removed.
- [`extract-errors.js`](https://github.com/facebook/react/blob/main/scripts/error-codes/extract-errors.js)
is an node script that traverses our codebase and updates `codes.json`. You
- can test it by running `yarn extract-errors`.
+ can test it by running `yarn extract-errors`. It works by crawling the build
+ artifacts directory, so you need to have either run the build script or
+ downloaded pre-built artifacts (e.g. with `yarn download build`). It works
+ with partial builds, too.
- [`transform-error-messages`](https://github.com/facebook/react/blob/main/scripts/error-codes/transform-error-messages.js)
is a Babel pass that rewrites error messages to IDs for a production
(minified) build.
diff --git a/scripts/error-codes/__tests__/__snapshots__/transform-error-messages.js.snap b/scripts/error-codes/__tests__/__snapshots__/transform-error-messages.js.snap
index bfb80ab375562..97870a4b31b8f 100644
--- a/scripts/error-codes/__tests__/__snapshots__/transform-error-messages.js.snap
+++ b/scripts/error-codes/__tests__/__snapshots__/transform-error-messages.js.snap
@@ -2,94 +2,47 @@
exports[`error transform handles escaped backticks in template string 1`] = `
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
-Error(__DEV__ ? \\"Expected \`\\" + listener + \\"\` listener to be a function, instead got a value of \`\\" + type + \\"\` type.\\" : _formatProdErrorMessage(231, listener, type));"
+Error(_formatProdErrorMessage(231, listener, type));"
`;
-exports[`error transform should correctly transform invariants that are not in the error codes map 1`] = `
-"import invariant from 'shared/invariant';
-
-/*FIXME (minify-errors-in-prod): Unminified error message in production build!*/
-if (!condition) {
- throw Error(\\"This is not a real error message.\\");
-}"
+exports[`error transform should not touch other calls or new expressions 1`] = `
+"new NotAnError();
+NotAnError();"
`;
-exports[`error transform should handle escaped characters 1`] = `
-"import invariant from 'shared/invariant';
+exports[`error transform should output FIXME for errors that don't have a matching error code 1`] = `
+"/*! FIXME (minify-errors-in-prod): Unminified error message in production build!*/
-/*FIXME (minify-errors-in-prod): Unminified error message in production build!*/
-if (!condition) {
- throw Error(\\"What's up?\\");
-}"
+/*! \\"This is not a real error message.\\"*/
+Error('This is not a real error message.');"
`;
-exports[`error transform should not touch other calls or new expressions 1`] = `
-"new NotAnError();
-NotAnError();"
+exports[`error transform should output FIXME for errors that don't have a matching error code, unless opted out with a comment 1`] = `
+"// eslint-disable-next-line react-internal/prod-error-codes
+Error('This is not a real error message.');"
`;
exports[`error transform should replace error constructors (no new) 1`] = `
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
-Error(__DEV__ ? 'Do not override existing functions.' : _formatProdErrorMessage(16));"
+Error(_formatProdErrorMessage(16));"
`;
exports[`error transform should replace error constructors 1`] = `
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
-Error(__DEV__ ? 'Do not override existing functions.' : _formatProdErrorMessage(16));"
-`;
-
-exports[`error transform should replace simple invariant calls 1`] = `
-"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
-import invariant from 'shared/invariant';
-
-if (!condition) {
- {
- throw Error(__DEV__ ? \\"Do not override existing functions.\\" : _formatProdErrorMessage(16));
- }
-}"
+Error(_formatProdErrorMessage(16));"
`;
exports[`error transform should support error constructors with concatenated messages 1`] = `
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
-Error(__DEV__ ? \\"Expected \\" + foo + \\" target to \\" + (\\"be an array; got \\" + bar) : _formatProdErrorMessage(7, foo, bar));"
+Error(_formatProdErrorMessage(7, foo, bar));"
`;
exports[`error transform should support interpolating arguments with concatenation 1`] = `
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
-Error(__DEV__ ? 'Expected ' + foo + ' target to be an array; got ' + bar : _formatProdErrorMessage(7, foo, bar));"
+Error(_formatProdErrorMessage(7, foo, bar));"
`;
exports[`error transform should support interpolating arguments with template strings 1`] = `
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
-Error(__DEV__ ? \\"Expected \\" + foo + \\" target to be an array; got \\" + bar : _formatProdErrorMessage(7, foo, bar));"
-`;
-
-exports[`error transform should support invariant calls with a concatenated template string and args 1`] = `
-"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
-import invariant from 'shared/invariant';
-
-if (!condition) {
- {
- throw Error(__DEV__ ? \\"Expected a component class, got \\" + Foo + \\".\\" + Bar : _formatProdErrorMessage(18, Foo, Bar));
- }
-}"
-`;
-
-exports[`error transform should support invariant calls with args 1`] = `
-"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
-import invariant from 'shared/invariant';
-
-if (!condition) {
- {
- throw Error(__DEV__ ? \\"Expected \\" + foo + \\" target to be an array; got \\" + bar : _formatProdErrorMessage(7, foo, bar));
- }
-}"
-`;
-
-exports[`error transform should support noMinify option 1`] = `
-"import invariant from 'shared/invariant';
-
-if (!condition) {
- throw Error(\\"Do not override existing functions.\\");
-}"
+Error(_formatProdErrorMessage(7, foo, bar));"
`;
diff --git a/scripts/error-codes/__tests__/transform-error-messages.js b/scripts/error-codes/__tests__/transform-error-messages.js
index 2bca7366321e2..3cf08b69e848f 100644
--- a/scripts/error-codes/__tests__/transform-error-messages.js
+++ b/scripts/error-codes/__tests__/transform-error-messages.js
@@ -28,87 +28,46 @@ describe('error transform', () => {
process.env.NODE_ENV = oldEnv;
});
- it('should replace simple invariant calls', () => {
- expect(
- transform(`
-import invariant from 'shared/invariant';
-invariant(condition, 'Do not override existing functions.');
-`)
- ).toMatchSnapshot();
- });
-
- it('should throw if invariant is not in an expression statement', () => {
- expect(() => {
- transform(`
-import invariant from 'shared/invariant';
-cond && invariant(condition, 'Do not override existing functions.');
-`);
- }).toThrow('invariant() cannot be called from expression context');
- });
-
- it('should support invariant calls with args', () => {
- expect(
- transform(`
-import invariant from 'shared/invariant';
-invariant(condition, 'Expected %s target to be an array; got %s', foo, bar);
-`)
- ).toMatchSnapshot();
- });
-
- it('should support invariant calls with a concatenated template string and args', () => {
- expect(
- transform(`
-import invariant from 'shared/invariant';
-invariant(condition, 'Expected a component class, ' + 'got %s.' + '%s', Foo, Bar);
-`)
- ).toMatchSnapshot();
- });
-
- it('should correctly transform invariants that are not in the error codes map', () => {
+ it('should replace error constructors', () => {
expect(
transform(`
-import invariant from 'shared/invariant';
-invariant(condition, 'This is not a real error message.');
+new Error('Do not override existing functions.');
`)
).toMatchSnapshot();
});
- it('should handle escaped characters', () => {
+ it('should replace error constructors (no new)', () => {
expect(
transform(`
-import invariant from 'shared/invariant';
-invariant(condition, 'What\\'s up?');
+Error('Do not override existing functions.');
`)
).toMatchSnapshot();
});
- it('should support noMinify option', () => {
- expect(
- transform(
- `
-import invariant from 'shared/invariant';
-invariant(condition, 'Do not override existing functions.');
-`,
- {noMinify: true}
- )
- ).toMatchSnapshot();
- });
-
- it('should replace error constructors', () => {
+ it("should output FIXME for errors that don't have a matching error code", () => {
expect(
transform(`
-new Error('Do not override existing functions.');
+Error('This is not a real error message.');
`)
).toMatchSnapshot();
});
- it('should replace error constructors (no new)', () => {
- expect(
- transform(`
-Error('Do not override existing functions.');
+ it(
+ "should output FIXME for errors that don't have a matching error " +
+ 'code, unless opted out with a comment',
+ () => {
+ // TODO: Since this only detects one of many ways to disable a lint
+ // rule, we should instead search for a custom directive (like
+ // no-minify-errors) instead of ESLint. Will need to update our lint
+ // rule to recognize the same directive.
+ expect(
+ transform(`
+// eslint-disable-next-line react-internal/prod-error-codes
+Error('This is not a real error message.');
`)
- ).toMatchSnapshot();
- });
+ ).toMatchSnapshot();
+ }
+ );
it('should not touch other calls or new expressions', () => {
expect(
diff --git a/scripts/error-codes/extract-errors.js b/scripts/error-codes/extract-errors.js
index d60ffe308cdbe..addb095b4ca1f 100644
--- a/scripts/error-codes/extract-errors.js
+++ b/scripts/error-codes/extract-errors.js
@@ -1,105 +1,74 @@
-/**
- * Copyright (c) Facebook, Inc. and its affiliates.
- *
- * This source code is licensed under the MIT license found in the
- * LICENSE file in the root directory of this source tree.
- */
'use strict';
-const parser = require('@babel/parser');
const fs = require('fs');
const path = require('path');
-const traverse = require('@babel/traverse').default;
-const {evalStringConcat} = require('../shared/evalToString');
-const invertObject = require('./invertObject');
+const {execSync} = require('child_process');
-const babylonOptions = {
- sourceType: 'module',
- // As a parser, babylon has its own options and we can't directly
- // import/require a babel preset. It should be kept **the same** as
- // the `babel-plugin-syntax-*` ones specified in
- // https://github.com/facebook/fbjs/blob/master/packages/babel-preset-fbjs/configure.js
- plugins: [
- 'classProperties',
- 'flow',
- 'jsx',
- 'trailingFunctionCommas',
- 'objectRestSpread',
- ],
-};
-
-module.exports = function(opts) {
- if (!opts || !('errorMapFilePath' in opts)) {
- throw new Error(
- 'Missing options. Ensure you pass an object with `errorMapFilePath`.'
- );
+async function main() {
+ const originalJSON = JSON.parse(
+ fs.readFileSync(path.resolve(__dirname, '../error-codes/codes.json'))
+ );
+ const existingMessages = new Set();
+ const codes = Object.keys(originalJSON);
+ let nextCode = 0;
+ for (let i = 0; i < codes.length; i++) {
+ const codeStr = codes[i];
+ const message = originalJSON[codeStr];
+ const code = parseInt(codeStr, 10);
+ existingMessages.add(message);
+ if (code >= nextCode) {
+ nextCode = code + 1;
+ }
}
- const errorMapFilePath = opts.errorMapFilePath;
- let existingErrorMap;
+ console.log('Searching `build` directory for unminified errors...\n');
+
+ let out;
try {
- // Using `fs.readFileSync` instead of `require` here, because `require()`
- // calls are cached, and the cache map is not properly invalidated after
- // file changes.
- existingErrorMap = JSON.parse(
- fs.readFileSync(
- path.join(__dirname, path.basename(errorMapFilePath)),
- 'utf8'
- )
- );
+ out = execSync(
+ "git --no-pager grep -n --untracked --no-exclude-standard '/*! ' -- build"
+ ).toString();
} catch (e) {
- existingErrorMap = {};
- }
-
- const allErrorIDs = Object.keys(existingErrorMap);
- let currentID;
-
- if (allErrorIDs.length === 0) {
- // Map is empty
- currentID = 0;
- } else {
- currentID = Math.max.apply(null, allErrorIDs) + 1;
+ if (e.status === 1 && e.stdout.toString() === '') {
+ // No unminified errors found.
+ return;
+ }
+ throw e;
}
- // Here we invert the map object in memory for faster error code lookup
- existingErrorMap = invertObject(existingErrorMap);
-
- function transform(source) {
- const ast = parser.parse(source, babylonOptions);
-
- traverse(ast, {
- CallExpression: {
- exit(astPath) {
- if (astPath.get('callee').isIdentifier({name: 'invariant'})) {
- const node = astPath.node;
+ let newJSON = null;
+ const regex = /\"(.+?)"\<\/expected-error-format\>/g;
+ do {
+ const match = regex.exec(out);
+ if (match === null) {
+ break;
+ } else {
+ const message = match[1].trim();
+ if (existingMessages.has(message)) {
+ // This probably means you ran the script twice.
+ continue;
+ }
+ existingMessages.add(message);
- // error messages can be concatenated (`+`) at runtime, so here's a
- // trivial partial evaluator that interprets the literal value
- const errorMsgLiteral = evalStringConcat(node.arguments[1]);
- addToErrorMap(errorMsgLiteral);
- }
- },
- },
- });
- }
-
- function addToErrorMap(errorMsgLiteral) {
- if (existingErrorMap.hasOwnProperty(errorMsgLiteral)) {
- return;
+ // Add to json map
+ if (newJSON === null) {
+ newJSON = Object.assign({}, originalJSON);
+ }
+ console.log(`"${nextCode}": "${message}"`);
+ newJSON[nextCode] = message;
+ nextCode += 1;
}
- existingErrorMap[errorMsgLiteral] = '' + currentID++;
- }
+ } while (true);
- function flush(cb) {
+ if (newJSON) {
fs.writeFileSync(
- errorMapFilePath,
- JSON.stringify(invertObject(existingErrorMap), null, 2) + '\n',
- 'utf-8'
+ path.resolve(__dirname, '../error-codes/codes.json'),
+ JSON.stringify(newJSON, null, 2)
);
}
+}
- return function extractErrors(source) {
- transform(source);
- flush();
- };
-};
+main().catch(error => {
+ console.error(error);
+ process.exit(1);
+});
diff --git a/scripts/error-codes/transform-error-messages.js b/scripts/error-codes/transform-error-messages.js
index 2baf4baa1c1a7..a429ed4008b68 100644
--- a/scripts/error-codes/transform-error-messages.js
+++ b/scripts/error-codes/transform-error-messages.js
@@ -7,10 +7,7 @@
'use strict';
const fs = require('fs');
-const {
- evalStringConcat,
- evalStringAndTemplateConcat,
-} = require('../shared/evalToString');
+const {evalStringAndTemplateConcat} = require('../shared/evalToString');
const invertObject = require('./invertObject');
const helperModuleImports = require('@babel/helper-module-imports');
@@ -23,11 +20,7 @@ const SEEN_SYMBOL = Symbol('transform-error-messages.seen');
module.exports = function(babel) {
const t = babel.types;
- // TODO: Instead of outputting __DEV__ conditions, only apply this transform
- // in production.
- const DEV_EXPRESSION = t.identifier('__DEV__');
-
- function CallOrNewExpression(path, file) {
+ function ErrorCallExpression(path, file) {
// Turns this code:
//
// new Error(`A ${adj} message that contains ${noun}`);
@@ -38,11 +31,7 @@ module.exports = function(babel) {
//
// into this:
//
- // Error(
- // __DEV__
- // ? `A ${adj} message that contains ${noun}`
- // : formatProdErrorMessage(ERR_CODE, adj, noun)
- // );
+ // Error(formatProdErrorMessage(ERR_CODE, adj, noun));
const node = path.node;
if (node[SEEN_SYMBOL]) {
return;
@@ -62,9 +51,44 @@ module.exports = function(babel) {
let prodErrorId = errorMap[errorMsgLiteral];
if (prodErrorId === undefined) {
- // There is no error code for this message. We use a lint rule to
- // enforce that messages can be minified, so assume this is
- // intentional and exit gracefully.
+ // There is no error code for this message. Add an inline comment
+ // that flags this as an unminified error. This allows the build
+ // to proceed, while also allowing a post-build linter to detect it.
+ //
+ // Outputs:
+ // /* FIXME (minify-errors-in-prod): Unminified error message in production build! */
+ // /* "A % message that contains %" */
+ // if (!condition) {
+ // throw Error(`A ${adj} message that contains ${noun}`);
+ // }
+
+ const statementParent = path.getStatementParent();
+ const leadingComments = statementParent.node.leadingComments;
+ if (leadingComments !== undefined) {
+ for (let i = 0; i < leadingComments.length; i++) {
+ // TODO: Since this only detects one of many ways to disable a lint
+ // rule, we should instead search for a custom directive (like
+ // no-minify-errors) instead of ESLint. Will need to update our lint
+ // rule to recognize the same directive.
+ const commentText = leadingComments[i].value;
+ if (
+ commentText.includes(
+ 'eslint-disable-next-line react-internal/prod-error-codes'
+ )
+ ) {
+ return;
+ }
+ }
+ }
+
+ statementParent.addComment(
+ 'leading',
+ `! "${errorMsgLiteral}"`
+ );
+ statementParent.addComment(
+ 'leading',
+ '! FIXME (minify-errors-in-prod): Unminified error message in production build!'
+ );
return;
}
prodErrorId = parseInt(prodErrorId, 10);
@@ -84,168 +108,25 @@ module.exports = function(babel) {
]);
// Outputs:
- // Error(
- // __DEV__
- // ? `A ${adj} message that contains ${noun}`
- // : formatProdErrorMessage(ERR_CODE, adj, noun)
- // );
- path.replaceWith(t.callExpression(t.identifier('Error'), [prodMessage]));
- path.replaceWith(
- t.callExpression(t.identifier('Error'), [
- t.conditionalExpression(DEV_EXPRESSION, errorMsgNode, prodMessage),
- ])
- );
+ // Error(formatProdErrorMessage(ERR_CODE, adj, noun));
+ const newErrorCall = t.callExpression(t.identifier('Error'), [prodMessage]);
+ newErrorCall[SEEN_SYMBOL] = true;
+ path.replaceWith(newErrorCall);
}
return {
visitor: {
NewExpression(path, file) {
- const noMinify = file.opts.noMinify;
- if (!noMinify && path.get('callee').isIdentifier({name: 'Error'})) {
- CallOrNewExpression(path, file);
+ if (path.get('callee').isIdentifier({name: 'Error'})) {
+ ErrorCallExpression(path, file);
}
},
CallExpression(path, file) {
- const node = path.node;
- const noMinify = file.opts.noMinify;
-
- if (!noMinify && path.get('callee').isIdentifier({name: 'Error'})) {
- CallOrNewExpression(path, file);
+ if (path.get('callee').isIdentifier({name: 'Error'})) {
+ ErrorCallExpression(path, file);
return;
}
-
- if (path.get('callee').isIdentifier({name: 'invariant'})) {
- // Turns this code:
- //
- // invariant(condition, 'A %s message that contains %s', adj, noun);
- //
- // into this:
- //
- // if (!condition) {
- // throw Error(
- // __DEV__
- // ? `A ${adj} message that contains ${noun}`
- // : formatProdErrorMessage(ERR_CODE, adj, noun)
- // );
- // }
- //
- // where ERR_CODE is an error code: a unique identifier (a number
- // string) that references a verbose error message. The mapping is
- // stored in `scripts/error-codes/codes.json`.
- const condition = node.arguments[0];
- const errorMsgLiteral = evalStringConcat(node.arguments[1]);
- const errorMsgExpressions = Array.from(node.arguments.slice(2));
- const errorMsgQuasis = errorMsgLiteral
- .split('%s')
- .map(raw => t.templateElement({raw, cooked: String.raw({raw})}));
-
- // Outputs:
- // `A ${adj} message that contains ${noun}`;
- const devMessage = t.templateLiteral(
- errorMsgQuasis,
- errorMsgExpressions
- );
-
- const parentStatementPath = path.parentPath;
- if (parentStatementPath.type !== 'ExpressionStatement') {
- throw path.buildCodeFrameError(
- 'invariant() cannot be called from expression context. Move ' +
- 'the call to its own statement.'
- );
- }
-
- if (noMinify) {
- // Error minification is disabled for this build.
- //
- // Outputs:
- // if (!condition) {
- // throw Error(`A ${adj} message that contains ${noun}`);
- // }
- parentStatementPath.replaceWith(
- t.ifStatement(
- t.unaryExpression('!', condition),
- t.blockStatement([
- t.throwStatement(
- t.callExpression(t.identifier('Error'), [devMessage])
- ),
- ])
- )
- );
- return;
- }
-
- let prodErrorId = errorMap[errorMsgLiteral];
-
- if (prodErrorId === undefined) {
- // There is no error code for this message. Add an inline comment
- // that flags this as an unminified error. This allows the build
- // to proceed, while also allowing a post-build linter to detect it.
- //
- // Outputs:
- // /* FIXME (minify-errors-in-prod): Unminified error message in production build! */
- // if (!condition) {
- // throw Error(`A ${adj} message that contains ${noun}`);
- // }
- parentStatementPath.replaceWith(
- t.ifStatement(
- t.unaryExpression('!', condition),
- t.blockStatement([
- t.throwStatement(
- t.callExpression(t.identifier('Error'), [devMessage])
- ),
- ])
- )
- );
- parentStatementPath.addComment(
- 'leading',
- 'FIXME (minify-errors-in-prod): Unminified error message in production build!'
- );
- return;
- }
- prodErrorId = parseInt(prodErrorId, 10);
-
- // Import formatProdErrorMessage
- const formatProdErrorMessageIdentifier = helperModuleImports.addDefault(
- path,
- 'shared/formatProdErrorMessage',
- {nameHint: 'formatProdErrorMessage'}
- );
-
- // Outputs:
- // formatProdErrorMessage(ERR_CODE, adj, noun);
- const prodMessage = t.callExpression(
- formatProdErrorMessageIdentifier,
- [t.numericLiteral(prodErrorId), ...errorMsgExpressions]
- );
-
- // Outputs:
- // if (!condition) {
- // throw Error(
- // __DEV__
- // ? `A ${adj} message that contains ${noun}`
- // : formatProdErrorMessage(ERR_CODE, adj, noun)
- // );
- // }
- parentStatementPath.replaceWith(
- t.ifStatement(
- t.unaryExpression('!', condition),
- t.blockStatement([
- t.blockStatement([
- t.throwStatement(
- t.callExpression(t.identifier('Error'), [
- t.conditionalExpression(
- DEV_EXPRESSION,
- devMessage,
- prodMessage
- ),
- ])
- ),
- ]),
- ])
- )
- );
- }
},
},
};
diff --git a/scripts/jest/preprocessor.js b/scripts/jest/preprocessor.js
index 072071be07fbd..d7a5a2cdab6da 100644
--- a/scripts/jest/preprocessor.js
+++ b/scripts/jest/preprocessor.js
@@ -13,9 +13,6 @@ const pathToBabel = path.join(
'../..',
'package.json'
);
-const pathToBabelPluginDevWithCode = require.resolve(
- '../error-codes/transform-error-messages'
-);
const pathToBabelPluginReplaceConsoleCalls = require.resolve(
'../babel/transform-replace-console-calls'
);
@@ -36,8 +33,6 @@ const babelOptions = {
// For Node environment only. For builds, Rollup takes care of ESM.
require.resolve('@babel/plugin-transform-modules-commonjs'),
- pathToBabelPluginDevWithCode,
-
// Keep stacks detailed in tests.
// Don't put this in .babelrc so that we don't embed filenames
// into ReactART builds that include JSX.
@@ -105,7 +100,6 @@ module.exports = {
__filename,
pathToBabel,
pathToBabelrc,
- pathToBabelPluginDevWithCode,
pathToTransformInfiniteLoops,
pathToTransformTestGatePragma,
pathToErrorCodes,
diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js
index 7e29c0e8c54d3..48401c002b431 100644
--- a/scripts/rollup/build.js
+++ b/scripts/rollup/build.js
@@ -19,7 +19,6 @@ const Sync = require('./sync');
const sizes = require('./plugins/sizes-plugin');
const useForks = require('./plugins/use-forks-plugin');
const stripUnusedImports = require('./plugins/strip-unused-imports');
-const extractErrorCodes = require('../error-codes/extract-errors');
const Packaging = require('./packaging');
const {asyncRimRaf} = require('./utils');
const codeFrame = require('babel-code-frame');
@@ -94,10 +93,6 @@ const forcePrettyOutput = argv.pretty;
const isWatchMode = argv.watch;
const syncFBSourcePath = argv['sync-fbsource'];
const syncWWWPath = argv['sync-www'];
-const shouldExtractErrors = argv['extract-errors'];
-const errorCodeOpts = {
- errorMapFilePath: 'scripts/error-codes/codes.json',
-};
const closureOptions = {
compilation_level: 'SIMPLE',
@@ -176,26 +171,13 @@ function getBabelConfig(
if (updateBabelOptions) {
options = updateBabelOptions(options);
}
+ // Controls whether to replace error messages with error codes in production.
+ // By default, error messages are replaced in production.
+ if (!isDevelopment && bundle.minifyWithProdErrorCodes !== false) {
+ options.plugins.push(require('../error-codes/transform-error-messages'));
+ }
+
switch (bundleType) {
- case FB_WWW_DEV:
- case FB_WWW_PROD:
- case FB_WWW_PROFILING:
- case RN_OSS_DEV:
- case RN_OSS_PROD:
- case RN_OSS_PROFILING:
- case RN_FB_DEV:
- case RN_FB_PROD:
- case RN_FB_PROFILING:
- return Object.assign({}, options, {
- plugins: options.plugins.concat([
- [
- require('../error-codes/transform-error-messages'),
- // Controls whether to replace error messages with error codes
- // in production. By default, error messages are replaced.
- {noMinify: bundle.minifyWithProdErrorCodes === false},
- ],
- ]),
- });
case UMD_DEV:
case UMD_PROD:
case UMD_PROFILING:
@@ -206,8 +188,6 @@ function getBabelConfig(
plugins: options.plugins.concat([
// Use object-assign polyfill in open source
path.resolve('./scripts/babel/transform-object-assign-require'),
- // Minify invariant messages
- require('../error-codes/transform-error-messages'),
]),
});
default:
@@ -339,7 +319,6 @@ function getPlugins(
pureExternalModules,
bundle
) {
- const findAndRecordErrorCodes = extractErrorCodes(errorCodeOpts);
const forks = Modules.getForks(bundleType, entry, moduleType, bundle);
const isProduction = isProductionBundleType(bundleType);
const isProfiling = isProfilingBundleType(bundleType);
@@ -360,13 +339,6 @@ function getPlugins(
bundleType === RN_FB_PROFILING;
const shouldStayReadable = isFBWWWBundle || isRNBundle || forcePrettyOutput;
return [
- // Extract error codes from invariant() messages into a file.
- shouldExtractErrors && {
- transform(source) {
- findAndRecordErrorCodes(source);
- return source;
- },
- },
// Shim any modules that need forking in this environment.
useForks(forks),
// Ensure we don't try to bundle any fbjs modules.
@@ -762,7 +734,7 @@ async function buildEverything() {
);
}
- if (!shouldExtractErrors && process.env.CIRCLE_NODE_TOTAL) {
+ if (process.env.CIRCLE_NODE_TOTAL) {
// In CI, parallelize bundles across multiple tasks.
const nodeTotal = parseInt(process.env.CIRCLE_NODE_TOTAL, 10);
const nodeIndex = parseInt(process.env.CIRCLE_NODE_INDEX, 10);
@@ -787,14 +759,6 @@ async function buildEverything() {
if (!forcePrettyOutput) {
Stats.saveResults();
}
-
- if (shouldExtractErrors) {
- console.warn(
- '\nWarning: this build was created with --extract-errors enabled.\n' +
- 'this will result in extremely slow builds and should only be\n' +
- 'used when the error map needs to be rebuilt.\n'
- );
- }
}
buildEverything();
diff --git a/scripts/rollup/bundles.js b/scripts/rollup/bundles.js
index af7b30b7feb99..40ae43ae7cbc0 100644
--- a/scripts/rollup/bundles.js
+++ b/scripts/rollup/bundles.js
@@ -141,7 +141,7 @@ const bundles = [
moduleType: ISOMORPHIC,
entry: 'react-fetch/index.browser',
global: 'ReactFetch',
- minifyWithProdErrorCodes: true,
+ minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,
externals: ['react'],
},
@@ -163,7 +163,7 @@ const bundles = [
moduleType: ISOMORPHIC,
entry: 'react-fs/index.browser.server',
global: 'ReactFilesystem',
- minifyWithProdErrorCodes: true,
+ minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,
externals: [],
},
@@ -185,7 +185,7 @@ const bundles = [
moduleType: ISOMORPHIC,
entry: 'react-pg/index.browser.server',
global: 'ReactPostgres',
- minifyWithProdErrorCodes: true,
+ minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,
externals: [],
},
@@ -349,7 +349,7 @@ const bundles = [
moduleType: RENDERER,
entry: 'react-server-dom-webpack',
global: 'ReactServerDOMReader',
- minifyWithProdErrorCodes: true,
+ minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,
externals: ['react'],
},
@@ -594,7 +594,7 @@ const bundles = [
moduleType: RENDERER,
entry: 'react-noop-renderer',
global: 'ReactNoopRenderer',
- minifyWithProdErrorCodes: true,
+ minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,
externals: ['react', 'scheduler', 'scheduler/unstable_mock', 'expect'],
},
@@ -605,7 +605,7 @@ const bundles = [
moduleType: RENDERER,
entry: 'react-noop-renderer/persistent',
global: 'ReactNoopRendererPersistent',
- minifyWithProdErrorCodes: true,
+ minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,
externals: ['react', 'scheduler', 'expect'],
},
@@ -616,7 +616,7 @@ const bundles = [
moduleType: RENDERER,
entry: 'react-noop-renderer/server',
global: 'ReactNoopRendererServer',
- minifyWithProdErrorCodes: true,
+ minifyWithProdErrorCodes: false,
wrapWithModuleBoundaries: false,
externals: ['react', 'scheduler', 'expect'],
},