1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.getRange = exports.validateQuery = exports.getDiagnostics = exports.DIAGNOSTIC_SEVERITY = exports.SEVERITY = void 0;
|
4 | const graphql_1 = require("graphql");
|
5 | const graphql_language_service_parser_1 = require("graphql-language-service-parser");
|
6 | const graphql_language_service_utils_1 = require("graphql-language-service-utils");
|
7 | exports.SEVERITY = {
|
8 | Error: 'Error',
|
9 | Warning: 'Warning',
|
10 | Information: 'Information',
|
11 | Hint: 'Hint',
|
12 | };
|
13 | exports.DIAGNOSTIC_SEVERITY = {
|
14 | [exports.SEVERITY.Error]: 1,
|
15 | [exports.SEVERITY.Warning]: 2,
|
16 | [exports.SEVERITY.Information]: 3,
|
17 | [exports.SEVERITY.Hint]: 4,
|
18 | };
|
19 | const invariant = (condition, message) => {
|
20 | if (!condition) {
|
21 | throw new Error(message);
|
22 | }
|
23 | };
|
24 | function getDiagnostics(query, schema = null, customRules, isRelayCompatMode) {
|
25 | let ast = null;
|
26 | try {
|
27 | ast = graphql_1.parse(query);
|
28 | }
|
29 | catch (error) {
|
30 | const range = getRange(error.locations[0], query);
|
31 | return [
|
32 | {
|
33 | severity: exports.DIAGNOSTIC_SEVERITY.Error,
|
34 | message: error.message,
|
35 | source: 'GraphQL: Syntax',
|
36 | range,
|
37 | },
|
38 | ];
|
39 | }
|
40 | return validateQuery(ast, schema, customRules, isRelayCompatMode);
|
41 | }
|
42 | exports.getDiagnostics = getDiagnostics;
|
43 | function validateQuery(ast, schema = null, customRules, isRelayCompatMode) {
|
44 | if (!schema) {
|
45 | return [];
|
46 | }
|
47 | const validationErrorAnnotations = mapCat(graphql_language_service_utils_1.validateWithCustomRules(schema, ast, customRules, isRelayCompatMode), error => annotations(error, exports.DIAGNOSTIC_SEVERITY.Error, 'Validation'));
|
48 | const deprecationWarningAnnotations = mapCat(graphql_1.findDeprecatedUsages(schema, ast), error => annotations(error, exports.DIAGNOSTIC_SEVERITY.Warning, 'Deprecation'));
|
49 | return validationErrorAnnotations.concat(deprecationWarningAnnotations);
|
50 | }
|
51 | exports.validateQuery = validateQuery;
|
52 | function mapCat(array, mapper) {
|
53 | return Array.prototype.concat.apply([], array.map(mapper));
|
54 | }
|
55 | function annotations(error, severity, type) {
|
56 | if (!error.nodes) {
|
57 | return [];
|
58 | }
|
59 | const highlightedNodes = [];
|
60 | error.nodes.forEach(node => {
|
61 | const highlightNode = node.kind !== 'Variable' && 'name' in node && node.name !== undefined
|
62 | ? node.name
|
63 | : 'variable' in node && node.variable !== undefined
|
64 | ? node.variable
|
65 | : node;
|
66 | if (highlightNode) {
|
67 | invariant(error.locations, 'GraphQL validation error requires locations.');
|
68 | const loc = error.locations[0];
|
69 | const highlightLoc = getLocation(highlightNode);
|
70 | const end = loc.column + (highlightLoc.end - highlightLoc.start);
|
71 | highlightedNodes.push({
|
72 | source: `GraphQL: ${type}`,
|
73 | message: error.message,
|
74 | severity,
|
75 | range: new graphql_language_service_utils_1.Range(new graphql_language_service_utils_1.Position(loc.line - 1, loc.column - 1), new graphql_language_service_utils_1.Position(loc.line - 1, end)),
|
76 | });
|
77 | }
|
78 | });
|
79 | return highlightedNodes;
|
80 | }
|
81 | function getRange(location, queryText) {
|
82 | const parser = graphql_language_service_parser_1.onlineParser();
|
83 | const state = parser.startState();
|
84 | const lines = queryText.split('\n');
|
85 | invariant(lines.length >= location.line, 'Query text must have more lines than where the error happened');
|
86 | let stream = null;
|
87 | for (let i = 0; i < location.line; i++) {
|
88 | stream = new graphql_language_service_parser_1.CharacterStream(lines[i]);
|
89 | while (!stream.eol()) {
|
90 | const style = parser.token(stream, state);
|
91 | if (style === 'invalidchar') {
|
92 | break;
|
93 | }
|
94 | }
|
95 | }
|
96 | invariant(stream, 'Expected Parser stream to be available.');
|
97 | const line = location.line - 1;
|
98 | const start = stream.getStartOfToken();
|
99 | const end = stream.getCurrentPosition();
|
100 | return new graphql_language_service_utils_1.Range(new graphql_language_service_utils_1.Position(line, start), new graphql_language_service_utils_1.Position(line, end));
|
101 | }
|
102 | exports.getRange = getRange;
|
103 | function getLocation(node) {
|
104 | const typeCastedNode = node;
|
105 | const location = typeCastedNode.loc;
|
106 | invariant(location, 'Expected ASTNode to have a location.');
|
107 | return location;
|
108 | }
|
109 |
|
\ | No newline at end of file |