vscode-apollo 1.19.3 → 1.20.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.
Files changed (155) 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/release.yml +95 -0
  7. package/.gitleaks.toml +26 -0
  8. package/.nvmrc +1 -0
  9. package/.prettierrc +5 -0
  10. package/.vscode/launch.json +61 -0
  11. package/.vscode/settings.json +16 -0
  12. package/.vscode/tasks.json +18 -0
  13. package/.vscodeignore +17 -1
  14. package/CHANGELOG.md +172 -1
  15. package/LICENSE +2 -2
  16. package/README.md +9 -9
  17. package/codegen.yml +12 -0
  18. package/images/IconRun.svg +8 -0
  19. package/jest.config.ts +11 -0
  20. package/package.json +102 -22
  21. package/renovate.json +23 -0
  22. package/src/__mocks__/fs.js +3 -0
  23. package/src/__tests__/statusBar.test.ts +8 -7
  24. package/src/debug.ts +2 -5
  25. package/src/env/fetch/fetch.ts +32 -0
  26. package/src/env/fetch/global.ts +30 -0
  27. package/src/env/fetch/index.d.ts +2 -0
  28. package/src/env/fetch/index.ts +2 -0
  29. package/src/env/fetch/url.ts +9 -0
  30. package/src/env/index.ts +4 -0
  31. package/src/env/polyfills/array.ts +17 -0
  32. package/src/env/polyfills/index.ts +2 -0
  33. package/src/env/polyfills/object.ts +7 -0
  34. package/src/env/typescript-utility-types.ts +2 -0
  35. package/src/extension.ts +106 -37
  36. package/src/language-server/__tests__/diagnostics.test.ts +86 -0
  37. package/src/language-server/__tests__/document.test.ts +187 -0
  38. package/src/language-server/__tests__/fileSet.test.ts +46 -0
  39. package/src/language-server/__tests__/fixtures/starwarsSchema.ts +1917 -0
  40. package/src/language-server/config/__tests__/config.ts +128 -0
  41. package/src/language-server/config/__tests__/loadConfig.ts +508 -0
  42. package/src/language-server/config/__tests__/utils.ts +106 -0
  43. package/src/language-server/config/config.ts +219 -0
  44. package/src/language-server/config/index.ts +3 -0
  45. package/src/language-server/config/loadConfig.ts +228 -0
  46. package/src/language-server/config/utils.ts +56 -0
  47. package/src/language-server/diagnostics.ts +109 -0
  48. package/src/language-server/document.ts +277 -0
  49. package/src/language-server/engine/GraphQLDataSource.ts +124 -0
  50. package/src/language-server/engine/index.ts +105 -0
  51. package/src/language-server/engine/operations/frontendUrlRoot.ts +7 -0
  52. package/src/language-server/engine/operations/schemaTagsAndFieldStats.ts +24 -0
  53. package/src/language-server/errors/__tests__/NoMissingClientDirectives.test.ts +220 -0
  54. package/src/language-server/errors/logger.ts +58 -0
  55. package/src/language-server/errors/validation.ts +277 -0
  56. package/src/language-server/fileSet.ts +65 -0
  57. package/src/language-server/format.ts +48 -0
  58. package/src/language-server/graphqlTypes.ts +7176 -0
  59. package/src/language-server/index.ts +29 -0
  60. package/src/language-server/languageProvider.ts +798 -0
  61. package/src/language-server/loadingHandler.ts +64 -0
  62. package/src/language-server/project/base.ts +399 -0
  63. package/src/language-server/project/client.ts +602 -0
  64. package/src/language-server/project/defaultClientSchema.ts +45 -0
  65. package/src/language-server/project/service.ts +48 -0
  66. package/src/language-server/providers/schema/__tests__/file.ts +150 -0
  67. package/src/language-server/providers/schema/base.ts +15 -0
  68. package/src/language-server/providers/schema/endpoint.ts +157 -0
  69. package/src/language-server/providers/schema/engine.ts +197 -0
  70. package/src/language-server/providers/schema/file.ts +167 -0
  71. package/src/language-server/providers/schema/index.ts +75 -0
  72. package/src/language-server/server.ts +252 -0
  73. package/src/language-server/typings/codemirror.d.ts +4 -0
  74. package/src/language-server/typings/graphql.d.ts +27 -0
  75. package/src/language-server/utilities/__tests__/graphql.test.ts +411 -0
  76. package/src/language-server/utilities/__tests__/uri.ts +55 -0
  77. package/src/language-server/utilities/debouncer.ts +8 -0
  78. package/src/language-server/utilities/debug.ts +89 -0
  79. package/src/language-server/utilities/graphql.ts +432 -0
  80. package/src/language-server/utilities/index.ts +3 -0
  81. package/src/language-server/utilities/source.ts +182 -0
  82. package/src/language-server/utilities/uri.ts +19 -0
  83. package/src/language-server/workspace.ts +262 -0
  84. package/src/languageServerClient.ts +19 -12
  85. package/src/messages.ts +84 -0
  86. package/src/statusBar.ts +5 -5
  87. package/src/tools/__tests__/buildServiceDefinition.test.ts +491 -0
  88. package/src/tools/__tests__/snapshotSerializers/astSerializer.ts +19 -0
  89. package/src/tools/__tests__/snapshotSerializers/graphQLTypeSerializer.ts +14 -0
  90. package/src/tools/buildServiceDefinition.ts +241 -0
  91. package/src/tools/index.ts +6 -0
  92. package/src/tools/schema/index.ts +2 -0
  93. package/src/tools/schema/resolveObject.ts +18 -0
  94. package/src/tools/schema/resolverMap.ts +23 -0
  95. package/src/tools/utilities/graphql.ts +22 -0
  96. package/src/tools/utilities/index.ts +3 -0
  97. package/src/tools/utilities/invariant.ts +5 -0
  98. package/src/tools/utilities/predicates.ts +5 -0
  99. package/src/utils.ts +1 -16
  100. package/syntaxes/graphql.js.json +3 -3
  101. package/syntaxes/graphql.json +13 -9
  102. package/syntaxes/graphql.lua.json +51 -0
  103. package/syntaxes/graphql.rb.json +1 -1
  104. package/tsconfig.build.json +4 -0
  105. package/tsconfig.json +20 -7
  106. package/create-server-symlink.js +0 -8
  107. package/lib/debug.d.ts +0 -11
  108. package/lib/debug.d.ts.map +0 -1
  109. package/lib/debug.js +0 -48
  110. package/lib/debug.js.map +0 -1
  111. package/lib/extension.d.ts +0 -4
  112. package/lib/extension.d.ts.map +0 -1
  113. package/lib/extension.js +0 -187
  114. package/lib/extension.js.map +0 -1
  115. package/lib/languageServerClient.d.ts +0 -4
  116. package/lib/languageServerClient.d.ts.map +0 -1
  117. package/lib/languageServerClient.js +0 -57
  118. package/lib/languageServerClient.js.map +0 -1
  119. package/lib/statusBar.d.ts +0 -24
  120. package/lib/statusBar.d.ts.map +0 -1
  121. package/lib/statusBar.js +0 -46
  122. package/lib/statusBar.js.map +0 -1
  123. package/lib/testRunner/index.d.ts +0 -3
  124. package/lib/testRunner/index.d.ts.map +0 -1
  125. package/lib/testRunner/index.js +0 -49
  126. package/lib/testRunner/index.js.map +0 -1
  127. package/lib/testRunner/jest-config.d.ts +0 -14
  128. package/lib/testRunner/jest-config.d.ts.map +0 -1
  129. package/lib/testRunner/jest-config.js +0 -18
  130. package/lib/testRunner/jest-config.js.map +0 -1
  131. package/lib/testRunner/jest-vscode-environment.d.ts +0 -2
  132. package/lib/testRunner/jest-vscode-environment.d.ts.map +0 -1
  133. package/lib/testRunner/jest-vscode-environment.js +0 -19
  134. package/lib/testRunner/jest-vscode-environment.js.map +0 -1
  135. package/lib/testRunner/jest-vscode-framework-setup.d.ts +0 -1
  136. package/lib/testRunner/jest-vscode-framework-setup.d.ts.map +0 -1
  137. package/lib/testRunner/jest-vscode-framework-setup.js +0 -3
  138. package/lib/testRunner/jest-vscode-framework-setup.js.map +0 -1
  139. package/lib/testRunner/vscode-test-script.d.ts +0 -2
  140. package/lib/testRunner/vscode-test-script.d.ts.map +0 -1
  141. package/lib/testRunner/vscode-test-script.js +0 -23
  142. package/lib/testRunner/vscode-test-script.js.map +0 -1
  143. package/lib/utils.d.ts +0 -18
  144. package/lib/utils.d.ts.map +0 -1
  145. package/lib/utils.js +0 -52
  146. package/lib/utils.js.map +0 -1
  147. package/src/testRunner/README.md +0 -23
  148. package/src/testRunner/index.ts +0 -72
  149. package/src/testRunner/jest-config.ts +0 -17
  150. package/src/testRunner/jest-vscode-environment.ts +0 -25
  151. package/src/testRunner/jest-vscode-framework-setup.ts +0 -10
  152. package/src/testRunner/jest.d.ts +0 -37
  153. package/src/testRunner/vscode-test-script.ts +0 -38
  154. package/tsconfig.test.json +0 -4
  155. package/tsconfig.tsbuildinfo +0 -2486
