mirror of
https://github.com/facebook/react.git
synced 2026-02-24 20:53:03 +00:00
* Transform invariant to custom error type
This transforms calls to the invariant module:
```js
invariant(condition, 'A %s message that contains %s', adj, noun);
```
Into throw statements:
```js
if (!condition) {
if (__DEV__) {
throw ReactError(`A ${adj} message that contains ${noun}`);
} else {
throw ReactErrorProd(ERR_CODE, adj, noun);
}
}
```
The only thing ReactError does is return an error whose name is set
to "Invariant Violation" to match the existing behavior.
ReactErrorProd is a special version used in production that throws
a minified error code, with a link to see to expanded form. This
replaces the reactProdInvariant module.
As a next step, I would like to replace our use of the invariant module
for user facing errors by transforming normal Error constructors to
ReactError and ReactErrorProd. (We can continue using invariant for
internal React errors that are meant to be unreachable, which was the
original purpose of invariant.)
* Use numbers instead of strings for error codes
* Use arguments instead of an array
I wasn't sure about this part so I asked Sebastian, and his rationale
was that using arguments will make ReactErrorProd slightly slower, but
using an array will likely make all the functions that throw slightly
slower to compile, so it's hard to say which way is better. But since
ReactErrorProd is in an error path, and fewer bytes is generally better,
no array is good.
* Casing nit
86 lines
2.1 KiB
JavaScript
86 lines
2.1 KiB
JavaScript
/**
|
|
* 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.
|
|
*/
|
|
/* eslint-disable quotes */
|
|
'use strict';
|
|
|
|
let babel = require('babel-core');
|
|
let devExpressionWithCodes = require('../minify-error-messages');
|
|
|
|
function transform(input) {
|
|
return babel.transform(input, {
|
|
plugins: [devExpressionWithCodes],
|
|
}).code;
|
|
}
|
|
|
|
let oldEnv;
|
|
|
|
describe('error transform', () => {
|
|
beforeEach(() => {
|
|
oldEnv = process.env.NODE_ENV;
|
|
process.env.NODE_ENV = '';
|
|
});
|
|
|
|
afterEach(() => {
|
|
process.env.NODE_ENV = oldEnv;
|
|
});
|
|
|
|
it('should replace simple invariant calls', () => {
|
|
expect(
|
|
transform(`
|
|
import invariant from 'shared/invariant';
|
|
invariant(condition, 'Do not override existing functions.');
|
|
`)
|
|
).toMatchSnapshot();
|
|
});
|
|
|
|
it('should only add `ReactError` and `ReactErrorProd` once each', () => {
|
|
expect(
|
|
transform(`
|
|
import invariant from 'shared/invariant';
|
|
invariant(condition, 'Do not override existing functions.');
|
|
invariant(condition, 'Do not override existing functions.');
|
|
`)
|
|
).toMatchSnapshot();
|
|
});
|
|
|
|
it('should support invariant calls with args', () => {
|
|
expect(
|
|
transform(`
|
|
import invariant from 'shared/invariant';
|
|
invariant(condition, 'Expected %s target to be an array; got %s', foo, bar);
|
|
`)
|
|
).toMatchSnapshot();
|
|
});
|
|
|
|
it('should support invariant calls with a concatenated template string and args', () => {
|
|
expect(
|
|
transform(`
|
|
import invariant from 'shared/invariant';
|
|
invariant(condition, 'Expected a component class, ' + 'got %s.' + '%s', Foo, Bar);
|
|
`)
|
|
).toMatchSnapshot();
|
|
});
|
|
|
|
it('should correctly transform invariants that are not in the error codes map', () => {
|
|
expect(
|
|
transform(`
|
|
import invariant from 'shared/invariant';
|
|
invariant(condition, 'This is not a real error message.');
|
|
`)
|
|
).toMatchSnapshot();
|
|
});
|
|
|
|
it('should handle escaped characters', () => {
|
|
expect(
|
|
transform(`
|
|
import invariant from 'shared/invariant';
|
|
invariant(condition, 'What\\'s up?');
|
|
`)
|
|
).toMatchSnapshot();
|
|
});
|
|
});
|