UNPKG

6.64 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.NoMissingClientDirectives = exports.NoTypenameAlias = exports.NoAnonymousQueries = exports.validateQueryDocument = exports.getValidationErrors = exports.defaultValidationRules = void 0;
4const graphql_1 = require("graphql");
5const vscode_languageserver_1 = require("vscode-languageserver");
6const logger_1 = require("./logger");
7const source_1 = require("../utilities/source");
8const execute_1 = require("graphql/execution/execute");
9const graphql_2 = require("../utilities/graphql");
10const utilities_1 = require("../utilities");
11const specifiedRulesToBeRemoved = [graphql_1.NoUnusedFragmentsRule];
12exports.defaultValidationRules = [
13 NoAnonymousQueries,
14 NoTypenameAlias,
15 NoMissingClientDirectives,
16 ...graphql_1.specifiedRules.filter((rule) => !specifiedRulesToBeRemoved.includes(rule)),
17];
18function getValidationErrors(schema, document, fragments, rules = exports.defaultValidationRules) {
19 const typeInfo = new graphql_1.TypeInfo(schema);
20 const errors = [];
21 const onError = (err) => errors.push(err);
22 const context = new graphql_1.ValidationContext(schema, document, typeInfo, onError);
23 if (fragments) {
24 context._fragments = fragments;
25 }
26 const visitors = rules.map((rule) => rule(context));
27 (0, graphql_1.visit)(document, (0, graphql_1.visitWithTypeInfo)(typeInfo, (0, graphql_1.visitInParallel)(visitors)));
28 if (typeof context.getErrors === "function")
29 return context.getErrors();
30 return errors;
31}
32exports.getValidationErrors = getValidationErrors;
33function validateQueryDocument(schema, document) {
34 try {
35 const validationErrors = getValidationErrors(schema, document);
36 if (validationErrors && validationErrors.length > 0) {
37 for (const error of validationErrors) {
38 (0, logger_1.logError)(error);
39 }
40 return utilities_1.Debug.error("Validation of GraphQL query document failed");
41 }
42 }
43 catch (e) {
44 console.error(e);
45 throw e;
46 }
47}
48exports.validateQueryDocument = validateQueryDocument;
49function NoAnonymousQueries(context) {
50 return {
51 OperationDefinition(node) {
52 if (!node.name) {
53 context.reportError(new graphql_1.GraphQLError("Apollo does not support anonymous operations", [
54 node,
55 ]));
56 }
57 return false;
58 },
59 };
60}
61exports.NoAnonymousQueries = NoAnonymousQueries;
62function NoTypenameAlias(context) {
63 return {
64 Field(node) {
65 const aliasName = node.alias && node.alias.value;
66 if (aliasName == "__typename") {
67 context.reportError(new graphql_1.GraphQLError("Apollo needs to be able to insert __typename when needed, please do not use it as an alias", [node]));
68 }
69 },
70 };
71}
72exports.NoTypenameAlias = NoTypenameAlias;
73function hasClientSchema(schema) {
74 const query = schema.getQueryType();
75 const mutation = schema.getMutationType();
76 const subscription = schema.getSubscriptionType();
77 return Boolean((query && query.clientSchema) ||
78 (mutation && mutation.clientSchema) ||
79 (subscription && subscription.clientSchema));
80}
81function NoMissingClientDirectives(context) {
82 const root = context.getDocument();
83 const schema = context.getSchema();
84 if (!hasClientSchema(schema))
85 return {};
86 const executionContext = (0, execute_1.buildExecutionContext)(schema, root, Object.create(null), Object.create(null), undefined, undefined, undefined);
87 function visitor(node) {
88 const parentType = node.kind === graphql_1.Kind.FRAGMENT_DEFINITION
89 ? schema.getType(node.typeCondition.name.value)
90 : context.getParentType();
91 const fieldDef = context.getFieldDef();
92 if (!parentType)
93 return;
94 const clientFields = parentType &&
95 (0, graphql_1.isObjectType)(parentType) &&
96 parentType.clientSchema &&
97 parentType.clientSchema.localFields;
98 let clientDirectivePresent = (0, graphql_2.hasClientDirective)(node);
99 let message = "@client directive is missing on ";
100 let selectsClientFieldSet = false;
101 switch (node.kind) {
102 case graphql_1.Kind.FIELD:
103 selectsClientFieldSet = Boolean(clientFields && clientFields.includes(fieldDef.name));
104 message += `local field "${node.name.value}"`;
105 break;
106 case graphql_1.Kind.INLINE_FRAGMENT:
107 case graphql_1.Kind.FRAGMENT_DEFINITION:
108 if (Array.isArray(executionContext))
109 break;
110 const fields = (0, graphql_2.simpleCollectFields)(executionContext, node.selectionSet, Object.create(null), Object.create(null));
111 const fieldNames = Object.entries(fields).map(([name]) => name);
112 selectsClientFieldSet = fieldNames.every((field) => clientFields && clientFields.includes(field));
113 message += `fragment ${"name" in node ? `"${node.name.value}" ` : ""}around local fields "${fieldNames.join(",")}"`;
114 break;
115 }
116 if (selectsClientFieldSet && !clientDirectivePresent) {
117 let extensions = null;
118 const name = "name" in node && node.name;
119 if (name && name.loc) {
120 let { source, end: locToInsertDirective } = name.loc;
121 if ("arguments" in node &&
122 node.arguments &&
123 node.arguments.length !== 0) {
124 const endOfArgs = source.body.indexOf(")", locToInsertDirective);
125 locToInsertDirective = endOfArgs + 1;
126 }
127 const codeAction = {
128 message: `Add @client directive to "${name.value}"`,
129 edits: [
130 vscode_languageserver_1.TextEdit.insert((0, source_1.positionFromSourceLocation)(source, (0, graphql_1.getLocation)(source, locToInsertDirective)), " @client"),
131 ],
132 };
133 extensions = { codeAction };
134 }
135 context.reportError(new graphql_1.GraphQLError(message, [node], null, null, null, null, extensions));
136 }
137 if (selectsClientFieldSet) {
138 return false;
139 }
140 return;
141 }
142 return {
143 InlineFragment: visitor,
144 FragmentDefinition: visitor,
145 Field: visitor,
146 };
147}
148exports.NoMissingClientDirectives = NoMissingClientDirectives;
149//# sourceMappingURL=validation.js.map
\No newline at end of file