nuxt-graphql-middleware 5.0.0-alpha.2 → 5.0.0-alpha.20
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/README.md +101 -19
- package/dist/client/200.html +10 -10
- package/dist/client/404.html +10 -10
- package/dist/client/_nuxt/BM34SYth.js +1 -0
- package/dist/client/_nuxt/CROlboVl.js +1 -0
- package/dist/client/_nuxt/D5hBL5aZ.js +25 -0
- package/dist/client/_nuxt/FTbv7CO6.js +1 -0
- package/dist/client/_nuxt/builds/latest.json +1 -1
- package/dist/client/_nuxt/builds/meta/de61b0f7-ec5c-4f2f-addb-b5017c30afb1.json +1 -0
- package/dist/client/_nuxt/entry.Cn9qfNGa.css +1 -0
- package/dist/client/_nuxt/error-404.ehK72JOs.css +1 -0
- package/dist/client/_nuxt/error-500._g0akJim.css +1 -0
- package/dist/client/_nuxt/index.DGEN-H8t.css +1 -0
- package/dist/client/_nuxt/lIgCBhS_.js +2 -0
- package/dist/client/index.html +10 -10
- package/dist/client-options.d.mts +6 -0
- package/dist/client-options.mjs +5 -0
- package/dist/module.d.mts +74 -340
- package/dist/module.json +4 -4
- package/dist/module.mjs +1384 -589
- package/dist/runtime/components/CodeFrame.vue +52 -0
- package/dist/runtime/components/CodeFrame.vue.d.ts +7 -0
- package/dist/runtime/components/DevModeOverlay.vue +52 -0
- package/dist/runtime/components/DevModeOverlay.vue.d.ts +3 -0
- package/dist/runtime/components/ErrorExtensions.vue +21 -0
- package/dist/runtime/components/ErrorExtensions.vue.d.ts +5 -0
- package/dist/runtime/components/ErrorGroup.vue +78 -0
- package/dist/runtime/components/ErrorGroup.vue.d.ts +9 -0
- package/dist/runtime/composables/nuxtApp.d.ts +2 -2
- package/dist/runtime/composables/nuxtApp.js +21 -1
- package/dist/runtime/composables/useAsyncGraphqlQuery.d.ts +7 -7
- package/dist/runtime/composables/useAsyncGraphqlQuery.js +10 -2
- package/dist/runtime/composables/useGraphqlMutation.d.ts +4 -4
- package/dist/runtime/composables/useGraphqlMutation.js +1 -1
- package/dist/runtime/composables/useGraphqlQuery.d.ts +4 -4
- package/dist/runtime/composables/useGraphqlQuery.js +1 -1
- package/dist/runtime/composables/useGraphqlState.d.ts +1 -1
- package/dist/runtime/composables/useGraphqlState.js +1 -1
- package/dist/runtime/composables/useGraphqlUploadMutation.d.ts +4 -4
- package/dist/runtime/composables/useGraphqlUploadMutation.js +2 -2
- package/dist/runtime/css/output.css +1 -0
- package/dist/runtime/helpers/composables.d.ts +17 -20
- package/dist/runtime/helpers/composables.js +0 -5
- package/dist/runtime/plugins/devMode.d.ts +2 -0
- package/dist/runtime/plugins/devMode.js +23 -0
- package/dist/runtime/plugins/provideState.d.ts +1 -1
- package/dist/runtime/{serverHandler → server/api}/debug.js +3 -7
- package/dist/runtime/server/api/mutation.js +29 -0
- package/dist/runtime/server/api/query.js +30 -0
- package/dist/runtime/server/api/upload.d.ts +2 -0
- package/dist/runtime/{serverHandler → server/api}/upload.js +13 -11
- package/dist/runtime/{serverHandler → server}/helpers/index.d.ts +10 -12
- package/dist/runtime/{serverHandler → server}/helpers/index.js +9 -26
- package/dist/runtime/server/utils/doGraphqlRequest.d.ts +18 -0
- package/dist/runtime/server/utils/doGraphqlRequest.js +68 -0
- package/dist/runtime/server/utils/index.d.ts +1 -1
- package/dist/runtime/server/utils/index.js +1 -1
- package/dist/runtime/server/utils/useGraphqlMutation.d.ts +4 -4
- package/dist/runtime/server/utils/useGraphqlQuery.d.ts +4 -4
- package/dist/runtime/settings/index.d.ts +0 -14
- package/dist/runtime/settings/index.js +0 -6
- package/dist/runtime/types.d.ts +204 -3
- package/dist/server-options.d.mts +8 -0
- package/dist/server-options.mjs +5 -0
- package/dist/shared/nuxt-graphql-middleware.xfMm4rGk.d.mts +523 -0
- package/dist/types.d.mts +1 -7
- package/dist/utils.d.mts +15 -0
- package/dist/utils.mjs +18 -0
- package/package.json +64 -57
- package/dist/client/_nuxt/BS583yk8.js +0 -25
- package/dist/client/_nuxt/CZ2Qwgdk.js +0 -2
- package/dist/client/_nuxt/DpxjPVZy.js +0 -1
- package/dist/client/_nuxt/GOrnHr4p.js +0 -1
- package/dist/client/_nuxt/builds/meta/f823ebfd-daab-468f-8f6f-07a236da64bd.json +0 -1
- package/dist/client/_nuxt/entry.AjgXSF89.css +0 -1
- package/dist/client/_nuxt/error-404.BJkSn6RI.css +0 -1
- package/dist/client/_nuxt/error-500.TOCKLquH.css +0 -1
- package/dist/client/_nuxt/exxdaCPN.js +0 -1
- package/dist/client/_nuxt/index.D19Q16VT.css +0 -1
- package/dist/module.cjs +0 -5
- package/dist/module.d.ts +0 -358
- package/dist/runtime/clientOptions/index.d.ts +0 -2
- package/dist/runtime/clientOptions/index.js +0 -3
- package/dist/runtime/serverHandler/index.js +0 -78
- package/dist/runtime/serverHandler/tsconfig.json +0 -3
- package/dist/runtime/serverOptions/defineGraphqlServerOptions.d.ts +0 -3
- package/dist/runtime/serverOptions/defineGraphqlServerOptions.js +0 -3
- package/dist/runtime/serverOptions/index.d.ts +0 -2
- package/dist/runtime/serverOptions/index.js +0 -2
- package/dist/types.d.ts +0 -7
- /package/dist/runtime/{serverHandler → server/api}/debug.d.ts +0 -0
- /package/dist/runtime/{serverHandler/index.d.ts → server/api/mutation.d.ts} +0 -0
- /package/dist/runtime/{serverHandler/upload.d.ts → server/api/query.d.ts} +0 -0
package/dist/module.mjs
CHANGED
|
@@ -1,94 +1,27 @@
|
|
|
1
|
-
import { loadSchema } from '@graphql-tools/load';
|
|
2
1
|
import { fileURLToPath } from 'url';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { useLogger, resolveFiles, defineNuxtModule, resolveAlias, createResolver, addImports, addServerImports, addTemplate, addServerHandler, addPlugin } from '@nuxt/kit';
|
|
6
|
-
import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
|
|
7
|
-
import { existsSync } from 'fs';
|
|
8
|
-
import { GraphqlMiddlewareTemplate } from '../dist/runtime/settings/index.js';
|
|
9
|
-
import { promises, existsSync as existsSync$1 } from 'node:fs';
|
|
10
|
-
import { generate } from '@graphql-codegen/cli';
|
|
11
|
-
import * as PluginSchemaAst from '@graphql-codegen/schema-ast';
|
|
2
|
+
import { useLogger, addTemplate, addServerTemplate, addTypeTemplate, createResolver, resolveAlias, resolveFiles, addPlugin, addServerHandler, addImports, addServerImports, useNitro, defineNuxtModule } from '@nuxt/kit';
|
|
3
|
+
import { existsSync, promises } from 'node:fs';
|
|
12
4
|
import { basename } from 'node:path';
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
5
|
+
import { relative, join } from 'pathe';
|
|
6
|
+
import { printSourceLocation, parse, Source, OperationTypeNode } from 'graphql';
|
|
7
|
+
import { Generator, FieldNotFoundError, TypeNotFoundError, FragmentNotFoundError } from 'graphql-typescript-deluxe';
|
|
8
|
+
import color from 'picocolors';
|
|
17
9
|
import { validateGraphQlDocuments } from '@graphql-tools/utils';
|
|
10
|
+
import fs from 'node:fs/promises';
|
|
11
|
+
import { generate } from '@graphql-codegen/cli';
|
|
12
|
+
import * as PluginSchemaAst from '@graphql-codegen/schema-ast';
|
|
13
|
+
import { loadSchema } from '@graphql-tools/load';
|
|
14
|
+
import { defu } from 'defu';
|
|
15
|
+
import micromatch from 'micromatch';
|
|
16
|
+
import { ConfirmPrompt } from '@clack/core';
|
|
17
|
+
import isUnicodeSupported from 'is-unicode-supported';
|
|
18
|
+
import { existsSync as existsSync$1 } from 'fs';
|
|
19
|
+
import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
|
|
18
20
|
|
|
19
21
|
const name = "nuxt-graphql-middleware";
|
|
20
|
-
const version = "5.0.0-alpha.
|
|
21
|
-
|
|
22
|
-
const DEVTOOLS_UI_ROUTE = "/__nuxt-graphql-middleware";
|
|
23
|
-
const DEVTOOLS_UI_LOCAL_PORT = 3300;
|
|
24
|
-
function setupDevToolsUI(nuxt, clientPath) {
|
|
25
|
-
const isProductionBuild = existsSync(clientPath);
|
|
26
|
-
if (isProductionBuild) {
|
|
27
|
-
nuxt.hook("vite:serverCreated", async (server) => {
|
|
28
|
-
const sirv = await import('sirv').then((r) => r.default || r);
|
|
29
|
-
server.middlewares.use(
|
|
30
|
-
DEVTOOLS_UI_ROUTE,
|
|
31
|
-
sirv(clientPath, { dev: true, single: true })
|
|
32
|
-
);
|
|
33
|
-
});
|
|
34
|
-
} else {
|
|
35
|
-
nuxt.hook("vite:extendConfig", (config) => {
|
|
36
|
-
config.server = config.server || {};
|
|
37
|
-
config.server.proxy = config.server.proxy || {};
|
|
38
|
-
config.server.proxy[DEVTOOLS_UI_ROUTE] = {
|
|
39
|
-
target: "http://localhost:" + DEVTOOLS_UI_LOCAL_PORT + DEVTOOLS_UI_ROUTE,
|
|
40
|
-
changeOrigin: true,
|
|
41
|
-
followRedirects: true,
|
|
42
|
-
rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
|
|
43
|
-
};
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
47
|
-
tabs.push({
|
|
48
|
-
// unique identifier
|
|
49
|
-
name: "nuxt-graphql-middleware",
|
|
50
|
-
// title to display in the tab
|
|
51
|
-
title: "GraphQL Middleware",
|
|
52
|
-
// any icon from Iconify, or a URL to an image
|
|
53
|
-
icon: "akar-icons:graphql-fill",
|
|
54
|
-
// iframe view
|
|
55
|
-
view: {
|
|
56
|
-
type: "iframe",
|
|
57
|
-
src: DEVTOOLS_UI_ROUTE
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function pluginLoader(name) {
|
|
64
|
-
switch (name) {
|
|
65
|
-
case "@graphql-codegen/schema-ast":
|
|
66
|
-
return Promise.resolve(PluginSchemaAst);
|
|
67
|
-
}
|
|
68
|
-
throw new Error(`graphql-codegen plugin not found: ${name}`);
|
|
69
|
-
}
|
|
70
|
-
function generateSchema(moduleOptions, dest, writeToDisk) {
|
|
71
|
-
const pluginConfig = moduleOptions.codegenSchemaConfig?.urlSchemaOptions;
|
|
72
|
-
const schemaAstConfig = moduleOptions.codegenSchemaConfig?.schemaAstConfig || {
|
|
73
|
-
sort: true
|
|
74
|
-
};
|
|
75
|
-
const config = {
|
|
76
|
-
schema: moduleOptions.graphqlEndpoint,
|
|
77
|
-
pluginLoader,
|
|
78
|
-
silent: true,
|
|
79
|
-
errorsOnly: true,
|
|
80
|
-
config: pluginConfig,
|
|
81
|
-
generates: {
|
|
82
|
-
[dest]: {
|
|
83
|
-
plugins: ["schema-ast"],
|
|
84
|
-
config: schemaAstConfig
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
return generate(config, writeToDisk).then((v) => v[0]);
|
|
89
|
-
}
|
|
22
|
+
const version = "5.0.0-alpha.20";
|
|
90
23
|
|
|
91
|
-
const logger = useLogger(
|
|
24
|
+
const logger = useLogger("nuxt-graphql-middleware");
|
|
92
25
|
const defaultOptions = {
|
|
93
26
|
downloadSchema: true,
|
|
94
27
|
schemaPath: "~~/schema.graphql",
|
|
@@ -97,150 +30,29 @@ const defaultOptions = {
|
|
|
97
30
|
debug: false,
|
|
98
31
|
includeComposables: true,
|
|
99
32
|
documents: [],
|
|
100
|
-
devtools: true
|
|
33
|
+
devtools: true,
|
|
34
|
+
errorOverlay: true,
|
|
35
|
+
graphqlConfigFilePath: "./graphql.config.ts"
|
|
101
36
|
};
|
|
102
37
|
function validateOptions(options) {
|
|
103
38
|
if (!options.graphqlEndpoint) {
|
|
104
39
|
throw new Error("Missing graphqlEndpoint.");
|
|
105
40
|
}
|
|
106
41
|
}
|
|
107
|
-
async function getSchemaPath(schemaPath, options, resolver, writeToDisk = false) {
|
|
108
|
-
const dest = resolver(schemaPath);
|
|
109
|
-
if (!options.downloadSchema) {
|
|
110
|
-
const fileExists2 = await promises.access(dest).then(() => true).catch(() => false);
|
|
111
|
-
if (!fileExists2) {
|
|
112
|
-
logger.error(
|
|
113
|
-
'"downloadSchema" is set to false but no schema exists at ' + dest
|
|
114
|
-
);
|
|
115
|
-
throw new Error("Missing GraphQL schema.");
|
|
116
|
-
}
|
|
117
|
-
const schemaContent = await promises.readFile(dest).then((v) => v.toString());
|
|
118
|
-
return { schemaPath, schemaContent };
|
|
119
|
-
}
|
|
120
|
-
if (!options.graphqlEndpoint) {
|
|
121
|
-
throw new Error("Missing graphqlEndpoint config.");
|
|
122
|
-
}
|
|
123
|
-
const result = await generateSchema(options, dest, writeToDisk);
|
|
124
|
-
return { schemaPath, schemaContent: result.content };
|
|
125
|
-
}
|
|
126
42
|
const fileExists = (path, extensions = ["js", "ts", "mjs"]) => {
|
|
127
43
|
if (!path) {
|
|
128
44
|
return null;
|
|
129
|
-
} else if (existsSync
|
|
45
|
+
} else if (existsSync(path)) {
|
|
130
46
|
return path;
|
|
131
47
|
}
|
|
132
48
|
const extension = extensions.find(
|
|
133
|
-
(extension2) => existsSync
|
|
49
|
+
(extension2) => existsSync(`${path}.${extension2}`)
|
|
134
50
|
);
|
|
135
51
|
return extension ? `${path}.${extension}` : null;
|
|
136
52
|
};
|
|
137
53
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
query: {},
|
|
141
|
-
mutation: {},
|
|
142
|
-
subscription: {}
|
|
143
|
-
};
|
|
144
|
-
for (const op of ops) {
|
|
145
|
-
result[op.operationType][op.graphqlName] = {
|
|
146
|
-
hasVariables: op.hasVariables,
|
|
147
|
-
variablesOptional: !op.needsVariables
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
return result;
|
|
151
|
-
}
|
|
152
|
-
function buildOperationTypeCode(operationMetadata, typeName, serverApiPrefix) {
|
|
153
|
-
const imports = [];
|
|
154
|
-
const resultTypes = [];
|
|
155
|
-
let code = "";
|
|
156
|
-
let nitroCode = "";
|
|
157
|
-
const operationNames = Object.keys(operationMetadata);
|
|
158
|
-
if (operationNames.length === 0) {
|
|
159
|
-
return { code, nitroCode, imports, resultTypes };
|
|
160
|
-
}
|
|
161
|
-
const lines = [];
|
|
162
|
-
const nitroLines = [];
|
|
163
|
-
for (const name of operationNames) {
|
|
164
|
-
const nameResult = pascalCase(`${name}${typeName}`);
|
|
165
|
-
const nameVariables = pascalCase(`${name}${typeName}Variables`);
|
|
166
|
-
resultTypes.push(nameResult);
|
|
167
|
-
imports.push(nameResult);
|
|
168
|
-
const { hasVariables, variablesOptional } = operationMetadata[name];
|
|
169
|
-
if (hasVariables) {
|
|
170
|
-
imports.push(nameVariables);
|
|
171
|
-
}
|
|
172
|
-
const variablesType = hasVariables ? nameVariables : "null";
|
|
173
|
-
lines.push(
|
|
174
|
-
` ${name}: [${variablesType}, ${variablesOptional ? "true" : "false"}, ${nameResult}]`
|
|
175
|
-
);
|
|
176
|
-
nitroLines.push(`
|
|
177
|
-
'${serverApiPrefix}/${typeName.toLowerCase()}/${name}': {
|
|
178
|
-
'default': GraphqlResponse<${nameResult}>
|
|
179
|
-
}`);
|
|
180
|
-
}
|
|
181
|
-
code += ` export type GraphqlMiddleware${typeName} = {
|
|
182
|
-
${lines.join(",\n")}
|
|
183
|
-
}
|
|
184
|
-
`;
|
|
185
|
-
nitroCode += nitroLines.join("\n");
|
|
186
|
-
return { code, nitroCode, imports, resultTypes };
|
|
187
|
-
}
|
|
188
|
-
function generateContextTemplate(collectedOperations, serverApiPrefix) {
|
|
189
|
-
const grouped = groupOperationsByType(collectedOperations);
|
|
190
|
-
const queryResult = buildOperationTypeCode(
|
|
191
|
-
grouped.query,
|
|
192
|
-
"Query",
|
|
193
|
-
serverApiPrefix
|
|
194
|
-
);
|
|
195
|
-
const mutationResult = buildOperationTypeCode(
|
|
196
|
-
grouped.mutation,
|
|
197
|
-
"Mutation",
|
|
198
|
-
serverApiPrefix
|
|
199
|
-
);
|
|
200
|
-
const subscriptionResult = buildOperationTypeCode(
|
|
201
|
-
grouped.subscription,
|
|
202
|
-
"Subscription",
|
|
203
|
-
serverApiPrefix
|
|
204
|
-
);
|
|
205
|
-
const allImports = [
|
|
206
|
-
...queryResult.imports,
|
|
207
|
-
...mutationResult.imports,
|
|
208
|
-
...subscriptionResult.imports
|
|
209
|
-
];
|
|
210
|
-
const allResultTypes = [
|
|
211
|
-
...queryResult.resultTypes,
|
|
212
|
-
...mutationResult.resultTypes,
|
|
213
|
-
...subscriptionResult.resultTypes
|
|
214
|
-
];
|
|
215
|
-
const combinedCode = [
|
|
216
|
-
queryResult.code,
|
|
217
|
-
mutationResult.code,
|
|
218
|
-
subscriptionResult.code
|
|
219
|
-
].join("\n");
|
|
220
|
-
const combinedNitroCode = [
|
|
221
|
-
queryResult.nitroCode,
|
|
222
|
-
mutationResult.nitroCode,
|
|
223
|
-
subscriptionResult.nitroCode
|
|
224
|
-
].join("\n");
|
|
225
|
-
return `
|
|
226
|
-
import type { GraphqlResponse } from '#graphql-middleware-server-options-build'
|
|
227
|
-
import type {
|
|
228
|
-
${allImports.join(",\n ")}
|
|
229
|
-
} from './../graphql-operations'
|
|
230
|
-
|
|
231
|
-
declare module '#nuxt-graphql-middleware/generated-types' {
|
|
232
|
-
export type GraphqlMiddlewareResponseUnion = ${allResultTypes.join(" | ")}
|
|
233
|
-
${combinedCode}
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
declare module 'nitropack' {
|
|
237
|
-
interface InternalApi {
|
|
238
|
-
${combinedNitroCode}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
`;
|
|
242
|
-
}
|
|
243
|
-
|
|
54
|
+
const SYMBOL_CROSS = "x";
|
|
55
|
+
const SYMBOL_CHECK = "\u2714";
|
|
244
56
|
function getMaxLengths(entries) {
|
|
245
57
|
let name = 0;
|
|
246
58
|
let path = 0;
|
|
@@ -263,11 +75,11 @@ function logAllEntries(entries) {
|
|
|
263
75
|
let prevHadError = false;
|
|
264
76
|
for (const entry of entries) {
|
|
265
77
|
const hasErrors = entry.errors.length > 0;
|
|
266
|
-
const icon = hasErrors ?
|
|
78
|
+
const icon = hasErrors ? color.red(SYMBOL_CROSS) : color.green(SYMBOL_CHECK);
|
|
267
79
|
const type = entry.type.padEnd(lengths.type);
|
|
268
|
-
const namePadded =
|
|
269
|
-
const name = hasErrors ?
|
|
270
|
-
const path =
|
|
80
|
+
const namePadded = color.bold(entry.name.padEnd(lengths.name));
|
|
81
|
+
const name = hasErrors ? color.red(namePadded) : color.green(namePadded);
|
|
82
|
+
const path = color.dim(entry.path);
|
|
271
83
|
const parts = [icon, type, name, path];
|
|
272
84
|
if (hasErrors && !prevHadError) {
|
|
273
85
|
process.stdout.write("-".repeat(process.stdout.columns) + "\n");
|
|
@@ -276,10 +88,10 @@ function logAllEntries(entries) {
|
|
|
276
88
|
if (hasErrors) {
|
|
277
89
|
const errorLines = [];
|
|
278
90
|
entry.errors.forEach((error) => {
|
|
279
|
-
let output =
|
|
91
|
+
let output = color.red(error.message);
|
|
280
92
|
if (error.source && error.locations) {
|
|
281
93
|
for (const location of error.locations) {
|
|
282
|
-
output += "\n\n" +
|
|
94
|
+
output += "\n\n" + color.red(printSourceLocation(error.source, location));
|
|
283
95
|
}
|
|
284
96
|
}
|
|
285
97
|
errorLines.push(output);
|
|
@@ -293,6 +105,7 @@ function logAllEntries(entries) {
|
|
|
293
105
|
}
|
|
294
106
|
logger.restoreStd();
|
|
295
107
|
}
|
|
108
|
+
|
|
296
109
|
class CollectedFile {
|
|
297
110
|
filePath;
|
|
298
111
|
fileContents;
|
|
@@ -306,6 +119,9 @@ class CollectedFile {
|
|
|
306
119
|
}
|
|
307
120
|
static async fromFilePath(filePath) {
|
|
308
121
|
const content = (await promises.readFile(filePath)).toString();
|
|
122
|
+
if (!content) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
309
125
|
return new CollectedFile(filePath, content, true);
|
|
310
126
|
}
|
|
311
127
|
/**
|
|
@@ -324,17 +140,37 @@ class CollectedFile {
|
|
|
324
140
|
return false;
|
|
325
141
|
}
|
|
326
142
|
}
|
|
143
|
+
|
|
327
144
|
class Collector {
|
|
328
|
-
constructor(schema,
|
|
145
|
+
constructor(schema, helper) {
|
|
329
146
|
this.schema = schema;
|
|
330
|
-
this.
|
|
331
|
-
|
|
332
|
-
|
|
147
|
+
this.helper = helper;
|
|
148
|
+
const mappedOptions = { ...helper.options.codegenConfig };
|
|
149
|
+
if (!mappedOptions.output) {
|
|
150
|
+
mappedOptions.output = {};
|
|
151
|
+
}
|
|
152
|
+
if (!mappedOptions.output.buildTypeDocFilePath) {
|
|
153
|
+
mappedOptions.output.buildTypeDocFilePath = (filePath) => {
|
|
154
|
+
if (filePath.startsWith("/")) {
|
|
155
|
+
return this.filePathToBuildRelative(filePath);
|
|
156
|
+
}
|
|
157
|
+
return filePath;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
this.generator = new Generator(schema, mappedOptions);
|
|
333
161
|
}
|
|
334
162
|
/**
|
|
335
163
|
* All collected files.
|
|
336
164
|
*/
|
|
337
165
|
files = /* @__PURE__ */ new Map();
|
|
166
|
+
/**
|
|
167
|
+
* All documents provided by hooks.
|
|
168
|
+
*/
|
|
169
|
+
hookDocuments = /* @__PURE__ */ new Map();
|
|
170
|
+
/**
|
|
171
|
+
* All file paths provided by hooks.
|
|
172
|
+
*/
|
|
173
|
+
hookFiles = /* @__PURE__ */ new Set();
|
|
338
174
|
/**
|
|
339
175
|
* The code generator.
|
|
340
176
|
*/
|
|
@@ -348,45 +184,78 @@ class Collector {
|
|
|
348
184
|
*/
|
|
349
185
|
rpcItems = /* @__PURE__ */ new Map();
|
|
350
186
|
/**
|
|
351
|
-
* The
|
|
352
|
-
*/
|
|
353
|
-
outputTypes = "";
|
|
354
|
-
/**
|
|
355
|
-
* The generated oeprations file.
|
|
356
|
-
*/
|
|
357
|
-
outputOperations = "";
|
|
358
|
-
/**
|
|
359
|
-
* The generated context template file.
|
|
187
|
+
* The registered templates.
|
|
360
188
|
*/
|
|
361
|
-
|
|
189
|
+
templates = [];
|
|
362
190
|
/**
|
|
363
|
-
*
|
|
191
|
+
* The generated template contents.
|
|
364
192
|
*/
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
193
|
+
templateResult = /* @__PURE__ */ new Map();
|
|
194
|
+
isInitialised = false;
|
|
195
|
+
async reset() {
|
|
196
|
+
this.files.clear();
|
|
197
|
+
this.generator.reset();
|
|
198
|
+
this.operationTimestamps.clear();
|
|
199
|
+
this.rpcItems.clear();
|
|
200
|
+
}
|
|
201
|
+
async updateSchema(schema) {
|
|
202
|
+
this.schema = schema;
|
|
203
|
+
this.generator.updateSchema(schema);
|
|
204
|
+
await this.reset();
|
|
205
|
+
await this.initDocuments();
|
|
206
|
+
}
|
|
207
|
+
filePathToBuildRelative(filePath) {
|
|
208
|
+
return "./" + this.helper.toModuleBuildRelative(filePath);
|
|
209
|
+
}
|
|
210
|
+
filePathToSourceRelative(filePath) {
|
|
211
|
+
if (filePath.startsWith("/")) {
|
|
212
|
+
return "./" + relative(process.cwd(), filePath);
|
|
213
|
+
}
|
|
214
|
+
return filePath;
|
|
368
215
|
}
|
|
369
216
|
operationToLogEntry(operation, errors) {
|
|
370
217
|
return {
|
|
371
218
|
name: operation.graphqlName,
|
|
372
219
|
type: operation.operationType,
|
|
373
|
-
path: operation.filePath,
|
|
220
|
+
path: this.filePathToSourceRelative(operation.filePath),
|
|
374
221
|
errors
|
|
375
222
|
};
|
|
376
223
|
}
|
|
224
|
+
getTemplate(template, type) {
|
|
225
|
+
const content = this.templateResult.get(template + "-" + type);
|
|
226
|
+
if (content === void 0) {
|
|
227
|
+
throw new Error(`Missing template content: ${template}`);
|
|
228
|
+
}
|
|
229
|
+
return content;
|
|
230
|
+
}
|
|
377
231
|
/**
|
|
378
232
|
* Executes code gen and performs validation for operations.
|
|
379
233
|
*/
|
|
380
|
-
buildState() {
|
|
234
|
+
async buildState() {
|
|
381
235
|
const output = this.generator.build();
|
|
382
236
|
const operations = output.getCollectedOperations();
|
|
383
237
|
const generatedCode = output.getGeneratedCode();
|
|
384
|
-
this.
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
238
|
+
this.templates.forEach((template) => {
|
|
239
|
+
const path = template.options.path;
|
|
240
|
+
if (template.build) {
|
|
241
|
+
this.templateResult.set(
|
|
242
|
+
path + "-default",
|
|
243
|
+
this.helper.processTemplate(
|
|
244
|
+
template.options.path,
|
|
245
|
+
template.build(output, this.helper, this)
|
|
246
|
+
)
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
if (template.buildTypes) {
|
|
250
|
+
this.templateResult.set(
|
|
251
|
+
template.options.path + "-types",
|
|
252
|
+
this.helper.processTemplate(
|
|
253
|
+
template.options.path,
|
|
254
|
+
template.buildTypes(output, this.helper, this)
|
|
255
|
+
)
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
390
259
|
const fragmentMap = /* @__PURE__ */ new Map();
|
|
391
260
|
const operationSourceMap = /* @__PURE__ */ new Map();
|
|
392
261
|
for (const code of generatedCode) {
|
|
@@ -415,90 +284,191 @@ class Collector {
|
|
|
415
284
|
} else {
|
|
416
285
|
this.operationTimestamps.set(operation.graphqlName, operation.timestamp);
|
|
417
286
|
}
|
|
418
|
-
|
|
287
|
+
const shouldLog = errors.length || !this.helper.options.logOnlyErrors;
|
|
288
|
+
if (shouldLog) {
|
|
289
|
+
logEntries.push(this.operationToLogEntry(operation, errors));
|
|
290
|
+
}
|
|
419
291
|
}
|
|
420
|
-
logAllEntries(
|
|
292
|
+
logAllEntries(
|
|
293
|
+
logEntries.sort((a, b) => {
|
|
294
|
+
return a.type.localeCompare(b.type) || a.name.localeCompare(b.name);
|
|
295
|
+
})
|
|
296
|
+
);
|
|
421
297
|
if (hasErrors) {
|
|
422
298
|
throw new Error("GraphQL errors");
|
|
423
299
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
300
|
+
await this.helper.nuxt.callHook("nuxt-graphql-middleware:build", { output });
|
|
301
|
+
if (this.helper.isDev) {
|
|
302
|
+
for (const code of generatedCode) {
|
|
303
|
+
const id = `${code.identifier}_${code.graphqlName}`;
|
|
304
|
+
if (code.identifier === "fragment" || code.identifier === "mutation" || code.identifier === "query") {
|
|
305
|
+
if (this.rpcItems.get(id)?.timestamp === code.timestamp) {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
const fragmentDepdendencies = code.getGraphQLFragmentDependencies().map((name) => fragmentMap.get(name) || "").join("\n\n");
|
|
309
|
+
this.rpcItems.set(id, {
|
|
310
|
+
id,
|
|
311
|
+
timestamp: code.timestamp,
|
|
312
|
+
source: code.source + "\n\n" + fragmentDepdendencies,
|
|
313
|
+
name: code.graphqlName,
|
|
314
|
+
filePath: code.filePath,
|
|
315
|
+
identifier: code.identifier
|
|
316
|
+
});
|
|
429
317
|
}
|
|
430
|
-
const fragmentDepdendencies = code.getGraphQLFragmentDependencies().map((name) => fragmentMap.get(name) || "").join("\n\n");
|
|
431
|
-
this.rpcItems.set(id, {
|
|
432
|
-
id,
|
|
433
|
-
timestamp: code.timestamp,
|
|
434
|
-
source: code.source + "\n\n" + fragmentDepdendencies,
|
|
435
|
-
name: code.graphqlName,
|
|
436
|
-
filePath: code.filePath,
|
|
437
|
-
identifier: code.identifier
|
|
438
|
-
});
|
|
439
318
|
}
|
|
440
319
|
}
|
|
441
320
|
}
|
|
321
|
+
buildErrorMessage(error) {
|
|
322
|
+
let output = "";
|
|
323
|
+
if (error instanceof FieldNotFoundError || error instanceof TypeNotFoundError || error instanceof FragmentNotFoundError) {
|
|
324
|
+
const filePath = error.context?.filePath;
|
|
325
|
+
const file = filePath ? this.files.get(filePath) : null;
|
|
326
|
+
if (filePath) {
|
|
327
|
+
output += ` | ${this.filePathToSourceRelative(filePath)}
|
|
328
|
+
`;
|
|
329
|
+
}
|
|
330
|
+
output += "\n" + error.message + "\n\n";
|
|
331
|
+
if (file) {
|
|
332
|
+
output += file.fileContents;
|
|
333
|
+
}
|
|
334
|
+
} else if (error instanceof Error) {
|
|
335
|
+
output += "\n" + error.message;
|
|
336
|
+
}
|
|
337
|
+
return output;
|
|
338
|
+
}
|
|
339
|
+
logError(error) {
|
|
340
|
+
let output = `${SYMBOL_CROSS}`;
|
|
341
|
+
output += this.buildErrorMessage(error);
|
|
342
|
+
logger.error(color.red(output));
|
|
343
|
+
}
|
|
442
344
|
/**
|
|
443
|
-
*
|
|
345
|
+
* Initialise the collector.
|
|
346
|
+
*
|
|
347
|
+
* In dev mode, the method will call itself recursively until all documents
|
|
348
|
+
* are valid.
|
|
349
|
+
*
|
|
350
|
+
* If not in dev mode the method will throw an error when documents are not
|
|
351
|
+
* valid.
|
|
444
352
|
*/
|
|
445
|
-
async
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
353
|
+
async init() {
|
|
354
|
+
try {
|
|
355
|
+
await this.initDocuments();
|
|
356
|
+
} catch {
|
|
357
|
+
if (this.helper.isDev) {
|
|
358
|
+
const shouldRevalidate = await this.helper.prompt.confirm(
|
|
359
|
+
"Do you want to revalidate the GraphQL documents?"
|
|
360
|
+
);
|
|
361
|
+
if (shouldRevalidate === "yes") {
|
|
362
|
+
await this.reset();
|
|
363
|
+
await this.init();
|
|
364
|
+
this.isInitialised = true;
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
throw new Error("Graphql document validation failed.");
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
addHookDocument(identifier, source) {
|
|
372
|
+
this.hookDocuments.set("hook:" + identifier, source);
|
|
373
|
+
}
|
|
374
|
+
async addOrUpdateHookDocument(identifier, source) {
|
|
375
|
+
const fullIdentifier = "hook:" + identifier;
|
|
376
|
+
const exists = this.hookDocuments.has(fullIdentifier);
|
|
377
|
+
this.hookDocuments.set(fullIdentifier, source);
|
|
378
|
+
if (exists && this.isInitialised) {
|
|
379
|
+
this.generator.update({
|
|
380
|
+
filePath: fullIdentifier,
|
|
381
|
+
document: source
|
|
449
382
|
});
|
|
383
|
+
await this.buildState();
|
|
450
384
|
}
|
|
451
|
-
|
|
385
|
+
}
|
|
386
|
+
addHookFile(filePath) {
|
|
387
|
+
this.hookFiles.add(filePath);
|
|
452
388
|
}
|
|
453
389
|
/**
|
|
454
390
|
* Initialise the collector.
|
|
455
391
|
*/
|
|
456
|
-
async
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
392
|
+
async initDocuments() {
|
|
393
|
+
try {
|
|
394
|
+
const files = await this.helper.getImportPatternFiles();
|
|
395
|
+
for (const filePath of files) {
|
|
396
|
+
await this.addFile(filePath);
|
|
397
|
+
}
|
|
398
|
+
const nuxtConfigDocuments = this.helper.options.documents.join("\n\n");
|
|
399
|
+
if (nuxtConfigDocuments.length) {
|
|
400
|
+
const filePath = this.helper.paths.nuxtConfig;
|
|
401
|
+
const file = new CollectedFile(filePath, nuxtConfigDocuments, false);
|
|
402
|
+
this.files.set(filePath, file);
|
|
403
|
+
this.generator.add({
|
|
404
|
+
filePath,
|
|
405
|
+
documentNode: file.parsed
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
const hookDocuments = [...this.hookDocuments.entries()];
|
|
409
|
+
hookDocuments.forEach(([identifier, source]) => {
|
|
410
|
+
const file = new CollectedFile(identifier, source, false);
|
|
411
|
+
this.files.set(identifier, file);
|
|
412
|
+
this.generator.add({
|
|
413
|
+
filePath: identifier,
|
|
414
|
+
documentNode: file.parsed
|
|
415
|
+
});
|
|
468
416
|
});
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
417
|
+
for (const filePath of this.hookFiles) {
|
|
418
|
+
await this.addFile(filePath);
|
|
419
|
+
}
|
|
420
|
+
await this.buildState();
|
|
421
|
+
logger.success("All GraphQL documents are valid.");
|
|
422
|
+
} catch (e) {
|
|
423
|
+
this.logError(e);
|
|
424
|
+
throw new Error("GraphQL document validation failed.");
|
|
425
|
+
}
|
|
472
426
|
}
|
|
473
427
|
/**
|
|
474
428
|
* Add a file.
|
|
475
429
|
*/
|
|
476
430
|
async addFile(filePath) {
|
|
477
431
|
const file = await CollectedFile.fromFilePath(filePath);
|
|
432
|
+
if (!file?.fileContents) {
|
|
433
|
+
return null;
|
|
434
|
+
}
|
|
478
435
|
this.files.set(filePath, file);
|
|
479
436
|
this.generator.add({
|
|
480
|
-
filePath
|
|
437
|
+
filePath,
|
|
481
438
|
documentNode: file.parsed
|
|
482
439
|
});
|
|
483
440
|
return file;
|
|
484
441
|
}
|
|
442
|
+
matchesPatternOrExists(filePath) {
|
|
443
|
+
return this.files.has(filePath) || this.hookFiles.has(filePath) || this.hookDocuments.has(filePath) || this.helper.matchesImportPattern(filePath);
|
|
444
|
+
}
|
|
485
445
|
async handleAdd(filePath) {
|
|
486
|
-
|
|
487
|
-
|
|
446
|
+
if (!this.matchesPatternOrExists(filePath)) {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
const result = await this.addFile(filePath);
|
|
450
|
+
return !!result;
|
|
488
451
|
}
|
|
489
452
|
async handleChange(filePath) {
|
|
453
|
+
if (!this.matchesPatternOrExists(filePath)) {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
490
456
|
const file = this.files.get(filePath);
|
|
491
457
|
if (!file) {
|
|
492
|
-
return
|
|
458
|
+
return this.handleAdd(filePath);
|
|
493
459
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
460
|
+
try {
|
|
461
|
+
const needsUpdate = await file.update();
|
|
462
|
+
if (!needsUpdate) {
|
|
463
|
+
return false;
|
|
464
|
+
}
|
|
465
|
+
this.generator.update({
|
|
466
|
+
filePath,
|
|
467
|
+
documentNode: file.parsed
|
|
468
|
+
});
|
|
469
|
+
} catch {
|
|
470
|
+
return this.handleUnlink(filePath);
|
|
497
471
|
}
|
|
498
|
-
this.generator.update({
|
|
499
|
-
filePath: this.filePathToRelative(filePath),
|
|
500
|
-
documentNode: file.parsed
|
|
501
|
-
});
|
|
502
472
|
return true;
|
|
503
473
|
}
|
|
504
474
|
handleUnlink(filePath) {
|
|
@@ -507,7 +477,7 @@ class Collector {
|
|
|
507
477
|
return false;
|
|
508
478
|
}
|
|
509
479
|
this.files.delete(filePath);
|
|
510
|
-
this.generator.remove(
|
|
480
|
+
this.generator.remove(filePath);
|
|
511
481
|
return true;
|
|
512
482
|
}
|
|
513
483
|
handleUnlinkDir(folderPath) {
|
|
@@ -527,6 +497,7 @@ class Collector {
|
|
|
527
497
|
*/
|
|
528
498
|
async handleWatchEvent(event, filePath) {
|
|
529
499
|
let hasChanged = false;
|
|
500
|
+
const oldOperationTimestamps = new Map(this.operationTimestamps);
|
|
530
501
|
try {
|
|
531
502
|
if (event === "add") {
|
|
532
503
|
hasChanged = await this.handleAdd(filePath);
|
|
@@ -536,357 +507,1181 @@ class Collector {
|
|
|
536
507
|
hasChanged = this.handleUnlink(filePath);
|
|
537
508
|
} else if (event === "unlinkDir") {
|
|
538
509
|
hasChanged = this.handleUnlinkDir(filePath);
|
|
539
|
-
} else if (event === "addDir") {
|
|
540
510
|
}
|
|
541
511
|
if (hasChanged) {
|
|
542
|
-
this.buildState();
|
|
512
|
+
await this.buildState();
|
|
543
513
|
}
|
|
544
514
|
} catch (e) {
|
|
545
515
|
this.generator.resetCaches();
|
|
546
516
|
logger.error("Failed to update GraphQL code.");
|
|
547
|
-
|
|
548
|
-
return
|
|
517
|
+
this.logError(e);
|
|
518
|
+
return {
|
|
519
|
+
hasChanged: false,
|
|
520
|
+
affectedOperations: [],
|
|
521
|
+
error: { message: this.buildErrorMessage(e) }
|
|
522
|
+
};
|
|
549
523
|
}
|
|
524
|
+
const affectedOperations = [];
|
|
550
525
|
if (hasChanged) {
|
|
551
|
-
logger.success("Finished GraphQL code update.");
|
|
526
|
+
logger.success("Finished GraphQL code update successfully.");
|
|
527
|
+
for (const [name, newTimestamp] of this.operationTimestamps) {
|
|
528
|
+
const oldTimestamp = oldOperationTimestamps.get(name);
|
|
529
|
+
if (!oldTimestamp || oldTimestamp !== newTimestamp) {
|
|
530
|
+
affectedOperations.push(name);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
552
533
|
}
|
|
553
|
-
return hasChanged;
|
|
534
|
+
return { hasChanged, affectedOperations };
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Adds a virtual template (not written to disk) for both Nuxt and Nitro.
|
|
538
|
+
*
|
|
539
|
+
* For some reason a template written to disk works for both Nuxt and Nitro,
|
|
540
|
+
* but a virtual template requires adding two templates.
|
|
541
|
+
*/
|
|
542
|
+
addVirtualTemplate(template) {
|
|
543
|
+
const filename = template.options.path + ".js";
|
|
544
|
+
const getContents = () => this.getTemplate(template.options.path, "default");
|
|
545
|
+
addTemplate({
|
|
546
|
+
filename,
|
|
547
|
+
getContents
|
|
548
|
+
});
|
|
549
|
+
addServerTemplate({
|
|
550
|
+
// Since this is a virtual template, the name must match the final
|
|
551
|
+
// alias, example:
|
|
552
|
+
// - nuxt-graphql-middleware/foobar.mjs => #nuxt-graphql-middleware/foobar
|
|
553
|
+
//
|
|
554
|
+
// That way we can reference the same template using the alias in both
|
|
555
|
+
// Nuxt and Nitro environments.
|
|
556
|
+
filename: "#" + template.options.path,
|
|
557
|
+
getContents
|
|
558
|
+
});
|
|
554
559
|
}
|
|
555
560
|
/**
|
|
556
|
-
*
|
|
561
|
+
* Adds a template that dependes on Collector state.
|
|
557
562
|
*/
|
|
558
|
-
|
|
559
|
-
|
|
563
|
+
addTemplate(template) {
|
|
564
|
+
this.templates.push(template);
|
|
565
|
+
if (template.build) {
|
|
566
|
+
if (template.options.virtual) {
|
|
567
|
+
this.addVirtualTemplate(template);
|
|
568
|
+
} else {
|
|
569
|
+
const path = template.options.path;
|
|
570
|
+
const filename = template.options.isFullPath ? path : path + ".js";
|
|
571
|
+
addTemplate({
|
|
572
|
+
filename,
|
|
573
|
+
write: true,
|
|
574
|
+
getContents: () => this.getTemplate(path, "default")
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
if (template.buildTypes) {
|
|
579
|
+
const path = template.options.path;
|
|
580
|
+
const filename = template.options.path + ".d.ts";
|
|
581
|
+
addTypeTemplate(
|
|
582
|
+
{
|
|
583
|
+
filename,
|
|
584
|
+
write: true,
|
|
585
|
+
getContents: () => this.getTemplate(path, "types")
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
nuxt: true,
|
|
589
|
+
nitro: true
|
|
590
|
+
}
|
|
591
|
+
);
|
|
592
|
+
}
|
|
560
593
|
}
|
|
561
594
|
/**
|
|
562
|
-
* Get the
|
|
595
|
+
* Get the hook documents.
|
|
563
596
|
*/
|
|
564
|
-
|
|
565
|
-
return this.
|
|
597
|
+
getHookDocuments() {
|
|
598
|
+
return [...this.hookDocuments.entries()].map(([identifier, source]) => {
|
|
599
|
+
return {
|
|
600
|
+
identifier,
|
|
601
|
+
source
|
|
602
|
+
};
|
|
603
|
+
});
|
|
566
604
|
}
|
|
567
605
|
/**
|
|
568
|
-
* Get the
|
|
606
|
+
* Get the hook documents.
|
|
569
607
|
*/
|
|
570
|
-
|
|
571
|
-
return this.
|
|
608
|
+
getHookFiles() {
|
|
609
|
+
return [...this.hookFiles.values()];
|
|
572
610
|
}
|
|
573
611
|
}
|
|
574
612
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
options.schemaPath = "~~/schema.graphql";
|
|
596
|
-
options.autoImportPatterns = [
|
|
597
|
-
"~~/playground/**/*.{gql,graphql}",
|
|
598
|
-
"!node_modules"
|
|
599
|
-
];
|
|
600
|
-
}
|
|
601
|
-
if (!passedOptions.autoImportPatterns) {
|
|
602
|
-
options.autoImportPatterns = ["~~/**/*.{gql,graphql}", "!node_modules"];
|
|
603
|
-
}
|
|
604
|
-
options.autoImportPatterns = (options.autoImportPatterns || []).map(
|
|
605
|
-
(pattern) => {
|
|
606
|
-
return resolveAlias(pattern);
|
|
613
|
+
class SchemaProvider {
|
|
614
|
+
constructor(helper) {
|
|
615
|
+
this.helper = helper;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* The raw schema content.
|
|
619
|
+
*/
|
|
620
|
+
schemaContent = "";
|
|
621
|
+
/**
|
|
622
|
+
* The parsed schema object.
|
|
623
|
+
*/
|
|
624
|
+
schema = null;
|
|
625
|
+
async init() {
|
|
626
|
+
try {
|
|
627
|
+
await this.loadSchema();
|
|
628
|
+
} catch (error) {
|
|
629
|
+
logger.error(error);
|
|
630
|
+
const hasLoaded = await this.loadFromDiskFallback();
|
|
631
|
+
if (!hasLoaded) {
|
|
632
|
+
throw new Error("Failed to load GraphQL schema.");
|
|
607
633
|
}
|
|
608
|
-
);
|
|
609
|
-
if (!nuxt.options._prepare) {
|
|
610
|
-
validateOptions(options);
|
|
611
|
-
}
|
|
612
|
-
const moduleResolver = createResolver(import.meta.url);
|
|
613
|
-
const serverResolver = createResolver(nuxt.options.serverDir);
|
|
614
|
-
const srcResolver = createResolver(nuxt.options.srcDir);
|
|
615
|
-
const appResolver = createResolver(nuxt.options.dir.app);
|
|
616
|
-
const rootDir = nuxt.options.rootDir;
|
|
617
|
-
const rootResolver = createResolver(rootDir);
|
|
618
|
-
const { schemaPath, schemaContent } = await getSchemaPath(
|
|
619
|
-
resolveAlias(options.schemaPath),
|
|
620
|
-
options,
|
|
621
|
-
rootResolver.resolve,
|
|
622
|
-
options.downloadSchema
|
|
623
|
-
);
|
|
624
|
-
const schema = await loadSchema(schemaContent, {
|
|
625
|
-
loaders: []
|
|
626
|
-
});
|
|
627
|
-
const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url));
|
|
628
|
-
nuxt.options.build.transpile.push(runtimeDir);
|
|
629
|
-
const context = {
|
|
630
|
-
patterns: options.autoImportPatterns || [],
|
|
631
|
-
srcDir: nuxt.options.srcDir,
|
|
632
|
-
buildDir: srcResolver.resolve(nuxt.options.buildDir),
|
|
633
|
-
schemaPath,
|
|
634
|
-
serverApiPrefix: options.serverApiPrefix
|
|
635
|
-
};
|
|
636
|
-
const collector = new Collector(
|
|
637
|
-
schema,
|
|
638
|
-
context,
|
|
639
|
-
options.documents,
|
|
640
|
-
options.codegenConfig
|
|
641
|
-
);
|
|
642
|
-
await collector.init();
|
|
643
|
-
const isDevToolsEnabled = nuxt.options.dev && options.devtools;
|
|
644
|
-
let rpc;
|
|
645
|
-
if (isDevToolsEnabled) {
|
|
646
|
-
const clientPath = moduleResolver.resolve("./client");
|
|
647
|
-
setupDevToolsUI(nuxt, clientPath);
|
|
648
|
-
onDevToolsInitialized(() => {
|
|
649
|
-
rpc = extendServerRpc(RPC_NAMESPACE, {
|
|
650
|
-
// register server RPC functions
|
|
651
|
-
getModuleOptions() {
|
|
652
|
-
return options;
|
|
653
|
-
},
|
|
654
|
-
getDocuments() {
|
|
655
|
-
return [...collector.rpcItems.values()];
|
|
656
|
-
}
|
|
657
|
-
});
|
|
658
|
-
});
|
|
659
634
|
}
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
if (options.includeComposables) {
|
|
671
|
-
const nuxtComposables = [
|
|
672
|
-
"useGraphqlQuery",
|
|
673
|
-
"useGraphqlMutation",
|
|
674
|
-
"useGraphqlState",
|
|
675
|
-
"useAsyncGraphqlQuery"
|
|
676
|
-
];
|
|
677
|
-
if (options.enableFileUploads) {
|
|
678
|
-
nuxtComposables.push("useGraphqlUploadMutation");
|
|
635
|
+
}
|
|
636
|
+
async loadFromDiskFallback() {
|
|
637
|
+
const hasSchemaOnDisk = await this.hasSchemaOnDisk();
|
|
638
|
+
if (this.helper.isDev && hasSchemaOnDisk && this.helper.options.downloadSchema) {
|
|
639
|
+
const shouldUseFromDisk = await this.helper.prompt.confirm(
|
|
640
|
+
"Do you want to continue with the previously downloaded schema from disk?"
|
|
641
|
+
);
|
|
642
|
+
if (shouldUseFromDisk === "yes") {
|
|
643
|
+
await this.loadSchema({ forceDisk: true });
|
|
644
|
+
return true;
|
|
679
645
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
646
|
+
}
|
|
647
|
+
return false;
|
|
648
|
+
}
|
|
649
|
+
/**
|
|
650
|
+
* Loads the schema from disk.
|
|
651
|
+
*
|
|
652
|
+
* @returns The schema contents from disk.
|
|
653
|
+
*/
|
|
654
|
+
async loadSchemaFromDisk() {
|
|
655
|
+
const fileExists = await this.hasSchemaOnDisk();
|
|
656
|
+
if (!fileExists) {
|
|
657
|
+
logger.error(
|
|
658
|
+
'"downloadSchema" is set to false but no schema exists at ' + this.helper.paths.schema
|
|
693
659
|
);
|
|
694
|
-
|
|
660
|
+
throw new Error("Missing GraphQL schema.");
|
|
695
661
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
write: true,
|
|
699
|
-
getContents: () => collector.getTemplateTypes()
|
|
700
|
-
});
|
|
701
|
-
addAlias("#graphql-operations", templateTypescript.dst);
|
|
702
|
-
const templateDocuments = addTemplate({
|
|
703
|
-
filename: GraphqlMiddlewareTemplate.Documents,
|
|
704
|
-
write: true,
|
|
705
|
-
getContents: () => collector.getTemplateOperations()
|
|
706
|
-
});
|
|
707
|
-
addAlias("#graphql-documents", templateDocuments.dst);
|
|
708
|
-
const templateContext = addTemplate({
|
|
709
|
-
filename: GraphqlMiddlewareTemplate.ComposableContext,
|
|
710
|
-
write: true,
|
|
711
|
-
getContents: () => collector.getTemplateContext()
|
|
712
|
-
});
|
|
713
|
-
addAlias("#nuxt-graphql-middleware/generated-types", templateContext.dst);
|
|
714
|
-
addTemplate({
|
|
715
|
-
write: true,
|
|
716
|
-
filename: "nuxt-graphql-middleware/graphql-documents.d.ts",
|
|
717
|
-
getContents: () => {
|
|
718
|
-
return `
|
|
719
|
-
import type {
|
|
720
|
-
GraphqlMiddlewareQuery,
|
|
721
|
-
GraphqlMiddlewareMutation,
|
|
722
|
-
} from '#nuxt-graphql-middleware/generated-types'
|
|
723
|
-
|
|
724
|
-
declare module '#graphql-documents' {
|
|
725
|
-
type Operations = {
|
|
726
|
-
query: GraphqlMiddlewareQuery
|
|
727
|
-
mutation: GraphqlMiddlewareMutation
|
|
662
|
+
logger.info(`Loading GraphQL schema from disk: ${this.helper.paths.schema}`);
|
|
663
|
+
return await fs.readFile(this.helper.paths.schema).then((v) => v.toString());
|
|
728
664
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
const
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
665
|
+
/**
|
|
666
|
+
* Downloads the schema and saves it to disk.
|
|
667
|
+
*
|
|
668
|
+
* @returns The schema contents.
|
|
669
|
+
*/
|
|
670
|
+
downloadSchema() {
|
|
671
|
+
const endpoint = this.helper.options.graphqlEndpoint;
|
|
672
|
+
if (!endpoint) {
|
|
673
|
+
throw new Error("Missing graphqlEndpoint config.");
|
|
674
|
+
}
|
|
675
|
+
const pluginConfig = this.helper.options.codegenSchemaConfig?.urlSchemaOptions;
|
|
676
|
+
const schemaAstConfig = this.helper.options.codegenSchemaConfig?.schemaAstConfig || {
|
|
677
|
+
sort: true
|
|
678
|
+
};
|
|
679
|
+
const config = {
|
|
680
|
+
schema: endpoint,
|
|
681
|
+
pluginLoader: (name) => {
|
|
682
|
+
switch (name) {
|
|
683
|
+
case "@graphql-codegen/schema-ast":
|
|
684
|
+
return Promise.resolve(PluginSchemaAst);
|
|
685
|
+
}
|
|
686
|
+
throw new Error(`graphql-codegen plugin not found: ${name}`);
|
|
687
|
+
},
|
|
688
|
+
silent: true,
|
|
689
|
+
errorsOnly: true,
|
|
690
|
+
config: pluginConfig,
|
|
691
|
+
generates: {
|
|
692
|
+
[this.helper.paths.schema]: {
|
|
693
|
+
plugins: ["schema-ast"],
|
|
694
|
+
config: schemaAstConfig
|
|
754
695
|
}
|
|
755
696
|
}
|
|
756
|
-
logger.info("No graphqlMiddleware.serverOptions file found.");
|
|
757
697
|
};
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
698
|
+
logger.info(`Downloading GraphQL schema from "${endpoint}".`);
|
|
699
|
+
return generate(config, true).then((v) => v[0]?.content);
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Determine if the schema exists on disk.
|
|
703
|
+
*
|
|
704
|
+
* @returns True if the schema file exists on disk.
|
|
705
|
+
*/
|
|
706
|
+
hasSchemaOnDisk() {
|
|
707
|
+
return fs.access(this.helper.paths.schema).then(() => true).catch(() => false);
|
|
708
|
+
}
|
|
709
|
+
/**
|
|
710
|
+
* Load the schema either from disk or by downloading it.
|
|
711
|
+
*
|
|
712
|
+
* @param forceDownload - Forces downloading the schema.
|
|
713
|
+
*/
|
|
714
|
+
async loadSchema(opts) {
|
|
715
|
+
if (opts?.forceDisk) {
|
|
716
|
+
this.schemaContent = await this.loadSchemaFromDisk();
|
|
717
|
+
} else if (this.helper.options.downloadSchema || opts?.forceDownload) {
|
|
718
|
+
this.schemaContent = await this.downloadSchema();
|
|
719
|
+
} else {
|
|
720
|
+
this.schemaContent = await this.loadSchemaFromDisk();
|
|
721
|
+
}
|
|
722
|
+
this.schema = await loadSchema(this.schemaContent, {
|
|
723
|
+
loaders: []
|
|
774
724
|
});
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
export type GraphqlResponse<T> = GraphqlServerResponse<T> & GraphqlResponseAdditions
|
|
790
|
-
|
|
791
|
-
export type GraphqlResponseTyped = GraphqlResponse<GraphqlMiddlewareResponseUnion>
|
|
725
|
+
}
|
|
726
|
+
/**
|
|
727
|
+
* Get the schema.
|
|
728
|
+
*
|
|
729
|
+
* @returns The parsed GraphQL schema object.
|
|
730
|
+
*/
|
|
731
|
+
getSchema() {
|
|
732
|
+
if (!this.schema) {
|
|
733
|
+
throw new Error("Failed to load schema.");
|
|
734
|
+
}
|
|
735
|
+
return this.schema;
|
|
736
|
+
}
|
|
737
|
+
}
|
|
792
738
|
|
|
793
|
-
|
|
739
|
+
const unicode = isUnicodeSupported();
|
|
740
|
+
const s = (c, fallback) => unicode ? c : fallback;
|
|
741
|
+
const S_BAR = s("\u2502", "|");
|
|
742
|
+
const S_STEP_ACTIVE = s("\u25C6", "*");
|
|
743
|
+
const S_STEP_CANCEL = s("\u25A0", "x");
|
|
744
|
+
const S_STEP_ERROR = s("\u25B2", "x");
|
|
745
|
+
const S_STEP_SUBMIT = s("\u25C7", "o");
|
|
746
|
+
const S_RADIO_ACTIVE = s("\u25CF", ">");
|
|
747
|
+
const S_RADIO_INACTIVE = s("\u25CB", " ");
|
|
748
|
+
const S_BAR_END = s("\u2514", "\u2014");
|
|
749
|
+
const symbol = (state) => {
|
|
750
|
+
switch (state) {
|
|
751
|
+
case "initial":
|
|
752
|
+
case "active":
|
|
753
|
+
return color.cyan(S_STEP_ACTIVE);
|
|
754
|
+
case "cancel":
|
|
755
|
+
return color.red(S_STEP_CANCEL);
|
|
756
|
+
case "error":
|
|
757
|
+
return color.yellow(S_STEP_ERROR);
|
|
758
|
+
case "submit":
|
|
759
|
+
return color.green(S_STEP_SUBMIT);
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
class ConsolePrompt {
|
|
763
|
+
abortController = null;
|
|
764
|
+
confirm(message) {
|
|
765
|
+
this.abort();
|
|
766
|
+
this.abortController = new AbortController();
|
|
767
|
+
const active = "Yes";
|
|
768
|
+
const inactive = "No";
|
|
769
|
+
return new ConfirmPrompt({
|
|
770
|
+
active,
|
|
771
|
+
inactive,
|
|
772
|
+
initialValue: true,
|
|
773
|
+
signal: this.abortController.signal,
|
|
774
|
+
render() {
|
|
775
|
+
const title = `${color.gray(S_BAR)}
|
|
776
|
+
${symbol(this.state)} ${message}
|
|
794
777
|
`;
|
|
778
|
+
const value = this.value ? active : inactive;
|
|
779
|
+
switch (this.state) {
|
|
780
|
+
case "submit":
|
|
781
|
+
return `${title}${color.gray(S_BAR)} ${color.dim(value)}`;
|
|
782
|
+
case "cancel":
|
|
783
|
+
return `${title}${color.gray(S_BAR)} ${color.strikethrough(
|
|
784
|
+
color.dim(value)
|
|
785
|
+
)}
|
|
786
|
+
${color.gray(S_BAR)}`;
|
|
787
|
+
default: {
|
|
788
|
+
return `${title}${color.cyan(S_BAR)} ${this.value ? `${color.green(S_RADIO_ACTIVE)} ${active}` : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(active)}`} ${color.dim("/")} ${!this.value ? `${color.green(S_RADIO_ACTIVE)} ${inactive}` : `${color.dim(S_RADIO_INACTIVE)} ${color.dim(inactive)}`}
|
|
789
|
+
${color.cyan(S_BAR_END)}
|
|
790
|
+
`;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
795
793
|
}
|
|
794
|
+
}).prompt().then((v) => {
|
|
795
|
+
const result = v;
|
|
796
|
+
if (result === true) {
|
|
797
|
+
return "yes";
|
|
798
|
+
} else if (result === false) {
|
|
799
|
+
return "no";
|
|
800
|
+
}
|
|
801
|
+
return "cancel";
|
|
796
802
|
});
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
);
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
803
|
+
}
|
|
804
|
+
abort() {
|
|
805
|
+
if (this.abortController) {
|
|
806
|
+
this.abortController.abort();
|
|
807
|
+
this.abortController = null;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
class ModuleHelper {
|
|
813
|
+
constructor(nuxt, moduleUrl, options) {
|
|
814
|
+
this.nuxt = nuxt;
|
|
815
|
+
const mergedOptions = defu({}, options, defaultOptions);
|
|
816
|
+
if (!mergedOptions.autoImportPatterns) {
|
|
817
|
+
mergedOptions.autoImportPatterns = [
|
|
818
|
+
"~~/**/*.{gql,graphql}",
|
|
819
|
+
"!node_modules"
|
|
820
|
+
];
|
|
821
|
+
}
|
|
822
|
+
const layerAliases = nuxt.options._layers.map((layer) => {
|
|
823
|
+
return {
|
|
824
|
+
"~~": layer.config.rootDir,
|
|
825
|
+
"@@": layer.config.rootDir,
|
|
826
|
+
"~": layer.config.srcDir,
|
|
827
|
+
"@": layer.config.srcDir,
|
|
828
|
+
// Merge any additional aliases defined by the layer.
|
|
829
|
+
// Must be last so that the layer may override the "default" aliases.
|
|
830
|
+
...layer.config.alias || {}
|
|
831
|
+
};
|
|
832
|
+
});
|
|
833
|
+
const srcResolver = createResolver(nuxt.options.srcDir);
|
|
834
|
+
const rootResolver = createResolver(nuxt.options.rootDir);
|
|
835
|
+
mergedOptions.autoImportPatterns = (mergedOptions.autoImportPatterns || []).flatMap((pattern) => {
|
|
836
|
+
if (pattern.startsWith("!") || pattern.startsWith("/")) {
|
|
837
|
+
return pattern;
|
|
838
|
+
} else if (pattern.startsWith("~") || pattern.startsWith("@")) {
|
|
839
|
+
return layerAliases.map((aliases) => resolveAlias(pattern, aliases));
|
|
804
840
|
}
|
|
841
|
+
return rootResolver.resolve(pattern);
|
|
842
|
+
}).map((pattern) => {
|
|
843
|
+
return pattern.replace(".{graphql}", ".graphql").replace(".{gql}", ".gql");
|
|
844
|
+
});
|
|
845
|
+
this.options = mergedOptions;
|
|
846
|
+
if (!nuxt.options._prepare) {
|
|
847
|
+
validateOptions(this.options);
|
|
848
|
+
}
|
|
849
|
+
this.isDev = nuxt.options.dev;
|
|
850
|
+
this.resolvers = {
|
|
851
|
+
module: createResolver(moduleUrl),
|
|
852
|
+
server: createResolver(nuxt.options.serverDir),
|
|
853
|
+
src: srcResolver,
|
|
854
|
+
app: createResolver(nuxt.options.dir.app),
|
|
855
|
+
root: rootResolver
|
|
805
856
|
};
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
857
|
+
this.paths = {
|
|
858
|
+
runtimeTypes: "",
|
|
859
|
+
root: nuxt.options.rootDir,
|
|
860
|
+
nuxtConfig: this.resolvers.root.resolve("nuxt.config.ts"),
|
|
861
|
+
serverDir: nuxt.options.serverDir,
|
|
862
|
+
schema: this.resolvers.root.resolve(
|
|
863
|
+
resolveAlias(this.options.schemaPath)
|
|
864
|
+
),
|
|
865
|
+
serverOptions: "",
|
|
866
|
+
clientOptions: this.findClientOptions(),
|
|
867
|
+
moduleBuildDir: nuxt.options.buildDir + "/nuxt-graphql-middleware",
|
|
868
|
+
moduleTypesDir: nuxt.options.buildDir + "/graphql-operations"
|
|
869
|
+
};
|
|
870
|
+
this.paths.runtimeTypes = this.toModuleBuildRelative(
|
|
871
|
+
this.resolvers.module.resolve("./runtime/types.ts")
|
|
872
|
+
);
|
|
873
|
+
this.paths.serverOptions = this.findServerOptions();
|
|
874
|
+
}
|
|
875
|
+
resolvers;
|
|
876
|
+
paths;
|
|
877
|
+
isDev;
|
|
878
|
+
options;
|
|
879
|
+
prompt = new ConsolePrompt();
|
|
880
|
+
nitroExternals = [];
|
|
881
|
+
tsPaths = {};
|
|
882
|
+
/**
|
|
883
|
+
* Find the path to the graphqlMiddleware.serverOptions.ts file.
|
|
884
|
+
*/
|
|
885
|
+
findServerOptions() {
|
|
886
|
+
const newPath = this.resolvers.server.resolve(
|
|
887
|
+
"graphqlMiddleware.serverOptions"
|
|
888
|
+
);
|
|
889
|
+
const serverPath = fileExists(newPath);
|
|
890
|
+
if (serverPath) {
|
|
891
|
+
return serverPath;
|
|
892
|
+
}
|
|
893
|
+
const candidates = [
|
|
894
|
+
this.resolvers.root.resolve("graphqlMiddleware.serverOptions"),
|
|
895
|
+
this.resolvers.root.resolve("app/graphqlMiddleware.serverOptions"),
|
|
896
|
+
this.resolvers.src.resolve("graphqlMiddleware.serverOptions")
|
|
897
|
+
];
|
|
898
|
+
for (let i = 0; i < candidates.length; i++) {
|
|
899
|
+
const path = candidates[i];
|
|
900
|
+
const filePath = fileExists(path);
|
|
901
|
+
if (filePath) {
|
|
902
|
+
throw new Error(
|
|
903
|
+
`The graphqlMiddleware.serverOptions file should be placed in Nuxt's <serverDir> ("${this.paths.serverDir}/graphqlMiddleware.serverOptions.ts").`
|
|
904
|
+
);
|
|
816
905
|
}
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
906
|
+
}
|
|
907
|
+
logger.info("No graphqlMiddleware.serverOptions file found.");
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
findClientOptions() {
|
|
911
|
+
const clientOptionsPath = this.resolvers.app.resolve(
|
|
912
|
+
"graphqlMiddleware.clientOptions"
|
|
821
913
|
);
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
914
|
+
if (fileExists(clientOptionsPath)) {
|
|
915
|
+
return clientOptionsPath;
|
|
916
|
+
}
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Transform the path relative to the module's build directory.
|
|
921
|
+
*
|
|
922
|
+
* @param path - The absolute path.
|
|
923
|
+
*
|
|
924
|
+
* @returns The path relative to the module's build directory.
|
|
925
|
+
*/
|
|
926
|
+
toModuleBuildRelative(path) {
|
|
927
|
+
return relative(this.paths.moduleBuildDir, path);
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* Transform the path relative to the Nuxt build directory.
|
|
931
|
+
*
|
|
932
|
+
* @param path - The absolute path.
|
|
933
|
+
*
|
|
934
|
+
* @returns The path relative to the module's build directory.
|
|
935
|
+
*/
|
|
936
|
+
toBuildRelative(path) {
|
|
937
|
+
return relative(this.nuxt.options.buildDir, path);
|
|
938
|
+
}
|
|
939
|
+
/**
|
|
940
|
+
* Get all file paths that match the import patterns.
|
|
941
|
+
*/
|
|
942
|
+
async getImportPatternFiles() {
|
|
943
|
+
return resolveFiles(
|
|
944
|
+
this.nuxt.options.srcDir,
|
|
945
|
+
this.options.autoImportPatterns
|
|
946
|
+
);
|
|
947
|
+
}
|
|
948
|
+
matchesImportPattern(filePath) {
|
|
949
|
+
return micromatch.isMatch(filePath, this.options.autoImportPatterns) || this.options.autoImportPatterns.includes(filePath);
|
|
950
|
+
}
|
|
951
|
+
addAlias(name, path) {
|
|
952
|
+
this.nuxt.options.alias[name] = path;
|
|
953
|
+
const pathFromName = `./${name.substring(1)}`;
|
|
954
|
+
this.tsPaths[name] = pathFromName;
|
|
955
|
+
this.tsPaths[name + "/*"] = pathFromName + "/*";
|
|
956
|
+
this.inlineNitroExternals(name);
|
|
957
|
+
}
|
|
958
|
+
inlineNitroExternals(arg) {
|
|
959
|
+
const path = typeof arg === "string" ? arg : arg.dst;
|
|
960
|
+
this.nitroExternals.push(path);
|
|
961
|
+
this.transpile(path);
|
|
962
|
+
}
|
|
963
|
+
transpile(path) {
|
|
964
|
+
this.nuxt.options.build.transpile.push(path);
|
|
965
|
+
}
|
|
966
|
+
applyBuildConfig() {
|
|
967
|
+
this.nuxt.options.nitro.externals ||= {};
|
|
968
|
+
this.nuxt.options.nitro.externals.inline ||= [];
|
|
969
|
+
this.nuxt.options.nitro.externals.inline.push(...this.nitroExternals);
|
|
970
|
+
this.nuxt.options.nitro.typescript ||= {};
|
|
971
|
+
this.nuxt.options.nitro.typescript.tsConfig ||= {};
|
|
972
|
+
this.nuxt.options.nitro.typescript.tsConfig.compilerOptions ||= {};
|
|
973
|
+
this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths ||= {};
|
|
974
|
+
this.nuxt.options.typescript.tsConfig ||= {};
|
|
975
|
+
this.nuxt.options.typescript.tsConfig.compilerOptions ||= {};
|
|
976
|
+
this.nuxt.options.typescript.tsConfig.compilerOptions.paths ||= {};
|
|
977
|
+
for (const [name, path] of Object.entries(this.tsPaths)) {
|
|
978
|
+
this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths[name] = [path];
|
|
979
|
+
this.nuxt.options.typescript.tsConfig.compilerOptions.paths[name] = [path];
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
processTemplate(path, content) {
|
|
983
|
+
if (path.includes("graphql-operations/") || path.endsWith(".graphql")) {
|
|
984
|
+
return content.trim();
|
|
985
|
+
}
|
|
986
|
+
const name = path.split("/")[1];
|
|
987
|
+
return `/*
|
|
988
|
+
* @see [Documentation](https://nuxt-graphql-middleware.dulnan.net/advanced/templates#${name})
|
|
989
|
+
*/
|
|
990
|
+
${content.trim()}`;
|
|
991
|
+
}
|
|
992
|
+
addTemplate(template) {
|
|
993
|
+
if (template.build) {
|
|
994
|
+
const content = this.processTemplate(
|
|
995
|
+
template.options.path,
|
|
996
|
+
template.build(this)
|
|
997
|
+
);
|
|
998
|
+
addTemplate({
|
|
999
|
+
filename: template.options.path + ".js",
|
|
1000
|
+
write: true,
|
|
1001
|
+
getContents: () => content
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
if (template.buildTypes) {
|
|
1005
|
+
const content = this.processTemplate(
|
|
1006
|
+
template.options.path,
|
|
1007
|
+
template.buildTypes(this)
|
|
1008
|
+
);
|
|
1009
|
+
const filename = template.options.path + ".d.ts";
|
|
1010
|
+
addTypeTemplate({
|
|
1011
|
+
filename,
|
|
1012
|
+
write: true,
|
|
1013
|
+
getContents: () => content
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
addPlugin(name) {
|
|
1018
|
+
addPlugin(this.resolvers.module.resolve("./runtime/plugins/" + name), {
|
|
1019
|
+
append: false
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
addServerHandler(name, path, method) {
|
|
1023
|
+
addServerHandler({
|
|
1024
|
+
handler: this.resolvers.module.resolve("./runtime/server/api/" + name),
|
|
1025
|
+
route: this.options.serverApiPrefix + path,
|
|
1026
|
+
method
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
addComposable(name) {
|
|
1030
|
+
addImports({
|
|
1031
|
+
from: this.resolvers.module.resolve("./runtime/composables/" + name),
|
|
1032
|
+
name
|
|
1033
|
+
});
|
|
1034
|
+
}
|
|
1035
|
+
addServerUtil(name) {
|
|
1036
|
+
addServerImports([
|
|
1037
|
+
{
|
|
1038
|
+
from: this.resolvers.module.resolve("./runtime/server/utils/" + name),
|
|
1039
|
+
name
|
|
1040
|
+
}
|
|
1041
|
+
]);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
function defineGeneratorTemplate(options, build, buildTypes) {
|
|
1046
|
+
return {
|
|
1047
|
+
type: "generator",
|
|
1048
|
+
options,
|
|
1049
|
+
build,
|
|
1050
|
+
buildTypes
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
function defineStaticTemplate(options, build, buildTypes) {
|
|
1054
|
+
return {
|
|
1055
|
+
type: "static",
|
|
1056
|
+
options,
|
|
1057
|
+
build,
|
|
1058
|
+
buildTypes
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
829
1061
|
|
|
830
|
-
|
|
1062
|
+
const ClientOptions = defineStaticTemplate(
|
|
1063
|
+
{ path: "nuxt-graphql-middleware/client-options" },
|
|
1064
|
+
(helper) => {
|
|
1065
|
+
if (helper.paths.clientOptions) {
|
|
1066
|
+
const pathRelative = helper.toModuleBuildRelative(
|
|
1067
|
+
helper.paths.clientOptions
|
|
1068
|
+
);
|
|
1069
|
+
return `import clientOptions from '${pathRelative}'
|
|
1070
|
+
export { clientOptions }
|
|
1071
|
+
`;
|
|
1072
|
+
}
|
|
1073
|
+
return `export const clientOptions = {}`;
|
|
1074
|
+
},
|
|
1075
|
+
(helper) => {
|
|
1076
|
+
if (helper.paths.clientOptions) {
|
|
1077
|
+
const pathRelative = helper.toModuleBuildRelative(
|
|
1078
|
+
helper.paths.clientOptions
|
|
1079
|
+
);
|
|
1080
|
+
return `import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
|
|
1081
|
+
import clientOptionsImport from '${pathRelative}'
|
|
831
1082
|
|
|
832
|
-
export
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
1083
|
+
declare export const clientOptions: GraphqlClientOptions
|
|
1084
|
+
export type GraphqlClientContext = typeof clientOptionsImport extends GraphqlClientOptions<infer R> ? R : {}
|
|
1085
|
+
`;
|
|
1086
|
+
}
|
|
1087
|
+
return `
|
|
1088
|
+
import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
|
|
836
1089
|
|
|
1090
|
+
declare export const clientOptions: GraphqlClientOptions
|
|
837
1091
|
export type GraphqlClientContext = {}
|
|
838
1092
|
`;
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
1093
|
+
}
|
|
1094
|
+
);
|
|
1095
|
+
|
|
1096
|
+
const Documents = defineGeneratorTemplate(
|
|
1097
|
+
{ path: "nuxt-graphql-middleware/documents", virtual: true },
|
|
1098
|
+
(output, helper) => {
|
|
1099
|
+
return output.getOperationsFile({
|
|
1100
|
+
exportName: "documents",
|
|
1101
|
+
minify: !helper.isDev
|
|
1102
|
+
}).getSource();
|
|
1103
|
+
},
|
|
1104
|
+
() => {
|
|
1105
|
+
return `
|
|
1106
|
+
import type { Query, Mutation } from './operation-types'
|
|
1107
|
+
|
|
1108
|
+
declare module '#nuxt-graphql-middleware/documents' {
|
|
1109
|
+
export type Documents = {
|
|
1110
|
+
query: Record<keyof Query, string>
|
|
1111
|
+
mutation: Record<keyof Mutation, string>
|
|
1112
|
+
}
|
|
1113
|
+
export const documents: Documents
|
|
1114
|
+
}`;
|
|
1115
|
+
}
|
|
1116
|
+
);
|
|
1117
|
+
|
|
1118
|
+
const GraphqlConfig = defineStaticTemplate(
|
|
1119
|
+
{ path: "nuxt-graphql-middleware/graphql.config" },
|
|
1120
|
+
(helper) => {
|
|
1121
|
+
const patterns = helper.options.autoImportPatterns || [];
|
|
1122
|
+
const configPath = helper.resolvers.root.resolve(
|
|
1123
|
+
(helper.options.graphqlConfigFilePath || "").replace(
|
|
1124
|
+
"/graphql.config.ts",
|
|
1125
|
+
""
|
|
1126
|
+
)
|
|
850
1127
|
);
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
1128
|
+
const schemaPath = "./" + relative(configPath, helper.paths.schema);
|
|
1129
|
+
const documents = patterns.filter((v) => !v.includes("!")).map((pattern) => {
|
|
1130
|
+
return "./" + relative(configPath, helper.resolvers.root.resolve(pattern));
|
|
854
1131
|
});
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1132
|
+
documents.push(
|
|
1133
|
+
"./" + relative(
|
|
1134
|
+
configPath,
|
|
1135
|
+
join(helper.paths.moduleBuildDir, "hook-documents.graphql")
|
|
1136
|
+
)
|
|
1137
|
+
);
|
|
1138
|
+
return `
|
|
1139
|
+
import { hookFiles } from './hook-files'
|
|
1140
|
+
|
|
1141
|
+
const schema = ${JSON.stringify(schemaPath)}
|
|
1142
|
+
|
|
1143
|
+
const documents = ${JSON.stringify(documents, null, 2)};
|
|
1144
|
+
|
|
1145
|
+
const config = {
|
|
1146
|
+
schema,
|
|
1147
|
+
documents: [
|
|
1148
|
+
...documents,
|
|
1149
|
+
...hookFiles
|
|
1150
|
+
]
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
export default config
|
|
1154
|
+
`;
|
|
1155
|
+
},
|
|
1156
|
+
() => {
|
|
1157
|
+
return `
|
|
1158
|
+
import type { IGraphQLProject } from 'graphql-config'
|
|
1159
|
+
|
|
1160
|
+
type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
|
|
1161
|
+
|
|
1162
|
+
const config: WithRequired<IGraphQLProject, 'schema' | 'documents'>;
|
|
1163
|
+
|
|
1164
|
+
export default config;
|
|
1165
|
+
`;
|
|
1166
|
+
}
|
|
1167
|
+
);
|
|
1168
|
+
|
|
1169
|
+
const Helpers = defineStaticTemplate(
|
|
1170
|
+
{ path: "nuxt-graphql-middleware/helpers" },
|
|
1171
|
+
(helper) => {
|
|
1172
|
+
return `export const serverApiPrefix = '${helper.options.serverApiPrefix}'
|
|
1173
|
+
export function getEndpoint(operation, operationName) {
|
|
1174
|
+
return serverApiPrefix + '/' + operation + '/' + operationName
|
|
1175
|
+
}
|
|
1176
|
+
`;
|
|
1177
|
+
},
|
|
1178
|
+
() => {
|
|
1179
|
+
return `export const serverApiPrefix: string;
|
|
1180
|
+
export function getEndpoint(operation: string, operationName: string): string`;
|
|
1181
|
+
}
|
|
1182
|
+
);
|
|
1183
|
+
|
|
1184
|
+
const NitroTypes = defineGeneratorTemplate(
|
|
1185
|
+
{ path: "nuxt-graphql-middleware/nitro" },
|
|
1186
|
+
null,
|
|
1187
|
+
(output, helper) => {
|
|
1188
|
+
const operations = output.getCollectedOperations();
|
|
1189
|
+
const serverApiPrefix = helper.options.serverApiPrefix;
|
|
1190
|
+
const endpoints = [];
|
|
1191
|
+
const imports = [];
|
|
1192
|
+
for (const operation of operations) {
|
|
1193
|
+
imports.push(operation.typeName);
|
|
1194
|
+
const method = operation.operationType === OperationTypeNode.QUERY ? "get" : "post";
|
|
1195
|
+
endpoints.push(
|
|
1196
|
+
` '${serverApiPrefix}/${operation.operationType}/${operation.graphqlName}': {
|
|
1197
|
+
'${method}': GraphqlResponse<${operation.typeName}>
|
|
1198
|
+
}`
|
|
1199
|
+
);
|
|
860
1200
|
}
|
|
861
|
-
|
|
862
|
-
|
|
1201
|
+
return `import type { GraphqlResponse } from './response'
|
|
1202
|
+
import type {
|
|
1203
|
+
${imports.sort().join(",\n ")}
|
|
1204
|
+
} from './../graphql-operations'
|
|
1205
|
+
|
|
1206
|
+
declare module 'nitropack/types' {
|
|
1207
|
+
interface InternalApi {
|
|
1208
|
+
${endpoints.sort().join("\n")}
|
|
1209
|
+
}
|
|
1210
|
+
}`;
|
|
1211
|
+
}
|
|
1212
|
+
);
|
|
1213
|
+
|
|
1214
|
+
const OperationTypesAll = defineGeneratorTemplate(
|
|
1215
|
+
{ path: "nuxt-graphql-middleware/operation-types" },
|
|
1216
|
+
() => `export {}`,
|
|
1217
|
+
(output) => {
|
|
1218
|
+
return output.getOperationTypesFile({
|
|
1219
|
+
importFrom: "./../graphql-operations"
|
|
1220
|
+
}).getSource();
|
|
1221
|
+
}
|
|
1222
|
+
);
|
|
1223
|
+
|
|
1224
|
+
const Operations = defineGeneratorTemplate(
|
|
1225
|
+
{ path: "graphql-operations/index" },
|
|
1226
|
+
(output) => {
|
|
1227
|
+
const typesFile = output.getOperations("js");
|
|
1228
|
+
return typesFile.getSource();
|
|
1229
|
+
},
|
|
1230
|
+
(output) => {
|
|
1231
|
+
const typesFile = output.getOperations("d.ts");
|
|
1232
|
+
return typesFile.getSource();
|
|
1233
|
+
}
|
|
1234
|
+
);
|
|
1235
|
+
|
|
1236
|
+
const Response = defineGeneratorTemplate(
|
|
1237
|
+
{ path: "nuxt-graphql-middleware/response" },
|
|
1238
|
+
null,
|
|
1239
|
+
(output, helper) => {
|
|
1240
|
+
const operations = output.getCollectedOperations();
|
|
1241
|
+
const allTypes = operations.map((v) => v.typeName).sort();
|
|
1242
|
+
return `import type {
|
|
1243
|
+
${allTypes.join(",\n ")}
|
|
1244
|
+
} from './../graphql-operations'
|
|
1245
|
+
import type { GraphqlResponseAdditions } from './server-options'
|
|
1246
|
+
import type { GraphqlServerResponse } from '${helper.paths.runtimeTypes}'
|
|
1247
|
+
|
|
1248
|
+
declare module '#nuxt-graphql-middleware/response' {
|
|
1249
|
+
export type GraphqlMiddlewareResponseUnion =
|
|
1250
|
+
| ${allTypes.join("\n | ") || "never"}
|
|
1251
|
+
|
|
1252
|
+
export type GraphqlResponse<T> = GraphqlServerResponse<T> & GraphqlResponseAdditions
|
|
1253
|
+
export type GraphqlResponseTyped = GraphqlResponse<GraphqlMiddlewareResponseUnion>
|
|
1254
|
+
}`;
|
|
1255
|
+
}
|
|
1256
|
+
);
|
|
1257
|
+
|
|
1258
|
+
const ServerOptions = defineStaticTemplate(
|
|
1259
|
+
{ path: "nuxt-graphql-middleware/server-options" },
|
|
1260
|
+
(helper) => {
|
|
1261
|
+
const resolvedPathRelative = helper.paths.serverOptions ? helper.toModuleBuildRelative(helper.paths.serverOptions) : null;
|
|
1262
|
+
const serverOptionsLine = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions = {}`;
|
|
1263
|
+
return `
|
|
1264
|
+
${serverOptionsLine}
|
|
1265
|
+
export { serverOptions }
|
|
1266
|
+
`;
|
|
1267
|
+
},
|
|
1268
|
+
(helper) => {
|
|
1269
|
+
if (helper.paths.serverOptions) {
|
|
1270
|
+
const resolvedPathRelative = helper.toModuleBuildRelative(
|
|
1271
|
+
helper.paths.serverOptions
|
|
1272
|
+
);
|
|
1273
|
+
return `
|
|
1274
|
+
import type { GraphqlMiddlewareServerOptions } from '${helper.paths.runtimeTypes}'
|
|
1275
|
+
import serverOptionsImport from '${resolvedPathRelative}'
|
|
1276
|
+
|
|
1277
|
+
export type GraphqlResponseAdditions =
|
|
1278
|
+
typeof serverOptionsImport extends GraphqlMiddlewareServerOptions<infer R, any, any> ? R : {}
|
|
1279
|
+
|
|
1280
|
+
declare export const serverOptions: GraphqlMiddlewareServerOptions
|
|
1281
|
+
`;
|
|
1282
|
+
}
|
|
1283
|
+
return `
|
|
1284
|
+
import type { GraphqlMiddlewareServerOptions } from '${helper.paths.runtimeTypes}'
|
|
1285
|
+
|
|
1286
|
+
declare export const serverOptions: GraphqlMiddlewareServerOptions
|
|
1287
|
+
|
|
1288
|
+
export type GraphqlResponseAdditions = object
|
|
1289
|
+
`;
|
|
1290
|
+
}
|
|
1291
|
+
);
|
|
1292
|
+
|
|
1293
|
+
const Sources = defineGeneratorTemplate(
|
|
1294
|
+
{ path: "nuxt-graphql-middleware/sources" },
|
|
1295
|
+
(output, helper) => {
|
|
1296
|
+
const operations = output.getCollectedOperations();
|
|
1297
|
+
const srcDir = helper.paths.root;
|
|
1298
|
+
const lines = [];
|
|
1299
|
+
for (const operation of operations) {
|
|
1300
|
+
const filePath = operation.filePath.startsWith("/") ? relative(srcDir, operation.filePath) : operation.filePath;
|
|
1301
|
+
lines.push(
|
|
1302
|
+
`${operation.operationType}_${operation.graphqlName}: '${filePath}',`
|
|
1303
|
+
);
|
|
1304
|
+
}
|
|
1305
|
+
return `
|
|
1306
|
+
export const operationSources = {
|
|
1307
|
+
${lines.join("\n ")}
|
|
1308
|
+
}
|
|
1309
|
+
`;
|
|
1310
|
+
},
|
|
1311
|
+
() => {
|
|
1312
|
+
return `export const operationSources: Record<string, string>`;
|
|
1313
|
+
}
|
|
1314
|
+
);
|
|
1315
|
+
|
|
1316
|
+
const HookDocuments = defineGeneratorTemplate(
|
|
1317
|
+
{
|
|
1318
|
+
path: "nuxt-graphql-middleware/hook-documents.graphql",
|
|
1319
|
+
virtual: false,
|
|
1320
|
+
isFullPath: true
|
|
1321
|
+
},
|
|
1322
|
+
(_output, _helper, collector) => {
|
|
1323
|
+
return collector.getHookDocuments().map((v) => {
|
|
1324
|
+
return `
|
|
1325
|
+
# ${v.identifier}
|
|
1326
|
+
${v.source}
|
|
1327
|
+
`;
|
|
1328
|
+
}).join("\n\n");
|
|
1329
|
+
},
|
|
1330
|
+
null
|
|
1331
|
+
);
|
|
1332
|
+
|
|
1333
|
+
const HookFiles = defineGeneratorTemplate(
|
|
1334
|
+
{
|
|
1335
|
+
path: "nuxt-graphql-middleware/hook-files",
|
|
1336
|
+
virtual: false
|
|
1337
|
+
},
|
|
1338
|
+
(_output, helper, collector) => {
|
|
1339
|
+
const configPath = helper.resolvers.root.resolve(
|
|
1340
|
+
(helper.options.graphqlConfigFilePath || "").replace(
|
|
1341
|
+
"/graphql.config.ts",
|
|
1342
|
+
""
|
|
1343
|
+
)
|
|
1344
|
+
);
|
|
1345
|
+
const files = collector.getHookFiles().map((filePath) => {
|
|
1346
|
+
return "./" + relative(configPath, filePath);
|
|
863
1347
|
});
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
1348
|
+
return `export const hookFiles = ${JSON.stringify(files, null, 2)}`;
|
|
1349
|
+
},
|
|
1350
|
+
null
|
|
1351
|
+
);
|
|
1352
|
+
|
|
1353
|
+
const TEMPLATES = [
|
|
1354
|
+
ClientOptions,
|
|
1355
|
+
Documents,
|
|
1356
|
+
GraphqlConfig,
|
|
1357
|
+
Helpers,
|
|
1358
|
+
NitroTypes,
|
|
1359
|
+
OperationTypesAll,
|
|
1360
|
+
Operations,
|
|
1361
|
+
Response,
|
|
1362
|
+
ServerOptions,
|
|
1363
|
+
Sources,
|
|
1364
|
+
HookDocuments,
|
|
1365
|
+
HookFiles
|
|
1366
|
+
];
|
|
1367
|
+
|
|
1368
|
+
const DEVTOOLS_UI_ROUTE = "/__nuxt-graphql-middleware";
|
|
1369
|
+
const DEVTOOLS_UI_LOCAL_PORT = 3300;
|
|
1370
|
+
function setupDevToolsUI(nuxt, clientPath) {
|
|
1371
|
+
const isProductionBuild = existsSync$1(clientPath);
|
|
1372
|
+
if (isProductionBuild) {
|
|
1373
|
+
nuxt.hook("vite:serverCreated", async (server) => {
|
|
1374
|
+
const sirv = await import('sirv').then((r) => r.default || r);
|
|
1375
|
+
server.middlewares.use(
|
|
1376
|
+
DEVTOOLS_UI_ROUTE,
|
|
1377
|
+
sirv(clientPath, { dev: true, single: true })
|
|
870
1378
|
);
|
|
871
1379
|
});
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
1380
|
+
} else {
|
|
1381
|
+
nuxt.hook("vite:extendConfig", (config) => {
|
|
1382
|
+
config.server = config.server || {};
|
|
1383
|
+
config.server.proxy = config.server.proxy || {};
|
|
1384
|
+
config.server.proxy[DEVTOOLS_UI_ROUTE] = {
|
|
1385
|
+
target: "http://localhost:" + DEVTOOLS_UI_LOCAL_PORT + DEVTOOLS_UI_ROUTE,
|
|
1386
|
+
changeOrigin: true,
|
|
1387
|
+
followRedirects: true,
|
|
1388
|
+
rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
|
|
1389
|
+
};
|
|
1390
|
+
});
|
|
1391
|
+
}
|
|
1392
|
+
nuxt.hook("devtools:customTabs", (tabs) => {
|
|
1393
|
+
tabs.push({
|
|
1394
|
+
// unique identifier
|
|
1395
|
+
name: "nuxt-graphql-middleware",
|
|
1396
|
+
// title to display in the tab
|
|
1397
|
+
title: "GraphQL Middleware",
|
|
1398
|
+
// any icon from Iconify, or a URL to an image
|
|
1399
|
+
icon: "akar-icons:graphql-fill",
|
|
1400
|
+
// iframe view
|
|
1401
|
+
view: {
|
|
1402
|
+
type: "iframe",
|
|
1403
|
+
src: DEVTOOLS_UI_ROUTE
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
const RPC_NAMESPACE = "nuxt-graphql-middleware";
|
|
1410
|
+
class DevModeHandler {
|
|
1411
|
+
constructor(nuxt, schemaProvider, collector, helper) {
|
|
1412
|
+
this.nuxt = nuxt;
|
|
1413
|
+
this.schemaProvider = schemaProvider;
|
|
1414
|
+
this.collector = collector;
|
|
1415
|
+
this.helper = helper;
|
|
1416
|
+
}
|
|
1417
|
+
devToolsRpc = null;
|
|
1418
|
+
nitro = null;
|
|
1419
|
+
viteWebSocket = null;
|
|
1420
|
+
operationsToReload = /* @__PURE__ */ new Set();
|
|
1421
|
+
init() {
|
|
1422
|
+
this.nuxt.hooks.hookOnce("ready", this.onReady.bind(this));
|
|
1423
|
+
this.nuxt.hooks.hookOnce(
|
|
1424
|
+
"vite:serverCreated",
|
|
1425
|
+
this.onViteServerCreated.bind(this)
|
|
1426
|
+
);
|
|
1427
|
+
this.nuxt.hook("builder:watch", this.onBuilderWatch.bind(this));
|
|
1428
|
+
if (this.helper.options.devtools) {
|
|
1429
|
+
const clientPath = this.helper.resolvers.module.resolve("./client");
|
|
1430
|
+
setupDevToolsUI(this.nuxt, clientPath);
|
|
1431
|
+
onDevToolsInitialized(() => {
|
|
1432
|
+
this.devToolsRpc = extendServerRpc(
|
|
1433
|
+
RPC_NAMESPACE,
|
|
1434
|
+
{
|
|
1435
|
+
// register server RPC functions
|
|
1436
|
+
getModuleOptions: () => {
|
|
1437
|
+
return this.helper.options;
|
|
1438
|
+
},
|
|
1439
|
+
getDocuments: () => {
|
|
1440
|
+
return [...this.collector.rpcItems.values()];
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
);
|
|
876
1444
|
});
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
onReady() {
|
|
1448
|
+
this.nitro = useNitro();
|
|
1449
|
+
this.nitro.hooks.hook("compiled", this.onNitroCompiled.bind(this));
|
|
1450
|
+
}
|
|
1451
|
+
async onBuilderWatch(event, providedFilePath) {
|
|
1452
|
+
if (!providedFilePath.endsWith(".graphql") && !providedFilePath.endsWith(".gql")) {
|
|
1453
|
+
return;
|
|
1454
|
+
}
|
|
1455
|
+
const pathAbsolute = providedFilePath.startsWith("/") ? providedFilePath : this.helper.resolvers.src.resolve(providedFilePath);
|
|
1456
|
+
if (pathAbsolute === this.helper.paths.schema) {
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
this.helper.prompt.abort();
|
|
1460
|
+
const { hasChanged, affectedOperations, error } = await this.collector.handleWatchEvent(event, pathAbsolute);
|
|
1461
|
+
if (error) {
|
|
1462
|
+
this.sendError(error);
|
|
1463
|
+
await this.helper.prompt.confirm("Do you want to download and update the GraphQL schema?").then(async (shouldReload) => {
|
|
1464
|
+
if (shouldReload !== "yes") {
|
|
882
1465
|
return;
|
|
883
1466
|
}
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
1467
|
+
try {
|
|
1468
|
+
await this.schemaProvider.loadSchema({ forceDownload: true });
|
|
1469
|
+
await this.collector.updateSchema(this.schemaProvider.getSchema());
|
|
1470
|
+
} catch (e) {
|
|
1471
|
+
logger.error(e);
|
|
887
1472
|
}
|
|
888
1473
|
});
|
|
1474
|
+
return;
|
|
1475
|
+
}
|
|
1476
|
+
if (!hasChanged) {
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
if (this.nitro) {
|
|
1480
|
+
await this.nitro.hooks.callHook("rollup:reload");
|
|
1481
|
+
}
|
|
1482
|
+
if (affectedOperations.length) {
|
|
1483
|
+
affectedOperations.forEach(
|
|
1484
|
+
(operation) => this.operationsToReload.add(operation)
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1487
|
+
if (this.devToolsRpc) {
|
|
1488
|
+
try {
|
|
1489
|
+
this.devToolsRpc.broadcast.documentsUpdated([
|
|
1490
|
+
...this.collector.rpcItems.values()
|
|
1491
|
+
]);
|
|
1492
|
+
} catch {
|
|
1493
|
+
logger.info(
|
|
1494
|
+
"Failed to update GraphQL documents in dev tools. The documents might be stale."
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
onViteServerCreated(server) {
|
|
1500
|
+
this.viteWebSocket = server.ws;
|
|
1501
|
+
}
|
|
1502
|
+
sendError(error) {
|
|
1503
|
+
if (!this.viteWebSocket) {
|
|
1504
|
+
return;
|
|
1505
|
+
}
|
|
1506
|
+
this.viteWebSocket.send({
|
|
1507
|
+
type: "error",
|
|
1508
|
+
err: {
|
|
1509
|
+
message: error.message,
|
|
1510
|
+
stack: ""
|
|
1511
|
+
}
|
|
1512
|
+
});
|
|
1513
|
+
}
|
|
1514
|
+
onNitroCompiled() {
|
|
1515
|
+
if (!this.operationsToReload.size) {
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
const operations = [...this.operationsToReload.values()];
|
|
1519
|
+
this.operationsToReload.clear();
|
|
1520
|
+
if (!this.viteWebSocket) {
|
|
1521
|
+
return;
|
|
1522
|
+
}
|
|
1523
|
+
this.viteWebSocket.send({
|
|
1524
|
+
type: "custom",
|
|
1525
|
+
event: "nuxt-graphql-middleware:reload",
|
|
1526
|
+
data: { operations }
|
|
1527
|
+
});
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
class ModuleContext {
|
|
1532
|
+
constructor(schemaProvider, collector) {
|
|
1533
|
+
this.schemaProvider = schemaProvider;
|
|
1534
|
+
this.collector = collector;
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Return the GraphQL schema.
|
|
1538
|
+
*
|
|
1539
|
+
* Note that the schema may be updated during development, so it can become
|
|
1540
|
+
* stale. Prefer using methods like `schemaHasType()` to query the schema.
|
|
1541
|
+
*
|
|
1542
|
+
* @returns The GraphQL schema.
|
|
1543
|
+
*/
|
|
1544
|
+
getSchema() {
|
|
1545
|
+
return this.schemaProvider.getSchema();
|
|
1546
|
+
}
|
|
1547
|
+
/**
|
|
1548
|
+
* Check if the given GraphQL type (interface, concrete type, enum, input type)
|
|
1549
|
+
* exists in the schema.
|
|
1550
|
+
*
|
|
1551
|
+
* @param name - The name of the type.
|
|
1552
|
+
*
|
|
1553
|
+
* @returns True if the type exists in the schema.
|
|
1554
|
+
*/
|
|
1555
|
+
schemaHasType(name) {
|
|
1556
|
+
return !!this.schemaProvider.getSchema().getType(name);
|
|
1557
|
+
}
|
|
1558
|
+
/**
|
|
1559
|
+
* Get a type from the schema.
|
|
1560
|
+
*
|
|
1561
|
+
* @param name - The name of the type.
|
|
1562
|
+
*
|
|
1563
|
+
* @returns The type.
|
|
1564
|
+
*/
|
|
1565
|
+
schemaGetType(name) {
|
|
1566
|
+
return this.schemaProvider.getSchema().getType(name);
|
|
1567
|
+
}
|
|
1568
|
+
/**
|
|
1569
|
+
* Add an additional static document.
|
|
1570
|
+
*
|
|
1571
|
+
* @param identifier - The unique identifier for your document.
|
|
1572
|
+
* @param source - The document source.
|
|
1573
|
+
*/
|
|
1574
|
+
addDocument(identifier, source) {
|
|
1575
|
+
this.collector.addHookDocument(identifier, source);
|
|
1576
|
+
return this;
|
|
1577
|
+
}
|
|
1578
|
+
/**
|
|
1579
|
+
* Add or update an additional static document.
|
|
1580
|
+
*
|
|
1581
|
+
* @param identifier - The unique identifier for your document.
|
|
1582
|
+
* @param source - The document source.
|
|
1583
|
+
*/
|
|
1584
|
+
async addOrUpdateDocument(identifier, source) {
|
|
1585
|
+
await this.collector.addOrUpdateHookDocument(identifier, source);
|
|
1586
|
+
return this;
|
|
1587
|
+
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Add an additional GraphQL file to import.
|
|
1590
|
+
*
|
|
1591
|
+
* @param filePath - The absolute path to the file.
|
|
1592
|
+
*/
|
|
1593
|
+
addImportFile(filePath) {
|
|
1594
|
+
if (!filePath.startsWith("/")) {
|
|
1595
|
+
throw new Error(
|
|
1596
|
+
`The provided file path "${filePath}" must be an absolute path.`
|
|
1597
|
+
);
|
|
1598
|
+
}
|
|
1599
|
+
if (!filePath.endsWith(".graphql") && !filePath.endsWith(".gql")) {
|
|
1600
|
+
throw new Error(
|
|
1601
|
+
`The provided file path "${filePath}" should have a .graphql or .gql extension.`
|
|
1602
|
+
);
|
|
889
1603
|
}
|
|
1604
|
+
this.collector.addHookFile(filePath);
|
|
1605
|
+
return this;
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1609
|
+
const module = defineNuxtModule({
|
|
1610
|
+
meta: {
|
|
1611
|
+
name,
|
|
1612
|
+
configKey: "graphqlMiddleware",
|
|
1613
|
+
version,
|
|
1614
|
+
compatibility: {
|
|
1615
|
+
nuxt: ">=3.15.0"
|
|
1616
|
+
}
|
|
1617
|
+
},
|
|
1618
|
+
defaults: defaultOptions,
|
|
1619
|
+
async setup(passedOptions, nuxt) {
|
|
1620
|
+
const helper = new ModuleHelper(nuxt, import.meta.url, passedOptions);
|
|
1621
|
+
const schemaProvider = new SchemaProvider(helper);
|
|
1622
|
+
await schemaProvider.init();
|
|
1623
|
+
const collector = new Collector(schemaProvider.getSchema(), helper);
|
|
1624
|
+
const moduleContext = new ModuleContext(schemaProvider, collector);
|
|
1625
|
+
nuxt._nuxt_graphql_middleware = moduleContext;
|
|
1626
|
+
nuxt.options.appConfig.graphqlMiddleware = {
|
|
1627
|
+
clientCacheEnabled: !!helper.options.clientCache?.enabled,
|
|
1628
|
+
clientCacheMaxSize: helper.options.clientCache?.maxSize ?? 100
|
|
1629
|
+
};
|
|
1630
|
+
nuxt.options.runtimeConfig.graphqlMiddleware = {
|
|
1631
|
+
graphqlEndpoint: helper.options.graphqlEndpoint || ""
|
|
1632
|
+
};
|
|
1633
|
+
helper.transpile(fileURLToPath(new URL("./runtime", import.meta.url)));
|
|
1634
|
+
helper.inlineNitroExternals(helper.resolvers.module.resolve("./runtime"));
|
|
1635
|
+
helper.inlineNitroExternals(helper.paths.moduleBuildDir);
|
|
1636
|
+
helper.inlineNitroExternals(helper.paths.moduleTypesDir);
|
|
1637
|
+
helper.addAlias("#nuxt-graphql-middleware", helper.paths.moduleBuildDir);
|
|
1638
|
+
helper.addAlias("#graphql-operations", helper.paths.moduleTypesDir);
|
|
1639
|
+
helper.addPlugin("provideState");
|
|
1640
|
+
if (helper.isDev && helper.options.errorOverlay) {
|
|
1641
|
+
helper.addPlugin("devMode");
|
|
1642
|
+
}
|
|
1643
|
+
helper.addServerHandler("query", "/query/:name", "get");
|
|
1644
|
+
helper.addServerHandler("mutation", "/mutation/:name", "post");
|
|
1645
|
+
if (helper.options.enableFileUploads) {
|
|
1646
|
+
helper.addServerHandler("upload", "/upload/:name", "post");
|
|
1647
|
+
}
|
|
1648
|
+
if (helper.isDev) {
|
|
1649
|
+
helper.addServerHandler("debug", "/debug", "get");
|
|
1650
|
+
}
|
|
1651
|
+
if (helper.options.includeComposables) {
|
|
1652
|
+
helper.addComposable("useGraphqlQuery");
|
|
1653
|
+
helper.addComposable("useGraphqlMutation");
|
|
1654
|
+
helper.addComposable("useGraphqlState");
|
|
1655
|
+
helper.addComposable("useAsyncGraphqlQuery");
|
|
1656
|
+
if (helper.options.enableFileUploads) {
|
|
1657
|
+
helper.addComposable("useGraphqlUploadMutation");
|
|
1658
|
+
}
|
|
1659
|
+
helper.addServerUtil("useGraphqlQuery");
|
|
1660
|
+
helper.addServerUtil("useGraphqlMutation");
|
|
1661
|
+
helper.addServerUtil("doGraphqlRequest");
|
|
1662
|
+
}
|
|
1663
|
+
TEMPLATES.forEach((template) => {
|
|
1664
|
+
if (template.type === "static") {
|
|
1665
|
+
helper.addTemplate(template);
|
|
1666
|
+
} else {
|
|
1667
|
+
collector.addTemplate(template);
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
helper.applyBuildConfig();
|
|
1671
|
+
nuxt.hooks.hookOnce("modules:done", async () => {
|
|
1672
|
+
await nuxt.hooks.callHook("nuxt-graphql-middleware:init", moduleContext);
|
|
1673
|
+
await collector.init();
|
|
1674
|
+
});
|
|
1675
|
+
if (!helper.isDev) {
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
const devModeHandler = new DevModeHandler(
|
|
1679
|
+
nuxt,
|
|
1680
|
+
schemaProvider,
|
|
1681
|
+
collector,
|
|
1682
|
+
helper
|
|
1683
|
+
);
|
|
1684
|
+
devModeHandler.init();
|
|
890
1685
|
}
|
|
891
1686
|
});
|
|
892
1687
|
|