graphql
Version:
A Query Language and Runtime which can target any service.
1 lines • 7.15 kB
Source Map (JSON)
{"version":3,"file":"UniqueDirectivesPerLocationRule.js","sourceRoot":"","sources":["../../../src/validation/rules/UniqueDirectivesPerLocationRule.ts"],"names":[],"mappings":"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","sourcesContent":["/** @category Validation Rules */\n\nimport { GraphQLError } from '../../error/GraphQLError.ts';\n\nimport type { DirectiveNode } from '../../language/ast.ts';\nimport { Kind } from '../../language/kinds.ts';\nimport {\n isTypeDefinitionNode,\n isTypeExtensionNode,\n} from '../../language/predicates.ts';\nimport type { ASTVisitor } from '../../language/visitor.ts';\n\nimport { specifiedDirectives } from '../../type/directives.ts';\n\nimport type {\n SDLValidationContext,\n ValidationContext,\n} from '../ValidationContext.ts';\n\n/**\n * Unique directive names per location\n *\n * A GraphQL document is only valid if all non-repeatable directives at\n * a given location are uniquely named.\n *\n * See https://spec.graphql.org/draft/#sec-Directives-Are-Unique-Per-Location\n * @param context - The validation context used while checking the document.\n * @returns A visitor that reports validation errors for this rule.\n * @example\n * ```ts\n * import { buildSchema, parse, validate } from 'graphql';\n * import { UniqueDirectivesPerLocationRule } from 'graphql/validation';\n *\n * const schema = buildSchema(`\n * type Query {\n * name: String\n * }\n * `);\n *\n * const invalidDocument = parse(`\n * { name @include(if: true) @include(if: false) }\n * `);\n * const invalidErrors = validate(schema, invalidDocument, [\n * UniqueDirectivesPerLocationRule,\n * ]);\n *\n * invalidErrors.length; // => 1\n *\n * const validDocument = parse(`\n * { name @include(if: true) }\n * `);\n * const validErrors = validate(schema, validDocument, [\n * UniqueDirectivesPerLocationRule,\n * ]);\n *\n * validErrors; // => []\n * ```\n */\nexport function UniqueDirectivesPerLocationRule(\n context: ValidationContext | SDLValidationContext,\n): ASTVisitor {\n const uniqueDirectiveMap = new Map<string, boolean>();\n\n const schema = context.getSchema();\n const definedDirectives = schema\n ? schema.getDirectives()\n : specifiedDirectives;\n for (const directive of definedDirectives) {\n uniqueDirectiveMap.set(directive.name, !directive.isRepeatable);\n }\n\n const astDefinitions = context.getDocument().definitions;\n for (const def of astDefinitions) {\n if (def.kind === Kind.DIRECTIVE_DEFINITION) {\n uniqueDirectiveMap.set(def.name.value, !def.repeatable);\n }\n }\n\n const schemaDirectives = new Map<string, DirectiveNode>();\n const typeDirectivesMap = new Map<string, Map<string, DirectiveNode>>();\n const directiveDirectivesMap = new Map<string, Map<string, DirectiveNode>>();\n\n return {\n // Many different AST nodes may contain directives. Rather than listing\n // them all, just listen for entering any node, and check to see if it\n // defines any directives.\n enter(node) {\n if (!('directives' in node) || !node.directives) {\n return;\n }\n\n let seenDirectives;\n if (\n node.kind === Kind.SCHEMA_DEFINITION ||\n node.kind === Kind.SCHEMA_EXTENSION\n ) {\n seenDirectives = schemaDirectives;\n } else if (isTypeDefinitionNode(node) || isTypeExtensionNode(node)) {\n const typeName = node.name.value;\n seenDirectives = typeDirectivesMap.get(typeName);\n if (seenDirectives === undefined) {\n seenDirectives = new Map();\n typeDirectivesMap.set(typeName, seenDirectives);\n }\n } else if (\n node.kind === Kind.DIRECTIVE_DEFINITION ||\n node.kind === Kind.DIRECTIVE_EXTENSION\n ) {\n const directiveName = node.name.value;\n seenDirectives = directiveDirectivesMap.get(directiveName);\n if (seenDirectives === undefined) {\n seenDirectives = new Map();\n directiveDirectivesMap.set(directiveName, seenDirectives);\n }\n } else {\n seenDirectives = new Map();\n }\n\n for (const directive of node.directives) {\n const directiveName = directive.name.value;\n\n if (uniqueDirectiveMap.get(directiveName) === true) {\n const seenDirective = seenDirectives.get(directiveName);\n if (seenDirective != null) {\n context.reportError(\n new GraphQLError(\n `The directive \"@${directiveName}\" can only be used once at this location.`,\n { nodes: [seenDirective, directive] },\n ),\n );\n } else {\n seenDirectives.set(directiveName, directive);\n }\n }\n }\n },\n };\n}\n"]}