vscode-apollo 1.19.3 → 1.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.changeset/README.md +8 -0
- package/.changeset/config.json +14 -0
- package/.circleci/config.yml +82 -0
- package/.eslintrc.js +10 -0
- package/.gitattributes +1 -0
- package/.github/workflows/build-prs.yml +57 -0
- package/.github/workflows/release.yml +114 -0
- package/.gitleaks.toml +26 -0
- package/.nvmrc +1 -0
- package/.prettierrc +5 -0
- package/.vscode/launch.json +61 -0
- package/.vscode/settings.json +16 -0
- package/.vscode/tasks.json +18 -0
- package/.vscodeignore +17 -1
- package/CHANGELOG.md +178 -1
- package/LICENSE +2 -2
- package/README.md +9 -9
- package/codegen.yml +12 -0
- package/images/IconRun.svg +8 -0
- package/jest.config.ts +11 -0
- package/package.json +102 -22
- package/renovate.json +23 -0
- package/src/__mocks__/fs.js +3 -0
- package/src/__tests__/statusBar.test.ts +8 -7
- package/src/debug.ts +2 -5
- package/src/env/fetch/fetch.ts +32 -0
- package/src/env/fetch/global.ts +30 -0
- package/src/env/fetch/index.d.ts +2 -0
- package/src/env/fetch/index.ts +2 -0
- package/src/env/fetch/url.ts +9 -0
- package/src/env/index.ts +4 -0
- package/src/env/polyfills/array.ts +17 -0
- package/src/env/polyfills/index.ts +2 -0
- package/src/env/polyfills/object.ts +7 -0
- package/src/env/typescript-utility-types.ts +2 -0
- package/src/extension.ts +106 -37
- package/src/language-server/__tests__/diagnostics.test.ts +86 -0
- package/src/language-server/__tests__/document.test.ts +187 -0
- package/src/language-server/__tests__/fileSet.test.ts +46 -0
- package/src/language-server/__tests__/fixtures/starwarsSchema.ts +1917 -0
- package/src/language-server/config/__tests__/config.ts +128 -0
- package/src/language-server/config/__tests__/loadConfig.ts +508 -0
- package/src/language-server/config/__tests__/utils.ts +106 -0
- package/src/language-server/config/config.ts +219 -0
- package/src/language-server/config/index.ts +3 -0
- package/src/language-server/config/loadConfig.ts +228 -0
- package/src/language-server/config/utils.ts +56 -0
- package/src/language-server/diagnostics.ts +109 -0
- package/src/language-server/document.ts +277 -0
- package/src/language-server/engine/GraphQLDataSource.ts +124 -0
- package/src/language-server/engine/index.ts +105 -0
- package/src/language-server/engine/operations/frontendUrlRoot.ts +7 -0
- package/src/language-server/engine/operations/schemaTagsAndFieldStats.ts +24 -0
- package/src/language-server/errors/__tests__/NoMissingClientDirectives.test.ts +220 -0
- package/src/language-server/errors/logger.ts +58 -0
- package/src/language-server/errors/validation.ts +277 -0
- package/src/language-server/fileSet.ts +65 -0
- package/src/language-server/format.ts +48 -0
- package/src/language-server/graphqlTypes.ts +7176 -0
- package/src/language-server/index.ts +29 -0
- package/src/language-server/languageProvider.ts +798 -0
- package/src/language-server/loadingHandler.ts +64 -0
- package/src/language-server/project/base.ts +399 -0
- package/src/language-server/project/client.ts +602 -0
- package/src/language-server/project/defaultClientSchema.ts +45 -0
- package/src/language-server/project/service.ts +48 -0
- package/src/language-server/providers/schema/__tests__/file.ts +150 -0
- package/src/language-server/providers/schema/base.ts +15 -0
- package/src/language-server/providers/schema/endpoint.ts +157 -0
- package/src/language-server/providers/schema/engine.ts +197 -0
- package/src/language-server/providers/schema/file.ts +167 -0
- package/src/language-server/providers/schema/index.ts +75 -0
- package/src/language-server/server.ts +252 -0
- package/src/language-server/typings/codemirror.d.ts +4 -0
- package/src/language-server/typings/graphql.d.ts +27 -0
- package/src/language-server/utilities/__tests__/graphql.test.ts +411 -0
- package/src/language-server/utilities/__tests__/uri.ts +55 -0
- package/src/language-server/utilities/debouncer.ts +8 -0
- package/src/language-server/utilities/debug.ts +89 -0
- package/src/language-server/utilities/graphql.ts +432 -0
- package/src/language-server/utilities/index.ts +3 -0
- package/src/language-server/utilities/source.ts +182 -0
- package/src/language-server/utilities/uri.ts +19 -0
- package/src/language-server/workspace.ts +262 -0
- package/src/languageServerClient.ts +19 -12
- package/src/messages.ts +84 -0
- package/src/statusBar.ts +5 -5
- package/src/tools/__tests__/buildServiceDefinition.test.ts +491 -0
- package/src/tools/__tests__/snapshotSerializers/astSerializer.ts +19 -0
- package/src/tools/__tests__/snapshotSerializers/graphQLTypeSerializer.ts +14 -0
- package/src/tools/buildServiceDefinition.ts +241 -0
- package/src/tools/index.ts +6 -0
- package/src/tools/schema/index.ts +2 -0
- package/src/tools/schema/resolveObject.ts +18 -0
- package/src/tools/schema/resolverMap.ts +23 -0
- package/src/tools/utilities/graphql.ts +22 -0
- package/src/tools/utilities/index.ts +3 -0
- package/src/tools/utilities/invariant.ts +5 -0
- package/src/tools/utilities/predicates.ts +5 -0
- package/src/utils.ts +1 -16
- package/syntaxes/graphql.js.json +3 -3
- package/syntaxes/graphql.json +13 -9
- package/syntaxes/graphql.lua.json +51 -0
- package/syntaxes/graphql.rb.json +1 -1
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +20 -7
- package/create-server-symlink.js +0 -8
- package/lib/debug.d.ts +0 -11
- package/lib/debug.d.ts.map +0 -1
- package/lib/debug.js +0 -48
- package/lib/debug.js.map +0 -1
- package/lib/extension.d.ts +0 -4
- package/lib/extension.d.ts.map +0 -1
- package/lib/extension.js +0 -187
- package/lib/extension.js.map +0 -1
- package/lib/languageServerClient.d.ts +0 -4
- package/lib/languageServerClient.d.ts.map +0 -1
- package/lib/languageServerClient.js +0 -57
- package/lib/languageServerClient.js.map +0 -1
- package/lib/statusBar.d.ts +0 -24
- package/lib/statusBar.d.ts.map +0 -1
- package/lib/statusBar.js +0 -46
- package/lib/statusBar.js.map +0 -1
- package/lib/testRunner/index.d.ts +0 -3
- package/lib/testRunner/index.d.ts.map +0 -1
- package/lib/testRunner/index.js +0 -49
- package/lib/testRunner/index.js.map +0 -1
- package/lib/testRunner/jest-config.d.ts +0 -14
- package/lib/testRunner/jest-config.d.ts.map +0 -1
- package/lib/testRunner/jest-config.js +0 -18
- package/lib/testRunner/jest-config.js.map +0 -1
- package/lib/testRunner/jest-vscode-environment.d.ts +0 -2
- package/lib/testRunner/jest-vscode-environment.d.ts.map +0 -1
- package/lib/testRunner/jest-vscode-environment.js +0 -19
- package/lib/testRunner/jest-vscode-environment.js.map +0 -1
- package/lib/testRunner/jest-vscode-framework-setup.d.ts +0 -1
- package/lib/testRunner/jest-vscode-framework-setup.d.ts.map +0 -1
- package/lib/testRunner/jest-vscode-framework-setup.js +0 -3
- package/lib/testRunner/jest-vscode-framework-setup.js.map +0 -1
- package/lib/testRunner/vscode-test-script.d.ts +0 -2
- package/lib/testRunner/vscode-test-script.d.ts.map +0 -1
- package/lib/testRunner/vscode-test-script.js +0 -23
- package/lib/testRunner/vscode-test-script.js.map +0 -1
- package/lib/utils.d.ts +0 -18
- package/lib/utils.d.ts.map +0 -1
- package/lib/utils.js +0 -52
- package/lib/utils.js.map +0 -1
- package/src/testRunner/README.md +0 -23
- package/src/testRunner/index.ts +0 -72
- package/src/testRunner/jest-config.ts +0 -17
- package/src/testRunner/jest-vscode-environment.ts +0 -25
- package/src/testRunner/jest-vscode-framework-setup.ts +0 -10
- package/src/testRunner/jest.d.ts +0 -37
- package/src/testRunner/vscode-test-script.ts +0 -38
- package/tsconfig.test.json +0 -4
- package/tsconfig.tsbuildinfo +0 -2486
package/src/extension.ts
CHANGED
|
@@ -9,14 +9,17 @@ import {
|
|
|
9
9
|
commands,
|
|
10
10
|
QuickPickItem,
|
|
11
11
|
Disposable,
|
|
12
|
-
OutputChannel
|
|
12
|
+
OutputChannel,
|
|
13
|
+
MarkdownString,
|
|
14
|
+
Range,
|
|
13
15
|
} from "vscode";
|
|
14
16
|
import StatusBar from "./statusBar";
|
|
15
17
|
import { getLanguageServerClient } from "./languageServerClient";
|
|
16
|
-
import {
|
|
18
|
+
import { NotificationType } from "vscode-languageclient";
|
|
19
|
+
import type { EngineDecoration, LanguageClient } from "./messages";
|
|
17
20
|
import {
|
|
18
21
|
printNoFileOpenMessage,
|
|
19
|
-
printStatsToClientOutputChannel
|
|
22
|
+
printStatsToClientOutputChannel,
|
|
20
23
|
} from "./utils";
|
|
21
24
|
import { Debug } from "./debug";
|
|
22
25
|
|
|
@@ -43,7 +46,7 @@ function isError(response: any): response is ErrorShape {
|
|
|
43
46
|
|
|
44
47
|
export function activate(context: ExtensionContext) {
|
|
45
48
|
const serverModule = context.asAbsolutePath(
|
|
46
|
-
join("
|
|
49
|
+
join("lib/language-server", "server.js")
|
|
47
50
|
);
|
|
48
51
|
|
|
49
52
|
// Initialize language client
|
|
@@ -52,7 +55,7 @@ export function activate(context: ExtensionContext) {
|
|
|
52
55
|
|
|
53
56
|
// Initialize disposables
|
|
54
57
|
statusBar = new StatusBar({
|
|
55
|
-
hasActiveTextEditor: Boolean(window.activeTextEditor)
|
|
58
|
+
hasActiveTextEditor: Boolean(window.activeTextEditor),
|
|
56
59
|
});
|
|
57
60
|
outputChannel = window.createOutputChannel("Apollo GraphQL");
|
|
58
61
|
Debug.SetOutputConsole(outputChannel);
|
|
@@ -68,7 +71,7 @@ export function activate(context: ExtensionContext) {
|
|
|
68
71
|
|
|
69
72
|
// Once client is ready, we can send messages and add listeners for various notifications
|
|
70
73
|
client.onReady().then(() => {
|
|
71
|
-
client.onNotification(serverDebugMessage, message => {
|
|
74
|
+
client.onNotification(serverDebugMessage, (message) => {
|
|
72
75
|
switch (message.type) {
|
|
73
76
|
case "info":
|
|
74
77
|
Debug.info(message.message, message.stack);
|
|
@@ -103,7 +106,7 @@ export function activate(context: ExtensionContext) {
|
|
|
103
106
|
client.outputChannel.show();
|
|
104
107
|
});
|
|
105
108
|
|
|
106
|
-
client.onNotification("apollographql/statsLoaded", params => {
|
|
109
|
+
client.onNotification("apollographql/statsLoaded", (params) => {
|
|
107
110
|
printStatsToClientOutputChannel(client, params, version);
|
|
108
111
|
client.outputChannel.show();
|
|
109
112
|
});
|
|
@@ -118,14 +121,14 @@ export function activate(context: ExtensionContext) {
|
|
|
118
121
|
if (isError(response)) {
|
|
119
122
|
statusBar.showWarningState({
|
|
120
123
|
hasActiveTextEditor,
|
|
121
|
-
tooltip: "Configuration Error"
|
|
124
|
+
tooltip: "Configuration Error",
|
|
122
125
|
});
|
|
123
126
|
outputChannel.append(response.stack);
|
|
124
127
|
|
|
125
128
|
const infoButtonText = "More Info";
|
|
126
129
|
window
|
|
127
130
|
.showInformationMessage(response.message, infoButtonText)
|
|
128
|
-
.then(clicked => {
|
|
131
|
+
.then((clicked) => {
|
|
129
132
|
if (clicked === infoButtonText) {
|
|
130
133
|
outputChannel.show();
|
|
131
134
|
}
|
|
@@ -134,7 +137,7 @@ export function activate(context: ExtensionContext) {
|
|
|
134
137
|
if (response.length === 0) {
|
|
135
138
|
statusBar.showWarningState({
|
|
136
139
|
hasActiveTextEditor,
|
|
137
|
-
tooltip: "No apollo.config.js file found"
|
|
140
|
+
tooltip: "No apollo.config.js file found",
|
|
138
141
|
});
|
|
139
142
|
} else {
|
|
140
143
|
statusBar.showLoadedState({ hasActiveTextEditor });
|
|
@@ -158,12 +161,12 @@ export function activate(context: ExtensionContext) {
|
|
|
158
161
|
|
|
159
162
|
// For some reason, non-strings can only be sent in one direction. For now, messages
|
|
160
163
|
// coming from the language server just need to be stringified and parsed.
|
|
161
|
-
client.onNotification("apollographql/tagsLoaded", params => {
|
|
164
|
+
client.onNotification("apollographql/tagsLoaded", (params) => {
|
|
162
165
|
const [serviceID, tags]: [string, string[]] = JSON.parse(params);
|
|
163
|
-
const items = tags.map(tag => ({
|
|
166
|
+
const items = tags.map((tag) => ({
|
|
164
167
|
label: tag,
|
|
165
168
|
description: "",
|
|
166
|
-
detail: serviceID
|
|
169
|
+
detail: serviceID,
|
|
167
170
|
}));
|
|
168
171
|
|
|
169
172
|
schemaTagItems = [...items, ...schemaTagItems];
|
|
@@ -178,9 +181,9 @@ export function activate(context: ExtensionContext) {
|
|
|
178
181
|
|
|
179
182
|
let currentLoadingResolve: Map<number, () => void> = new Map();
|
|
180
183
|
|
|
181
|
-
client.onNotification("apollographql/loadingComplete", token => {
|
|
184
|
+
client.onNotification("apollographql/loadingComplete", (token) => {
|
|
182
185
|
statusBar.showLoadedState({
|
|
183
|
-
hasActiveTextEditor: Boolean(window.activeTextEditor)
|
|
186
|
+
hasActiveTextEditor: Boolean(window.activeTextEditor),
|
|
184
187
|
});
|
|
185
188
|
const inMap = currentLoadingResolve.get(token);
|
|
186
189
|
if (inMap) {
|
|
@@ -194,46 +197,112 @@ export function activate(context: ExtensionContext) {
|
|
|
194
197
|
{
|
|
195
198
|
location: ProgressLocation.Notification,
|
|
196
199
|
title: message,
|
|
197
|
-
cancellable: false
|
|
200
|
+
cancellable: false,
|
|
198
201
|
},
|
|
199
202
|
() => {
|
|
200
|
-
return new Promise<void>(resolve => {
|
|
203
|
+
return new Promise<void>((resolve) => {
|
|
201
204
|
currentLoadingResolve.set(token, resolve);
|
|
202
205
|
});
|
|
203
206
|
}
|
|
204
207
|
);
|
|
205
208
|
});
|
|
206
209
|
|
|
207
|
-
const
|
|
208
|
-
|
|
210
|
+
const runIconOnDiskPath = Uri.file(
|
|
211
|
+
join(context.extensionPath, "images", "IconRun.svg")
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const textDecorationType = window.createTextEditorDecorationType({});
|
|
215
|
+
const runGlyphDecorationType = window.createTextEditorDecorationType({});
|
|
216
|
+
let latestDecorations: EngineDecoration[] | undefined = undefined;
|
|
209
217
|
|
|
210
218
|
const updateDecorations = () => {
|
|
211
|
-
if (window.activeTextEditor &&
|
|
219
|
+
if (window.activeTextEditor && latestDecorations) {
|
|
212
220
|
const editor = window.activeTextEditor!;
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
221
|
+
|
|
222
|
+
const decorationsForDocument = latestDecorations.filter(
|
|
223
|
+
(decoration) =>
|
|
224
|
+
decoration.document ===
|
|
225
|
+
window.activeTextEditor!.document.uri.toString()
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
const textDecorations = decorationsForDocument.flatMap(
|
|
229
|
+
(decoration): DecorationOptions | DecorationOptions[] => {
|
|
230
|
+
if (decoration.type !== "text") {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
|
|
218
234
|
return {
|
|
219
|
-
range: editor.document.lineAt(
|
|
235
|
+
range: editor.document.lineAt(decoration.range.start.line).range,
|
|
220
236
|
renderOptions: {
|
|
221
237
|
after: {
|
|
222
|
-
contentText:
|
|
223
|
-
textDecoration: "none; padding-left: 15px; opacity: .5"
|
|
224
|
-
}
|
|
225
|
-
}
|
|
238
|
+
contentText: decoration.message,
|
|
239
|
+
textDecoration: "none; padding-left: 15px; opacity: .5",
|
|
240
|
+
},
|
|
241
|
+
},
|
|
226
242
|
};
|
|
227
|
-
}
|
|
243
|
+
}
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
const runGlyphDecorations = decorationsForDocument.flatMap(
|
|
247
|
+
(decoration): DecorationOptions | DecorationOptions[] => {
|
|
248
|
+
if (decoration.type !== "runGlyph") {
|
|
249
|
+
return [];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const hoverMessage =
|
|
253
|
+
decoration.hoverMessage === undefined
|
|
254
|
+
? undefined
|
|
255
|
+
: new MarkdownString(decoration.hoverMessage);
|
|
256
|
+
if (hoverMessage) {
|
|
257
|
+
hoverMessage.isTrusted = true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const endOfLinePosition = editor.document.lineAt(
|
|
261
|
+
decoration.range.start.line
|
|
262
|
+
).range.end;
|
|
263
|
+
return {
|
|
264
|
+
// Hover range of just the end of the line (and the icon) so the hover shows above the icon,
|
|
265
|
+
// not over at the start of the line
|
|
266
|
+
range: new Range(endOfLinePosition, endOfLinePosition),
|
|
267
|
+
renderOptions: {
|
|
268
|
+
after: {
|
|
269
|
+
contentIconPath: runIconOnDiskPath,
|
|
270
|
+
textDecoration:
|
|
271
|
+
"none; border-radius: .20rem; margin-left: 8px; text-align: center;",
|
|
272
|
+
backgroundColor: "#2075D6",
|
|
273
|
+
width: "18px",
|
|
274
|
+
height: "18px",
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
hoverMessage,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
);
|
|
228
281
|
|
|
229
|
-
window.activeTextEditor!.setDecorations(
|
|
282
|
+
window.activeTextEditor!.setDecorations(
|
|
283
|
+
textDecorationType,
|
|
284
|
+
textDecorations
|
|
285
|
+
);
|
|
286
|
+
if (
|
|
287
|
+
workspace
|
|
288
|
+
.getConfiguration("apollographql")
|
|
289
|
+
.get("display.showRunInStudioButton")
|
|
290
|
+
) {
|
|
291
|
+
window.activeTextEditor!.setDecorations(
|
|
292
|
+
runGlyphDecorationType,
|
|
293
|
+
runGlyphDecorations
|
|
294
|
+
);
|
|
295
|
+
}
|
|
230
296
|
}
|
|
231
297
|
};
|
|
232
298
|
|
|
233
|
-
client.onNotification(
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
299
|
+
client.onNotification(
|
|
300
|
+
"apollographql/engineDecorations",
|
|
301
|
+
({ decorations }) => {
|
|
302
|
+
latestDecorations = decorations;
|
|
303
|
+
updateDecorations();
|
|
304
|
+
}
|
|
305
|
+
);
|
|
237
306
|
|
|
238
307
|
window.onDidChangeActiveTextEditor(() => {
|
|
239
308
|
updateDecorations();
|
|
@@ -243,7 +312,7 @@ export function activate(context: ExtensionContext) {
|
|
|
243
312
|
provideTextDocumentContent(uri: Uri) {
|
|
244
313
|
// the schema source is provided inside the URI, just return that here
|
|
245
314
|
return uri.query;
|
|
246
|
-
}
|
|
315
|
+
},
|
|
247
316
|
});
|
|
248
317
|
});
|
|
249
318
|
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Source, buildClientSchema } from "graphql";
|
|
2
|
+
import { GraphQLDocument } from "../document";
|
|
3
|
+
import { collectExecutableDefinitionDiagnositics } from "../diagnostics";
|
|
4
|
+
import { starwarsSchema } from "./fixtures/starwarsSchema";
|
|
5
|
+
|
|
6
|
+
const schema = buildClientSchema(starwarsSchema);
|
|
7
|
+
|
|
8
|
+
const validDocument = new GraphQLDocument(
|
|
9
|
+
new Source(`
|
|
10
|
+
query HeroAndFriendsNames {
|
|
11
|
+
hero {
|
|
12
|
+
name
|
|
13
|
+
friends {
|
|
14
|
+
name
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}`)
|
|
18
|
+
);
|
|
19
|
+
const invalidDocument = new GraphQLDocument(
|
|
20
|
+
new Source(`
|
|
21
|
+
query HeroAndFriendsNames {
|
|
22
|
+
hero {
|
|
23
|
+
nam # Missing letter 'e'
|
|
24
|
+
friend { # Missing letter 's'
|
|
25
|
+
name
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}`)
|
|
29
|
+
);
|
|
30
|
+
const documentWithTypes = new GraphQLDocument(
|
|
31
|
+
new Source(`
|
|
32
|
+
type SomeType {
|
|
33
|
+
thing: String
|
|
34
|
+
}
|
|
35
|
+
enum SomeEnum {
|
|
36
|
+
THING_ONE
|
|
37
|
+
THING_TWO
|
|
38
|
+
}
|
|
39
|
+
query HeroAndFriendsNames {
|
|
40
|
+
hero {
|
|
41
|
+
name
|
|
42
|
+
friends {
|
|
43
|
+
name
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}`)
|
|
47
|
+
);
|
|
48
|
+
const documentWithOffset = new GraphQLDocument(
|
|
49
|
+
new Source(`query QueryWithOffset { hero { nam } }`, "testDocument", {
|
|
50
|
+
line: 5,
|
|
51
|
+
column: 10,
|
|
52
|
+
})
|
|
53
|
+
);
|
|
54
|
+
describe("Language server diagnostics", () => {
|
|
55
|
+
describe("#collectExecutableDefinitionDiagnositics", () => {
|
|
56
|
+
it("returns no diagnostics for a correct document", () => {
|
|
57
|
+
const diagnostics = collectExecutableDefinitionDiagnositics(
|
|
58
|
+
schema,
|
|
59
|
+
validDocument
|
|
60
|
+
);
|
|
61
|
+
expect(diagnostics.length).toEqual(0);
|
|
62
|
+
});
|
|
63
|
+
it("returns two diagnostics for a document with two errors", () => {
|
|
64
|
+
const diagnostics = collectExecutableDefinitionDiagnositics(
|
|
65
|
+
schema,
|
|
66
|
+
invalidDocument
|
|
67
|
+
);
|
|
68
|
+
expect(diagnostics.length).toEqual(2);
|
|
69
|
+
});
|
|
70
|
+
it("returns no diagnostics for a document that includes type definitions", () => {
|
|
71
|
+
const diagnostics = collectExecutableDefinitionDiagnositics(
|
|
72
|
+
schema,
|
|
73
|
+
documentWithTypes
|
|
74
|
+
);
|
|
75
|
+
expect(diagnostics.length).toEqual(0);
|
|
76
|
+
});
|
|
77
|
+
it("correctly offsets locations", () => {
|
|
78
|
+
const diagnostics = collectExecutableDefinitionDiagnositics(
|
|
79
|
+
schema,
|
|
80
|
+
documentWithOffset
|
|
81
|
+
);
|
|
82
|
+
expect(diagnostics.length).toEqual(1);
|
|
83
|
+
expect(diagnostics[0].range.start.character).toEqual(40);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { extractGraphQLDocuments } from "../document";
|
|
2
|
+
import { TextDocument, Position } from "vscode-languageserver";
|
|
3
|
+
|
|
4
|
+
describe("extractGraphQLDocuments", () => {
|
|
5
|
+
describe("extracting documents from JavaScript template literals", () => {
|
|
6
|
+
const mockTextDocument = (text: string): TextDocument => ({
|
|
7
|
+
getText: jest.fn().mockReturnValue(text),
|
|
8
|
+
offsetAt(): number {
|
|
9
|
+
return 0;
|
|
10
|
+
},
|
|
11
|
+
positionAt(): Position {
|
|
12
|
+
return {
|
|
13
|
+
character: 0,
|
|
14
|
+
line: 0,
|
|
15
|
+
};
|
|
16
|
+
},
|
|
17
|
+
languageId: "javascript",
|
|
18
|
+
lineCount: 0,
|
|
19
|
+
uri: "",
|
|
20
|
+
version: 1,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("works with placeholders that span multiple rows", () => {
|
|
24
|
+
const textDocument = mockTextDocument(`
|
|
25
|
+
gql\`
|
|
26
|
+
{
|
|
27
|
+
hero {
|
|
28
|
+
...Hero_character
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
\${Hero.fragments
|
|
33
|
+
.character}
|
|
34
|
+
\`
|
|
35
|
+
`);
|
|
36
|
+
const documents = extractGraphQLDocuments(textDocument);
|
|
37
|
+
|
|
38
|
+
expect(documents?.length).toEqual(1);
|
|
39
|
+
expect(documents?.[0].syntaxErrors.length).toBe(0);
|
|
40
|
+
expect(documents?.[0].ast?.definitions.length).toBe(1);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("works with multiple placeholders in a document", () => {
|
|
44
|
+
const textDocument = mockTextDocument(`
|
|
45
|
+
gql\`
|
|
46
|
+
{
|
|
47
|
+
hero {
|
|
48
|
+
...Hero_character
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
\${Hero.fragments.character}
|
|
53
|
+
|
|
54
|
+
{
|
|
55
|
+
reviews(episode: NEWHOPE) {
|
|
56
|
+
...ReviewList_reviews
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
\${ReviewList.fragments.reviews}
|
|
61
|
+
\`
|
|
62
|
+
`);
|
|
63
|
+
const documents = extractGraphQLDocuments(textDocument);
|
|
64
|
+
|
|
65
|
+
expect(documents?.length).toEqual(1);
|
|
66
|
+
expect(documents?.[0].syntaxErrors.length).toBe(0);
|
|
67
|
+
expect(documents?.[0].ast?.definitions.length).toBe(2);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("works with a custom tagname", () => {
|
|
71
|
+
const textDocument = mockTextDocument(`
|
|
72
|
+
gqltag\`
|
|
73
|
+
{
|
|
74
|
+
hero {
|
|
75
|
+
...Hero_character
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
\${Hero.fragments.character}
|
|
80
|
+
|
|
81
|
+
{
|
|
82
|
+
reviews(episode: NEWHOPE) {
|
|
83
|
+
...ReviewList_reviews
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
\${ReviewList.fragments.reviews}
|
|
88
|
+
\`
|
|
89
|
+
`);
|
|
90
|
+
const documents = extractGraphQLDocuments(textDocument, "gqltag");
|
|
91
|
+
|
|
92
|
+
expect(documents?.length).toEqual(1);
|
|
93
|
+
expect(documents?.[0].syntaxErrors.length).toBe(0);
|
|
94
|
+
expect(documents?.[0].ast?.definitions.length).toBe(2);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it("works with parens", () => {
|
|
98
|
+
const textDocument = mockTextDocument(`
|
|
99
|
+
gql(\`
|
|
100
|
+
{
|
|
101
|
+
hero {
|
|
102
|
+
...Hero_character
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
\${Hero.fragments.character}
|
|
107
|
+
|
|
108
|
+
{
|
|
109
|
+
reviews(episode: NEWHOPE) {
|
|
110
|
+
...ReviewList_reviews
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
\${ReviewList.fragments.reviews}
|
|
115
|
+
\`)
|
|
116
|
+
`);
|
|
117
|
+
const documents = extractGraphQLDocuments(textDocument);
|
|
118
|
+
|
|
119
|
+
expect(documents?.length).toEqual(1);
|
|
120
|
+
expect(documents?.[0].syntaxErrors.length).toBe(0);
|
|
121
|
+
expect(documents?.[0].ast?.definitions.length).toBe(2);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("extracting documents from ReasonML extension nodes", () => {
|
|
126
|
+
const mockTextDocument = (text: string): TextDocument => ({
|
|
127
|
+
getText: jest.fn().mockReturnValue(text),
|
|
128
|
+
offsetAt(): number {
|
|
129
|
+
return 0;
|
|
130
|
+
},
|
|
131
|
+
positionAt(): Position {
|
|
132
|
+
return {
|
|
133
|
+
character: 0,
|
|
134
|
+
line: 0,
|
|
135
|
+
};
|
|
136
|
+
},
|
|
137
|
+
languageId: "reason",
|
|
138
|
+
lineCount: 0,
|
|
139
|
+
uri: "",
|
|
140
|
+
version: 1,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("works with ReasonRelay nodes", () => {
|
|
144
|
+
const textDocument = mockTextDocument(`
|
|
145
|
+
module Query = [%relay.query
|
|
146
|
+
{|
|
|
147
|
+
query SomeQuery {
|
|
148
|
+
id
|
|
149
|
+
}
|
|
150
|
+
|}
|
|
151
|
+
];
|
|
152
|
+
|
|
153
|
+
module Fragment = [%relay.fragment
|
|
154
|
+
{|
|
|
155
|
+
fragment X on Hero {
|
|
156
|
+
id
|
|
157
|
+
}
|
|
158
|
+
|}
|
|
159
|
+
];
|
|
160
|
+
`);
|
|
161
|
+
const documents = extractGraphQLDocuments(textDocument);
|
|
162
|
+
|
|
163
|
+
expect(documents?.length).toEqual(2);
|
|
164
|
+
expect(documents?.[0].syntaxErrors.length).toBe(0);
|
|
165
|
+
expect(documents?.[1].syntaxErrors.length).toBe(0);
|
|
166
|
+
expect(documents?.[0].ast?.definitions.length).toBe(1);
|
|
167
|
+
expect(documents?.[1].ast?.definitions.length).toBe(1);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("works with graphql_ppx style node", () => {
|
|
171
|
+
const textDocument = mockTextDocument(`
|
|
172
|
+
module Query = [%graphql
|
|
173
|
+
{|
|
|
174
|
+
query SomeQuery {
|
|
175
|
+
id
|
|
176
|
+
}
|
|
177
|
+
|}
|
|
178
|
+
];
|
|
179
|
+
`);
|
|
180
|
+
const documents = extractGraphQLDocuments(textDocument);
|
|
181
|
+
|
|
182
|
+
expect(documents?.length).toEqual(1);
|
|
183
|
+
expect(documents?.[0].syntaxErrors.length).toBe(0);
|
|
184
|
+
expect(documents?.[0].ast?.definitions.length).toBe(1);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { FileSet } from "../fileSet";
|
|
2
|
+
import URI from "vscode-uri";
|
|
3
|
+
|
|
4
|
+
describe("fileSet", () => {
|
|
5
|
+
describe("includesFile", () => {
|
|
6
|
+
it("matches includes starting with ./", () => {
|
|
7
|
+
const fileSet = new FileSet({
|
|
8
|
+
excludes: [],
|
|
9
|
+
includes: ["./src/**/*.tsx"],
|
|
10
|
+
rootURI: URI.parse("/project"),
|
|
11
|
+
});
|
|
12
|
+
const file = "file:///project/src/Component.tsx";
|
|
13
|
+
expect(fileSet.includesFile(file)).toBe(true);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("matches includes not starting with ./", () => {
|
|
17
|
+
const fileSet = new FileSet({
|
|
18
|
+
excludes: [],
|
|
19
|
+
includes: ["src/**/*.tsx"],
|
|
20
|
+
rootURI: URI.parse("/project"),
|
|
21
|
+
});
|
|
22
|
+
const file = "file:///project/src/Component.tsx";
|
|
23
|
+
expect(fileSet.includesFile(file)).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("does not match excludes starting with ./", () => {
|
|
27
|
+
const fileSet = new FileSet({
|
|
28
|
+
excludes: ["./src/Component.tsx"],
|
|
29
|
+
includes: ["./src/**/*.tsx"],
|
|
30
|
+
rootURI: URI.parse("/project"),
|
|
31
|
+
});
|
|
32
|
+
const file = "file:///project/src/Component.tsx";
|
|
33
|
+
expect(fileSet.includesFile(file)).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("does not match excludes not starting with ./", () => {
|
|
37
|
+
const fileSet = new FileSet({
|
|
38
|
+
excludes: ["src/Component.tsx"],
|
|
39
|
+
includes: ["src/**/*.tsx"],
|
|
40
|
+
rootURI: URI.parse("/project"),
|
|
41
|
+
});
|
|
42
|
+
const file = "file:///project/src/Component.tsx";
|
|
43
|
+
expect(fileSet.includesFile(file)).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|