@@ -0,0 +1,106 @@
1
+ import {
2
+ ApolloConfig,
3
+ ApolloConfigFormat,
4
+ getServiceFromKey,
5
+ getGraphIdFromConfig,
6
+ isClientConfig,
7
+ isLocalServiceConfig,
8
+ isServiceConfig,
9
+ parseServiceSpecifier,
10
+ DefaultConfigBase,
11
+ } from "../";
12
+
13
+ describe("getServiceFromKey", () => {
14
+ it("returns undefined with no provided key", () => {
15
+ expect(getServiceFromKey()).toBeUndefined();
16
+ });
17
+
18
+ it("returns service name from service api key", () => {
19
+ const key = "service:bob-123:489fhseo4";
20
+ expect(getServiceFromKey(key)).toEqual("bob-123");
21
+ });
22
+
23
+ it("returns nothing if key is not a service key", () => {
24
+ const key = "not-a-service:bob-123:489fhseo4";
25
+ expect(getServiceFromKey(key)).toBeUndefined();
26
+ });
27
+
28
+ it("returns nothing if key is malformed", () => {
29
+ const key = "service/bob-123:489fhseo4";
30
+ expect(getServiceFromKey(key)).toBeUndefined();
31
+ });
32
+ });
33
+
34
+ describe("getServiceName", () => {
35
+ describe("client config", () => {
36
+ it("finds service name when client.service is a string", () => {
37
+ const rawConfig: ApolloConfigFormat = {
38
+ client: { service: "my-service", ...DefaultConfigBase },
39
+ };
40
+ expect(getGraphIdFromConfig(rawConfig)).toEqual("my-service");
41
+
42
+ const rawConfigWithTag: ApolloConfigFormat = {
43
+ client: { service: "my-service@master", ...DefaultConfigBase },
44
+ };
45
+ expect(getGraphIdFromConfig(rawConfigWithTag)).toEqual("my-service");
46
+ });
47
+
48
+ it("finds service name when client.service is an object", () => {
49
+ const rawConfig: ApolloConfigFormat = {
50
+ client: {
51
+ service: { name: "my-service", localSchemaFile: "./someFile" },
52
+ ...DefaultConfigBase,
53
+ },
54
+ };
55
+ expect(getGraphIdFromConfig(rawConfig)).toEqual("my-service");
56
+ });
57
+ });
58
+ describe("service config", () => {
59
+ it("finds service name from raw service config", () => {
60
+ const rawConfig: ApolloConfigFormat = {
61
+ service: {
62
+ name: "my-service",
63
+ localSchemaFile: "./someFile",
64
+ ...DefaultConfigBase,
65
+ },
66
+ };
67
+ expect(getGraphIdFromConfig(rawConfig)).toEqual("my-service");
68
+ });
69
+ });
70
+ });
71
+
72
+ describe("isClientConfig", () => {
73
+ it("identifies client config properly", () => {
74
+ const config = new ApolloConfig({
75
+ client: { service: "hello", ...DefaultConfigBase },
76
+ });
77
+ expect(isClientConfig(config)).toBeTruthy();
78
+ });
79
+ });
80
+
81
+ describe("isLocalServiceConfig", () => {
82
+ it("properly identifies a client config that uses localSchemaFiles", () => {
83
+ const clientServiceConfig = { name: "my-service", localSchemaFile: "okay" };
84
+ expect(isLocalServiceConfig(clientServiceConfig)).toBeTruthy();
85
+ });
86
+ });
87
+
88
+ describe("isServiceConfig", () => {
89
+ it("identifies service config properly", () => {
90
+ const config = new ApolloConfig({ service: { ...DefaultConfigBase } });
91
+ expect(isServiceConfig(config)).toBeTruthy();
92
+ });
93
+ });
94
+
95
+ describe("parseServiceSpecifier", () => {
96
+ it("parses service identifier for service id and tag properly", () => {
97
+ const [id, tag] = parseServiceSpecifier("my-service@master");
98
+ expect(id).toEqual("my-service");
99
+ expect(tag).toEqual("master");
100
+
101
+ const [idFromSimpleName, tagFromSimpleName] =
102
+ parseServiceSpecifier("my-service");
103
+ expect(idFromSimpleName).toEqual("my-service");
104
+ expect(tagFromSimpleName).toBeUndefined();
105
+ });
106
+ });
@@ -0,0 +1,219 @@
1
+ import { dirname } from "path";
2
+ import merge from "lodash.merge";
3
+ import { ClientID, ServiceID, ServiceSpecifier } from "../engine";
4
+ import URI from "vscode-uri";
5
+ import { WithRequired } from "../../env";
6
+ import { getGraphIdFromConfig, parseServiceSpecifier } from "./utils";
7
+ import { ValidationRule } from "graphql/validation/ValidationContext";
8
+
9
+ export interface EngineStatsWindow {
10
+ to: number;
11
+ from: number;
12
+ }
13
+
14
+ export const DefaultEngineStatsWindow = {
15
+ to: -0,
16
+ from: -86400, // one day
17
+ };
18
+
19
+ export interface HistoricalEngineStatsWindow extends EngineStatsWindow {}
20
+
21
+ export type EndpointURI = string;
22
+ export interface RemoteServiceConfig {
23
+ name: ServiceID;
24
+ url: EndpointURI;
25
+ headers?: { [key: string]: string };
26
+ skipSSLValidation?: boolean;
27
+ }
28
+
29
+ export interface LocalServiceConfig {
30
+ name: ServiceID;
31
+ localSchemaFile: string | string[];
32
+ }
33
+
34
+ export interface EngineConfig {
35
+ endpoint?: EndpointURI;
36
+ readonly apiKey?: string;
37
+ }
38
+
39
+ export const DefaultEngineConfig = {
40
+ endpoint: "https://graphql.api.apollographql.com/api/graphql",
41
+ };
42
+
43
+ export const DefaultConfigBase = {
44
+ includes: ["src/**/*.{ts,tsx,js,jsx,graphql,gql}"],
45
+ excludes: ["**/node_modules", "**/__tests__"],
46
+ };
47
+
48
+ export interface ConfigBase {
49
+ includes: string[];
50
+ excludes: string[];
51
+ }
52
+
53
+ export type ClientServiceConfig = RemoteServiceConfig | LocalServiceConfig;
54
+
55
+ export interface ClientConfigFormat extends ConfigBase {
56
+ // service linking
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
+ }
96
+
97
+ export const DefaultClientConfig = {
98
+ ...DefaultConfigBase,
99
+ tagName: "gql",
100
+ clientOnlyDirectives: ["connection", "type"],
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
+ }
111
+
112
+ export const DefaultServiceConfig = {
113
+ ...DefaultConfigBase,
114
+ endpoint: {
115
+ url: "http://localhost:4000/graphql",
116
+ },
117
+ };
118
+
119
+ export interface ConfigBaseFormat {
120
+ client?: ClientConfigFormat;
121
+ service?: ServiceConfigFormat;
122
+ engine?: EngineConfig;
123
+ }
124
+
125
+ export type ApolloConfigFormat =
126
+ | WithRequired<ConfigBaseFormat, "client">
127
+ | WithRequired<ConfigBaseFormat, "service">;
128
+
129
+ export class ApolloConfig {
130
+ public isClient: boolean;
131
+ public isService: boolean;
132
+ public engine: EngineConfig;
133
+ public service?: ServiceConfigFormat;
134
+ public client?: ClientConfigFormat;
135
+ private _variant?: string;
136
+ private _graphId?: string;
137
+
138
+ constructor(public rawConfig: ApolloConfigFormat, public configURI?: URI) {
139
+ this.isService = !!rawConfig.service;
140
+ this.isClient = !!rawConfig.client;
141
+ this.engine = rawConfig.engine!;
142
+ this._graphId = getGraphIdFromConfig(rawConfig);
143
+ this.client = rawConfig.client;
144
+ this.service = rawConfig.service;
145
+ }
146
+
147
+ get configDirURI() {
148
+ // if the filepath has a _file_ in it, then we get its dir
149
+ return this.configURI && this.configURI.fsPath.match(/\.(ts|js|cjs|json)$/i)
150
+ ? URI.parse(dirname(this.configURI.fsPath))
151
+ : this.configURI;
152
+ }
153
+
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
+ set variant(variant: string) {
164
+ this._variant = variant;
165
+ }
166
+
167
+ get variant(): string {
168
+ if (this._variant) return this._variant;
169
+ let tag: string = "current";
170
+ if (this.client && typeof this.client.service === "string") {
171
+ const parsedVariant = parseServiceSpecifier(this.client.service)[1];
172
+ 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
+ }
177
+ return tag;
178
+ }
179
+
180
+ set graph(graphId: string | undefined) {
181
+ this._graphId = graphId;
182
+ }
183
+
184
+ get graph(): string | undefined {
185
+ if (this._graphId) return this._graphId;
186
+ return getGraphIdFromConfig(this.rawConfig);
187
+ }
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
+ }
208
+
209
+ export class ClientConfig extends ApolloConfig {
210
+ public client!: ClientConfigFormat;
211
+ public isClient!: true;
212
+ public isService!: false;
213
+ }
214
+
215
+ export class ServiceConfig extends ApolloConfig {
216
+ public service!: ServiceConfigFormat;
217
+ public isClient!: false;
218
+ public isService!: true;
219
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./utils";
2
+ export * from "./config";
3
+ export * from "./loadConfig";
@@ -0,0 +1,228 @@
1
+ import cosmiconfig from "cosmiconfig";
2
+ import { LoaderEntry } from "cosmiconfig";
3
+ import TypeScriptLoader from "@endemolshinegroup/cosmiconfig-typescript-loader";
4
+ import { resolve } from "path";
5
+ import { readFileSync, existsSync, lstatSync } from "fs";
6
+ import merge from "lodash.merge";
7
+ import {
8
+ ApolloConfig,
9
+ ApolloConfigFormat,
10
+ DefaultConfigBase,
11
+ DefaultClientConfig,
12
+ DefaultServiceConfig,
13
+ DefaultEngineConfig,
14
+ } from "./config";
15
+ import { getServiceFromKey } from "./utils";
16
+ import URI from "vscode-uri";
17
+ import { Debug } from "../utilities";
18
+
19
+ // config settings
20
+ const MODULE_NAME = "apollo";
21
+ const defaultFileNames = [
22
+ "package.json",
23
+ `${MODULE_NAME}.config.js`,
24
+ `${MODULE_NAME}.config.ts`,
25
+ `${MODULE_NAME}.config.cjs`,
26
+ ];
27
+ const envFileNames = [".env", ".env.local"];
28
+
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
+ export const keyEnvVar = "APOLLO_KEY";
41
+
42
+ export interface LoadConfigSettings {
43
+ // the current working directory to start looking for the config
44
+ // config loading only works on node so we default to
45
+ // 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";
63
+ }
64
+
65
+ export type ConfigResult<T> = {
66
+ config: T;
67
+ filepath: string;
68
+ } | null;
69
+
70
+ // XXX load .env files automatically
71
+ export async function loadConfig({
72
+ configPath,
73
+ configFileName,
74
+ requireConfig = false,
75
+ name,
76
+ type,
77
+ }: LoadConfigSettings): Promise<ApolloConfig | null> {
78
+ const explorer = cosmiconfig(MODULE_NAME, {
79
+ searchPlaces: configFileName ? [configFileName] : defaultFileNames,
80
+ loaders,
81
+ });
82
+
83
+ // 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;
85
+ try {
86
+ loadedConfig = (await explorer.search(
87
+ configPath
88
+ )) as ConfigResult<ApolloConfigFormat>;
89
+ } catch (error) {
90
+ Debug.error(`A config file failed to load with options: ${JSON.stringify(
91
+ arguments[0]
92
+ )}.
93
+ The error was: ${error}`);
94
+ return null;
95
+ }
96
+
97
+ if (configPath && !loadedConfig) {
98
+ Debug.error(
99
+ `A config file failed to load at '${configPath}'. This is likely because this file is empty or malformed. For more information, please refer to: https://go.apollo.dev/t/config`
100
+ );
101
+ return null;
102
+ }
103
+
104
+ if (loadedConfig && loadedConfig.filepath.endsWith("package.json")) {
105
+ 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'
107
+ );
108
+ }
109
+
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
+ // add API key from the env
118
+ let engineConfig = {},
119
+ apiKey,
120
+ nameFromKey;
121
+
122
+ // loop over the list of possible .env files and try to parse for key
123
+ // and service name. Files are scanned and found values are preferred
124
+ // in order of appearance in `envFileNames`.
125
+ envFileNames.forEach((envFile) => {
126
+ const dotEnvPath = configPath
127
+ ? resolve(configPath, envFile)
128
+ : resolve(process.cwd(), envFile);
129
+
130
+ if (existsSync(dotEnvPath) && lstatSync(dotEnvPath).isFile()) {
131
+ const env: { [key: string]: string } = require("dotenv").parse(
132
+ readFileSync(dotEnvPath)
133
+ );
134
+ const legacyKey = env[legacyKeyEnvVar];
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;
147
+ }
148
+ });
149
+
150
+ if (apiKey) {
151
+ engineConfig = { engine: { apiKey } };
152
+ nameFromKey = getServiceFromKey(apiKey);
153
+ }
154
+
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
+ let { config, filepath } = loadedConfig;
217
+
218
+ // selectively apply defaults when loading the config
219
+ // this is just the includes/excludes defaults.
220
+ // These need to go on _all_ configs. That's why this is last.
221
+ if (config.client) config = merge({ client: DefaultClientConfig }, config);
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)));
228
+ }
@@ -0,0 +1,56 @@
1
+ import {
2
+ ApolloConfig,
3
+ ClientConfig,
4
+ ClientServiceConfig,
5
+ LocalServiceConfig,
6
+ ServiceConfig,
7
+ ApolloConfigFormat,
8
+ } from "./config";
9
+ import { ServiceSpecifier, ServiceIDAndTag } from "../engine";
10
+
11
+ export function isClientConfig(config: ApolloConfig): config is ClientConfig {
12
+ return config.isClient;
13
+ }
14
+
15
+ // checks the `config.client.service` object for a localSchemaFile
16
+ export function isLocalServiceConfig(
17
+ config: ClientServiceConfig
18
+ ): config is LocalServiceConfig {
19
+ return !!(config as LocalServiceConfig).localSchemaFile;
20
+ }
21
+
22
+ export function isServiceConfig(config: ApolloConfig): config is ServiceConfig {
23
+ return config.isService;
24
+ }
25
+
26
+ export function isServiceKey(key?: string) {
27
+ return key && /service:.*:.*/.test(key);
28
+ }
29
+
30
+ export function getServiceFromKey(key?: string) {
31
+ if (key) {
32
+ const [type, service] = key.split(":");
33
+ if (type === "service") return service;
34
+ }
35
+ return;
36
+ }
37
+
38
+ export function getGraphIdFromConfig(config: ApolloConfigFormat) {
39
+ if (config.service && config.service.name)
40
+ return parseServiceSpecifier(config.service.name)[0];
41
+ if (config.client) {
42
+ if (typeof config.client.service === "string") {
43
+ return parseServiceSpecifier(
44
+ config.client.service as ServiceSpecifier
45
+ )[0];
46
+ }
47
+ return config.client.service && config.client.service.name;
48
+ } else {
49
+ return undefined;
50
+ }
51
+ }
52
+
53
+ export function parseServiceSpecifier(specifier: ServiceSpecifier) {
54
+ const [id, tag] = specifier.split("@").map((x) => x.trim());
55
+ return [id, tag] as ServiceIDAndTag;
56
+ }