nuxt-graphql-middleware 5.0.0-alpha.5 → 5.0.0-alpha.7

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 (44) hide show
  1. package/dist/client/200.html +7 -7
  2. package/dist/client/404.html +7 -7
  3. package/dist/client/_nuxt/{CZ2Qwgdk.js → B4KMzhZo.js} +1 -1
  4. package/dist/client/_nuxt/BawWjxPx.js +25 -0
  5. package/dist/client/_nuxt/BtHrwWER.js +1 -0
  6. package/dist/client/_nuxt/BvMfLM9s.js +1 -0
  7. package/dist/client/_nuxt/{GOrnHr4p.js → DkAo05uu.js} +1 -1
  8. package/dist/client/_nuxt/builds/latest.json +1 -1
  9. package/dist/client/_nuxt/builds/meta/1d4cde1f-51c0-4bab-b233-3b063cc8ad1c.json +1 -0
  10. package/dist/client/index.html +7 -7
  11. package/dist/module.d.mts +192 -162
  12. package/dist/module.d.ts +192 -162
  13. package/dist/module.json +1 -1
  14. package/dist/module.mjs +1027 -620
  15. package/dist/runtime/components/CodeFrame.vue +1 -1
  16. package/dist/runtime/composables/useAsyncGraphqlQuery.d.ts +1 -1
  17. package/dist/runtime/composables/useAsyncGraphqlQuery.js +9 -1
  18. package/dist/runtime/composables/useGraphqlMutation.d.ts +1 -1
  19. package/dist/runtime/composables/useGraphqlQuery.d.ts +1 -1
  20. package/dist/runtime/composables/useGraphqlUploadMutation.d.ts +1 -1
  21. package/dist/runtime/helpers/composables.d.ts +1 -1
  22. package/dist/runtime/server/api/mutation.js +28 -0
  23. package/dist/runtime/server/api/query.js +29 -0
  24. package/dist/runtime/server/api/upload.d.ts +2 -0
  25. package/dist/runtime/{serverHandler → server/api}/upload.js +11 -9
  26. package/dist/runtime/{serverHandler → server}/helpers/index.d.ts +8 -10
  27. package/dist/runtime/{serverHandler → server}/helpers/index.js +8 -25
  28. package/dist/runtime/server/utils/doGraphqlRequest.d.ts +18 -0
  29. package/dist/runtime/server/utils/doGraphqlRequest.js +67 -0
  30. package/dist/runtime/server/utils/useGraphqlMutation.d.ts +1 -1
  31. package/dist/runtime/server/utils/useGraphqlQuery.d.ts +1 -1
  32. package/dist/runtime/settings/index.d.ts +0 -39
  33. package/dist/runtime/settings/index.js +0 -13
  34. package/package.json +9 -7
  35. package/dist/client/_nuxt/BS583yk8.js +0 -25
  36. package/dist/client/_nuxt/DpxjPVZy.js +0 -1
  37. package/dist/client/_nuxt/builds/meta/c22c2916-33e9-427d-b6fe-10f11766c207.json +0 -1
  38. package/dist/client/_nuxt/exxdaCPN.js +0 -1
  39. package/dist/runtime/serverHandler/index.js +0 -78
  40. package/dist/runtime/serverHandler/tsconfig.json +0 -3
  41. /package/dist/runtime/{serverHandler → server/api}/debug.d.ts +0 -0
  42. /package/dist/runtime/{serverHandler → server/api}/debug.js +0 -0
  43. /package/dist/runtime/{serverHandler/index.d.ts → server/api/mutation.d.ts} +0 -0
  44. /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';
