From b2e38a94937dd653d95620caadc9ceb687daa8df Mon Sep 17 00:00:00 2001 From: Andrew Clark Date: Wed, 6 Oct 2021 18:00:32 -0400 Subject: [PATCH] Replace old extract-errors script with new one Deletes the old extract-errors in favor of extract-errors2 --- .circleci/config.yml | 2 +- package.json | 3 +- scripts/error-codes/README.md | 5 +- scripts/error-codes/extract-errors.js | 143 ++++++++++--------------- scripts/error-codes/extract-errors2.js | 74 ------------- scripts/rollup/build.js | 23 +--- 6 files changed, 63 insertions(+), 187 deletions(-) delete mode 100644 scripts/error-codes/extract-errors2.js diff --git a/.circleci/config.yml b/.circleci/config.yml index f3a2d6e9b9f66..8c847fc05f39e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -221,7 +221,7 @@ jobs: - run: name: Search build artifacts for unminified errors command: | - yarn extract-errors2 + 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: diff --git a/package.json b/package.json index 7065775e249ee..e4ea1c83be801 100644 --- a/package.json +++ b/package.json @@ -114,8 +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-errors2": "node scripts/error-codes/extract-errors2.js", + "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/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/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/extract-errors2.js b/scripts/error-codes/extract-errors2.js deleted file mode 100644 index addb095b4ca1f..0000000000000 --- a/scripts/error-codes/extract-errors2.js +++ /dev/null @@ -1,74 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const {execSync} = require('child_process'); - -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; - } - } - - console.log('Searching `build` directory for unminified errors...\n'); - - let out; - try { - out = execSync( - "git --no-pager grep -n --untracked --no-exclude-standard '/*! ' -- build" - ).toString(); - } catch (e) { - if (e.status === 1 && e.stdout.toString() === '') { - // No unminified errors found. - return; - } - throw e; - } - - 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); - - // Add to json map - if (newJSON === null) { - newJSON = Object.assign({}, originalJSON); - } - console.log(`"${nextCode}": "${message}"`); - newJSON[nextCode] = message; - nextCode += 1; - } - } while (true); - - if (newJSON) { - fs.writeFileSync( - path.resolve(__dirname, '../error-codes/codes.json'), - JSON.stringify(newJSON, null, 2) - ); - } -} - -main().catch(error => { - console.error(error); - process.exit(1); -}); diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index ea797cb2d8d0d..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', @@ -324,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); @@ -345,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. @@ -747,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); @@ -772,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();