UNPKG

4.72 kBJavaScriptView Raw
1import { inspect } from '../jsutils/inspect.mjs';
2import { invariant } from '../jsutils/invariant.mjs';
3import { isIterableObject } from '../jsutils/isIterableObject.mjs';
4import { isObjectLike } from '../jsutils/isObjectLike.mjs';
5import { Kind } from '../language/kinds.mjs';
6import {
7 isEnumType,
8 isInputObjectType,
9 isLeafType,
10 isListType,
11 isNonNullType,
12} from '../type/definition.mjs';
13import { GraphQLID } from '../type/scalars.mjs';
14/**
15 * Produces a GraphQL Value AST given a JavaScript object.
16 * Function will match JavaScript/JSON values to GraphQL AST schema format
17 * by using suggested GraphQLInputType. For example:
18 *
19 * astFromValue("value", GraphQLString)
20 *
21 * A GraphQL type must be provided, which will be used to interpret different
22 * JavaScript values.
23 *
24 * | JSON Value | GraphQL Value |
25 * | ------------- | -------------------- |
26 * | Object | Input Object |
27 * | Array | List |
28 * | Boolean | Boolean |
29 * | String | String / Enum Value |
30 * | Number | Int / Float |
31 * | Unknown | Enum Value |
32 * | null | NullValue |
33 *
34 */
35
36export function astFromValue(value, type) {
37 if (isNonNullType(type)) {
38 const astValue = astFromValue(value, type.ofType);
39
40 if (
41 (astValue === null || astValue === void 0 ? void 0 : astValue.kind) ===
42 Kind.NULL
43 ) {
44 return null;
45 }
46
47 return astValue;
48 } // only explicit null, not undefined, NaN
49
50 if (value === null) {
51 return {
52 kind: Kind.NULL,
53 };
54 } // undefined
55
56 if (value === undefined) {
57 return null;
58 } // Convert JavaScript array to GraphQL list. If the GraphQLType is a list, but
59 // the value is not an array, convert the value using the list's item type.
60
61 if (isListType(type)) {
62 const itemType = type.ofType;
63
64 if (isIterableObject(value)) {
65 const valuesNodes = [];
66
67 for (const item of value) {
68 const itemNode = astFromValue(item, itemType);
69
70 if (itemNode != null) {
71 valuesNodes.push(itemNode);
72 }
73 }
74
75 return {
76 kind: Kind.LIST,
77 values: valuesNodes,
78 };
79 }
80
81 return astFromValue(value, itemType);
82 } // Populate the fields of the input object by creating ASTs from each value
83 // in the JavaScript object according to the fields in the input type.
84
85 if (isInputObjectType(type)) {
86 if (!isObjectLike(value)) {
87 return null;
88 }
89
90 const fieldNodes = [];
91
92 for (const field of Object.values(type.getFields())) {
93 const fieldValue = astFromValue(value[field.name], field.type);
94
95 if (fieldValue) {
96 fieldNodes.push({
97 kind: Kind.OBJECT_FIELD,
98 name: {
99 kind: Kind.NAME,
100 value: field.name,
101 },
102 value: fieldValue,
103 });
104 }
105 }
106
107 return {
108 kind: Kind.OBJECT,
109 fields: fieldNodes,
110 };
111 }
112
113 if (isLeafType(type)) {
114 // Since value is an internally represented value, it must be serialized
115 // to an externally represented value before converting into an AST.
116 const serialized = type.serialize(value);
117
118 if (serialized == null) {
119 return null;
120 } // Others serialize based on their corresponding JavaScript scalar types.
121
122 if (typeof serialized === 'boolean') {
123 return {
124 kind: Kind.BOOLEAN,
125 value: serialized,
126 };
127 } // JavaScript numbers can be Int or Float values.
128
129 if (typeof serialized === 'number' && Number.isFinite(serialized)) {
130 const stringNum = String(serialized);
131 return integerStringRegExp.test(stringNum)
132 ? {
133 kind: Kind.INT,
134 value: stringNum,
135 }
136 : {
137 kind: Kind.FLOAT,
138 value: stringNum,
139 };
140 }
141
142 if (typeof serialized === 'string') {
143 // Enum types use Enum literals.
144 if (isEnumType(type)) {
145 return {
146 kind: Kind.ENUM,
147 value: serialized,
148 };
149 } // ID types can use Int literals.
150
151 if (type === GraphQLID && integerStringRegExp.test(serialized)) {
152 return {
153 kind: Kind.INT,
154 value: serialized,
155 };
156 }
157
158 return {
159 kind: Kind.STRING,
160 value: serialized,
161 };
162 }
163
164 throw new TypeError(`Cannot convert value to AST: ${inspect(serialized)}.`);
165 }
166 /* c8 ignore next 3 */
167 // Not reachable, all possible types have been considered.
168
169 false || invariant(false, 'Unexpected input type: ' + inspect(type));
170}
171/**
172 * IntValue:
173 * - NegativeSign? 0
174 * - NegativeSign? NonZeroDigit ( Digit+ )?
175 */
176
177const integerStringRegExp = /^-?(?:0|[1-9][0-9]*)$/;