vscode-apollo 2.2.0 → 2.3.0
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/.circleci/config.yml +1 -1
- package/.github/workflows/build-prs.yml +55 -0
- package/.github/workflows/release.yml +1 -1
- package/.gitleaks.toml +10 -3
- package/.vscodeignore +0 -1
- package/CHANGELOG.md +26 -0
- package/README.md +68 -52
- package/package.json +31 -3
- package/renovate.json +5 -5
- package/sampleWorkspace/httpSchema/apollo.config.ts +2 -0
- package/sampleWorkspace/httpSchema/self-signed.crt +22 -0
- package/sampleWorkspace/httpSchema/self-signed.key +28 -0
- package/sampleWorkspace/localSchemaArray/apollo.config.json +9 -0
- package/sampleWorkspace/rover/apollo.config.yaml +2 -0
- package/sampleWorkspace/rover/supergraph.yaml +0 -0
- package/schemas/apollo.config.schema.json +184 -0
- package/src/__e2e__/mockServer.js +37 -11
- package/src/__e2e__/mocks.js +11 -7
- package/src/__e2e__/runTests.js +8 -6
- package/src/build.js +53 -1
- package/src/language-server/__e2e__/studioGraph.e2e.ts +4 -3
- package/src/language-server/config/__tests__/loadConfig.ts +35 -2
- package/src/language-server/config/cache-busting-resolver.js +65 -0
- package/src/language-server/config/cache-busting-resolver.types.ts +45 -0
- package/src/language-server/config/config.ts +136 -60
- package/src/language-server/config/loadConfig.ts +27 -6
- package/src/language-server/config/loadTsConfig.ts +74 -38
- package/src/language-server/project/base.ts +8 -8
- package/src/language-server/project/rover/DocumentSynchronization.ts +44 -22
- package/src/language-server/project/rover/project.ts +6 -0
- package/src/language-server/providers/schema/endpoint.ts +15 -8
- package/src/language-server/server.ts +8 -7
- package/src/language-server/workspace.ts +2 -5
- package/src/languageServerClient.ts +3 -1
- package/syntaxes/graphql.json +2 -2
- package/sampleWorkspace/localSchemaArray/apollo.config.js +0 -12
- package/sampleWorkspace/rover/apollo.config.js +0 -3
- /package/sampleWorkspace/localSchema/{apollo.config.js → apollo.config.ts} +0 -0
|
@@ -25,7 +25,10 @@ function ignoredFieldWarning(
|
|
|
25
25
|
Debug.warning(getMessage(ctx.path.join(".")));
|
|
26
26
|
}
|
|
27
27
|
})
|
|
28
|
-
.optional()
|
|
28
|
+
.optional()
|
|
29
|
+
.describe(
|
|
30
|
+
`This option is no longer supported, please remove it from your configuration file.`,
|
|
31
|
+
);
|
|
29
32
|
}
|
|
30
33
|
export interface Context {
|
|
31
34
|
apiKey?: string;
|
|
@@ -34,53 +37,113 @@ export interface Context {
|
|
|
34
37
|
}
|
|
35
38
|
const contextStore = new AsyncLocalStorage<Context>();
|
|
36
39
|
|
|
37
|
-
const
|
|
40
|
+
const NAME_DESCRIPTION =
|
|
41
|
+
"The name your project will be referred to by the Apollo GraphQL extension.";
|
|
38
42
|
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const studioServiceConfig = z
|
|
44
|
+
.string()
|
|
45
|
+
.describe(
|
|
46
|
+
"The name of the Apollo Studio graph to use. Alternatively pass in an object to configure a local schema.",
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const remoteServiceConfig = z
|
|
50
|
+
.object({
|
|
51
|
+
name: z.string().optional().describe(NAME_DESCRIPTION),
|
|
52
|
+
url: z
|
|
53
|
+
.string()
|
|
54
|
+
.describe(
|
|
55
|
+
"URL of a GraphQL to use for the GraphQL Schema for this project. Needs introspection enabled.",
|
|
56
|
+
),
|
|
57
|
+
headers: z
|
|
58
|
+
.record(z.string())
|
|
59
|
+
.default({})
|
|
60
|
+
.describe("Additional headers to send to the server."),
|
|
61
|
+
skipSSLValidation: z
|
|
62
|
+
.boolean()
|
|
63
|
+
.default(false)
|
|
64
|
+
.describe(
|
|
65
|
+
"Skip SSL validation. May be required for self-signed certificates.",
|
|
66
|
+
),
|
|
67
|
+
})
|
|
68
|
+
.describe("Configuration for using a local schema from a URL.");
|
|
45
69
|
export type RemoteServiceConfig = z.infer<typeof remoteServiceConfig>;
|
|
46
70
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
71
|
+
const LOCAL_SCHEMA_FILE_DESCRIPTION =
|
|
72
|
+
"Path to a local schema file to use as GraphQL Schema for this project. Can be a string or an array of strings to merge multiple partial schemas into one.";
|
|
73
|
+
const localServiceConfig = z
|
|
74
|
+
.object({
|
|
75
|
+
name: z.string().optional().describe(NAME_DESCRIPTION),
|
|
76
|
+
localSchemaFile: z
|
|
77
|
+
.union([
|
|
78
|
+
z.string().describe(LOCAL_SCHEMA_FILE_DESCRIPTION),
|
|
79
|
+
z.array(z.string()).describe(LOCAL_SCHEMA_FILE_DESCRIPTION),
|
|
80
|
+
])
|
|
81
|
+
.describe(LOCAL_SCHEMA_FILE_DESCRIPTION),
|
|
82
|
+
})
|
|
83
|
+
.describe("Configuration for using a local schema from a file.");
|
|
51
84
|
export type LocalServiceConfig = z.infer<typeof localServiceConfig>;
|
|
52
85
|
|
|
53
|
-
const clientServiceConfig = z
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
86
|
+
const clientServiceConfig = z
|
|
87
|
+
.preprocess(
|
|
88
|
+
(value) => value || contextStore.getStore()?.serviceName,
|
|
89
|
+
z.union([studioServiceConfig, remoteServiceConfig, localServiceConfig]),
|
|
90
|
+
)
|
|
91
|
+
.describe(
|
|
92
|
+
"A string to refer to a graph in Apollo Studio, or an object for a local schema.",
|
|
93
|
+
);
|
|
57
94
|
export type ClientServiceConfig = z.infer<typeof clientServiceConfig>;
|
|
58
95
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
const VALIDATION_RULES_DESCRIPTION =
|
|
97
|
+
"Additional validation rules to check for. To use this feature, please use a configuration file format that allows passing JavaScript objects.";
|
|
98
|
+
export const clientConfig = z
|
|
99
|
+
.object({
|
|
100
|
+
service: clientServiceConfig,
|
|
101
|
+
validationRules: z
|
|
102
|
+
.union([
|
|
103
|
+
z
|
|
104
|
+
.array(z.custom<ValidationRule>())
|
|
105
|
+
.describe(VALIDATION_RULES_DESCRIPTION),
|
|
106
|
+
z
|
|
107
|
+
.function()
|
|
108
|
+
.args(z.custom<ValidationRule>())
|
|
109
|
+
.returns(z.boolean())
|
|
110
|
+
.describe(VALIDATION_RULES_DESCRIPTION),
|
|
111
|
+
])
|
|
112
|
+
.optional()
|
|
113
|
+
.describe(VALIDATION_RULES_DESCRIPTION),
|
|
114
|
+
// maybe shared with rover?
|
|
115
|
+
includes: z
|
|
116
|
+
.array(z.string())
|
|
117
|
+
.optional()
|
|
118
|
+
.describe(
|
|
119
|
+
"An array of glob patterns this project should be active on. The Apollo GraphQL extension will only support IntelliSense-like features in files listed here.",
|
|
120
|
+
),
|
|
121
|
+
// maybe shared with rover?
|
|
122
|
+
excludes: z
|
|
123
|
+
.array(z.string())
|
|
124
|
+
.default(["**/node_modules", "**/__tests__"])
|
|
125
|
+
.describe(
|
|
126
|
+
"Files to exclude from this project. The Apollo GraphQL extension will not provide IntelliSense-like features in these files.",
|
|
127
|
+
),
|
|
128
|
+
// maybe shared with rover?
|
|
129
|
+
tagName: z
|
|
130
|
+
.string()
|
|
131
|
+
.default("gql")
|
|
132
|
+
.describe(
|
|
133
|
+
"The name of the template literal tag or function used in JavaScript files to declare GraphQL Documents.",
|
|
134
|
+
),
|
|
135
|
+
// removed:
|
|
136
|
+
clientOnlyDirectives: ignoredFieldWarning(),
|
|
137
|
+
clientSchemaDirectives: ignoredFieldWarning(),
|
|
138
|
+
statsWindow: ignoredFieldWarning(),
|
|
139
|
+
name: ignoredFieldWarning(),
|
|
140
|
+
referenceId: ignoredFieldWarning(),
|
|
141
|
+
version: ignoredFieldWarning(),
|
|
142
|
+
})
|
|
143
|
+
.describe("Configuration for a Client project.");
|
|
81
144
|
export type ClientConfigFormat = z.infer<typeof clientConfig>;
|
|
82
145
|
|
|
83
|
-
const roverConfig = z.object({
|
|
146
|
+
export const roverConfig = z.object({
|
|
84
147
|
bin: z
|
|
85
148
|
.preprocess(
|
|
86
149
|
(val) => val || which.sync("rover", { nothrow: true }) || undefined,
|
|
@@ -104,18 +167,19 @@ const roverConfig = z.object({
|
|
|
104
167
|
message:
|
|
105
168
|
"Rover binary is not marked as an executable. If you are using OS X or Linux, ensure to set the executable bit.",
|
|
106
169
|
},
|
|
107
|
-
)
|
|
108
|
-
|
|
170
|
+
)
|
|
171
|
+
.describe("The path to your Rover binary. If omitted, will look in PATH."),
|
|
172
|
+
profile: z.string().optional().describe("The name of the profile to use."),
|
|
109
173
|
supergraphConfig: z
|
|
110
174
|
.preprocess((value) => {
|
|
111
175
|
if (value !== undefined) return value;
|
|
112
176
|
const configPath = contextStore.getStore()?.configPath!;
|
|
113
|
-
const supergraphConfig = join(configPath, "supergraph.
|
|
177
|
+
const supergraphConfig = join(configPath, "supergraph.yaml");
|
|
114
178
|
return existsSync(supergraphConfig) ? supergraphConfig : undefined;
|
|
115
179
|
}, z.string().nullable().optional())
|
|
116
180
|
.describe(
|
|
117
|
-
"The path to your `supergraph.
|
|
118
|
-
"Defaults to a `supergraph.
|
|
181
|
+
"The path to your `supergraph.yaml` file. \n" +
|
|
182
|
+
"Defaults to a `supergraph.yaml` in the folder of your `apollo.config.js`, if there is one.",
|
|
119
183
|
),
|
|
120
184
|
extraArgs: z
|
|
121
185
|
.array(z.string())
|
|
@@ -124,24 +188,36 @@ const roverConfig = z.object({
|
|
|
124
188
|
});
|
|
125
189
|
type RoverConfigFormat = z.infer<typeof roverConfig>;
|
|
126
190
|
|
|
127
|
-
const engineConfig = z
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
z
|
|
137
|
-
|
|
138
|
-
|
|
191
|
+
export const engineConfig = z
|
|
192
|
+
.object({
|
|
193
|
+
endpoint: z
|
|
194
|
+
.string()
|
|
195
|
+
.default(
|
|
196
|
+
process.env.APOLLO_ENGINE_ENDPOINT ||
|
|
197
|
+
"https://graphql.api.apollographql.com/api/graphql",
|
|
198
|
+
)
|
|
199
|
+
.describe("The URL of the Apollo Studio API."),
|
|
200
|
+
apiKey: z
|
|
201
|
+
.preprocess(
|
|
202
|
+
(val) => val || contextStore.getStore()?.apiKey,
|
|
203
|
+
z.string().optional(),
|
|
204
|
+
)
|
|
205
|
+
.describe(
|
|
206
|
+
"The API key to use for Apollo Studio. If possible, use a `.env` file or `.env.local` file instead to store secrets like this.",
|
|
207
|
+
),
|
|
208
|
+
})
|
|
209
|
+
.describe("Network configuration for Apollo Studio API.");
|
|
139
210
|
export type EngineConfig = z.infer<typeof engineConfig>;
|
|
140
211
|
|
|
141
|
-
const baseConfig = z.object({
|
|
212
|
+
export const baseConfig = z.object({
|
|
142
213
|
engine: engineConfig.default({}),
|
|
143
|
-
client: z.unknown().optional(),
|
|
144
|
-
|
|
214
|
+
client: z.unknown().optional().describe(clientConfig.description!),
|
|
215
|
+
...ifRoverAvailable(
|
|
216
|
+
{
|
|
217
|
+
rover: z.unknown().optional(),
|
|
218
|
+
},
|
|
219
|
+
{},
|
|
220
|
+
),
|
|
145
221
|
service: ignoredFieldWarning(
|
|
146
222
|
(path) =>
|
|
147
223
|
`Service-type projects are no longer supported. Please remove the "${path}" field from your configuration file.`,
|
|
@@ -268,7 +344,7 @@ export abstract class ApolloConfig {
|
|
|
268
344
|
get configDirURI() {
|
|
269
345
|
// if the filepath has a _file_ in it, then we get its dir
|
|
270
346
|
return this.configURI &&
|
|
271
|
-
this.configURI.fsPath.match(/\.(ts|js|cjs|mjs|json)$/i)
|
|
347
|
+
this.configURI.fsPath.match(/\.(ts|js|cjs|mjs|yaml|yml|json)$/i)
|
|
272
348
|
? URI.parse(dirname(this.configURI.fsPath))
|
|
273
349
|
: this.configURI;
|
|
274
350
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cosmiconfig, defaultLoaders } from "cosmiconfig";
|
|
1
|
+
import { cosmiconfig, defaultLoaders, Loader } from "cosmiconfig";
|
|
2
2
|
import { dirname, resolve } from "path";
|
|
3
3
|
import { readFileSync, existsSync, lstatSync } from "fs";
|
|
4
4
|
import {
|
|
@@ -9,18 +9,22 @@ import {
|
|
|
9
9
|
import { getServiceFromKey } from "./utils";
|
|
10
10
|
import { URI } from "vscode-uri";
|
|
11
11
|
import { Debug } from "../utilities";
|
|
12
|
-
import {
|
|
12
|
+
import { ParseError, parse as parseJsonC } from "jsonc-parser";
|
|
13
|
+
import { loadJs, loadTs } from "./loadTsConfig";
|
|
13
14
|
|
|
14
15
|
// config settings
|
|
15
16
|
const MODULE_NAME = "apollo";
|
|
16
|
-
const
|
|
17
|
+
export const supportedConfigFileNames = [
|
|
17
18
|
"package.json",
|
|
18
19
|
`${MODULE_NAME}.config.js`,
|
|
19
20
|
`${MODULE_NAME}.config.ts`,
|
|
20
21
|
`${MODULE_NAME}.config.mjs`,
|
|
21
22
|
`${MODULE_NAME}.config.cjs`,
|
|
23
|
+
`${MODULE_NAME}.config.yaml`,
|
|
24
|
+
`${MODULE_NAME}.config.yml`,
|
|
25
|
+
`${MODULE_NAME}.config.json`,
|
|
22
26
|
];
|
|
23
|
-
const envFileNames = [".env", ".env.local"];
|
|
27
|
+
export const envFileNames = [".env", ".env.local"];
|
|
24
28
|
|
|
25
29
|
export const keyEnvVar = "APOLLO_KEY";
|
|
26
30
|
|
|
@@ -39,14 +43,31 @@ export type ConfigResult<T> = {
|
|
|
39
43
|
|
|
40
44
|
// XXX load .env files automatically
|
|
41
45
|
|
|
46
|
+
const loadJsonc: Loader = (filename, contents) => {
|
|
47
|
+
const errors: ParseError[] = [];
|
|
48
|
+
try {
|
|
49
|
+
return parseJsonC(contents, errors);
|
|
50
|
+
} finally {
|
|
51
|
+
if (errors.length) {
|
|
52
|
+
Debug.error(
|
|
53
|
+
`Error parsing JSONC file ${filename}, file might not be valid JSONC`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
42
59
|
export async function loadConfig({
|
|
43
60
|
configPath,
|
|
44
61
|
}: LoadConfigSettings): Promise<ApolloConfig | null> {
|
|
45
62
|
const explorer = cosmiconfig(MODULE_NAME, {
|
|
46
|
-
searchPlaces:
|
|
63
|
+
searchPlaces: supportedConfigFileNames,
|
|
47
64
|
loaders: {
|
|
48
65
|
...defaultLoaders,
|
|
49
|
-
|
|
66
|
+
".ts": loadTs,
|
|
67
|
+
".mjs": loadJs,
|
|
68
|
+
".cjs": loadJs,
|
|
69
|
+
".js": loadJs,
|
|
70
|
+
".json": loadJsonc,
|
|
50
71
|
},
|
|
51
72
|
});
|
|
52
73
|
|
|
@@ -1,56 +1,78 @@
|
|
|
1
|
-
import { Loader
|
|
1
|
+
import { Loader } from "cosmiconfig";
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import typescript from "typescript";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { register } from "node:module";
|
|
6
|
+
import { ImportAttributes } from "./cache-busting-resolver.types";
|
|
6
7
|
// implementation based on https://github.com/cosmiconfig/cosmiconfig/blob/a5a842547c13392ebb89a485b9e56d9f37e3cbd3/src/loaders.ts
|
|
7
8
|
// Copyright (c) 2015 David Clark licensed MIT. Full license can be found here:
|
|
8
9
|
// https://github.com/cosmiconfig/cosmiconfig/blob/a5a842547c13392ebb89a485b9e56d9f37e3cbd3/LICENSE
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
if (process.env.JEST_WORKER_ID === undefined) {
|
|
12
|
+
register(
|
|
13
|
+
pathToFileURL(require.resolve("./config/cache-busting-resolver.js")),
|
|
14
|
+
);
|
|
15
|
+
} else {
|
|
16
|
+
register(pathToFileURL(require.resolve("./cache-busting-resolver.js")));
|
|
17
|
+
}
|
|
18
|
+
|
|
11
19
|
export const loadTs: Loader = async function loadTs(filepath, content) {
|
|
12
20
|
try {
|
|
13
|
-
return await
|
|
21
|
+
return await load(filepath, content, "module", {
|
|
22
|
+
module: typescript.ModuleKind.ES2022,
|
|
23
|
+
});
|
|
14
24
|
} catch (error) {
|
|
15
25
|
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
26
|
+
error instanceof Error &&
|
|
27
|
+
// [ERROR] ReferenceError: module is not defined in ES module scope
|
|
28
|
+
error.message.includes("module is not defined")
|
|
29
|
+
) {
|
|
30
|
+
return await load(filepath, content, "commonjs", {
|
|
31
|
+
module: typescript.ModuleKind.CommonJS,
|
|
32
|
+
});
|
|
33
|
+
} else {
|
|
19
34
|
throw error;
|
|
35
|
+
}
|
|
20
36
|
}
|
|
37
|
+
};
|
|
21
38
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
39
|
+
async function load(
|
|
40
|
+
filepath: string,
|
|
41
|
+
content: string,
|
|
42
|
+
type: "module" | "commonjs",
|
|
43
|
+
compilerOptions: Partial<import("typescript").CompilerOptions>,
|
|
44
|
+
) {
|
|
26
45
|
let transpiledContent;
|
|
46
|
+
|
|
27
47
|
try {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
config.compilerOptions
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
await writeFile(compiledFilepath, transpiledContent);
|
|
42
|
-
} catch (error: any) {
|
|
43
|
-
error.message = `TypeScript Error in ${filepath}:\n${error.message}`;
|
|
44
|
-
throw error;
|
|
45
|
-
}
|
|
46
|
-
// eslint-disable-next-line @typescript-eslint/return-await
|
|
47
|
-
return await defaultLoaders[".js"](compiledFilepath, transpiledContent);
|
|
48
|
-
} finally {
|
|
49
|
-
if (existsSync(compiledFilepath)) {
|
|
50
|
-
await rm(compiledFilepath);
|
|
51
|
-
}
|
|
48
|
+
const config = resolveTsConfig(dirname(filepath)) ?? {};
|
|
49
|
+
config.compilerOptions = {
|
|
50
|
+
...config.compilerOptions,
|
|
51
|
+
|
|
52
|
+
moduleResolution: typescript.ModuleResolutionKind.Bundler,
|
|
53
|
+
target: typescript.ScriptTarget.ES2022,
|
|
54
|
+
noEmit: false,
|
|
55
|
+
...compilerOptions,
|
|
56
|
+
};
|
|
57
|
+
transpiledContent = typescript.transpileModule(content, config).outputText;
|
|
58
|
+
} catch (error: any) {
|
|
59
|
+
error.message = `TypeScript Error in ${filepath}:\n${error.message}`;
|
|
60
|
+
throw error;
|
|
52
61
|
}
|
|
53
|
-
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/return-await
|
|
63
|
+
const imported = await import(
|
|
64
|
+
filepath,
|
|
65
|
+
//@ts-ignore
|
|
66
|
+
{
|
|
67
|
+
with: {
|
|
68
|
+
as: "cachebust",
|
|
69
|
+
contents: transpiledContent,
|
|
70
|
+
format: type,
|
|
71
|
+
} satisfies ImportAttributes,
|
|
72
|
+
}
|
|
73
|
+
);
|
|
74
|
+
return imported.default;
|
|
75
|
+
}
|
|
54
76
|
|
|
55
77
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
78
|
function resolveTsConfig(directory: string): any {
|
|
@@ -68,3 +90,17 @@ function resolveTsConfig(directory: string): any {
|
|
|
68
90
|
}
|
|
69
91
|
return;
|
|
70
92
|
}
|
|
93
|
+
|
|
94
|
+
export const loadJs: Loader = async function loadJs(filepath, contents) {
|
|
95
|
+
return (
|
|
96
|
+
await import(
|
|
97
|
+
filepath, // @ts-ignore
|
|
98
|
+
{
|
|
99
|
+
with: {
|
|
100
|
+
as: "cachebust",
|
|
101
|
+
contents,
|
|
102
|
+
} satisfies ImportAttributes,
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
).default;
|
|
106
|
+
};
|
|
@@ -18,7 +18,13 @@ import { TextDocument } from "vscode-languageserver-textdocument";
|
|
|
18
18
|
|
|
19
19
|
import type { LoadingHandler } from "../loadingHandler";
|
|
20
20
|
import { FileSet } from "../fileSet";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
ApolloConfig,
|
|
23
|
+
ClientConfig,
|
|
24
|
+
envFileNames,
|
|
25
|
+
RoverConfig,
|
|
26
|
+
supportedConfigFileNames,
|
|
27
|
+
} from "../config";
|
|
22
28
|
import type { ProjectStats } from "../../messages";
|
|
23
29
|
|
|
24
30
|
export type DocumentUri = string;
|
|
@@ -67,13 +73,7 @@ export abstract class GraphQLProject {
|
|
|
67
73
|
|
|
68
74
|
this.configFileSet = new FileSet({
|
|
69
75
|
rootURI: this.rootURI,
|
|
70
|
-
includes:
|
|
71
|
-
".env",
|
|
72
|
-
"apollo.config.js",
|
|
73
|
-
"apollo.config.cjs",
|
|
74
|
-
"apollo.config.mjs",
|
|
75
|
-
"apollo.config.ts",
|
|
76
|
-
],
|
|
76
|
+
includes: supportedConfigFileNames.concat(envFileNames),
|
|
77
77
|
excludes: [],
|
|
78
78
|
});
|
|
79
79
|
|
|
@@ -99,10 +99,17 @@ export class DocumentSynchronization {
|
|
|
99
99
|
private pendingDocumentChanges = new Map<DocumentUri, TextDocument>();
|
|
100
100
|
private knownFiles = new Map<
|
|
101
101
|
DocumentUri,
|
|
102
|
-
{
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
102
|
+
| {
|
|
103
|
+
source: "editor";
|
|
104
|
+
full: TextDocument;
|
|
105
|
+
parts: ReadonlyArray<FilePart>;
|
|
106
|
+
}
|
|
107
|
+
| {
|
|
108
|
+
source: "lsp";
|
|
109
|
+
full: Pick<TextDocument, "uri">;
|
|
110
|
+
parts?: undefined;
|
|
111
|
+
diagnostics?: Diagnostic[];
|
|
112
|
+
}
|
|
106
113
|
>();
|
|
107
114
|
|
|
108
115
|
constructor(
|
|
@@ -158,7 +165,11 @@ export class DocumentSynchronization {
|
|
|
158
165
|
const newObj = Object.fromEntries(
|
|
159
166
|
newParts.map((p) => [p.fractionalIndex, p]),
|
|
160
167
|
);
|
|
161
|
-
this.knownFiles.set(document.uri, {
|
|
168
|
+
this.knownFiles.set(document.uri, {
|
|
169
|
+
source: "editor",
|
|
170
|
+
full: document,
|
|
171
|
+
parts: newParts,
|
|
172
|
+
});
|
|
162
173
|
|
|
163
174
|
for (const newPart of newParts) {
|
|
164
175
|
const previousPart = previousObj[newPart.fractionalIndex];
|
|
@@ -198,7 +209,9 @@ export class DocumentSynchronization {
|
|
|
198
209
|
|
|
199
210
|
async resendAllDocuments() {
|
|
200
211
|
for (const file of this.knownFiles.values()) {
|
|
201
|
-
|
|
212
|
+
if (file.source === "editor") {
|
|
213
|
+
await this.sendDocumentChanges(file.full, []);
|
|
214
|
+
}
|
|
202
215
|
}
|
|
203
216
|
}
|
|
204
217
|
|
|
@@ -208,7 +221,7 @@ export class DocumentSynchronization {
|
|
|
208
221
|
this.documentDidChange(params.document);
|
|
209
222
|
};
|
|
210
223
|
|
|
211
|
-
onDidCloseTextDocument: NonNullable<GraphQLProject["onDidClose"]> = (
|
|
224
|
+
onDidCloseTextDocument: NonNullable<GraphQLProject["onDidClose"]> = async (
|
|
212
225
|
params,
|
|
213
226
|
) => {
|
|
214
227
|
const known = this.knownFiles.get(params.document.uri);
|
|
@@ -216,15 +229,15 @@ export class DocumentSynchronization {
|
|
|
216
229
|
return;
|
|
217
230
|
}
|
|
218
231
|
this.knownFiles.delete(params.document.uri);
|
|
219
|
-
|
|
220
|
-
known.parts
|
|
221
|
-
this.sendNotification(DidCloseTextDocumentNotification.type, {
|
|
232
|
+
if (known.source === "editor") {
|
|
233
|
+
for (const part of known.parts) {
|
|
234
|
+
await this.sendNotification(DidCloseTextDocumentNotification.type, {
|
|
222
235
|
textDocument: {
|
|
223
236
|
uri: getUri(known.full, part),
|
|
224
237
|
},
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
228
241
|
};
|
|
229
242
|
|
|
230
243
|
async documentDidChange(document: TextDocument) {
|
|
@@ -254,7 +267,7 @@ export class DocumentSynchronization {
|
|
|
254
267
|
): Promise<T | undefined> {
|
|
255
268
|
await this.synchronizedWithDocument(positionParams.textDocument.uri);
|
|
256
269
|
const found = this.knownFiles.get(positionParams.textDocument.uri);
|
|
257
|
-
if (!found) {
|
|
270
|
+
if (!found || found.source !== "editor") {
|
|
258
271
|
return;
|
|
259
272
|
}
|
|
260
273
|
const match = findContainedSourceAndPosition(
|
|
@@ -274,11 +287,14 @@ export class DocumentSynchronization {
|
|
|
274
287
|
handlePartDiagnostics(params: PublishDiagnosticsParams) {
|
|
275
288
|
DEBUG && console.log("Received diagnostics", params);
|
|
276
289
|
const uriDetails = splitUri(params.uri);
|
|
277
|
-
if (!uriDetails) {
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
290
|
const found = this.knownFiles.get(uriDetails.uri);
|
|
281
|
-
if (!found) {
|
|
291
|
+
if (!found || found.source === "lsp") {
|
|
292
|
+
this.knownFiles.set(uriDetails.uri, {
|
|
293
|
+
source: "lsp",
|
|
294
|
+
full: { uri: uriDetails.uri },
|
|
295
|
+
diagnostics: params.diagnostics,
|
|
296
|
+
});
|
|
297
|
+
this.sendDiagnostics(params);
|
|
282
298
|
return;
|
|
283
299
|
}
|
|
284
300
|
const part = found.parts.find(
|
|
@@ -304,13 +320,19 @@ export class DocumentSynchronization {
|
|
|
304
320
|
}
|
|
305
321
|
|
|
306
322
|
get openDocuments() {
|
|
307
|
-
return [...this.knownFiles.values()]
|
|
323
|
+
return [...this.knownFiles.values()]
|
|
324
|
+
.filter((f) => f.source === "editor")
|
|
325
|
+
.map((f) => f.full);
|
|
308
326
|
}
|
|
309
327
|
|
|
310
328
|
clearAllDiagnostics() {
|
|
311
329
|
for (const file of this.knownFiles.values()) {
|
|
312
|
-
|
|
313
|
-
part.
|
|
330
|
+
if (file.source === "editor") {
|
|
331
|
+
for (const part of file.parts) {
|
|
332
|
+
part.diagnostics = [];
|
|
333
|
+
}
|
|
334
|
+
} else {
|
|
335
|
+
file.diagnostics = [];
|
|
314
336
|
}
|
|
315
337
|
this.sendDiagnostics({ uri: file.full.uri, diagnostics: [] });
|
|
316
338
|
}
|
|
@@ -332,7 +354,7 @@ export class DocumentSynchronization {
|
|
|
332
354
|
): Promise<SemanticTokens | null> {
|
|
333
355
|
await this.synchronizedWithDocument(params.textDocument.uri);
|
|
334
356
|
const found = this.knownFiles.get(params.textDocument.uri);
|
|
335
|
-
if (!found) {
|
|
357
|
+
if (!found || found.source !== "editor") {
|
|
336
358
|
return null;
|
|
337
359
|
}
|
|
338
360
|
const allParts = await Promise.all(
|
|
@@ -24,6 +24,7 @@ import {
|
|
|
24
24
|
SemanticTokensRegistrationType,
|
|
25
25
|
SemanticTokensOptions,
|
|
26
26
|
SemanticTokensRegistrationOptions,
|
|
27
|
+
DefinitionRequest,
|
|
27
28
|
} from "vscode-languageserver/node";
|
|
28
29
|
import cp from "node:child_process";
|
|
29
30
|
import { GraphQLProjectConfig } from "../base";
|
|
@@ -281,6 +282,11 @@ export class RoverProject extends GraphQLProject {
|
|
|
281
282
|
this.sendRequest(HoverRequest.type, virtualParams, token),
|
|
282
283
|
);
|
|
283
284
|
|
|
285
|
+
onDefinition: GraphQLProject["onDefinition"] = async (params, token) =>
|
|
286
|
+
this.documents.insideVirtualDocument(params, (virtualParams) =>
|
|
287
|
+
this.sendRequest(DefinitionRequest.type, virtualParams, token),
|
|
288
|
+
);
|
|
289
|
+
|
|
284
290
|
onUnhandledRequest: GraphQLProject["onUnhandledRequest"] = async (
|
|
285
291
|
type,
|
|
286
292
|
params,
|