Comparing version 16.8.2 to 16.9.0-canary.pr.4159.0fa29326c53fcd63c6473c7357c28aa13fa0019d
@@ -9,2 +9,6 @@ import type { ObjMap } from '../jsutils/ObjMap'; | ||
import type { GraphQLSchema } from '../type/schema'; | ||
export interface FieldDetails { | ||
node: FieldNode; | ||
fragmentVariableValues?: ObjMap<unknown> | undefined; | ||
} | ||
/** | ||
@@ -27,3 +31,3 @@ * Given a selectionSet, collects all of the fields and returns them. | ||
selectionSet: SelectionSetNode, | ||
): Map<string, ReadonlyArray<FieldNode>>; | ||
): Map<string, ReadonlyArray<FieldDetails>>; | ||
/** | ||
@@ -46,3 +50,3 @@ * Given an array of field nodes, collects all of the subfields of the passed | ||
returnType: GraphQLObjectType, | ||
fieldNodes: ReadonlyArray<FieldNode>, | ||
): Map<string, ReadonlyArray<FieldNode>>; | ||
fieldEntries: ReadonlyArray<FieldDetails>, | ||
): Map<string, ReadonlyArray<FieldDetails>>; |
@@ -44,2 +44,3 @@ 'use strict'; | ||
new Set(), | ||
undefined, | ||
); | ||
@@ -64,9 +65,9 @@ return fields; | ||
returnType, | ||
fieldNodes, | ||
fieldEntries, | ||
) { | ||
const subFieldNodes = new Map(); | ||
const subFieldEntries = new Map(); | ||
const visitedFragmentNames = new Set(); | ||
for (const node of fieldNodes) { | ||
if (node.selectionSet) { | ||
for (const entry of fieldEntries) { | ||
if (entry.node.selectionSet) { | ||
collectFieldsImpl( | ||
@@ -77,4 +78,4 @@ schema, | ||
returnType, | ||
node.selectionSet, | ||
subFieldNodes, | ||
entry.node.selectionSet, | ||
subFieldEntries, | ||
visitedFragmentNames, | ||
@@ -85,3 +86,3 @@ ); | ||
return subFieldNodes; | ||
return subFieldEntries; | ||
} | ||
@@ -97,2 +98,3 @@ | ||
visitedFragmentNames, | ||
localVariableValues, | ||
) { | ||
@@ -102,3 +104,8 @@ for (const selection of selectionSet.selections) { | ||
case _kinds.Kind.FIELD: { | ||
if (!shouldIncludeNode(variableValues, selection)) { | ||
const vars = | ||
localVariableValues !== null && localVariableValues !== void 0 | ||
? localVariableValues | ||
: variableValues; | ||
if (!shouldIncludeNode(vars, selection)) { | ||
continue; | ||
@@ -111,5 +118,19 @@ } | ||
if (fieldList !== undefined) { | ||
fieldList.push(selection); | ||
fieldList.push({ | ||
node: selection, | ||
fragmentVariableValues: | ||
localVariableValues !== null && localVariableValues !== void 0 | ||
? localVariableValues | ||
: undefined, | ||
}); | ||
} else { | ||
fields.set(name, [selection]); | ||
fields.set(name, [ | ||
{ | ||
node: selection, | ||
fragmentVariableValues: | ||
localVariableValues !== null && localVariableValues !== void 0 | ||
? localVariableValues | ||
: undefined, | ||
}, | ||
]); | ||
} | ||
@@ -158,4 +179,21 @@ | ||
continue; | ||
} | ||
} // We need to introduce a concept of shadowing: | ||
// | ||
// - when a fragment defines a variable that is in the parent scope but not given | ||
// in the fragment-spread we need to look at this variable as undefined and check | ||
// whether the definition has a defaultValue, if not remove it from the variableValues. | ||
// - when a fragment does not define a variable we need to copy it over from the parent | ||
// scope as that variable can still get used in spreads later on in the selectionSet. | ||
// - when a value is passed in through the fragment-spread we need to copy over the key-value | ||
// into our variable-values. | ||
const fragmentVariableValues = fragment.variableDefinitions | ||
? (0, _values.getArgumentValuesFromSpread)( | ||
selection, | ||
schema, | ||
fragment.variableDefinitions, | ||
variableValues, | ||
localVariableValues, | ||
) | ||
: undefined; | ||
collectFieldsImpl( | ||
@@ -169,2 +207,3 @@ schema, | ||
visitedFragmentNames, | ||
fragmentVariableValues, | ||
); | ||
@@ -171,0 +210,0 @@ break; |
@@ -9,3 +9,2 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
DocumentNode, | ||
FieldNode, | ||
FragmentDefinitionNode, | ||
@@ -22,2 +21,3 @@ OperationDefinitionNode, | ||
import type { GraphQLSchema } from '../type/schema'; | ||
import type { FieldDetails } from './collectFields'; | ||
/** | ||
@@ -147,3 +147,3 @@ * Terminology | ||
fieldDef: GraphQLField<unknown, unknown>, | ||
fieldNodes: ReadonlyArray<FieldNode>, | ||
fieldEntries: ReadonlyArray<FieldDetails>, | ||
parentType: GraphQLObjectType, | ||
@@ -187,3 +187,3 @@ path: Path, | ||
parentType: GraphQLObjectType, | ||
fieldNode: FieldNode, | ||
entry: FieldDetails, | ||
): Maybe<GraphQLField<unknown, unknown>>; |
@@ -456,6 +456,6 @@ 'use strict'; | ||
function executeField(exeContext, parentType, source, fieldNodes, path) { | ||
function executeField(exeContext, parentType, source, fieldEntries, path) { | ||
var _fieldDef$resolve; | ||
const fieldDef = getFieldDef(exeContext.schema, parentType, fieldNodes[0]); | ||
const fieldDef = getFieldDef(exeContext.schema, parentType, fieldEntries[0]); | ||
@@ -475,3 +475,3 @@ if (!fieldDef) { | ||
fieldDef, | ||
fieldNodes, | ||
fieldEntries, | ||
parentType, | ||
@@ -486,5 +486,6 @@ path, | ||
const args = (0, _values.getArgumentValues)( | ||
fieldDef, | ||
fieldNodes[0], | ||
fieldEntries[0].node, | ||
fieldDef.args, | ||
exeContext.variableValues, | ||
fieldEntries[0].fragmentVariableValues, | ||
); // The resolve function's optional third argument is a context value that | ||
@@ -500,3 +501,10 @@ // is provided to every resolve function within an execution. It is commonly | ||
completed = result.then((resolved) => | ||
completeValue(exeContext, returnType, fieldNodes, info, path, resolved), | ||
completeValue( | ||
exeContext, | ||
returnType, | ||
fieldEntries, | ||
info, | ||
path, | ||
resolved, | ||
), | ||
); | ||
@@ -507,3 +515,3 @@ } else { | ||
returnType, | ||
fieldNodes, | ||
fieldEntries, | ||
info, | ||
@@ -521,3 +529,3 @@ path, | ||
rawError, | ||
fieldNodes, | ||
fieldEntries.map((entry) => entry.node), | ||
(0, _Path.pathToArray)(path), | ||
@@ -533,3 +541,3 @@ ); | ||
rawError, | ||
fieldNodes, | ||
fieldEntries.map((entry) => entry.node), | ||
(0, _Path.pathToArray)(path), | ||
@@ -544,3 +552,9 @@ ); | ||
function buildResolveInfo(exeContext, fieldDef, fieldNodes, parentType, path) { | ||
function buildResolveInfo( | ||
exeContext, | ||
fieldDef, | ||
fieldEntries, | ||
parentType, | ||
path, | ||
) { | ||
// The resolve function's optional fourth argument is a collection of | ||
@@ -550,3 +564,3 @@ // information about the current execution state. | ||
fieldName: fieldDef.name, | ||
fieldNodes, | ||
fieldNodes: fieldEntries.map((entry) => entry.node), | ||
returnType: fieldDef.type, | ||
@@ -682,3 +696,3 @@ parentType, | ||
returnType, | ||
fieldNodes, | ||
fieldEntries, | ||
info, | ||
@@ -710,3 +724,3 @@ path, | ||
itemType, | ||
fieldNodes, | ||
fieldEntries, | ||
info, | ||
@@ -721,3 +735,3 @@ itemPath, | ||
itemType, | ||
fieldNodes, | ||
fieldEntries, | ||
info, | ||
@@ -736,3 +750,3 @@ itemPath, | ||
rawError, | ||
fieldNodes, | ||
fieldEntries.map((entry) => entry.node), | ||
(0, _Path.pathToArray)(itemPath), | ||
@@ -748,3 +762,3 @@ ); | ||
rawError, | ||
fieldNodes, | ||
fieldEntries.map((entry) => entry.node), | ||
(0, _Path.pathToArray)(itemPath), | ||
@@ -785,3 +799,3 @@ ); | ||
returnType, | ||
fieldNodes, | ||
fieldEntries, | ||
info, | ||
@@ -800,2 +814,3 @@ path, | ||
const runtimeType = resolveTypeFn(result, contextValue, info, returnType); | ||
const fieldNodes = fieldEntries.map((entry) => entry.node); | ||
@@ -814,3 +829,3 @@ if ((0, _isPromise.isPromise)(runtimeType)) { | ||
), | ||
fieldNodes, | ||
fieldEntries, | ||
info, | ||
@@ -833,3 +848,3 @@ path, | ||
), | ||
fieldNodes, | ||
fieldEntries, | ||
info, | ||
@@ -909,3 +924,3 @@ path, | ||
returnType, | ||
fieldNodes, | ||
fieldEntries, | ||
info, | ||
@@ -916,3 +931,3 @@ path, | ||
// Collect sub-fields to execute to complete this value. | ||
const subFieldNodes = collectSubfields(exeContext, returnType, fieldNodes); // If there is an isTypeOf predicate function, call it with the | ||
const subFieldNodes = collectSubfields(exeContext, returnType, fieldEntries); // If there is an isTypeOf predicate function, call it with the | ||
// current result. If isTypeOf returns false, then raise an error rather | ||
@@ -927,3 +942,7 @@ // than continuing execution. | ||
if (!resolvedIsTypeOf) { | ||
throw invalidReturnTypeError(returnType, result, fieldNodes); | ||
throw invalidReturnTypeError( | ||
returnType, | ||
result, | ||
fieldEntries.map((entry) => entry.node), | ||
); | ||
} | ||
@@ -942,3 +961,7 @@ | ||
if (!isTypeOf) { | ||
throw invalidReturnTypeError(returnType, result, fieldNodes); | ||
throw invalidReturnTypeError( | ||
returnType, | ||
result, | ||
fieldEntries.map((entry) => entry.node), | ||
); | ||
} | ||
@@ -1041,4 +1064,4 @@ } | ||
function getFieldDef(schema, parentType, fieldNode) { | ||
const fieldName = fieldNode.name.value; | ||
function getFieldDef(schema, parentType, entry) { | ||
const fieldName = entry.node.name.value; | ||
@@ -1045,0 +1068,0 @@ if ( |
@@ -185,11 +185,11 @@ 'use strict'; | ||
); | ||
const [responseName, fieldNodes] = [...rootFields.entries()][0]; | ||
const fieldDef = (0, _execute.getFieldDef)(schema, rootType, fieldNodes[0]); | ||
const [responseName, fieldEntries] = [...rootFields.entries()][0]; | ||
const fieldDef = (0, _execute.getFieldDef)(schema, rootType, fieldEntries[0]); | ||
if (!fieldDef) { | ||
const fieldName = fieldNodes[0].name.value; | ||
const fieldName = fieldEntries[0].node.name.value; | ||
throw new _GraphQLError.GraphQLError( | ||
`The subscription field "${fieldName}" is not defined.`, | ||
{ | ||
nodes: fieldNodes, | ||
nodes: fieldEntries.map((entry) => entry.node), | ||
}, | ||
@@ -203,3 +203,3 @@ ); | ||
fieldDef, | ||
fieldNodes, | ||
fieldEntries, | ||
rootType, | ||
@@ -217,4 +217,4 @@ path, | ||
const args = (0, _values.getArgumentValues)( | ||
fieldDef, | ||
fieldNodes[0], | ||
fieldEntries[0].node, | ||
fieldDef.args, | ||
variableValues, | ||
@@ -243,3 +243,3 @@ ); // The resolve function's optional third argument is a context value that | ||
error, | ||
fieldNodes, | ||
fieldEntries.map((entry) => entry.node), | ||
(0, _Path.pathToArray)(path), | ||
@@ -246,0 +246,0 @@ ); |
@@ -7,5 +7,6 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
FieldNode, | ||
FragmentSpreadNode, | ||
VariableDefinitionNode, | ||
} from '../language/ast'; | ||
import type { GraphQLField } from '../type/definition'; | ||
import type { GraphQLArgument } from '../type/definition'; | ||
import type { GraphQLDirective } from '../type/directives'; | ||
@@ -52,8 +53,19 @@ import type { GraphQLSchema } from '../type/schema'; | ||
export declare function getArgumentValues( | ||
def: GraphQLField<unknown, unknown> | GraphQLDirective, | ||
node: FieldNode | DirectiveNode, | ||
argDefs: ReadonlyArray<GraphQLArgument>, | ||
variableValues?: Maybe<ObjMap<unknown>>, | ||
fragmentArgValues?: Maybe<ObjMap<unknown>>, | ||
): { | ||
[argument: string]: unknown; | ||
}; | ||
export declare function getArgumentValuesFromSpread( | ||
/** NOTE: For error annotations only */ | ||
node: FragmentSpreadNode, | ||
schema: GraphQLSchema, | ||
fragmentVarDefs: ReadonlyArray<VariableDefinitionNode>, | ||
variableValues: Maybe<ObjMap<unknown>>, | ||
fragmentArgValues?: Maybe<ObjMap<unknown>>, | ||
): { | ||
[argument: string]: unknown; | ||
}; | ||
/** | ||
@@ -60,0 +72,0 @@ * Prepares an object map of argument values given a directive definition |
@@ -7,2 +7,3 @@ 'use strict'; | ||
exports.getArgumentValues = getArgumentValues; | ||
exports.getArgumentValuesFromSpread = getArgumentValuesFromSpread; | ||
exports.getDirectiveValues = getDirectiveValues; | ||
@@ -31,2 +32,4 @@ exports.getVariableValues = getVariableValues; | ||
var _valueFromASTUntyped = require('../utilities/valueFromASTUntyped.js'); | ||
/** | ||
@@ -169,3 +172,3 @@ * Prepares an object map of variableValues of the correct type based on the | ||
function getArgumentValues(def, node, variableValues) { | ||
function getArgumentValues(node, argDefs, variableValues, fragmentArgValues) { | ||
var _node$arguments; | ||
@@ -186,3 +189,3 @@ | ||
for (const argDef of def.args) { | ||
for (const argDef of argDefs) { | ||
const name = argDef.name; | ||
@@ -210,3 +213,3 @@ const argType = argDef.type; | ||
const valueNode = argumentNode.value; | ||
let isNull = valueNode.kind === _kinds.Kind.NULL; | ||
let hasValue = valueNode.kind !== _kinds.Kind.NULL; | ||
@@ -217,26 +220,35 @@ if (valueNode.kind === _kinds.Kind.VARIABLE) { | ||
if ( | ||
variableValues == null || | ||
!hasOwnProperty(variableValues, variableName) | ||
fragmentArgValues != null && | ||
hasOwnProperty(fragmentArgValues, variableName) | ||
) { | ||
if (argDef.defaultValue !== undefined) { | ||
hasValue = fragmentArgValues[variableName] != null; | ||
if (!hasValue && argDef.defaultValue !== undefined) { | ||
coercedValues[name] = argDef.defaultValue; | ||
} else if ((0, _definition.isNonNullType)(argType)) { | ||
throw new _GraphQLError.GraphQLError( | ||
`Argument "${name}" of required type "${(0, _inspect.inspect)( | ||
argType, | ||
)}" ` + | ||
`was provided the variable "$${variableName}" which was not provided a runtime value.`, | ||
{ | ||
nodes: valueNode, | ||
}, | ||
); | ||
continue; | ||
} | ||
} else if ( | ||
variableValues != null && | ||
hasOwnProperty(variableValues, variableName) | ||
) { | ||
hasValue = variableValues[variableName] != null; | ||
} else if (argDef.defaultValue !== undefined) { | ||
coercedValues[name] = argDef.defaultValue; | ||
continue; | ||
} else if ((0, _definition.isNonNullType)(argType)) { | ||
throw new _GraphQLError.GraphQLError( | ||
`Argument "${name}" of required type "${(0, _inspect.inspect)( | ||
argType, | ||
)}" ` + | ||
`was provided the variable "$${variableName}" which was not provided a runtime value.`, | ||
{ | ||
nodes: valueNode, | ||
}, | ||
); | ||
} else { | ||
continue; | ||
} | ||
isNull = variableValues[variableName] == null; | ||
} | ||
if (isNull && (0, _definition.isNonNullType)(argType)) { | ||
if (!hasValue && (0, _definition.isNonNullType)(argType)) { | ||
throw new _GraphQLError.GraphQLError( | ||
@@ -252,7 +264,6 @@ `Argument "${name}" of non-null type "${(0, _inspect.inspect)( | ||
const coercedValue = (0, _valueFromAST.valueFromAST)( | ||
valueNode, | ||
argType, | ||
variableValues, | ||
); | ||
const coercedValue = (0, _valueFromAST.valueFromAST)(valueNode, argType, { | ||
...variableValues, | ||
...fragmentArgValues, | ||
}); | ||
@@ -278,2 +289,93 @@ if (coercedValue === undefined) { | ||
} | ||
function getArgumentValuesFromSpread( | ||
/** NOTE: For error annotations only */ | ||
node, | ||
schema, | ||
fragmentVarDefs, | ||
variableValues, | ||
fragmentArgValues, | ||
) { | ||
var _node$arguments2; | ||
const coercedValues = {}; | ||
const argNodeMap = (0, _keyMap.keyMap)( | ||
(_node$arguments2 = | ||
node === null || node === void 0 ? void 0 : node.arguments) !== null && | ||
_node$arguments2 !== void 0 | ||
? _node$arguments2 | ||
: [], | ||
(arg) => arg.name.value, | ||
); | ||
for (const varDef of fragmentVarDefs) { | ||
const name = varDef.variable.name.value; | ||
const argType = (0, _typeFromAST.typeFromAST)(schema, varDef.type); | ||
const argumentNode = argNodeMap[name]; | ||
if (argumentNode == null) { | ||
if (varDef.defaultValue !== undefined) { | ||
coercedValues[name] = (0, _valueFromASTUntyped.valueFromASTUntyped)( | ||
varDef.defaultValue, | ||
); | ||
} else if ((0, _definition.isNonNullType)(argType)) { | ||
throw new _GraphQLError.GraphQLError( | ||
`Argument "${name}" of required type "${(0, _inspect.inspect)( | ||
argType, | ||
)}" ` + 'was not provided.', | ||
{ | ||
nodes: node, | ||
}, | ||
); | ||
} else { | ||
coercedValues[name] = undefined; | ||
} | ||
continue; | ||
} | ||
const valueNode = argumentNode.value; | ||
let hasValue = valueNode.kind !== _kinds.Kind.NULL; | ||
if (valueNode.kind === _kinds.Kind.VARIABLE) { | ||
const variableName = valueNode.name.value; | ||
if ( | ||
fragmentArgValues != null && | ||
hasOwnProperty(fragmentArgValues, variableName) | ||
) { | ||
hasValue = fragmentArgValues[variableName] != null; | ||
} else if ( | ||
variableValues != null && | ||
hasOwnProperty(variableValues, variableName) | ||
) { | ||
hasValue = variableValues[variableName] != null; | ||
} | ||
} | ||
if (!hasValue && (0, _definition.isNonNullType)(argType)) { | ||
throw new _GraphQLError.GraphQLError( | ||
`Argument "${name}" of non-null type "${(0, _inspect.inspect)( | ||
argType, | ||
)}" ` + 'must not be null.', | ||
{ | ||
nodes: valueNode, | ||
}, | ||
); | ||
} | ||
let coercedValue; | ||
if (argType && (0, _definition.isInputType)(argType)) { | ||
coercedValue = (0, _valueFromAST.valueFromAST)(valueNode, argType, { | ||
...variableValues, | ||
...fragmentArgValues, | ||
}); | ||
} | ||
coercedValues[name] = coercedValue; | ||
} | ||
return coercedValues; | ||
} | ||
/** | ||
@@ -302,3 +404,3 @@ * Prepares an object map of argument values given a directive definition | ||
if (directiveNode) { | ||
return getArgumentValues(directiveDef, directiveNode, variableValues); | ||
return getArgumentValues(directiveNode, directiveDef.args, variableValues); | ||
} | ||
@@ -305,0 +407,0 @@ } |
@@ -57,2 +57,3 @@ /** | ||
GraphQLSpecifiedByDirective, | ||
GraphQLOneOfDirective, | ||
TypeKind, | ||
@@ -304,2 +305,3 @@ DEFAULT_DEPRECATION_REASON, | ||
specifiedRules, | ||
recommendedRules, | ||
ExecutableDefinitionsRule, | ||
@@ -331,2 +333,3 @@ FieldsOnCorrectTypeRule, | ||
VariablesInAllowedPositionRule, | ||
MaxIntrospectionDepthRule, | ||
LoneSchemaDefinitionRule, | ||
@@ -333,0 +336,0 @@ UniqueOperationTypesRule, |
18
index.js
@@ -150,2 +150,8 @@ 'use strict'; | ||
}); | ||
Object.defineProperty(exports, 'GraphQLOneOfDirective', { | ||
enumerable: true, | ||
get: function () { | ||
return _index.GraphQLOneOfDirective; | ||
}, | ||
}); | ||
Object.defineProperty(exports, 'GraphQLScalarType', { | ||
@@ -241,2 +247,8 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, 'MaxIntrospectionDepthRule', { | ||
enumerable: true, | ||
get: function () { | ||
return _index4.MaxIntrospectionDepthRule; | ||
}, | ||
}); | ||
Object.defineProperty(exports, 'NoDeprecatedCustomRule', { | ||
@@ -1136,2 +1148,8 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, 'recommendedRules', { | ||
enumerable: true, | ||
get: function () { | ||
return _index4.recommendedRules; | ||
}, | ||
}); | ||
Object.defineProperty(exports, 'resolveObjMapThunk', { | ||
@@ -1138,0 +1156,0 @@ enumerable: true, |
@@ -236,2 +236,3 @@ import type { Kind } from './kinds'; | ||
readonly name: NameNode; | ||
readonly arguments?: ReadonlyArray<ArgumentNode>; | ||
readonly directives?: ReadonlyArray<DirectiveNode>; | ||
@@ -250,3 +251,2 @@ } | ||
readonly name: NameNode; | ||
/** @deprecated variableDefinitions will be removed in v17.0.0 */ | ||
readonly variableDefinitions?: ReadonlyArray<VariableDefinitionNode>; | ||
@@ -253,0 +253,0 @@ readonly typeCondition: NamedTypeNode; |
@@ -144,6 +144,6 @@ 'use strict'; | ||
Argument: ['name', 'value'], | ||
FragmentSpread: ['name', 'directives'], | ||
FragmentSpread: ['name', 'arguments', 'directives'], | ||
InlineFragment: ['typeCondition', 'directives', 'selectionSet'], | ||
FragmentDefinition: [ | ||
'name', // Note: fragment variable definitions are deprecated and will removed in v17.0.0 | ||
'name', | ||
'variableDefinitions', | ||
@@ -150,0 +150,0 @@ 'typeCondition', |
@@ -26,2 +26,3 @@ /** | ||
INPUT_FIELD_DEFINITION = 'INPUT_FIELD_DEFINITION', | ||
FRAGMENT_VARIABLE_DEFINITION = 'FRAGMENT_VARIABLE_DEFINITION', | ||
} | ||
@@ -28,0 +29,0 @@ export { DirectiveLocation }; |
@@ -34,2 +34,4 @@ 'use strict'; | ||
DirectiveLocation['INPUT_FIELD_DEFINITION'] = 'INPUT_FIELD_DEFINITION'; | ||
DirectiveLocation['FRAGMENT_VARIABLE_DEFINITION'] = | ||
'FRAGMENT_VARIABLE_DEFINITION'; | ||
})(DirectiveLocation || (exports.DirectiveLocation = DirectiveLocation = {})); | ||
@@ -36,0 +38,0 @@ /** |
@@ -91,2 +91,22 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
allowLegacyFragmentVariables?: boolean; | ||
/** | ||
* EXPERIMENTAL: | ||
* | ||
* If enabled, the parser will understand and parse fragment variable definitions | ||
* and arguments on fragment spreads. Fragment variable definitions will be represented | ||
* in the `variableDefinitions` field of the FragmentDefinitionNode. | ||
* Fragment spread arguments will be represented in the `arguments` field of FragmentSpreadNode. | ||
* | ||
* For example: | ||
* | ||
* ```graphql | ||
* { | ||
* t { ...A(var: true) } | ||
* } | ||
* fragment A($var: Boolean = false) on T { | ||
* ...B(x: $var) | ||
* } | ||
* ``` | ||
*/ | ||
experimentalFragmentArguments?: boolean | undefined; | ||
} | ||
@@ -240,3 +260,3 @@ /** | ||
* | ||
* FragmentSpread : ... FragmentName Directives? | ||
* FragmentSpread : ... FragmentName Arguments? Directives? | ||
* | ||
@@ -248,3 +268,3 @@ * InlineFragment : ... TypeCondition? Directives? SelectionSet | ||
* FragmentDefinition : | ||
* - fragment FragmentName on TypeCondition Directives? SelectionSet | ||
* - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet | ||
* | ||
@@ -251,0 +271,0 @@ * TypeCondition : NamedType |
@@ -403,3 +403,3 @@ 'use strict'; | ||
* | ||
* FragmentSpread : ... FragmentName Directives? | ||
* FragmentSpread : ... FragmentName Arguments? Directives? | ||
* | ||
@@ -415,5 +415,19 @@ * InlineFragment : ... TypeCondition? Directives? SelectionSet | ||
if (!hasTypeCondition && this.peek(_tokenKind.TokenKind.NAME)) { | ||
const name = this.parseFragmentName(); | ||
if ( | ||
this.peek(_tokenKind.TokenKind.PAREN_L) && | ||
this._options.experimentalFragmentArguments | ||
) { | ||
return this.node(start, { | ||
kind: _kinds.Kind.FRAGMENT_SPREAD, | ||
name, | ||
arguments: this.parseArguments(false), | ||
directives: this.parseDirectives(false), | ||
}); | ||
} | ||
return this.node(start, { | ||
kind: _kinds.Kind.FRAGMENT_SPREAD, | ||
name: this.parseFragmentName(), | ||
name, | ||
directives: this.parseDirectives(false), | ||
@@ -432,3 +446,3 @@ }); | ||
* FragmentDefinition : | ||
* - fragment FragmentName on TypeCondition Directives? SelectionSet | ||
* - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet | ||
* | ||
@@ -440,20 +454,11 @@ * TypeCondition : NamedType | ||
const start = this._lexer.token; | ||
this.expectKeyword('fragment'); // Legacy support for defining variables within fragments changes | ||
// the grammar of FragmentDefinition: | ||
// - fragment FragmentName VariableDefinitions? on TypeCondition Directives? SelectionSet | ||
if (this._options.allowLegacyFragmentVariables === true) { | ||
return this.node(start, { | ||
kind: _kinds.Kind.FRAGMENT_DEFINITION, | ||
name: this.parseFragmentName(), | ||
variableDefinitions: this.parseVariableDefinitions(), | ||
typeCondition: (this.expectKeyword('on'), this.parseNamedType()), | ||
directives: this.parseDirectives(false), | ||
selectionSet: this.parseSelectionSet(), | ||
}); | ||
} | ||
this.expectKeyword('fragment'); | ||
return this.node(start, { | ||
kind: _kinds.Kind.FRAGMENT_DEFINITION, | ||
name: this.parseFragmentName(), | ||
variableDefinitions: | ||
this._options.experimentalFragmentArguments === true || | ||
this._options.allowLegacyFragmentVariables === true | ||
? this.parseVariableDefinitions() | ||
: undefined, | ||
typeCondition: (this.expectKeyword('on'), this.parseNamedType()), | ||
@@ -460,0 +465,0 @@ directives: this.parseDirectives(false), |
@@ -64,9 +64,6 @@ 'use strict'; | ||
const prefix = wrap('', alias, ': ') + name; | ||
let argsLine = prefix + wrap('(', join(args, ', '), ')'); | ||
if (argsLine.length > MAX_LINE_LENGTH) { | ||
argsLine = prefix + wrap('(\n', indent(join(args, '\n')), '\n)'); | ||
} | ||
return join([argsLine, join(directives, ' '), selectionSet], ' '); | ||
return join( | ||
[wrappedLineAndArgs(prefix, args), join(directives, ' '), selectionSet], | ||
' ', | ||
); | ||
}, | ||
@@ -79,4 +76,8 @@ }, | ||
FragmentSpread: { | ||
leave: ({ name, directives }) => | ||
'...' + name + wrap(' ', join(directives, ' ')), | ||
leave: ({ name, arguments: args, directives }) => { | ||
const prefix = '...' + name; | ||
return ( | ||
wrappedLineAndArgs(prefix, args) + wrap(' ', join(directives, ' ')) | ||
); | ||
}, | ||
}, | ||
@@ -354,1 +355,11 @@ InlineFragment: { | ||
} | ||
function wrappedLineAndArgs(prefix, args) { | ||
let argsLine = prefix + wrap('(', join(args, ', '), ')'); | ||
if (argsLine.length > MAX_LINE_LENGTH) { | ||
argsLine = prefix + wrap('(\n', indent(join(args, '\n')), '\n)'); | ||
} | ||
return argsLine; | ||
} |
{ | ||
"name": "graphql", | ||
"version": "16.8.2", | ||
"version": "16.9.0-canary.pr.4159.0fa29326c53fcd63c6473c7357c28aa13fa0019d", | ||
"description": "A Query Language and Runtime which can target any service.", | ||
@@ -37,4 +37,5 @@ "license": "MIT", | ||
"publishConfig": { | ||
"tag": "latest" | ||
} | ||
} | ||
"tag": "canary-pr-4159" | ||
}, | ||
"deprecated": "You are using canary version build from https://github.com/graphql/graphql-js/pull/4159, no gurantees provided so please use your own discretion." | ||
} |
@@ -0,1 +1,3 @@ | ||
[![GraphQLConf 2024 Banner: September 10-12, San Francisco. Hosted by the GraphQL Foundation](https://github.com/user-attachments/assets/2d048502-e5b2-4e9d-a02a-50b841824de6)](https://graphql.org/conf/2024/?utm_source=github&utm_medium=graphql_js&utm_campaign=readme) | ||
# GraphQL.js | ||
@@ -2,0 +4,0 @@ |
@@ -792,3 +792,3 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
description?: Maybe<string>; | ||
values: GraphQLEnumValueConfigMap; | ||
values: ThunkObjMap<GraphQLEnumValueConfig>; | ||
extensions?: Maybe<Readonly<GraphQLEnumTypeExtensions>>; | ||
@@ -799,2 +799,3 @@ astNode?: Maybe<EnumTypeDefinitionNode>; | ||
interface GraphQLEnumTypeNormalizedConfig extends GraphQLEnumTypeConfig { | ||
values: ObjMap<GraphQLEnumValueConfig>; | ||
extensions: Readonly<GraphQLEnumTypeExtensions>; | ||
@@ -870,2 +871,3 @@ extensionASTNodes: ReadonlyArray<EnumTypeExtensionNode>; | ||
extensionASTNodes: ReadonlyArray<InputObjectTypeExtensionNode>; | ||
isOneOf: boolean; | ||
private _fields; | ||
@@ -886,2 +888,3 @@ constructor(config: Readonly<GraphQLInputObjectTypeConfig>); | ||
extensionASTNodes?: Maybe<ReadonlyArray<InputObjectTypeExtensionNode>>; | ||
isOneOf?: boolean; | ||
} | ||
@@ -888,0 +891,0 @@ interface GraphQLInputObjectTypeNormalizedConfig |
@@ -1080,7 +1080,8 @@ 'use strict'; | ||
: []; | ||
this._values = defineEnumValues(this.name, config.values); | ||
this._valueLookup = new Map( | ||
this._values.map((enumValue) => [enumValue.value, enumValue]), | ||
); | ||
this._nameLookup = (0, _keyMap.keyMap)(this._values, (value) => value.name); | ||
this._values = | ||
typeof config.values === 'function' | ||
? config.values | ||
: defineEnumValues(this.name, config.values); | ||
this._valueLookup = null; | ||
this._nameLookup = null; | ||
} | ||
@@ -1093,2 +1094,6 @@ | ||
getValues() { | ||
if (typeof this._values === 'function') { | ||
this._values = defineEnumValues(this.name, this._values()); | ||
} | ||
return this._values; | ||
@@ -1098,2 +1103,9 @@ } | ||
getValue(name) { | ||
if (this._nameLookup === null) { | ||
this._nameLookup = (0, _keyMap.keyMap)( | ||
this.getValues(), | ||
(value) => value.name, | ||
); | ||
} | ||
return this._nameLookup[name]; | ||
@@ -1103,2 +1115,8 @@ } | ||
serialize(outputValue) { | ||
if (this._valueLookup === null) { | ||
this._valueLookup = new Map( | ||
this.getValues().map((enumValue) => [enumValue.value, enumValue]), | ||
); | ||
} | ||
const enumValue = this._valueLookup.get(outputValue); | ||
@@ -1260,3 +1278,3 @@ | ||
constructor(config) { | ||
var _config$extensionASTN6; | ||
var _config$extensionASTN6, _config$isOneOf; | ||
@@ -1272,2 +1290,6 @@ this.name = (0, _assertName.assertName)(config.name); | ||
: []; | ||
this.isOneOf = | ||
(_config$isOneOf = config.isOneOf) !== null && _config$isOneOf !== void 0 | ||
? _config$isOneOf | ||
: false; | ||
this._fields = defineInputFieldMap.bind(undefined, config); | ||
@@ -1304,2 +1326,3 @@ } | ||
extensionASTNodes: this.extensionASTNodes, | ||
isOneOf: this.isOneOf, | ||
}; | ||
@@ -1306,0 +1329,0 @@ } |
@@ -80,2 +80,6 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
/** | ||
* Used to indicate an Input Object is a OneOf Input Object. | ||
*/ | ||
export declare const GraphQLOneOfDirective: GraphQLDirective; | ||
/** | ||
* The full list of specified directives. | ||
@@ -82,0 +86,0 @@ */ |
@@ -8,2 +8,3 @@ 'use strict'; | ||
exports.GraphQLSkipDirective = | ||
exports.GraphQLOneOfDirective = | ||
exports.GraphQLIncludeDirective = | ||
@@ -211,6 +212,18 @@ exports.GraphQLDirective = | ||
/** | ||
* Used to indicate an Input Object is a OneOf Input Object. | ||
*/ | ||
exports.GraphQLSpecifiedByDirective = GraphQLSpecifiedByDirective; | ||
const GraphQLOneOfDirective = new GraphQLDirective({ | ||
name: 'oneOf', | ||
description: | ||
'Indicates exactly one field must be supplied and this field must not be `null`.', | ||
locations: [_directiveLocation.DirectiveLocation.INPUT_OBJECT], | ||
args: {}, | ||
}); | ||
/** | ||
* The full list of specified directives. | ||
*/ | ||
exports.GraphQLSpecifiedByDirective = GraphQLSpecifiedByDirective; | ||
exports.GraphQLOneOfDirective = GraphQLOneOfDirective; | ||
const specifiedDirectives = Object.freeze([ | ||
@@ -221,2 +234,3 @@ GraphQLIncludeDirective, | ||
GraphQLSpecifiedByDirective, | ||
GraphQLOneOfDirective, | ||
]); | ||
@@ -223,0 +237,0 @@ exports.specifiedDirectives = specifiedDirectives; |
@@ -116,2 +116,3 @@ export type { Path as ResponsePath } from '../jsutils/Path'; | ||
GraphQLSpecifiedByDirective, | ||
GraphQLOneOfDirective, | ||
DEFAULT_DEPRECATION_REASON, | ||
@@ -118,0 +119,0 @@ } from './directives'; |
@@ -102,2 +102,8 @@ 'use strict'; | ||
}); | ||
Object.defineProperty(exports, 'GraphQLOneOfDirective', { | ||
enumerable: true, | ||
get: function () { | ||
return _directives.GraphQLOneOfDirective; | ||
}, | ||
}); | ||
Object.defineProperty(exports, 'GraphQLScalarType', { | ||
@@ -104,0 +110,0 @@ enumerable: true, |
@@ -170,4 +170,8 @@ 'use strict'; | ||
value: _directiveLocation.DirectiveLocation.VARIABLE_DEFINITION, | ||
description: 'Location adjacent to a variable definition.', | ||
description: 'Location adjacent to an operation variable definition.', | ||
}, | ||
FRAGMENT_VARIABLE_DEFINITION: { | ||
value: _directiveLocation.DirectiveLocation.FRAGMENT_VARIABLE_DEFINITION, | ||
description: 'Location adjacent to a fragment variable definition.', | ||
}, | ||
SCHEMA: { | ||
@@ -377,2 +381,10 @@ value: _directiveLocation.DirectiveLocation.SCHEMA, | ||
}, | ||
isOneOf: { | ||
type: _scalars.GraphQLBoolean, | ||
resolve: (type) => { | ||
if ((0, _definition.isInputObjectType)(type)) { | ||
return type.isOneOf; | ||
} | ||
}, | ||
}, | ||
}), | ||
@@ -379,0 +391,0 @@ }); |
@@ -571,5 +571,29 @@ 'use strict'; | ||
} | ||
if (inputObj.isOneOf) { | ||
validateOneOfInputObjectField(inputObj, field, context); | ||
} | ||
} | ||
} | ||
function validateOneOfInputObjectField(type, field, context) { | ||
if ((0, _definition.isNonNullType)(field.type)) { | ||
var _field$astNode4; | ||
context.reportError( | ||
`OneOf input field ${type.name}.${field.name} must be nullable.`, | ||
(_field$astNode4 = field.astNode) === null || _field$astNode4 === void 0 | ||
? void 0 | ||
: _field$astNode4.type, | ||
); | ||
} | ||
if (field.defaultValue !== undefined) { | ||
context.reportError( | ||
`OneOf input field ${type.name}.${field.name} cannot have a default value.`, | ||
field.astNode, | ||
); | ||
} | ||
} | ||
function createInputObjectCircularRefsValidator(context) { | ||
@@ -576,0 +600,0 @@ // Modified copy of algorithm from 'src/validation/rules/NoFragmentCycles.js'. |
@@ -108,2 +108,6 @@ 'use strict'; | ||
: options.allowLegacyFragmentVariables, | ||
experimentalFragmentArguments: | ||
options === null || options === void 0 | ||
? void 0 | ||
: options.experimentalFragmentArguments, | ||
}); | ||
@@ -110,0 +114,0 @@ return buildASTSchema(document, { |
@@ -282,2 +282,3 @@ 'use strict'; | ||
fields: () => buildInputValueDefMap(inputObjectIntrospection.inputFields), | ||
isOneOf: inputObjectIntrospection.isOneOf, | ||
}); | ||
@@ -284,0 +285,0 @@ } |
@@ -142,2 +142,27 @@ 'use strict'; | ||
if (type.isOneOf) { | ||
const keys = Object.keys(coercedValue); | ||
if (keys.length !== 1) { | ||
onError( | ||
(0, _Path.pathToArray)(path), | ||
inputValue, | ||
new _GraphQLError.GraphQLError( | ||
`Exactly one key must be specified for OneOf type "${type.name}".`, | ||
), | ||
); | ||
} | ||
const key = keys[0]; | ||
const value = coercedValue[key]; | ||
if (value === null) { | ||
onError( | ||
(0, _Path.pathToArray)(path).concat(key), | ||
value, | ||
new _GraphQLError.GraphQLError(`Field "${key}" must be non-null.`), | ||
); | ||
} | ||
} | ||
return coercedValue; | ||
@@ -144,0 +169,0 @@ } |
@@ -760,2 +760,3 @@ 'use strict'; | ||
extensionASTNodes, | ||
isOneOf: isOneOf(astNode), | ||
}); | ||
@@ -800,1 +801,10 @@ } | ||
} | ||
/** | ||
* Given an input object node, returns if the node should be OneOf. | ||
*/ | ||
function isOneOf(node) { | ||
return Boolean( | ||
(0, _values.getDirectiveValues)(_directives.GraphQLOneOfDirective, node), | ||
); | ||
} |
@@ -29,2 +29,7 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
inputValueDeprecation?: boolean; | ||
/** | ||
* Whether target GraphQL server supports `@oneOf` input objects. | ||
* Default: false | ||
*/ | ||
oneOf?: boolean; | ||
} | ||
@@ -116,2 +121,3 @@ /** | ||
readonly inputFields: ReadonlyArray<IntrospectionInputValue>; | ||
readonly isOneOf: boolean; | ||
} | ||
@@ -118,0 +124,0 @@ export interface IntrospectionListTypeRef< |
@@ -19,2 +19,3 @@ 'use strict'; | ||
inputValueDeprecation: false, | ||
oneOf: false, | ||
...options, | ||
@@ -37,2 +38,3 @@ }; | ||
const oneOf = optionsWithDefault.oneOf ? 'isOneOf' : ''; | ||
return ` | ||
@@ -65,2 +67,3 @@ query IntrospectionQuery { | ||
${specifiedByUrl} | ||
${oneOf} | ||
fields(includeDeprecated: true) { | ||
@@ -67,0 +70,0 @@ name |
@@ -31,2 +31,3 @@ 'use strict'; | ||
inputValueDeprecation: true, | ||
oneOf: true, | ||
...options, | ||
@@ -33,0 +34,0 @@ }; |
@@ -217,3 +217,8 @@ 'use strict'; | ||
); | ||
return printDescription(type) + `input ${type.name}` + printBlock(fields); | ||
return ( | ||
printDescription(type) + | ||
`input ${type.name}` + | ||
(type.isOneOf ? ' @oneOf' : '') + | ||
printBlock(fields) | ||
); | ||
} | ||
@@ -220,0 +225,0 @@ |
@@ -30,2 +30,4 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
private _enumValue; | ||
private _fragmentSpread; | ||
private _fragmentDefinitions; | ||
private _getFieldDef; | ||
@@ -32,0 +34,0 @@ constructor( |
@@ -21,2 +21,4 @@ 'use strict'; | ||
var _valueFromAST = require('./valueFromAST.js'); | ||
/** | ||
@@ -47,2 +49,4 @@ * TypeInfo is a utility class which, given a GraphQL schema, can keep track | ||
this._enumValue = null; | ||
this._fragmentSpread = null; | ||
this._fragmentDefinitions = {}; | ||
this._getFieldDef = | ||
@@ -127,2 +131,15 @@ getFieldDefFn !== null && getFieldDefFn !== void 0 | ||
switch (node.kind) { | ||
case _kinds.Kind.DOCUMENT: { | ||
// A document's fragment definitions are type signatures | ||
// referenced via fragment spreads. Ensure we can use definitions | ||
// before visiting their call sites. | ||
for (const astNode of node.definitions) { | ||
if (astNode.kind === _kinds.Kind.FRAGMENT_DEFINITION) { | ||
this._fragmentDefinitions[astNode.name.value] = astNode; | ||
} | ||
} | ||
break; | ||
} | ||
case _kinds.Kind.SELECTION_SET: { | ||
@@ -174,2 +191,7 @@ const namedType = (0, _definition.getNamedType)(this.getType()); | ||
case _kinds.Kind.FRAGMENT_SPREAD: { | ||
this._fragmentSpread = node; | ||
break; | ||
} | ||
case _kinds.Kind.INLINE_FRAGMENT: | ||
@@ -200,22 +222,62 @@ case _kinds.Kind.FRAGMENT_DEFINITION: { | ||
case _kinds.Kind.ARGUMENT: { | ||
var _this$getDirective; | ||
let argDef; | ||
let argType; | ||
const fieldOrDirective = | ||
(_this$getDirective = this.getDirective()) !== null && | ||
_this$getDirective !== void 0 | ||
? _this$getDirective | ||
: this.getFieldDef(); | ||
const directive = this.getDirective(); | ||
const fragmentSpread = this._fragmentSpread; | ||
const fieldDef = this.getFieldDef(); | ||
if (fieldOrDirective) { | ||
argDef = fieldOrDirective.args.find( | ||
(arg) => arg.name === node.name.value, | ||
); | ||
if (directive) { | ||
argDef = directive.args.find((arg) => arg.name === node.name.value); | ||
} else if (fragmentSpread) { | ||
var _fragmentDef$variable; | ||
if (argDef) { | ||
argType = argDef.type; | ||
const fragmentDef = | ||
this._fragmentDefinitions[fragmentSpread.name.value]; | ||
const fragVarDef = | ||
fragmentDef === null || fragmentDef === void 0 | ||
? void 0 | ||
: (_fragmentDef$variable = fragmentDef.variableDefinitions) === | ||
null || _fragmentDef$variable === void 0 | ||
? void 0 | ||
: _fragmentDef$variable.find( | ||
(varDef) => varDef.variable.name.value === node.name.value, | ||
); | ||
if (fragVarDef) { | ||
const fragVarType = (0, _typeFromAST.typeFromAST)( | ||
schema, | ||
fragVarDef.type, | ||
); | ||
if ((0, _definition.isInputType)(fragVarType)) { | ||
const fragVarDefault = fragVarDef.defaultValue | ||
? (0, _valueFromAST.valueFromAST)( | ||
fragVarDef.defaultValue, | ||
fragVarType, | ||
) | ||
: undefined; | ||
const schemaArgDef = { | ||
name: fragVarDef.variable.name.value, | ||
type: fragVarType, | ||
defaultValue: fragVarDefault, | ||
description: undefined, | ||
deprecationReason: undefined, | ||
extensions: {}, | ||
astNode: { | ||
...fragVarDef, | ||
kind: _kinds.Kind.INPUT_VALUE_DEFINITION, | ||
name: fragVarDef.variable.name, | ||
}, | ||
}; | ||
argDef = schemaArgDef; | ||
} | ||
} | ||
} else if (fieldDef) { | ||
argDef = fieldDef.args.find((arg) => arg.name === node.name.value); | ||
} | ||
if (argDef) { | ||
argType = argDef.type; | ||
} | ||
this._argument = argDef; | ||
@@ -291,2 +353,6 @@ | ||
switch (node.kind) { | ||
case _kinds.Kind.DOCUMENT: | ||
this._fragmentDefinitions = {}; | ||
break; | ||
case _kinds.Kind.SELECTION_SET: | ||
@@ -308,2 +374,6 @@ this._parentTypeStack.pop(); | ||
case _kinds.Kind.FRAGMENT_SPREAD: | ||
this._fragmentSpread = null; | ||
break; | ||
case _kinds.Kind.OPERATION_DEFINITION: | ||
@@ -310,0 +380,0 @@ case _kinds.Kind.INLINE_FRAGMENT: |
@@ -148,2 +148,14 @@ 'use strict'; | ||
if (type.isOneOf) { | ||
const keys = Object.keys(coercedObj); | ||
if (keys.length !== 1) { | ||
return; // Invalid: not exactly one key, intentionally return no value. | ||
} | ||
if (coercedObj[keys[0]] === null) { | ||
return; // Invalid: value not non-null, intentionally return no value. | ||
} | ||
} | ||
return coercedObj; | ||
@@ -150,0 +162,0 @@ } |
export { validate } from './validate'; | ||
export { ValidationContext } from './ValidationContext'; | ||
export type { ValidationRule } from './ValidationContext'; | ||
export { specifiedRules } from './specifiedRules'; | ||
export { specifiedRules, recommendedRules } from './specifiedRules'; | ||
export { ExecutableDefinitionsRule } from './rules/ExecutableDefinitionsRule'; | ||
@@ -20,2 +20,3 @@ export { FieldsOnCorrectTypeRule } from './rules/FieldsOnCorrectTypeRule'; | ||
export { ProvidedRequiredArgumentsRule } from './rules/ProvidedRequiredArgumentsRule'; | ||
export { NoUnusedFragmentVariablesRule } from './rules/NoUnusedFragmentVariablesRule'; | ||
export { ScalarLeafsRule } from './rules/ScalarLeafsRule'; | ||
@@ -32,2 +33,3 @@ export { SingleFieldSubscriptionsRule } from './rules/SingleFieldSubscriptionsRule'; | ||
export { VariablesInAllowedPositionRule } from './rules/VariablesInAllowedPositionRule'; | ||
export { MaxIntrospectionDepthRule } from './rules/MaxIntrospectionDepthRule'; | ||
export { LoneSchemaDefinitionRule } from './rules/LoneSchemaDefinitionRule'; | ||
@@ -34,0 +36,0 @@ export { UniqueOperationTypesRule } from './rules/UniqueOperationTypesRule'; |
@@ -60,2 +60,8 @@ 'use strict'; | ||
}); | ||
Object.defineProperty(exports, 'MaxIntrospectionDepthRule', { | ||
enumerable: true, | ||
get: function () { | ||
return _MaxIntrospectionDepthRule.MaxIntrospectionDepthRule; | ||
}, | ||
}); | ||
Object.defineProperty(exports, 'NoDeprecatedCustomRule', { | ||
@@ -85,2 +91,8 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, 'NoUnusedFragmentVariablesRule', { | ||
enumerable: true, | ||
get: function () { | ||
return _NoUnusedFragmentVariablesRule.NoUnusedFragmentVariablesRule; | ||
}, | ||
}); | ||
Object.defineProperty(exports, 'NoUnusedFragmentsRule', { | ||
@@ -230,2 +242,8 @@ enumerable: true, | ||
}); | ||
Object.defineProperty(exports, 'recommendedRules', { | ||
enumerable: true, | ||
get: function () { | ||
return _specifiedRules.recommendedRules; | ||
}, | ||
}); | ||
Object.defineProperty(exports, 'specifiedRules', { | ||
@@ -280,2 +298,4 @@ enumerable: true, | ||
var _NoUnusedFragmentVariablesRule = require('./rules/NoUnusedFragmentVariablesRule.js'); | ||
var _ScalarLeafsRule = require('./rules/ScalarLeafsRule.js'); | ||
@@ -303,2 +323,4 @@ | ||
var _MaxIntrospectionDepthRule = require('./rules/MaxIntrospectionDepthRule.js'); | ||
var _LoneSchemaDefinitionRule = require('./rules/LoneSchemaDefinitionRule.js'); | ||
@@ -305,0 +327,0 @@ |
@@ -99,4 +99,9 @@ 'use strict'; | ||
case _kinds.Kind.VARIABLE_DEFINITION: | ||
return _directiveLocation.DirectiveLocation.VARIABLE_DEFINITION; | ||
case _kinds.Kind.VARIABLE_DEFINITION: { | ||
const parentNode = ancestors[ancestors.length - 3]; | ||
'kind' in parentNode || (0, _invariant.invariant)(false); | ||
return parentNode.kind === _kinds.Kind.OPERATION_DEFINITION | ||
? _directiveLocation.DirectiveLocation.VARIABLE_DEFINITION | ||
: _directiveLocation.DirectiveLocation.FRAGMENT_VARIABLE_DEFINITION; | ||
} | ||
@@ -103,0 +108,0 @@ case _kinds.Kind.SCHEMA_DEFINITION: |
@@ -29,3 +29,7 @@ 'use strict'; | ||
for (const { node } of usages) { | ||
for (const { node, fragmentVarDef } of usages) { | ||
if (fragmentVarDef) { | ||
continue; | ||
} | ||
const varName = node.name.value; | ||
@@ -32,0 +36,0 @@ |
@@ -9,3 +9,3 @@ import type { ASTVisitor } from '../../language/visitor'; | ||
* | ||
* See https://spec.graphql.org/draft/#sec-All-Variables-Used | ||
* See https://spec.graphql.org/draft/#sec-All-Operation-Variables-Used | ||
*/ | ||
@@ -12,0 +12,0 @@ export declare function NoUnusedVariablesRule( |
@@ -16,3 +16,3 @@ 'use strict'; | ||
* | ||
* See https://spec.graphql.org/draft/#sec-All-Variables-Used | ||
* See https://spec.graphql.org/draft/#sec-All-Operation-Variables-Used | ||
*/ | ||
@@ -29,3 +29,3 @@ function NoUnusedVariablesRule(context) { | ||
const variableNameUsed = Object.create(null); | ||
const usages = context.getRecursiveVariableUsages(operation); | ||
const usages = context.getOperationVariableUsages(operation); | ||
@@ -32,0 +32,0 @@ for (const { node } of usages) { |
@@ -16,2 +16,4 @@ 'use strict'; | ||
var _visitor = require('../../language/visitor.js'); | ||
var _definition = require('../../type/definition.js'); | ||
@@ -54,3 +56,3 @@ | ||
const cachedFieldsAndFragmentNames = new Map(); | ||
const cachedFieldsAndFragmentSpreads = new Map(); | ||
return { | ||
@@ -60,3 +62,3 @@ SelectionSet(selectionSet) { | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -82,2 +84,12 @@ context.getParentType(), | ||
const printFragmentSpreadArguments = (fragmentSpread) => { | ||
if (!fragmentSpread.arguments || fragmentSpread.arguments.length === 0) { | ||
return fragmentSpread.name.value; | ||
} | ||
const printedArguments = fragmentSpread.arguments | ||
.map(_printer.print) | ||
.sort((a, b) => a.localeCompare(b)); | ||
return fragmentSpread.name.value + '(' + printedArguments.join(',') + ')'; | ||
}; | ||
/** | ||
@@ -140,5 +152,6 @@ * Algorithm: | ||
// GraphQL Document. | ||
function findConflictsWithinSelectionSet( | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -149,8 +162,8 @@ parentType, | ||
const conflicts = []; | ||
const [fieldMap, fragmentNames] = getFieldsAndFragmentNames( | ||
const [fieldMap, fragmentSpreadMap] = getFieldsAndFragmentSpreads( | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
parentType, | ||
selectionSet, | ||
); // (A) Find find all conflicts "within" the fields of this selection set. | ||
); // (A) First find all conflicts "within" the fields and fragment-spreads of this selection set. | ||
// Note: this is the *only place* `collectConflictsWithin` is called. | ||
@@ -161,19 +174,24 @@ | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
fieldMap, | ||
); | ||
const allFragmentSpreads = []; | ||
if (fragmentNames.length !== 0) { | ||
for (const [, fragmentSpreads] of Object.entries(fragmentSpreadMap)) { | ||
allFragmentSpreads.push(...fragmentSpreads); | ||
} | ||
if (allFragmentSpreads.length !== 0) { | ||
// (B) Then collect conflicts between these fields and those represented by | ||
// each spread fragment name found. | ||
for (let i = 0; i < fragmentNames.length; i++) { | ||
for (let i = 0; i < allFragmentSpreads.length; i++) { | ||
collectConflictsBetweenFieldsAndFragment( | ||
context, | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
false, | ||
fieldMap, | ||
fragmentNames[i], | ||
allFragmentSpreads[i], | ||
); // (C) Then compare this fragment with all other fragments found in this | ||
@@ -184,11 +202,11 @@ // selection set to collect conflicts between fragments spread together. | ||
for (let j = i + 1; j < fragmentNames.length; j++) { | ||
for (let j = i + 1; j < allFragmentSpreads.length; j++) { | ||
collectConflictsBetweenFragments( | ||
context, | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
false, | ||
fragmentNames[i], | ||
fragmentNames[j], | ||
allFragmentSpreads[i], | ||
allFragmentSpreads[j], | ||
); | ||
@@ -206,8 +224,9 @@ } | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
areMutuallyExclusive, | ||
fieldMap, | ||
fragmentName, | ||
fragmentSpread, | ||
) { | ||
const fragmentName = fragmentSpread.name.value; | ||
const fragment = context.getFragment(fragmentName); | ||
@@ -220,6 +239,7 @@ | ||
const [fieldMap2, referencedFragmentNames] = | ||
getReferencedFieldsAndFragmentNames( | ||
getReferencedFieldsAndFragmentSpreads( | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
fragment, | ||
fragmentSpread, | ||
); // Do not compare a fragment's fieldMap to itself. | ||
@@ -235,3 +255,3 @@ | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -244,3 +264,5 @@ areMutuallyExclusive, | ||
for (const referencedFragmentName of referencedFragmentNames) { | ||
for (const [referencedFragmentName, [spread]] of Object.entries( | ||
referencedFragmentNames, | ||
)) { | ||
// Memoize so two fragments are not compared for conflicts more than once. | ||
@@ -265,7 +287,7 @@ if ( | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
areMutuallyExclusive, | ||
fieldMap, | ||
referencedFragmentName, | ||
spread, | ||
); | ||
@@ -279,19 +301,33 @@ } | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
areMutuallyExclusive, | ||
fragmentName1, | ||
fragmentName2, | ||
fragmentSpread1, | ||
fragmentSpread2, | ||
) { | ||
// No need to compare a fragment to itself. | ||
if (fragmentName1 === fragmentName2) { | ||
const fragmentName1 = fragmentSpread1.name.value; | ||
const fragmentName2 = fragmentSpread2.name.value; | ||
if ( | ||
fragmentName1 === fragmentName2 && | ||
!sameArguments(fragmentSpread1, fragmentSpread2) | ||
) { | ||
context.reportError( | ||
new _GraphQLError.GraphQLError( | ||
`Spreads "${fragmentName1}" conflict because ${printFragmentSpreadArguments( | ||
fragmentSpread1, | ||
)} and ${printFragmentSpreadArguments( | ||
fragmentSpread2, | ||
)} have different fragment arguments.`, | ||
{ | ||
nodes: [fragmentSpread1, fragmentSpread2], | ||
}, | ||
), | ||
); | ||
return; | ||
} // Memoize so two fragments are not compared for conflicts more than once. | ||
} // No need to compare a fragment to itself. | ||
if ( | ||
comparedFragmentPairs.has( | ||
fragmentName1, | ||
fragmentName2, | ||
areMutuallyExclusive, | ||
) | ||
fragmentName1 === fragmentName2 && | ||
sameArguments(fragmentSpread1, fragmentSpread2) | ||
) { | ||
@@ -301,3 +337,10 @@ return; | ||
comparedFragmentPairs.add(fragmentName1, fragmentName2, areMutuallyExclusive); | ||
const fragKey1 = printFragmentSpreadArguments(fragmentSpread1); | ||
const fragKey2 = printFragmentSpreadArguments(fragmentSpread2); // Memoize so two fragments are not compared for conflicts more than once. | ||
if (comparedFragmentPairs.has(fragKey1, fragKey2, areMutuallyExclusive)) { | ||
return; | ||
} | ||
comparedFragmentPairs.add(fragKey1, fragKey2, areMutuallyExclusive); | ||
const fragment1 = context.getFragment(fragmentName1); | ||
@@ -310,13 +353,15 @@ const fragment2 = context.getFragment(fragmentName2); | ||
const [fieldMap1, referencedFragmentNames1] = | ||
getReferencedFieldsAndFragmentNames( | ||
const [fieldMap1, referencedFragmentSpreads1] = | ||
getReferencedFieldsAndFragmentSpreads( | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
fragment1, | ||
fragmentSpread1, | ||
); | ||
const [fieldMap2, referencedFragmentNames2] = | ||
getReferencedFieldsAndFragmentNames( | ||
const [fieldMap2, referencedFragmentSpreads2] = | ||
getReferencedFieldsAndFragmentSpreads( | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
fragment2, | ||
fragmentSpread2, | ||
); // (F) First, collect all conflicts between these two collections of fields | ||
@@ -328,3 +373,3 @@ // (not including any nested fragments). | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -337,11 +382,13 @@ areMutuallyExclusive, | ||
for (const referencedFragmentName2 of referencedFragmentNames2) { | ||
for (const [referencedFragmentSpread2] of Object.values( | ||
referencedFragmentSpreads2, | ||
)) { | ||
collectConflictsBetweenFragments( | ||
context, | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
areMutuallyExclusive, | ||
fragmentName1, | ||
referencedFragmentName2, | ||
fragmentSpread1, | ||
referencedFragmentSpread2, | ||
); | ||
@@ -351,11 +398,13 @@ } // (G) Then collect conflicts between the second fragment and any nested | ||
for (const referencedFragmentName1 of referencedFragmentNames1) { | ||
for (const [referencedFragmentSpread1] of Object.values( | ||
referencedFragmentSpreads1, | ||
)) { | ||
collectConflictsBetweenFragments( | ||
context, | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
areMutuallyExclusive, | ||
referencedFragmentName1, | ||
fragmentName2, | ||
referencedFragmentSpread1, | ||
fragmentSpread2, | ||
); | ||
@@ -369,3 +418,3 @@ } | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -379,11 +428,11 @@ areMutuallyExclusive, | ||
const conflicts = []; | ||
const [fieldMap1, fragmentNames1] = getFieldsAndFragmentNames( | ||
const [fieldMap1, fragmentSpreadsByName1] = getFieldsAndFragmentSpreads( | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
parentType1, | ||
selectionSet1, | ||
); | ||
const [fieldMap2, fragmentNames2] = getFieldsAndFragmentNames( | ||
const [fieldMap2, fragmentSpreadsByName2] = getFieldsAndFragmentSpreads( | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
parentType2, | ||
@@ -396,3 +445,3 @@ selectionSet2, | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -405,11 +454,11 @@ areMutuallyExclusive, | ||
for (const fragmentName2 of fragmentNames2) { | ||
for (const [fragmentSpread2] of Object.values(fragmentSpreadsByName2)) { | ||
collectConflictsBetweenFieldsAndFragment( | ||
context, | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
areMutuallyExclusive, | ||
fieldMap1, | ||
fragmentName2, | ||
fragmentSpread2, | ||
); | ||
@@ -419,11 +468,11 @@ } // (I) Then collect conflicts between the second collection of fields and | ||
for (const fragmentName1 of fragmentNames1) { | ||
for (const [fragmentSpread1] of Object.values(fragmentSpreadsByName1)) { | ||
collectConflictsBetweenFieldsAndFragment( | ||
context, | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
areMutuallyExclusive, | ||
fieldMap2, | ||
fragmentName1, | ||
fragmentSpread1, | ||
); | ||
@@ -434,12 +483,12 @@ } // (J) Also collect conflicts between any fragment names by the first and | ||
for (const fragmentName1 of fragmentNames1) { | ||
for (const fragmentName2 of fragmentNames2) { | ||
for (const [fragmentSpread1] of Object.values(fragmentSpreadsByName1)) { | ||
for (const [fragmentSpread2] of Object.values(fragmentSpreadsByName2)) { | ||
collectConflictsBetweenFragments( | ||
context, | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
areMutuallyExclusive, | ||
fragmentName1, | ||
fragmentName2, | ||
fragmentSpread1, | ||
fragmentSpread2, | ||
); | ||
@@ -455,3 +504,3 @@ } | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -473,3 +522,3 @@ fieldMap, | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -498,3 +547,3 @@ false, // within one collection is never mutually exclusive | ||
conflicts, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -518,3 +567,3 @@ parentFieldsAreMutuallyExclusive, | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -539,3 +588,3 @@ parentFieldsAreMutuallyExclusive, | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -609,3 +658,3 @@ parentFieldsAreMutuallyExclusive, | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
comparedFragmentPairs, | ||
@@ -693,9 +742,9 @@ areMutuallyExclusive, | ||
function getFieldsAndFragmentNames( | ||
function getFieldsAndFragmentSpreads( | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
parentType, | ||
selectionSet, | ||
) { | ||
const cached = cachedFieldsAndFragmentNames.get(selectionSet); | ||
const cached = cachedFieldsAndFragmentSpreads.get(selectionSet); | ||
@@ -707,3 +756,3 @@ if (cached) { | ||
const nodeAndDefs = Object.create(null); | ||
const fragmentNames = Object.create(null); | ||
const fragmentSpreadsByName = Object.create(null); | ||
@@ -715,7 +764,7 @@ _collectFieldsAndFragmentNames( | ||
nodeAndDefs, | ||
fragmentNames, | ||
fragmentSpreadsByName, | ||
); | ||
const result = [nodeAndDefs, Object.keys(fragmentNames)]; | ||
cachedFieldsAndFragmentNames.set(selectionSet, result); | ||
const result = [nodeAndDefs, fragmentSpreadsByName]; | ||
cachedFieldsAndFragmentSpreads.set(selectionSet, result); | ||
return result; | ||
@@ -725,10 +774,27 @@ } // Given a reference to a fragment, return the represented collection of fields | ||
function getReferencedFieldsAndFragmentNames( | ||
function getReferencedFieldsAndFragmentSpreads( | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
fragment, | ||
fragmentSpread, | ||
) { | ||
// Short-circuit building a type from the node if possible. | ||
const cached = cachedFieldsAndFragmentNames.get(fragment.selectionSet); | ||
const args = fragmentSpread.arguments; | ||
const fragmentSelectionSet = (0, _visitor.visit)(fragment.selectionSet, { | ||
Variable: (node) => { | ||
const name = node.name.value; | ||
const argNode = | ||
args === null || args === void 0 | ||
? void 0 | ||
: args.find((arg) => arg.name.value === name); | ||
if (argNode) { | ||
return argNode.value; | ||
} | ||
return node; | ||
}, | ||
}); // Short-circuit building a type from the node if possible. | ||
const cached = cachedFieldsAndFragmentSpreads.get(fragmentSelectionSet); | ||
if (cached) { | ||
@@ -742,7 +808,7 @@ return cached; | ||
); | ||
return getFieldsAndFragmentNames( | ||
return getFieldsAndFragmentSpreads( | ||
context, | ||
cachedFieldsAndFragmentNames, | ||
cachedFieldsAndFragmentSpreads, | ||
fragmentType, | ||
fragment.selectionSet, | ||
fragmentSelectionSet, | ||
); | ||
@@ -756,3 +822,3 @@ } | ||
nodeAndDefs, | ||
fragmentNames, | ||
fragmentSpreadsByName, | ||
) { | ||
@@ -784,5 +850,13 @@ for (const selection of selectionSet.selections) { | ||
case _kinds.Kind.FRAGMENT_SPREAD: | ||
fragmentNames[selection.name.value] = true; | ||
case _kinds.Kind.FRAGMENT_SPREAD: { | ||
const existing = fragmentSpreadsByName[selection.name.value]; | ||
if (existing) { | ||
existing.push(selection); | ||
} else { | ||
fragmentSpreadsByName[selection.name.value] = [selection]; | ||
} | ||
break; | ||
} | ||
@@ -800,3 +874,3 @@ case _kinds.Kind.INLINE_FRAGMENT: { | ||
nodeAndDefs, | ||
fragmentNames, | ||
fragmentSpreadsByName, | ||
); | ||
@@ -803,0 +877,0 @@ |
@@ -71,2 +71,47 @@ 'use strict'; | ||
}, | ||
FragmentSpread: { | ||
// Validate on leave to allow for directive errors to appear first. | ||
leave(spreadNode) { | ||
var _spreadNode$arguments; | ||
const fragmentDef = context.getFragment(spreadNode.name.value); | ||
if (!fragmentDef) { | ||
return false; | ||
} | ||
const providedArgs = new Set( // FIXME: https://github.com/graphql/graphql-js/issues/2203 | ||
/* c8 ignore next */ | ||
(_spreadNode$arguments = spreadNode.arguments) === null || | ||
_spreadNode$arguments === void 0 | ||
? void 0 | ||
: _spreadNode$arguments.map((arg) => arg.name.value), | ||
); // FIXME: https://github.com/graphql/graphql-js/issues/2203 | ||
/* c8 ignore next */ | ||
for (const varDef of (_fragmentDef$variable = | ||
fragmentDef.variableDefinitions) !== null && | ||
_fragmentDef$variable !== void 0 | ||
? _fragmentDef$variable | ||
: []) { | ||
var _fragmentDef$variable; | ||
if ( | ||
!providedArgs.has(varDef.variable.name.value) && | ||
isRequiredArgumentNode(varDef) | ||
) { | ||
const argTypeStr = (0, _inspect.inspect)(varDef.type); | ||
context.reportError( | ||
new _GraphQLError.GraphQLError( | ||
`Fragment "${spreadNode.name.value}" argument "${varDef.variable.name.value}" of type "${argTypeStr}" is required, but it was not provided.`, | ||
{ | ||
nodes: [spreadNode, varDef], | ||
}, | ||
), | ||
); | ||
} | ||
} | ||
}, | ||
}, | ||
}; | ||
@@ -73,0 +118,0 @@ } |
@@ -59,3 +59,3 @@ 'use strict'; | ||
{ | ||
nodes: extraFieldSelections, | ||
nodes: extraFieldSelections.map((entry) => entry.node), | ||
}, | ||
@@ -68,3 +68,3 @@ ), | ||
const field = fieldNodes[0]; | ||
const fieldName = field.name.value; | ||
const fieldName = field.node.name.value; | ||
@@ -78,3 +78,3 @@ if (fieldName.startsWith('__')) { | ||
{ | ||
nodes: fieldNodes, | ||
nodes: fieldNodes.map((entry) => entry.node), | ||
}, | ||
@@ -81,0 +81,0 @@ ), |
@@ -18,2 +18,4 @@ 'use strict'; | ||
var _kinds = require('../../language/kinds.js'); | ||
var _printer = require('../../language/printer.js'); | ||
@@ -32,3 +34,14 @@ | ||
function ValuesOfCorrectTypeRule(context) { | ||
let variableDefinitions = {}; | ||
return { | ||
OperationDefinition: { | ||
enter() { | ||
variableDefinitions = {}; | ||
}, | ||
}, | ||
VariableDefinition(definition) { | ||
variableDefinitions[definition.variable.name.value] = definition; | ||
}, | ||
ListValue(node) { | ||
@@ -75,2 +88,12 @@ // Note: TypeInfo will traverse into a list's item type, so look to the | ||
} | ||
if (type.isOneOf) { | ||
validateOneOfInputObject( | ||
context, | ||
node, | ||
type, | ||
fieldNodeMap, | ||
variableDefinitions, | ||
); | ||
} | ||
}, | ||
@@ -196,1 +219,66 @@ | ||
} | ||
function validateOneOfInputObject( | ||
context, | ||
node, | ||
type, | ||
fieldNodeMap, | ||
variableDefinitions, | ||
) { | ||
var _fieldNodeMap$keys$; | ||
const keys = Object.keys(fieldNodeMap); | ||
const isNotExactlyOneField = keys.length !== 1; | ||
if (isNotExactlyOneField) { | ||
context.reportError( | ||
new _GraphQLError.GraphQLError( | ||
`OneOf Input Object "${type.name}" must specify exactly one key.`, | ||
{ | ||
nodes: [node], | ||
}, | ||
), | ||
); | ||
return; | ||
} | ||
const value = | ||
(_fieldNodeMap$keys$ = fieldNodeMap[keys[0]]) === null || | ||
_fieldNodeMap$keys$ === void 0 | ||
? void 0 | ||
: _fieldNodeMap$keys$.value; | ||
const isNullLiteral = !value || value.kind === _kinds.Kind.NULL; | ||
const isVariable = | ||
(value === null || value === void 0 ? void 0 : value.kind) === | ||
_kinds.Kind.VARIABLE; | ||
if (isNullLiteral) { | ||
context.reportError( | ||
new _GraphQLError.GraphQLError( | ||
`Field "${type.name}.${keys[0]}" must be non-null.`, | ||
{ | ||
nodes: [node], | ||
}, | ||
), | ||
); | ||
return; | ||
} | ||
if (isVariable) { | ||
const variableName = value.name.value; | ||
const definition = variableDefinitions[variableName]; | ||
const isNullableVariable = | ||
definition.type.kind !== _kinds.Kind.NON_NULL_TYPE; | ||
if (isNullableVariable) { | ||
context.reportError( | ||
new _GraphQLError.GraphQLError( | ||
`Variable "${variableName}" must be non-nullable to be used for OneOf Input Object "${type.name}".`, | ||
{ | ||
nodes: [node], | ||
}, | ||
), | ||
); | ||
} | ||
} | ||
} |
@@ -38,5 +38,8 @@ 'use strict'; | ||
for (const { node, type, defaultValue } of usages) { | ||
for (const { node, type, defaultValue, fragmentVarDef } of usages) { | ||
const varName = node.name.value; | ||
const varDef = varDefMap[varName]; | ||
const varDef = | ||
fragmentVarDef !== null && fragmentVarDef !== void 0 | ||
? fragmentVarDef | ||
: varDefMap[varName]; | ||
@@ -43,0 +46,0 @@ if (varDef && type) { |
@@ -0,3 +1,9 @@ | ||
import { MaxIntrospectionDepthRule } from './rules/MaxIntrospectionDepthRule'; | ||
import type { SDLValidationRule, ValidationRule } from './ValidationContext'; | ||
/** | ||
* Technically these aren't part of the spec but they are strongly encouraged | ||
* validation rules. | ||
*/ | ||
export declare const recommendedRules: readonly typeof MaxIntrospectionDepthRule[]; | ||
/** | ||
* This set includes all validation rules defined by the GraphQL spec. | ||
@@ -4,0 +10,0 @@ * |
@@ -6,3 +6,6 @@ 'use strict'; | ||
}); | ||
exports.specifiedSDLRules = exports.specifiedRules = void 0; | ||
exports.specifiedSDLRules = | ||
exports.specifiedRules = | ||
exports.recommendedRules = | ||
void 0; | ||
@@ -27,2 +30,4 @@ var _ExecutableDefinitionsRule = require('./rules/ExecutableDefinitionsRule.js'); | ||
var _MaxIntrospectionDepthRule = require('./rules/MaxIntrospectionDepthRule.js'); | ||
var _NoFragmentCyclesRule = require('./rules/NoFragmentCyclesRule.js'); | ||
@@ -34,2 +39,4 @@ | ||
var _NoUnusedFragmentVariablesRule = require('./rules/NoUnusedFragmentVariablesRule.js'); | ||
var _NoUnusedVariablesRule = require('./rules/NoUnusedVariablesRule.js'); | ||
@@ -88,5 +95,7 @@ | ||
// SDL-specific validation rules | ||
// TODO: Spec Section | ||
// Spec Section: "Fragments must not form cycles" | ||
// Spec Section: "All Variable Used Defined" | ||
// Spec Section: "Fragments must be used" | ||
// Spec Section: "All Fragment Variables Used" | ||
// Spec Section: "All Variables Used" | ||
@@ -109,2 +118,9 @@ // Spec Section: "Field Selection Merging" | ||
/** | ||
* Technically these aren't part of the spec but they are strongly encouraged | ||
* validation rules. | ||
*/ | ||
const recommendedRules = Object.freeze([ | ||
_MaxIntrospectionDepthRule.MaxIntrospectionDepthRule, | ||
]); | ||
/** | ||
* This set includes all validation rules defined by the GraphQL spec. | ||
@@ -115,2 +131,4 @@ * | ||
*/ | ||
exports.recommendedRules = recommendedRules; | ||
const specifiedRules = Object.freeze([ | ||
@@ -135,2 +153,3 @@ _ExecutableDefinitionsRule.ExecutableDefinitionsRule, | ||
_KnownDirectivesRule.KnownDirectivesRule, | ||
_NoUnusedFragmentVariablesRule.NoUnusedFragmentVariablesRule, | ||
_UniqueDirectivesPerLocationRule.UniqueDirectivesPerLocationRule, | ||
@@ -144,2 +163,3 @@ _KnownArgumentNamesRule.KnownArgumentNamesRule, | ||
_UniqueInputFieldNamesRule.UniqueInputFieldNamesRule, | ||
...recommendedRules, | ||
]); | ||
@@ -146,0 +166,0 @@ /** |
@@ -9,2 +9,3 @@ import type { Maybe } from '../jsutils/Maybe'; | ||
SelectionSetNode, | ||
VariableDefinitionNode, | ||
VariableNode, | ||
@@ -23,3 +24,3 @@ } from '../language/ast'; | ||
import type { GraphQLSchema } from '../type/schema'; | ||
import { TypeInfo } from '../utilities/TypeInfo'; | ||
import type { TypeInfo } from '../utilities/TypeInfo'; | ||
declare type NodeWithSelectionSet = | ||
@@ -32,2 +33,3 @@ | OperationDefinitionNode | ||
readonly defaultValue: Maybe<unknown>; | ||
readonly fragmentVarDef: Maybe<VariableDefinitionNode>; | ||
} | ||
@@ -88,2 +90,5 @@ /** | ||
): ReadonlyArray<VariableUsage>; | ||
getOperationVariableUsages( | ||
operation: OperationDefinitionNode, | ||
): ReadonlyArray<VariableUsage>; | ||
getType(): Maybe<GraphQLOutputType>; | ||
@@ -90,0 +95,0 @@ getParentType(): Maybe<GraphQLCompositeType>; |
@@ -160,3 +160,7 @@ 'use strict'; | ||
const newUsages = []; | ||
const typeInfo = new _TypeInfo.TypeInfo(this._schema); | ||
const typeInfo = this._typeInfo; | ||
const fragmentVariableDefinitions = | ||
node.kind === _kinds.Kind.FRAGMENT_DEFINITION | ||
? node.variableDefinitions | ||
: undefined; | ||
(0, _visitor.visit)( | ||
@@ -168,2 +172,10 @@ node, | ||
Variable(variable) { | ||
const fragmentVarDef = | ||
fragmentVariableDefinitions === null || | ||
fragmentVariableDefinitions === void 0 | ||
? void 0 | ||
: fragmentVariableDefinitions.find( | ||
(varDef) => | ||
varDef.variable.name.value === variable.name.value, | ||
); | ||
newUsages.push({ | ||
@@ -173,2 +185,3 @@ node: variable, | ||
defaultValue: typeInfo.getDefaultValue(), | ||
fragmentVarDef, | ||
}); | ||
@@ -202,2 +215,18 @@ }, | ||
getOperationVariableUsages(operation) { | ||
let usages = this._recursiveVariableUsages.get(operation); | ||
if (!usages) { | ||
usages = this.getVariableUsages(operation); | ||
for (const frag of this.getRecursivelyReferencedFragments(operation)) { | ||
usages = usages.concat(this.getVariableUsages(frag)); | ||
} | ||
this._recursiveVariableUsages.set(operation, usages); | ||
} | ||
return usages.filter(({ fragmentVarDef }) => !fragmentVarDef); | ||
} | ||
getType() { | ||
@@ -204,0 +233,0 @@ return this._typeInfo.getType(); |
@@ -13,3 +13,3 @@ 'use strict'; | ||
*/ | ||
const version = '16.8.2'; | ||
const version = '16.9.0'; | ||
/** | ||
@@ -22,6 +22,6 @@ * An object containing the components of the GraphQL.js version string | ||
major: 16, | ||
minor: 8, | ||
patch: 2, | ||
minor: 9, | ||
patch: 0, | ||
preReleaseTag: null, | ||
}); | ||
exports.versionInfo = versionInfo; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
1392287
394
43315
161
0
1
1