Skip to content

Commit e58762a

Browse files
Use 'invariant' only for real invariants, add 'devAssert' for t… (#2066)
1 parent ebcc754 commit e58762a

File tree

15 files changed

+172
-158
lines changed

15 files changed

+172
-158
lines changed

.flowconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ unsafe-getters-setters=error
2626
inexact-spread=error
2727
implicit-inexact-object=error
2828
unnecessary-optional-chain=error
29-
unnecessary-invariant=off
29+
unnecessary-invariant=error
3030
signature-verification-failure=error
3131
uninitialized-instance-property=error
3232
non-array-spread=error

resources/inline-invariant.js

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,37 @@
1414
* !<cond> ? invariant(0, ...) : undefined;
1515
*/
1616
module.exports = function inlineInvariant(context) {
17-
const replaceTemplate = context.template(`
17+
const invariantTemplate = context.template(`
1818
(%%cond%%) || invariant(0, %%args%%)
1919
`);
20+
const assertTemplate = context.template(`
21+
(%%cond%%) || devAssert(0, %%args%%)
22+
`);
2023

2124
return {
2225
visitor: {
2326
CallExpression(path) {
2427
const node = path.node;
2528
const parent = path.parent;
2629

27-
if (!isAppropriateInvariantCall(node, parent)) {
30+
if (
31+
parent.type !== 'ExpressionStatement' ||
32+
node.callee.type !== 'Identifier' ||
33+
node.arguments.length === 0
34+
) {
2835
return;
2936
}
3037

31-
const [cond, args] = node.arguments;
32-
path.replaceWith(replaceTemplate({ cond, args }));
38+
const calleeName = node.callee.name;
39+
if (calleeName === 'invariant') {
40+
const [cond, args] = node.arguments;
41+
path.addComment('leading', ' istanbul ignore next ');
42+
path.replaceWith(invariantTemplate({ cond, args }));
43+
} else if (calleeName === 'devAssert') {
44+
const [cond, args] = node.arguments;
45+
path.replaceWith(assertTemplate({ cond, args }));
46+
}
3347
},
3448
},
3549
};
3650
};
37-
38-
function isAppropriateInvariantCall(node, parent) {
39-
return (
40-
parent.type === 'ExpressionStatement' &&
41-
node.callee.type === 'Identifier' &&
42-
node.callee.name === 'invariant' &&
43-
node.arguments.length > 0
44-
);
45-
}

src/error/formatError.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @flow strict
22

3-
import invariant from '../jsutils/invariant';
3+
import devAssert from '../jsutils/devAssert';
44

55
import { type SourceLocation } from '../language/location';
66

@@ -11,7 +11,7 @@ import { type GraphQLError } from './GraphQLError';
1111
* Response Format, Errors section of the GraphQL Specification.
1212
*/
1313
export function formatError(error: GraphQLError): GraphQLFormattedError {
14-
invariant(error, 'Received null or undefined error.');
14+
devAssert(error, 'Received null or undefined error.');
1515
const message = error.message || 'An unknown error occurred.';
1616
const locations = error.locations;
1717
const path = error.path;

src/execution/execute.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { forEach, isCollection } from 'iterall';
44

55
import inspect from '../jsutils/inspect';
66
import memoize3 from '../jsutils/memoize3';
7-
import invariant from '../jsutils/invariant';
7+
import devAssert from '../jsutils/devAssert';
88
import isInvalid from '../jsutils/isInvalid';
99
import isNullish from '../jsutils/isNullish';
1010
import isPromise from '../jsutils/isPromise';
@@ -249,13 +249,13 @@ export function assertValidExecutionArguments(
249249
document: DocumentNode,
250250
rawVariableValues: ?{ +[variable: string]: mixed, ... },
251251
): void {
252-
invariant(document, 'Must provide document');
252+
devAssert(document, 'Must provide document');
253253

254254
// If the schema used for execution is invalid, throw an error.
255255
assertValidSchema(schema);
256256

257257
// Variables, if provided, must be an object.
258-
invariant(
258+
devAssert(
259259
rawVariableValues == null || isObjectLike(rawVariableValues),
260260
'Variables must be provided as an Object where each property is a variable value. Perhaps look to see if an unparsed JSON string was provided.',
261261
);
@@ -892,10 +892,11 @@ function completeListValue(
892892
path: Path,
893893
result: mixed,
894894
): PromiseOrValue<$ReadOnlyArray<mixed>> {
895-
invariant(
896-
isCollection(result),
897-
`Expected Iterable, but did not find one for field ${info.parentType.name}.${info.fieldName}.`,
898-
);
895+
if (!isCollection(result)) {
896+
throw new GraphQLError(
897+
`Expected Iterable, but did not find one for field ${info.parentType.name}.${info.fieldName}.`,
898+
);
899+
}
899900

900901
// This is specified as a simple map, however we're optimizing the path
901902
// where the list contains no Promises by avoiding creating another Promise.

src/jsutils/devAssert.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @flow strict
2+
3+
/* istanbul ignore file */
4+
export default function devAssert(condition: mixed, message: string): void {
5+
const booleanCondition = Boolean(condition);
6+
/* istanbul ignore else */
7+
if (!booleanCondition) {
8+
throw new Error(message);
9+
}
10+
}

src/language/parser.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @flow strict
22

33
import inspect from '../jsutils/inspect';
4-
import invariant from '../jsutils/invariant';
4+
import devAssert from '../jsutils/devAssert';
55
import defineToJSON from '../jsutils/defineToJSON';
66

77
import { syntaxError } from '../error/syntaxError';
@@ -173,7 +173,7 @@ class Parser {
173173

174174
constructor(source: string | Source, options?: ParseOptions) {
175175
const sourceObj = typeof source === 'string' ? new Source(source) : source;
176-
invariant(
176+
devAssert(
177177
sourceObj instanceof Source,
178178
`Must provide Source. Received: ${inspect(sourceObj)}`,
179179
);

src/language/source.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// @flow strict
22

3-
import invariant from '../jsutils/invariant';
3+
import devAssert from '../jsutils/devAssert';
44
import defineToStringTag from '../jsutils/defineToStringTag';
55

66
type Location = {|
@@ -25,11 +25,11 @@ export class Source {
2525
this.body = body;
2626
this.name = name || 'GraphQL request';
2727
this.locationOffset = locationOffset || { line: 1, column: 1 };
28-
invariant(
28+
devAssert(
2929
this.locationOffset.line > 0,
3030
'line in locationOffset is 1-indexed and must be positive',
3131
);
32-
invariant(
32+
devAssert(
3333
this.locationOffset.column > 0,
3434
'column in locationOffset is 1-indexed and must be positive',
3535
);

0 commit comments

Comments
 (0)