UNPKG

5.08 kBJavaScriptView Raw
1import { inspect } from '../jsutils/inspect.mjs';
2import { invariant } from '../jsutils/invariant.mjs';
3import { keyMap } from '../jsutils/keyMap.mjs';
4import { Kind } from '../language/kinds.mjs';
5import {
6 isInputObjectType,
7 isLeafType,
8 isListType,
9 isNonNullType,
10} from '../type/definition.mjs';
11/**
12 * Produces a JavaScript value given a GraphQL Value AST.
13 *
14 * A GraphQL type must be provided, which will be used to interpret different
15 * GraphQL Value literals.
16 *
17 * Returns `undefined` when the value could not be validly coerced according to
18 * the provided type.
19 *
20 * | GraphQL Value | JSON Value |
21 * | -------------------- | ------------- |
22 * | Input Object | Object |
23 * | List | Array |
24 * | Boolean | Boolean |
25 * | String | String |
26 * | Int / Float | Number |
27 * | Enum Value | Unknown |
28 * | NullValue | null |
29 *
30 */
31
32export function valueFromAST(valueNode, type, variables) {
33 if (!valueNode) {
34 // When there is no node, then there is also no value.
35 // Importantly, this is different from returning the value null.
36 return;
37 }
38
39 if (valueNode.kind === Kind.VARIABLE) {
40 const variableName = valueNode.name.value;
41
42 if (variables == null || variables[variableName] === undefined) {
43 // No valid return value.
44 return;
45 }
46
47 const variableValue = variables[variableName];
48
49 if (variableValue === null && isNonNullType(type)) {
50 return; // Invalid: intentionally return no value.
51 } // Note: This does no further checking that this variable is correct.
52 // This assumes that this query has been validated and the variable
53 // usage here is of the correct type.
54
55 return variableValue;
56 }
57
58 if (isNonNullType(type)) {
59 if (valueNode.kind === Kind.NULL) {
60 return; // Invalid: intentionally return no value.
61 }
62
63 return valueFromAST(valueNode, type.ofType, variables);
64 }
65
66 if (valueNode.kind === Kind.NULL) {
67 // This is explicitly returning the value null.
68 return null;
69 }
70
71 if (isListType(type)) {
72 const itemType = type.ofType;
73
74 if (valueNode.kind === Kind.LIST) {
75 const coercedValues = [];
76
77 for (const itemNode of valueNode.values) {
78 if (isMissingVariable(itemNode, variables)) {
79 // If an array contains a missing variable, it is either coerced to
80 // null or if the item type is non-null, it considered invalid.
81 if (isNonNullType(itemType)) {
82 return; // Invalid: intentionally return no value.
83 }
84
85 coercedValues.push(null);
86 } else {
87 const itemValue = valueFromAST(itemNode, itemType, variables);
88
89 if (itemValue === undefined) {
90 return; // Invalid: intentionally return no value.
91 }
92
93 coercedValues.push(itemValue);
94 }
95 }
96
97 return coercedValues;
98 }
99
100 const coercedValue = valueFromAST(valueNode, itemType, variables);
101
102 if (coercedValue === undefined) {
103 return; // Invalid: intentionally return no value.
104 }
105
106 return [coercedValue];
107 }
108
109 if (isInputObjectType(type)) {
110 if (valueNode.kind !== Kind.OBJECT) {
111 return; // Invalid: intentionally return no value.
112 }
113
114 const coercedObj = Object.create(null);
115 const fieldNodes = keyMap(valueNode.fields, (field) => field.name.value);
116
117 for (const field of Object.values(type.getFields())) {
118 const fieldNode = fieldNodes[field.name];
119
120 if (!fieldNode || isMissingVariable(fieldNode.value, variables)) {
121 if (field.defaultValue !== undefined) {
122 coercedObj[field.name] = field.defaultValue;
123 } else if (isNonNullType(field.type)) {
124 return; // Invalid: intentionally return no value.
125 }
126
127 continue;
128 }
129
130 const fieldValue = valueFromAST(fieldNode.value, field.type, variables);
131
132 if (fieldValue === undefined) {
133 return; // Invalid: intentionally return no value.
134 }
135
136 coercedObj[field.name] = fieldValue;
137 }
138
139 return coercedObj;
140 }
141
142 if (isLeafType(type)) {
143 // Scalars and Enums fulfill parsing a literal value via parseLiteral().
144 // Invalid values represent a failure to parse correctly, in which case
145 // no value is returned.
146 let result;
147
148 try {
149 result = type.parseLiteral(valueNode, variables);
150 } catch (_error) {
151 return; // Invalid: intentionally return no value.
152 }
153
154 if (result === undefined) {
155 return; // Invalid: intentionally return no value.
156 }
157
158 return result;
159 }
160 /* c8 ignore next 3 */
161 // Not reachable, all possible input types have been considered.
162
163 false || invariant(false, 'Unexpected input type: ' + inspect(type));
164} // Returns true if the provided valueNode is a variable which is not defined
165// in the set of variables.
166
167function isMissingVariable(valueNode, variables) {
168 return (
169 valueNode.kind === Kind.VARIABLE &&
170 (variables == null || variables[valueNode.name.value] === undefined)
171 );
172}