Skip to content

Commit f2475ce

Browse files
authored
Flow: Readonly errors (#1116)
This uses $ReadOnlyArray and `+` properties to ensure Errors are read-only, removing some use of `*`.
1 parent f6a2de2 commit f2475ce

File tree

7 files changed

+72
-62
lines changed

7 files changed

+72
-62
lines changed

src/error/GraphQLError.js

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ import { getLocation } from '../language/location';
1111
import type { ASTNode } from '../language/ast';
1212
import type { Source } from '../language/source';
1313

14+
export type GraphQLErrorLocation = {|
15+
+line: number,
16+
+column: number,
17+
|};
18+
1419
/**
1520
* A GraphQLError describes an Error found during the parse, validate, or
1621
* execute phases of performing a GraphQL operation. In addition to a message
@@ -20,10 +25,10 @@ import type { Source } from '../language/source';
2025
declare class GraphQLError extends Error {
2126
constructor(
2227
message: string,
23-
nodes?: ?Array<*>,
28+
nodes?: ?$ReadOnlyArray<ASTNode>,
2429
source?: ?Source,
25-
positions?: ?Array<number>,
26-
path?: ?Array<string | number>,
30+
positions?: ?$ReadOnlyArray<number>,
31+
path?: ?$ReadOnlyArray<string | number>,
2732
originalError?: ?Error,
2833
extensions?: ?{ [key: string]: mixed },
2934
): void;
@@ -32,6 +37,8 @@ declare class GraphQLError extends Error {
3237
* A message describing the Error for debugging purposes.
3338
*
3439
* Enumerable, and appears in the result of JSON.stringify().
40+
*
41+
* Note: should be treated as readonly, despite invariant usage.
3542
*/
3643
message: string;
3744

@@ -45,49 +52,49 @@ declare class GraphQLError extends Error {
4552
*
4653
* Enumerable, and appears in the result of JSON.stringify().
4754
*/
48-
locations: Array<{ line: number, column: number }> | void;
55+
+locations: $ReadOnlyArray<GraphQLErrorLocation> | void;
4956

5057
/**
5158
* An array describing the JSON-path into the execution response which
5259
* corresponds to this error. Only included for errors during execution.
5360
*
5461
* Enumerable, and appears in the result of JSON.stringify().
5562
*/
56-
path: Array<string | number> | void;
63+
+path: $ReadOnlyArray<string | number> | void;
5764

5865
/**
5966
* An array of GraphQL AST Nodes corresponding to this error.
6067
*/
61-
nodes: Array<ASTNode> | void;
68+
+nodes: $ReadOnlyArray<ASTNode> | void;
6269

6370
/**
6471
* The source GraphQL document corresponding to this error.
6572
*/
66-
source: Source | void;
73+
+source: Source | void;
6774

6875
/**
6976
* An array of character offsets within the source GraphQL document
7077
* which correspond to this error.
7178
*/
72-
positions: Array<number> | void;
79+
+positions: $ReadOnlyArray<number> | void;
7380

7481
/**
7582
* The original error thrown from a field resolver during execution.
7683
*/
77-
originalError: ?Error;
84+
+originalError: ?Error;
7885

7986
/**
8087
* The original error thrown from a field resolver during execution.
8188
*/
82-
extensions: ?{ [key: string]: mixed };
89+
+extensions: ?{ [key: string]: mixed };
8390
}
8491

8592
export function GraphQLError( // eslint-disable-line no-redeclare
8693
message: string,
87-
nodes?: ?Array<*>,
94+
nodes?: ?$ReadOnlyArray<ASTNode>,
8895
source?: ?Source,
89-
positions?: ?Array<number>,
90-
path?: ?Array<string | number>,
96+
positions?: ?$ReadOnlyArray<number>,
97+
path?: ?$ReadOnlyArray<string | number>,
9198
originalError?: ?Error,
9299
extensions?: ?{ [key: string]: mixed },
93100
) {
@@ -100,9 +107,12 @@ export function GraphQLError( // eslint-disable-line no-redeclare
100107

101108
let _positions = positions;
102109
if (!_positions && nodes) {
103-
_positions = nodes
104-
.filter(node => Boolean(node.loc))
105-
.map(node => node.loc.start);
110+
_positions = nodes.reduce((list, node) => {
111+
if (node.loc) {
112+
list.push(node.loc.start);
113+
}
114+
return list;
115+
}, []);
106116
}
107117
if (_positions && _positions.length === 0) {
108118
_positions = undefined;

src/error/formatError.js

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99

1010
import invariant from '../jsutils/invariant';
11-
import type { GraphQLError } from './GraphQLError';
11+
import type { GraphQLError, GraphQLErrorLocation } from './GraphQLError';
1212

1313
/**
1414
* Given a GraphQLError, format it according to the rules described by the
@@ -25,12 +25,9 @@ export function formatError(error: GraphQLError): GraphQLFormattedError {
2525
}
2626

2727
export type GraphQLFormattedError = {
28-
message: string,
29-
locations: ?Array<GraphQLErrorLocation>,
30-
path: ?Array<string | number>,
31-
};
32-
33-
export type GraphQLErrorLocation = {
34-
line: number,
35-
column: number,
28+
+message: string,
29+
+locations: $ReadOnlyArray<GraphQLErrorLocation> | void,
30+
+path: $ReadOnlyArray<string | number> | void,
31+
// Extensions
32+
+[key: string]: mixed,
3633
};

src/error/index.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,5 @@ export { syntaxError } from './syntaxError';
1212
export { locatedError } from './locatedError';
1313
export { formatError } from './formatError';
1414

15-
export type {
16-
GraphQLFormattedError,
17-
GraphQLErrorLocation,
18-
} from './formatError';
15+
export type { GraphQLErrorLocation } from './GraphQLError';
16+
export type { GraphQLFormattedError } from './formatError';

src/error/locatedError.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
*/
99

1010
import { GraphQLError } from './GraphQLError';
11+
import type { ASTNode } from '../language/ast';
1112

1213
/**
1314
* Given an arbitrary Error, presumably thrown while attempting to execute a
@@ -16,8 +17,8 @@ import { GraphQLError } from './GraphQLError';
1617
*/
1718
export function locatedError(
1819
originalError: ?Error,
19-
nodes: Array<*>,
20-
path: Array<string | number>,
20+
nodes: $ReadOnlyArray<ASTNode>,
21+
path: $ReadOnlyArray<string | number>,
2122
): GraphQLError {
2223
// Note: this uses a brand-check to support GraphQL errors originating from
2324
// other contexts.

src/execution/execute.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export type ExecutionContext = {
101101
* - `data` is the result of a successful execution of the query.
102102
*/
103103
export type ExecutionResult = {
104-
errors?: Array<GraphQLError>,
104+
errors?: $ReadOnlyArray<GraphQLError>,
105105
data?: ObjMap<mixed>,
106106
};
107107

@@ -235,7 +235,7 @@ function buildResponse(
235235
*/
236236
export function responsePathAsArray(
237237
path: ResponsePath,
238-
): Array<string | number> {
238+
): $ReadOnlyArray<string | number> {
239239
const flattened = [];
240240
let curr = path;
241241
while (curr) {
@@ -663,7 +663,7 @@ function resolveField(
663663
exeContext: ExecutionContext,
664664
parentType: GraphQLObjectType,
665665
source: mixed,
666-
fieldNodes: Array<FieldNode>,
666+
fieldNodes: $ReadOnlyArray<FieldNode>,
667667
path: ResponsePath,
668668
): mixed {
669669
const fieldNode = fieldNodes[0];
@@ -708,7 +708,7 @@ function resolveField(
708708
export function buildResolveInfo(
709709
exeContext: ExecutionContext,
710710
fieldDef: GraphQLField<*, *>,
711-
fieldNodes: Array<FieldNode>,
711+
fieldNodes: $ReadOnlyArray<FieldNode>,
712712
parentType: GraphQLObjectType,
713713
path: ResponsePath,
714714
): GraphQLResolveInfo {
@@ -733,7 +733,7 @@ export function buildResolveInfo(
733733
export function resolveFieldValueOrError<TSource>(
734734
exeContext: ExecutionContext,
735735
fieldDef: GraphQLField<TSource, *>,
736-
fieldNodes: Array<FieldNode>,
736+
fieldNodes: $ReadOnlyArray<FieldNode>,
737737
resolveFn: GraphQLFieldResolver<TSource, *>,
738738
source: TSource,
739739
info: GraphQLResolveInfo,
@@ -766,7 +766,7 @@ export function resolveFieldValueOrError<TSource>(
766766
function completeValueCatchingError(
767767
exeContext: ExecutionContext,
768768
returnType: GraphQLType,
769-
fieldNodes: Array<FieldNode>,
769+
fieldNodes: $ReadOnlyArray<FieldNode>,
770770
info: GraphQLResolveInfo,
771771
path: ResponsePath,
772772
result: mixed,
@@ -820,7 +820,7 @@ function completeValueCatchingError(
820820
function completeValueWithLocatedError(
821821
exeContext: ExecutionContext,
822822
returnType: GraphQLType,
823-
fieldNodes: Array<FieldNode>,
823+
fieldNodes: $ReadOnlyArray<FieldNode>,
824824
info: GraphQLResolveInfo,
825825
path: ResponsePath,
826826
result: mixed,
@@ -872,7 +872,7 @@ function completeValueWithLocatedError(
872872
function completeValue(
873873
exeContext: ExecutionContext,
874874
returnType: GraphQLType,
875-
fieldNodes: Array<FieldNode>,
875+
fieldNodes: $ReadOnlyArray<FieldNode>,
876876
info: GraphQLResolveInfo,
877877
path: ResponsePath,
878878
result: mixed,
@@ -972,7 +972,7 @@ function completeValue(
972972
function completeListValue(
973973
exeContext: ExecutionContext,
974974
returnType: GraphQLList<*>,
975-
fieldNodes: Array<FieldNode>,
975+
fieldNodes: $ReadOnlyArray<FieldNode>,
976976
info: GraphQLResolveInfo,
977977
path: ResponsePath,
978978
result: mixed,
@@ -1034,7 +1034,7 @@ function completeLeafValue(returnType: GraphQLLeafType, result: mixed): mixed {
10341034
function completeAbstractValue(
10351035
exeContext: ExecutionContext,
10361036
returnType: GraphQLAbstractType,
1037-
fieldNodes: Array<FieldNode>,
1037+
fieldNodes: $ReadOnlyArray<FieldNode>,
10381038
info: GraphQLResolveInfo,
10391039
path: ResponsePath,
10401040
result: mixed,
@@ -1085,7 +1085,7 @@ function ensureValidRuntimeType(
10851085
runtimeTypeOrName: ?GraphQLObjectType | string,
10861086
exeContext: ExecutionContext,
10871087
returnType: GraphQLAbstractType,
1088-
fieldNodes: Array<FieldNode>,
1088+
fieldNodes: $ReadOnlyArray<FieldNode>,
10891089
info: GraphQLResolveInfo,
10901090
result: mixed,
10911091
): GraphQLObjectType {
@@ -1123,7 +1123,7 @@ function ensureValidRuntimeType(
11231123
function completeObjectValue(
11241124
exeContext: ExecutionContext,
11251125
returnType: GraphQLObjectType,
1126-
fieldNodes: Array<FieldNode>,
1126+
fieldNodes: $ReadOnlyArray<FieldNode>,
11271127
info: GraphQLResolveInfo,
11281128
path: ResponsePath,
11291129
result: mixed,
@@ -1169,7 +1169,7 @@ function completeObjectValue(
11691169
function invalidReturnTypeError(
11701170
returnType: GraphQLObjectType,
11711171
result: mixed,
1172-
fieldNodes: Array<FieldNode>,
1172+
fieldNodes: $ReadOnlyArray<FieldNode>,
11731173
): GraphQLError {
11741174
return new GraphQLError(
11751175
`Expected value of type "${returnType.name}" but got: ${String(result)}.`,
@@ -1180,7 +1180,7 @@ function invalidReturnTypeError(
11801180
function collectAndExecuteSubfields(
11811181
exeContext: ExecutionContext,
11821182
returnType: GraphQLObjectType,
1183-
fieldNodes: Array<FieldNode>,
1183+
fieldNodes: $ReadOnlyArray<FieldNode>,
11841184
info: GraphQLResolveInfo,
11851185
path: ResponsePath,
11861186
result: mixed,

src/type/definition.js

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -619,20 +619,23 @@ export type GraphQLFieldResolver<TSource, TContext> = (
619619
info: GraphQLResolveInfo,
620620
) => mixed;
621621

622-
export type GraphQLResolveInfo = {
623-
fieldName: string,
624-
fieldNodes: Array<FieldNode>,
625-
returnType: GraphQLOutputType,
626-
parentType: GraphQLObjectType,
627-
path: ResponsePath,
628-
schema: GraphQLSchema,
629-
fragments: ObjMap<FragmentDefinitionNode>,
630-
rootValue: mixed,
631-
operation: OperationDefinitionNode,
632-
variableValues: { [variable: string]: mixed },
633-
};
634-
635-
export type ResponsePath = { prev: ResponsePath | void, key: string | number };
622+
export type GraphQLResolveInfo = {|
623+
+fieldName: string,
624+
+fieldNodes: $ReadOnlyArray<FieldNode>,
625+
+returnType: GraphQLOutputType,
626+
+parentType: GraphQLObjectType,
627+
+path: ResponsePath,
628+
+schema: GraphQLSchema,
629+
+fragments: ObjMap<FragmentDefinitionNode>,
630+
+rootValue: mixed,
631+
+operation: OperationDefinitionNode,
632+
+variableValues: { [variable: string]: mixed },
633+
|};
634+
635+
export type ResponsePath = {|
636+
+prev: ResponsePath | void,
637+
+key: string | number,
638+
|};
636639

637640
export type GraphQLFieldConfig<TSource, TContext> = {
638641
type: GraphQLOutputType,

src/validation/rules/ScalarLeafs.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,17 +43,18 @@ export function ScalarLeafs(context: ValidationContext): any {
4343
return {
4444
Field(node: FieldNode) {
4545
const type = context.getType();
46+
const selectionSet = node.selectionSet;
4647
if (type) {
4748
if (isLeafType(getNamedType(type))) {
48-
if (node.selectionSet) {
49+
if (selectionSet) {
4950
context.reportError(
5051
new GraphQLError(
5152
noSubselectionAllowedMessage(node.name.value, type),
52-
[node.selectionSet],
53+
[selectionSet],
5354
),
5455
);
5556
}
56-
} else if (!node.selectionSet) {
57+
} else if (!selectionSet) {
5758
context.reportError(
5859
new GraphQLError(
5960
requiredSubselectionMessage(node.name.value, type),

0 commit comments

Comments
 (0)