-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Eliminate circular dependency in validation rules (#1263)
`validation/validate.js` depends on all of the rules, and all of the rules depend on `ValidationContext`, which was previously exported by `validation/validate.js`. This moves `ValidationContext` out of `validate.js` to resolve this. Circular dependencies are fine, but they tend to slow down flow, since it moves more work to the merging phase, and the merging phase isn't as fast as the inference phase. As a result, this should make flow marginally faster. This does have a minor breaking change: `graphql/validatation/validate` no longer exports `ValidationContext`, but `graphql/validation` still does. I can add an export to `validate.js` to preserve that behavior, but it's a minor change to a non-public API, and graphql-js has broken these things in the past.
- Loading branch information
Showing
32 changed files
with
257 additions
and
238 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
/** | ||
* Copyright (c) 2015-present, Facebook, Inc. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
* | ||
* @flow strict | ||
*/ | ||
|
||
import type { ObjMap } from '../jsutils/ObjMap'; | ||
import { GraphQLError } from '../error'; | ||
import { visit, visitWithTypeInfo } from '../language/visitor'; | ||
import { Kind } from '../language/kinds'; | ||
import type { | ||
DocumentNode, | ||
OperationDefinitionNode, | ||
VariableNode, | ||
SelectionSetNode, | ||
FragmentSpreadNode, | ||
FragmentDefinitionNode, | ||
} from '../language/ast'; | ||
import { GraphQLSchema } from '../type/schema'; | ||
import type { | ||
GraphQLInputType, | ||
GraphQLOutputType, | ||
GraphQLCompositeType, | ||
GraphQLField, | ||
GraphQLArgument, | ||
} from '../type/definition'; | ||
import type { GraphQLDirective } from '../type/directives'; | ||
import { TypeInfo } from '../utilities/TypeInfo'; | ||
|
||
type NodeWithSelectionSet = OperationDefinitionNode | FragmentDefinitionNode; | ||
type VariableUsage = { node: VariableNode, type: ?GraphQLInputType }; | ||
|
||
/** | ||
* An instance of this class is passed as the "this" context to all validators, | ||
* allowing access to commonly useful contextual information from within a | ||
* validation rule. | ||
*/ | ||
export default class ValidationContext { | ||
_schema: GraphQLSchema; | ||
_ast: DocumentNode; | ||
_typeInfo: TypeInfo; | ||
_errors: Array<GraphQLError>; | ||
_fragments: ObjMap<FragmentDefinitionNode>; | ||
_fragmentSpreads: Map<SelectionSetNode, $ReadOnlyArray<FragmentSpreadNode>>; | ||
_recursivelyReferencedFragments: Map< | ||
OperationDefinitionNode, | ||
$ReadOnlyArray<FragmentDefinitionNode>, | ||
>; | ||
_variableUsages: Map<NodeWithSelectionSet, $ReadOnlyArray<VariableUsage>>; | ||
_recursiveVariableUsages: Map< | ||
OperationDefinitionNode, | ||
$ReadOnlyArray<VariableUsage>, | ||
>; | ||
|
||
constructor( | ||
schema: GraphQLSchema, | ||
ast: DocumentNode, | ||
typeInfo: TypeInfo, | ||
): void { | ||
this._schema = schema; | ||
this._ast = ast; | ||
this._typeInfo = typeInfo; | ||
this._errors = []; | ||
this._fragmentSpreads = new Map(); | ||
this._recursivelyReferencedFragments = new Map(); | ||
this._variableUsages = new Map(); | ||
this._recursiveVariableUsages = new Map(); | ||
} | ||
|
||
reportError(error: GraphQLError): void { | ||
this._errors.push(error); | ||
} | ||
|
||
getErrors(): $ReadOnlyArray<GraphQLError> { | ||
return this._errors; | ||
} | ||
|
||
getSchema(): GraphQLSchema { | ||
return this._schema; | ||
} | ||
|
||
getDocument(): DocumentNode { | ||
return this._ast; | ||
} | ||
|
||
getFragment(name: string): ?FragmentDefinitionNode { | ||
let fragments = this._fragments; | ||
if (!fragments) { | ||
this._fragments = fragments = this.getDocument().definitions.reduce( | ||
(frags, statement) => { | ||
if (statement.kind === Kind.FRAGMENT_DEFINITION) { | ||
frags[statement.name.value] = statement; | ||
} | ||
return frags; | ||
}, | ||
Object.create(null), | ||
); | ||
} | ||
return fragments[name]; | ||
} | ||
|
||
getFragmentSpreads( | ||
node: SelectionSetNode, | ||
): $ReadOnlyArray<FragmentSpreadNode> { | ||
let spreads = this._fragmentSpreads.get(node); | ||
if (!spreads) { | ||
spreads = []; | ||
const setsToVisit: Array<SelectionSetNode> = [node]; | ||
while (setsToVisit.length !== 0) { | ||
const set = setsToVisit.pop(); | ||
for (let i = 0; i < set.selections.length; i++) { | ||
const selection = set.selections[i]; | ||
if (selection.kind === Kind.FRAGMENT_SPREAD) { | ||
spreads.push(selection); | ||
} else if (selection.selectionSet) { | ||
setsToVisit.push(selection.selectionSet); | ||
} | ||
} | ||
} | ||
this._fragmentSpreads.set(node, spreads); | ||
} | ||
return spreads; | ||
} | ||
|
||
getRecursivelyReferencedFragments( | ||
operation: OperationDefinitionNode, | ||
): $ReadOnlyArray<FragmentDefinitionNode> { | ||
let fragments = this._recursivelyReferencedFragments.get(operation); | ||
if (!fragments) { | ||
fragments = []; | ||
const collectedNames = Object.create(null); | ||
const nodesToVisit: Array<SelectionSetNode> = [operation.selectionSet]; | ||
while (nodesToVisit.length !== 0) { | ||
const node = nodesToVisit.pop(); | ||
const spreads = this.getFragmentSpreads(node); | ||
for (let i = 0; i < spreads.length; i++) { | ||
const fragName = spreads[i].name.value; | ||
if (collectedNames[fragName] !== true) { | ||
collectedNames[fragName] = true; | ||
const fragment = this.getFragment(fragName); | ||
if (fragment) { | ||
fragments.push(fragment); | ||
nodesToVisit.push(fragment.selectionSet); | ||
} | ||
} | ||
} | ||
} | ||
this._recursivelyReferencedFragments.set(operation, fragments); | ||
} | ||
return fragments; | ||
} | ||
|
||
getVariableUsages(node: NodeWithSelectionSet): $ReadOnlyArray<VariableUsage> { | ||
let usages = this._variableUsages.get(node); | ||
if (!usages) { | ||
const newUsages = []; | ||
const typeInfo = new TypeInfo(this._schema); | ||
visit( | ||
node, | ||
visitWithTypeInfo(typeInfo, { | ||
VariableDefinition: () => false, | ||
Variable(variable) { | ||
newUsages.push({ node: variable, type: typeInfo.getInputType() }); | ||
}, | ||
}), | ||
); | ||
usages = newUsages; | ||
this._variableUsages.set(node, usages); | ||
} | ||
return usages; | ||
} | ||
|
||
getRecursiveVariableUsages( | ||
operation: OperationDefinitionNode, | ||
): $ReadOnlyArray<VariableUsage> { | ||
let usages = this._recursiveVariableUsages.get(operation); | ||
if (!usages) { | ||
usages = this.getVariableUsages(operation); | ||
const fragments = this.getRecursivelyReferencedFragments(operation); | ||
for (let i = 0; i < fragments.length; i++) { | ||
Array.prototype.push.apply( | ||
usages, | ||
this.getVariableUsages(fragments[i]), | ||
); | ||
} | ||
this._recursiveVariableUsages.set(operation, usages); | ||
} | ||
return usages; | ||
} | ||
|
||
getType(): ?GraphQLOutputType { | ||
return this._typeInfo.getType(); | ||
} | ||
|
||
getParentType(): ?GraphQLCompositeType { | ||
return this._typeInfo.getParentType(); | ||
} | ||
|
||
getInputType(): ?GraphQLInputType { | ||
return this._typeInfo.getInputType(); | ||
} | ||
|
||
getParentInputType(): ?GraphQLInputType { | ||
return this._typeInfo.getParentInputType(); | ||
} | ||
|
||
getFieldDef(): ?GraphQLField<*, *> { | ||
return this._typeInfo.getFieldDef(); | ||
} | ||
|
||
getDirective(): ?GraphQLDirective { | ||
return this._typeInfo.getDirective(); | ||
} | ||
|
||
getArgument(): ?GraphQLArgument { | ||
return this._typeInfo.getArgument(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.