summaryrefslogtreecommitdiff
path: root/school/node_modules/graphql/utilities/valueFromAST.js.flow
diff options
context:
space:
mode:
Diffstat (limited to 'school/node_modules/graphql/utilities/valueFromAST.js.flow')
-rw-r--r--school/node_modules/graphql/utilities/valueFromAST.js.flow164
1 files changed, 164 insertions, 0 deletions
diff --git a/school/node_modules/graphql/utilities/valueFromAST.js.flow b/school/node_modules/graphql/utilities/valueFromAST.js.flow
new file mode 100644
index 0000000..23d5588
--- /dev/null
+++ b/school/node_modules/graphql/utilities/valueFromAST.js.flow
@@ -0,0 +1,164 @@
+// @flow strict
+import objectValues from '../polyfills/objectValues';
+
+import type { ObjMap } from '../jsutils/ObjMap';
+import keyMap from '../jsutils/keyMap';
+import inspect from '../jsutils/inspect';
+import invariant from '../jsutils/invariant';
+
+import type { ValueNode } from '../language/ast';
+import { Kind } from '../language/kinds';
+
+import type { GraphQLInputType } from '../type/definition';
+import {
+ isLeafType,
+ isInputObjectType,
+ isListType,
+ isNonNullType,
+} from '../type/definition';
+
+/**
+ * Produces a JavaScript value given a GraphQL Value AST.
+ *
+ * A GraphQL type must be provided, which will be used to interpret different
+ * GraphQL Value literals.
+ *
+ * Returns `undefined` when the value could not be validly coerced according to
+ * the provided type.
+ *
+ * | GraphQL Value | JSON Value |
+ * | -------------------- | ------------- |
+ * | Input Object | Object |
+ * | List | Array |
+ * | Boolean | Boolean |
+ * | String | String |
+ * | Int / Float | Number |
+ * | Enum Value | Mixed |
+ * | NullValue | null |
+ *
+ */
+export function valueFromAST(
+ valueNode: ?ValueNode,
+ type: GraphQLInputType,
+ variables?: ?ObjMap<mixed>,
+): mixed | void {
+ if (!valueNode) {
+ // When there is no node, then there is also no value.
+ // Importantly, this is different from returning the value null.
+ return;
+ }
+
+ if (valueNode.kind === Kind.VARIABLE) {
+ const variableName = valueNode.name.value;
+ if (variables == null || variables[variableName] === undefined) {
+ // No valid return value.
+ return;
+ }
+ const variableValue = variables[variableName];
+ if (variableValue === null && isNonNullType(type)) {
+ return; // Invalid: intentionally return no value.
+ }
+ // Note: This does no further checking that this variable is correct.
+ // This assumes that this query has been validated and the variable
+ // usage here is of the correct type.
+ return variableValue;
+ }
+
+ if (isNonNullType(type)) {
+ if (valueNode.kind === Kind.NULL) {
+ return; // Invalid: intentionally return no value.
+ }
+ return valueFromAST(valueNode, type.ofType, variables);
+ }
+
+ if (valueNode.kind === Kind.NULL) {
+ // This is explicitly returning the value null.
+ return null;
+ }
+
+ if (isListType(type)) {
+ const itemType = type.ofType;
+ if (valueNode.kind === Kind.LIST) {
+ const coercedValues = [];
+ for (const itemNode of valueNode.values) {
+ if (isMissingVariable(itemNode, variables)) {
+ // If an array contains a missing variable, it is either coerced to
+ // null or if the item type is non-null, it considered invalid.
+ if (isNonNullType(itemType)) {
+ return; // Invalid: intentionally return no value.
+ }
+ coercedValues.push(null);
+ } else {
+ const itemValue = valueFromAST(itemNode, itemType, variables);
+ if (itemValue === undefined) {
+ return; // Invalid: intentionally return no value.
+ }
+ coercedValues.push(itemValue);
+ }
+ }
+ return coercedValues;
+ }
+ const coercedValue = valueFromAST(valueNode, itemType, variables);
+ if (coercedValue === undefined) {
+ return; // Invalid: intentionally return no value.
+ }
+ return [coercedValue];
+ }
+
+ if (isInputObjectType(type)) {
+ if (valueNode.kind !== Kind.OBJECT) {
+ return; // Invalid: intentionally return no value.
+ }
+ const coercedObj = Object.create(null);
+ const fieldNodes = keyMap(valueNode.fields, (field) => field.name.value);
+ for (const field of objectValues(type.getFields())) {
+ const fieldNode = fieldNodes[field.name];
+ if (!fieldNode || isMissingVariable(fieldNode.value, variables)) {
+ if (field.defaultValue !== undefined) {
+ coercedObj[field.name] = field.defaultValue;
+ } else if (isNonNullType(field.type)) {
+ return; // Invalid: intentionally return no value.
+ }
+ continue;
+ }
+ const fieldValue = valueFromAST(fieldNode.value, field.type, variables);
+ if (fieldValue === undefined) {
+ return; // Invalid: intentionally return no value.
+ }
+ coercedObj[field.name] = fieldValue;
+ }
+ return coercedObj;
+ }
+
+ // istanbul ignore else (See: 'https://github.com/graphql/graphql-js/issues/2618')
+ if (isLeafType(type)) {
+ // Scalars and Enums fulfill parsing a literal value via parseLiteral().
+ // Invalid values represent a failure to parse correctly, in which case
+ // no value is returned.
+ let result;
+ try {
+ result = type.parseLiteral(valueNode, variables);
+ } catch (_error) {
+ return; // Invalid: intentionally return no value.
+ }
+ if (result === undefined) {
+ return; // Invalid: intentionally return no value.
+ }
+ return result;
+ }
+
+ // istanbul ignore next (Not reachable. All possible input types have been considered)
+ invariant(false, 'Unexpected input type: ' + inspect((type: empty)));
+}
+
+// Returns true if the provided valueNode is a variable which is not defined
+// in the set of variables.
+function isMissingVariable(
+ valueNode: ValueNode,
+ variables: ?ObjMap<mixed>,
+): boolean {
+ return (
+ valueNode.kind === Kind.VARIABLE &&
+ (variables == null || variables[valueNode.name.value] === undefined)
+ );
+}