vscode-apollo 1.19.3 → 2.0.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/.changeset/README.md +8 -0
- package/.changeset/config.json +14 -0
- package/.circleci/config.yml +91 -0
- package/.eslintrc.js +10 -0
- package/.git-blame-ignore-revs +2 -0
- package/.gitattributes +1 -0
- package/.github/workflows/release.yml +95 -0
- package/.gitleaks.toml +26 -0
- package/.nvmrc +1 -0
- package/.prettierrc +5 -0
- package/.vscode/launch.json +66 -0
- package/.vscode/settings.json +16 -0
- package/.vscode/tasks.json +60 -0
- package/.vscodeignore +28 -1
- package/CHANGELOG.md +250 -1
- package/CODEOWNERS +4 -0
- package/LICENSE +2 -2
- package/README.md +104 -55
- package/codegen.yml +12 -0
- package/graphql.configuration.json +5 -1
- package/images/IconRun.svg +8 -0
- package/images/marketplace/apollo-wordmark.png +0 -0
- package/jest.config.ts +21 -0
- package/jest.e2e.config.js +17 -0
- package/package.json +102 -23
- package/renovate.json +30 -0
- package/sampleWorkspace/clientSchema/apollo.config.cjs +10 -0
- package/sampleWorkspace/clientSchema/src/clientSchema.js +16 -0
- package/sampleWorkspace/clientSchema/src/test.js +18 -0
- package/sampleWorkspace/fixtures/starwarsSchema.graphql +299 -0
- package/sampleWorkspace/httpSchema/apollo.config.ts +8 -0
- package/sampleWorkspace/httpSchema/src/test.js +9 -0
- package/sampleWorkspace/localSchema/apollo.config.js +8 -0
- package/sampleWorkspace/localSchema/src/test.js +8 -0
- package/sampleWorkspace/localSchemaArray/apollo.config.js +12 -0
- package/sampleWorkspace/localSchemaArray/planets.graphql +20 -0
- package/sampleWorkspace/localSchemaArray/src/test.js +12 -0
- package/sampleWorkspace/sampleWorkspace.code-workspace +20 -0
- package/sampleWorkspace/spotifyGraph/apollo.config.mjs +5 -0
- package/sampleWorkspace/spotifyGraph/src/test.js +11 -0
- package/src/__e2e__/mockServer.js +117 -0
- package/src/__e2e__/mocks.js +13094 -0
- package/src/__e2e__/run.js +23 -0
- package/src/__e2e__/runTests.js +44 -0
- package/src/__e2e__/setup.js +1 -0
- package/src/__e2e__/vscode-environment.js +16 -0
- package/src/__e2e__/vscode.js +1 -0
- package/src/__mocks__/fs.js +3 -0
- package/src/__tests__/statusBar.test.ts +8 -7
- package/src/build.js +57 -0
- package/src/debug.ts +2 -5
- package/src/env/index.ts +1 -0
- package/src/env/typescript-utility-types.ts +2 -0
- package/src/extension.ts +265 -170
- package/src/language-server/__e2e__/clientSchema.e2e.ts +147 -0
- package/src/language-server/__e2e__/httpSchema.e2e.ts +21 -0
- package/src/language-server/__e2e__/localSchema.e2e.ts +25 -0
- package/src/language-server/__e2e__/localSchemaArray.e2e.ts +31 -0
- package/src/language-server/__e2e__/studioGraph.e2e.ts +65 -0
- package/src/language-server/__e2e__/utils.ts +151 -0
- 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 +54 -0
- package/src/language-server/config/__tests__/loadConfig.ts +384 -0
- package/src/language-server/config/__tests__/utils.ts +99 -0
- package/src/language-server/config/config.ts +284 -0
- package/src/language-server/config/index.ts +3 -0
- package/src/language-server/config/loadConfig.ts +101 -0
- package/src/language-server/config/utils.ts +45 -0
- package/src/language-server/diagnostics.ts +118 -0
- package/src/language-server/document.ts +277 -0
- package/src/language-server/engine/index.ts +123 -0
- package/src/language-server/engine/operations/frontendUrlRoot.ts +15 -0
- package/src/language-server/engine/operations/schemaTagsAndFieldStats.ts +32 -0
- package/src/language-server/errors/__tests__/NoMissingClientDirectives.test.ts +225 -0
- package/src/language-server/errors/logger.ts +58 -0
- package/src/language-server/errors/validation.ts +274 -0
- package/src/language-server/fileSet.ts +63 -0
- package/src/language-server/format.ts +48 -0
- package/src/language-server/graphqlTypes.ts +16741 -0
- package/src/language-server/index.ts +28 -0
- package/src/language-server/languageProvider.ts +795 -0
- package/src/language-server/loadingHandler.ts +47 -0
- package/src/language-server/project/base.ts +406 -0
- package/src/language-server/project/client.ts +568 -0
- package/src/language-server/project/defaultClientSchema.ts +70 -0
- package/src/language-server/providers/schema/__tests__/file.ts +191 -0
- package/src/language-server/providers/schema/base.ts +15 -0
- package/src/language-server/providers/schema/endpoint.ts +138 -0
- package/src/language-server/providers/schema/engine.ts +204 -0
- package/src/language-server/providers/schema/file.ts +176 -0
- package/src/language-server/providers/schema/index.ts +59 -0
- package/src/language-server/server.ts +274 -0
- package/src/language-server/typings/graphql.d.ts +27 -0
- package/src/language-server/utilities/__tests__/graphql.test.ts +399 -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 +90 -0
- package/src/language-server/utilities/graphql.ts +433 -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 +254 -0
- package/src/languageServerClient.ts +22 -15
- package/src/messages.ts +75 -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 +7 -21
- package/syntaxes/graphql.dart.json +2 -4
- package/syntaxes/graphql.ex.json +1 -4
- 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 +11 -0
- package/tsconfig.json +22 -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
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// FileSchemaProvider (FileProvider (SDL || IntrospectionResult) => schema)
|
|
2
|
+
import {
|
|
3
|
+
GraphQLSchema,
|
|
4
|
+
buildClientSchema,
|
|
5
|
+
Source,
|
|
6
|
+
printSchema,
|
|
7
|
+
parse,
|
|
8
|
+
buildASTSchema,
|
|
9
|
+
} from "graphql";
|
|
10
|
+
import { mergeTypeDefs } from "@graphql-tools/merge";
|
|
11
|
+
import { existsSync, readFileSync } from "fs";
|
|
12
|
+
import { extname, resolve, isAbsolute } from "path";
|
|
13
|
+
import { GraphQLSchemaProvider, SchemaChangeUnsubscribeHandler } from "./base";
|
|
14
|
+
import { NotificationHandler } from "vscode-languageserver/node";
|
|
15
|
+
import { Debug } from "../../utilities";
|
|
16
|
+
import { buildSubgraphSchema } from "@apollo/subgraph";
|
|
17
|
+
import { URI } from "vscode-uri";
|
|
18
|
+
// import federationDirectives from "@apollo/federation/src/directives";
|
|
19
|
+
|
|
20
|
+
export interface FileSchemaProviderConfig {
|
|
21
|
+
path?: string;
|
|
22
|
+
paths?: string[];
|
|
23
|
+
}
|
|
24
|
+
// XXX file subscription
|
|
25
|
+
export class FileSchemaProvider implements GraphQLSchemaProvider {
|
|
26
|
+
private schema?: GraphQLSchema;
|
|
27
|
+
private federatedServiceSDL?: string;
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
private config: FileSchemaProviderConfig,
|
|
31
|
+
private configDir: URI | undefined,
|
|
32
|
+
) {}
|
|
33
|
+
|
|
34
|
+
async resolveSchema() {
|
|
35
|
+
if (this.schema) return this.schema;
|
|
36
|
+
const { path, paths } = this.config;
|
|
37
|
+
|
|
38
|
+
// load each path and get sdl string from each, if a list, concatenate them all
|
|
39
|
+
const documents = path
|
|
40
|
+
? [this.loadFileAndGetDocument(path)]
|
|
41
|
+
: paths
|
|
42
|
+
? paths.map(this.loadFileAndGetDocument, this)
|
|
43
|
+
: undefined;
|
|
44
|
+
|
|
45
|
+
if (!documents)
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Schema could not be loaded for [${
|
|
48
|
+
path ? path : paths ? paths.join(", ") : "undefined"
|
|
49
|
+
}]`,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
this.schema = buildASTSchema(mergeTypeDefs(documents));
|
|
53
|
+
|
|
54
|
+
if (!this.schema) throw new Error(`Schema could not be loaded for ${path}`);
|
|
55
|
+
return this.schema;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// load a graphql file or introspection result and return the GraphQL DocumentNode
|
|
59
|
+
// this is the mechanism for loading a single file's DocumentNode
|
|
60
|
+
loadFileAndGetDocument(path: string) {
|
|
61
|
+
let result;
|
|
62
|
+
try {
|
|
63
|
+
result = this.readFileSync(path);
|
|
64
|
+
} catch (err: any) {
|
|
65
|
+
throw new Error(`Unable to read file ${path}. ${err.message}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const ext = extname(path);
|
|
69
|
+
|
|
70
|
+
// an actual introspection query result, convert to DocumentNode
|
|
71
|
+
if (ext === ".json") {
|
|
72
|
+
const parsed = JSON.parse(result);
|
|
73
|
+
const __schema = parsed.data
|
|
74
|
+
? parsed.data.__schema
|
|
75
|
+
: parsed.__schema
|
|
76
|
+
? parsed.__schema
|
|
77
|
+
: parsed;
|
|
78
|
+
|
|
79
|
+
const schema = buildClientSchema({ __schema });
|
|
80
|
+
return parse(printSchema(schema));
|
|
81
|
+
} else if (ext === ".graphql" || ext === ".graphqls" || ext === ".gql") {
|
|
82
|
+
const uri = URI.file(resolve(path)).toString();
|
|
83
|
+
return parse(new Source(result, uri));
|
|
84
|
+
}
|
|
85
|
+
throw new Error(
|
|
86
|
+
"File Type not supported for schema loading. Must be a .json, .graphql, .gql, or .graphqls file",
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
onSchemaChange(
|
|
91
|
+
_handler: NotificationHandler<GraphQLSchema>,
|
|
92
|
+
): SchemaChangeUnsubscribeHandler {
|
|
93
|
+
throw new Error("File watching not implemented yet");
|
|
94
|
+
return () => {};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Load SDL from files. This is only used with federated services,
|
|
98
|
+
// since they need full SDL and not the printout of GraphQLSchema
|
|
99
|
+
async resolveFederatedServiceSDL() {
|
|
100
|
+
if (this.federatedServiceSDL) return this.federatedServiceSDL;
|
|
101
|
+
|
|
102
|
+
const { path, paths } = this.config;
|
|
103
|
+
|
|
104
|
+
// load each path and get sdl string from each, if a list, concatenate them all
|
|
105
|
+
const SDLs = path
|
|
106
|
+
? [this.loadFileAndGetSDL(path)]
|
|
107
|
+
: paths
|
|
108
|
+
? paths.map(this.loadFileAndGetSDL, this)
|
|
109
|
+
: undefined;
|
|
110
|
+
|
|
111
|
+
if (!SDLs || SDLs.filter((s) => !Boolean(s)).length > 0)
|
|
112
|
+
return Debug.error(
|
|
113
|
+
`SDL could not be loaded for one of more files: [${
|
|
114
|
+
path ? path : paths ? paths.join(", ") : "undefined"
|
|
115
|
+
}]`,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const federatedSchema = buildSubgraphSchema(
|
|
119
|
+
SDLs.map((sdl) => ({ typeDefs: parse(sdl as string) })),
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
// call the `Query._service` resolver to get the actual printed sdl
|
|
123
|
+
const queryType = federatedSchema.getQueryType();
|
|
124
|
+
if (!queryType)
|
|
125
|
+
return Debug.error("No query type found for federated schema");
|
|
126
|
+
const serviceField = queryType.getFields()["_service"];
|
|
127
|
+
const serviceResults = serviceField?.resolve?.(
|
|
128
|
+
null,
|
|
129
|
+
{},
|
|
130
|
+
null,
|
|
131
|
+
{} as any,
|
|
132
|
+
) as { sdl?: string };
|
|
133
|
+
|
|
134
|
+
if (!serviceResults || !serviceResults.sdl)
|
|
135
|
+
return Debug.error(
|
|
136
|
+
"No SDL resolver or result from federated schema after building",
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
this.federatedServiceSDL = serviceResults.sdl;
|
|
140
|
+
return this.federatedServiceSDL;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// this is the mechanism for loading a single file's SDL
|
|
144
|
+
loadFileAndGetSDL(path: string) {
|
|
145
|
+
let result;
|
|
146
|
+
try {
|
|
147
|
+
result = this.readFileSync(path);
|
|
148
|
+
} catch (err: any) {
|
|
149
|
+
return Debug.error(`Unable to read file ${path}. ${err.message}`);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const ext = extname(path);
|
|
153
|
+
|
|
154
|
+
// this file should already be in sdl format
|
|
155
|
+
if (ext === ".graphql" || ext === ".graphqls" || ext === ".gql") {
|
|
156
|
+
return result as string;
|
|
157
|
+
} else {
|
|
158
|
+
return Debug.error(
|
|
159
|
+
"When using localSchemaFile to check or push a federated service, you can only use .graphql, .gql, and .graphqls files",
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private readFileSync(path: string) {
|
|
165
|
+
let finalPath = path;
|
|
166
|
+
if (!isAbsolute(finalPath) && this.configDir) {
|
|
167
|
+
const resolvedPath = resolve(this.configDir?.fsPath, path);
|
|
168
|
+
if (existsSync(resolvedPath)) {
|
|
169
|
+
finalPath = resolvedPath;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return readFileSync(finalPath, {
|
|
173
|
+
encoding: "utf-8",
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
GraphQLSchemaProvider,
|
|
3
|
+
SchemaChangeUnsubscribeHandler,
|
|
4
|
+
SchemaResolveConfig,
|
|
5
|
+
} from "./base";
|
|
6
|
+
import {
|
|
7
|
+
ApolloConfig,
|
|
8
|
+
isClientConfig,
|
|
9
|
+
isLocalServiceConfig,
|
|
10
|
+
ClientConfig,
|
|
11
|
+
} from "../../config";
|
|
12
|
+
|
|
13
|
+
import { EndpointSchemaProvider } from "./endpoint";
|
|
14
|
+
import { EngineSchemaProvider } from "./engine";
|
|
15
|
+
import { FileSchemaProvider } from "./file";
|
|
16
|
+
import { ClientIdentity } from "../../engine";
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
GraphQLSchemaProvider,
|
|
20
|
+
SchemaChangeUnsubscribeHandler,
|
|
21
|
+
SchemaResolveConfig,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export function schemaProviderFromConfig(
|
|
25
|
+
config: ApolloConfig,
|
|
26
|
+
clientIdentity: ClientIdentity, // engine provider needs this
|
|
27
|
+
): GraphQLSchemaProvider {
|
|
28
|
+
if (isClientConfig(config)) {
|
|
29
|
+
if (typeof config.client.service === "string") {
|
|
30
|
+
return new EngineSchemaProvider(config, clientIdentity);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (config.client.service) {
|
|
34
|
+
if (isLocalServiceConfig(config.client.service)) {
|
|
35
|
+
const isListOfSchemaFiles = Array.isArray(
|
|
36
|
+
config.client.service.localSchemaFile,
|
|
37
|
+
);
|
|
38
|
+
return new FileSchemaProvider(
|
|
39
|
+
isListOfSchemaFiles
|
|
40
|
+
? { paths: config.client.service.localSchemaFile as string[] }
|
|
41
|
+
: {
|
|
42
|
+
path: config.client.service.localSchemaFile as string,
|
|
43
|
+
},
|
|
44
|
+
config.configDirURI,
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return new EndpointSchemaProvider(config.client.service);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (config.graph && config.engine) {
|
|
53
|
+
return new EngineSchemaProvider(config as ClientConfig, clientIdentity);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
throw new Error(
|
|
57
|
+
"No schema provider was created, because the project type was unable to be resolved from your config. Please add either a client or service config. For more information, please refer to https://go.apollo.dev/t/config",
|
|
58
|
+
);
|
|
59
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import "../env";
|
|
2
|
+
import {
|
|
3
|
+
createConnection,
|
|
4
|
+
ProposedFeatures,
|
|
5
|
+
TextDocuments,
|
|
6
|
+
FileChangeType,
|
|
7
|
+
ServerCapabilities,
|
|
8
|
+
TextDocumentSyncKind,
|
|
9
|
+
} from "vscode-languageserver/node";
|
|
10
|
+
import { TextDocument } from "vscode-languageserver-textdocument";
|
|
11
|
+
import type { QuickPickItem } from "vscode";
|
|
12
|
+
import { GraphQLWorkspace } from "./workspace";
|
|
13
|
+
import { GraphQLLanguageProvider } from "./languageProvider";
|
|
14
|
+
import { LanguageServerLoadingHandler } from "./loadingHandler";
|
|
15
|
+
import { debounceHandler, Debug } from "./utilities";
|
|
16
|
+
import { URI } from "vscode-uri";
|
|
17
|
+
import {
|
|
18
|
+
LanguageServerNotifications as Notifications,
|
|
19
|
+
LanguageServerCommands as Commands,
|
|
20
|
+
LanguageServerRequests as Requests,
|
|
21
|
+
} from "../messages";
|
|
22
|
+
import { isValidationError } from "zod-validation-error";
|
|
23
|
+
|
|
24
|
+
const connection = createConnection(ProposedFeatures.all);
|
|
25
|
+
|
|
26
|
+
Debug.SetConnection(connection);
|
|
27
|
+
const { sendNotification: originalSendNotification } = connection;
|
|
28
|
+
connection.sendNotification = async (...args: [any, ...any[]]) => {
|
|
29
|
+
await whenConnectionInitialized;
|
|
30
|
+
connection.sendNotification = originalSendNotification;
|
|
31
|
+
connection.sendNotification(...args);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
let hasWorkspaceFolderCapability = false;
|
|
35
|
+
|
|
36
|
+
// Awaitable promise for sending messages before the connection is initialized
|
|
37
|
+
let initializeConnection: () => void;
|
|
38
|
+
const whenConnectionInitialized: Promise<void> = new Promise(
|
|
39
|
+
(resolve) => (initializeConnection = resolve),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const workspace = new GraphQLWorkspace(
|
|
43
|
+
new LanguageServerLoadingHandler(connection),
|
|
44
|
+
{
|
|
45
|
+
clientIdentity: {
|
|
46
|
+
name: process.env["APOLLO_CLIENT_NAME"] || "Apollo Language Server",
|
|
47
|
+
version:
|
|
48
|
+
process.env["APOLLO_CLIENT_VERSION"] ||
|
|
49
|
+
"146d29c0-912c-46d3-b686-920e52586be6",
|
|
50
|
+
referenceID:
|
|
51
|
+
process.env["APOLLO_CLIENT_REFERENCE_ID"] ||
|
|
52
|
+
require("../../package.json").version,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
workspace.onDiagnostics((params) => {
|
|
58
|
+
connection.sendDiagnostics(params);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
workspace.onDecorations((params) => {
|
|
62
|
+
connection.sendNotification(Notifications.EngineDecorations, {
|
|
63
|
+
decorations: params,
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
workspace.onSchemaTags((params) => {
|
|
68
|
+
connection.sendNotification(Notifications.TagsLoaded, JSON.stringify(params));
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
workspace.onConfigFilesFound(async (params) => {
|
|
72
|
+
connection.sendNotification(
|
|
73
|
+
Notifications.ConfigFilesFound,
|
|
74
|
+
JSON.stringify(params, (_key, value) =>
|
|
75
|
+
!value
|
|
76
|
+
? value
|
|
77
|
+
: value instanceof Error || isValidationError(value)
|
|
78
|
+
? { message: value.message, stack: value.stack }
|
|
79
|
+
: value,
|
|
80
|
+
),
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
connection.onInitialize(async ({ capabilities, workspaceFolders }) => {
|
|
85
|
+
hasWorkspaceFolderCapability = !!(
|
|
86
|
+
capabilities.workspace && capabilities.workspace.workspaceFolders
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
if (workspaceFolders) {
|
|
90
|
+
// We wait until all projects are added, because after `initialize` returns we can get additional requests
|
|
91
|
+
// like `textDocument/codeLens`, and that way these can await `GraphQLProject#whenReady` to make sure
|
|
92
|
+
// we provide them eventually.
|
|
93
|
+
await Promise.all(
|
|
94
|
+
workspaceFolders.map((folder) => workspace.addProjectsInFolder(folder)),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
capabilities: {
|
|
100
|
+
hoverProvider: true,
|
|
101
|
+
completionProvider: {
|
|
102
|
+
resolveProvider: false,
|
|
103
|
+
triggerCharacters: ["...", "@"],
|
|
104
|
+
},
|
|
105
|
+
definitionProvider: true,
|
|
106
|
+
referencesProvider: true,
|
|
107
|
+
documentSymbolProvider: true,
|
|
108
|
+
workspaceSymbolProvider: true,
|
|
109
|
+
codeLensProvider: {
|
|
110
|
+
resolveProvider: false,
|
|
111
|
+
},
|
|
112
|
+
codeActionProvider: true,
|
|
113
|
+
executeCommandProvider: {
|
|
114
|
+
commands: [],
|
|
115
|
+
},
|
|
116
|
+
textDocumentSync: TextDocumentSyncKind.Full,
|
|
117
|
+
} as ServerCapabilities,
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
connection.onInitialized(async () => {
|
|
122
|
+
initializeConnection();
|
|
123
|
+
if (hasWorkspaceFolderCapability) {
|
|
124
|
+
connection.workspace.onDidChangeWorkspaceFolders(async (event) => {
|
|
125
|
+
await Promise.all([
|
|
126
|
+
...event.removed.map((folder) =>
|
|
127
|
+
workspace.removeProjectsInFolder(folder),
|
|
128
|
+
),
|
|
129
|
+
...event.added.map((folder) => workspace.addProjectsInFolder(folder)),
|
|
130
|
+
]);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const documents = new TextDocuments(TextDocument);
|
|
136
|
+
|
|
137
|
+
// Make the text document manager listen on the connection
|
|
138
|
+
// for open, change and close text document events
|
|
139
|
+
documents.listen(connection);
|
|
140
|
+
|
|
141
|
+
function isFile(uri: string) {
|
|
142
|
+
return URI.parse(uri).scheme === "file";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
documents.onDidChangeContent(
|
|
146
|
+
debounceHandler((params) => {
|
|
147
|
+
const project = workspace.projectForFile(params.document.uri);
|
|
148
|
+
if (!project) return;
|
|
149
|
+
|
|
150
|
+
// Only watch changes to files
|
|
151
|
+
if (!isFile(params.document.uri)) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
project.documentDidChange(params.document);
|
|
156
|
+
}),
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
connection.onDidChangeWatchedFiles((params) => {
|
|
160
|
+
for (const { uri, type } of params.changes) {
|
|
161
|
+
if (
|
|
162
|
+
uri.endsWith("apollo.config.js") ||
|
|
163
|
+
uri.endsWith("apollo.config.cjs") ||
|
|
164
|
+
uri.endsWith("apollo.config.mjs") ||
|
|
165
|
+
uri.endsWith("apollo.config.ts") ||
|
|
166
|
+
uri.endsWith(".env")
|
|
167
|
+
) {
|
|
168
|
+
workspace.reloadProjectForConfig(uri);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Don't respond to changes in files that are currently open,
|
|
172
|
+
// because we'll get content change notifications instead
|
|
173
|
+
if (type === FileChangeType.Changed) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Only watch changes to files
|
|
178
|
+
if (!isFile(uri)) {
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const project = workspace.projectForFile(uri);
|
|
183
|
+
if (!project) continue;
|
|
184
|
+
|
|
185
|
+
switch (type) {
|
|
186
|
+
case FileChangeType.Created:
|
|
187
|
+
project.fileDidChange(uri);
|
|
188
|
+
break;
|
|
189
|
+
case FileChangeType.Deleted:
|
|
190
|
+
project.fileWasDeleted(uri);
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
const languageProvider = new GraphQLLanguageProvider(workspace);
|
|
197
|
+
|
|
198
|
+
connection.onHover((params, token) =>
|
|
199
|
+
languageProvider.provideHover(
|
|
200
|
+
params.textDocument.uri,
|
|
201
|
+
params.position,
|
|
202
|
+
token,
|
|
203
|
+
),
|
|
204
|
+
);
|
|
205
|
+
|
|
206
|
+
connection.onDefinition((params, token) =>
|
|
207
|
+
languageProvider.provideDefinition(
|
|
208
|
+
params.textDocument.uri,
|
|
209
|
+
params.position,
|
|
210
|
+
token,
|
|
211
|
+
),
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
connection.onReferences((params, token) =>
|
|
215
|
+
languageProvider.provideReferences(
|
|
216
|
+
params.textDocument.uri,
|
|
217
|
+
params.position,
|
|
218
|
+
params.context,
|
|
219
|
+
token,
|
|
220
|
+
),
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
connection.onDocumentSymbol((params, token) =>
|
|
224
|
+
languageProvider.provideDocumentSymbol(params.textDocument.uri, token),
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
connection.onWorkspaceSymbol((params, token) =>
|
|
228
|
+
languageProvider.provideWorkspaceSymbol(params.query, token),
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
connection.onCompletion(
|
|
232
|
+
debounceHandler((params, token) =>
|
|
233
|
+
languageProvider.provideCompletionItems(
|
|
234
|
+
params.textDocument.uri,
|
|
235
|
+
params.position,
|
|
236
|
+
token,
|
|
237
|
+
),
|
|
238
|
+
),
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
connection.onCodeLens(
|
|
242
|
+
debounceHandler((params, token) =>
|
|
243
|
+
languageProvider.provideCodeLenses(params.textDocument.uri, token),
|
|
244
|
+
),
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
connection.onCodeAction(
|
|
248
|
+
debounceHandler((params, token) =>
|
|
249
|
+
languageProvider.provideCodeAction(
|
|
250
|
+
params.textDocument.uri,
|
|
251
|
+
params.range,
|
|
252
|
+
token,
|
|
253
|
+
),
|
|
254
|
+
),
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
connection.onNotification(Commands.ReloadService, () =>
|
|
258
|
+
workspace.reloadService(),
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
connection.onNotification(Commands.TagSelected, (selection: QuickPickItem) =>
|
|
262
|
+
workspace.updateSchemaTag(selection),
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
connection.onNotification(Commands.GetStats, async ({ uri }) => {
|
|
266
|
+
const status = await languageProvider.provideStats(uri);
|
|
267
|
+
connection.sendNotification(Notifications.StatsLoaded, status);
|
|
268
|
+
});
|
|
269
|
+
connection.onRequest(Requests.FileStats, async ({ uri }) => {
|
|
270
|
+
return languageProvider.provideStats(uri);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Listen on the connection
|
|
274
|
+
connection.listen();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ASTNode,
|
|
3
|
+
TypeSystemDefinitionNode,
|
|
4
|
+
TypeSystemExtensionNode,
|
|
5
|
+
FragmentDefinitionNode,
|
|
6
|
+
OperationDefinitionNode,
|
|
7
|
+
} from "graphql";
|
|
8
|
+
|
|
9
|
+
// FIXME: We should add proper type guards for these predicate functions
|
|
10
|
+
// to `@types/graphql`.
|
|
11
|
+
declare module "graphql/language/predicates" {
|
|
12
|
+
function isExecutableDefinitionNode(
|
|
13
|
+
node: ASTNode,
|
|
14
|
+
): node is OperationDefinitionNode | FragmentDefinitionNode;
|
|
15
|
+
function isTypeSystemDefinitionNode(
|
|
16
|
+
node: ASTNode,
|
|
17
|
+
): node is TypeSystemDefinitionNode;
|
|
18
|
+
function isTypeSystemExtensionNode(
|
|
19
|
+
node: ASTNode,
|
|
20
|
+
): node is TypeSystemExtensionNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
declare module "graphql/validation/validate" {
|
|
24
|
+
interface ValidationContext {
|
|
25
|
+
_fragments: { [fragmentName: string]: FragmentDefinitionNode };
|
|
26
|
+
}
|
|
27
|
+
}
|