tsdx
Version:
Zero-config TypeScript package development
113 lines (112 loc) • 6.11 kB
JavaScript
"use strict";
// largely borrowed from https://github.com/facebook/react/blob/2c8832075b05009bd261df02171bf9888ac76350/scripts/error-codes/transform-error-messages.js
/**
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const fs_1 = tslib_1.__importDefault(require("fs"));
const invertObject_1 = require("./invertObject");
const evalToString_1 = require("./evalToString");
const helper_module_imports_1 = require("@babel/helper-module-imports");
const constants_1 = require("../constants");
function transformErrorMessages(babel) {
const t = babel.types;
const DEV_EXPRESSION = t.identifier('__DEV__');
return {
visitor: {
CallExpression(path, file) {
const node = path.node;
const noMinify = file.opts.noMinify;
if (path.get('callee').isIdentifier({ name: 'invariant' })) {
// Turns this code:
//
// invariant(condition, 'A %s message that contains %s', adj, noun);
//
// into this:
//
// if (!condition) {
// if (__DEV__) {
// throw ReactError(`A ${adj} message that contains ${noun}`);
// } else {
// throw ReactErrorProd(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 `paths.appErrorsJson`.
const condition = node.arguments[0];
const errorMsgLiteral = evalToString_1.evalToString(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 }) }));
// Import ReactError
const reactErrorIdentfier = helper_module_imports_1.addDefault(path, constants_1.paths.appRoot + '/errors/ErrorDev.js', {
nameHint: 'InvariantError',
});
// Outputs:
// throw ReactError(`A ${adj} message that contains ${noun}`);
const devThrow = t.throwStatement(t.callExpression(reactErrorIdentfier, [
t.templateLiteral(errorMsgQuasis, errorMsgExpressions),
]));
if (noMinify) {
// Error minification is disabled for this build.
//
// Outputs:
// if (!condition) {
// throw ReactError(`A ${adj} message that contains ${noun}`);
// }
path.replaceWith(t.ifStatement(t.unaryExpression('!', condition), t.blockStatement([devThrow])));
return;
}
// Avoid caching because we write it as we go.
const existingErrorMap = JSON.parse(fs_1.default.readFileSync(constants_1.paths.appErrorsJson, 'utf-8'));
const errorMap = invertObject_1.invertObject(existingErrorMap);
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 ReactError(`A ${adj} message that contains ${noun}`);
// }
path.replaceWith(t.ifStatement(t.unaryExpression('!', condition), t.blockStatement([devThrow])));
path.addComment('leading', 'FIXME (minify-errors-in-prod): Unminified error message in production build!');
return;
}
prodErrorId = parseInt(prodErrorId, 10);
// Import ReactErrorProd
const reactErrorProdIdentfier = helper_module_imports_1.addDefault(path, constants_1.paths.appRoot + '/errors/ErrorProd.js', {
nameHint: 'InvariantErrorProd',
});
// Outputs:
// throw ReactErrorProd(ERR_CODE, adj, noun);
const prodThrow = t.throwStatement(t.callExpression(reactErrorProdIdentfier, [
t.numericLiteral(prodErrorId),
...errorMsgExpressions,
]));
// Outputs:
// if (!condition) {
// if (__DEV__) {
// throw ReactError(`A ${adj} message that contains ${noun}`);
// } else {
// throw ReactErrorProd(ERR_CODE, adj, noun);
// }
// }
path.replaceWith(t.ifStatement(t.unaryExpression('!', condition), t.blockStatement([
t.ifStatement(DEV_EXPRESSION, t.blockStatement([devThrow]), t.blockStatement([prodThrow])),
])));
}
},
},
};
}
exports.default = transformErrorMessages;