@mo36924/graphql-plugin 1.6.4 → 5.0.69

Sign up to get free protection for your applications and to get access to all the features.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021 mo36924
3
+ Copyright (c) 2023 mo36924
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/dist/index.cjs CHANGED
@@ -1,127 +1,90 @@
1
1
  'use strict';
2
2
 
3
- const fs = require('fs');
4
- const path = require('path');
5
- const graphqlSchema = require('@mo36924/graphql-schema');
6
- const graphql = require('graphql');
7
- const graphqlLanguageServiceInterface = require('graphql-language-service-interface');
8
- const graphqlLanguageServiceUtils = require('graphql-language-service-utils');
9
- const vscodeLanguageserverTypes = require('vscode-languageserver-types');
3
+ var graphql = require('@mo36924/graphql');
4
+ var graphql$1 = require('graphql');
5
+ var graphqlLanguageService = require('graphql-language-service');
10
6
 
11
- const init = ({ typescript: ts }) => {
12
- return {
13
- create(info) {
14
- const languageService = info.languageService;
15
- const config = info.config;
16
- const cwd = info.project.getCurrentDirectory();
17
- const modelPath = config.model && path.resolve(cwd, config.model);
18
- const schemaPath = config.schema && path.resolve(cwd, config.schema);
19
- const watchPath = modelPath || schemaPath;
20
- let schema = graphql.buildSchema("scalar Unknown");
21
- const addScalarUnknownType = (schemaCode) => schemaCode.includes("scalar Unknown") ? schemaCode : `${schemaCode}\nscalar Unknown`;
22
- const changeModel = () => {
23
- schema = graphql.buildSchema(addScalarUnknownType(graphqlSchema.printSchemaModel(fs.readFileSync(modelPath, "utf8"))));
24
- };
25
- const changeSchema = () => {
26
- schema = graphql.buildSchema(addScalarUnknownType(fs.readFileSync(schemaPath, "utf8")));
27
- };
28
- const update = modelPath ? changeModel : changeSchema;
29
- const listener = () => {
30
- try {
31
- update();
32
- }
33
- catch { }
34
- };
35
- try {
36
- update();
37
- fs.watch(watchPath, listener);
7
+ const init = ({ typescript: ts })=>{
8
+ const isQueryTag = (node)=>ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && node.tag.escapedText === "gql";
9
+ const getScriptElementKind = (completionItemKind)=>{
10
+ switch(completionItemKind){
11
+ case graphqlLanguageService.CompletionItemKind.Function:
12
+ case graphqlLanguageService.CompletionItemKind.Constructor:
13
+ return ts.ScriptElementKind.functionElement;
14
+ case graphqlLanguageService.CompletionItemKind.Field:
15
+ case graphqlLanguageService.CompletionItemKind.Variable:
16
+ return ts.ScriptElementKind.memberVariableElement;
17
+ default:
18
+ return ts.ScriptElementKind.unknown;
19
+ }
20
+ };
21
+ const getDiagnosticCategory = (diagnosticSeverity)=>{
22
+ switch(diagnosticSeverity){
23
+ case graphqlLanguageService.DIAGNOSTIC_SEVERITY.Warning:
24
+ return ts.DiagnosticCategory.Warning;
25
+ case graphqlLanguageService.DIAGNOSTIC_SEVERITY.Information:
26
+ return ts.DiagnosticCategory.Message;
27
+ case graphqlLanguageService.DIAGNOSTIC_SEVERITY.Hint:
28
+ return ts.DiagnosticCategory.Suggestion;
29
+ default:
30
+ return ts.DiagnosticCategory.Error;
31
+ }
32
+ };
33
+ const getHoverQueryTag = (sourceFile, position)=>{
34
+ const tag = ts.forEachChild(sourceFile, function visitor(node) {
35
+ if (position < node.pos) {
36
+ return true;
38
37
  }
39
- catch {
40
- fs.watchFile(watchPath, () => {
41
- try {
42
- update();
43
- fs.watch(watchPath, listener);
44
- fs.unwatchFile(watchPath);
45
- }
46
- catch { }
47
- });
38
+ if (position >= node.end) {
39
+ return;
48
40
  }
49
- const getSourceFile = (fileName) => languageService.getProgram()?.getSourceFile(fileName);
50
- const isGraphqlTag = (tag) => {
51
- switch (tag) {
52
- case "query":
53
- case "mutation":
54
- case "subscription":
55
- return true;
56
- default:
57
- return false;
58
- }
59
- };
60
- const getDiagnosticCategory = (severity) => {
61
- switch (severity) {
62
- case vscodeLanguageserverTypes.DiagnosticSeverity.Error:
63
- return ts.DiagnosticCategory.Error;
64
- case vscodeLanguageserverTypes.DiagnosticSeverity.Warning:
65
- return ts.DiagnosticCategory.Warning;
66
- case vscodeLanguageserverTypes.DiagnosticSeverity.Information:
67
- return ts.DiagnosticCategory.Message;
68
- case vscodeLanguageserverTypes.DiagnosticSeverity.Hint:
69
- return ts.DiagnosticCategory.Suggestion;
70
- default:
71
- return ts.DiagnosticCategory.Error;
72
- }
73
- };
74
- const hover = (sourceFile, position) => {
75
- const tag = ts.forEachChild(sourceFile, function visitor(node) {
76
- if (position < node.pos) {
77
- return true;
41
+ if (isQueryTag(node)) {
42
+ const template = node.template;
43
+ if (ts.isNoSubstitutionTemplateLiteral(template)) {
44
+ if (position >= template.getStart() + 1 && position < template.getEnd() - 1) {
45
+ return node;
78
46
  }
79
- if (position >= node.end) {
80
- return;
47
+ } else {
48
+ const head = template.head;
49
+ if (position >= head.getStart() + 1 && position < head.getEnd() - 2) {
50
+ return node;
81
51
  }
82
- if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && isGraphqlTag(node.tag.getText())) {
83
- const template = node.template;
84
- if (ts.isNoSubstitutionTemplateLiteral(template)) {
85
- if (position >= template.getStart() + 1 && position < template.getEnd() - 1) {
86
- return node;
87
- }
88
- }
89
- else {
90
- const head = template.head;
91
- if (position >= head.getStart() + 1 && position < head.getEnd() - 2) {
92
- return node;
93
- }
94
- for (const { literal } of template.templateSpans) {
95
- if (position >= literal.getStart() + 1 &&
96
- position < literal.getEnd() - (ts.isTemplateMiddle(literal) ? 2 : 1)) {
97
- return node;
98
- }
99
- }
52
+ for (const { literal } of template.templateSpans){
53
+ if (position >= literal.getStart() + 1 && position < literal.getEnd() - (ts.isTemplateMiddle(literal) ? 2 : 1)) {
54
+ return node;
100
55
  }
101
56
  }
102
- return ts.forEachChild(node, visitor);
103
- });
104
- if (tag === true) {
105
- return;
106
57
  }
107
- return tag;
108
- };
109
- const fix = (node) => {
58
+ }
59
+ return ts.forEachChild(node, visitor);
60
+ });
61
+ if (tag === true) {
62
+ return;
63
+ }
64
+ return tag;
65
+ };
66
+ return {
67
+ create (info) {
68
+ const { path, schema } = graphql.getConfig();
69
+ const languageService = info.languageService;
70
+ if (!path) {
71
+ return languageService;
72
+ }
73
+ const getSourceFile = (fileName)=>languageService.getProgram()?.getSourceFile(fileName);
74
+ const normalizeQuery = (node)=>{
110
75
  const template = node.template;
111
76
  let query = "";
112
- let variables = "";
113
77
  if (ts.isNoSubstitutionTemplateLiteral(template)) {
114
- // 2 ``
78
+ // 2 \`\`
115
79
  const templateWidth = template.getWidth() - 2;
116
80
  query = template.text.padStart(templateWidth);
117
- }
118
- else {
81
+ } else {
119
82
  const head = template.head;
120
83
  const templateSpans = template.templateSpans;
121
- // 3 `...${
84
+ // 3 \`...\${
122
85
  const templateWidth = head.getWidth() - 3;
123
86
  query = head.text.padStart(templateWidth);
124
- templateSpans.forEach((span, i) => {
87
+ templateSpans.forEach((span, i)=>{
125
88
  const spanWidth = span.getFullWidth();
126
89
  const literal = span.literal;
127
90
  const literalWidth = literal.getWidth();
@@ -131,168 +94,130 @@ const init = ({ typescript: ts }) => {
131
94
  const templateWidth = literalWidth - (ts.isTemplateTail(literal) ? 2 : 3);
132
95
  const template = literal.text.padStart(templateWidth);
133
96
  query += variable + template;
134
- variables += variableName + ":Unknown";
135
97
  });
136
98
  }
137
- const tag = node.tag.getText();
138
- let offset = template.getStart() + 1;
139
- query = query.replace(/\n|\r/g, " ");
140
- if (variables) {
141
- query = `${tag}(${variables}){${query}}`;
142
- offset -= tag.length + variables.length + 3;
143
- }
144
- else if (tag === "query") {
145
- query = `{${query}}`;
146
- offset -= 1;
147
- }
148
- else {
149
- query = `${tag}{${query}}`;
150
- offset -= tag.length + 1;
151
- }
152
- const documentNode = graphql.parse(query);
153
- const errors = graphql.validate(schema, documentNode);
154
- for (const error of errors) {
155
- const match = error.message.match(/^Variable ".*?" of type "Unknown" used in position expecting type "(.*?)"\.$/);
156
- if (match) {
157
- query = query.replace("Unknown", match[1]);
158
- offset += 7 - match[1].length;
159
- }
160
- }
99
+ const field = query.match(/\w+/)?.[0] ?? "";
100
+ const isMutation = !!schema.getMutationType()?.getFields()[field];
101
+ const operation = isMutation ? "mutation" : "query";
102
+ query = operation + query.replace(/\n|\r/g, " ");
103
+ const offset = -operation.length + template.getStart() + 1;
161
104
  return {
162
105
  query,
163
- offset,
106
+ offset
164
107
  };
165
108
  };
166
- const proxy = Object.create(null);
167
- for (const [key, value] of Object.entries(languageService)) {
168
- proxy[key] = value.bind(languageService);
169
- }
170
- proxy.getQuickInfoAtPosition = (fileName, position) => {
171
- const sourceFile = getSourceFile(fileName);
172
- if (!sourceFile) {
173
- return undefined;
174
- }
175
- const tag = hover(sourceFile, position);
176
- if (!tag) {
177
- return languageService.getQuickInfoAtPosition(fileName, position);
178
- }
179
- let result;
180
- try {
181
- result = fix(tag);
182
- }
183
- catch {
184
- return languageService.getQuickInfoAtPosition(fileName, position);
185
- }
186
- const { query, offset } = result;
187
- const cursor = new graphqlLanguageServiceUtils.Position(0, position - offset + 1);
188
- const token = graphqlLanguageServiceInterface.getTokenAtPosition(query, cursor);
189
- const marked = graphqlLanguageServiceInterface.getHoverInformation(schema, query, cursor, token);
190
- if (marked === "" || typeof marked !== "string") {
191
- return;
192
- }
193
- return {
194
- kind: ts.ScriptElementKind.string,
195
- textSpan: {
196
- start: offset + token.start,
197
- length: token.end - token.start,
198
- },
199
- kindModifiers: "",
200
- displayParts: [{ text: marked, kind: "" }],
201
- };
202
- };
203
- proxy.getCompletionsAtPosition = (fileName, position, options) => {
204
- const sourceFile = getSourceFile(fileName);
205
- if (!sourceFile) {
206
- return undefined;
207
- }
208
- const tag = hover(sourceFile, position);
209
- if (!tag) {
210
- return languageService.getCompletionsAtPosition(fileName, position, options);
211
- }
212
- let result;
213
- try {
214
- result = fix(tag);
215
- }
216
- catch {
217
- return languageService.getCompletionsAtPosition(fileName, position, options);
218
- }
219
- const { query, offset } = result;
220
- const cursor = new graphqlLanguageServiceUtils.Position(0, position - offset);
221
- const items = graphqlLanguageServiceInterface.getAutocompleteSuggestions(schema, query, cursor);
222
- if (!items.length) {
223
- return;
224
- }
225
- return {
226
- isGlobalCompletion: false,
227
- isMemberCompletion: false,
228
- isNewIdentifierLocation: false,
229
- entries: items.map((item) => {
230
- let kind;
231
- switch (item.kind) {
232
- case vscodeLanguageserverTypes.CompletionItemKind.Function:
233
- case vscodeLanguageserverTypes.CompletionItemKind.Constructor:
234
- kind = ts.ScriptElementKind.functionElement;
235
- break;
236
- case vscodeLanguageserverTypes.CompletionItemKind.Field:
237
- case vscodeLanguageserverTypes.CompletionItemKind.Variable:
238
- kind = ts.ScriptElementKind.memberVariableElement;
239
- break;
240
- default:
241
- kind = ts.ScriptElementKind.unknown;
242
- break;
243
- }
244
- return {
245
- name: item.label,
246
- kindModifiers: "",
247
- kind,
248
- sortText: "",
249
- };
250
- }),
251
- };
252
- };
253
- proxy.getSemanticDiagnostics = (fileName) => {
254
- const diagnostics = languageService.getSemanticDiagnostics(fileName);
255
- const sourceFile = getSourceFile(fileName);
256
- if (!sourceFile) {
257
- return diagnostics;
258
- }
259
- ts.forEachChild(sourceFile, function visitor(node) {
260
- if (ts.isTaggedTemplateExpression(node) && ts.isIdentifier(node.tag) && isGraphqlTag(node.tag.getText())) {
261
- try {
262
- const { query, offset } = fix(node);
263
- const _diagnostics = graphqlLanguageServiceInterface.getDiagnostics(query, schema);
264
- for (const { range: { start, end }, severity, message, } of _diagnostics) {
265
- diagnostics.push({
266
- category: getDiagnosticCategory(severity),
267
- code: 9999,
268
- messageText: message,
269
- file: sourceFile,
270
- start: start.character + offset,
271
- length: end.character - start.character,
272
- });
109
+ const proxy = {
110
+ ...languageService,
111
+ getQuickInfoAtPosition (fileName, position) {
112
+ const sourceFile = getSourceFile(fileName);
113
+ if (!sourceFile) {
114
+ return;
115
+ }
116
+ const tag = getHoverQueryTag(sourceFile, position);
117
+ if (!tag) {
118
+ return languageService.getQuickInfoAtPosition(fileName, position);
119
+ }
120
+ const { query, offset } = normalizeQuery(tag);
121
+ const cursor = new graphqlLanguageService.Position(0, position - offset);
122
+ const token = graphqlLanguageService.getTokenAtPosition(query, cursor);
123
+ const marked = graphqlLanguageService.getHoverInformation(schema, query, cursor, token);
124
+ if (marked === "" || typeof marked !== "string") {
125
+ return;
126
+ }
127
+ return {
128
+ kind: ts.ScriptElementKind.string,
129
+ textSpan: {
130
+ start: offset + token.start,
131
+ length: token.end - token.start
132
+ },
133
+ kindModifiers: "",
134
+ displayParts: [
135
+ {
136
+ text: marked,
137
+ kind: ""
273
138
  }
274
- }
275
- catch (error) {
276
- if (error instanceof graphql.GraphQLError) {
277
- diagnostics.push({
278
- category: ts.DiagnosticCategory.Error,
279
- code: 9999,
280
- messageText: error.message,
281
- file: sourceFile,
282
- start: node.template.getStart() + 1,
283
- length: node.template.getWidth() - 2,
284
- });
139
+ ]
140
+ };
141
+ },
142
+ getCompletionsAtPosition (fileName, position, options) {
143
+ const sourceFile = getSourceFile(fileName);
144
+ if (!sourceFile) {
145
+ return;
146
+ }
147
+ const tag = getHoverQueryTag(sourceFile, position);
148
+ if (!tag) {
149
+ return languageService.getCompletionsAtPosition(fileName, position, options);
150
+ }
151
+ const { query, offset } = normalizeQuery(tag);
152
+ const cursor = new graphqlLanguageService.Position(0, position - offset);
153
+ const items = graphqlLanguageService.getAutocompleteSuggestions(schema, query, cursor);
154
+ if (/^\s*\{\s*\}\s*$/.test(query)) {
155
+ const operation = "mutation";
156
+ const cursor = new graphqlLanguageService.Position(0, operation.length + position - offset);
157
+ const labels = new Set(items.map((item)=>item.label));
158
+ const mutationItems = graphqlLanguageService.getAutocompleteSuggestions(schema, operation + query, cursor).filter((item)=>!labels.has(item.label));
159
+ items.push(...mutationItems);
160
+ }
161
+ if (!items.length) {
162
+ return;
163
+ }
164
+ return {
165
+ isGlobalCompletion: false,
166
+ isMemberCompletion: false,
167
+ isNewIdentifierLocation: false,
168
+ entries: items.map((item)=>({
169
+ name: item.label,
170
+ kindModifiers: "",
171
+ kind: getScriptElementKind(item.kind),
172
+ sortText: ""
173
+ }))
174
+ };
175
+ },
176
+ getSemanticDiagnostics (fileName) {
177
+ const diagnostics = languageService.getSemanticDiagnostics(fileName);
178
+ const sourceFile = getSourceFile(fileName);
179
+ if (!sourceFile) {
180
+ return diagnostics;
181
+ }
182
+ ts.forEachChild(sourceFile, function visitor(node) {
183
+ if (isQueryTag(node)) {
184
+ try {
185
+ const { query, offset } = normalizeQuery(node);
186
+ const _diagnostics = graphqlLanguageService.getDiagnostics(query, schema);
187
+ for (const { range: { start, end }, severity, message } of _diagnostics){
188
+ if (/Variable "\$.*?" is not defined/.test(message)) {
189
+ continue;
190
+ }
191
+ diagnostics.push({
192
+ category: getDiagnosticCategory(severity),
193
+ code: 9999,
194
+ messageText: message,
195
+ file: sourceFile,
196
+ start: start.character + offset,
197
+ length: end.character - start.character
198
+ });
199
+ }
200
+ } catch (error) {
201
+ if (error instanceof graphql$1.GraphQLError) {
202
+ diagnostics.push({
203
+ category: ts.DiagnosticCategory.Error,
204
+ code: 9999,
205
+ messageText: error.message,
206
+ file: sourceFile,
207
+ start: node.template.getStart() + 1,
208
+ length: node.template.getWidth() - 2
209
+ });
210
+ }
285
211
  }
286
212
  }
287
- }
288
- ts.forEachChild(node, visitor);
289
- });
290
- return diagnostics;
213
+ ts.forEachChild(node, visitor);
214
+ });
215
+ return diagnostics;
216
+ }
291
217
  };
292
218
  return proxy;
293
- },
219
+ }
294
220
  };
295
221
  };
296
222
 
297
223
  module.exports = init;
298
- //# sourceMappingURL=index.cjs.map
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { server } from 'typescript/lib/tsserverlibrary';
1
+ import ts__default from 'typescript';
2
2
 
3
- declare const init: server.PluginModuleFactory;
3
+ declare const init: ts__default.server.PluginModuleFactory;
4
4
 
5
5
  export { init as default };