nuxt-graphql-middleware 5.0.0-alpha.1 → 5.0.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/client/200.html +11 -0
  2. package/dist/client/404.html +11 -0
  3. package/dist/client/_nuxt/AZpplOcD.js +1 -0
  4. package/dist/client/_nuxt/B2Rg1ezw.js +1 -0
  5. package/dist/client/_nuxt/Bt6N0bOg.js +2 -0
  6. package/dist/client/_nuxt/M311G39J.js +25 -0
  7. package/dist/client/_nuxt/builds/latest.json +1 -0
  8. package/dist/client/_nuxt/builds/meta/88c25798-8ea2-4139-90f2-506f3146e7b3.json +1 -0
  9. package/dist/client/_nuxt/entry.Cn9qfNGa.css +1 -0
  10. package/dist/client/_nuxt/error-404.ehK72JOs.css +1 -0
  11. package/dist/client/_nuxt/error-500._g0akJim.css +1 -0
  12. package/dist/client/_nuxt/index.DGEN-H8t.css +1 -0
  13. package/dist/client/_nuxt/u9er1db2.js +1 -0
  14. package/dist/client/index.html +11 -0
  15. package/dist/module.d.mts +57 -205
  16. package/dist/module.d.ts +57 -205
  17. package/dist/module.json +2 -2
  18. package/dist/module.mjs +1138 -563
  19. package/dist/runtime/components/CodeFrame.vue +61 -0
  20. package/dist/runtime/components/DevModeOverlay.vue +60 -0
  21. package/dist/runtime/components/ErrorExtensions.vue +23 -0
  22. package/dist/runtime/components/ErrorGroup.vue +89 -0
  23. package/dist/runtime/composables/nuxtApp.d.ts +2 -2
  24. package/dist/runtime/composables/nuxtApp.js +21 -1
  25. package/dist/runtime/composables/useAsyncGraphqlQuery.d.ts +7 -7
  26. package/dist/runtime/composables/useAsyncGraphqlQuery.js +10 -2
  27. package/dist/runtime/composables/useGraphqlMutation.d.ts +4 -4
  28. package/dist/runtime/composables/useGraphqlMutation.js +1 -1
  29. package/dist/runtime/composables/useGraphqlQuery.d.ts +4 -4
  30. package/dist/runtime/composables/useGraphqlQuery.js +1 -1
  31. package/dist/runtime/composables/useGraphqlState.d.ts +1 -1
  32. package/dist/runtime/composables/useGraphqlState.js +1 -1
  33. package/dist/runtime/composables/useGraphqlUploadMutation.d.ts +4 -4
  34. package/dist/runtime/composables/useGraphqlUploadMutation.js +2 -2
  35. package/dist/runtime/css/output.css +1 -0
  36. package/dist/runtime/helpers/composables.d.ts +17 -20
  37. package/dist/runtime/helpers/composables.js +0 -5
  38. package/dist/runtime/plugins/devMode.d.ts +2 -0
  39. package/dist/runtime/plugins/devMode.js +23 -0
  40. package/dist/runtime/plugins/provideState.d.ts +1 -1
  41. package/dist/runtime/{serverHandler → server/api}/debug.js +3 -7
  42. package/dist/runtime/server/api/mutation.js +28 -0
  43. package/dist/runtime/server/api/query.js +29 -0
  44. package/dist/runtime/server/api/upload.d.ts +2 -0
  45. package/dist/runtime/{serverHandler → server/api}/upload.js +13 -11
  46. package/dist/runtime/{serverHandler → server}/helpers/index.d.ts +10 -12
  47. package/dist/runtime/{serverHandler → server}/helpers/index.js +9 -26
  48. package/dist/runtime/server/utils/doGraphqlRequest.d.ts +18 -0
  49. package/dist/runtime/server/utils/doGraphqlRequest.js +67 -0
  50. package/dist/runtime/server/utils/index.d.ts +1 -1
  51. package/dist/runtime/server/utils/index.js +1 -1
  52. package/dist/runtime/server/utils/useGraphqlMutation.d.ts +4 -4
  53. package/dist/runtime/server/utils/useGraphqlQuery.d.ts +4 -4
  54. package/dist/runtime/serverOptions/defineGraphqlServerOptions.d.ts +4 -3
  55. package/dist/runtime/settings/index.d.ts +0 -14
  56. package/dist/runtime/settings/index.js +0 -6
  57. package/dist/runtime/types.d.ts +204 -3
  58. package/dist/types.d.mts +5 -5
  59. package/dist/types.d.ts +5 -5
  60. package/package.json +39 -36
  61. package/dist/runtime/serverHandler/index.js +0 -78
  62. package/dist/runtime/serverHandler/tsconfig.json +0 -3
  63. /package/dist/runtime/{serverHandler → server/api}/debug.d.ts +0 -0
  64. /package/dist/runtime/{serverHandler/index.d.ts → server/api/mutation.d.ts} +0 -0
  65. /package/dist/runtime/{serverHandler/upload.d.ts → server/api/query.d.ts} +0 -0
package/dist/module.mjs CHANGED
@@ -1,92 +1,25 @@
1
- import { loadSchema } from '@graphql-tools/load';
2
1
  import { fileURLToPath } from 'url';
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';
3
4
  import { relative } from 'pathe';
4
- import { defu } from 'defu';
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';
12
5
  import { basename } from 'node:path';
13
- import { Source, parse, printSourceLocation } from 'graphql';
14
- import { Generator } from 'graphql-typescript-deluxe';
15
- import { pascalCase } from 'change-case-all';
16
- import colors from 'picocolors';
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.1";
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.12";
90
23
 
91
24
  const logger = useLogger(name);
92
25
  const defaultOptions = {
@@ -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$1(path)) {
45
+ } else if (existsSync(path)) {
130
46
  return path;
131
47
  }
132
48
  const extension = extensions.find(
133
- (extension2) => existsSync$1(`${path}.${extension2}`)
49
+ (extension2) => existsSync(`${path}.${extension2}`)
134
50
  );
135
51
  return extension ? `${path}.${extension}` : null;
136
52
  };
137
53
 
