nuxt-graphql-middleware 5.0.0-alpha.0 → 5.0.0-alpha.10

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/BLvMh1Ga.js +1 -0
  4. package/dist/client/_nuxt/C9pb_2rp.js +2 -0
  5. package/dist/client/_nuxt/CBwfSTyQ.js +1 -0
  6. package/dist/client/_nuxt/CPyoLiCY.js +1 -0
  7. package/dist/client/_nuxt/VpkRx2_e.js +25 -0
  8. package/dist/client/_nuxt/builds/latest.json +1 -0
  9. package/dist/client/_nuxt/builds/meta/074548b5-7348-4637-940b-ab6e87b521a5.json +1 -0
  10. package/dist/client/_nuxt/entry.Cn9qfNGa.css +1 -0
  11. package/dist/client/_nuxt/error-404.BJkSn6RI.css +1 -0
  12. package/dist/client/_nuxt/error-500.TOCKLquH.css +1 -0
  13. package/dist/client/_nuxt/index.DGEN-H8t.css +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 +1132 -565
  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, resolveAlias, createResolver, 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.0";
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.10";
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,151 @@ 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
+ if (!this.helper.matchesImportPattern(filePath)) {
391
+ return false;
392
+ }
470
393
  const file = this.files.get(filePath);
471
394
  if (!file) {
472
- return false;
395
+ return this.handleAdd(filePath);
473
396
  }
474
- const needsUpdate = await file.update();
475
- if (!needsUpdate) {
476
- return false;
397
+ try {
398
+ const needsUpdate = await file.update();
399
+ if (!needsUpdate) {
400
+ return false;
401
+ }
402
+ this.generator.update({
403
+ filePath,
404
+ documentNode: file.parsed
405
+ });
406
+ } catch {
407
+ return this.handleUnlink(filePath);
477
408
  }
478
- this.generator.update({
479
- filePath: this.filePathToRelative(filePath),
480
- documentNode: file.parsed
481
- });
482
409
  return true;
483
410
  }
484
411
  handleUnlink(filePath) {
@@ -487,7 +414,7 @@ class Collector {
487
414
  return false;
488
415
  }
489
416
  this.files.delete(filePath);
490
- this.generator.remove(this.filePathToRelative(filePath));
417
+ this.generator.remove(filePath);
491
418
  return true;
492
419
  }
