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'], },