Skip to content

Commit f36a740

Browse files
committed
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.
1 parent 5b85a39 commit f36a740

File tree

4 files changed

+12
-287
lines changed

4 files changed

+12
-287
lines changed

scripts/error-codes/__tests__/__snapshots__/transform-error-messages.js.snap

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,6 @@ exports[`error transform handles escaped backticks in template string 1`] = `
55
Error(__DEV__ ? \\"Expected \`\\" + listener + \\"\` listener to be a function, instead got a value of \`\\" + type + \\"\` type.\\" : _formatProdErrorMessage(231, listener, type));"
66
`;
77

8-
exports[`error transform should correctly transform invariants that are not in the error codes map 1`] = `
9-
"import invariant from 'shared/invariant';
10-
11-
/*FIXME (minify-errors-in-prod): Unminified error message in production build!*/
12-
if (!condition) {
13-
throw Error(\\"This is not a real error message.\\");
14-
}"
15-
`;
16-
17-
exports[`error transform should handle escaped characters 1`] = `
18-
"import invariant from 'shared/invariant';
19-
20-
/*FIXME (minify-errors-in-prod): Unminified error message in production build!*/
21-
if (!condition) {
22-
throw Error(\\"What's up?\\");
23-
}"
24-
`;
25-
268
exports[`error transform should not touch other calls or new expressions 1`] = `
279
"new NotAnError();
2810
NotAnError();"
@@ -48,17 +30,6 @@ exports[`error transform should replace error constructors 1`] = `
4830
Error(__DEV__ ? 'Do not override existing functions.' : _formatProdErrorMessage(16));"
4931
`;
5032

51-
exports[`error transform should replace simple invariant calls 1`] = `
52-
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
53-
import invariant from 'shared/invariant';
54-
55-
if (!condition) {
56-
{
57-
throw Error(__DEV__ ? \\"Do not override existing functions.\\" : _formatProdErrorMessage(16));
58-
}
59-
}"
60-
`;
61-
6233
exports[`error transform should support error constructors with concatenated messages 1`] = `
6334
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
6435
Error(__DEV__ ? \\"Expected \\" + foo + \\" target to \\" + (\\"be an array; got \\" + bar) : _formatProdErrorMessage(7, foo, bar));"
@@ -73,33 +44,3 @@ exports[`error transform should support interpolating arguments with template st
7344
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
7445
Error(__DEV__ ? \\"Expected \\" + foo + \\" target to be an array; got \\" + bar : _formatProdErrorMessage(7, foo, bar));"
7546
`;
76-
77-
exports[`error transform should support invariant calls with a concatenated template string and args 1`] = `
78-
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
79-
import invariant from 'shared/invariant';
80-
81-
if (!condition) {
82-
{
83-
throw Error(__DEV__ ? \\"Expected a component class, got \\" + Foo + \\".\\" + Bar : _formatProdErrorMessage(18, Foo, Bar));
84-
}
85-
}"
86-
`;
87-
88-
exports[`error transform should support invariant calls with args 1`] = `
89-
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
90-
import invariant from 'shared/invariant';
91-
92-
if (!condition) {
93-
{
94-
throw Error(__DEV__ ? \\"Expected \\" + foo + \\" target to be an array; got \\" + bar : _formatProdErrorMessage(7, foo, bar));
95-
}
96-
}"
97-
`;
98-
99-
exports[`error transform should support noMinify option 1`] = `
100-
"import invariant from 'shared/invariant';
101-
102-
if (!condition) {
103-
throw Error(\\"Do not override existing functions.\\");
104-
}"
105-
`;

scripts/error-codes/__tests__/transform-error-messages.js

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -28,72 +28,6 @@ describe('error transform', () => {
2828
process.env.NODE_ENV = oldEnv;
2929
});
3030

31-
it('should replace simple invariant calls', () => {
32-
expect(
33-
transform(`
34-
import invariant from 'shared/invariant';
35-
invariant(condition, 'Do not override existing functions.');
36-
`)
37-
).toMatchSnapshot();
38-
});
39-
40-
it('should throw if invariant is not in an expression statement', () => {
41-
expect(() => {
42-
transform(`
43-
import invariant from 'shared/invariant';
44-
cond && invariant(condition, 'Do not override existing functions.');
45-
`);
46-
}).toThrow('invariant() cannot be called from expression context');
47-
});
48-
49-
it('should support invariant calls with args', () => {
50-
expect(
51-
transform(`
52-
import invariant from 'shared/invariant';
53-
invariant(condition, 'Expected %s target to be an array; got %s', foo, bar);
54-
`)
55-
).toMatchSnapshot();
56-
});
57-
58-
it('should support invariant calls with a concatenated template string and args', () => {
59-
expect(
60-
transform(`
61-
import invariant from 'shared/invariant';
62-
invariant(condition, 'Expected a component class, ' + 'got %s.' + '%s', Foo, Bar);
63-
`)
64-
).toMatchSnapshot();
65-
});
66-
67-
it('should correctly transform invariants that are not in the error codes map', () => {
68-
expect(
69-
transform(`
70-
import invariant from 'shared/invariant';
71-
invariant(condition, 'This is not a real error message.');
72-
`)
73-
).toMatchSnapshot();
74-
});
75-
76-
it('should handle escaped characters', () => {
77-
expect(
78-
transform(`
79-
import invariant from 'shared/invariant';
80-
invariant(condition, 'What\\'s up?');
81-
`)
82-
).toMatchSnapshot();
83-
});
84-
85-
it('should support noMinify option', () => {
86-
expect(
87-
transform(
88-
`
89-
import invariant from 'shared/invariant';
90-
invariant(condition, 'Do not override existing functions.');
91-
`,
92-
{noMinify: true}
93-
)
94-
).toMatchSnapshot();
95-
});
96-
9731
it('should replace error constructors', () => {
9832
expect(
9933
transform(`

scripts/error-codes/transform-error-messages.js

Lines changed: 6 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,7 @@
77
'use strict';
88

99
const fs = require('fs');
10-
const {
11-
evalStringConcat,
12-
evalStringAndTemplateConcat,
13-
} = require('../shared/evalToString');
10+
const {evalStringAndTemplateConcat} = require('../shared/evalToString');
1411
const invertObject = require('./invertObject');
1512
const helperModuleImports = require('@babel/helper-module-imports');
1613

@@ -27,7 +24,7 @@ module.exports = function(babel) {
2724
// in production.
2825
const DEV_EXPRESSION = t.identifier('__DEV__');
2926

30-
function CallOrNewExpression(path, file) {
27+
function ErrorCallExpression(path, file) {
3128
// Turns this code:
3229
//
3330
// new Error(`A ${adj} message that contains ${noun}`);
@@ -129,148 +126,16 @@ module.exports = function(babel) {
129126
return {
130127
visitor: {
131128
NewExpression(path, file) {
132-
const noMinify = file.opts.noMinify;
133-
if (!noMinify && path.get('callee').isIdentifier({name: 'Error'})) {
134-
CallOrNewExpression(path, file);
129+
if (path.get('callee').isIdentifier({name: 'Error'})) {
130+
ErrorCallExpression(path, file);
135131
}
136132
},
137133

138134
CallExpression(path, file) {
139-
const node = path.node;
140-
const noMinify = file.opts.noMinify;
141-
142-
if (!noMinify && path.get('callee').isIdentifier({name: 'Error'})) {
143-
CallOrNewExpression(path, file);
135+
if (path.get('callee').isIdentifier({name: 'Error'})) {
136+
ErrorCallExpression(path, file);
144137
return;
145138
}
146-
147-
if (path.get('callee').isIdentifier({name: 'invariant'})) {
148-
// Turns this code:
149-
//
150-
// invariant(condition, 'A %s message that contains %s', adj, noun);
151-
//
152-
// into this:
153-
//
154-
// if (!condition) {
155-
// throw Error(
156-
// __DEV__
157-
// ? `A ${adj} message that contains ${noun}`
158-
// : formatProdErrorMessage(ERR_CODE, adj, noun)
159-
// );
160-
// }
161-
//
162-
// where ERR_CODE is an error code: a unique identifier (a number
163-
// string) that references a verbose error message. The mapping is
164-
// stored in `scripts/error-codes/codes.json`.
165-
const condition = node.arguments[0];
166-
const errorMsgLiteral = evalStringConcat(node.arguments[1]);
167-
const errorMsgExpressions = Array.from(node.arguments.slice(2));
168-
const errorMsgQuasis = errorMsgLiteral
169-
.split('%s')
170-
.map(raw => t.templateElement({raw, cooked: String.raw({raw})}));
171-
172-
// Outputs:
173-
// `A ${adj} message that contains ${noun}`;
174-
const devMessage = t.templateLiteral(
175-
errorMsgQuasis,
176-
errorMsgExpressions
177-
);
178-
179-
const parentStatementPath = path.parentPath;
180-
if (parentStatementPath.type !== 'ExpressionStatement') {
181-
throw path.buildCodeFrameError(
182-
'invariant() cannot be called from expression context. Move ' +
183-
'the call to its own statement.'
184-
);
185-
}
186-
187-
if (noMinify) {
188-
// Error minification is disabled for this build.
189-
//
190-
// Outputs:
191-
// if (!condition) {
192-
// throw Error(`A ${adj} message that contains ${noun}`);
193-
// }
194-
const errorCallNode = t.callExpression(t.identifier('Error'), [
195-
devMessage,
196-
]);
197-
errorCallNode[SEEN_SYMBOL] = true;
198-
parentStatementPath.replaceWith(
199-
t.ifStatement(
200-
t.unaryExpression('!', condition),
201-
t.blockStatement([t.throwStatement(errorCallNode)])
202-
)
203-
);
204-
205-
return;
206-
}
207-
208-
let prodErrorId = errorMap[errorMsgLiteral];
209-
210-
if (prodErrorId === undefined) {
211-
// There is no error code for this message. Add an inline comment
212-
// that flags this as an unminified error. This allows the build
213-
// to proceed, while also allowing a post-build linter to detect it.
214-
//
215-
// Outputs:
216-
// /* FIXME (minify-errors-in-prod): Unminified error message in production build! */
217-
// if (!condition) {
218-
// throw Error(`A ${adj} message that contains ${noun}`);
219-
// }
220-
const errorCall = t.callExpression(t.identifier('Error'), [
221-
devMessage,
222-
]);
223-
errorCall[SEEN_SYMBOL] = true;
224-
parentStatementPath.replaceWith(
225-
t.ifStatement(
226-
t.unaryExpression('!', condition),
227-
t.blockStatement([t.throwStatement(errorCall)])
228-
)
229-
);
230-
parentStatementPath.addComment(
231-
'leading',
232-
'FIXME (minify-errors-in-prod): Unminified error message in production build!'
233-
);
234-
return;
235-
}
236-
prodErrorId = parseInt(prodErrorId, 10);
237-
238-
// Import formatProdErrorMessage
239-
const formatProdErrorMessageIdentifier = helperModuleImports.addDefault(
240-
path,
241-
'shared/formatProdErrorMessage',
242-
{nameHint: 'formatProdErrorMessage'}
243-
);
244-
245-
// Outputs:
246-
// formatProdErrorMessage(ERR_CODE, adj, noun);
247-
const prodMessage = t.callExpression(
248-
formatProdErrorMessageIdentifier,
249-
[t.numericLiteral(prodErrorId), ...errorMsgExpressions]
250-
);
251-
252-
const errorCall = t.callExpression(t.identifier('Error'), [
253-
t.conditionalExpression(DEV_EXPRESSION, devMessage, prodMessage),
254-
]);
255-
errorCall[SEEN_SYMBOL] = true;
256-
257-
// Outputs:
258-
// if (!condition) {
259-
// throw Error(
260-
// __DEV__
261-
// ? `A ${adj} message that contains ${noun}`
262-
// : formatProdErrorMessage(ERR_CODE, adj, noun)
263-
// );
264-
// }
265-
parentStatementPath.replaceWith(
266-
t.ifStatement(
267-
t.unaryExpression('!', condition),
268-
t.blockStatement([
269-
t.blockStatement([t.throwStatement(errorCall)]),
270-
])
271-
)
272-
);
273-
}
274139
},
275140
},
276141
};

scripts/rollup/build.js

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -176,26 +176,13 @@ function getBabelConfig(
176176
if (updateBabelOptions) {
177177
options = updateBabelOptions(options);
178178
}
179+
// Controls whether to replace error messages with error codes in production.
180+
// By default, error messages are replaced in production.
181+
if (!isDevelopment && bundle.minifyWithProdErrorCodes !== false) {
182+
options.plugins.push(require('../error-codes/transform-error-messages'));
183+
}
184+
179185
switch (bundleType) {
180-
case FB_WWW_DEV:
181-
case FB_WWW_PROD:
182-
case FB_WWW_PROFILING:
183-
case RN_OSS_DEV:
184-
case RN_OSS_PROD:
185-
case RN_OSS_PROFILING:
186-
case RN_FB_DEV:
187-
case RN_FB_PROD:
188-
case RN_FB_PROFILING:
189-
return Object.assign({}, options, {
190-
plugins: options.plugins.concat([
191-
[
192-
require('../error-codes/transform-error-messages'),
193-
// Controls whether to replace error messages with error codes
194-
// in production. By default, error messages are replaced.
195-
{noMinify: bundle.minifyWithProdErrorCodes === false},
196-
],
197-
]),
198-
});
199186
case UMD_DEV:
200187
case UMD_PROD:
201188
case UMD_PROFILING:
@@ -206,8 +193,6 @@ function getBabelConfig(
206193
plugins: options.plugins.concat([
207194
// Use object-assign polyfill in open source
208195
path.resolve('./scripts/babel/transform-object-assign-require'),
209-
// Minify invariant messages
210-
require('../error-codes/transform-error-messages'),
211196
]),
212197
});
213198
default:

0 commit comments

Comments
 (0)