Skip to content

Commit 779a164

Browse files
committed
Pass prod error messages directly to constructor
Updates the `invariant` transform to pass an error message string directly to the Error constructor, instead of mutating the message property. Turns this code: ```js invariant(condition, 'A %s message that contains %s', adj, noun); ``` into this: ```js if (!condition) { if (__DEV__) { throw Error(`A ${adj} message that contains ${noun}`); } else { throw Error(formatProdErrorMessage(ERR_CODE, adj, noun)); } } ```
1 parent 44f69de commit 779a164

File tree

4 files changed

+27
-34
lines changed

4 files changed

+27
-34
lines changed

packages/shared/__tests__/ReactErrorProd-test.internal.js

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99
'use strict';
1010

11-
let ReactErrorProd;
11+
let formatProdErrorMessage;
1212

1313
describe('ReactErrorProd', () => {
1414
let globalErrorMock;
@@ -25,7 +25,7 @@ describe('ReactErrorProd', () => {
2525
expect(typeof global.Error).toBe('function');
2626
}
2727
jest.resetModules();
28-
ReactErrorProd = require('shared/ReactErrorProd').default;
28+
formatProdErrorMessage = require('shared/formatProdErrorMessage').default;
2929
});
3030

3131
afterEach(() => {
@@ -35,27 +35,21 @@ describe('ReactErrorProd', () => {
3535
});
3636

3737
it('should throw with the correct number of `%s`s in the URL', () => {
38-
expect(function() {
39-
throw ReactErrorProd(Error(124), 'foo', 'bar');
40-
}).toThrowError(
38+
expect(formatProdErrorMessage(124, 'foo', 'bar')).toEqual(
4139
'Minified React error #124; visit ' +
4240
'https://reactjs.org/docs/error-decoder.html?invariant=124&args[]=foo&args[]=bar' +
4341
' for the full message or use the non-minified dev environment' +
4442
' for full errors and additional helpful warnings.',
4543
);
4644

47-
expect(function() {
48-
throw ReactErrorProd(Error(20));
49-
}).toThrowError(
45+
expect(formatProdErrorMessage(20)).toEqual(
5046
'Minified React error #20; visit ' +
5147
'https://reactjs.org/docs/error-decoder.html?invariant=20' +
5248
' for the full message or use the non-minified dev environment' +
5349
' for full errors and additional helpful warnings.',
5450
);
5551

56-
expect(function() {
57-
throw ReactErrorProd(Error(77), '<div>', '&?bar');
58-
}).toThrowError(
52+
expect(formatProdErrorMessage(77, '<div>', '&?bar')).toEqual(
5953
'Minified React error #77; visit ' +
6054
'https://reactjs.org/docs/error-decoder.html?invariant=77&args[]=%3Cdiv%3E&args[]=%26%3Fbar' +
6155
' for the full message or use the non-minified dev environment' +

packages/shared/ReactErrorProd.js renamed to packages/shared/formatProdErrorMessage.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,19 @@
77
*/
88

99
// Do not require this module directly! Use normal `invariant` calls with
10-
// template literal strings. The messages will be converted to ReactError during
11-
// build, and in production they will be minified.
10+
// template literal strings. The messages will be replaced with error codes
11+
// during build.
1212

13-
function ReactErrorProd(error) {
14-
const code = error.message;
13+
function formatProdErrorMessage(code) {
1514
let url = 'https://reactjs.org/docs/error-decoder.html?invariant=' + code;
1615
for (let i = 1; i < arguments.length; i++) {
1716
url += '&args[]=' + encodeURIComponent(arguments[i]);
1817
}
19-
error.message =
18+
return (
2019
`Minified React error #${code}; visit ${url} for the full message or ` +
2120
'use the non-minified dev environment for full errors and additional ' +
22-
'helpful warnings. ';
23-
return error;
21+
'helpful warnings.'
22+
);
2423
}
2524

26-
export default ReactErrorProd;
25+
export default formatProdErrorMessage;

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,40 +23,40 @@ exports[`error transform should handle escaped characters 1`] = `
2323
`;
2424

2525
exports[`error transform should replace simple invariant calls 1`] = `
26-
"import _ReactErrorProd from \\"shared/ReactErrorProd\\";
26+
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
2727
import invariant from 'shared/invariant';
2828
2929
if (!condition) {
3030
if (__DEV__) {
3131
throw Error(\\"Do not override existing functions.\\");
3232
} else {
33-
throw _ReactErrorProd(Error(16));
33+
throw Error(_formatProdErrorMessage(16));
3434
}
3535
}"
3636
`;
3737

3838
exports[`error transform should support invariant calls with a concatenated template string and args 1`] = `
39-
"import _ReactErrorProd from \\"shared/ReactErrorProd\\";
39+
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
4040
import invariant from 'shared/invariant';
4141
4242
if (!condition) {
4343
if (__DEV__) {
4444
throw Error(\\"Expected a component class, got \\" + Foo + \\".\\" + Bar);
4545
} else {
46-
throw _ReactErrorProd(Error(18), Foo, Bar);
46+
throw Error(_formatProdErrorMessage(18, Foo, Bar));
4747
}
4848
}"
4949
`;
5050

5151
exports[`error transform should support invariant calls with args 1`] = `
52-
"import _ReactErrorProd from \\"shared/ReactErrorProd\\";
52+
"import _formatProdErrorMessage from \\"shared/formatProdErrorMessage\\";
5353
import invariant from 'shared/invariant';
5454
5555
if (!condition) {
5656
if (__DEV__) {
5757
throw Error(\\"Expected \\" + foo + \\" target to be an array; got \\" + bar);
5858
} else {
59-
throw _ReactErrorProd(Error(7), foo, bar);
59+
throw Error(_formatProdErrorMessage(7, foo, bar));
6060
}
6161
}"
6262
`;

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ module.exports = function(babel) {
3232
// if (__DEV__) {
3333
// throw Error(`A ${adj} message that contains ${noun}`);
3434
// } else {
35-
// throw ReactErrorProd(Error(ERR_CODE), adj, noun);
35+
// throw Error(formatProdErrorMessage(ERR_CODE, adj, noun));
3636
// }
3737
// }
3838
//
@@ -111,20 +111,20 @@ module.exports = function(babel) {
111111
prodErrorId = parseInt(prodErrorId, 10);
112112

113113
// Import ReactErrorProd
114-
const reactErrorProdIdentfier = helperModuleImports.addDefault(
114+
const formatProdErrorMessageIdentifier = helperModuleImports.addDefault(
115115
path,
116-
'shared/ReactErrorProd',
117-
{nameHint: 'ReactErrorProd'}
116+
'shared/formatProdErrorMessage',
117+
{nameHint: 'formatProdErrorMessage'}
118118
);
119119

120120
// Outputs:
121-
// throw ReactErrorProd(Error(ERR_CODE), adj, noun);
121+
// throw Error(formatProdErrorMessage(ERR_CODE, adj, noun));
122122
const prodThrow = t.throwStatement(
123-
t.callExpression(reactErrorProdIdentfier, [
124-
t.callExpression(t.identifier('Error'), [
123+
t.callExpression(t.identifier('Error'), [
124+
t.callExpression(formatProdErrorMessageIdentifier, [
125125
t.numericLiteral(prodErrorId),
126+
...errorMsgExpressions,
126127
]),
127-
...errorMsgExpressions,
128128
])
129129
);
130130

@@ -133,7 +133,7 @@ module.exports = function(babel) {
133133
// if (__DEV__) {
134134
// throw ReactError(Error(`A ${adj} message that contains ${noun}`));
135135
// } else {
136-
// throw ReactErrorProd(Error(ERR_CODE), adj, noun);
136+
// throw Error(formatProdErrorMessage(ERR_CODE, adj, noun));
137137
// }
138138
// }
139139
parentStatementPath.replaceWith(

0 commit comments

Comments
 (0)