vscode-apollo 1.20.0 → 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/.circleci/config.yml +27 -18
- package/.git-blame-ignore-revs +2 -0
- package/.nvmrc +1 -1
- package/.vscode/launch.json +9 -4
- package/.vscode/tasks.json +58 -16
- package/.vscodeignore +12 -1
- package/CHANGELOG.md +78 -0
- package/CODEOWNERS +4 -0
- package/README.md +97 -48
- package/graphql.configuration.json +5 -1
- package/images/marketplace/apollo-wordmark.png +0 -0
- package/jest.config.ts +14 -4
- package/jest.e2e.config.js +17 -0
- package/package.json +67 -68
- package/renovate.json +7 -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/build.js +57 -0
- package/src/env/index.ts +0 -3
- package/src/extension.ts +251 -225
- 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 +8 -8
- package/src/language-server/__tests__/fileSet.test.ts +1 -1
- package/src/language-server/__tests__/fixtures/starwarsSchema.ts +2 -2
- package/src/language-server/config/__tests__/config.ts +22 -96
- package/src/language-server/config/__tests__/loadConfig.ts +97 -221
- package/src/language-server/config/__tests__/utils.ts +22 -29
- package/src/language-server/config/config.ts +221 -156
- package/src/language-server/config/loadConfig.ts +26 -153
- package/src/language-server/config/utils.ts +5 -16
- package/src/language-server/diagnostics.ts +17 -8
- package/src/language-server/document.ts +16 -16
- package/src/language-server/engine/index.ts +57 -39
- package/src/language-server/engine/operations/frontendUrlRoot.ts +9 -1
- package/src/language-server/engine/operations/schemaTagsAndFieldStats.ts +9 -1
- package/src/language-server/errors/__tests__/NoMissingClientDirectives.test.ts +10 -5
- package/src/language-server/errors/logger.ts +1 -1
- package/src/language-server/errors/validation.ts +20 -23
- package/src/language-server/fileSet.ts +10 -12
- package/src/language-server/format.ts +1 -1
- package/src/language-server/graphqlTypes.ts +13020 -3455
- package/src/language-server/index.ts +0 -1
- package/src/language-server/languageProvider.ts +29 -32
- package/src/language-server/loadingHandler.ts +10 -27
- package/src/language-server/project/base.ts +32 -25
- package/src/language-server/project/client.ts +80 -114
- package/src/language-server/project/defaultClientSchema.ts +29 -4
- package/src/language-server/providers/schema/__tests__/file.ts +60 -19
- package/src/language-server/providers/schema/base.ts +2 -2
- package/src/language-server/providers/schema/endpoint.ts +15 -34
- package/src/language-server/providers/schema/engine.ts +25 -18
- package/src/language-server/providers/schema/file.ts +41 -32
- package/src/language-server/providers/schema/index.ts +5 -21
- package/src/language-server/server.ts +72 -50
- package/src/language-server/typings/graphql.d.ts +3 -3
- package/src/language-server/utilities/__tests__/graphql.test.ts +42 -54
- package/src/language-server/utilities/debouncer.ts +1 -1
- package/src/language-server/utilities/debug.ts +6 -5
- package/src/language-server/utilities/graphql.ts +17 -16
- package/src/language-server/utilities/source.ts +16 -16
- package/src/language-server/utilities/uri.ts +2 -2
- package/src/language-server/workspace.ts +29 -37
- package/src/languageServerClient.ts +4 -4
- package/src/messages.ts +38 -47
- package/src/tools/__tests__/buildServiceDefinition.test.ts +2 -2
- package/src/tools/buildServiceDefinition.ts +11 -11
- package/src/tools/schema/resolveObject.ts +1 -1
- package/src/tools/utilities/predicates.ts +1 -1
- package/src/utils.ts +7 -6
- package/syntaxes/graphql.dart.json +2 -4
- package/syntaxes/graphql.ex.json +1 -4
- package/tsconfig.build.json +8 -1
- package/tsconfig.json +5 -3
- package/src/env/fetch/fetch.ts +0 -32
- package/src/env/fetch/global.ts +0 -30
- package/src/env/fetch/index.d.ts +0 -2
- package/src/env/fetch/index.ts +0 -2
- package/src/env/fetch/url.ts +0 -9
- package/src/env/polyfills/array.ts +0 -17
- package/src/env/polyfills/index.ts +0 -2
- package/src/env/polyfills/object.ts +0 -7
- package/src/language-server/engine/GraphQLDataSource.ts +0 -124
- package/src/language-server/project/service.ts +0 -48
- package/src/language-server/typings/codemirror.d.ts +0 -4
|
@@ -1,165 +1,240 @@
|
|
|
1
1
|
import { dirname } from "path";
|
|
2
|
-
import
|
|
3
|
-
import { ClientID, ServiceID, ServiceSpecifier } from "../engine";
|
|
4
|
-
import URI from "vscode-uri";
|
|
5
|
-
import { WithRequired } from "../../env";
|
|
2
|
+
import { URI } from "vscode-uri";
|
|
6
3
|
import { getGraphIdFromConfig, parseServiceSpecifier } from "./utils";
|
|
4
|
+
import { Debug } from "../utilities";
|
|
5
|
+
import z, { ZodError } from "zod";
|
|
7
6
|
import { ValidationRule } from "graphql/validation/ValidationContext";
|
|
7
|
+
import { Slot } from "@wry/context";
|
|
8
|
+
import { fromZodError } from "zod-validation-error";
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
const ROVER_AVAILABLE = (process.env.APOLLO_FEATURE_FLAGS || "")
|
|
11
|
+
.split(",")
|
|
12
|
+
.includes("rover");
|
|
13
|
+
|
|
14
|
+
function ignoredFieldWarning(
|
|
15
|
+
getMessage = (path: string) =>
|
|
16
|
+
`The option ${path} is no longer supported, please remove it from your configuration file.`,
|
|
17
|
+
) {
|
|
18
|
+
return z
|
|
19
|
+
.custom<unknown>(() => true)
|
|
20
|
+
.superRefine((val, ctx) => {
|
|
21
|
+
if (val) {
|
|
22
|
+
Debug.warning(getMessage(ctx.path.join(".")));
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
.optional();
|
|
26
|
+
}
|
|
27
|
+
export interface Context {
|
|
28
|
+
apiKey?: string;
|
|
29
|
+
serviceName?: string;
|
|
12
30
|
}
|
|
31
|
+
const context = new Slot<Context>();
|
|
13
32
|
|
|
14
|
-
|
|
15
|
-
to: -0,
|
|
16
|
-
from: -86400, // one day
|
|
17
|
-
};
|
|
33
|
+
const studioServiceConfig = z.string();
|
|
18
34
|
|
|
19
|
-
|
|
35
|
+
const remoteServiceConfig = z.object({
|
|
36
|
+
name: z.string().optional(),
|
|
37
|
+
url: z.string(),
|
|
38
|
+
headers: z.record(z.string()).default({}),
|
|
39
|
+
skipSSLValidation: z.boolean().default(false),
|
|
40
|
+
});
|
|
41
|
+
export type RemoteServiceConfig = z.infer<typeof remoteServiceConfig>;
|
|
20
42
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
skipSSLValidation?: boolean;
|
|
27
|
-
}
|
|
43
|
+
const localServiceConfig = z.object({
|
|
44
|
+
name: z.string().optional(),
|
|
45
|
+
localSchemaFile: z.union([z.string(), z.array(z.string())]),
|
|
46
|
+
});
|
|
47
|
+
export type LocalServiceConfig = z.infer<typeof localServiceConfig>;
|
|
28
48
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
49
|
+
const clientServiceConfig = z.preprocess(
|
|
50
|
+
(value) => value || context.getValue()?.serviceName,
|
|
51
|
+
z.union([studioServiceConfig, remoteServiceConfig, localServiceConfig]),
|
|
52
|
+
);
|
|
53
|
+
export type ClientServiceConfig = z.infer<typeof clientServiceConfig>;
|
|
33
54
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
55
|
+
const clientConfig = z.object({
|
|
56
|
+
service: clientServiceConfig,
|
|
57
|
+
validationRules: z
|
|
58
|
+
.union([
|
|
59
|
+
z.array(z.custom<ValidationRule>()),
|
|
60
|
+
z.function().args(z.custom<ValidationRule>()).returns(z.boolean()),
|
|
61
|
+
])
|
|
62
|
+
.optional(),
|
|
63
|
+
// maybe shared with rover?
|
|
64
|
+
includes: z
|
|
65
|
+
.array(z.string())
|
|
66
|
+
.default(["src/**/*.{ts,tsx,js,jsx,graphql,gql}"]),
|
|
67
|
+
// maybe shared with rover?
|
|
68
|
+
excludes: z.array(z.string()).default(["**/node_modules", "**/__tests__"]),
|
|
69
|
+
// maybe shared with rover?
|
|
70
|
+
tagName: z.string().default("gql"),
|
|
71
|
+
// removed:
|
|
72
|
+
clientOnlyDirectives: ignoredFieldWarning(),
|
|
73
|
+
clientSchemaDirectives: ignoredFieldWarning(),
|
|
74
|
+
statsWindow: ignoredFieldWarning(),
|
|
75
|
+
name: ignoredFieldWarning(),
|
|
76
|
+
referenceId: ignoredFieldWarning(),
|
|
77
|
+
version: ignoredFieldWarning(),
|
|
78
|
+
});
|
|
79
|
+
export type ClientConfigFormat = z.infer<typeof clientConfig>;
|
|
38
80
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
81
|
+
const roverConfig = z.object({
|
|
82
|
+
bin: z.string().optional(),
|
|
83
|
+
profile: z.string().optional(),
|
|
84
|
+
});
|
|
85
|
+
type RoverConfigFormat = z.infer<typeof roverConfig>;
|
|
42
86
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
87
|
+
const engineConfig = z.object({
|
|
88
|
+
endpoint: z
|
|
89
|
+
.string()
|
|
90
|
+
.default(
|
|
91
|
+
process.env.APOLLO_ENGINE_ENDPOINT ||
|
|
92
|
+
"https://graphql.api.apollographql.com/api/graphql",
|
|
93
|
+
),
|
|
94
|
+
apiKey: z.preprocess(
|
|
95
|
+
(val) => val || context.getValue()?.apiKey,
|
|
96
|
+
z.string().optional(),
|
|
97
|
+
),
|
|
98
|
+
});
|
|
99
|
+
export type EngineConfig = z.infer<typeof engineConfig>;
|
|
47
100
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
101
|
+
const baseConfig = z.object({
|
|
102
|
+
engine: engineConfig.default({}),
|
|
103
|
+
client: z.unknown().optional(),
|
|
104
|
+
rover: z.unknown().optional(),
|
|
105
|
+
service: ignoredFieldWarning(
|
|
106
|
+
(path) =>
|
|
107
|
+
`Service-type projects are no longer supported. Please remove the "${path}" field from your configuration file.`,
|
|
108
|
+
),
|
|
109
|
+
});
|
|
52
110
|
|
|
53
|
-
export type
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
service?: ServiceSpecifier | ClientServiceConfig;
|
|
58
|
-
// client identity
|
|
59
|
-
name?: ClientID;
|
|
60
|
-
referenceID?: string;
|
|
61
|
-
version?: string;
|
|
62
|
-
// client schemas
|
|
63
|
-
clientOnlyDirectives?: string[];
|
|
64
|
-
clientSchemaDirectives?: string[];
|
|
65
|
-
addTypename?: boolean;
|
|
66
|
-
tagName?: string;
|
|
67
|
-
// stats window config
|
|
68
|
-
statsWindow?: EngineStatsWindow;
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Rules that will be applied when validating GraphQL documents.
|
|
72
|
-
*
|
|
73
|
-
* If you wish to modify the default list of validation rules, import them from the apollo package and
|
|
74
|
-
* assign your custom list:
|
|
75
|
-
*
|
|
76
|
-
* ```js
|
|
77
|
-
* const { defaultValidationRules } = require("apollo/lib/defaultValidationRules");
|
|
78
|
-
*
|
|
79
|
-
* module.exports = {
|
|
80
|
-
* // ...
|
|
81
|
-
* validationRules: [...defaultValidationRules, ...customRules]
|
|
82
|
-
* }
|
|
83
|
-
* ```
|
|
84
|
-
*
|
|
85
|
-
* Or, if you simply wish to filter out some rules from the default list, you can specify a filter function:
|
|
86
|
-
*
|
|
87
|
-
* ```js
|
|
88
|
-
* module.exports = {
|
|
89
|
-
* // ...
|
|
90
|
-
* validationRules: rule => rule.name !== "NoAnonymousQueries"
|
|
91
|
-
* }
|
|
92
|
-
* ```
|
|
93
|
-
*/
|
|
94
|
-
validationRules?: ValidationRule[] | ((rule: ValidationRule) => boolean);
|
|
95
|
-
}
|
|
111
|
+
export type FullClientConfigFormat = Extract<
|
|
112
|
+
ParsedApolloConfigFormat,
|
|
113
|
+
{ client: {} }
|
|
114
|
+
>;
|
|
96
115
|
|
|
97
|
-
export
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
clientSchemaDirectives: ["client", "rest"],
|
|
102
|
-
addTypename: true,
|
|
103
|
-
statsWindow: DefaultEngineStatsWindow,
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
export interface ServiceConfigFormat extends ConfigBase {
|
|
107
|
-
name?: string;
|
|
108
|
-
endpoint?: Exclude<RemoteServiceConfig, "name">;
|
|
109
|
-
localSchemaFile?: string | string[];
|
|
110
|
-
}
|
|
116
|
+
export type FullRoverConfigFormat = Extract<
|
|
117
|
+
ParsedApolloConfigFormat,
|
|
118
|
+
{ rover: {} }
|
|
119
|
+
>;
|
|
111
120
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
url: "http://localhost:4000/graphql",
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
export interface ConfigBaseFormat {
|
|
120
|
-
client?: ClientConfigFormat;
|
|
121
|
-
service?: ServiceConfigFormat;
|
|
122
|
-
engine?: EngineConfig;
|
|
121
|
+
/** Helper function for TypeScript - we just want the first type to make it into the types, not the "no feature flag" fallback */
|
|
122
|
+
function ifRoverAvailable<T>(yes: T, no: any): T {
|
|
123
|
+
return ROVER_AVAILABLE ? yes : no;
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
export
|
|
126
|
-
|
|
127
|
-
|
|
126
|
+
export const configSchema = baseConfig
|
|
127
|
+
.superRefine((val, ctx) => {
|
|
128
|
+
if (ROVER_AVAILABLE) {
|
|
129
|
+
if ("client" in val && "rover" in val) {
|
|
130
|
+
ctx.addIssue({
|
|
131
|
+
code: "custom",
|
|
132
|
+
message: "Config cannot contain both 'client' and 'rover' fields",
|
|
133
|
+
fatal: true,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (!("client" in val) && !("rover" in val)) {
|
|
137
|
+
ctx.addIssue({
|
|
138
|
+
code: "custom",
|
|
139
|
+
message: "Config needs to contain either 'client' or 'rover' fields",
|
|
140
|
+
fatal: true,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
if (!("client" in val)) {
|
|
145
|
+
ctx.addIssue({
|
|
146
|
+
code: "custom",
|
|
147
|
+
message: "Config needs to contain a 'client' field.",
|
|
148
|
+
fatal: true,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
.and(
|
|
154
|
+
ifRoverAvailable(
|
|
155
|
+
z.union([
|
|
156
|
+
z
|
|
157
|
+
.object({
|
|
158
|
+
client: clientConfig,
|
|
159
|
+
})
|
|
160
|
+
.transform((val): typeof val & { rover?: never } => val),
|
|
161
|
+
z
|
|
162
|
+
.object({
|
|
163
|
+
rover: roverConfig,
|
|
164
|
+
})
|
|
165
|
+
.transform((val): typeof val & { client?: never } => val),
|
|
166
|
+
]),
|
|
167
|
+
z.object({
|
|
168
|
+
client: clientConfig,
|
|
169
|
+
}),
|
|
170
|
+
),
|
|
171
|
+
);
|
|
172
|
+
export type RawApolloConfigFormat = z.input<typeof configSchema>;
|
|
173
|
+
export type ParsedApolloConfigFormat = z.output<typeof configSchema>;
|
|
174
|
+
|
|
175
|
+
export function parseApolloConfig(
|
|
176
|
+
rawConfig: RawApolloConfigFormat,
|
|
177
|
+
configURI?: URI,
|
|
178
|
+
ctx: Context = {},
|
|
179
|
+
) {
|
|
180
|
+
const parsed = context.withValue(ctx, () =>
|
|
181
|
+
configSchema.safeParse(rawConfig),
|
|
182
|
+
);
|
|
183
|
+
if (!parsed.success) {
|
|
184
|
+
// Remove "or Required at rover" errors when a client config is provided
|
|
185
|
+
// Remove "or Required at client" errors when a rover config is provided
|
|
186
|
+
for (const [index, error] of parsed.error.errors.entries()) {
|
|
187
|
+
if (error.code === z.ZodIssueCode.invalid_union) {
|
|
188
|
+
error.unionErrors = error.unionErrors.filter((e) => {
|
|
189
|
+
return !(
|
|
190
|
+
e instanceof ZodError &&
|
|
191
|
+
e.errors.length === 1 &&
|
|
192
|
+
e.errors[0].message === "Required" &&
|
|
193
|
+
e.errors[0].path.length === 1
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
throw fromZodError(parsed.error, {
|
|
199
|
+
prefix: `Error parsing config file ${configURI?.fsPath}:`,
|
|
200
|
+
prefixSeparator: "\n",
|
|
201
|
+
issueSeparator: ";\n",
|
|
202
|
+
unionSeparator: "\n or\n",
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
if (parsed.data.client) {
|
|
206
|
+
return new ClientConfig(parsed.data, configURI);
|
|
207
|
+
} else if (parsed.data.rover) {
|
|
208
|
+
return new RoverConfig(parsed.data, configURI);
|
|
209
|
+
} else {
|
|
210
|
+
// should never happen
|
|
211
|
+
throw new Error("Invalid config file format!");
|
|
212
|
+
}
|
|
213
|
+
}
|
|
128
214
|
|
|
129
|
-
export class ApolloConfig {
|
|
130
|
-
public isClient: boolean;
|
|
131
|
-
public isService: boolean;
|
|
215
|
+
export abstract class ApolloConfig {
|
|
132
216
|
public engine: EngineConfig;
|
|
133
|
-
public service?: ServiceConfigFormat;
|
|
134
217
|
public client?: ClientConfigFormat;
|
|
218
|
+
public rover?: RoverConfigFormat;
|
|
135
219
|
private _variant?: string;
|
|
136
220
|
private _graphId?: string;
|
|
137
221
|
|
|
138
|
-
constructor(
|
|
139
|
-
|
|
140
|
-
|
|
222
|
+
protected constructor(
|
|
223
|
+
public rawConfig: ParsedApolloConfigFormat,
|
|
224
|
+
public configURI?: URI,
|
|
225
|
+
) {
|
|
141
226
|
this.engine = rawConfig.engine!;
|
|
142
227
|
this._graphId = getGraphIdFromConfig(rawConfig);
|
|
143
|
-
this.client = rawConfig.client;
|
|
144
|
-
this.service = rawConfig.service;
|
|
145
228
|
}
|
|
146
229
|
|
|
147
230
|
get configDirURI() {
|
|
148
231
|
// if the filepath has a _file_ in it, then we get its dir
|
|
149
|
-
return this.configURI &&
|
|
232
|
+
return this.configURI &&
|
|
233
|
+
this.configURI.fsPath.match(/\.(ts|js|cjs|mjs|json)$/i)
|
|
150
234
|
? URI.parse(dirname(this.configURI.fsPath))
|
|
151
235
|
: this.configURI;
|
|
152
236
|
}
|
|
153
237
|
|
|
154
|
-
get projects(): (ClientConfig | ServiceConfig)[] {
|
|
155
|
-
const configs = [];
|
|
156
|
-
const { client, service } = this.rawConfig;
|
|
157
|
-
if (client) configs.push(new ClientConfig(this.rawConfig, this.configURI));
|
|
158
|
-
if (service)
|
|
159
|
-
configs.push(new ServiceConfig(this.rawConfig, this.configURI));
|
|
160
|
-
return configs;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
238
|
set variant(variant: string) {
|
|
164
239
|
this._variant = variant;
|
|
165
240
|
}
|
|
@@ -170,9 +245,6 @@ export class ApolloConfig {
|
|
|
170
245
|
if (this.client && typeof this.client.service === "string") {
|
|
171
246
|
const parsedVariant = parseServiceSpecifier(this.client.service)[1];
|
|
172
247
|
if (parsedVariant) tag = parsedVariant;
|
|
173
|
-
} else if (this.service && typeof this.service.name === "string") {
|
|
174
|
-
const parsedVariant = parseServiceSpecifier(this.service.name)[1];
|
|
175
|
-
if (parsedVariant) tag = parsedVariant;
|
|
176
248
|
}
|
|
177
249
|
return tag;
|
|
178
250
|
}
|
|
@@ -185,35 +257,28 @@ export class ApolloConfig {
|
|
|
185
257
|
if (this._graphId) return this._graphId;
|
|
186
258
|
return getGraphIdFromConfig(this.rawConfig);
|
|
187
259
|
}
|
|
188
|
-
|
|
189
|
-
// this type needs to be an "EveryKeyIsOptionalApolloConfig"
|
|
190
|
-
public setDefaults({
|
|
191
|
-
engine,
|
|
192
|
-
client,
|
|
193
|
-
service,
|
|
194
|
-
}: {
|
|
195
|
-
engine?: EngineConfig;
|
|
196
|
-
client?: ClientConfigFormat;
|
|
197
|
-
service?: ServiceConfigFormat;
|
|
198
|
-
}): void {
|
|
199
|
-
const config = merge(this.rawConfig, { client, engine, service });
|
|
200
|
-
this.rawConfig = config;
|
|
201
|
-
this.client = config.client;
|
|
202
|
-
this.service = config.service;
|
|
203
|
-
if (config.engine) {
|
|
204
|
-
this.engine = config.engine;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
260
|
}
|
|
208
261
|
|
|
209
262
|
export class ClientConfig extends ApolloConfig {
|
|
210
263
|
public client!: ClientConfigFormat;
|
|
211
|
-
|
|
212
|
-
|
|
264
|
+
|
|
265
|
+
constructor(
|
|
266
|
+
public rawConfig: FullClientConfigFormat,
|
|
267
|
+
public configURI?: URI,
|
|
268
|
+
) {
|
|
269
|
+
super(rawConfig, configURI);
|
|
270
|
+
this.client = rawConfig.client;
|
|
271
|
+
}
|
|
213
272
|
}
|
|
214
273
|
|
|
215
|
-
export class
|
|
216
|
-
public
|
|
217
|
-
|
|
218
|
-
|
|
274
|
+
export class RoverConfig extends ApolloConfig {
|
|
275
|
+
public rover!: RoverConfigFormat;
|
|
276
|
+
|
|
277
|
+
constructor(
|
|
278
|
+
public rawConfig: FullRoverConfigFormat,
|
|
279
|
+
public configURI?: URI,
|
|
280
|
+
) {
|
|
281
|
+
super(rawConfig, configURI);
|
|
282
|
+
this.rover = rawConfig.rover;
|
|
283
|
+
}
|
|
219
284
|
}
|
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
import cosmiconfig from "cosmiconfig";
|
|
2
|
-
import { LoaderEntry } from "cosmiconfig";
|
|
3
|
-
import TypeScriptLoader from "@endemolshinegroup/cosmiconfig-typescript-loader";
|
|
1
|
+
import { cosmiconfig } from "cosmiconfig";
|
|
4
2
|
import { resolve } from "path";
|
|
5
3
|
import { readFileSync, existsSync, lstatSync } from "fs";
|
|
6
|
-
import merge from "lodash.merge";
|
|
7
4
|
import {
|
|
8
5
|
ApolloConfig,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
DefaultClientConfig,
|
|
12
|
-
DefaultServiceConfig,
|
|
13
|
-
DefaultEngineConfig,
|
|
6
|
+
RawApolloConfigFormat,
|
|
7
|
+
parseApolloConfig,
|
|
14
8
|
} from "./config";
|
|
15
9
|
import { getServiceFromKey } from "./utils";
|
|
16
|
-
import URI from "vscode-uri";
|
|
10
|
+
import { URI } from "vscode-uri";
|
|
17
11
|
import { Debug } from "../utilities";
|
|
18
12
|
|
|
19
13
|
// config settings
|
|
@@ -22,207 +16,86 @@ const defaultFileNames = [
|
|
|
22
16
|
"package.json",
|
|
23
17
|
`${MODULE_NAME}.config.js`,
|
|
24
18
|
`${MODULE_NAME}.config.ts`,
|
|
19
|
+
`${MODULE_NAME}.config.mjs`,
|
|
25
20
|
`${MODULE_NAME}.config.cjs`,
|
|
26
21
|
];
|
|
27
22
|
const envFileNames = [".env", ".env.local"];
|
|
28
23
|
|
|
29
|
-
const loaders = {
|
|
30
|
-
// XXX improve types for config
|
|
31
|
-
".json": (cosmiconfig as any).loadJson as LoaderEntry,
|
|
32
|
-
".js": (cosmiconfig as any).loadJs as LoaderEntry,
|
|
33
|
-
".cjs": (cosmiconfig as any).loadJs as LoaderEntry,
|
|
34
|
-
".ts": {
|
|
35
|
-
async: TypeScriptLoader,
|
|
36
|
-
},
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
export const legacyKeyEnvVar = "ENGINE_API_KEY";
|
|
40
24
|
export const keyEnvVar = "APOLLO_KEY";
|
|
41
25
|
|
|
42
26
|
export interface LoadConfigSettings {
|
|
43
27
|
// the current working directory to start looking for the config
|
|
44
28
|
// config loading only works on node so we default to
|
|
45
29
|
// process.cwd()
|
|
46
|
-
|
|
47
|
-
// configPath and fileName are used in conjunction with one another.
|
|
48
|
-
// i.e. /User/myProj/my.config.js
|
|
49
|
-
// => { configPath: '/User/myProj/', configFileName: 'my.config.js' }
|
|
50
|
-
configPath?: string;
|
|
51
|
-
|
|
52
|
-
// if a configFileName is passed in, loadConfig won't accept any other
|
|
53
|
-
// configs as a fallback.
|
|
54
|
-
configFileName?: string;
|
|
55
|
-
|
|
56
|
-
// used when run by a `Workspace` where we _know_ a config file should be present.
|
|
57
|
-
requireConfig?: boolean;
|
|
58
|
-
|
|
59
|
-
// for CLI usage, we don't _require_ a config file for everything. This allows us to pass in
|
|
60
|
-
// options to build one at runtime
|
|
61
|
-
name?: string;
|
|
62
|
-
type?: "service" | "client";
|
|
30
|
+
configPath: string;
|
|
63
31
|
}
|
|
64
32
|
|
|
65
33
|
export type ConfigResult<T> = {
|
|
66
34
|
config: T;
|
|
67
35
|
filepath: string;
|
|
36
|
+
isEmpty?: boolean;
|
|
68
37
|
} | null;
|
|
69
38
|
|
|
70
39
|
// XXX load .env files automatically
|
|
71
40
|
export async function loadConfig({
|
|
72
41
|
configPath,
|
|
73
|
-
configFileName,
|
|
74
|
-
requireConfig = false,
|
|
75
|
-
name,
|
|
76
|
-
type,
|
|
77
42
|
}: LoadConfigSettings): Promise<ApolloConfig | null> {
|
|
78
43
|
const explorer = cosmiconfig(MODULE_NAME, {
|
|
79
|
-
searchPlaces:
|
|
80
|
-
loaders,
|
|
44
|
+
searchPlaces: defaultFileNames,
|
|
81
45
|
});
|
|
82
46
|
|
|
83
47
|
// search can fail if a file can't be parsed (ex: a nonsense js file) so we wrap in a try/catch
|
|
84
|
-
let loadedConfig
|
|
48
|
+
let loadedConfig: ConfigResult<RawApolloConfigFormat>;
|
|
85
49
|
try {
|
|
86
50
|
loadedConfig = (await explorer.search(
|
|
87
|
-
configPath
|
|
88
|
-
)) as ConfigResult<
|
|
51
|
+
configPath,
|
|
52
|
+
)) as ConfigResult<RawApolloConfigFormat>;
|
|
89
53
|
} catch (error) {
|
|
90
|
-
|
|
91
|
-
arguments[0]
|
|
54
|
+
throw new Error(`A config file failed to load with options: ${JSON.stringify(
|
|
55
|
+
arguments[0],
|
|
92
56
|
)}.
|
|
93
57
|
The error was: ${error}`);
|
|
94
|
-
return null;
|
|
95
58
|
}
|
|
96
59
|
|
|
97
|
-
if (
|
|
60
|
+
if (!loadedConfig || loadedConfig.isEmpty) {
|
|
98
61
|
Debug.error(
|
|
99
|
-
`
|
|
62
|
+
`No Apollo config found for project or config file failed to load. For more information, please refer to: https://go.apollo.dev/t/config`,
|
|
100
63
|
);
|
|
64
|
+
// deliberately returning `null` here, but not throwing an error - the user may not have a config file and that's okay, it might just be a project without a graph
|
|
101
65
|
return null;
|
|
102
66
|
}
|
|
103
67
|
|
|
104
|
-
if (loadedConfig
|
|
68
|
+
if (loadedConfig.filepath.endsWith("package.json")) {
|
|
105
69
|
Debug.warning(
|
|
106
|
-
'The "apollo" package.json configuration key will no longer be supported in Apollo v3. Please use the apollo.config.js file for Apollo project configuration. For more information, see: https://go.apollo.dev/t/config'
|
|
70
|
+
'The "apollo" package.json configuration key will no longer be supported in Apollo v3. Please use the apollo.config.js file for Apollo project configuration. For more information, see: https://go.apollo.dev/t/config',
|
|
107
71
|
);
|
|
108
72
|
}
|
|
109
73
|
|
|
110
|
-
if (requireConfig && !loadedConfig) {
|
|
111
|
-
Debug.error(
|
|
112
|
-
`No Apollo config found for project. For more information, please refer to: https://go.apollo.dev/t/config`
|
|
113
|
-
);
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
74
|
// add API key from the env
|
|
118
|
-
let
|
|
119
|
-
apiKey,
|
|
120
|
-
nameFromKey;
|
|
75
|
+
let apiKey, nameFromKey;
|
|
121
76
|
|
|
122
77
|
// loop over the list of possible .env files and try to parse for key
|
|
123
78
|
// and service name. Files are scanned and found values are preferred
|
|
124
79
|
// in order of appearance in `envFileNames`.
|
|
125
80
|
envFileNames.forEach((envFile) => {
|
|
126
|
-
const dotEnvPath = configPath
|
|
127
|
-
? resolve(configPath, envFile)
|
|
128
|
-
: resolve(process.cwd(), envFile);
|
|
81
|
+
const dotEnvPath = resolve(configPath, envFile);
|
|
129
82
|
|
|
130
83
|
if (existsSync(dotEnvPath) && lstatSync(dotEnvPath).isFile()) {
|
|
131
84
|
const env: { [key: string]: string } = require("dotenv").parse(
|
|
132
|
-
readFileSync(dotEnvPath)
|
|
85
|
+
readFileSync(dotEnvPath),
|
|
133
86
|
);
|
|
134
|
-
|
|
135
|
-
const key = env[keyEnvVar];
|
|
136
|
-
if (legacyKey && key) {
|
|
137
|
-
Debug.warning(
|
|
138
|
-
`Both ${legacyKeyEnvVar} and ${keyEnvVar} were found. ${keyEnvVar} will take precedence.`
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
if (legacyKey) {
|
|
142
|
-
Debug.warning(
|
|
143
|
-
`[Deprecation warning] Setting the key via ${legacyKeyEnvVar} is deprecated and will not be supported in future versions. Please use ${keyEnvVar} instead.`
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
apiKey = key || legacyKey;
|
|
87
|
+
apiKey = env[keyEnvVar];
|
|
147
88
|
}
|
|
148
89
|
});
|
|
149
90
|
|
|
150
91
|
if (apiKey) {
|
|
151
|
-
engineConfig = { engine: { apiKey } };
|
|
152
92
|
nameFromKey = getServiceFromKey(apiKey);
|
|
153
93
|
}
|
|
154
94
|
|
|
155
|
-
// DETERMINE PROJECT TYPE
|
|
156
|
-
// The CLI passes in a type when loading config. The editor extension
|
|
157
|
-
// does not. So we determine the type of the config here, and use it if
|
|
158
|
-
// the type wasn't explicitly passed in.
|
|
159
|
-
let projectType: "client" | "service";
|
|
160
|
-
if (type) {
|
|
161
|
-
projectType = type;
|
|
162
|
-
} else if (loadedConfig && loadedConfig.config.client) {
|
|
163
|
-
projectType = "client";
|
|
164
|
-
} else if (loadedConfig && loadedConfig.config.service) {
|
|
165
|
-
projectType = "service";
|
|
166
|
-
} else {
|
|
167
|
-
Debug.error(
|
|
168
|
-
"Unable to resolve project type. Please add either a client or service config. For more information, please refer to https://go.apollo.dev/t/config"
|
|
169
|
-
);
|
|
170
|
-
return null;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
// DETERMINE SERVICE NAME
|
|
174
|
-
// precedence: 1. (highest) config.js (client only) 2. name passed into loadConfig 3. name from api key
|
|
175
|
-
let serviceName = name || nameFromKey;
|
|
176
|
-
if (
|
|
177
|
-
projectType === "client" &&
|
|
178
|
-
loadedConfig &&
|
|
179
|
-
loadedConfig.config.client &&
|
|
180
|
-
typeof loadedConfig.config.client.service === "string"
|
|
181
|
-
) {
|
|
182
|
-
serviceName = loadedConfig.config.client.service;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// if there wasn't a config loaded from a file, build one.
|
|
186
|
-
// if there was a service name found in the env, merge it with the new/existing config object.
|
|
187
|
-
// if the config loaded doesn't have a client/service key, add one based on projectType
|
|
188
|
-
if (
|
|
189
|
-
!loadedConfig ||
|
|
190
|
-
serviceName ||
|
|
191
|
-
!(loadedConfig.config.client || loadedConfig.config.service)
|
|
192
|
-
) {
|
|
193
|
-
loadedConfig = {
|
|
194
|
-
filepath: configPath || process.cwd(),
|
|
195
|
-
config: {
|
|
196
|
-
...(loadedConfig && loadedConfig.config),
|
|
197
|
-
...(projectType === "client"
|
|
198
|
-
? {
|
|
199
|
-
client: {
|
|
200
|
-
...DefaultConfigBase,
|
|
201
|
-
...(loadedConfig && loadedConfig.config.client),
|
|
202
|
-
service: serviceName,
|
|
203
|
-
},
|
|
204
|
-
}
|
|
205
|
-
: {
|
|
206
|
-
service: {
|
|
207
|
-
...DefaultConfigBase,
|
|
208
|
-
...(loadedConfig && loadedConfig.config.service),
|
|
209
|
-
name: serviceName,
|
|
210
|
-
},
|
|
211
|
-
}),
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
95
|
let { config, filepath } = loadedConfig;
|
|
217
96
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
if (config.service) config = merge({ service: DefaultServiceConfig }, config);
|
|
223
|
-
if (engineConfig) config = merge(engineConfig, config);
|
|
224
|
-
|
|
225
|
-
config = merge({ engine: DefaultEngineConfig }, config);
|
|
226
|
-
|
|
227
|
-
return new ApolloConfig(config, URI.file(resolve(filepath)));
|
|
97
|
+
return parseApolloConfig(config, URI.file(resolve(filepath)), {
|
|
98
|
+
apiKey,
|
|
99
|
+
serviceName: nameFromKey,
|
|
100
|
+
});
|
|
228
101
|
}
|