493
420
  handleUnlinkDir(folderPath) {
@@ -506,8 +433,9 @@ class Collector {
506
433
  * Handle the watcher event for the given file path.
507
434
  */
508
435
  async handleWatchEvent(event, filePath) {
436
+ let hasChanged = false;
437
+ const oldOperationTimestamps = new Map(this.operationTimestamps);
509
438
  try {
510
- let hasChanged = false;
511
439
  if (event === "add") {
512
440
  hasChanged = await this.handleAdd(filePath);
513
441
  } else if (event === "change") {
@@ -516,357 +444,996 @@ class Collector {
516
444
  hasChanged = this.handleUnlink(filePath);
517
445
  } else if (event === "unlinkDir") {
518
446
  hasChanged = this.handleUnlinkDir(filePath);
519
- } else if (event === "addDir") {
520
447
  }
521
448
  if (hasChanged) {
522
449
  this.buildState();
523
450
  }
524
451
  } catch (e) {
525
452
  this.generator.resetCaches();
526
- console.log(e);
453
+ logger.error("Failed to update GraphQL code.");
454
+ this.logError(e);
455
+ return {
456
+ hasChanged: false,
457
+ affectedOperations: [],
458
+ error: { message: this.buildErrorMessage(e) }
459
+ };
460
+ }
461
+ const affectedOperations = [];
462
+ if (hasChanged) {
463
+ logger.success("Finished GraphQL code update successfully.");
464
+ for (const [name, newTimestamp] of this.operationTimestamps) {
465
+ const oldTimestamp = oldOperationTimestamps.get(name);
466
+ if (!oldTimestamp || oldTimestamp !== newTimestamp) {
467
+ affectedOperations.push(name);
468
+ }
469
+ }
527
470
  }
471
+ return { hasChanged, affectedOperations };
528
472
  }
529
473
  /**
530
- * Get the TypeScript types template contents.
474
+ * Adds a virtual template (not written to disk) for both Nuxt and Nitro.
475
+ *
476
+ * For some reason a template written to disk works for both Nuxt and Nitro,
477
+ * but a virtual template requires adding two templates.
531
478
  */
532
- getTemplateTypes() {
533
- return this.outputTypes;
479
+ addVirtualTemplate(template) {
480
+ const filename = template.options.path + ".js";
481
+ const getContents = () => this.getTemplate(filename);
482
+ addTemplate({
483
+ filename,
484
+ getContents
485
+ });
486
+ addServerTemplate({
487
+ // Since this is a virtual template, the name must match the final
488
+ // alias, example:
489
+ // - nuxt-graphql-middleware/foobar.mjs => #nuxt-graphql-middleware/foobar
490
+ //
491
+ // That way we can reference the same template using the alias in both
492
+ // Nuxt and Nitro environments.
493
+ filename: "#" + template.options.path,
494
+ getContents
495
+ });
534
496
  }
535
497
  /**
536
- * Get the context template contents.
498
+ * Adds a template that dependes on Collector state.
537
499
  */
538
- getTemplateContext() {
539
- return this.outputContext;
500
+ addTemplate(template) {
501
+ this.templates.push(template);
502
+ if (template.build) {
503
+ if (template.options.virtual) {
504
+ this.addVirtualTemplate(template);
505
+ } else {
506
+ const filename = template.options.path + ".js";
507
+ addTemplate({
508
+ filename,
509
+ write: true,
510
+ getContents: () => this.getTemplate(filename)
511
+ });
512
+ }
513
+ }
514
+ if (template.buildTypes) {
515
+ const filename = template.options.path + ".d.ts";
516
+ addTypeTemplate(
517
+ {
518
+ filename,
519
+ write: true,
520
+ getContents: () => this.getTemplate(filename)
521
+ },
522
+ {
523
+ nuxt: true,
524
+ nitro: true
525
+ }
526
+ );
527
+ }
528
+ }
529
+ }
530
+
531
+ class SchemaProvider {
532
+ constructor(helper) {
533
+ this.helper = helper;
540
534
  }
541
535
  /**
542
- * Get the operations template contents.
536
+ * The raw schema content.
543
537
  */
544
- getTemplateOperations() {
545
- return this.outputOperations;
538
+ schemaContent = "";
539
+ /**
540
+ * The parsed schema object.
541
+ */
542
+ schema = null;
543
+ async init() {
544
+ try {
545
+ await this.loadSchema();
546
+ } catch (error) {
547
+ logger.error(error);
548
+ const hasLoaded = await this.loadFromDiskFallback();
549
+ if (!hasLoaded) {
550
+ throw new Error("Failed to load GraphQL schema.");
551
+ }
552
+ }
553
+ }
554
+ async loadFromDiskFallback() {
555
+ const hasSchemaOnDisk = await this.hasSchemaOnDisk();
556
+ if (this.helper.isDev && hasSchemaOnDisk && this.helper.options.downloadSchema) {
557
+ const shouldUseFromDisk = await this.helper.prompt.confirm(
558
+ "Do you want to continue with the previously downloaded schema from disk?"
559
+ );
560
+ if (shouldUseFromDisk === "yes") {
561
+ await this.loadSchema({ forceDisk: true });
562
+ return true;
563
+ }
564
+ }
565
+ return false;
546
566
  }
547
567
  /**
548
- * Log results (including parse/validation errors).
568
+ * Loads the schema from disk.
569
+ *
570
+ * @returns The schema contents from disk.
549
571
  */
550
- logDocuments(logEverything) {
572
+ async loadSchemaFromDisk() {
573
+ const fileExists = await this.hasSchemaOnDisk();
574
+ if (!fileExists) {
575
+ logger.error(
576
+ '"downloadSchema" is set to false but no schema exists at ' + this.helper.paths.schema
577
+ );
578
+ throw new Error("Missing GraphQL schema.");
579
+ }
580
+ logger.info(`Loading GraphQL schema from disk: ${this.helper.paths.schema}`);
581
+ return await fs.readFile(this.helper.paths.schema).then((v) => v.toString());
582
+ }
583
+ /**
584
+ * Downloads the schema and saves it to disk.
585
+ *
586
+ * @returns The schema contents.
587
+ */
588
+ downloadSchema() {
589
+ const endpoint = this.helper.options.graphqlEndpoint;
590
+ if (!endpoint) {
591
+ throw new Error("Missing graphqlEndpoint config.");
592
+ }
593
+ const pluginConfig = this.helper.options.codegenSchemaConfig?.urlSchemaOptions;
594
+ const schemaAstConfig = this.helper.options.codegenSchemaConfig?.schemaAstConfig || {
595
+ sort: true
596
+ };
597
+ const config = {
598
+ schema: endpoint,
599
+ pluginLoader: (name) => {
600
+ switch (name) {
601
+ case "@graphql-codegen/schema-ast":
602
+ return Promise.resolve(PluginSchemaAst);
603
+ }
604
+ throw new Error(`graphql-codegen plugin not found: ${name}`);
605
+ },
606
+ silent: true,
607
+ errorsOnly: true,
608
+ config: pluginConfig,
609
+ generates: {
610
+ [this.helper.paths.schema]: {
611
+ plugins: ["schema-ast"],
612
+ config: schemaAstConfig
613
+ }
614
+ }
615
+ };
616
+ logger.info(`Downloading GraphQL schema from "${endpoint}".`);
617
+ return generate(config, true).then((v) => v[0]?.content);
618
+ }
619
+ /**
620
+ * Determine if the schema exists on disk.
621
+ *
622
+ * @returns True if the schema file exists on disk.
623
+ */
624
+ hasSchemaOnDisk() {
625
+ return fs.access(this.helper.paths.schema).then(() => true).catch(() => false);
626
+ }
627
+ /**
628
+ * Load the schema either from disk or by downloading it.
629
+ *
630
+ * @param forceDownload - Forces downloading the schema.
631
+ */
632
+ async loadSchema(opts) {
633
+ if (opts?.forceDisk) {
634
+ this.schemaContent = await this.loadSchemaFromDisk();
635
+ } else if (this.helper.options.downloadSchema || opts?.forceDownload) {
636
+ this.schemaContent = await this.downloadSchema();
637
+ } else {
638
+ this.schemaContent = await this.loadSchemaFromDisk();
639
+ }
640
+ this.schema = await loadSchema(this.schemaContent, {
641
+ loaders: []
642
+ });
643
+ }
644
+ /**
645
+ * Get the schema.
646
+ *
647
+ * @returns The parsed GraphQL schema object.
648
+ */
649
+ getSchema() {
650
+ if (!this.schema) {
651
+ throw new Error("Failed to load schema.");
652
+ }
653
+ return this.schema;
551
654
  }
552
655
  }
553
656
 
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;
657
+ const unicode = isUnicodeSupported();
658
+ const s = (c, fallback) => unicode ? c : fallback;
659
+ const S_BAR = s("\u2502", "|");
660
+ const S_STEP_ACTIVE = s("\u25C6", "*");
661
+ const S_STEP_CANCEL = s("\u25A0", "x");
662
+ const S_STEP_ERROR = s("\u25B2", "x");
663
+ const S_STEP_SUBMIT = s("\u25C7", "o");
664
+ const S_RADIO_ACTIVE = s("\u25CF", ">");
665
+ const S_RADIO_INACTIVE = s("\u25CB", " ");
666
+ const S_BAR_END = s("\u2514", "\u2014");
667
+ const symbol = (state) => {
668
+ switch (state) {
669
+ case "initial":
670
+ case "active":
671
+ return color.cyan(S_STEP_ACTIVE);
672
+ case "cancel":
673
+ return color.red(S_STEP_CANCEL);
674
+ case "error":
675
+ return color.yellow(S_STEP_ERROR);
676
+ case "submit":
677
+ return color.green(S_STEP_SUBMIT);
678
+ }
679
+ };
680
+ class ConsolePrompt {
681
+ abortController = null;
682
+ confirm(message) {
683
+ this.abort();
684
+ this.abortController = new AbortController();
685
+ const active = "Yes";
686
+ const inactive = "No";
687
+ return new ConfirmPrompt({
688
+ active,
689
+ inactive,
690
+ initialValue: true,
691
+ signal: this.abortController.signal,
692
+ render() {
693
+ const title = `${color.gray(S_BAR)}
694
+ ${symbol(this.state)} ${message}
695
+ `;
696
+ const value = this.value ? active : inactive;
697
+ switch (this.state) {
698
+ case "submit":
699
+ return `${title}${color.gray(S_BAR)} ${color.dim(value)}`;
700
+ case "cancel":
701
+ return `${title}${color.gray(S_BAR)} ${color.strikethrough(
702
+ color.dim(value)
703
+ )}
704
+ ${color.gray(S_BAR)}`;
705
+ default: {
706
+ 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)}`}
707
+ ${color.cyan(S_BAR_END)}
708
+ `;
709
+ }
710
+ }
711
+ }
712
+ }).prompt().then((v) => {
713
+ const result = v;
714
+ if (result === true) {
715
+ return "yes";
716
+ } else if (result === false) {
717
+ return "no";
718
+ }
719
+ return "cancel";
720
+ });
721
+ }
722
+ abort() {
723
+ if (this.abortController) {
724
+ this.abortController.abort();
725
+ this.abortController = null;
569
726
  }
727
+ }
728
+ }
729
+
730
+ class ModuleHelper {
731
+ constructor(nuxt, moduleUrl, options) {
732
+ this.nuxt = nuxt;
570
733
  const isModuleBuild = process.env.MODULE_BUILD === "true" && nuxt.options._prepare;
734
+ const mergedOptions = defu({}, options, defaultOptions);
735
+ if (!mergedOptions.autoImportPatterns) {
736
+ mergedOptions.autoImportPatterns = [
737
+ "~~/**/*.{gql,graphql}",
738
+ "!node_modules"
739
+ ];
740
+ }
571
741
  if (isModuleBuild) {
572
- options.graphqlEndpoint = "http://localhost";
573
- options.downloadSchema = false;
574
- options.schemaPath = "~~/schema.graphql";
575
- options.autoImportPatterns = [
742
+ mergedOptions.graphqlEndpoint = "http://localhost";
743
+ mergedOptions.downloadSchema = false;
744
+ mergedOptions.schemaPath = "~~/schema.graphql";
745
+ mergedOptions.autoImportPatterns = [
576
746
  "~~/playground/**/*.{gql,graphql}",
577
747
  "!node_modules"
578
748
  ];
579
749
  }
580
- if (!passedOptions.autoImportPatterns) {
581
- options.autoImportPatterns = ["~~/**/*.{gql,graphql}", "!node_modules"];
582
- }
583
- options.autoImportPatterns = (options.autoImportPatterns || []).map(
584
- (pattern) => {
585
- return resolveAlias(pattern);
586
- }
587
- );
588
- if (!nuxt.options._prepare) {
589
- validateOptions(options);
590
- }
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: []
750
+ mergedOptions.autoImportPatterns = (mergedOptions.autoImportPatterns || []).map((pattern) => {
751
+ return resolveAlias(pattern);
605
752
  });
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
753
+ this.options = mergedOptions;
754
+ if (!nuxt.options._prepare) {
755
+ validateOptions(this.options);
756
+ }
757
+ this.isDev = nuxt.options.dev;
758
+ this.resolvers = {
759
+ module: createResolver(moduleUrl),
760
+ server: createResolver(nuxt.options.serverDir),
761
+ src: createResolver(nuxt.options.srcDir),
762
+ app: createResolver(nuxt.options.dir.app),
763
+ root: createResolver(nuxt.options.rootDir)
613
764
  };
614
- const collector = new Collector(
615
- schema,
616
- context,
617
- options.documents,
618
- options.codegenConfig
765
+ this.paths = {
766
+ runtimeTypes: "",
767
+ root: nuxt.options.rootDir,
768
+ nuxtConfig: this.resolvers.root.resolve("nuxt.config.ts"),
769
+ serverDir: nuxt.options.serverDir,
770
+ schema: this.resolvers.root.resolve(
771
+ resolveAlias(this.options.schemaPath)
772
+ ),
773
+ serverOptions: "",
774
+ clientOptions: this.findClientOptions(),
775
+ moduleBuildDir: nuxt.options.buildDir + "/nuxt-graphql-middleware",
776
+ moduleTypesDir: nuxt.options.buildDir + "/graphql-operations"
777
+ };
778
+ this.paths.runtimeTypes = this.toModuleBuildRelative(
779
+ this.resolvers.module.resolve("./runtime/types.ts")
619
780
  );
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
- });
781
+ this.paths.serverOptions = this.findServerOptions();
782
+ }
783
+ resolvers;
784
+ paths;
785
+ isDev;
786
+ options;
787
+ prompt = new ConsolePrompt();
788
+ nitroExternals = [];
789
+ /**
790
+ * Find the path to the graphqlMiddleware.serverOptions.ts file.
791
+ */
792
+ findServerOptions() {
793
+ const newPath = this.resolvers.server.resolve(
794
+ "graphqlMiddleware.serverOptions"
795
+ );
796
+ const serverPath = fileExists(newPath);
797
+ if (serverPath) {
798
+ return serverPath;
799
+ }
800
+ const candidates = [
801
+ this.resolvers.root.resolve("graphqlMiddleware.serverOptions"),
802
+ this.resolvers.root.resolve("app/graphqlMiddleware.serverOptions"),
803
+ this.resolvers.src.resolve("graphqlMiddleware.serverOptions")
804
+ ];
805
+ for (let i = 0; i < candidates.length; i++) {
806
+ const path = candidates[i];
807
+ const filePath = fileExists(path);
808
+ if (filePath) {
809
+ throw new Error(
810
+ `The graphqlMiddleware.serverOptions file should be placed in Nuxt's <serverDir> ("${this.paths.serverDir}/graphqlMiddleware.serverOptions.ts").`
811
+ );
641
812
  }
642
813
  }
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");
814
+ logger.info("No graphqlMiddleware.serverOptions file found.");
815
+ return null;
816
+ }
817
+ findClientOptions() {
818
+ const clientOptionsPath = this.resolvers.app.resolve(
819
+ "graphqlMiddleware.clientOptions"
820
+ );
821
+ if (fileExists(clientOptionsPath)) {
822
+ return clientOptionsPath;
823
+ }
824
+ return null;
825
+ }
826
+ /**
827
+ * Transform the path relative to the module's build directory.
828
+ *
829
+ * @param path - The absolute path.
830
+ *
831
+ * @returns The path relative to the module's build directory.
832
+ */
833
+ toModuleBuildRelative(path) {
834
+ return relative(this.paths.moduleBuildDir, path);
835
+ }
836
+ /**
837
+ * Transform the path relative to the Nuxt build directory.
838
+ *
839
+ * @param path - The absolute path.
840
+ *
841
+ * @returns The path relative to the module's build directory.
842
+ */
843
+ toBuildRelative(path) {
844
+ return relative(this.nuxt.options.buildDir, path);
845
+ }
846
+ /**
847
+ * Get all file paths that match the import patterns.
848
+ */
849
+ async getImportPatternFiles() {
850
+ return resolveFiles(
851
+ this.nuxt.options.srcDir,
852
+ this.options.autoImportPatterns,
853
+ {
854
+ followSymbolicLinks: false
662
855
  }
663
- nuxtComposables.forEach((name2) => {
664
- addImports({
665
- from: moduleResolver.resolve("./runtime/composables/" + name2),
666
- name: name2
667
- });
856
+ );
857
+ }
858
+ matchesImportPattern(filePath) {
859
+ return micromatch.isMatch(filePath, this.options.autoImportPatterns);
860
+ }
861
+ addAlias(name, path) {
862
+ this.nuxt.options.alias[name] = path;
863
+ const pathFromName = `./${name.substring(1)}`;
864
+ this.nuxt.options.nitro.typescript ||= {};
865
+ this.nuxt.options.nitro.typescript.tsConfig ||= {};
866
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions ||= {};
867
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths ||= {};
868
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths[name] = [
869
+ pathFromName
870
+ ];
871
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths[name + "/*"] = [pathFromName + "/*"];
872
+ this.nuxt.options.typescript.tsConfig ||= {};
873
+ this.nuxt.options.typescript.tsConfig.compilerOptions ||= {};
874
+ this.nuxt.options.typescript.tsConfig.compilerOptions.paths ||= {};
875
+ this.nuxt.options.typescript.tsConfig.compilerOptions.paths[name] = [
876
+ pathFromName
877
+ ];
878
+ this.nuxt.options.typescript.tsConfig.compilerOptions.paths[name + "/*"] = [
879
+ pathFromName + "/*"
880
+ ];
881
+ this.inlineNitroExternals(name);
882
+ }
883
+ inlineNitroExternals(arg) {
884
+ const path = typeof arg === "string" ? arg : arg.dst;
885
+ this.nitroExternals.push(path);
886
+ this.transpile(path);
887
+ }
888
+ transpile(path) {
889
+ this.nuxt.options.build.transpile.push(path);
890
+ }
891
+ applyBuildConfig() {
892
+ this.nuxt.options.nitro.externals ||= {};
893
+ this.nuxt.options.nitro.externals.inline ||= [];
894
+ this.nuxt.options.nitro.externals.inline.push(...this.nitroExternals);
895
+ }
896
+ addTemplate(template) {
897
+ if (template.build) {
898
+ const content = template.build(this).trim();
899
+ addTemplate({
900
+ filename: template.options.path + ".js",
901
+ write: true,
902
+ getContents: () => content
668
903
  });
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
904
  }
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()
905
+ if (template.buildTypes) {
906
+ const content = template.buildTypes(this).trim();
907
+ const filename = template.options.path + ".d.ts";
908
+ addTypeTemplate({
909
+ filename,
910
+ write: true,
911
+ getContents: () => content
912
+ });
913
+ }
914
+ }
915
+ addPlugin(path) {
916
+ addPlugin(this.resolvers.module.resolve(path), {
917
+ append: false
689
918
  });
690
- addAlias("#graphql-documents", templateDocuments.dst);
691
- const templateContext = addTemplate({
692
- filename: GraphqlMiddlewareTemplate.ComposableContext,
693
- write: true,
694
- getContents: () => collector.getTemplateContext()
919
+ }
920
+ addServerHandler(name, path, method) {
921
+ addServerHandler({
922
+ handler: this.resolvers.module.resolve("./runtime/server/api/" + name),
923
+ route: this.options.serverApiPrefix + path,
924
+ method
695
925
  });
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
926
  }
712
- const operations: Operations
713
- export { operations, Operations }
714
- }
715
- `;
716
- }
927
+ addComposable(name) {
928
+ addImports({
929
+ from: this.resolvers.module.resolve("./runtime/composables/" + name),
930
+ name
717
931
  });
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
- `;
932
+ }
933
+ addServerUtil(name) {
934
+ addServerImports([
935
+ {
936
+ from: this.resolvers.module.resolve("./runtime/server/utils/" + name),
937
+ name
756
938
  }
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
939
+ ]);
940
+ }
941
+ }
773
942
 
774
- export type GraphqlResponseTyped = GraphqlResponse<GraphqlMiddlewareResponseUnion>
943
+ function defineGeneratorTemplate(options, build, buildTypes) {
944
+ return {
945
+ type: "generator",
946
+ options,
947
+ build,
948
+ buildTypes
949
+ };
950
+ }
951
+ function defineStaticTemplate(options, build, buildTypes) {
952
+ return {
953
+ type: "static",
954
+ options,
955
+ build,
956
+ buildTypes
957
+ };
958
+ }
775
959
 
776
- export { serverOptions }
960
+ const ClientOptions = defineStaticTemplate(
961
+ { path: "nuxt-graphql-middleware/client-options" },
962
+ (helper) => {
963
+ if (helper.paths.clientOptions) {
964
+ const pathRelative = helper.toModuleBuildRelative(
965
+ helper.paths.clientOptions
966
+ );
967
+ return `import clientOptions from '${pathRelative}'
968
+ export { clientOptions }
777
969
  `;
778
- }
779
- });
780
- const getClientOptionsImport = () => {
781
- const clientOptionsPath = appResolver.resolve(
782
- "graphqlMiddleware.clientOptions"
970
+ }
971
+ return `export const clientOptions = {}`;
972
+ },
973
+ (helper) => {
974
+ if (helper.paths.clientOptions) {
975
+ const pathRelative = helper.toModuleBuildRelative(
976
+ helper.paths.clientOptions
783
977
  );
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}
978
+ return `import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
979
+ import { clientOptions } from '${pathRelative}'
812
980
 
813
981
  export type GraphqlClientContext = typeof clientOptions extends GraphqlClientOptions<infer R> ? R : {}
814
982
 
815
983
  export { clientOptions }`;
816
- }
817
- return `import type { GraphqlClientOptions } from '${runtimeTypesPath}'
984
+ }
985
+ return `
986
+ import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
818
987
  export const clientOptions: GraphqlClientOptions
819
988
 
820
989
  export type GraphqlClientContext = {}
821
990
  `;
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")
991
+ }
992
+ );
993
+
994
+ const Documents = defineGeneratorTemplate(
995
+ { path: "nuxt-graphql-middleware/documents", virtual: true },
996
+ (output, helper) => {
997
+ return output.getOperationsFile({
998
+ exportName: "documents",
999
+ minify: !helper.isDev
1000
+ }).getSource();
1001
+ },
1002
+ () => {
1003
+ return `
1004
+ import type { Query, Mutation } from './operation-types'
1005
+
1006
+ declare module '#nuxt-graphql-middleware/documents' {
1007
+ export type Documents = {
1008
+ query: Record<keyof Query, string>
1009
+ mutation: Record<keyof Mutation, string>
1010
+ }
1011
+ export const documents: Documents
1012
+ }`;
1013
+ }
1014
+ );
1015
+
1016
+ const GraphqlConfig = defineStaticTemplate(
1017
+ { path: "nuxt-graphql-middleware/graphql.config" },
1018
+ (helper) => {
1019
+ const patterns = helper.options.autoImportPatterns || [];
1020
+ const configPath = helper.resolvers.root.resolve(
1021
+ (helper.options.graphqlConfigFilePath || "").replace(
1022
+ "/graphql.config.ts",
1023
+ ""
1024
+ )
833
1025
  );
834
- addServerHandler({
835
- handler: moduleResolver.resolve("./runtime/serverHandler/index"),
836
- route: options.serverApiPrefix + "/:operation/:name"
1026
+ const schemaPath = "./" + relative(configPath, helper.paths.schema);
1027
+ const documents = patterns.filter((v) => !v.includes("!")).map((pattern) => {
1028
+ return "./" + relative(configPath, helper.resolvers.root.resolve(pattern));
837
1029
  });
838
- if (options.enableFileUploads) {
839
- addServerHandler({
840
- handler: moduleResolver.resolve("./runtime/serverHandler/upload"),
841
- route: options.serverApiPrefix + "/upload/:name"
842
- });
1030
+ return `const schema = ${JSON.stringify(schemaPath)}
1031
+
1032
+ const documents = ${JSON.stringify(documents, null, 2)};
1033
+
1034
+ const config = {
1035
+ schema,
1036
+ documents,
1037
+ }
1038
+
1039
+ export default config
1040
+ `;
1041
+ },
1042
+ () => {
1043
+ return `
1044
+ import type { IGraphQLProject } from 'graphql-config'
1045
+
1046
+ type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
1047
+
1048
+ const config: WithRequired<IGraphQLProject, 'schema' | 'documents'>;
1049
+
1050
+ export default config;
1051
+ `;
1052
+ }
1053
+ );
1054
+
1055
+ const Helpers = defineStaticTemplate(
1056
+ { path: "nuxt-graphql-middleware/helpers" },
1057
+ (helper) => {
1058
+ return `export const serverApiPrefix = '${helper.options.serverApiPrefix}'
1059
+ export function getEndpoint(operation, operationName) {
1060
+ return serverApiPrefix + '/' + operation + '/' + operationName
1061
+ }
1062
+ `;
1063
+ },
1064
+ () => {
1065
+ return `export const serverApiPrefix: string;
1066
+ export function getEndpoint(operation: string, operationName: string): string`;
1067
+ }
1068
+ );
1069
+
1070
+ const NitroTypes = defineGeneratorTemplate(
1071
+ { path: "nuxt-graphql-middleware/nitro" },
1072
+ null,
1073
+ (output, helper) => {
1074
+ const operations = output.getCollectedOperations();
1075
+ const serverApiPrefix = helper.options.serverApiPrefix;
1076
+ const endpoints = [];
1077
+ const imports = [];
1078
+ for (const operation of operations) {
1079
+ imports.push(operation.typeName);
1080
+ const method = operation.operationType === OperationTypeNode.QUERY ? "get" : "post";
1081
+ endpoints.push(
1082
+ ` '${serverApiPrefix}/${operation.operationType}/${operation.graphqlName}': {
1083
+ '${method}': GraphqlResponse<${operation.typeName}>
1084
+ }`
1085
+ );
843
1086
  }
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
- }
1087
+ return `import type { GraphqlResponse } from './response'
1088
+ import type {
1089
+ ${imports.sort().join(",\n ")}
1090
+ } from './../graphql-operations'
1091
+
1092
+ declare module 'nitropack/types' {
1093
+ interface InternalApi {
1094
+ ${endpoints.sort().join("\n")}
1095
+ }
1096
+ }`;
1097
+ }
1098
+ );
1099
+
1100
+ const OperationTypesAll = defineGeneratorTemplate(
1101
+ { path: "nuxt-graphql-middleware/operation-types" },
1102
+ () => `export {}`,
1103
+ (output) => {
1104
+ return output.getOperationTypesFile({
1105
+ importFrom: "./../graphql-operations"
1106
+ }).getSource();
1107
+ }
1108
+ );
1109
+
1110
+ const Operations = defineGeneratorTemplate(
1111
+ { path: "graphql-operations/index" },
1112
+ (output) => {
1113
+ const typesFile = output.getOperations("js");
1114
+ return typesFile.getSource();
1115
+ },
1116
+ (output) => {
1117
+ const typesFile = output.getOperations("d.ts");
1118
+ return typesFile.getSource();
1119
+ }
1120
+ );
1121
+
1122
+ const Response = defineGeneratorTemplate(
1123
+ { path: "nuxt-graphql-middleware/response" },
1124
+ null,
1125
+ (output, helper) => {
1126
+ const operations = output.getCollectedOperations();
1127
+ const allTypes = operations.map((v) => v.typeName).sort();
1128
+ return `import type {
1129
+ ${allTypes.join(",\n ")}
1130
+ } from './../graphql-operations'
1131
+ import type { GraphqlResponseAdditions } from './server-options'
1132
+ import type { GraphqlServerResponse } from '${helper.paths.runtimeTypes}'
1133
+
1134
+ declare module '#nuxt-graphql-middleware/response' {
1135
+ export type GraphqlMiddlewareResponseUnion =
1136
+ | ${allTypes.join("\n | ") || "never"}
1137
+
1138
+ export type GraphqlResponse<T> = GraphqlServerResponse<T> & GraphqlResponseAdditions
1139
+ export type GraphqlResponseTyped = GraphqlResponse<GraphqlMiddlewareResponseUnion>
1140
+ }`;
1141
+ }
1142
+ );
1143
+
1144
+ const ServerOptions = defineStaticTemplate(
1145
+ { path: "nuxt-graphql-middleware/server-options" },
1146
+ (helper) => {
1147
+ const resolvedPathRelative = helper.paths.serverOptions ? helper.toModuleBuildRelative(helper.paths.serverOptions) : null;
1148
+ const serverOptionsLine = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions = {}`;
1149
+ return `
1150
+ ${serverOptionsLine}
1151
+ export { serverOptions }
1152
+ `;
1153
+ },
1154
+ (helper) => {
1155
+ const resolvedPathRelative = helper.paths.serverOptions ? helper.toModuleBuildRelative(helper.paths.serverOptions) : null;
1156
+ const serverOptionsLineTypes = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions: GraphqlMiddlewareServerOptions = {}`;
1157
+ return `
1158
+ import type { GraphqlMiddlewareServerOptions } from '${helper.paths.runtimeTypes}'
1159
+ ${serverOptionsLineTypes}
1160
+
1161
+ export type GraphqlResponseAdditions =
1162
+ typeof serverOptions extends GraphqlMiddlewareServerOptions<infer R, any, any> ? R : {}
1163
+
1164
+ export { serverOptions }`;
1165
+ }
1166
+ );
1167
+
1168
+ const Sources = defineGeneratorTemplate(
1169
+ { path: "nuxt-graphql-middleware/sources" },
1170
+ (output, helper) => {
1171
+ const operations = output.getCollectedOperations();
1172
+ const srcDir = helper.paths.root;
1173
+ const lines = [];
1174
+ for (const operation of operations) {
1175
+ const filePath = relative(srcDir, operation.filePath);
1176
+ lines.push(
1177
+ `${operation.operationType}_${operation.graphqlName}: '${filePath}',`
1178
+ );
1179
+ }
1180
+ return `
1181
+ export const operationSources = {
1182
+ ${lines.join("\n ")}
1183
+ }
1184
+ `;
1185
+ },
1186
+ () => {
1187
+ return `export const operationSources: Record<string, string>`;
1188
+ }
1189
+ );
1190
+
1191
+ const TEMPLATES = [
1192
+ ClientOptions,
1193
+ Documents,
1194
+ GraphqlConfig,
1195
+ Helpers,
1196
+ NitroTypes,
1197
+ OperationTypesAll,
1198
+ Operations,
1199
+ Response,
1200
+ ServerOptions,
1201
+ Sources
1202
+ ];
1203
+
1204
+ const DEVTOOLS_UI_ROUTE = "/__nuxt-graphql-middleware";
1205
+ const DEVTOOLS_UI_LOCAL_PORT = 3300;
1206
+ function setupDevToolsUI(nuxt, clientPath) {
1207
+ const isProductionBuild = existsSync$1(clientPath);
1208
+ if (isProductionBuild) {
1209
+ nuxt.hook("vite:serverCreated", async (server) => {
1210
+ const sirv = await import('sirv').then((r) => r.default || r);
1211
+ server.middlewares.use(
1212
+ DEVTOOLS_UI_ROUTE,
1213
+ sirv(clientPath, { dev: true, single: true })
853
1214
  );
854
1215
  });
855
- if (nuxt.options.dev || nuxt.options._prepare) {
856
- addServerHandler({
857
- handler: moduleResolver.resolve("./runtime/serverHandler/debug"),
858
- route: options.serverApiPrefix + "/debug"
1216
+ } else {
1217
+ nuxt.hook("vite:extendConfig", (config) => {
1218
+ config.server = config.server || {};
1219
+ config.server.proxy = config.server.proxy || {};
1220
+ config.server.proxy[DEVTOOLS_UI_ROUTE] = {
1221
+ target: "http://localhost:" + DEVTOOLS_UI_LOCAL_PORT + DEVTOOLS_UI_ROUTE,
1222
+ changeOrigin: true,
1223
+ followRedirects: true,
1224
+ rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
1225
+ };
1226
+ });
1227
+ }
1228
+ nuxt.hook("devtools:customTabs", (tabs) => {
1229
+ tabs.push({
1230
+ // unique identifier
1231
+ name: "nuxt-graphql-middleware",
1232
+ // title to display in the tab
1233
+ title: "GraphQL Middleware",
1234
+ // any icon from Iconify, or a URL to an image
1235
+ icon: "akar-icons:graphql-fill",
1236
+ // iframe view
1237
+ view: {
1238
+ type: "iframe",
1239
+ src: DEVTOOLS_UI_ROUTE
1240
+ }
1241
+ });
1242
+ });
1243
+ }
1244
+
1245
+ const RPC_NAMESPACE = "nuxt-graphql-middleware";
1246
+ class DevModeHandler {
1247
+ constructor(nuxt, schemaProvider, collector, helper) {
1248
+ this.nuxt = nuxt;
1249
+ this.schemaProvider = schemaProvider;
1250
+ this.collector = collector;
1251
+ this.helper = helper;
1252
+ }
1253
+ devToolsRpc = null;
1254
+ nitro = null;
1255
+ viteWebSocket = null;
1256
+ operationsToReload = /* @__PURE__ */ new Set();
1257
+ init() {
1258
+ this.nuxt.hooks.hookOnce("ready", this.onReady.bind(this));
1259
+ this.nuxt.hooks.hookOnce(
1260
+ "vite:serverCreated",
1261
+ this.onViteServerCreated.bind(this)
1262
+ );
1263
+ this.nuxt.hook("builder:watch", this.onBuilderWatch.bind(this));
1264
+ if (this.helper.options.devtools) {
1265
+ const clientPath = this.helper.resolvers.module.resolve("./client");
1266
+ setupDevToolsUI(this.nuxt, clientPath);
1267
+ onDevToolsInitialized(() => {
1268
+ this.devToolsRpc = extendServerRpc(
1269
+ RPC_NAMESPACE,
1270
+ {
1271
+ // register server RPC functions
1272
+ getModuleOptions: () => {
1273
+ return this.helper.options;
1274
+ },
1275
+ getDocuments: () => {
1276
+ return [...this.collector.rpcItems.values()];
1277
+ }
1278
+ }
1279
+ );
859
1280
  });
860
- nuxt.hook("builder:watch", async (event, pathAbsolute) => {
861
- if (pathAbsolute === schemaPath) {
1281
+ }
1282
+ }
1283
+ onReady() {
1284
+ this.nitro = useNitro();
1285
+ this.nitro.hooks.hook("compiled", this.onNitroCompiled.bind(this));
1286
+ }
1287
+ async onBuilderWatch(event, pathAbsolute) {
1288
+ if (pathAbsolute === this.helper.paths.schema) {
1289
+ return;
1290
+ }
1291
+ if (!pathAbsolute.match(/\.(gql|graphql)$/)) {
1292
+ return;
1293
+ }
1294
+ this.helper.prompt.abort();
1295
+ const { hasChanged, affectedOperations, error } = await this.collector.handleWatchEvent(event, pathAbsolute);
1296
+ if (error) {
1297
+ this.sendError(error);
1298
+ await this.helper.prompt.confirm("Do you want to download and update the GraphQL schema?").then(async (shouldReload) => {
1299
+ if (shouldReload !== "yes") {
862
1300
  return;
863
1301
  }
864
- if (!pathAbsolute.match(/\.(gql|graphql)$/)) {
865
- return;
1302
+ try {
1303
+ await this.schemaProvider.loadSchema({ forceDownload: true });
1304
+ await this.collector.updateSchema(this.schemaProvider.getSchema());
1305
+ } catch (e) {
1306
+ logger.error(e);
866
1307
  }
867
- await collector.handleWatchEvent(event, pathAbsolute);
868
1308
  });
1309
+ return;
1310
+ }
1311
+ if (!hasChanged) {
1312
+ return;
1313
+ }
1314
+ if (this.nitro) {
1315
+ await this.nitro.hooks.callHook("rollup:reload");
1316
+ }
1317
+ if (affectedOperations.length) {
1318
+ affectedOperations.forEach(
1319
+ (operation) => this.operationsToReload.add(operation)
1320
+ );
1321
+ }
1322
+ if (this.devToolsRpc) {
1323
+ try {
1324
+ this.devToolsRpc.broadcast.documentsUpdated([
1325
+ ...this.collector.rpcItems.values()
1326
+ ]);
1327
+ } catch {
1328
+ logger.info(
1329
+ "Failed to update GraphQL documents in dev tools. The documents might be stale."
1330
+ );
1331
+ }
1332
+ }
1333
+ }
1334
+ onViteServerCreated(server) {
1335
+ this.viteWebSocket = server.ws;
1336
+ }
1337
+ sendError(error) {
1338
+ if (!this.viteWebSocket) {
1339
+ return;
1340
+ }
1341
+ this.viteWebSocket.send({
1342
+ type: "error",
1343
+ err: {
1344
+ message: error.message,
1345
+ stack: ""
1346
+ }
1347
+ });
1348
+ }
1349
+ onNitroCompiled() {
1350
+ if (!this.operationsToReload.size) {
1351
+ return;
1352
+ }
1353
+ const operations = [...this.operationsToReload.values()];
1354
+ this.operationsToReload.clear();
1355
+ if (!this.viteWebSocket) {
1356
+ return;
1357
+ }
1358
+ this.viteWebSocket.send({
1359
+ type: "custom",
1360
+ event: "nuxt-graphql-middleware:reload",
1361
+ data: { operations }
1362
+ });
1363
+ }
1364
+ }
1365
+
1366
+ const module = defineNuxtModule({
1367
+ meta: {
1368
+ name,
1369
+ configKey: "graphqlMiddleware",
1370
+ version,
1371
+ compatibility: {
1372
+ nuxt: ">=3.15.0"
869
1373
  }
1374
+ },
1375
+ defaults: defaultOptions,
1376
+ async setup(passedOptions, nuxt) {
1377
+ const helper = new ModuleHelper(nuxt, import.meta.url, passedOptions);
1378
+ const schemaProvider = new SchemaProvider(helper);
1379
+ await schemaProvider.init();
1380
+ const collector = new Collector(schemaProvider.getSchema(), helper);
1381
+ nuxt.options.appConfig.graphqlMiddleware = {
1382
+ clientCacheEnabled: !!helper.options.clientCache?.enabled,
1383
+ clientCacheMaxSize: helper.options.clientCache?.maxSize ?? 100
1384
+ };
1385
+ nuxt.options.runtimeConfig.graphqlMiddleware = {
1386
+ graphqlEndpoint: helper.options.graphqlEndpoint || ""
1387
+ };
1388
+ helper.transpile(fileURLToPath(new URL("./runtime", import.meta.url)));
1389
+ helper.inlineNitroExternals(helper.resolvers.module.resolve("./runtime"));
1390
+ helper.inlineNitroExternals(helper.paths.moduleBuildDir);
1391
+ helper.inlineNitroExternals(helper.paths.moduleTypesDir);
1392
+ helper.addAlias("#nuxt-graphql-middleware", helper.paths.moduleBuildDir);
1393
+ helper.addAlias("#graphql-operations", helper.paths.moduleTypesDir);
1394
+ helper.addPlugin("./runtime/plugins/provideState");
1395
+ if (helper.isDev && helper.options.errorOverlay) {
1396
+ helper.addPlugin("./runtime/plugins/devMode");
1397
+ }
1398
+ helper.addServerHandler("query", "/query/:name", "get");
1399
+ helper.addServerHandler("mutation", "/mutation/:name", "post");
1400
+ if (helper.options.enableFileUploads) {
1401
+ helper.addServerHandler("upload", "/upload/:name", "post");
1402
+ }
1403
+ if (helper.isDev) {
1404
+ helper.addServerHandler("debug", "/debug", "get");
1405
+ }
1406
+ if (helper.options.includeComposables) {
1407
+ helper.addComposable("useGraphqlQuery");
1408
+ helper.addComposable("useGraphqlMutation");
1409
+ helper.addComposable("useGraphqlState");
1410
+ helper.addComposable("useAsyncGraphqlQuery");
1411
+ if (helper.options.enableFileUploads) {
1412
+ helper.addComposable("useGraphqlUploadMutation");
1413
+ }
1414
+ helper.addServerUtil("useGraphqlQuery");
1415
+ helper.addServerUtil("useGraphqlMutation");
1416
+ helper.addServerUtil("doGraphqlRequest");
1417
+ }
1418
+ TEMPLATES.forEach((template) => {
1419
+ if (template.type === "static") {
1420
+ helper.addTemplate(template);
1421
+ } else {
1422
+ collector.addTemplate(template);
1423
+ }
1424
+ });
1425
+ helper.applyBuildConfig();
1426
+ await collector.init();
1427
+ if (!helper.isDev) {
1428
+ return;
1429
+ }
1430
+ const devModeHandler = new DevModeHandler(
1431
+ nuxt,
1432
+ schemaProvider,
1433
+ collector,
1434
+ helper
1435
+ );
1436
+ devModeHandler.init();
870
1437
  }
871
1438
  });
872
1439