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
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
{
|
|
2
|
+
"allOf": [
|
|
3
|
+
{
|
|
4
|
+
"$ref": "#/definitions/baseConfig"
|
|
5
|
+
},
|
|
6
|
+
{
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"client": {
|
|
10
|
+
"$ref": "#/definitions/clientConfig"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"required": [
|
|
14
|
+
"client"
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"definitions": {
|
|
19
|
+
"clientConfig": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"properties": {
|
|
22
|
+
"service": {
|
|
23
|
+
"anyOf": [
|
|
24
|
+
{
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "The name of the Apollo Studio graph to use. Alternatively pass in an object to configure a local schema."
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"type": "object",
|
|
30
|
+
"properties": {
|
|
31
|
+
"name": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "The name your project will be referred to by the Apollo GraphQL extension."
|
|
34
|
+
},
|
|
35
|
+
"url": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"description": "URL of a GraphQL to use for the GraphQL Schema for this project. Needs introspection enabled."
|
|
38
|
+
},
|
|
39
|
+
"headers": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"additionalProperties": {
|
|
42
|
+
"type": "string"
|
|
43
|
+
},
|
|
44
|
+
"default": {},
|
|
45
|
+
"description": "Additional headers to send to the server."
|
|
46
|
+
},
|
|
47
|
+
"skipSSLValidation": {
|
|
48
|
+
"type": "boolean",
|
|
49
|
+
"default": false,
|
|
50
|
+
"description": "Skip SSL validation. May be required for self-signed certificates."
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
"required": [
|
|
54
|
+
"url"
|
|
55
|
+
],
|
|
56
|
+
"additionalProperties": false,
|
|
57
|
+
"description": "Configuration for using a local schema from a URL."
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"type": "object",
|
|
61
|
+
"properties": {
|
|
62
|
+
"name": {
|
|
63
|
+
"type": "string",
|
|
64
|
+
"description": "The name your project will be referred to by the Apollo GraphQL extension."
|
|
65
|
+
},
|
|
66
|
+
"localSchemaFile": {
|
|
67
|
+
"anyOf": [
|
|
68
|
+
{
|
|
69
|
+
"type": "string",
|
|
70
|
+
"description": "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."
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
"type": "array",
|
|
74
|
+
"items": {
|
|
75
|
+
"type": "string"
|
|
76
|
+
},
|
|
77
|
+
"description": "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."
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
"description": "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."
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
"required": [
|
|
84
|
+
"localSchemaFile"
|
|
85
|
+
],
|
|
86
|
+
"additionalProperties": false,
|
|
87
|
+
"description": "Configuration for using a local schema from a file."
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
"description": "A string to refer to a graph in Apollo Studio, or an object for a local schema."
|
|
91
|
+
},
|
|
92
|
+
"validationRules": {
|
|
93
|
+
"anyOf": [
|
|
94
|
+
{
|
|
95
|
+
"type": "array",
|
|
96
|
+
"description": "Additional validation rules to check for. To use this feature, please use a configuration file format that allows passing JavaScript objects."
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"description": "Additional validation rules to check for. To use this feature, please use a configuration file format that allows passing JavaScript objects."
|
|
100
|
+
},
|
|
101
|
+
"includes": {
|
|
102
|
+
"type": "array",
|
|
103
|
+
"items": {
|
|
104
|
+
"type": "string"
|
|
105
|
+
},
|
|
106
|
+
"description": "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."
|
|
107
|
+
},
|
|
108
|
+
"excludes": {
|
|
109
|
+
"type": "array",
|
|
110
|
+
"items": {
|
|
111
|
+
"type": "string"
|
|
112
|
+
},
|
|
113
|
+
"default": [
|
|
114
|
+
"**/node_modules",
|
|
115
|
+
"**/__tests__"
|
|
116
|
+
],
|
|
117
|
+
"description": "Files to exclude from this project. The Apollo GraphQL extension will not provide IntelliSense-like features in these files."
|
|
118
|
+
},
|
|
119
|
+
"tagName": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"default": "gql",
|
|
122
|
+
"description": "The name of the template literal tag or function used in JavaScript files to declare GraphQL Documents."
|
|
123
|
+
},
|
|
124
|
+
"clientOnlyDirectives": {
|
|
125
|
+
"description": "This option is no longer supported, please remove it from your configuration file."
|
|
126
|
+
},
|
|
127
|
+
"clientSchemaDirectives": {
|
|
128
|
+
"description": "This option is no longer supported, please remove it from your configuration file."
|
|
129
|
+
},
|
|
130
|
+
"statsWindow": {
|
|
131
|
+
"description": "This option is no longer supported, please remove it from your configuration file."
|
|
132
|
+
},
|
|
133
|
+
"name": {
|
|
134
|
+
"description": "This option is no longer supported, please remove it from your configuration file."
|
|
135
|
+
},
|
|
136
|
+
"referenceId": {
|
|
137
|
+
"description": "This option is no longer supported, please remove it from your configuration file."
|
|
138
|
+
},
|
|
139
|
+
"version": {
|
|
140
|
+
"description": "This option is no longer supported, please remove it from your configuration file."
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
"required": [
|
|
144
|
+
"service"
|
|
145
|
+
],
|
|
146
|
+
"additionalProperties": false,
|
|
147
|
+
"description": "Configuration for a Client project."
|
|
148
|
+
},
|
|
149
|
+
"engineConfig": {
|
|
150
|
+
"type": "object",
|
|
151
|
+
"properties": {
|
|
152
|
+
"endpoint": {
|
|
153
|
+
"type": "string",
|
|
154
|
+
"default": "https://graphql.api.apollographql.com/api/graphql",
|
|
155
|
+
"description": "The URL of the Apollo Studio API."
|
|
156
|
+
},
|
|
157
|
+
"apiKey": {
|
|
158
|
+
"type": "string",
|
|
159
|
+
"description": "The API key to use for Apollo Studio. If possible, use a `.env` file or `.env.local` file instead to store secrets like this."
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
"additionalProperties": false,
|
|
163
|
+
"description": "Network configuration for Apollo Studio API."
|
|
164
|
+
},
|
|
165
|
+
"baseConfig": {
|
|
166
|
+
"type": "object",
|
|
167
|
+
"properties": {
|
|
168
|
+
"engine": {
|
|
169
|
+
"$ref": "#/definitions/engineConfig",
|
|
170
|
+
"default": {},
|
|
171
|
+
"description": "Network configuration for Apollo Studio API."
|
|
172
|
+
},
|
|
173
|
+
"client": {
|
|
174
|
+
"description": "Configuration for a Client project."
|
|
175
|
+
},
|
|
176
|
+
"service": {
|
|
177
|
+
"description": "This option is no longer supported, please remove it from your configuration file."
|
|
178
|
+
}
|
|
179
|
+
},
|
|
180
|
+
"additionalProperties": false
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
"$schema": "http://json-schema.org/draft-07/schema#"
|
|
184
|
+
}
|
|
@@ -1,19 +1,43 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
const http = require("http");
|
|
3
2
|
const {
|
|
4
3
|
parseRequestParams,
|
|
5
4
|
createHandler,
|
|
6
5
|
} = require("graphql-http/lib/use/http");
|
|
7
6
|
const { buildSchema } = require("graphql");
|
|
8
7
|
const { Trie } = require("@wry/trie");
|
|
8
|
+
const { readFileSync } = require("fs");
|
|
9
|
+
const { join } = require("path");
|
|
9
10
|
|
|
10
|
-
function runMockServer(
|
|
11
|
+
async function runMockServer(
|
|
11
12
|
/** @type {number} */ port,
|
|
12
|
-
|
|
13
|
+
useSelfSignedCert = false,
|
|
14
|
+
onStart = (/** @type {string} */ baseUri) => {},
|
|
13
15
|
) {
|
|
14
16
|
const mocks = new Trie(false);
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param {import('node:http').RequestListener} listener
|
|
21
|
+
* @returns
|
|
22
|
+
*/
|
|
23
|
+
function createServer(listener) {
|
|
24
|
+
if (useSelfSignedCert) {
|
|
25
|
+
return require("node:https").createServer(
|
|
26
|
+
{
|
|
27
|
+
key: readFileSync(
|
|
28
|
+
join(__dirname, "../../sampleWorkspace/httpSchema/self-signed.key"),
|
|
29
|
+
),
|
|
30
|
+
cert: readFileSync(
|
|
31
|
+
join(__dirname, "../../sampleWorkspace/httpSchema/self-signed.crt"),
|
|
32
|
+
),
|
|
33
|
+
},
|
|
34
|
+
listener,
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
return require("node:http").createServer(listener);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const server = createServer(async (req, res) => {
|
|
17
41
|
if (req.url === "/apollo") {
|
|
18
42
|
if (req.method === "POST") {
|
|
19
43
|
await handleApolloPost(req, res);
|
|
@@ -33,8 +57,9 @@ function runMockServer(
|
|
|
33
57
|
|
|
34
58
|
console.log("Starting server...");
|
|
35
59
|
server.listen(port);
|
|
36
|
-
|
|
37
|
-
|
|
60
|
+
const baseUri = `${useSelfSignedCert ? "https" : "http"}://localhost:${port}`;
|
|
61
|
+
await onStart(baseUri);
|
|
62
|
+
console.log(`Server ready at: ${baseUri}`);
|
|
38
63
|
return {
|
|
39
64
|
[Symbol.dispose]() {
|
|
40
65
|
console.log("Closing server...");
|
|
@@ -45,8 +70,8 @@ function runMockServer(
|
|
|
45
70
|
|
|
46
71
|
/**
|
|
47
72
|
* Mock GraphQL Endpoint Handler
|
|
48
|
-
* @param {http.IncomingMessage} req
|
|
49
|
-
* @param {http.ServerResponse} res
|
|
73
|
+
* @param {import('node:http').IncomingMessage} req
|
|
74
|
+
* @param {import('node:http').ServerResponse} res
|
|
50
75
|
*/
|
|
51
76
|
async function handleApolloPost(req, res) {
|
|
52
77
|
const { operationName, variables } =
|
|
@@ -79,8 +104,8 @@ function runMockServer(
|
|
|
79
104
|
|
|
80
105
|
/**
|
|
81
106
|
* Handler to accept new GraphQL Mocks
|
|
82
|
-
* @param {http.IncomingMessage} req
|
|
83
|
-
* @param {http.ServerResponse} res
|
|
107
|
+
* @param {import('node:http').IncomingMessage} req
|
|
108
|
+
* @param {import('node:http').ServerResponse} res
|
|
84
109
|
*/
|
|
85
110
|
async function handleApolloPut(req, res) {
|
|
86
111
|
const body = await new Promise((resolve) => {
|
|
@@ -111,7 +136,8 @@ const schemaHandler = createHandler({
|
|
|
111
136
|
});
|
|
112
137
|
|
|
113
138
|
if (require.main === module) {
|
|
114
|
-
runMockServer(7096, require("./mocks.js").loadDefaultMocks);
|
|
139
|
+
runMockServer(7096, false, require("./mocks.js").loadDefaultMocks);
|
|
140
|
+
runMockServer(7097, true, require("./mocks.js").loadDefaultMocks);
|
|
115
141
|
}
|
|
116
142
|
|
|
117
143
|
module.exports.runMockServer = runMockServer;
|
package/src/__e2e__/mocks.js
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
await sendMock(
|
|
5
|
-
await sendMock(
|
|
6
|
-
await sendMock(port, GetSchemaByTag);
|
|
2
|
+
async function loadDefaultMocks(/** @type {string} */ baseUri) {
|
|
3
|
+
await sendMock(baseUri, FrontendUrlRoot);
|
|
4
|
+
await sendMock(baseUri, SchemaTagsAndFieldStats);
|
|
5
|
+
await sendMock(baseUri, GetSchemaByTag);
|
|
7
6
|
}
|
|
8
7
|
|
|
9
8
|
function sendMock(
|
|
10
|
-
/** @type {
|
|
9
|
+
/** @type {string} */ baseUri,
|
|
11
10
|
/** @type { { operationName: string, variables: Record<string, string>, response: unknown }} */ {
|
|
12
11
|
operationName,
|
|
13
12
|
variables,
|
|
14
13
|
response,
|
|
15
14
|
},
|
|
16
15
|
) {
|
|
17
|
-
return fetch(
|
|
16
|
+
return require("undici").fetch(`${baseUri}/apollo`, {
|
|
18
17
|
method: "PUT",
|
|
19
18
|
body: JSON.stringify({
|
|
20
19
|
operationName,
|
|
21
20
|
variables,
|
|
22
21
|
response,
|
|
23
22
|
}),
|
|
23
|
+
dispatcher: new (require("undici").Agent)({
|
|
24
|
+
connect: {
|
|
25
|
+
rejectUnauthorized: false,
|
|
26
|
+
},
|
|
27
|
+
}),
|
|
24
28
|
});
|
|
25
29
|
}
|
|
26
30
|
|
package/src/__e2e__/runTests.js
CHANGED
|
@@ -5,7 +5,7 @@ const { runMockServer } = require("./mockServer.js");
|
|
|
5
5
|
const { loadDefaultMocks } = require("./mocks.js");
|
|
6
6
|
|
|
7
7
|
async function main() {
|
|
8
|
-
|
|
8
|
+
const disposables = /**{@type Disposable[]}*/ [];
|
|
9
9
|
try {
|
|
10
10
|
// The folder containing the Extension Manifest package.json
|
|
11
11
|
// Passed to `--extensionDevelopmentPath`
|
|
@@ -18,8 +18,12 @@ async function main() {
|
|
|
18
18
|
const TEST_PORT = 7096;
|
|
19
19
|
process.env.APOLLO_ENGINE_ENDPOINT = "http://localhost:7096/apollo";
|
|
20
20
|
process.env.MOCK_SERVER_PORT = String(TEST_PORT);
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
disposables.push(
|
|
22
|
+
...(await Promise.all([
|
|
23
|
+
runMockServer(TEST_PORT, false, loadDefaultMocks),
|
|
24
|
+
runMockServer(TEST_PORT + 1, true, loadDefaultMocks),
|
|
25
|
+
])),
|
|
26
|
+
);
|
|
23
27
|
// Download VS Code, unzip it and run the integration test
|
|
24
28
|
const exitCode = await runTests({
|
|
25
29
|
extensionDevelopmentPath,
|
|
@@ -35,9 +39,7 @@ async function main() {
|
|
|
35
39
|
console.error("Failed to run tests");
|
|
36
40
|
process.exit(1);
|
|
37
41
|
} finally {
|
|
38
|
-
|
|
39
|
-
disposable[Symbol.dispose]();
|
|
40
|
-
}
|
|
42
|
+
disposables.forEach((d) => d[Symbol.dispose]());
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
45
|
|
package/src/build.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
const esbuild = require("esbuild");
|
|
2
|
+
const { zodToJsonSchema } = require("zod-to-json-schema");
|
|
3
|
+
const { writeFileSync } = require("fs");
|
|
4
|
+
const importFresh = require("import-fresh");
|
|
2
5
|
|
|
3
6
|
const production = process.argv.includes("--production");
|
|
4
7
|
const watch = process.argv.includes("--watch");
|
|
5
8
|
|
|
6
9
|
async function main() {
|
|
7
10
|
const ctx = await esbuild.context({
|
|
8
|
-
entryPoints: [
|
|
11
|
+
entryPoints: [
|
|
12
|
+
"src/extension.ts",
|
|
13
|
+
"src/language-server/server.ts",
|
|
14
|
+
"src/language-server/config/config.ts",
|
|
15
|
+
"src/language-server/config/cache-busting-resolver.js",
|
|
16
|
+
],
|
|
9
17
|
bundle: true,
|
|
10
18
|
format: "cjs",
|
|
11
19
|
minify: production,
|
|
@@ -19,6 +27,8 @@ async function main() {
|
|
|
19
27
|
plugins: [
|
|
20
28
|
/* add to the end of plugins array */
|
|
21
29
|
esbuildProblemMatcherPlugin,
|
|
30
|
+
buildJsonSchemaPlugin,
|
|
31
|
+
resolvePlugin,
|
|
22
32
|
],
|
|
23
33
|
});
|
|
24
34
|
if (watch) {
|
|
@@ -51,6 +61,48 @@ const esbuildProblemMatcherPlugin = {
|
|
|
51
61
|
},
|
|
52
62
|
};
|
|
53
63
|
|
|
64
|
+
const buildJsonSchemaPlugin = {
|
|
65
|
+
name: "build-json-schema",
|
|
66
|
+
setup(build) {
|
|
67
|
+
build.onEnd(() => {
|
|
68
|
+
const {
|
|
69
|
+
configSchema,
|
|
70
|
+
clientConfig,
|
|
71
|
+
// roverConfig,
|
|
72
|
+
engineConfig,
|
|
73
|
+
baseConfig,
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
} = importFresh("../lib/language-server/config/config.js");
|
|
76
|
+
|
|
77
|
+
const jsonSchema = zodToJsonSchema(configSchema, {
|
|
78
|
+
errorMessages: true,
|
|
79
|
+
definitions: {
|
|
80
|
+
clientConfig,
|
|
81
|
+
//roverConfig,
|
|
82
|
+
engineConfig,
|
|
83
|
+
baseConfig,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
writeFileSync(
|
|
87
|
+
"./schemas/apollo.config.schema.json",
|
|
88
|
+
JSON.stringify(jsonSchema, null, 2),
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const resolvePlugin = {
|
|
95
|
+
name: "resolve",
|
|
96
|
+
setup(build) {
|
|
97
|
+
build.onResolve(
|
|
98
|
+
{ filter: /^jsonc-parser$/ },
|
|
99
|
+
async ({ path, ...options }) => {
|
|
100
|
+
return build.resolve("jsonc-parser/lib/esm/main.js", options);
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
54
106
|
main().catch((e) => {
|
|
55
107
|
console.error(e);
|
|
56
108
|
process.exit(1);
|
|
@@ -37,9 +37,10 @@ Get detailed profile information about the current user (including the current u
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
test("wrong token", async () => {
|
|
40
|
+
const baseUri = `http://localhost:${mockPort}`;
|
|
40
41
|
try {
|
|
41
|
-
await mocks.sendMock(
|
|
42
|
-
await mocks.sendMock(
|
|
42
|
+
await mocks.sendMock(baseUri, mocks.GetSchemaByTag_WRONG_TOKEN);
|
|
43
|
+
await mocks.sendMock(baseUri, mocks.SchemaTagsAndFieldStats_WRONG_TOKEN);
|
|
43
44
|
|
|
44
45
|
const ext = getExtension();
|
|
45
46
|
ext.outputChannel.clear();
|
|
@@ -59,7 +60,7 @@ Invalid credentials provided
|
|
|
59
60
|
at new ApolloError`.trim(),
|
|
60
61
|
);
|
|
61
62
|
} finally {
|
|
62
|
-
await mocks.loadDefaultMocks(
|
|
63
|
+
await mocks.loadDefaultMocks(baseUri);
|
|
63
64
|
await reloadService();
|
|
64
65
|
}
|
|
65
66
|
});
|
|
@@ -174,7 +174,10 @@ Object {
|
|
|
174
174
|
expect(config?.client?.service).toEqual("hello");
|
|
175
175
|
});
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
// we skip these tests because ts-jest transpiles every `import` down to a `require` call,
|
|
178
|
+
// which messes up all the importing anyways.
|
|
179
|
+
// we have to rely on our E2E tests to ensure that config files resolve correctly
|
|
180
|
+
it.skip("loads config from a cjs file", async () => {
|
|
178
181
|
writeFilesToDir(dir, {
|
|
179
182
|
"apollo.config.cjs": `module.exports = {"client": {"service": "hello"} }`,
|
|
180
183
|
});
|
|
@@ -182,13 +185,43 @@ Object {
|
|
|
182
185
|
expect(config?.client?.service).toEqual("hello");
|
|
183
186
|
});
|
|
184
187
|
|
|
185
|
-
it("loads config from a mjs file", async () => {
|
|
188
|
+
it.skip("loads config from a mjs file", async () => {
|
|
186
189
|
writeFilesToDir(dir, {
|
|
187
190
|
"apollo.config.mjs": `export default {"client": {"service": "hello"} }`,
|
|
188
191
|
});
|
|
189
192
|
const config = await loadConfig({ configPath: dirPath });
|
|
190
193
|
expect(config?.client?.service).toEqual("hello");
|
|
191
194
|
});
|
|
195
|
+
|
|
196
|
+
it("loads config from a yml file", async () => {
|
|
197
|
+
writeFilesToDir(dir, {
|
|
198
|
+
"apollo.config.yml": `
|
|
199
|
+
client:
|
|
200
|
+
service: hello
|
|
201
|
+
`,
|
|
202
|
+
});
|
|
203
|
+
const config = await loadConfig({ configPath: dirPath });
|
|
204
|
+
expect(config?.client?.service).toEqual("hello");
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("loads config from a yaml file", async () => {
|
|
208
|
+
writeFilesToDir(dir, {
|
|
209
|
+
"apollo.config.yaml": `
|
|
210
|
+
client:
|
|
211
|
+
service: hello
|
|
212
|
+
`,
|
|
213
|
+
});
|
|
214
|
+
const config = await loadConfig({ configPath: dirPath });
|
|
215
|
+
expect(config?.client?.service).toEqual("hello");
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it("loads config from a json file", async () => {
|
|
219
|
+
writeFilesToDir(dir, {
|
|
220
|
+
"apollo.config.json": `{"client": /* testing jsonc */ {"service": "hello"} }`,
|
|
221
|
+
});
|
|
222
|
+
const config = await loadConfig({ configPath: dirPath });
|
|
223
|
+
expect(config?.client?.service).toEqual("hello");
|
|
224
|
+
});
|
|
192
225
|
});
|
|
193
226
|
|
|
194
227
|
describe("errors", () => {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
const { pathToFileURL } = require("node:url");
|
|
3
|
+
|
|
4
|
+
/** @import { ResolveContext, ResolutionResult, LoadResult, ImportContext } from "./cache-busting-resolver.types" */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} specifier
|
|
8
|
+
* @returns {string}
|
|
9
|
+
*/
|
|
10
|
+
function bustFileName(specifier) {
|
|
11
|
+
const url = pathToFileURL(specifier);
|
|
12
|
+
url.pathname = url.pathname + "." + Date.now() + ".js";
|
|
13
|
+
return url.toString();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
*
|
|
18
|
+
* @param {string} specifier
|
|
19
|
+
* @param {ResolveContext} context
|
|
20
|
+
* @param {(specifier: string,context: ResolveContext) => Promise<ResolutionResult>} nextResolve
|
|
21
|
+
* @returns {Promise<ResolutionResult>}
|
|
22
|
+
*/
|
|
23
|
+
async function resolve(specifier, context, nextResolve) {
|
|
24
|
+
if (context.importAttributes.as !== "cachebust") {
|
|
25
|
+
return nextResolve(specifier, context);
|
|
26
|
+
}
|
|
27
|
+
if (context.importAttributes.format) {
|
|
28
|
+
// no need to resolve at all, we have all necessary information
|
|
29
|
+
return {
|
|
30
|
+
url: bustFileName(specifier),
|
|
31
|
+
format: context.importAttributes.format,
|
|
32
|
+
importAttributes: context.importAttributes,
|
|
33
|
+
shortCircuit: true,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const result = await nextResolve(specifier, context);
|
|
37
|
+
return {
|
|
38
|
+
...result,
|
|
39
|
+
url: bustFileName(result.url),
|
|
40
|
+
importAttributes: context.importAttributes,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
*
|
|
46
|
+
* @param {string} url
|
|
47
|
+
* @param {ImportContext} context
|
|
48
|
+
* @param {(url: string, context: ImportContext) => Promise<LoadResult>} nextLoad
|
|
49
|
+
* @returns {Promise<LoadResult>}
|
|
50
|
+
*/
|
|
51
|
+
async function load(url, context, nextLoad) {
|
|
52
|
+
if (context.importAttributes.as !== "cachebust") {
|
|
53
|
+
return nextLoad(url, context);
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
format: context.format || "module",
|
|
57
|
+
shortCircuit: true,
|
|
58
|
+
source: context.importAttributes.contents,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
resolve,
|
|
64
|
+
load,
|
|
65
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
|
|
3
|
+
export type ImportAttributes =
|
|
4
|
+
| {
|
|
5
|
+
as: "cachebust";
|
|
6
|
+
contents: string;
|
|
7
|
+
format?: Format;
|
|
8
|
+
}
|
|
9
|
+
| { as?: undefined };
|
|
10
|
+
|
|
11
|
+
type Format =
|
|
12
|
+
| "builtin"
|
|
13
|
+
| "commonjs"
|
|
14
|
+
| "json"
|
|
15
|
+
| "module"
|
|
16
|
+
| "wasm"
|
|
17
|
+
| null
|
|
18
|
+
| undefined;
|
|
19
|
+
|
|
20
|
+
export interface ResolveContext {
|
|
21
|
+
conditions: string[];
|
|
22
|
+
importAttributes: ImportAttributes;
|
|
23
|
+
parentURL?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface ImportContext {
|
|
27
|
+
conditions: string[];
|
|
28
|
+
importAttributes: ImportAttributes;
|
|
29
|
+
format: Format;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ResolutionResult {
|
|
33
|
+
format: Format;
|
|
34
|
+
importAttributes?: ImportAttributes;
|
|
35
|
+
shortCircuit?: boolean;
|
|
36
|
+
url: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface LoadResult {
|
|
40
|
+
format: Format;
|
|
41
|
+
shortCircuit?: boolean;
|
|
42
|
+
source: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export {};
|