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.
Files changed (156) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +14 -0
  3. package/.circleci/config.yml +82 -0
  4. package/.eslintrc.js +10 -0
  5. package/.gitattributes +1 -0
  6. package/.github/workflows/build-prs.yml +57 -0
  7. package/.github/workflows/release.yml +114 -0
  8. package/.gitleaks.toml +26 -0
  9. package/.nvmrc +1 -0
  10. package/.prettierrc +5 -0
  11. package/.vscode/launch.json +61 -0
  12. package/.vscode/settings.json +16 -0
  13. package/.vscode/tasks.json +18 -0
  14. package/.vscodeignore +17 -1
  15. package/CHANGELOG.md +178 -1
  16. package/LICENSE +2 -2
  17. package/README.md +9 -9
  18. package/codegen.yml +12 -0
  19. package/images/IconRun.svg +8 -0
  20. package/jest.config.ts +11 -0
  21. package/package.json +102 -22
  22. package/renovate.json +23 -0
  23. package/src/__mocks__/fs.js +3 -0
  24. package/src/__tests__/statusBar.test.ts +8 -7
  25. package/src/debug.ts +2 -5
  26. package/src/env/fetch/fetch.ts +32 -0
  27. package/src/env/fetch/global.ts +30 -0
  28. package/src/env/fetch/index.d.ts +2 -0
  29. package/src/env/fetch/index.ts +2 -0
  30. package/src/env/fetch/url.ts +9 -0
  31. package/src/env/index.ts +4 -0
  32. package/src/env/polyfills/array.ts +17 -0
  33. package/src/env/polyfills/index.ts +2 -0
  34. package/src/env/polyfills/object.ts +7 -0
  35. package/src/env/typescript-utility-types.ts +2 -0
  36. package/src/extension.ts +106 -37
  37. package/src/language-server/__tests__/diagnostics.test.ts +86 -0
  38. package/src/language-server/__tests__/document.test.ts +187 -0
  39. package/src/language-server/__tests__/fileSet.test.ts +46 -0
  40. package/src/language-server/__tests__/fixtures/starwarsSchema.ts +1917 -0
  41. package/src/language-server/config/__tests__/config.ts +128 -0
  42. package/src/language-server/config/__tests__/loadConfig.ts +508 -0
  43. package/src/language-server/config/__tests__/utils.ts +106 -0
  44. package/src/language-server/config/config.ts +219 -0
  45. package/src/language-server/config/index.ts +3 -0
  46. package/src/language-server/config/loadConfig.ts +228 -0
  47. package/src/language-server/config/utils.ts +56 -0
  48. package/src/language-server/diagnostics.ts +109 -0
  49. package/src/language-server/document.ts +277 -0
  50. package/src/language-server/engine/GraphQLDataSource.ts +124 -0
  51. package/src/language-server/engine/index.ts +105 -0
  52. package/src/language-server/engine/operations/frontendUrlRoot.ts +7 -0
  53. package/src/language-server/engine/operations/schemaTagsAndFieldStats.ts +24 -0
  54. package/src/language-server/errors/__tests__/NoMissingClientDirectives.test.ts +220 -0
  55. package/src/language-server/errors/logger.ts +58 -0
  56. package/src/language-server/errors/validation.ts +277 -0
  57. package/src/language-server/fileSet.ts +65 -0
  58. package/src/language-server/format.ts +48 -0
  59. package/src/language-server/graphqlTypes.ts +7176 -0
  60. package/src/language-server/index.ts +29 -0
  61. package/src/language-server/languageProvider.ts +798 -0
  62. package/src/language-server/loadingHandler.ts +64 -0
  63. package/src/language-server/project/base.ts +399 -0
  64. package/src/language-server/project/client.ts +602 -0
  65. package/src/language-server/project/defaultClientSchema.ts +45 -0
  66. package/src/language-server/project/service.ts +48 -0
  67. package/src/language-server/providers/schema/__tests__/file.ts +150 -0
  68. package/src/language-server/providers/schema/base.ts +15 -0
  69. package/src/language-server/providers/schema/endpoint.ts +157 -0
  70. package/src/language-server/providers/schema/engine.ts +197 -0
  71. package/src/language-server/providers/schema/file.ts +167 -0
  72. package/src/language-server/providers/schema/index.ts +75 -0
  73. package/src/language-server/server.ts +252 -0
  74. package/src/language-server/typings/codemirror.d.ts +4 -0
  75. package/src/language-server/typings/graphql.d.ts +27 -0
  76. package/src/language-server/utilities/__tests__/graphql.test.ts +411 -0
  77. package/src/language-server/utilities/__tests__/uri.ts +55 -0
  78. package/src/language-server/utilities/debouncer.ts +8 -0
  79. package/src/language-server/utilities/debug.ts +89 -0
  80. package/src/language-server/utilities/graphql.ts +432 -0
  81. package/src/language-server/utilities/index.ts +3 -0
  82. package/src/language-server/utilities/source.ts +182 -0
  83. package/src/language-server/utilities/uri.ts +19 -0
  84. package/src/language-server/workspace.ts +262 -0
  85. package/src/languageServerClient.ts +19 -12
  86. package/src/messages.ts +84 -0
  87. package/src/statusBar.ts +5 -5
  88. package/src/tools/__tests__/buildServiceDefinition.test.ts +491 -0
  89. package/src/tools/__tests__/snapshotSerializers/astSerializer.ts +19 -0
  90. package/src/tools/__tests__/snapshotSerializers/graphQLTypeSerializer.ts +14 -0
  91. package/src/tools/buildServiceDefinition.ts +241 -0
  92. package/src/tools/index.ts +6 -0
  93. package/src/tools/schema/index.ts +2 -0
  94. package/src/tools/schema/resolveObject.ts +18 -0
  95. package/src/tools/schema/resolverMap.ts +23 -0
  96. package/src/tools/utilities/graphql.ts +22 -0
  97. package/src/tools/utilities/index.ts +3 -0
  98. package/src/tools/utilities/invariant.ts +5 -0
  99. package/src/tools/utilities/predicates.ts +5 -0
  100. package/src/utils.ts +1 -16
  101. package/syntaxes/graphql.js.json +3 -3
  102. package/syntaxes/graphql.json +13 -9
  103. package/syntaxes/graphql.lua.json +51 -0
  104. package/syntaxes/graphql.rb.json +1 -1
  105. package/tsconfig.build.json +4 -0
  106. package/tsconfig.json +20 -7
  107. package/create-server-symlink.js +0 -8
  108. package/lib/debug.d.ts +0 -11
  109. package/lib/debug.d.ts.map +0 -1
  110. package/lib/debug.js +0 -48
  111. package/lib/debug.js.map +0 -1
  112. package/lib/extension.d.ts +0 -4
  113. package/lib/extension.d.ts.map +0 -1
  114. package/lib/extension.js +0 -187
  115. package/lib/extension.js.map +0 -1
  116. package/lib/languageServerClient.d.ts +0 -4
  117. package/lib/languageServerClient.d.ts.map +0 -1
  118. package/lib/languageServerClient.js +0 -57
  119. package/lib/languageServerClient.js.map +0 -1
  120. package/lib/statusBar.d.ts +0 -24
  121. package/lib/statusBar.d.ts.map +0 -1
  122. package/lib/statusBar.js +0 -46
  123. package/lib/statusBar.js.map +0 -1
  124. package/lib/testRunner/index.d.ts +0 -3
  125. package/lib/testRunner/index.d.ts.map +0 -1
  126. package/lib/testRunner/index.js +0 -49
  127. package/lib/testRunner/index.js.map +0 -1
  128. package/lib/testRunner/jest-config.d.ts +0 -14
  129. package/lib/testRunner/jest-config.d.ts.map +0 -1
  130. package/lib/testRunner/jest-config.js +0 -18
  131. package/lib/testRunner/jest-config.js.map +0 -1
  132. package/lib/testRunner/jest-vscode-environment.d.ts +0 -2
  133. package/lib/testRunner/jest-vscode-environment.d.ts.map +0 -1
  134. package/lib/testRunner/jest-vscode-environment.js +0 -19
  135. package/lib/testRunner/jest-vscode-environment.js.map +0 -1
  136. package/lib/testRunner/jest-vscode-framework-setup.d.ts +0 -1
  137. package/lib/testRunner/jest-vscode-framework-setup.d.ts.map +0 -1
  138. package/lib/testRunner/jest-vscode-framework-setup.js +0 -3
  139. package/lib/testRunner/jest-vscode-framework-setup.js.map +0 -1
  140. package/lib/testRunner/vscode-test-script.d.ts +0 -2
  141. package/lib/testRunner/vscode-test-script.d.ts.map +0 -1
  142. package/lib/testRunner/vscode-test-script.js +0 -23
  143. package/lib/testRunner/vscode-test-script.js.map +0 -1
  144. package/lib/utils.d.ts +0 -18
  145. package/lib/utils.d.ts.map +0 -1
  146. package/lib/utils.js +0 -52
  147. package/lib/utils.js.map +0 -1
  148. package/src/testRunner/README.md +0 -23
  149. package/src/testRunner/index.ts +0 -72
  150. package/src/testRunner/jest-config.ts +0 -17
  151. package/src/testRunner/jest-vscode-environment.ts +0 -25
  152. package/src/testRunner/jest-vscode-framework-setup.ts +0 -10
  153. package/src/testRunner/jest.d.ts +0 -37
  154. package/src/testRunner/vscode-test-script.ts +0 -38
  155. package/tsconfig.test.json +0 -4
  156. 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 { LanguageClient, NotificationType } from "vscode-languageclient";
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("node_modules/apollo-language-server/lib", "server.js")
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 engineDecoration = window.createTextEditorDecorationType({});
208
- let latestDecs: any[] | undefined = undefined;
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 && latestDecs) {
219
+ if (window.activeTextEditor && latestDecorations) {
212
220
  const editor = window.activeTextEditor!;
213
- const decorations: DecorationOptions[] = latestDecs
214
- .filter(
215
- d => d.document === window.activeTextEditor!.document.uri.toString()
216
- )
217
- .map(dec => {
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(dec.range.start.line).range,
235
+ range: editor.document.lineAt(decoration.range.start.line).range,
220
236
  renderOptions: {
221
237
  after: {
222
- contentText: `${dec.message}`,
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(engineDecoration, decorations);
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("apollographql/engineDecorations", (...decs) => {
234
- latestDecs = decs;
235
- updateDecorations();
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
+ });