3
- import { relative } from 'pathe';
4
- import { defu } from 'defu';
5
- import { useLogger, resolveFiles, defineNuxtModule, resolveAlias, createResolver, addImports, addServerImports, addTypeTemplate, 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';
2
+ import { useLogger, addTemplate, addServerTemplate, addTypeTemplate, resolveAlias, createResolver, resolveFiles, addPlugin, addServerHandler, addImports, addServerImports, useNitro, defineNuxtModule } from '@nuxt/kit';
9
3
  import fs from 'node:fs/promises';
10
- import { existsSync as existsSync$1, promises } from 'node:fs';
11
- import { generate } from '@graphql-codegen/cli';
12
- import * as PluginSchemaAst from '@graphql-codegen/schema-ast';
4
+ import { existsSync, promises } from 'node:fs';
5
+ import { relative } from 'pathe';
13
6
  import { basename } from 'node:path';
14
- import { printSourceLocation, parse, OperationTypeNode, Source } from 'graphql';
7
+ import { printSourceLocation, parse, Source, OperationTypeNode } from 'graphql';
15
8
  import { Generator, FieldNotFoundError, TypeNotFoundError, FragmentNotFoundError } from 'graphql-typescript-deluxe';
16
- import colors from 'picocolors';
9
+ import color from 'picocolors';
17
10
  import { validateGraphQlDocuments } from '@graphql-tools/utils';
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 * as 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.5";
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.7";
90
23
 
91
24
  const logger = useLogger(name);
92
25
  const defaultOptions = {
@@ -98,59 +31,26 @@ const defaultOptions = {
98
31
  includeComposables: true,
99
32
  documents: [],
100
33
  devtools: true,
101
- errorOverlay: true
34
+ errorOverlay: true,
35
+ graphqlConfigFilePath: "./graphql.config.ts"
102
36
  };
103
37
  function validateOptions(options) {
104
38
  if (!options.graphqlEndpoint) {
105
39
  throw new Error("Missing graphqlEndpoint.");
106
40
  }
107
41
  }
108
- async function getSchemaPath(schemaPath, options, resolver, writeToDisk = false) {
109
- const dest = resolver(schemaPath);
110
- if (!options.downloadSchema) {
111
- const fileExists2 = await fs.access(dest).then(() => true).catch(() => false);
112
- if (!fileExists2) {
113
- logger.error(
114
- '"downloadSchema" is set to false but no schema exists at ' + dest
115
- );
116
- throw new Error("Missing GraphQL schema.");
117
- }
118
- const schemaContent = await fs.readFile(dest).then((v) => v.toString());
119
- return { schemaPath, schemaContent };
120
- }
121
- if (!options.graphqlEndpoint) {
122
- throw new Error("Missing graphqlEndpoint config.");
123
- }
124
- const result = await generateSchema(options, dest, writeToDisk);
125
- return { schemaPath, schemaContent: result.content };
126
- }
127
42
  const fileExists = (path, extensions = ["js", "ts", "mjs"]) => {
128
43
  if (!path) {
129
44
  return null;
130
- } else if (existsSync$1(path)) {
45
+ } else if (existsSync(path)) {
131
46
  return path;
132
47
  }
133
48
  const extension = extensions.find(
134
- (extension2) => existsSync$1(`${path}.${extension2}`)
49
+ (extension2) => existsSync(`${path}.${extension2}`)
135
50
  );
136
51
  return extension ? `${path}.${extension}` : null;
137
52
  };
138
53
 
139
- function generateResponseTypeTemplate(operations, context) {
140
- const allTypes = operations.map((v) => v.typeName).sort();
141
- return `import type {
142
- ${allTypes.join(",\n ")}
143
- } from './../graphql-operations'
144
- import type { GraphqlResponseAdditions } from './server-options'
145
- import type { GraphqlServerResponse } from '${context.runtimeTypesPath}'
146
-
147
- export type GraphqlMiddlewareResponseUnion =
148
- | ${allTypes.join("\n | ") || "never"}
149
-
150
- export type GraphqlResponse<T> = GraphqlServerResponse<T> & GraphqlResponseAdditions
151
- export type GraphqlResponseTyped = GraphqlResponse<GraphqlMiddlewareResponseUnion>`;
152
- }
153
-
154
54
  const SYMBOL_CROSS = "x";
155
55
  const SYMBOL_CHECK = "\u2714";
156
56
  function getMaxLengths(entries) {
@@ -175,11 +75,11 @@ function logAllEntries(entries) {
175
75
  let prevHadError = false;
176
76
  for (const entry of entries) {
177
77
  const hasErrors = entry.errors.length > 0;
178
- const icon = hasErrors ? colors.red(SYMBOL_CROSS) : colors.green(SYMBOL_CHECK);
78
+ const icon = hasErrors ? color.red(SYMBOL_CROSS) : color.green(SYMBOL_CHECK);
179
79
  const type = entry.type.padEnd(lengths.type);
180
- const namePadded = colors.bold(entry.name.padEnd(lengths.name));
181
- const name = hasErrors ? colors.red(namePadded) : colors.green(namePadded);
182
- const path = colors.dim(entry.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);
183
83
  const parts = [icon, type, name, path];
184
84
  if (hasErrors && !prevHadError) {
185
85
  process.stdout.write("-".repeat(process.stdout.columns) + "\n");
@@ -188,10 +88,10 @@ function logAllEntries(entries) {
188
88
  if (hasErrors) {
189
89
  const errorLines = [];
190
90
  entry.errors.forEach((error) => {
191
- let output = colors.red(error.message);
91
+ let output = color.red(error.message);
192
92
  if (error.source && error.locations) {
193
93
  for (const location of error.locations) {
194
- output += "\n\n" + colors.red(printSourceLocation(error.source, location));
94
+ output += "\n\n" + color.red(printSourceLocation(error.source, location));
195
95
  }
196
96
  }
197
97
  errorLines.push(output);
@@ -238,51 +138,11 @@ class CollectedFile {
238
138
  }
239
139
  }
240
140
 
241
- function generateNitroTypes(operations, serverApiPrefix) {
242
- const endpoints = [];
243
- const imports = [];
244
- for (const operation of operations) {
245
- imports.push(operation.typeName);
246
- const method = operation.operationType === OperationTypeNode.QUERY ? "get" : "post";
247
- endpoints.push(
248
- ` '${serverApiPrefix}/${operation.operationType}/${operation.graphqlName}': {
249
- '${method}': GraphqlResponse<${operation.typeName}>
250
- }`
251
- );
252
- }
253
- return `import type { GraphqlResponse } from './response'
254
- import type {
255
- ${imports.sort().join(",\n ")}
256
- } from './../graphql-operations'
257
-
258
- declare module 'nitropack/types' {
259
- interface InternalApi {
260
- ${endpoints.sort().join("\n")}
261
- }
262
- }`;
263
- }
264
-
265
- function generateSourcesTemplate(operations, srcDir) {
266
- const lines = [];
267
- for (const operation of operations) {
268
- const filePath = relative(srcDir, operation.filePath);
269
- lines.push(
270
- `${operation.operationType}_${operation.graphqlName}: '${filePath}',`
271
- );
272
- }
273
- return `
274
- export const operationSources = {
275
- ${lines.join("\n ")}
276
- }
277
- `;
278
- }
279
-
280
141
  class Collector {
281
- constructor(schema, context, nuxtConfigDocuments = [], generatorOptions = {}) {
142
+ constructor(schema, helper) {
282
143
  this.schema = schema;
283
- this.context = context;
284
- this.nuxtConfigDocuments = nuxtConfigDocuments;
285
- const mappedOptions = { ...generatorOptions };
144
+ this.helper = helper;
145
+ const mappedOptions = { ...helper.options.codegenConfig };
286
146
  if (!mappedOptions.output) {
287
147
  mappedOptions.output = {};
288
148
  }
@@ -310,35 +170,27 @@ class Collector {
310
170
  */
311
171
  rpcItems = /* @__PURE__ */ new Map();
312
172
  /**
313
- * The generated TypeScript type template output.
314
- */
315
- outputTypes = "";
316
- /**
317
- * The generated TypeScript enum template output.
318
- */
319
- outputEnums = "";
320
- /**
321
- * The generated oeprations file.
322
- */
323
- outputOperations = "";
324
- /**
325
- * The generated oepration types file.
173
+ * The registered templates.
326
174
  */
327
- outputOperationTypes = "";
175
+ templates = [];
328
176
  /**
329
- * The generated context template file.
177
+ * The generated template contents.
330
178
  */
331
- outputResponseTypes = "";
332
- /**
333
- * The generated nitro template file.
334
- */
335
- outputNitroTypes = "";
336
- /**
337
- * The generated nitro template file.
338
- */
339
- outputSources = "";
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
+ }
340
192
  filePathToBuildRelative(filePath) {
341
- return "./" + relative(this.context.buildDir, filePath);
193
+ return "./" + this.helper.toBuildRelative(filePath);
342
194
  }
343
195
  filePathToSourceRelative(filePath) {
344
196
  return "./" + relative(process.cwd(), filePath);
@@ -351,16 +203,12 @@ class Collector {
351
203
  errors
352
204
  };
353
205
  }
354
- buildOutputTypes(file) {
355
- let output = "";
356
- const enumImports = file.getTypeScriptEnumDependencies();
357
- if (enumImports.length) {
358
- output += `import type { ${enumImports.join(", ")} } from './enums'
359
-
360
- `;
206
+ getTemplate(template) {
207
+ const content = this.templateResult.get(template);
208
+ if (content === void 0) {
209
+ throw new Error(`Missing template content: ${template}`);
361
210
  }
362
- output += file.getSource();
363
- return output;
211
+ return content;
364
212
  }
365
213
  /**
366
214
  * Executes code gen and performs validation for operations.
@@ -369,27 +217,22 @@ class Collector {
369
217
  const output = this.generator.build();
370
218
  const operations = output.getCollectedOperations();
371
219
  const generatedCode = output.getGeneratedCode();
372
- this.outputOperations = output.getOperationsFile({
373
- exportName: "documents",
374
- minify: !this.context.isDev
375
- }).getSource();
376
- this.outputOperationTypes = output.getOperationTypesFile({
377
- importFrom: "./../graphql-operations"
378
- }).getSource();
379
- this.outputEnums = output.buildFile(["enum"]).getSource();
380
- this.outputTypes = this.buildOutputTypes(output.getTypes());
381
- this.outputResponseTypes = generateResponseTypeTemplate(
382
- operations,
383
- this.context
384
- );
385
- this.outputNitroTypes = generateNitroTypes(
386
- operations,
387
- this.context.serverApiPrefix
388
- );
389
- this.outputSources = generateSourcesTemplate(
390
- operations,
391
- this.context.rootDir
392
- );
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
+ });
393
236
  const fragmentMap = /* @__PURE__ */ new Map();
394
237
  const operationSourceMap = /* @__PURE__ */ new Map();
395
238
  for (const code of generatedCode) {
@@ -418,7 +261,7 @@ class Collector {
418
261
  } else {
419
262
  this.operationTimestamps.set(operation.graphqlName, operation.timestamp);
420
263
  }
421
- const shouldLog = errors.length || !this.context.logOnlyErrors;
264
+ const shouldLog = errors.length || !this.helper.options.logOnlyErrors;
422
265
  if (shouldLog) {
423
266
  logEntries.push(this.operationToLogEntry(operation, errors));
424
267
  }
@@ -427,21 +270,23 @@ class Collector {
427
270
  if (hasErrors) {
428
271
  throw new Error("GraphQL errors");
429
272
  }
430
- for (const code of generatedCode) {
431
- const id = `${code.identifier}_${code.graphqlName}`;
432
- if (code.identifier === "fragment" || code.identifier === "mutation" || code.identifier === "query") {
433
- if (this.rpcItems.get(id)?.timestamp === code.timestamp) {
434
- continue;
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
+ });
435
289
  }
436
- const fragmentDepdendencies = code.getGraphQLFragmentDependencies().map((name) => fragmentMap.get(name) || "").join("\n\n");
437
- this.rpcItems.set(id, {
438
- id,
439
- timestamp: code.timestamp,
440
- source: code.source + "\n\n" + fragmentDepdendencies,
441
- name: code.graphqlName,
442
- filePath: code.filePath,
443
- identifier: code.identifier
444
- });
445
290
  }
446
291
  }
447
292
  }
@@ -466,31 +311,45 @@ class Collector {
466
311
  logError(error) {
467
312
  let output = `${SYMBOL_CROSS}`;
468
313
  output += this.buildErrorMessage(error);
469
- logger.error(colors.red(output));
314
+ logger.error(color.red(output));
470
315
  }
471
316
  /**
472
- * 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.
473
324
  */
474
- async getImportPatternFiles() {
475
- if (this.context.patterns.length) {
476
- return resolveFiles(this.context.srcDir, this.context.patterns, {
477
- followSymbolicLinks: false
478
- });
325
+ async init() {
326
+ try {
327
+ await this.initDocuments();
328
+ } catch (e) {
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.");
479
339
  }
480
- return [];
481
340
  }
482
341
  /**
483
342
  * Initialise the collector.
484
343
  */
485
- async init() {
344
+ async initDocuments() {
486
345
  try {
487
- const files = await this.getImportPatternFiles();
346
+ const files = await this.helper.getImportPatternFiles();
488
347
  for (const filePath of files) {
489
348
  await this.addFile(filePath);
490
349
  }
491
- const nuxtConfigDocuments = this.nuxtConfigDocuments.join("\n\n");
350
+ const nuxtConfigDocuments = this.helper.options.documents.join("\n\n");
492
351
  if (nuxtConfigDocuments.length) {
493
- const filePath = this.context.nuxtConfigPath;
352
+ const filePath = this.helper.paths.nuxtConfig;
494
353
  const file = new CollectedFile(filePath, nuxtConfigDocuments, false);
495
354
  this.files.set(filePath, file);
496
355
  this.generator.add({
@@ -510,6 +369,9 @@ class Collector {
510
369
  */
511
370
  async addFile(filePath) {
512
371
  const file = await CollectedFile.fromFilePath(filePath);
372
+ if (!file.fileContents) {
373
+ return null;
374
+ }
513
375
  this.files.set(filePath, file);
514
376
  this.generator.add({
515
377
  filePath,
@@ -518,26 +380,32 @@ class Collector {
518
380
  return file;
519
381
  }
520
382
  async handleAdd(filePath) {
521
- const matching = await this.getImportPatternFiles();
522
- if (!matching.includes(filePath)) {
383
+ if (!this.helper.matchesImportPattern(filePath)) {
523
384
  return false;
524
385
  }
525
- await this.addFile(filePath);
526
- return true;
386
+ const result = await this.addFile(filePath);
387
+ return !!result;
527
388
  }
528
389
  async handleChange(filePath) {
390
+ if (!this.helper.matchesImportPattern(filePath)) {
391
+ return false;
392
+ }
529
393
  const file = this.files.get(filePath);
530
394
  if (!file) {
531
- return false;
395
+ return this.handleAdd(filePath);
532
396
  }
533
- const needsUpdate = await file.update();
534
- if (!needsUpdate) {
535
- 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);
536
408
  }
537
- this.generator.update({
538
- filePath,
539
- documentNode: file.parsed
540
- });
541
409
  return true;
542
410
  }
543
411
  handleUnlink(filePath) {
@@ -566,6 +434,7 @@ class Collector {
566
434
  */
567
435
  async handleWatchEvent(event, filePath) {
568
436
  let hasChanged = false;
437
+ const oldOperationTimestamps = new Map(this.operationTimestamps);
569
438
  try {
570
439
  if (event === "add") {
571
440
  hasChanged = await this.handleAdd(filePath);
@@ -575,7 +444,6 @@ class Collector {
575
444
  hasChanged = this.handleUnlink(filePath);
576
445
  } else if (event === "unlinkDir") {
577
446
  hasChanged = this.handleUnlinkDir(filePath);
578
- } else if (event === "addDir") {
579
447
  }
580
448
  if (hasChanged) {
581
449
  this.buildState();
@@ -586,327 +454,700 @@ class Collector {
586
454
  this.logError(e);
587
455
  return {
588
456
  hasChanged: false,
457
+ affectedOperations: [],
589
458
  error: { message: this.buildErrorMessage(e) }
590
459
  };
591
460
  }
461
+ const affectedOperations = [];
592
462
  if (hasChanged) {
593
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
+ }
594
470
  }
595
- return { hasChanged };
471
+ return { hasChanged, affectedOperations };
472
+ }
473
+ /**
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.
478
+ */
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
+ });
596
496
  }
597
497
  /**
598
- * Get the TypeScript types template contents.
498
+ * Adds a template that dependes on Collector state.
599
499
  */
600
- getTemplateTypes() {
601
- return this.outputTypes;
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;
602
534
  }
603
535
  /**
604
- * Get the TypeScript Enums template contents.
536
+ * The raw schema content.
605
537
  */
606
- getTemplateEnums() {
607
- return this.outputEnums;
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;
608
566
  }
609
567
  /**
610
- * Get the context template contents.
568
+ * Loads the schema from disk.
569
+ *
570
+ * @returns The schema contents from disk.
611
571
  */
612
- getTemplateResponseTypes() {
613
- return this.outputResponseTypes;
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());
614
582
  }
615
583
  /**
616
- * Get the operations template contents.
584
+ * Downloads the schema and saves it to disk.
585
+ *
586
+ * @returns The schema contents.
617
587
  */
618
- getTemplateOperations() {
619
- return this.outputOperations;
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);
620
618
  }
621
619
  /**
622
- * Get the operation types template contents.
620
+ * Determine if the schema exists on disk.
621
+ *
622
+ * @returns True if the schema file exists on disk.
623
623
  */
624
- getTemplateOperationTypes() {
625
- return this.outputOperationTypes;
624
+ hasSchemaOnDisk() {
625
+ return fs.access(this.helper.paths.schema).then(() => true).catch(() => false);
626
626
  }
627
627
  /**
628
- * Get the nitro types template contents.
628
+ * Load the schema either from disk or by downloading it.
629
+ *
630
+ * @param forceDownload - Forces downloading the schema.
629
631
  */
630
- getTemplateNitroTypes() {
631
- return this.outputNitroTypes;
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
+ });
632
643
  }
633
644
  /**
634
- * Get the nitro types template contents.
645
+ * Get the schema.
646
+ *
647
+ * @returns The parsed GraphQL schema object.
635
648
  */
636
- getTemplateSources() {
637
- return this.outputSources;
649
+ getSchema() {
650
+ if (!this.schema) {
651
+ throw new Error("Failed to load schema.");
652
+ }
653
+ return this.schema;
638
654
  }
639
655
  }
640
656
 
641
- function generateDocumentTypesTemplate() {
642
- return `
643
- import type { Query, Mutation } from './operations'
644
-
645
- declare module '#nuxt-graphql-middleware/documents' {
646
- export type Documents = {
647
- query: Record<keyof Query, string>
648
- mutation: Record<keyof Mutation, string>
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);
649
678
  }
650
- export const documents: Documents
651
- }`;
652
- }
653
-
654
- function useViteWebSocket(nuxt) {
655
- return new Promise((resolve) => {
656
- nuxt.hooks.hook("vite:serverCreated", (viteServer) => {
657
- resolve(viteServer.ws);
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";
658
720
  });
659
- });
660
- }
661
- const RPC_NAMESPACE = "nuxt-graphql-middleware";
662
- const module = defineNuxtModule({
663
- meta: {
664
- name,
665
- configKey: "graphqlMiddleware",
666
- version,
667
- compatibility: {
668
- nuxt: ">=3.15.0"
669
- }
670
- },
671
- defaults: defaultOptions,
672
- async setup(passedOptions, nuxt) {
673
- const options = defu({}, passedOptions, defaultOptions);
674
- function addAlias(name2, path) {
675
- nuxt.options.alias[name2] = path;
676
- }
677
- function inlineNitroExternals(path) {
678
- nuxt.options.nitro.externals = nuxt.options.nitro.externals || {};
679
- nuxt.options.nitro.externals.inline = nuxt.options.nitro.externals.inline || [];
680
- nuxt.options.nitro.externals.inline.push(path);
721
+ }
722
+ abort() {
723
+ if (this.abortController) {
724
+ this.abortController.abort();
725
+ this.abortController = null;
681
726
  }
727
+ }
728
+ }
729
+
730
+ class ModuleHelper {
731
+ constructor(nuxt, moduleUrl, options) {
732
+ this.nuxt = nuxt;
682
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
+ }
683
741
  if (isModuleBuild) {
684
- options.graphqlEndpoint = "http://localhost";
685
- options.downloadSchema = false;
686
- options.schemaPath = "~~/schema.graphql";
687
- options.autoImportPatterns = [
742
+ mergedOptions.graphqlEndpoint = "http://localhost";
743
+ mergedOptions.downloadSchema = false;
744
+ mergedOptions.schemaPath = "~~/schema.graphql";
745
+ mergedOptions.autoImportPatterns = [
688
746
  "~~/playground/**/*.{gql,graphql}",
689
747
  "!node_modules"
690
748
  ];
691
749
  }
692
- if (!passedOptions.autoImportPatterns) {
693
- options.autoImportPatterns = ["~~/**/*.{gql,graphql}", "!node_modules"];
694
- }
695
- options.autoImportPatterns = (options.autoImportPatterns || []).map(
696
- (pattern) => {
697
- return resolveAlias(pattern);
698
- }
699
- );
750
+ mergedOptions.autoImportPatterns = (mergedOptions.autoImportPatterns || []).map((pattern) => {
751
+ return resolveAlias(pattern);
752
+ });
753
+ this.options = mergedOptions;
700
754
  if (!nuxt.options._prepare) {
701
- validateOptions(options);
755
+ validateOptions(this.options);
702
756
  }
703
- const moduleResolver = createResolver(import.meta.url);
704
- const serverResolver = createResolver(nuxt.options.serverDir);
705
- const srcResolver = createResolver(nuxt.options.srcDir);
706
- const appResolver = createResolver(nuxt.options.dir.app);
707
- const rootDir = nuxt.options.rootDir;
708
- const rootResolver = createResolver(rootDir);
709
- const { schemaPath, schemaContent } = await getSchemaPath(
710
- resolveAlias(options.schemaPath),
711
- options,
712
- rootResolver.resolve,
713
- options.downloadSchema
714
- );
715
- const schema = await loadSchema(schemaContent, {
716
- loaders: []
717
- });
718
- const runtimeDir = fileURLToPath(new URL("./runtime", import.meta.url));
719
- nuxt.options.build.transpile.push(runtimeDir);
720
- const nuxtGraphqlMiddlewareBuildDir = nuxt.options.buildDir + "/nuxt-graphql-middleware";
721
- const operationTypesBuildDir = nuxt.options.buildDir + "/graphql-operations";
722
- const toBuildRelative = (path) => {
723
- return relative(nuxtGraphqlMiddlewareBuildDir, path);
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)
724
764
  };
725
- addAlias("#nuxt-graphql-middleware", nuxtGraphqlMiddlewareBuildDir);
726
- addAlias("#graphql-operations", operationTypesBuildDir);
727
- const context = {
728
- isDev: nuxt.options.dev,
729
- patterns: options.autoImportPatterns || [],
730
- srcDir: nuxt.options.srcDir,
731
- rootDir: nuxt.options.rootDir,
732
- buildDir: srcResolver.resolve(nuxt.options.buildDir),
733
- nuxtConfigPath: rootResolver.resolve("nuxt.config.ts"),
734
- schemaPath,
735
- serverApiPrefix: options.serverApiPrefix,
736
- logOnlyErrors: !!options.logOnlyErrors,
737
- runtimeTypesPath: toBuildRelative(
738
- moduleResolver.resolve("./runtime/types.ts")
739
- )
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: this.findServerOptions(),
774
+ clientOptions: this.findClientOptions(),
775
+ moduleBuildDir: nuxt.options.buildDir + "/nuxt-graphql-middleware",
776
+ moduleTypesDir: nuxt.options.buildDir + "/graphql-operations"
740
777
  };
741
- const collector = new Collector(
742
- schema,
743
- context,
744
- options.documents,
745
- options.codegenConfig
778
+ this.paths.runtimeTypes = this.toModuleBuildRelative(
779
+ this.resolvers.module.resolve("./runtime/types.ts")
746
780
  );
747
- await collector.init();
748
- const isDevToolsEnabled = nuxt.options.dev && options.devtools;
749
- let rpc;
750
- if (isDevToolsEnabled) {
751
- const clientPath = moduleResolver.resolve("./client");
752
- setupDevToolsUI(nuxt, clientPath);
753
- onDevToolsInitialized(() => {
754
- rpc = extendServerRpc(RPC_NAMESPACE, {
755
- // register server RPC functions
756
- getModuleOptions() {
757
- return options;
758
- },
759
- getDocuments() {
760
- return [...collector.rpcItems.values()];
761
- }
762
- });
763
- });
781
+ }
782
+ resolvers;
783
+ paths;
784
+ isDev;
785
+ options;
786
+ prompt = new ConsolePrompt();
787
+ nitroExternals = [];
788
+ /**
789
+ * Find the path to the graphqlMiddleware.serverOptions.ts file.
790
+ */
791
+ findServerOptions() {
792
+ const newPath = this.resolvers.server.resolve(
793
+ "graphqlMiddleware.serverOptions"
794
+ );
795
+ const serverPath = fileExists(newPath);
796
+ if (serverPath) {
797
+ return serverPath;
764
798
  }
765
- nuxt.options.appConfig.graphqlMiddleware = {
766
- clientCacheEnabled: !!options.clientCache?.enabled,
767
- clientCacheMaxSize: options.clientCache?.maxSize || 100
768
- };
769
- nuxt.options.runtimeConfig.graphqlMiddleware = {
770
- graphqlEndpoint: options.graphqlEndpoint || ""
771
- };
772
- if (options.includeComposables) {
773
- const nuxtComposables = [
774
- "useGraphqlQuery",
775
- "useGraphqlMutation",
776
- "useGraphqlState",
777
- "useAsyncGraphqlQuery"
778
- ];
779
- if (options.enableFileUploads) {
780
- nuxtComposables.push("useGraphqlUploadMutation");
799
+ const candidates = [
800
+ this.resolvers.root.resolve("graphqlMiddleware.serverOptions"),
801
+ this.resolvers.root.resolve("app/graphqlMiddleware.serverOptions"),
802
+ this.resolvers.src.resolve("graphqlMiddleware.serverOptions")
803
+ ];
804
+ for (let i = 0; i < candidates.length; i++) {
805
+ const path = candidates[i];
806
+ const filePath = fileExists(path);
807
+ if (filePath) {
808
+ throw new Error(
809
+ `The graphqlMiddleware.serverOptions file should be placed in Nuxt's <serverDir> ("${this.paths.serverDir}/graphqlMiddleware.serverOptions.ts").`
810
+ );
781
811
  }
782
- nuxtComposables.forEach((name2) => {
783
- addImports({
784
- from: moduleResolver.resolve("./runtime/composables/" + name2),
785
- name: name2
786
- });
812
+ }
813
+ logger.info("No graphqlMiddleware.serverOptions file found.");
814
+ return null;
815
+ }
816
+ findClientOptions() {
817
+ const clientOptionsPath = this.resolvers.app.resolve(
818
+ "graphqlMiddleware.clientOptions"
819
+ );
820
+ if (fileExists(clientOptionsPath)) {
821
+ return clientOptionsPath;
822
+ }
823
+ return null;
824
+ }
825
+ /**
826
+ * Transform the path relative to the module's build directory.
827
+ *
828
+ * @param path - The absolute path.
829
+ *
830
+ * @returns The path relative to the module's build directory.
831
+ */
832
+ toModuleBuildRelative(path) {
833
+ return relative(this.paths.moduleBuildDir, path);
834
+ }
835
+ /**
836
+ * Transform the path relative to the Nuxt build directory.
837
+ *
838
+ * @param path - The absolute path.
839
+ *
840
+ * @returns The path relative to the module's build directory.
841
+ */
842
+ toBuildRelative(path) {
843
+ return relative(this.nuxt.options.buildDir, path);
844
+ }
845
+ /**
846
+ * Get all file paths that match the import patterns.
847
+ */
848
+ async getImportPatternFiles() {
849
+ return resolveFiles(
850
+ this.nuxt.options.srcDir,
851
+ this.options.autoImportPatterns,
852
+ {
853
+ followSymbolicLinks: false
854
+ }
855
+ );
856
+ }
857
+ matchesImportPattern(filePath) {
858
+ return micromatch.isMatch(filePath, this.options.autoImportPatterns);
859
+ }
860
+ addAlias(name, path) {
861
+ this.nuxt.options.alias[name] = path;
862
+ const pathFromName = `./${name.substring(1)}`;
863
+ this.nuxt.options.nitro.typescript ||= {};
864
+ this.nuxt.options.nitro.typescript.tsConfig ||= {};
865
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions ||= {};
866
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths ||= {};
867
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths[name] = [
868
+ pathFromName
869
+ ];
870
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths[name + "/*"] = [pathFromName + "/*"];
871
+ this.inlineNitroExternals(name);
872
+ }
873
+ inlineNitroExternals(arg) {
874
+ const path = typeof arg === "string" ? arg : arg.dst;
875
+ this.nitroExternals.push(path);
876
+ this.transpile(path);
877
+ }
878
+ transpile(path) {
879
+ this.nuxt.options.build.transpile.push(path);
880
+ }
881
+ applyBuildConfig() {
882
+ this.nuxt.options.nitro.externals ||= {};
883
+ this.nuxt.options.nitro.externals.inline ||= [];
884
+ this.nuxt.options.nitro.externals.inline.push(...this.nitroExternals);
885
+ }
886
+ addTemplate(template) {
887
+ if (template.build) {
888
+ const content = template.build(this).trim();
889
+ addTemplate({
890
+ filename: template.options.path + ".js",
891
+ write: true,
892
+ getContents: () => content
893
+ });
894
+ }
895
+ if (template.buildTypes) {
896
+ const content = template.buildTypes(this).trim();
897
+ const filename = template.options.path + ".d.ts";
898
+ addTypeTemplate({
899
+ filename,
900
+ write: true,
901
+ getContents: () => content
787
902
  });
788
- const serverUtils = ["useGraphqlQuery", "useGraphqlMutation"].map(
789
- (name2) => {
790
- return {
791
- from: moduleResolver.resolve("./runtime/server/utils/" + name2),
792
- name: name2
793
- };
794
- }
795
- );
796
- addServerImports(serverUtils);
797
903
  }
798
- addTypeTemplate({
799
- filename: GraphqlMiddlewareTemplate.OperationTypes,
800
- write: true,
801
- getContents: () => collector.getTemplateTypes()
904
+ }
905
+ addPlugin(path) {
906
+ addPlugin(this.resolvers.module.resolve(path), {
907
+ append: false
802
908
  });
803
- addTypeTemplate({
804
- filename: GraphqlMiddlewareTemplate.Types,
805
- write: true,
806
- getContents: () => {
807
- return `
808
- declare module '#nuxt-graphql-middleware/sources' {
809
- export const operationSources: Record<string, string>
810
- }
811
- `;
812
- }
909
+ }
910
+ addServerHandler(name, path, method) {
911
+ addServerHandler({
912
+ handler: this.resolvers.module.resolve("./runtime/server/api/" + name),
913
+ route: this.options.serverApiPrefix + path,
914
+ method
813
915
  });
814
- addTemplate({
815
- filename: GraphqlMiddlewareTemplate.Enums,
816
- write: true,
817
- getContents: () => collector.getTemplateEnums()
916
+ }
917
+ addComposable(name) {
918
+ addImports({
919
+ from: this.resolvers.module.resolve("./runtime/composables/" + name),
920
+ name
818
921
  });
819
- addTemplate({
820
- filename: GraphqlMiddlewareTemplate.OperationSources,
821
- write: true,
822
- getContents: () => collector.getTemplateSources()
922
+ }
923
+ addServerUtil(name) {
924
+ addServerImports([
925
+ {
926
+ from: this.resolvers.module.resolve("./runtime/server/utils/" + name),
927
+ name
928
+ }
929
+ ]);
930
+ }
931
+ }
932
+
933
+ function defineGeneratorTemplate(options, build, buildTypes) {
934
+ return {
935
+ type: "generator",
936
+ options,
937
+ build,
938
+ buildTypes
939
+ };
940
+ }
941
+ function defineStaticTemplate(options, build, buildTypes) {
942
+ return {
943
+ type: "static",
944
+ options,
945
+ build,
946
+ buildTypes
947
+ };
948
+ }
949
+
950
+ const ClientOptions = defineStaticTemplate(
951
+ { path: "nuxt-graphql-middleware/client-options" },
952
+ (helper) => {
953
+ if (helper.paths.clientOptions) {
954
+ const pathRelative = helper.toModuleBuildRelative(
955
+ helper.paths.clientOptions
956
+ );
957
+ return `import clientOptions from '${pathRelative}'
958
+ export { clientOptions }
959
+ `;
960
+ }
961
+ return `export const clientOptions = {}`;
962
+ },
963
+ (helper) => {
964
+ if (helper.paths.clientOptions) {
965
+ const pathRelative = helper.toModuleBuildRelative(
966
+ helper.paths.clientOptions
967
+ );
968
+ return `import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
969
+ import { clientOptions } from '${pathRelative}'
970
+
971
+ export type GraphqlClientContext = typeof clientOptions extends GraphqlClientOptions<infer R> ? R : {}
972
+
973
+ export { clientOptions }`;
974
+ }
975
+ return `
976
+ import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
977
+ export const clientOptions: GraphqlClientOptions
978
+
979
+ export type GraphqlClientContext = {}
980
+ `;
981
+ }
982
+ );
983
+
984
+ const Documents = defineGeneratorTemplate(
985
+ { path: "nuxt-graphql-middleware/documents", virtual: true },
986
+ (output, helper) => {
987
+ return output.getOperationsFile({
988
+ exportName: "documents",
989
+ minify: !helper.isDev
990
+ }).getSource();
991
+ },
992
+ () => {
993
+ return `
994
+ import type { Query, Mutation } from './operation-types'
995
+
996
+ declare module '#nuxt-graphql-middleware/documents' {
997
+ export type Documents = {
998
+ query: Record<keyof Query, string>
999
+ mutation: Record<keyof Mutation, string>
1000
+ }
1001
+ export const documents: Documents
1002
+ }`;
1003
+ }
1004
+ );
1005
+
1006
+ const GraphqlConfig = defineStaticTemplate(
1007
+ { path: "nuxt-graphql-middleware/graphql.config" },
1008
+ (helper) => {
1009
+ const patterns = helper.options.autoImportPatterns || [];
1010
+ const configPath = helper.resolvers.root.resolve(
1011
+ (helper.options.graphqlConfigFilePath || "").replace(
1012
+ "/graphql.config.ts",
1013
+ ""
1014
+ )
1015
+ );
1016
+ const schemaPath = "./" + relative(configPath, helper.paths.schema);
1017
+ const documents = patterns.filter((v) => !v.includes("!")).map((pattern) => {
1018
+ return "./" + relative(configPath, helper.resolvers.root.resolve(pattern));
823
1019
  });
824
- addTemplate({
825
- filename: GraphqlMiddlewareTemplate.Helpers,
826
- write: true,
827
- getContents: () => `export const serverApiPrefix = '${context.serverApiPrefix}'
1020
+ return `const schema = ${JSON.stringify(schemaPath)}
1021
+
1022
+ const documents = ${JSON.stringify(documents, null, 2)};
1023
+
1024
+ const config = {
1025
+ schema,
1026
+ documents,
1027
+ }
1028
+
1029
+ export default config
1030
+ `;
1031
+ },
1032
+ () => {
1033
+ return `
1034
+ import type { IGraphQLProject } from 'graphql-config'
1035
+
1036
+ type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
1037
+
1038
+ const config: WithRequired<IGraphQLProject, 'schema' | 'documents'>;
1039
+
1040
+ export default config;
1041
+ `;
1042
+ }
1043
+ );
1044
+
1045
+ const Helpers = defineStaticTemplate(
1046
+ { path: "nuxt-graphql-middleware/helpers" },
1047
+ (helper) => {
1048
+ return `export const serverApiPrefix = '${helper.options.serverApiPrefix}'
828
1049
  export function getEndpoint(operation, operationName) {
829
- return '${context.serverApiPrefix}' + '/' + operation + '/' + operationName
1050
+ return serverApiPrefix + '/' + operation + '/' + operationName
830
1051
  }
831
- `
832
- });
833
- addTypeTemplate({
834
- filename: GraphqlMiddlewareTemplate.HelpersTypes,
835
- write: true,
836
- getContents: () => `export const serverApiPrefix: string;
837
- export function getEndpoint(operation: string, operationName: string): string
838
- `
839
- });
840
- addTypeTemplate({
841
- filename: GraphqlMiddlewareTemplate.NitroTypes,
842
- write: true,
843
- getContents: () => collector.getTemplateNitroTypes()
844
- });
845
- addTypeTemplate({
846
- filename: GraphqlMiddlewareTemplate.OperationTypesAll,
847
- write: true,
848
- getContents: () => collector.getTemplateOperationTypes()
849
- });
850
- const templateDocuments = addTemplate({
851
- filename: GraphqlMiddlewareTemplate.Documents,
852
- write: true,
853
- getContents: () => collector.getTemplateOperations()
854
- });
855
- inlineNitroExternals(templateDocuments.dst);
856
- addTypeTemplate({
857
- filename: GraphqlMiddlewareTemplate.ResponseTypes,
858
- write: true,
859
- getContents: () => collector.getTemplateResponseTypes()
860
- });
861
- addTypeTemplate({
862
- write: true,
863
- filename: "nuxt-graphql-middleware/documents.d.ts",
864
- getContents: () => generateDocumentTypesTemplate()
865
- });
866
- const findServerOptions = () => {
867
- const newPath = serverResolver.resolve("graphqlMiddleware.serverOptions");
868
- const serverPath = fileExists(newPath);
869
- if (serverPath) {
870
- return serverPath;
871
- }
872
- const candidates = [
873
- rootResolver.resolve("graphqlMiddleware.serverOptions"),
874
- rootResolver.resolve("app/graphqlMiddleware.serverOptions"),
875
- srcResolver.resolve("graphqlMiddleware.serverOptions")
876
- ];
877
- for (let i = 0; i < candidates.length; i++) {
878
- const path = candidates[i];
879
- const filePath = fileExists(path);
880
- if (filePath) {
881
- logger.warn(
882
- `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.`
883
- );
884
- return filePath;
885
- }
886
- }
887
- logger.info("No graphqlMiddleware.serverOptions file found.");
888
- };
889
- const resolvedServerOptionsPath = findServerOptions();
890
- const moduleTypesPath = toBuildRelative(moduleResolver.resolve("./types"));
891
- const resolvedPathRelative = resolvedServerOptionsPath ? toBuildRelative(resolvedServerOptionsPath) : null;
892
- const template = addTemplate({
893
- filename: "nuxt-graphql-middleware/server-options.mjs",
894
- write: true,
895
- getContents: () => {
896
- const serverOptionsLine = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions = {}`;
897
- return `
1052
+ `;
1053
+ },
1054
+ () => {
1055
+ return `export const serverApiPrefix: string;
1056
+ export function getEndpoint(operation: string, operationName: string): string`;
1057
+ }
1058
+ );
1059
+
1060
+ const NitroTypes = defineGeneratorTemplate(
1061
+ { path: "nuxt-graphql-middleware/nitro" },
1062
+ null,
1063
+ (output, helper) => {
1064
+ const operations = output.getCollectedOperations();
1065
+ const serverApiPrefix = helper.options.serverApiPrefix;
1066
+ const endpoints = [];
1067
+ const imports = [];
1068
+ for (const operation of operations) {
1069
+ imports.push(operation.typeName);
1070
+ const method = operation.operationType === OperationTypeNode.QUERY ? "get" : "post";
1071
+ endpoints.push(
1072
+ ` '${serverApiPrefix}/${operation.operationType}/${operation.graphqlName}': {
1073
+ '${method}': GraphqlResponse<${operation.typeName}>
1074
+ }`
1075
+ );
1076
+ }
1077
+ return `import type { GraphqlResponse } from './response'
1078
+ import type {
1079
+ ${imports.sort().join(",\n ")}
1080
+ } from './../graphql-operations'
1081
+
1082
+ declare module 'nitropack/types' {
1083
+ interface InternalApi {
1084
+ ${endpoints.sort().join("\n")}
1085
+ }
1086
+ }`;
1087
+ }
1088
+ );
1089
+
1090
+ const OperationTypesAll = defineGeneratorTemplate(
1091
+ { path: "nuxt-graphql-middleware/operation-types" },
1092
+ () => `export {}`,
1093
+ (output) => {
1094
+ return output.getOperationTypesFile({
1095
+ importFrom: "./../graphql-operations"
1096
+ }).getSource();
1097
+ }
1098
+ );
1099
+
1100
+ const Operations = defineGeneratorTemplate(
1101
+ { path: "graphql-operations/index" },
1102
+ (output) => {
1103
+ const typesFile = output.getOperations("js");
1104
+ return typesFile.getSource();
1105
+ },
1106
+ (output) => {
1107
+ const typesFile = output.getOperations("d.ts");
1108
+ return typesFile.getSource();
1109
+ }
1110
+ );
1111
+
1112
+ const Response = defineGeneratorTemplate(
1113
+ { path: "nuxt-graphql-middleware/response" },
1114
+ null,
1115
+ (output, helper) => {
1116
+ const operations = output.getCollectedOperations();
1117
+ const allTypes = operations.map((v) => v.typeName).sort();
1118
+ return `import type {
1119
+ ${allTypes.join(",\n ")}
1120
+ } from './../graphql-operations'
1121
+ import type { GraphqlResponseAdditions } from './server-options'
1122
+ import type { GraphqlServerResponse } from '${helper.paths.runtimeTypes}'
1123
+
1124
+ declare module '#nuxt-graphql-middleware/response' {
1125
+ export type GraphqlMiddlewareResponseUnion =
1126
+ | ${allTypes.join("\n | ") || "never"}
1127
+
1128
+ export type GraphqlResponse<T> = GraphqlServerResponse<T> & GraphqlResponseAdditions
1129
+ export type GraphqlResponseTyped = GraphqlResponse<GraphqlMiddlewareResponseUnion>
1130
+ }`;
1131
+ }
1132
+ );
1133
+
1134
+ const ServerOptions = defineStaticTemplate(
1135
+ { path: "nuxt-graphql-middleware/server-options" },
1136
+ (helper) => {
1137
+ const resolvedPathRelative = helper.paths.serverOptions ? helper.toModuleBuildRelative(helper.paths.serverOptions) : null;
1138
+ const serverOptionsLine = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions = {}`;
1139
+ return `
898
1140
  ${serverOptionsLine}
899
1141
  export { serverOptions }
900
1142
  `;
901
- }
902
- });
903
- inlineNitroExternals(template.dst);
904
- addTemplate({
905
- filename: "nuxt-graphql-middleware/server-options.d.ts",
906
- write: true,
907
- getContents: () => {
908
- const serverOptionsLineTypes = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions: GraphqlMiddlewareServerOptions = {}`;
909
- return `
1143
+ },
1144
+ (helper) => {
1145
+ const resolvedPathRelative = helper.paths.serverOptions ? helper.toModuleBuildRelative(helper.paths.serverOptions) : null;
1146
+ const serverOptionsLineTypes = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions: GraphqlMiddlewareServerOptions = {}`;
1147
+ const moduleTypesPath = helper.toModuleBuildRelative(
1148
+ helper.resolvers.module.resolve("./types")
1149
+ );
1150
+ return `
910
1151
  import type { GraphqlMiddlewareServerOptions } from '${moduleTypesPath}'
911
1152
  ${serverOptionsLineTypes}
912
1153
 
@@ -914,112 +1155,278 @@ export type GraphqlResponseAdditions =
914
1155
  typeof serverOptions extends GraphqlMiddlewareServerOptions<infer R, any, any> ? R : {}
915
1156
 
916
1157
  export { serverOptions }`;
917
- }
918
- });
919
- const getClientOptionsImport = () => {
920
- const clientOptionsPath = appResolver.resolve(
921
- "graphqlMiddleware.clientOptions"
922
- );
923
- if (fileExists(clientOptionsPath)) {
924
- const pathRelative = toBuildRelative(clientOptionsPath);
925
- return `import clientOptions from '${pathRelative}'`;
926
- }
927
- };
928
- const clientOptionsImport = getClientOptionsImport();
929
- addTemplate({
930
- filename: "nuxt-graphql-middleware/client-options.mjs",
931
- write: true,
932
- getContents: () => {
933
- if (clientOptionsImport) {
934
- return `${clientOptionsImport}
935
- export { clientOptions }`;
936
- }
937
- return `export const clientOptions = {}`;
938
- }
939
- });
940
- addTemplate({
941
- filename: "nuxt-graphql-middleware/client-options.d.ts",
942
- write: true,
943
- getContents: () => {
944
- if (clientOptionsImport) {
945
- return `import type { GraphqlClientOptions } from '${context.runtimeTypesPath}'
946
- ${clientOptionsImport}
1158
+ }
1159
+ );
947
1160
 
948
- export type GraphqlClientContext = typeof clientOptions extends GraphqlClientOptions<infer R> ? R : {}
1161
+ const Sources = defineGeneratorTemplate(
1162
+ { path: "nuxt-graphql-middleware/sources" },
1163
+ (output, helper) => {
1164
+ const operations = output.getCollectedOperations();
1165
+ const srcDir = helper.paths.root;
1166
+ const lines = [];
1167
+ for (const operation of operations) {
1168
+ const filePath = relative(srcDir, operation.filePath);
1169
+ lines.push(
1170
+ `${operation.operationType}_${operation.graphqlName}: '${filePath}',`
1171
+ );
1172
+ }
1173
+ return `
1174
+ export const operationSources = {
1175
+ ${lines.join("\n ")}
1176
+ }
1177
+ `;
1178
+ },
1179
+ () => {
1180
+ return `export const operationSources: Record<string, string>`;
1181
+ }
1182
+ );
949
1183
 
950
- export { clientOptions }`;
951
- }
952
- return `import type { GraphqlClientOptions } from '${context.runtimeTypesPath}'
953
- export const clientOptions: GraphqlClientOptions
1184
+ const TEMPLATES = [
1185
+ ClientOptions,
1186
+ Documents,
1187
+ GraphqlConfig,
1188
+ Helpers,
1189
+ NitroTypes,
1190
+ OperationTypesAll,
1191
+ Operations,
1192
+ Response,
1193
+ ServerOptions,
1194
+ Sources
1195
+ ];
954
1196
 
955
- export type GraphqlClientContext = {}
956
- `;
957
- }
958
- });
959
- addServerHandler({
960
- handler: moduleResolver.resolve("./runtime/serverHandler/index"),
961
- route: options.serverApiPrefix + "/:operation/:name"
1197
+ const DEVTOOLS_UI_ROUTE = "/__nuxt-graphql-middleware";
1198
+ const DEVTOOLS_UI_LOCAL_PORT = 3300;
1199
+ function setupDevToolsUI(nuxt, clientPath) {
1200
+ const isProductionBuild = existsSync$1(clientPath);
1201
+ if (isProductionBuild) {
1202
+ nuxt.hook("vite:serverCreated", async (server) => {
1203
+ const sirv = await import('sirv').then((r) => r.default || r);
1204
+ server.middlewares.use(
1205
+ DEVTOOLS_UI_ROUTE,
1206
+ sirv(clientPath, { dev: true, single: true })
1207
+ );
962
1208
  });
963
- if (options.enableFileUploads) {
964
- addServerHandler({
965
- handler: moduleResolver.resolve("./runtime/serverHandler/upload"),
966
- route: options.serverApiPrefix + "/upload/:name"
967
- });
968
- }
969
- addPlugin(moduleResolver.resolve("./runtime/plugins/provideState"), {
970
- append: false
1209
+ } else {
1210
+ nuxt.hook("vite:extendConfig", (config) => {
1211
+ config.server = config.server || {};
1212
+ config.server.proxy = config.server.proxy || {};
1213
+ config.server.proxy[DEVTOOLS_UI_ROUTE] = {
1214
+ target: "http://localhost:" + DEVTOOLS_UI_LOCAL_PORT + DEVTOOLS_UI_ROUTE,
1215
+ changeOrigin: true,
1216
+ followRedirects: true,
1217
+ rewrite: (path) => path.replace(DEVTOOLS_UI_ROUTE, "")
1218
+ };
971
1219
  });
972
- if (context.isDev && options.errorOverlay) {
973
- addPlugin(moduleResolver.resolve("./runtime/plugins/devMode"), {
974
- append: false
975
- });
976
- }
977
- nuxt.hook("nitro:config", (nitroConfig) => {
978
- nitroConfig.externals = defu(
979
- typeof nitroConfig.externals === "object" ? nitroConfig.externals : {},
980
- {
981
- inline: [moduleResolver.resolve("./runtime")]
982
- }
983
- );
1220
+ }
1221
+ nuxt.hook("devtools:customTabs", (tabs) => {
1222
+ tabs.push({
1223
+ // unique identifier
1224
+ name: "nuxt-graphql-middleware",
1225
+ // title to display in the tab
1226
+ title: "GraphQL Middleware",
1227
+ // any icon from Iconify, or a URL to an image
1228
+ icon: "akar-icons:graphql-fill",
1229
+ // iframe view
1230
+ view: {
1231
+ type: "iframe",
1232
+ src: DEVTOOLS_UI_ROUTE
1233
+ }
984
1234
  });
985
- if (nuxt.options.dev || nuxt.options._prepare) {
986
- let sendError = function(error) {
987
- wsPromise.then((ws) => {
988
- ws.send({
989
- type: "error",
990
- err: {
991
- message: error.message,
992
- stack: ""
1235
+ });
1236
+ }
1237
+
1238
+ const RPC_NAMESPACE = "nuxt-graphql-middleware";
1239
+ class DevModeHandler {
1240
+ constructor(nuxt, schemaProvider, collector, helper) {
1241
+ this.nuxt = nuxt;
1242
+ this.schemaProvider = schemaProvider;
1243
+ this.collector = collector;
1244
+ this.helper = helper;
1245
+ }
1246
+ devToolsRpc = null;
1247
+ nitro = null;
1248
+ viteWebSocket = null;
1249
+ operationsToReload = /* @__PURE__ */ new Set();
1250
+ init() {
1251
+ this.nuxt.hooks.hookOnce("ready", this.onReady.bind(this));
1252
+ this.nuxt.hooks.hookOnce(
1253
+ "vite:serverCreated",
1254
+ this.onViteServerCreated.bind(this)
1255
+ );
1256
+ this.nuxt.hook("builder:watch", this.onBuilderWatch.bind(this));
1257
+ if (this.helper.options.devtools) {
1258
+ const clientPath = this.helper.resolvers.module.resolve("./client");
1259
+ setupDevToolsUI(this.nuxt, clientPath);
1260
+ onDevToolsInitialized(() => {
1261
+ this.devToolsRpc = extendServerRpc(
1262
+ RPC_NAMESPACE,
1263
+ {
1264
+ // register server RPC functions
1265
+ getModuleOptions: () => {
1266
+ return this.helper.options;
1267
+ },
1268
+ getDocuments: () => {
1269
+ return [...this.collector.rpcItems.values()];
993
1270
  }
994
- });
995
- });
996
- };
997
- addServerHandler({
998
- handler: moduleResolver.resolve("./runtime/serverHandler/debug"),
999
- route: options.serverApiPrefix + "/debug"
1271
+ }
1272
+ );
1000
1273
  });
1001
- const wsPromise = useViteWebSocket(nuxt);
1002
- nuxt.hook("builder:watch", async (event, pathAbsolute) => {
1003
- if (pathAbsolute === schemaPath) {
1004
- return;
1005
- }
1006
- if (!pathAbsolute.match(/\.(gql|graphql)$/)) {
1274
+ }
1275
+ }
1276
+ onReady() {
1277
+ this.nitro = useNitro();
1278
+ this.nitro.hooks.hook("compiled", this.onNitroCompiled.bind(this));
1279
+ }
1280
+ async onBuilderWatch(event, pathAbsolute) {
1281
+ if (pathAbsolute === this.helper.paths.schema) {
1282
+ return;
1283
+ }
1284
+ if (!pathAbsolute.match(/\.(gql|graphql)$/)) {
1285
+ return;
1286
+ }
1287
+ this.helper.prompt.abort();
1288
+ const { hasChanged, affectedOperations, error } = await this.collector.handleWatchEvent(event, pathAbsolute);
1289
+ if (error) {
1290
+ this.sendError(error);
1291
+ await this.helper.prompt.confirm("Do you want to download and update the GraphQL schema?").then(async (shouldReload) => {
1292
+ if (shouldReload !== "yes") {
1007
1293
  return;
1008
1294
  }
1009
- const { hasChanged, error } = await collector.handleWatchEvent(
1010
- event,
1011
- pathAbsolute
1012
- );
1013
- if (error) {
1014
- sendError(error);
1015
- }
1016
- if (hasChanged) {
1017
- if (rpc) {
1018
- rpc.broadcast.documentsUpdated([...collector.rpcItems.values()]);
1019
- }
1295
+ try {
1296
+ await this.schemaProvider.loadSchema({ forceDownload: true });
1297
+ await this.collector.updateSchema(this.schemaProvider.getSchema());
1298
+ } catch (e) {
1299
+ logger.error(e);
1020
1300
  }
1021
1301
  });
1302
+ return;
1303
+ }
1304
+ if (!hasChanged) {
1305
+ return;
1306
+ }
1307
+ if (this.nitro) {
1308
+ await this.nitro.hooks.callHook("rollup:reload");
1309
+ }
1310
+ if (affectedOperations.length) {
1311
+ affectedOperations.forEach(
1312
+ (operation) => this.operationsToReload.add(operation)
1313
+ );
1314
+ }
1315
+ if (this.devToolsRpc) {
1316
+ try {
1317
+ this.devToolsRpc.broadcast.documentsUpdated([
1318
+ ...this.collector.rpcItems.values()
1319
+ ]);
1320
+ } catch {
1321
+ logger.info(
1322
+ "Failed to update GraphQL documents in dev tools. The documents might be stale."
1323
+ );
1324
+ }
1325
+ }
1326
+ }
1327
+ onViteServerCreated(server) {
1328
+ this.viteWebSocket = server.ws;
1329
+ }
1330
+ sendError(error) {
1331
+ if (!this.viteWebSocket) {
1332
+ return;
1333
+ }
1334
+ this.viteWebSocket.send({
1335
+ type: "error",
1336
+ err: {
1337
+ message: error.message,
1338
+ stack: ""
1339
+ }
1340
+ });
1341
+ }
1342
+ onNitroCompiled() {
1343
+ if (!this.operationsToReload.size) {
1344
+ return;
1345
+ }
1346
+ const operations = [...this.operationsToReload.values()];
1347
+ this.operationsToReload.clear();
1348
+ if (!this.viteWebSocket) {
1349
+ return;
1350
+ }
1351
+ this.viteWebSocket.send({
1352
+ type: "custom",
1353
+ event: "nuxt-graphql-middleware:reload",
1354
+ data: { operations }
1355
+ });
1356
+ }
1357
+ }
1358
+
1359
+ const module = defineNuxtModule({
1360
+ meta: {
1361
+ name,
1362
+ configKey: "graphqlMiddleware",
1363
+ version,
1364
+ compatibility: {
1365
+ nuxt: ">=3.15.0"
1022
1366
  }
1367
+ },
1368
+ defaults: defaultOptions,
1369
+ async setup(passedOptions, nuxt) {
1370
+ const helper = new ModuleHelper(nuxt, import.meta.url, passedOptions);
1371
+ const schemaProvider = new SchemaProvider(helper);
1372
+ await schemaProvider.init();
1373
+ const collector = new Collector(schemaProvider.getSchema(), helper);
1374
+ nuxt.options.appConfig.graphqlMiddleware = {
1375
+ clientCacheEnabled: !!helper.options.clientCache?.enabled,
1376
+ clientCacheMaxSize: helper.options.clientCache?.maxSize ?? 100
1377
+ };
1378
+ nuxt.options.runtimeConfig.graphqlMiddleware = {
1379
+ graphqlEndpoint: helper.options.graphqlEndpoint || ""
1380
+ };
1381
+ helper.transpile(fileURLToPath(new URL("./runtime", import.meta.url)));
1382
+ helper.inlineNitroExternals(helper.resolvers.module.resolve("./runtime"));
1383
+ helper.inlineNitroExternals(helper.paths.moduleBuildDir);
1384
+ helper.inlineNitroExternals(helper.paths.moduleTypesDir);
1385
+ helper.addAlias("#nuxt-graphql-middleware", helper.paths.moduleBuildDir);
1386
+ helper.addAlias("#graphql-operations", helper.paths.moduleTypesDir);
1387
+ helper.addPlugin("./runtime/plugins/provideState");
1388
+ if (helper.isDev && helper.options.errorOverlay) {
1389
+ helper.addPlugin("./runtime/plugins/devMode");
1390
+ }
1391
+ helper.addServerHandler("query", "/query/:name", "get");
1392
+ helper.addServerHandler("mutation", "/mutation/:name", "post");
1393
+ if (helper.options.enableFileUploads) {
1394
+ helper.addServerHandler("upload", "/upload/:name", "post");
1395
+ }
1396
+ if (helper.isDev) {
1397
+ helper.addServerHandler("debug", "/debug", "get");
1398
+ }
1399
+ if (helper.options.includeComposables) {
1400
+ helper.addComposable("useGraphqlQuery");
1401
+ helper.addComposable("useGraphqlMutation");
1402
+ helper.addComposable("useGraphqlState");
1403
+ helper.addComposable("useAsyncGraphqlQuery");
1404
+ if (helper.options.enableFileUploads) {
1405
+ helper.addComposable("useGraphqlUploadMutation");
1406
+ }
1407
+ helper.addServerUtil("useGraphqlQuery");
1408
+ helper.addServerUtil("useGraphqlMutation");
1409
+ helper.addServerUtil("doGraphqlRequest");
1410
+ }
1411
+ TEMPLATES.forEach((template) => {
1412
+ if (template.type === "static") {
1413
+ helper.addTemplate(template);
1414
+ } else {
1415
+ collector.addTemplate(template);
1416
+ }
1417
+ });
1418
+ helper.applyBuildConfig();
1419
+ await collector.init();
1420
+ if (!helper.isDev) {
1421
+ return;
1422
+ }
1423
+ const devModeHandler = new DevModeHandler(
1424
+ nuxt,
1425
+ schemaProvider,
1426
+ collector,
1427
+ helper
1428
+ );
1429
+ devModeHandler.init();
1023
1430
  }
1024
1431
  });
1025
1432