Skip to content

Commit

Permalink
Replace old extract-errors script with new one
Browse files Browse the repository at this point in the history
Deletes the old extract-errors in favor of extract-errors2
  • Loading branch information
acdlite committed Oct 31, 2021
1 parent 24feb63 commit b2e38a9
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 187 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
5 changes: 4 additions & 1 deletion scripts/error-codes/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
143 changes: 56 additions & 87 deletions scripts/error-codes/extract-errors.js
Original file line number Diff line number Diff line change
@@ -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 '/*! <expected-error-format>' -- 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\>"(.+?)"\<\/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);
});
74 changes: 0 additions & 74 deletions scripts/error-codes/extract-errors2.js

This file was deleted.

23 changes: 1 addition & 22 deletions scripts/rollup/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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);
Expand All @@ -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.
Expand Down Expand Up @@ -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);
Expand All @@ -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();

0 comments on commit b2e38a9

Please sign in to comment.