138
- function groupOperationsByType(ops) {
139
- const result = {
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 ? colors.red(" x ") : colors.green(" \u2713 ");
78
+ const icon = hasErrors ? color.red(SYMBOL_CROSS) : color.green(SYMBOL_CHECK);
267
79
  const type = entry.type.padEnd(lengths.type);
268
- const namePadded = colors.bold(entry.name.padEnd(lengths.name));
269
- const name = hasErrors ? colors.red(namePadded) : colors.green(namePadded);
270
- const path = colors.dim(entry.path.padEnd(lengths.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 = colors.red(error.message);
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" + colors.red(printSourceLocation(error.source, location));
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;
@@ -324,12 +137,21 @@ class CollectedFile {
324
137
  return false;
325
138
  }
326
139
  }
140
+
327
141
  class Collector {
328
- constructor(schema, context, nuxtConfigDocuments = [], generatorOptions) {
142
+ constructor(schema, helper) {
329
143
  this.schema = schema;
330
- this.context = context;
331
- this.nuxtConfigDocuments = nuxtConfigDocuments;
332
- this.generator = new Generator(schema, generatorOptions);
144
+ this.helper = helper;
145
+ const mappedOptions = { ...helper.options.codegenConfig };
146
+ if (!mappedOptions.output) {
147
+ mappedOptions.output = {};
148
+ }
149
+ if (!mappedOptions.output.buildTypeDocFilePath) {
150
+ mappedOptions.output.buildTypeDocFilePath = (filePath) => {
151
+ return this.filePathToBuildRelative(filePath);
152
+ };
153
+ }
154
+ this.generator = new Generator(schema, mappedOptions);
333
155
  }
334
156
  /**
335
157
  * All collected files.
@@ -344,32 +166,50 @@ class Collector {
344
166
  */
345
167
  operationTimestamps = /* @__PURE__ */ new Map();
346
168
  /**
347
- * The generated TypeScript type template output.
169
+ * The generated operations and fragments.
348
170
  */
349
- outputTypes = "";
171
+ rpcItems = /* @__PURE__ */ new Map();
350
172
  /**
351
- * The generated oeprations file.
173
+ * The registered templates.
352
174
  */
353
- outputOperations = "";
175
+ templates = [];
354
176
  /**
355
- * The generated context template file.
177
+ * The generated template contents.
356
178
  */
357
- outputContext = "";
358
- /**
359
- * Whether we need to rebuild the Generator state.
360
- */
361
- needsRebuild = false;
362
- filePathToRelative(filePath) {
363
- return filePath.replace(this.context.srcDir, "~");
179
+ templateResult = /* @__PURE__ */ new Map();
180
+ async reset() {
181
+ this.files.clear();
182
+ this.generator.reset();
183
+ this.operationTimestamps.clear();
184
+ this.rpcItems.clear();
185
+ }
186
+ async updateSchema(schema) {
187
+ this.schema = schema;
188
+ this.generator.updateSchema(schema);
189
+ await this.reset();
190
+ await this.initDocuments();
191
+ }
192
+ filePathToBuildRelative(filePath) {
193
+ return "./" + this.helper.toBuildRelative(filePath);
194
+ }
195
+ filePathToSourceRelative(filePath) {
196
+ return "./" + relative(process.cwd(), filePath);
364
197
  }
365
198
  operationToLogEntry(operation, errors) {
366
199
  return {
367
200
  name: operation.graphqlName,
368
201
  type: operation.operationType,
369
- path: this.filePathToRelative(operation.filePath),
202
+ path: this.filePathToSourceRelative(operation.filePath),
370
203
  errors
371
204
  };
372
205
  }
206
+ getTemplate(template) {
207
+ const content = this.templateResult.get(template);
208
+ if (content === void 0) {
209
+ throw new Error(`Missing template content: ${template}`);
210
+ }
211
+ return content;
212
+ }
373
213
  /**
374
214
  * Executes code gen and performs validation for operations.
375
215
  */
@@ -377,12 +217,22 @@ class Collector {
377
217
  const output = this.generator.build();
378
218
  const operations = output.getCollectedOperations();
379
219
  const generatedCode = output.getGeneratedCode();
380
- this.outputOperations = output.getOperationsFile();
381
- this.outputTypes = output.getEverything();
382
- this.outputContext = generateContextTemplate(
383
- operations,
384
- this.context.serverApiPrefix
385
- );
220
+ this.templates.forEach((template) => {
221
+ if (template.build) {
222
+ const filename = template.options.path + ".js";
223
+ this.templateResult.set(
224
+ filename,
225
+ template.build(output, this.helper).trim()
226
+ );
227
+ }
228
+ if (template.buildTypes) {
229
+ const filename = template.options.path + ".d.ts";
230
+ this.templateResult.set(
231
+ filename,
232
+ template.buildTypes(output, this.helper).trim()
233
+ );
234
+ }
235
+ });
386
236
  const fragmentMap = /* @__PURE__ */ new Map();
387
237
  const operationSourceMap = /* @__PURE__ */ new Map();
388
238
  for (const code of generatedCode) {
@@ -401,9 +251,7 @@ class Collector {
401
251
  if (previousTimestamp === operation.timestamp) {
402
252
  continue;
403
253
  }
404
- const fragments = operation.dependencies.map(
405
- (v) => v.type === "fragment-name" ? fragmentMap.get(v.value) || "" : ""
406
- ).join("\n");
254
+ const fragments = operation.getGraphQLFragmentDependencies().map((v) => fragmentMap.get(v) || "").join("\n");
407
255
  const fullOperation = operationSourceMap.get(operation.graphqlName) + fragments;
408
256
  const source = new Source(fullOperation, basename(operation.filePath));
409
257
  const document = parse(source);
@@ -413,72 +261,153 @@ class Collector {
413
261
  } else {
414
262
  this.operationTimestamps.set(operation.graphqlName, operation.timestamp);
415
263
  }
416
- logEntries.push(this.operationToLogEntry(operation, errors));
264
+ const shouldLog = errors.length || !this.helper.options.logOnlyErrors;
265
+ if (shouldLog) {
266
+ logEntries.push(this.operationToLogEntry(operation, errors));
267
+ }
417
268
  }
418
269
  logAllEntries(logEntries);
419
270
  if (hasErrors) {
420
271
  throw new Error("GraphQL errors");
421
272
  }
273
+ if (this.helper.isDev) {
274
+ for (const code of generatedCode) {
275
+ const id = `${code.identifier}_${code.graphqlName}`;
276
+ if (code.identifier === "fragment" || code.identifier === "mutation" || code.identifier === "query") {
277
+ if (this.rpcItems.get(id)?.timestamp === code.timestamp) {
278
+ continue;
279
+ }
280
+ const fragmentDepdendencies = code.getGraphQLFragmentDependencies().map((name) => fragmentMap.get(name) || "").join("\n\n");
281
+ this.rpcItems.set(id, {
282
+ id,
283
+ timestamp: code.timestamp,
284
+ source: code.source + "\n\n" + fragmentDepdendencies,
285
+ name: code.graphqlName,
286
+ filePath: code.filePath,
287
+ identifier: code.identifier
288
+ });
289
+ }
290
+ }
291
+ }
292
+ }
293
+ buildErrorMessage(error) {
294
+ let output = "";
295
+ if (error instanceof FieldNotFoundError || error instanceof TypeNotFoundError || error instanceof FragmentNotFoundError) {
296
+ const filePath = error.context?.filePath;
297
+ const file = filePath ? this.files.get(filePath) : null;
298
+ if (filePath) {
299
+ output += ` | ${this.filePathToSourceRelative(filePath)}
300
+ `;
301
+ }
302
+ output += "\n" + error.message + "\n\n";
303
+ if (file) {
304
+ output += file.fileContents;
305
+ }
306
+ } else if (error instanceof Error) {
307
+ output += "\n" + error.message;
308
+ }
309
+ return output;
310
+ }
311
+ logError(error) {
312
+ let output = `${SYMBOL_CROSS}`;
313
+ output += this.buildErrorMessage(error);
314
+ logger.error(color.red(output));
422
315
  }
423
316
  /**
424
- * Get all file paths that match the import patterns.
317
+ * Initialise the collector.
318
+ *
319
+ * In dev mode, the method will call itself recursively until all documents
320
+ * are valid.
321
+ *
322
+ * If not in dev mode the method will throw an error when documents are not
323
+ * valid.
425
324
  */
426
- async getImportPatternFiles() {
427
- if (this.context.patterns.length) {
428
- return resolveFiles(this.context.srcDir, this.context.patterns, {
429
- followSymbolicLinks: false
430
- });
325
+ async init() {
326
+ try {
327
+ await this.initDocuments();
328
+ } catch {
329
+ if (this.helper.isDev) {
330
+ const shouldRevalidate = await this.helper.prompt.confirm(
331
+ "Do you want to revalidate the GraphQL documents?"
332
+ );
333
+ if (shouldRevalidate === "yes") {
334
+ await this.reset();
335
+ return this.init();
336
+ }
337
+ }
338
+ throw new Error("Graphql document validation failed.");
431
339
  }
432
- return [];
433
340
  }
434
341
  /**
435
342
  * Initialise the collector.
436
343
  */
437
- async init() {
438
- const files = await this.getImportPatternFiles();
439
- for (const filePath of files) {
440
- await this.addFile(filePath);
441
- }
442
- this.nuxtConfigDocuments.forEach((docString, i) => {
443
- const pseudoPath = `nuxt.config.ts[${i}]`;
444
- const file = new CollectedFile(pseudoPath, docString, false);
445
- this.files.set(pseudoPath, file);
446
- this.generator.add({
447
- filePath: "~/nuxt.config.ts",
448
- documentNode: file.parsed
449
- });
450
- });
451
- this.buildState();
344
+ async initDocuments() {
345
+ try {
346
+ const files = await this.helper.getImportPatternFiles();
347
+ for (const filePath of files) {
348
+ await this.addFile(filePath);
349
+ }
350
+ const nuxtConfigDocuments = this.helper.options.documents.join("\n\n");
351
+ if (nuxtConfigDocuments.length) {
352
+ const filePath = this.helper.paths.nuxtConfig;
353
+ const file = new CollectedFile(filePath, nuxtConfigDocuments, false);
354
+ this.files.set(filePath, file);
355
+ this.generator.add({
356
+ filePath,
357
+ documentNode: file.parsed
358
+ });
359
+ }
360
+ this.buildState();
361
+ logger.success("All GraphQL documents are valid.");
362
+ } catch (e) {
363
+ this.logError(e);
364
+ throw new Error("GraphQL document validation failed.");
365
+ }
452
366
  }
453
367
  /**
454
368
  * Add a file.
455
369
  */
456
370
  async addFile(filePath) {
457
371
  const file = await CollectedFile.fromFilePath(filePath);
372
+ if (!file.fileContents) {
373
+ return null;
374
+ }
458
375
  this.files.set(filePath, file);
459
376
  this.generator.add({
460
- filePath: this.filePathToRelative(filePath),
377
+ filePath,
461
378
  documentNode: file.parsed
462
379
  });
463
380
  return file;
464
381
  }
465
382
  async handleAdd(filePath) {
466
- await this.addFile(filePath);
467
- return true;
383
+ if (!this.helper.matchesImportPattern(filePath)) {
384
+ return false;
385
+ }
386
+ const result = await this.addFile(filePath);
387
+ return !!result;
468
388
  }
469
389
  async handleChange(filePath) {
390
+ const matchesImportPattern = this.helper.matchesImportPattern(filePath);
391
+ const fileExists = this.files.has(filePath);
392
+ if (!matchesImportPattern && !fileExists) {
393
+ return false;
394
+ }
470
395
  const file = this.files.get(filePath);
471
396
  if (!file) {
472
- return false;
397
+ return this.handleAdd(filePath);
473
398
  }
474
- const needsUpdate = await file.update();
475
- if (!needsUpdate) {
476
- return false;
399
+ try {
400
+ const needsUpdate = await file.update();
401
+ if (!needsUpdate) {
402
+ return false;
403
+ }
404
+ this.generator.update({
405
+ filePath,
406
+ documentNode: file.parsed
407
+ });
408
+ } catch {
409
+ return this.handleUnlink(filePath);
477
410
  }
478
- this.generator.update({
479
- filePath: this.filePathToRelative(filePath),
480
- documentNode: file.parsed
481
- });
482
411
  return true;
483
412
  }
484
413
  handleUnlink(filePath) {
@@ -487,7 +416,7 @@ class Collector {
487
416
  return false;
488
417
  }
489
418
  this.files.delete(filePath);
490
- this.generator.remove(this.filePathToRelative(filePath));
419
+ this.generator.remove(filePath);
491
420
  return true;
492
421
  }
493
422
  handleUnlinkDir(folderPath) {
@@ -506,8 +435,9 @@ class Collector {
506
435
  * Handle the watcher event for the given file path.
507
436
  */
508
437
  async handleWatchEvent(event, filePath) {
438
+ let hasChanged = false;
439
+ const oldOperationTimestamps = new Map(this.operationTimestamps);
509
440
  try {
510
- let hasChanged = false;
511
441
  if (event === "add") {
512
442
  hasChanged = await this.handleAdd(filePath);
513
443
  } else if (event === "change") {
@@ -516,357 +446,1002 @@ class Collector {
516
446
  hasChanged = this.handleUnlink(filePath);
517
447
  } else if (event === "unlinkDir") {
518
448
  hasChanged = this.handleUnlinkDir(filePath);
519
- } else if (event === "addDir") {
520
449
  }
521
450
  if (hasChanged) {
522
451
  this.buildState();
523
452
  }
524
453
  } catch (e) {
525
454
  this.generator.resetCaches();
526
- console.log(e);
455
+ logger.error("Failed to update GraphQL code.");
456
+ this.logError(e);
457
+ return {
458
+ hasChanged: false,
459
+ affectedOperations: [],
460
+ error: { message: this.buildErrorMessage(e) }
461
+ };
462
+ }
463
+ const affectedOperations = [];
464
+ if (hasChanged) {
465
+ logger.success("Finished GraphQL code update successfully.");
466
+ for (const [name, newTimestamp] of this.operationTimestamps) {
467
+ const oldTimestamp = oldOperationTimestamps.get(name);
468
+ if (!oldTimestamp || oldTimestamp !== newTimestamp) {
469
+ affectedOperations.push(name);
470
+ }
471
+ }
527
472
  }
473
+ return { hasChanged, affectedOperations };
528
474
  }
529
475
  /**
530
- * Get the TypeScript types template contents.
476
+ * Adds a virtual template (not written to disk) for both Nuxt and Nitro.
477
+ *
478
+ * For some reason a template written to disk works for both Nuxt and Nitro,
479
+ * but a virtual template requires adding two templates.
531
480
  */
532
- getTemplateTypes() {
533
- return this.outputTypes;
481
+ addVirtualTemplate(template) {
482
+ const filename = template.options.path + ".js";
483
+ const getContents = () => this.getTemplate(filename);
484
+ addTemplate({
485
+ filename,
486
+ getContents
487
+ });
488
+ addServerTemplate({
489
+ // Since this is a virtual template, the name must match the final
490
+ // alias, example:
491
+ // - nuxt-graphql-middleware/foobar.mjs => #nuxt-graphql-middleware/foobar
492
+ //
493
+ // That way we can reference the same template using the alias in both
494
+ // Nuxt and Nitro environments.
495
+ filename: "#" + template.options.path,
496
+ getContents
497
+ });
534
498
  }
535
499
  /**
536
- * Get the context template contents.
500
+ * Adds a template that dependes on Collector state.
537
501
  */
538
- getTemplateContext() {
539
- return this.outputContext;
502
+ addTemplate(template) {
503
+ this.templates.push(template);
504
+ if (template.build) {
505
+ if (template.options.virtual) {
506
+ this.addVirtualTemplate(template);
507
+ } else {
508
+ const filename = template.options.path + ".js";
509
+ addTemplate({
510
+ filename,
511
+ write: true,
512
+ getContents: () => this.getTemplate(filename)
513
+ });
514
+ }
515
+ }
516
+ if (template.buildTypes) {
517
+ const filename = template.options.path + ".d.ts";
518
+ addTypeTemplate(
519
+ {
520
+ filename,
521
+ write: true,
522
+ getContents: () => this.getTemplate(filename)
523
+ },
524
+ {
525
+ nuxt: true,
526
+ nitro: true
527
+ }
528
+ );
529
+ }
530
+ }
531
+ }
532
+
533
+ class SchemaProvider {
534
+ constructor(helper) {
535
+ this.helper = helper;
540
536
  }
541
537
  /**
542
- * Get the operations template contents.
538
+ * The raw schema content.
543
539
  */
544
- getTemplateOperations() {
545
- return this.outputOperations;
540
+ schemaContent = "";
541
+ /**
542
+ * The parsed schema object.
543
+ */
544
+ schema = null;
545
+ async init() {
546
+ try {
547
+ await this.loadSchema();
548
+ } catch (error) {
549
+ logger.error(error);
550
+ const hasLoaded = await this.loadFromDiskFallback();
551
+ if (!hasLoaded) {
552
+ throw new Error("Failed to load GraphQL schema.");
553
+ }
554
+ }
555
+ }
556
+ async loadFromDiskFallback() {
557
+ const hasSchemaOnDisk = await this.hasSchemaOnDisk();
558
+ if (this.helper.isDev && hasSchemaOnDisk && this.helper.options.downloadSchema) {
559
+ const shouldUseFromDisk = await this.helper.prompt.confirm(
560
+ "Do you want to continue with the previously downloaded schema from disk?"
561
+ );
562
+ if (shouldUseFromDisk === "yes") {
563
+ await this.loadSchema({ forceDisk: true });
564
+ return true;
565
+ }
566
+ }
567
+ return false;
546
568
  }
547
569
  /**
548
- * Log results (including parse/validation errors).
570
+ * Loads the schema from disk.
571
+ *
572
+ * @returns The schema contents from disk.
549
573
  */
550
- logDocuments(logEverything) {
574
+ async loadSchemaFromDisk() {
575
+ const fileExists = await this.hasSchemaOnDisk();
576
+ if (!fileExists) {
577
+ logger.error(
578
+ '"downloadSchema" is set to false but no schema exists at ' + this.helper.paths.schema
579
+ );
580
+ throw new Error("Missing GraphQL schema.");
581
+ }
582
+ logger.info(`Loading GraphQL schema from disk: ${this.helper.paths.schema}`);
583
+ return await fs.readFile(this.helper.paths.schema).then((v) => v.toString());
584
+ }
585
+ /**
586
+ * Downloads the schema and saves it to disk.
587
+ *
588
+ * @returns The schema contents.
589
+ */
590
+ downloadSchema() {
591
+ const endpoint = this.helper.options.graphqlEndpoint;
592
+ if (!endpoint) {
593
+ throw new Error("Missing graphqlEndpoint config.");
594
+ }
595
+ const pluginConfig = this.helper.options.codegenSchemaConfig?.urlSchemaOptions;
596
+ const schemaAstConfig = this.helper.options.codegenSchemaConfig?.schemaAstConfig || {
597
+ sort: true
598
+ };
599
+ const config = {
600
+ schema: endpoint,
601
+ pluginLoader: (name) => {
602
+ switch (name) {
603
+ case "@graphql-codegen/schema-ast":
604
+ return Promise.resolve(PluginSchemaAst);
605
+ }
606
+ throw new Error(`graphql-codegen plugin not found: ${name}`);
607
+ },
608
+ silent: true,
609
+ errorsOnly: true,
610
+ config: pluginConfig,
611
+ generates: {
612
+ [this.helper.paths.schema]: {
613
+ plugins: ["schema-ast"],
614
+ config: schemaAstConfig
615
+ }
616
+ }
617
+ };
618
+ logger.info(`Downloading GraphQL schema from "${endpoint}".`);
619
+ return generate(config, true).then((v) => v[0]?.content);
620
+ }
621
+ /**
622
+ * Determine if the schema exists on disk.
623
+ *
624
+ * @returns True if the schema file exists on disk.
625
+ */
626
+ hasSchemaOnDisk() {
627
+ return fs.access(this.helper.paths.schema).then(() => true).catch(() => false);
628
+ }
629
+ /**
630
+ * Load the schema either from disk or by downloading it.
631
+ *
632
+ * @param forceDownload - Forces downloading the schema.
633
+ */
634
+ async loadSchema(opts) {
635
+ if (opts?.forceDisk) {
636
+ this.schemaContent = await this.loadSchemaFromDisk();
637
+ } else if (this.helper.options.downloadSchema || opts?.forceDownload) {
638
+ this.schemaContent = await this.downloadSchema();
639
+ } else {
640
+ this.schemaContent = await this.loadSchemaFromDisk();
641
+ }
642
+ this.schema = await loadSchema(this.schemaContent, {
643
+ loaders: []
644
+ });
645
+ }
646
+ /**
647
+ * Get the schema.
648
+ *
649
+ * @returns The parsed GraphQL schema object.
650
+ */
651
+ getSchema() {
652
+ if (!this.schema) {
653
+ throw new Error("Failed to load schema.");
654
+ }
655
+ return this.schema;
551
656
  }
552
657
  }
553
658
 
554
- const RPC_NAMESPACE = "nuxt-graphql-middleware";
555
- const module = defineNuxtModule({
556
- meta: {
557
- name,
558
- configKey: "graphqlMiddleware",
559
- version,
560
- compatibility: {
561
- nuxt: ">=3.13.0"
562
- }
563
- },
564
- defaults: defaultOptions,
565
- async setup(passedOptions, nuxt) {
566
- const options = defu({}, passedOptions, defaultOptions);
567
- function addAlias(name2, aliasPath) {
568
- nuxt.options.alias[name2] = aliasPath;
659
+ const unicode = isUnicodeSupported();
660
+ const s = (c, fallback) => unicode ? c : fallback;
661
+ const S_BAR = s("\u2502", "|");
662
+ const S_STEP_ACTIVE = s("\u25C6", "*");
663
+ const S_STEP_CANCEL = s("\u25A0", "x");
664
+ const S_STEP_ERROR = s("\u25B2", "x");
665
+ const S_STEP_SUBMIT = s("\u25C7", "o");
666
+ const S_RADIO_ACTIVE = s("\u25CF", ">");
667
+ const S_RADIO_INACTIVE = s("\u25CB", " ");
668
+ const S_BAR_END = s("\u2514", "\u2014");
669
+ const symbol = (state) => {
670
+ switch (state) {
671
+ case "initial":
672
+ case "active":
673
+ return color.cyan(S_STEP_ACTIVE);
674
+ case "cancel":
675
+ return color.red(S_STEP_CANCEL);
676
+ case "error":
677
+ return color.yellow(S_STEP_ERROR);
678
+ case "submit":
679
+ return color.green(S_STEP_SUBMIT);
680
+ }
681
+ };
682
+ class ConsolePrompt {
683
+ abortController = null;
684
+ confirm(message) {
685
+ this.abort();
686
+ this.abortController = new AbortController();
687
+ const active = "Yes";
688
+ const inactive = "No";
689
+ return new ConfirmPrompt({
690
+ active,
691
+ inactive,
692
+ initialValue: true,
693
+ signal: this.abortController.signal,
694
+ render() {
695
+ const title = `${color.gray(S_BAR)}
696
+ ${symbol(this.state)} ${message}
697
+ `;
698
+ const value = this.value ? active : inactive;
699
+ switch (this.state) {
700
+ case "submit":
701
+ return `${title}${color.gray(S_BAR)} ${color.dim(value)}`;
702
+ case "cancel":
703
+ return `${title}${color.gray(S_BAR)} ${color.strikethrough(
704
+ color.dim(value)
705
+ )}
706
+ ${color.gray(S_BAR)}`;
707
+ default: {
708
+ 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)}`}
709
+ ${color.cyan(S_BAR_END)}
710
+ `;
711
+ }
712
+ }
713
+ }
714
+ }).prompt().then((v) => {
715
+ const result = v;
716
+ if (result === true) {
717
+ return "yes";
718
+ } else if (result === false) {
719
+ return "no";
720
+ }
721
+ return "cancel";
722
+ });
723
+ }
724
+ abort() {
725
+ if (this.abortController) {
726
+ this.abortController.abort();
727
+ this.abortController = null;
569
728
  }
729
+ }
730
+ }
731
+
732
+ class ModuleHelper {
733
+ constructor(nuxt, moduleUrl, options) {
734
+ this.nuxt = nuxt;
570
735
  const isModuleBuild = process.env.MODULE_BUILD === "true" && nuxt.options._prepare;
736
+ const mergedOptions = defu({}, options, defaultOptions);
737
+ if (!mergedOptions.autoImportPatterns) {
738
+ mergedOptions.autoImportPatterns = [
739
+ "~~/**/*.{gql,graphql}",
740
+ "!node_modules"
741
+ ];
742
+ }
571
743
  if (isModuleBuild) {
572
- options.graphqlEndpoint = "http://localhost";
573
- options.downloadSchema = false;
574
- options.schemaPath = "~~/schema.graphql";
575
- options.autoImportPatterns = [
744
+ mergedOptions.graphqlEndpoint = "http://localhost";
745
+ mergedOptions.downloadSchema = false;
746
+ mergedOptions.schemaPath = "~~/schema.graphql";
747
+ mergedOptions.autoImportPatterns = [
576
748
  "~~/playground/**/*.{gql,graphql}",
577
749
  "!node_modules"
578
750
  ];
579
751
  }
580
- if (!passedOptions.autoImportPatterns) {
581
- options.autoImportPatterns = ["~~/**/*.{gql,graphql}", "!node_modules"];
582
- }
583
- options.autoImportPatterns = (options.autoImportPatterns || []).map(
584
- (pattern) => {
585
- return resolveAlias(pattern);
752
+ const srcResolver = createResolver(nuxt.options.srcDir);
753
+ mergedOptions.autoImportPatterns = (mergedOptions.autoImportPatterns || []).map((pattern) => {
754
+ if (pattern.startsWith("!")) {
755
+ return pattern;
586
756
  }
587
- );
757
+ const resolved = resolveAlias(pattern);
758
+ return srcResolver.resolve(resolved);
759
+ });
760
+ this.options = mergedOptions;
588
761
  if (!nuxt.options._prepare) {
589
- validateOptions(options);
762
+ validateOptions(this.options);
590
763
  }
591
- const moduleResolver = createResolver(import.meta.url);
592
- const serverResolver = createResolver(nuxt.options.serverDir);
593
- const srcResolver = createResolver(nuxt.options.srcDir);
594
- const appResolver = createResolver(nuxt.options.dir.app);
595
- const rootDir = nuxt.options.rootDir;
596
- const rootResolver = createResolver(rootDir);
597
- const { schemaPath, schemaContent } = await getSchemaPath(
598
- resolveAlias(options.schemaPath),
599
- options,
600
- rootResolver.resolve,
601
- options.downloadSchema
602
- );
603
- const schema = await loadSchema(schemaContent, {
604
- loaders: []
605
- });
606
- const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url));
607
- nuxt.options.build.transpile.push(runtimeDir);
608
- const context = {
609
- patterns: options.autoImportPatterns || [],
610
- srcDir: nuxt.options.srcDir,
611
- schemaPath,
612
- serverApiPrefix: options.serverApiPrefix
764
+ this.isDev = nuxt.options.dev;
765
+ this.resolvers = {
766
+ module: createResolver(moduleUrl),
767
+ server: createResolver(nuxt.options.serverDir),
768
+ src: srcResolver,
769
+ app: createResolver(nuxt.options.dir.app),
770
+ root: createResolver(nuxt.options.rootDir)
613
771
  };
614
- const collector = new Collector(
615
- schema,
616
- context,
617
- options.documents,
618
- options.codegenConfig
772
+ this.paths = {
773
+ runtimeTypes: "",
774
+ root: nuxt.options.rootDir,
775
+ nuxtConfig: this.resolvers.root.resolve("nuxt.config.ts"),
776
+ serverDir: nuxt.options.serverDir,
777
+ schema: this.resolvers.root.resolve(
778
+ resolveAlias(this.options.schemaPath)
779
+ ),
780
+ serverOptions: "",
781
+ clientOptions: this.findClientOptions(),
782
+ moduleBuildDir: nuxt.options.buildDir + "/nuxt-graphql-middleware",
783
+ moduleTypesDir: nuxt.options.buildDir + "/graphql-operations"
784
+ };
785
+ this.paths.runtimeTypes = this.toModuleBuildRelative(
786
+ this.resolvers.module.resolve("./runtime/types.ts")
619
787
  );
620
- await collector.init();
621
- if (options.devtools) {
622
- const clientPath = moduleResolver.resolve("./client");
623
- setupDevToolsUI(nuxt, clientPath);
624
- const setupRpc = () => {
625
- extendServerRpc(RPC_NAMESPACE, {
626
- // register server RPC functions
627
- getModuleOptions() {
628
- return options;
629
- },
630
- getDocuments() {
631
- return [];
632
- }
633
- });
634
- };
635
- try {
636
- setupRpc();
637
- } catch (_e) {
638
- onDevToolsInitialized(() => {
639
- setupRpc();
640
- });
788
+ this.paths.serverOptions = this.findServerOptions();
789
+ }
790
+ resolvers;
791
+ paths;
792
+ isDev;
793
+ options;
794
+ prompt = new ConsolePrompt();
795
+ nitroExternals = [];
796
+ /**
797
+ * Find the path to the graphqlMiddleware.serverOptions.ts file.
798
+ */
799
+ findServerOptions() {
800
+ const newPath = this.resolvers.server.resolve(
801
+ "graphqlMiddleware.serverOptions"
802
+ );
803
+ const serverPath = fileExists(newPath);
804
+ if (serverPath) {
805
+ return serverPath;
806
+ }
807
+ const candidates = [
808
+ this.resolvers.root.resolve("graphqlMiddleware.serverOptions"),
809
+ this.resolvers.root.resolve("app/graphqlMiddleware.serverOptions"),
810
+ this.resolvers.src.resolve("graphqlMiddleware.serverOptions")
811
+ ];
812
+ for (let i = 0; i < candidates.length; i++) {
813
+ const path = candidates[i];
814
+ const filePath = fileExists(path);
815
+ if (filePath) {
816
+ throw new Error(
817
+ `The graphqlMiddleware.serverOptions file should be placed in Nuxt's <serverDir> ("${this.paths.serverDir}/graphqlMiddleware.serverOptions.ts").`
818
+ );
641
819
  }
642
820
  }
643
- nuxt.options.runtimeConfig.public["nuxt-graphql-middleware"] = {
644
- serverApiPrefix: options.serverApiPrefix
645
- };
646
- nuxt.options.appConfig.graphqlMiddleware = {
647
- clientCacheEnabled: !!options.clientCache?.enabled,
648
- clientCacheMaxSize: options.clientCache?.maxSize || 100
649
- };
650
- nuxt.options.runtimeConfig.graphqlMiddleware = {
651
- graphqlEndpoint: options.graphqlEndpoint || ""
652
- };
653
- if (options.includeComposables) {
654
- const nuxtComposables = [
655
- "useGraphqlQuery",
656
- "useGraphqlMutation",
657
- "useGraphqlState",
658
- "useAsyncGraphqlQuery"
659
- ];
660
- if (options.enableFileUploads) {
661
- nuxtComposables.push("useGraphqlUploadMutation");
821
+ logger.info("No graphqlMiddleware.serverOptions file found.");
822
+ return null;
823
+ }
824
+ findClientOptions() {
825
+ const clientOptionsPath = this.resolvers.app.resolve(
826
+ "graphqlMiddleware.clientOptions"
827
+ );
828
+ if (fileExists(clientOptionsPath)) {
829
+ return clientOptionsPath;
830
+ }
831
+ return null;
832
+ }
833
+ /**
834
+ * Transform the path relative to the module's build directory.
835
+ *
836
+ * @param path - The absolute path.
837
+ *
838
+ * @returns The path relative to the module's build directory.
839
+ */
840
+ toModuleBuildRelative(path) {
841
+ return relative(this.paths.moduleBuildDir, path);
842
+ }
843
+ /**
844
+ * Transform the path relative to the Nuxt build directory.
845
+ *
846
+ * @param path - The absolute path.
847
+ *
848
+ * @returns The path relative to the module's build directory.
849
+ */
850
+ toBuildRelative(path) {
851
+ return relative(this.nuxt.options.buildDir, path);
852
+ }
853
+ /**
854
+ * Get all file paths that match the import patterns.
855
+ */
856
+ async getImportPatternFiles() {
857
+ return resolveFiles(
858
+ this.nuxt.options.srcDir,
859
+ this.options.autoImportPatterns,
860
+ {
861
+ followSymbolicLinks: false
662
862
  }
663
- nuxtComposables.forEach((name2) => {
664
- addImports({
665
- from: moduleResolver.resolve("./runtime/composables/" + name2),
666
- name: name2
667
- });
863
+ );
864
+ }
865
+ matchesImportPattern(filePath) {
866
+ return micromatch.isMatch(filePath, this.options.autoImportPatterns) || this.options.autoImportPatterns.includes(filePath);
867
+ }
868
+ addAlias(name, path) {
869
+ this.nuxt.options.alias[name] = path;
870
+ const pathFromName = `./${name.substring(1)}`;
871
+ this.nuxt.options.nitro.typescript ||= {};
872
+ this.nuxt.options.nitro.typescript.tsConfig ||= {};
873
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions ||= {};
874
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths ||= {};
875
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths[name] = [
876
+ pathFromName
877
+ ];
878
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths[name + "/*"] = [pathFromName + "/*"];
879
+ this.nuxt.options.typescript.tsConfig ||= {};
880
+ this.nuxt.options.typescript.tsConfig.compilerOptions ||= {};
881
+ this.nuxt.options.typescript.tsConfig.compilerOptions.paths ||= {};
882
+ this.nuxt.options.typescript.tsConfig.compilerOptions.paths[name] = [
883
+ pathFromName
884
+ ];
885
+ this.nuxt.options.typescript.tsConfig.compilerOptions.paths[name + "/*"] = [
886
+ pathFromName + "/*"
887
+ ];
888
+ this.inlineNitroExternals(name);
889
+ }
890
+ inlineNitroExternals(arg) {
891
+ const path = typeof arg === "string" ? arg : arg.dst;
892
+ this.nitroExternals.push(path);
893
+ this.transpile(path);
894
+ }
895
+ transpile(path) {
896
+ this.nuxt.options.build.transpile.push(path);
897
+ }
898
+ applyBuildConfig() {
899
+ this.nuxt.options.nitro.externals ||= {};
900
+ this.nuxt.options.nitro.externals.inline ||= [];
901
+ this.nuxt.options.nitro.externals.inline.push(...this.nitroExternals);
902
+ }
903
+ addTemplate(template) {
904
+ if (template.build) {
905
+ const content = template.build(this).trim();
906
+ addTemplate({
907
+ filename: template.options.path + ".js",
908
+ write: true,
909
+ getContents: () => content
668
910
  });
669
- const serverUtils = ["useGraphqlQuery", "useGraphqlMutation"].map(
670
- (name2) => {
671
- return {
672
- from: moduleResolver.resolve("./runtime/server/utils/" + name2),
673
- name: name2
674
- };
675
- }
676
- );
677
- addServerImports(serverUtils);
678
911
  }
679
- const templateTypescript = addTemplate({
680
- filename: GraphqlMiddlewareTemplate.OperationTypes,
681
- write: true,
682
- getContents: () => collector.getTemplateTypes()
683
- });
684
- addAlias("#graphql-operations", templateTypescript.dst);
685
- const templateDocuments = addTemplate({
686
- filename: GraphqlMiddlewareTemplate.Documents,
687
- write: true,
688
- getContents: () => collector.getTemplateOperations()
912
+ if (template.buildTypes) {
913
+ const content = template.buildTypes(this).trim();
914
+ const filename = template.options.path + ".d.ts";
915
+ addTypeTemplate({
916
+ filename,
917
+ write: true,
918
+ getContents: () => content
919
+ });
920
+ }
921
+ }
922
+ addPlugin(path) {
923
+ addPlugin(this.resolvers.module.resolve(path), {
924
+ append: false
689
925
  });
690
- addAlias("#graphql-documents", templateDocuments.dst);
691
- const templateContext = addTemplate({
692
- filename: GraphqlMiddlewareTemplate.ComposableContext,
693
- write: true,
694
- getContents: () => collector.getTemplateContext()
926
+ }
927
+ addServerHandler(name, path, method) {
928
+ addServerHandler({
929
+ handler: this.resolvers.module.resolve("./runtime/server/api/" + name),
930
+ route: this.options.serverApiPrefix + path,
931
+ method
695
932
  });
696
- addAlias("#nuxt-graphql-middleware/generated-types", templateContext.dst);
697
- addTemplate({
698
- write: true,
699
- filename: "nuxt-graphql-middleware/graphql-documents.d.ts",
700
- getContents: () => {
701
- return `
702
- import type {
703
- GraphqlMiddlewareQuery,
704
- GraphqlMiddlewareMutation,
705
- } from '#nuxt-graphql-middleware/generated-types'
706
-
707
- declare module '#graphql-documents' {
708
- type Operations = {
709
- query: GraphqlMiddlewareQuery
710
- mutation: GraphqlMiddlewareMutation
711
933
  }
712
- const operations: Operations
713
- export { operations, Operations }
714
- }
715
- `;
716
- }
934
+ addComposable(name) {
935
+ addImports({
936
+ from: this.resolvers.module.resolve("./runtime/composables/" + name),
937
+ name
717
938
  });
718
- const findServerOptions = () => {
719
- const newPath = serverResolver.resolve("graphqlMiddleware.serverOptions");
720
- const serverPath = fileExists(newPath);
721
- if (serverPath) {
722
- return serverPath;
723
- }
724
- const candidates = [
725
- rootResolver.resolve("graphqlMiddleware.serverOptions"),
726
- rootResolver.resolve("app/graphqlMiddleware.serverOptions"),
727
- srcResolver.resolve("graphqlMiddleware.serverOptions")
728
- ];
729
- for (let i = 0; i < candidates.length; i++) {
730
- const path = candidates[i];
731
- const filePath = fileExists(path);
732
- if (filePath) {
733
- logger.warn(
734
- `The graphqlMiddleware.serverOptions file should be placed in Nuxt's <serverDir> ("${nuxt.options.serverDir}/graphqlMiddleware.serverOptions.ts"). The new path will be enforced in the next major release.`
735
- );
736
- return filePath;
737
- }
738
- }
739
- logger.info("No graphqlMiddleware.serverOptions file found.");
740
- };
741
- const resolvedPath = findServerOptions();
742
- const moduleTypesPath = relative(
743
- nuxt.options.buildDir,
744
- moduleResolver.resolve("./types")
745
- );
746
- const resolvedPathRelative = resolvedPath ? relative(nuxt.options.buildDir, resolvedPath) : null;
747
- const template = addTemplate({
748
- filename: "graphqlMiddleware.serverOptions.mjs",
749
- write: true,
750
- getContents: () => {
751
- const serverOptionsLine = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions = {}`;
752
- return `
753
- ${serverOptionsLine}
754
- export { serverOptions }
755
- `;
939
+ }
940
+ addServerUtil(name) {
941
+ addServerImports([
942
+ {
943
+ from: this.resolvers.module.resolve("./runtime/server/utils/" + name),
944
+ name
756
945
  }
757
- });
758
- addTemplate({
759
- filename: "graphqlMiddleware.serverOptions.d.ts",
760
- write: true,
761
- getContents: () => {
762
- const serverOptionsLineTypes = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions: GraphqlMiddlewareServerOptions = {}`;
763
- return `
764
- import type { GraphqlMiddlewareServerOptions } from '${moduleTypesPath}'
765
- ${serverOptionsLineTypes}
766
- import type { GraphqlServerResponse } from '${runtimeTypesPath}'
767
- import type { GraphqlMiddlewareResponseUnion } from '#nuxt-graphql-middleware/generated-types'
768
-
769
- type GraphqlResponseAdditions =
770
- typeof serverOptions extends GraphqlMiddlewareServerOptions<infer R, any, any> ? R : {}
771
-
772
- export type GraphqlResponse<T> = GraphqlServerResponse<T> & GraphqlResponseAdditions
946
+ ]);
947
+ }
948
+ }
773
949
 
774
- export type GraphqlResponseTyped = GraphqlResponse<GraphqlMiddlewareResponseUnion>
950
+ function defineGeneratorTemplate(options, build, buildTypes) {
951
+ return {
952
+ type: "generator",
953
+ options,
954
+ build,
955
+ buildTypes
956
+ };
957
+ }
958
+ function defineStaticTemplate(options, build, buildTypes) {
959
+ return {
960
+ type: "static",
961
+ options,
962
+ build,
963
+ buildTypes
964
+ };
965
+ }
775
966
 
776
- export { serverOptions }
967
+ const ClientOptions = defineStaticTemplate(
968
+ { path: "nuxt-graphql-middleware/client-options" },
969
+ (helper) => {
970
+ if (helper.paths.clientOptions) {
971
+ const pathRelative = helper.toModuleBuildRelative(
972
+ helper.paths.clientOptions
973
+ );
974
+ return `import clientOptions from '${pathRelative}'
975
+ export { clientOptions }
777
976
  `;
778
- }
779
- });
780
- const getClientOptionsImport = () => {
781
- const clientOptionsPath = appResolver.resolve(
782
- "graphqlMiddleware.clientOptions"
977
+ }
978
+ return `export const clientOptions = {}`;
979
+ },
980
+ (helper) => {
981
+ if (helper.paths.clientOptions) {
982
+ const pathRelative = helper.toModuleBuildRelative(
983
+ helper.paths.clientOptions
783
984
  );
784
- if (fileExists(clientOptionsPath)) {
785
- const pathRelative = relative(nuxt.options.buildDir, clientOptionsPath);
786
- return `import clientOptions from '${pathRelative}'`;
787
- }
788
- };
789
- const clientOptionsImport = getClientOptionsImport();
790
- const clientOptionsTemplate = addTemplate({
791
- filename: "graphqlMiddleware.clientOptions.mjs",
792
- write: true,
793
- getContents: () => {
794
- if (clientOptionsImport) {
795
- return `${clientOptionsImport}
796
- export { clientOptions }`;
797
- }
798
- return `export const clientOptions = {}`;
799
- }
800
- });
801
- const runtimeTypesPath = relative(
802
- nuxt.options.buildDir,
803
- moduleResolver.resolve("./runtime/types.ts")
804
- );
805
- addTemplate({
806
- filename: "graphqlMiddleware.clientOptions.d.ts",
807
- write: true,
808
- getContents: () => {
809
- if (clientOptionsImport) {
810
- return `import type { GraphqlClientOptions } from '${runtimeTypesPath}'
811
- ${clientOptionsImport}
985
+ return `import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
986
+ import { clientOptions } from '${pathRelative}'
812
987
 
813
988
  export type GraphqlClientContext = typeof clientOptions extends GraphqlClientOptions<infer R> ? R : {}
814
989
 
815
990
  export { clientOptions }`;
816
- }
817
- return `import type { GraphqlClientOptions } from '${runtimeTypesPath}'
991
+ }
992
+ return `
993
+ import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
818
994
  export const clientOptions: GraphqlClientOptions
819
995
 
820
996
  export type GraphqlClientContext = {}
821
997
  `;
822
- }
823
- });
824
- addAlias("#graphql-middleware-client-options", clientOptionsTemplate.dst);
825
- nuxt.options.nitro.externals = nuxt.options.nitro.externals || {};
826
- nuxt.options.nitro.externals.inline = nuxt.options.nitro.externals.inline || [];
827
- nuxt.options.nitro.externals.inline.push(template.dst);
828
- nuxt.options.nitro.externals.inline.push(templateDocuments.dst);
829
- addAlias("#graphql-middleware-server-options-build", template.dst);
830
- addAlias(
831
- "#graphql-middleware/types",
832
- moduleResolver.resolve("./runtime/types.ts")
998
+ }
999
+ );
1000
+
1001
+ const Documents = defineGeneratorTemplate(
1002
+ { path: "nuxt-graphql-middleware/documents", virtual: true },
1003
+ (output, helper) => {
1004
+ return output.getOperationsFile({
1005
+ exportName: "documents",
1006
+ minify: !helper.isDev
1007
+ }).getSource();
1008
+ },
1009
+ () => {
1010
+ return `
1011
+ import type { Query, Mutation } from './operation-types'
1012
+
1013
+ declare module '#nuxt-graphql-middleware/documents' {
1014
+ export type Documents = {
1015
+ query: Record<keyof Query, string>
1016
+ mutation: Record<keyof Mutation, string>
1017
+ }
1018
+ export const documents: Documents
1019
+ }`;
1020
+ }
1021
+ );
1022
+
1023
+ const GraphqlConfig = defineStaticTemplate(
1024
+ { path: "nuxt-graphql-middleware/graphql.config" },
1025
+ (helper) => {
1026
+ const patterns = helper.options.autoImportPatterns || [];
1027
+ const configPath = helper.resolvers.root.resolve(
1028
+ (helper.options.graphqlConfigFilePath || "").replace(
1029
+ "/graphql.config.ts",
1030
+ ""
1031
+ )
833
1032
  );
834
- addServerHandler({
835
- handler: moduleResolver.resolve("./runtime/serverHandler/index"),
836
- route: options.serverApiPrefix + "/:operation/:name"
1033
+ const schemaPath = "./" + relative(configPath, helper.paths.schema);
1034
+ const documents = patterns.filter((v) => !v.includes("!")).map((pattern) => {
1035
+ return "./" + relative(configPath, helper.resolvers.root.resolve(pattern));
837
1036
  });
838
- if (options.enableFileUploads) {
839
- addServerHandler({
840
- handler: moduleResolver.resolve("./runtime/serverHandler/upload"),
841
- route: options.serverApiPrefix + "/upload/:name"
842
- });
1037
+ return `const schema = ${JSON.stringify(schemaPath)}
1038
+
1039
+ const documents = ${JSON.stringify(documents, null, 2)};
1040
+
1041
+ const config = {
1042
+ schema,
1043
+ documents,
1044
+ }
1045
+
1046
+ export default config
1047
+ `;
1048
+ },
1049
+ () => {
1050
+ return `
1051
+ import type { IGraphQLProject } from 'graphql-config'
1052
+
1053
+ type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
1054
+
1055
+ const config: WithRequired<IGraphQLProject, 'schema' | 'documents'>;
1056
+
1057
+ export default config;
1058
+ `;
1059
+ }
1060
+ );
1061
+
1062
+ const Helpers = defineStaticTemplate(
1063
+ { path: "nuxt-graphql-middleware/helpers" },
1064
+ (helper) => {
1065
+ return `export const serverApiPrefix = '${helper.options.serverApiPrefix}'
1066
+ export function getEndpoint(operation, operationName) {
1067
+ return serverApiPrefix + '/' + operation + '/' + operationName
1068
+ }
1069
+ `;
1070
+ },
1071
+ () => {
1072
+ return `export const serverApiPrefix: string;
1073
+ export function getEndpoint(operation: string, operationName: string): string`;
1074
+ }
1075
+ );
1076
+
1077
+ const NitroTypes = defineGeneratorTemplate(
1078
+ { path: "nuxt-graphql-middleware/nitro" },
1079
+ null,
1080
+ (output, helper) => {
1081
+ const operations = output.getCollectedOperations();
1082
+ const serverApiPrefix = helper.options.serverApiPrefix;
1083
+ const endpoints = [];
1084
+ const imports = [];
1085
+ for (const operation of operations) {
1086
+ imports.push(operation.typeName);
1087
+ const method = operation.operationType === OperationTypeNode.QUERY ? "get" : "post";
1088
+ endpoints.push(
1089
+ ` '${serverApiPrefix}/${operation.operationType}/${operation.graphqlName}': {
1090
+ '${method}': GraphqlResponse<${operation.typeName}>
1091
+ }`
1092
+ );
843
1093
  }
844
- addPlugin(moduleResolver.resolve("./runtime/plugins/provideState"), {
845
- append: false
846
- });
847
- nuxt.hook("nitro:config", (nitroConfig) => {
848
- nitroConfig.externals = defu(
849
- typeof nitroConfig.externals === "object" ? nitroConfig.externals : {},
850
- {
851
- inline: [moduleResolver.resolve("./runtime")]
852
- }
1094
+ return `import type { GraphqlResponse } from './response'
1095
+ import type {
1096
+ ${imports.sort().join(",\n ")}
1097
+ } from './../graphql-operations'
1098
+
1099
+ declare module 'nitropack/types' {
1100
+ interface InternalApi {
1101
+ ${endpoints.sort().join("\n")}
1102
+ }
1103
+ }`;
1104
+ }
1105
+ );
1106
+
1107
+ const OperationTypesAll = defineGeneratorTemplate(
1108
+ { path: "nuxt-graphql-middleware/operation-types" },
1109
+ () => `export {}`,
1110
+ (output) => {
1111
+ return output.getOperationTypesFile({
1112
+ importFrom: "./../graphql-operations"
1113
+ }).getSource();
1114
+ }
1115
+ );
1116
+
1117
+ const Operations = defineGeneratorTemplate(
1118
+ { path: "graphql-operations/index" },
1119
+ (output) => {
1120
+ const typesFile = output.getOperations("js");
1121
+ return typesFile.getSource();
1122
+ },
1123
+ (output) => {
1124
+ const typesFile = output.getOperations("d.ts");
1125
+ return typesFile.getSource();
1126
+ }
1127
+ );
1128
+
1129
+ const Response = defineGeneratorTemplate(
1130
+ { path: "nuxt-graphql-middleware/response" },
1131
+ null,
1132
+ (output, helper) => {
1133
+ const operations = output.getCollectedOperations();
1134
+ const allTypes = operations.map((v) => v.typeName).sort();
1135
+ return `import type {
1136
+ ${allTypes.join(",\n ")}
1137
+ } from './../graphql-operations'
1138
+ import type { GraphqlResponseAdditions } from './server-options'
1139
+ import type { GraphqlServerResponse } from '${helper.paths.runtimeTypes}'
1140
+
1141
+ declare module '#nuxt-graphql-middleware/response' {
1142
+ export type GraphqlMiddlewareResponseUnion =
1143
+ | ${allTypes.join("\n | ") || "never"}
1144
+
1145
+ export type GraphqlResponse<T> = GraphqlServerResponse<T> & GraphqlResponseAdditions
1146
+ export type GraphqlResponseTyped = GraphqlResponse<GraphqlMiddlewareResponseUnion>
1147
+ }`;
1148
+ }
1149
+ );
1150
+
1151
+ const ServerOptions = defineStaticTemplate(
1152
+ { path: "nuxt-graphql-middleware/server-options" },
1153
+ (helper) => {
1154
+ const resolvedPathRelative = helper.paths.serverOptions ? helper.toModuleBuildRelative(helper.paths.serverOptions) : null;
1155
+ const serverOptionsLine = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions = {}`;
1156
+ return `
1157
+ ${serverOptionsLine}
1158
+ export { serverOptions }
1159
+ `;
1160
+ },
1161
+ (helper) => {
1162
+ const resolvedPathRelative = helper.paths.serverOptions ? helper.toModuleBuildRelative(helper.paths.serverOptions) : null;
1163
+ const serverOptionsLineTypes = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions: GraphqlMiddlewareServerOptions = {}`;
1164
+ return `
1165
+ import type { GraphqlMiddlewareServerOptions } from '${helper.paths.runtimeTypes}'
1166
+ ${serverOptionsLineTypes}
1167
+
1168
+ export type GraphqlResponseAdditions =
1169
+ typeof serverOptions extends GraphqlMiddlewareServerOptions<infer R, any, any> ? R : {}
1170
+
1171
+ export { serverOptions }`;
1172
+ }
1173
+ );
1174
+
1175
+ const Sources = defineGeneratorTemplate(
1176
+ { path: "nuxt-graphql-middleware/sources" },
1177
+ (output, helper) => {
1178
+ const operations = output.getCollectedOperations();
1179
+ const srcDir = helper.paths.root;
1180
+ const lines = [];
1181
+ for (const operation of operations) {
1182
+ const filePath = relative(srcDir, operation.filePath);
1183
+ lines.push(
1184
+ `${operation.operationType}_${operation.graphqlName}: '${filePath}',`
1185
+ );
1186
+ }
1187
+ return `
1188
+ export const operationSources = {
1189
+ ${lines.join("\n ")}
1190
+ }
1191
+ `;
1192
+ },
1193
+ () => {
1194
+ return `export const operationSources: Record<string, string>`;
1195
+ }
1196
+ );
1197
+
1198
+ const TEMPLATES = [
1199
+ ClientOptions,
1200
+ Documents,
1201
+ GraphqlConfig,
1202
+ Helpers,
1203
+ NitroTypes,
1204
+ OperationTypesAll,
1205
+ Operations,
1206
+ Response,
1207
+ ServerOptions,
1208
+ Sources
1209
+ ];
1210
+
1211
+ const DEVTOOLS_UI_ROUTE = "/__nuxt-graphql-middleware";
1212
+ const DEVTOOLS_UI_LOCAL_PORT = 3300;
1213
+ function setupDevToolsUI(nuxt, clientPath) {
1214
+ const isProductionBuild = existsSync$1(clientPath);
1215
+ if (isProductionBuild) {
1216
+ nuxt.hook("vite:serverCreated", async (server) => {
1217
+ const sirv = await import('sirv').then((r) => r.default || r);
1218
+ server.middlewares.use(
1219
+ DEVTOOLS_UI_ROUTE,
1220
+ sirv(clientPath, { dev: true, single: true })
853
1221
  );
854
1222
  });
855
- if (nuxt.options.dev || nuxt.options._prepare) {
856
- addServerHandler({
857
- handler: moduleResolver.resolve("./runtime/serverHandler/debug"),
858
- route: options.serverApiPrefix + "/debug"
1223
+ } else {
1224
+ nuxt.hook("vite:extendConfig", (config) => {
1225
+ config.server = config.server || {};
1226
+ config.server.proxy = config.server.proxy || {};
1227
+ config.server.proxy[DEVTOOLS_UI_ROUTE] = {
1228
+ target: "http://localhost:" + DEVTOOLS_UI_LOCAL_PORT + DEVTOOLS_UI_ROUTE,
1229
+ changeOrigin: true,
1230
+ followRedirects: true,
1231
+ rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
1232
+ };
1233
+ });
1234
+ }
1235
+ nuxt.hook("devtools:customTabs", (tabs) => {
1236
+ tabs.push({
1237
+ // unique identifier
1238
+ name: "nuxt-graphql-middleware",
1239
+ // title to display in the tab
1240
+ title: "GraphQL Middleware",
1241
+ // any icon from Iconify, or a URL to an image
1242
+ icon: "akar-icons:graphql-fill",
1243
+ // iframe view
1244
+ view: {
1245
+ type: "iframe",
1246
+ src: DEVTOOLS_UI_ROUTE
1247
+ }
1248
+ });
1249
+ });
1250
+ }
1251
+
1252
+ const RPC_NAMESPACE = "nuxt-graphql-middleware";
1253
+ class DevModeHandler {
1254
+ constructor(nuxt, schemaProvider, collector, helper) {
1255
+ this.nuxt = nuxt;
1256
+ this.schemaProvider = schemaProvider;
1257
+ this.collector = collector;
1258
+ this.helper = helper;
1259
+ }
1260
+ devToolsRpc = null;
1261
+ nitro = null;
1262
+ viteWebSocket = null;
1263
+ operationsToReload = /* @__PURE__ */ new Set();
1264
+ init() {
1265
+ this.nuxt.hooks.hookOnce("ready", this.onReady.bind(this));
1266
+ this.nuxt.hooks.hookOnce(
1267
+ "vite:serverCreated",
1268
+ this.onViteServerCreated.bind(this)
1269
+ );
1270
+ this.nuxt.hook("builder:watch", this.onBuilderWatch.bind(this));
1271
+ if (this.helper.options.devtools) {
1272
+ const clientPath = this.helper.resolvers.module.resolve("./client");
1273
+ setupDevToolsUI(this.nuxt, clientPath);
1274
+ onDevToolsInitialized(() => {
1275
+ this.devToolsRpc = extendServerRpc(
1276
+ RPC_NAMESPACE,
1277
+ {
1278
+ // register server RPC functions
1279
+ getModuleOptions: () => {
1280
+ return this.helper.options;
1281
+ },
1282
+ getDocuments: () => {
1283
+ return [...this.collector.rpcItems.values()];
1284
+ }
1285
+ }
1286
+ );
859
1287
  });
860
- nuxt.hook("builder:watch", async (event, pathAbsolute) => {
861
- if (pathAbsolute === schemaPath) {
1288
+ }
1289
+ }
1290
+ onReady() {
1291
+ this.nitro = useNitro();
1292
+ this.nitro.hooks.hook("compiled", this.onNitroCompiled.bind(this));
1293
+ }
1294
+ async onBuilderWatch(event, providedFilePath) {
1295
+ if (!providedFilePath.endsWith(".graphql") && !providedFilePath.endsWith(".gql")) {
1296
+ return;
1297
+ }
1298
+ const pathAbsolute = providedFilePath.startsWith("/") ? providedFilePath : this.helper.resolvers.src.resolve(providedFilePath);
1299
+ if (pathAbsolute === this.helper.paths.schema) {
1300
+ return;
1301
+ }
1302
+ this.helper.prompt.abort();
1303
+ const { hasChanged, affectedOperations, error } = await this.collector.handleWatchEvent(event, pathAbsolute);
1304
+ if (error) {
1305
+ this.sendError(error);
1306
+ await this.helper.prompt.confirm("Do you want to download and update the GraphQL schema?").then(async (shouldReload) => {
1307
+ if (shouldReload !== "yes") {
862
1308
  return;
863
1309
  }
864
- if (!pathAbsolute.match(/\.(gql|graphql)$/)) {
865
- return;
1310
+ try {
1311
+ await this.schemaProvider.loadSchema({ forceDownload: true });
1312
+ await this.collector.updateSchema(this.schemaProvider.getSchema());
1313
+ } catch (e) {
1314
+ logger.error(e);
866
1315
  }
867
- await collector.handleWatchEvent(event, pathAbsolute);
868
1316
  });
1317
+ return;
1318
+ }
1319
+ if (!hasChanged) {
1320
+ return;
1321
+ }
1322
+ if (this.nitro) {
1323
+ await this.nitro.hooks.callHook("rollup:reload");
1324
+ }
1325
+ if (affectedOperations.length) {
1326
+ affectedOperations.forEach(
1327
+ (operation) => this.operationsToReload.add(operation)
1328
+ );
1329
+ }
1330
+ if (this.devToolsRpc) {
1331
+ try {
1332
+ this.devToolsRpc.broadcast.documentsUpdated([
1333
+ ...this.collector.rpcItems.values()
1334
+ ]);
1335
+ } catch {
1336
+ logger.info(
1337
+ "Failed to update GraphQL documents in dev tools. The documents might be stale."
1338
+ );
1339
+ }
1340
+ }
1341
+ }
1342
+ onViteServerCreated(server) {
1343
+ this.viteWebSocket = server.ws;
1344
+ }
1345
+ sendError(error) {
1346
+ if (!this.viteWebSocket) {
1347
+ return;
1348
+ }
1349
+ this.viteWebSocket.send({
1350
+ type: "error",
1351
+ err: {
1352
+ message: error.message,
1353
+ stack: ""
1354
+ }
1355
+ });
1356
+ }
1357
+ onNitroCompiled() {
1358
+ if (!this.operationsToReload.size) {
1359
+ return;
1360
+ }
1361
+ const operations = [...this.operationsToReload.values()];
1362
+ this.operationsToReload.clear();
1363
+ if (!this.viteWebSocket) {
1364
+ return;
1365
+ }
1366
+ this.viteWebSocket.send({
1367
+ type: "custom",
1368
+ event: "nuxt-graphql-middleware:reload",
1369
+ data: { operations }
1370
+ });
1371
+ }
1372
+ }
1373
+
1374
+ const module = defineNuxtModule({
1375
+ meta: {
1376
+ name,
1377
+ configKey: "graphqlMiddleware",
1378
+ version,
1379
+ compatibility: {
1380
+ nuxt: ">=3.15.0"
869
1381
  }
1382
+ },
1383
+ defaults: defaultOptions,
1384
+ async setup(passedOptions, nuxt) {
1385
+ const helper = new ModuleHelper(nuxt, import.meta.url, passedOptions);
1386
+ const schemaProvider = new SchemaProvider(helper);
1387
+ await schemaProvider.init();
1388
+ const collector = new Collector(schemaProvider.getSchema(), helper);
1389
+ nuxt.options.appConfig.graphqlMiddleware = {
1390
+ clientCacheEnabled: !!helper.options.clientCache?.enabled,
1391
+ clientCacheMaxSize: helper.options.clientCache?.maxSize ?? 100
1392
+ };
1393
+ nuxt.options.runtimeConfig.graphqlMiddleware = {
1394
+ graphqlEndpoint: helper.options.graphqlEndpoint || ""
1395
+ };
1396
+ helper.transpile(fileURLToPath(new URL("./runtime", import.meta.url)));
1397
+ helper.inlineNitroExternals(helper.resolvers.module.resolve("./runtime"));
1398
+ helper.inlineNitroExternals(helper.paths.moduleBuildDir);
1399
+ helper.inlineNitroExternals(helper.paths.moduleTypesDir);
1400
+ helper.addAlias("#nuxt-graphql-middleware", helper.paths.moduleBuildDir);
1401
+ helper.addAlias("#graphql-operations", helper.paths.moduleTypesDir);
1402
+ helper.addPlugin("./runtime/plugins/provideState");
1403
+ if (helper.isDev && helper.options.errorOverlay) {
1404
+ helper.addPlugin("./runtime/plugins/devMode");
1405
+ }
1406
+ helper.addServerHandler("query", "/query/:name", "get");
1407
+ helper.addServerHandler("mutation", "/mutation/:name", "post");
1408
+ if (helper.options.enableFileUploads) {
1409
+ helper.addServerHandler("upload", "/upload/:name", "post");
1410
+ }
1411
+ if (helper.isDev) {
1412
+ helper.addServerHandler("debug", "/debug", "get");
1413
+ }
1414
+ if (helper.options.includeComposables) {
1415
+ helper.addComposable("useGraphqlQuery");
1416
+ helper.addComposable("useGraphqlMutation");
1417
+ helper.addComposable("useGraphqlState");
1418
+ helper.addComposable("useAsyncGraphqlQuery");
1419
+ if (helper.options.enableFileUploads) {
1420
+ helper.addComposable("useGraphqlUploadMutation");
1421
+ }
1422
+ helper.addServerUtil("useGraphqlQuery");
1423
+ helper.addServerUtil("useGraphqlMutation");
1424
+ helper.addServerUtil("doGraphqlRequest");
1425
+ }
1426
+ TEMPLATES.forEach((template) => {
1427
+ if (template.type === "static") {
1428
+ helper.addTemplate(template);
1429
+ } else {
1430
+ collector.addTemplate(template);
1431
+ }
1432
+ });
1433
+ helper.applyBuildConfig();
1434
+ await collector.init();
1435
+ if (!helper.isDev) {
1436
+ return;
1437
+ }
1438
+ const devModeHandler = new DevModeHandler(
1439
+ nuxt,
1440
+ schemaProvider,
1441
+ collector,
1442
+ helper
1443
+ );
1444
+ devModeHandler.init();
870
1445
  }
871
1446
  });
872
1447