react/scripts/jest/preprocessor.js
Andrew Clark 7034408ff7
Follow-up improvements to error code extraction infra (#22516)
* Output FIXME during build for unminified errors

The invariant Babel transform used to output a FIXME comment if it
could not find a matching error code. This could happen if there were
a configuration mistake that caused an unminified message to
slip through.

Linting the compiled bundles is the most reliable way to do it because
there's not a one-to-one mapping between source modules and bundles. For
example, the same source module may appear in multiple bundles, some
which are minified and others which aren't.

This updates the transform to output the same messages for Error calls.

The source lint rule is still useful for catching mistakes during
development, to prompt you to update the error codes map before pushing
the PR to CI.

* Don't run error transform in development

We used to run the error transform in both production and development,
because in development it was used to convert `invariant` calls into
throw statements.

Now that don't use `invariant` anymore, we only have to run the
transform for production builds.

* Add ! to FIXME comment so Closure doesn't strip it

Don't love this solution because Closure could change this heuristic,
or we could switch to a differnt compiler that doesn't support it. But
it works.

Could add a bundle that contains an unminified error solely for the
purpose of testing it, but that seems like overkill.

* Alternate extract-errors that scrapes artifacts

The build script outputs a special FIXME comment when it fails to minify
an error message. CI will detect these comments and fail the workflow.

The comments also include the expected error message. So I added an
alternate extract-errors that scrapes unminified messages from the
build artifacts and updates `codes.json`.

This is nice because it works on partial builds. And you can also run it
after the fact, instead of needing build all over again.

* Disable error minification in more bundles

Not worth it because the number of errors does not outweight the size
of the formatProdErrorMessage runtime.

* Run extract-errors script in CI

The lint_build job already checks for unminified errors, but the output
isn't super helpful.

Instead I've added a new job that runs the extract-errors script and
fails the build if `codes.json` changes. It also outputs the expected
diff so you can easily see which messages were missing from the map.

* Replace old extract-errors script with new one

Deletes the old extract-errors in favor of extract-errors2
2021-10-31 15:37:32 -07:00

108 lines
3.3 KiB
JavaScript

'use strict';
const path = require('path');
const babel = require('@babel/core');
const coffee = require('coffee-script');
const tsPreprocessor = require('./typescript/preprocessor');
const createCacheKeyFunction = require('fbjs-scripts/jest/createCacheKeyFunction');
const pathToBabel = path.join(
require.resolve('@babel/core'),
'../..',
'package.json'
);
const pathToBabelPluginReplaceConsoleCalls = require.resolve(
'../babel/transform-replace-console-calls'
);
const pathToBabelPluginAsyncToGenerator = require.resolve(
'@babel/plugin-transform-async-to-generator'
);
const pathToTransformInfiniteLoops = require.resolve(
'../babel/transform-prevent-infinite-loops'
);
const pathToTransformTestGatePragma = require.resolve(
'../babel/transform-test-gate-pragma'
);
const pathToBabelrc = path.join(__dirname, '..', '..', 'babel.config.js');
const pathToErrorCodes = require.resolve('../error-codes/codes.json');
const babelOptions = {
plugins: [
// For Node environment only. For builds, Rollup takes care of ESM.
require.resolve('@babel/plugin-transform-modules-commonjs'),
// Keep stacks detailed in tests.
// Don't put this in .babelrc so that we don't embed filenames
// into ReactART builds that include JSX.
// TODO: I have not verified that this actually works.
require.resolve('@babel/plugin-transform-react-jsx-source'),
pathToTransformInfiniteLoops,
pathToTransformTestGatePragma,
// This optimization is important for extremely performance-sensitive (e.g. React source).
// It's okay to disable it for tests.
[
require.resolve('@babel/plugin-transform-block-scoping'),
{throwIfClosureRequired: false},
],
],
retainLines: true,
};
module.exports = {
process: function(src, filePath) {
if (filePath.match(/\.css$/)) {
// Don't try to parse CSS modules; they aren't needed for tests anyway.
return '';
}
if (filePath.match(/\.coffee$/)) {
return coffee.compile(src, {bare: true});
}
if (filePath.match(/\.ts$/) && !filePath.match(/\.d\.ts$/)) {
return tsPreprocessor.compile(src, filePath);
}
if (filePath.match(/\.json$/)) {
return src;
}
if (!filePath.match(/\/third_party\//)) {
// for test files, we also apply the async-await transform, but we want to
// make sure we don't accidentally apply that transform to product code.
const isTestFile = !!filePath.match(/\/__tests__\//);
const isInDevToolsPackages = !!filePath.match(
/\/packages\/react-devtools.*\//
);
const testOnlyPlugins = [pathToBabelPluginAsyncToGenerator];
const sourceOnlyPlugins = [];
if (process.env.NODE_ENV === 'development' && !isInDevToolsPackages) {
sourceOnlyPlugins.push(pathToBabelPluginReplaceConsoleCalls);
}
const plugins = (isTestFile ? testOnlyPlugins : sourceOnlyPlugins).concat(
babelOptions.plugins
);
return babel.transform(
src,
Object.assign(
{filename: path.relative(process.cwd(), filePath)},
babelOptions,
{
plugins,
}
)
);
}
return src;
},
getCacheKey: createCacheKeyFunction([
__filename,
pathToBabel,
pathToBabelrc,
pathToTransformInfiniteLoops,
pathToTransformTestGatePragma,
pathToErrorCodes,
]),
};