···
68
-
const nameRe = /[_A-Za-z]\w*/y;
70
-
// NOTE: This should be compressed by our build step
71
-
// This merges all possible value parsing into one regular expression
72
-
const valueRe = new RegExp(
74
-
// `null`, `true`, and `false` literals (BooleanValue & NullValue)
75
-
'(null|true|false)|' +
76
-
// Variables starting with `$` then having a name (VariableNode)
80
-
// Numbers, starting with int then optionally following with a float part (IntValue and FloatValue)
81
-
'(-?\\d+)((?:\\.\\d+)?[eE][+-]?\\d+|\\.\\d+)?|' +
82
-
// Block strings starting with `"""` until the next unescaped `"""` (StringValue)
83
-
'("""(?:"""|(?:[\\s\\S]*?[^\\\\])"""))|' +
84
-
// Strings starting with `"` must be on one line (StringValue)
85
-
'("(?:"|[^\\r\\n]*?[^\\\\]"))|' + // string
86
-
// Enums are simply names except for our literals (EnumValue)
93
-
// NOTE: Each of the groups above end up in the RegExpExecArray at the specified indices (starting with 1)
94
-
const enum ValueGroup {
68
+
function name(): string {
71
+
let char = input.charCodeAt(idx++) | 0;
72
+
(char >= 48 /*'0'*/ && char <= 57) /*'9'*/ ||
73
+
(char >= 65 /*'A'*/ && char <= 90) /*'Z'*/ ||
74
+
char === 95 /*'_'*/ ||
75
+
(char >= 97 /*'a'*/ && char <= 122) /*'z'*/;
76
+
char = input.charCodeAt(idx++) | 0
78
+
if (start === idx - 1) throw error('Name');
79
+
const value = input.slice(start, --idx);
104
-
type ValueExec = RegExpExecArray & {
105
-
[Prop in ValueGroup]: string | undefined;
84
+
function nameNode(): ast.NameNode {
86
+
kind: 'Name' as Kind.NAME,
108
-
const complexStringRe = /\\/;
91
+
const restBlockStringRe = /(?:"""|(?:[\s\S]*?[^\\])""")/y;
92
+
const floatPartRe = /(?:(?:\.\d+)?[eE][+-]?\d+|\.\d+)/y;
function value(constant: true): ast.ConstValueNode;
function value(constant: boolean): ast.ValueNode;
function value(constant: boolean): ast.ValueNode {
let match: string | undefined;
115
-
let exec: ValueExec | null;
116
-
valueRe.lastIndex = idx;
117
-
if (input.charCodeAt(idx) === 91 /*'['*/) {
118
-
// Lists are checked ahead of time with `[` chars
121
-
const values: ast.ValueNode[] = [];
122
-
while (input.charCodeAt(idx) !== 93 /*']'*/) values.push(value(constant));
126
-
kind: 'ListValue' as Kind.LIST,
129
-
} else if (input.charCodeAt(idx) === 123 /*'{'*/) {
130
-
// Objects are checked ahead of time with `{` chars
133
-
const fields: ast.ObjectFieldNode[] = [];
134
-
while (input.charCodeAt(idx) !== 125 /*'}'*/) {
135
-
if ((match = advance(nameRe)) == null) throw error('ObjectField');
99
+
switch (input.charCodeAt(idx)) {
103
+
const values: ast.ValueNode[] = [];
104
+
while (input.charCodeAt(idx) !== 93 /*']'*/) values.push(value(constant));
108
+
kind: 'ListValue' as Kind.LIST,
137
-
if (input.charCodeAt(idx++) !== 58 /*':'*/) throw error('ObjectField');
115
+
const fields: ast.ObjectFieldNode[] = [];
116
+
while (input.charCodeAt(idx) !== 125 /*'}'*/) {
117
+
const name = nameNode();
118
+
if (input.charCodeAt(idx++) !== 58 /*':'*/) throw error('ObjectField');
121
+
kind: 'ObjectField' as Kind.OBJECT_FIELD,
123
+
value: value(constant),
140
-
kind: 'ObjectField' as Kind.OBJECT_FIELD,
141
-
name: { kind: 'Name' as Kind.NAME, value: match },
142
-
value: value(constant),
148
-
kind: 'ObjectValue' as Kind.OBJECT,
151
-
} else if ((exec = valueRe.exec(input) as ValueExec) != null) {
152
-
// Starting from here, the merged `valueRe` is used
153
-
idx = valueRe.lastIndex;
155
-
if ((match = exec[ValueGroup.Const]) != null) {
156
-
return match === 'null'
157
-
? { kind: 'NullValue' as Kind.NULL }
159
-
kind: 'BooleanValue' as Kind.BOOLEAN,
160
-
value: match === 'true',
162
-
} else if ((match = exec[ValueGroup.Var]) != null) {
164
-
throw error('Variable');
129
+
kind: 'ObjectValue' as Kind.OBJECT,
134
+
if (constant) throw error('Variable');
137
+
kind: 'Variable' as Kind.VARIABLE,
142
+
if (input.charCodeAt(idx + 1) === 34 && input.charCodeAt(idx + 2) === 34) {
144
+
if ((match = advance(restBlockStringRe)) == null) throw error('StringValue');
147
+
kind: 'StringValue' as Kind.STRING,
148
+
value: blockString(match.slice(0, -3)),
155
+
let isComplex = false;
157
+
char = input.charCodeAt(idx++) | 0;
158
+
(char === 92 /*'\\'*/ && (idx++, (isComplex = true))) ||
159
+
(char !== 10 /*'\n'*/ && char !== 13 /*'\r'*/ && char !== 34 /*'"'*/ && char);
160
+
char = input.charCodeAt(idx++) | 0
162
+
if (char !== 34) throw error('StringValue');
163
+
match = input.slice(start, idx);
167
-
kind: 'Variable' as Kind.VARIABLE,
169
-
kind: 'Name' as Kind.NAME,
166
+
kind: 'StringValue' as Kind.STRING,
167
+
value: isComplex ? (JSON.parse(match) as string) : match.slice(1, -1),
174
-
} else if ((match = exec[ValueGroup.Int]) != null) {
175
-
let floatPart: string | undefined;
176
-
if ((floatPart = exec[ValueGroup.Float]) != null) {
183
+
const start = idx++;
185
+
while ((char = input.charCodeAt(idx++) | 0) >= 48 /*'0'*/ && char <= 57 /*'9'*/);
186
+
const intPart = input.slice(start, --idx);
188
+
(char = input.charCodeAt(idx)) === 46 /*'.'*/ ||
189
+
char === 69 /*'E'*/ ||
190
+
char === 101 /*'e'*/
192
+
if ((match = advance(floatPartRe)) == null) throw error('FloatValue');
kind: 'FloatValue' as Kind.FLOAT,
179
-
value: match + floatPart,
196
+
value: intPart + match,
kind: 'IntValue' as Kind.INT,
187
-
} else if ((match = exec[ValueGroup.BlockString]) != null) {
189
-
kind: 'StringValue' as Kind.STRING,
190
-
value: blockString(match.slice(3, -3)),
193
-
} else if ((match = exec[ValueGroup.String]) != null) {
195
-
kind: 'StringValue' as Kind.STRING,
196
-
// When strings don't contain escape codes, a simple slice will be enough, otherwise
197
-
// `JSON.parse` matches GraphQL's string parsing perfectly
198
-
value: complexStringRe.test(match) ? (JSON.parse(match) as string) : match.slice(1, -1),
201
-
} else if ((match = exec[ValueGroup.Enum]) != null) {
203
-
kind: 'EnumValue' as Kind.ENUM,
208
+
input.charCodeAt(idx + 1) === 117 &&
209
+
input.charCodeAt(idx + 2) === 108 &&
210
+
input.charCodeAt(idx + 3) === 108
214
+
return { kind: 'NullValue' as Kind.NULL };
219
+
input.charCodeAt(idx + 1) === 114 &&
220
+
input.charCodeAt(idx + 2) === 117 &&
221
+
input.charCodeAt(idx + 3) === 101
225
+
return { kind: 'BooleanValue' as Kind.BOOLEAN, value: true };
230
+
input.charCodeAt(idx + 1) === 97 &&
231
+
input.charCodeAt(idx + 2) === 108 &&
232
+
input.charCodeAt(idx + 3) === 115 &&
233
+
input.charCodeAt(idx + 4) === 101
237
+
return { kind: 'BooleanValue' as Kind.BOOLEAN, value: false };
209
-
throw error('Value');
242
+
kind: 'EnumValue' as Kind.ENUM,
function arguments_(constant: boolean): ast.ArgumentNode[] | undefined {
···
const args: ast.ArgumentNode[] = [];
217
-
let _name: string | undefined;
219
-
if ((_name = advance(nameRe)) == null) throw error('Argument');
253
+
const name = nameNode();
if (input.charCodeAt(idx++) !== 58 /*':'*/) throw error('Argument');
kind: 'Argument' as Kind.ARGUMENT,
225
-
name: { kind: 'Name' as Kind.NAME, value: _name },
} while (input.charCodeAt(idx) !== 41 /*')'*/);
···
function directives(constant: boolean): ast.DirectiveNode[] | undefined {
if (input.charCodeAt(idx) === 64 /*'@'*/) {
const directives: ast.DirectiveNode[] = [];
241
-
let _name: string | undefined;
244
-
if ((_name = advance(nameRe)) == null) throw error('Directive');
kind: 'Directive' as Kind.DIRECTIVE,
248
-
name: { kind: 'Name' as Kind.NAME, value: _name },
arguments: arguments_(constant),
} while (input.charCodeAt(idx) === 64 /*'@'*/);
···
function type(): ast.TypeNode {
257
-
let match: string | undefined;
while (input.charCodeAt(idx) === 91 /*'['*/) {
264
-
if ((match = advance(nameRe)) == null) throw error('NamedType');
let type: ast.TypeNode = {
kind: 'NamedType' as Kind.NAMED_TYPE,
268
-
name: { kind: 'Name' as Kind.NAME, value: match },
if (input.charCodeAt(idx) === 33 /*'!'*/) {
···
291
-
// NOTE: This should be compressed by our build step
292
-
// This merges the two possible selection parsing branches into one regular expression
293
-
const selectionRe = new RegExp(
295
-
// fragment spreads (FragmentSpread or InlineFragment nodes)
297
-
// field aliases or names (FieldNode)
304
-
// NOTE: Each of the groups above end up in the RegExpExecArray at the indices 1&2
305
-
const enum SelectionGroup {
318
+
function selectionSetStart(): ast.SelectionSetNode {
319
+
if (input.charCodeAt(idx++) !== 123 /*'{'*/) throw error('SelectionSet');
321
+
return selectionSet();
310
-
type SelectionExec = RegExpExecArray & {
311
-
[Prop in SelectionGroup]: string | undefined;
function selectionSet(): ast.SelectionSetNode {
const selections: ast.SelectionNode[] = [];
316
-
let match: string | undefined;
317
-
let exec: SelectionExec | null;
319
-
selectionRe.lastIndex = idx;
320
-
if ((exec = selectionRe.exec(input) as SelectionExec) != null) {
321
-
idx = selectionRe.lastIndex;
322
-
if (exec[SelectionGroup.Spread] != null) {
324
-
let match = advance(nameRe);
325
-
if (match != null && match !== 'on') {
326
-
// A simple `...Name` spread with optional directives
327
+
if (input.charCodeAt(idx) === 46 /*'.'*/) {
328
+
if (input.charCodeAt(++idx) !== 46 /*'.'*/ || input.charCodeAt(++idx) !== 46 /*'.'*/)
329
+
throw error('SelectionSet');
332
+
switch (input.charCodeAt(idx)) {
329
-
kind: 'FragmentSpread' as Kind.FRAGMENT_SPREAD,
330
-
name: { kind: 'Name' as Kind.NAME, value: match },
335
+
kind: 'InlineFragment' as Kind.INLINE_FRAGMENT,
336
+
typeCondition: undefined,
directives: directives(false),
338
+
selectionSet: selectionSetStart(),
335
-
if (match === 'on') {
336
-
// An inline `... on Name` spread; if this doesn't match, the type condition has been omitted
337
-
if ((match = advance(nameRe)) == null) throw error('NamedType');
343
+
if (input.charCodeAt(idx + 1) === 110 /*'n'*/) {
347
+
kind: 'InlineFragment' as Kind.INLINE_FRAGMENT,
349
+
kind: 'NamedType' as Kind.NAMED_TYPE,
352
+
directives: directives(false),
353
+
selectionSet: selectionSetStart(),
357
+
kind: 'FragmentSpread' as Kind.FRAGMENT_SPREAD,
359
+
directives: directives(false),
340
-
const _directives = directives(false);
341
-
if (input.charCodeAt(idx++) !== 123 /*'{'*/) throw error('InlineFragment');
kind: 'InlineFragment' as Kind.INLINE_FRAGMENT,
345
-
typeCondition: match
347
-
kind: 'NamedType' as Kind.NAMED_TYPE,
348
-
name: { kind: 'Name' as Kind.NAME, value: match },
351
-
directives: _directives,
369
+
typeCondition: undefined,
370
+
directives: undefined,
selectionSet: selectionSet(),
355
-
} else if ((match = exec[SelectionGroup.Name]) != null) {
356
-
let _alias: string | undefined;
377
+
kind: 'FragmentSpread' as Kind.FRAGMENT_SPREAD,
379
+
directives: directives(false),
383
+
let name = nameNode();
384
+
let alias: ast.NameNode | undefined;
385
+
if (input.charCodeAt(idx) === 58 /*':'*/) {
358
-
// Parse the optional alias, by reassigning and then getting the name
359
-
if (input.charCodeAt(idx) === 58 /*':'*/) {
363
-
if ((match = advance(nameRe)) == null) throw error('Field');
366
-
const _arguments = arguments_(false);
391
+
const _arguments = arguments_(false);
392
+
const _directives = directives(false);
393
+
let _selectionSet: ast.SelectionSetNode | undefined;
394
+
if (input.charCodeAt(idx) === 123 /*'{'*/) {
368
-
const _directives = directives(false);
369
-
let _selectionSet: ast.SelectionSetNode | undefined;
370
-
if (input.charCodeAt(idx) === 123 /*'{'*/) {
373
-
_selectionSet = selectionSet();
376
-
kind: 'Field' as Kind.FIELD,
377
-
alias: _alias ? { kind: 'Name' as Kind.NAME, value: _alias } : undefined,
378
-
name: { kind: 'Name' as Kind.NAME, value: match },
379
-
arguments: _arguments,
380
-
directives: _directives,
381
-
selectionSet: _selectionSet,
397
+
_selectionSet = selectionSet();
385
-
throw error('SelectionSet');
400
+
kind: 'Field' as Kind.FIELD,
403
+
arguments: _arguments,
404
+
directives: _directives,
405
+
selectionSet: _selectionSet,
} while (input.charCodeAt(idx) !== 125 /*'}'*/);
···
const vars: ast.VariableDefinitionNode[] = [];
402
-
let _name: string | undefined;
if (input.charCodeAt(idx++) !== 36 /*'$'*/) throw error('Variable');
405
-
if ((_name = advance(nameRe)) == null) throw error('Variable');
425
+
const name = nameNode();
if (input.charCodeAt(idx++) !== 58 /*':'*/) throw error('VariableDefinition');
···
kind: 'VariableDefinition' as Kind.VARIABLE_DEFINITION,
kind: 'Variable' as Kind.VARIABLE,
421
-
name: { kind: 'Name' as Kind.NAME, value: _name },
defaultValue: _defaultValue,
···
function fragmentDefinition(): ast.FragmentDefinitionNode {
435
-
let _name: string | undefined;
436
-
let _condition: string | undefined;
437
-
if ((_name = advance(nameRe)) == null) throw error('FragmentDefinition');
439
-
if (advance(nameRe) !== 'on') throw error('FragmentDefinition');
441
-
if ((_condition = advance(nameRe)) == null) throw error('FragmentDefinition');
443
-
const _directives = directives(false);
444
-
if (input.charCodeAt(idx++) !== 123 /*'{'*/) throw error('FragmentDefinition');
454
+
const name = nameNode();
455
+
if (input.charCodeAt(idx++) !== 111 /*'o'*/ || input.charCodeAt(idx++) !== 110 /*'n'*/)
456
+
throw error('FragmentDefinition');
kind: 'FragmentDefinition' as Kind.FRAGMENT_DEFINITION,
448
-
name: { kind: 'Name' as Kind.NAME, value: _name },
kind: 'NamedType' as Kind.NAMED_TYPE,
451
-
name: { kind: 'Name' as Kind.NAME, value: _condition },
453
-
directives: _directives,
454
-
selectionSet: selectionSet(),
465
+
directives: directives(false),
466
+
selectionSet: selectionSetStart(),
458
-
const definitionRe = /(?:query|mutation|subscription|fragment)/y;
460
-
function operationDefinition(
461
-
operation: OperationTypeNode | undefined
462
-
): ast.OperationDefinitionNode | undefined {
463
-
let _name: string | undefined;
464
-
let _variableDefinitions: ast.VariableDefinitionNode[] | undefined;
465
-
let _directives: ast.DirectiveNode[] | undefined;
468
-
_name = advance(nameRe);
469
-
_variableDefinitions = variableDefinitions();
470
-
_directives = directives(false);
472
-
if (input.charCodeAt(idx) === 123 /*'{'*/) {
476
-
kind: 'OperationDefinition' as Kind.OPERATION_DEFINITION,
477
-
operation: operation || ('query' as OperationTypeNode.QUERY),
478
-
name: _name ? { kind: 'Name' as Kind.NAME, value: _name } : undefined,
479
-
variableDefinitions: _variableDefinitions,
480
-
directives: _directives,
481
-
selectionSet: selectionSet(),
function document(input: string, noLoc: boolean): ast.DocumentNode {
487
-
let match: string | undefined;
488
-
let definition: ast.OperationDefinitionNode | undefined;
const definitions: ast.ExecutableDefinitionNode[] = [];
492
-
if ((match = advance(definitionRe)) === 'fragment') {
494
-
definitions.push(fragmentDefinition());
495
-
} else if ((definition = operationDefinition(match as OperationTypeNode)) != null) {
496
-
definitions.push(definition);
474
+
if (input.charCodeAt(idx) === 123 /*'{'*/) {
476
+
kind: 'OperationDefinition' as Kind.OPERATION_DEFINITION,
477
+
operation: 'query' as OperationTypeNode.QUERY,
479
+
variableDefinitions: undefined,
480
+
directives: undefined,
481
+
selectionSet: selectionSetStart(),
498
-
throw error('Document');
484
+
const definition = name();
485
+
switch (definition) {
487
+
definitions.push(fragmentDefinition());
491
+
case 'subscription':
493
+
let name: ast.NameNode | undefined;
495
+
(char = input.charCodeAt(idx)) !== 40 /*'('*/ &&
496
+
char !== 64 /*'@'*/ &&
497
+
char !== 123 /*'{'*/
502
+
kind: 'OperationDefinition' as Kind.OPERATION_DEFINITION,
503
+
operation: definition as OperationTypeNode,
505
+
variableDefinitions: variableDefinitions(),
506
+
directives: directives(false),
507
+
selectionSet: selectionSetStart(),
511
+
throw error('Document');
} while (idx < input.length);
···
options?: ParseOptions | undefined
546
-
input = typeof string.body === 'string' ? string.body : string;
560
+
input = string.body ? string.body : string;
return document(input, options && options.noLocation);
···
_options?: ParseOptions | undefined
555
-
input = typeof string.body === 'string' ? string.body : string;
569
+
input = string.body ? string.body : string;
···
_options?: ParseOptions | undefined
565
-
input = typeof string.body === 'string' ? string.body : string;
579
+
input = string.body ? string.body : string;