nuxt-graphql-middleware 5.0.0-alpha.9 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +101 -19
  2. package/dist/client/200.html +8 -8
  3. package/dist/client/404.html +8 -8
  4. package/dist/client/_nuxt/{CPyoLiCY.js → BM34SYth.js} +1 -1
  5. package/dist/client/_nuxt/CROlboVl.js +1 -0
  6. package/dist/client/_nuxt/D5hBL5aZ.js +25 -0
  7. package/dist/client/_nuxt/{BLvMh1Ga.js → FTbv7CO6.js} +1 -1
  8. package/dist/client/_nuxt/builds/latest.json +1 -1
  9. package/dist/client/_nuxt/builds/meta/83f9fcd5-bd28-4608-b499-05e08fe0f7d0.json +1 -0
  10. package/dist/client/_nuxt/error-404.ehK72JOs.css +1 -0
  11. package/dist/client/_nuxt/error-500._g0akJim.css +1 -0
  12. package/dist/client/_nuxt/{C9pb_2rp.js → lIgCBhS_.js} +2 -2
  13. package/dist/client/index.html +8 -8
  14. package/dist/client-options.d.mts +6 -0
  15. package/dist/client-options.mjs +5 -0
  16. package/dist/module.d.mts +63 -181
  17. package/dist/module.json +3 -3
  18. package/dist/module.mjs +338 -90
  19. package/dist/runtime/components/CodeFrame.vue +19 -28
  20. package/dist/runtime/components/CodeFrame.vue.d.ts +7 -0
  21. package/dist/runtime/components/DevModeOverlay.vue +25 -33
  22. package/dist/runtime/components/DevModeOverlay.vue.d.ts +3 -0
  23. package/dist/runtime/components/ErrorExtensions.vue +9 -11
  24. package/dist/runtime/components/ErrorExtensions.vue.d.ts +5 -0
  25. package/dist/runtime/components/ErrorGroup.vue +28 -39
  26. package/dist/runtime/components/ErrorGroup.vue.d.ts +9 -0
  27. package/dist/runtime/server/api/mutation.js +2 -1
  28. package/dist/runtime/server/api/query.js +2 -1
  29. package/dist/runtime/server/utils/doGraphqlRequest.js +5 -4
  30. package/dist/runtime/types.d.ts +2 -2
  31. package/dist/server-options.d.mts +8 -0
  32. package/dist/server-options.mjs +5 -0
  33. package/dist/shared/nuxt-graphql-middleware.cXfDI4U3.d.mts +517 -0
  34. package/dist/types.d.mts +1 -7
  35. package/dist/utils.d.mts +15 -0
  36. package/dist/utils.mjs +18 -0
  37. package/package.json +34 -30
  38. package/dist/client/_nuxt/CBwfSTyQ.js +0 -1
  39. package/dist/client/_nuxt/VpkRx2_e.js +0 -25
  40. package/dist/client/_nuxt/builds/meta/826a43da-d42c-4fbf-8dfd-2572141eaf8f.json +0 -1
  41. package/dist/client/_nuxt/error-404.BJkSn6RI.css +0 -1
  42. package/dist/client/_nuxt/error-500.TOCKLquH.css +0 -1
  43. package/dist/module.cjs +0 -5
  44. package/dist/module.d.ts +0 -210
  45. package/dist/runtime/clientOptions/index.d.ts +0 -2
  46. package/dist/runtime/clientOptions/index.js +0 -3
  47. package/dist/runtime/serverOptions/defineGraphqlServerOptions.d.ts +0 -4
  48. package/dist/runtime/serverOptions/defineGraphqlServerOptions.js +0 -3
  49. package/dist/runtime/serverOptions/index.d.ts +0 -2
  50. package/dist/runtime/serverOptions/index.js +0 -2
  51. package/dist/types.d.ts +0 -7
package/dist/module.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { fileURLToPath } from 'url';
2
- import { useLogger, addTemplate, addServerTemplate, addTypeTemplate, resolveAlias, createResolver, resolveFiles, addPlugin, addServerHandler, addImports, addServerImports, useNitro, defineNuxtModule } from '@nuxt/kit';
2
+ import { useLogger, addTemplate, addServerTemplate, addTypeTemplate, createResolver, resolveAlias, resolveFiles, addPlugin, addServerHandler, addImports, addServerImports, useNitro, defineNuxtModule } from '@nuxt/kit';
3
3
  import { existsSync, promises } from 'node:fs';
4
- import { relative } from 'pathe';
5
4
  import { basename } from 'node:path';
5
+ import { relative, join } from 'pathe';
6
6
  import { printSourceLocation, parse, Source, OperationTypeNode } from 'graphql';
7
7
  import { Generator, FieldNotFoundError, TypeNotFoundError, FragmentNotFoundError } from 'graphql-typescript-deluxe';
8
8
  import color from 'picocolors';
@@ -12,16 +12,16 @@ import { generate } from '@graphql-codegen/cli';
12
12
  import * as PluginSchemaAst from '@graphql-codegen/schema-ast';
13
13
  import { loadSchema } from '@graphql-tools/load';
14
14
  import { defu } from 'defu';
15
- import * as micromatch from 'micromatch';
15
+ import micromatch from 'micromatch';
16
16
  import { ConfirmPrompt } from '@clack/core';
17
17
  import isUnicodeSupported from 'is-unicode-supported';
18
18
  import { existsSync as existsSync$1 } from 'fs';
19
19
  import { onDevToolsInitialized, extendServerRpc } from '@nuxt/devtools-kit';
20
20
 
21
21
  const name = "nuxt-graphql-middleware";
22
- const version = "5.0.0-alpha.9";
22
+ const version = "5.0.0";
23
23
 
24
- const logger = useLogger(name);
24
+ const logger = useLogger("nuxt-graphql-middleware");
25
25
  const defaultOptions = {
26
26
  downloadSchema: true,
27
27
  schemaPath: "~~/schema.graphql",
@@ -119,6 +119,9 @@ class CollectedFile {
119
119
  }
120
120
  static async fromFilePath(filePath) {
121
121
  const content = (await promises.readFile(filePath)).toString();
122
+ if (!content) {
123
+ return null;
124
+ }
122
125
  return new CollectedFile(filePath, content, true);
123
126
  }
124
127
  /**
@@ -148,7 +151,10 @@ class Collector {
148
151
  }
149
152
  if (!mappedOptions.output.buildTypeDocFilePath) {
150
153
  mappedOptions.output.buildTypeDocFilePath = (filePath) => {
151
- return this.filePathToBuildRelative(filePath);
154
+ if (filePath.startsWith("/")) {
155
+ return this.filePathToBuildRelative(filePath);
156
+ }
157
+ return filePath;
152
158
  };
153
159
  }
154
160
  this.generator = new Generator(schema, mappedOptions);
@@ -157,6 +163,14 @@ class Collector {
157
163
  * All collected files.
158
164
  */
159
165
  files = /* @__PURE__ */ new Map();
166
+ /**
167
+ * All documents provided by hooks.
168
+ */
169
+ hookDocuments = /* @__PURE__ */ new Map();
170
+ /**
171
+ * All file paths provided by hooks.
172
+ */
173
+ hookFiles = /* @__PURE__ */ new Set();
160
174
  /**
161
175
  * The code generator.
162
176
  */
@@ -177,6 +191,7 @@ class Collector {
177
191
  * The generated template contents.
178
192
  */
179
193
  templateResult = /* @__PURE__ */ new Map();
194
+ isInitialised = false;
180
195
  async reset() {
181
196
  this.files.clear();
182
197
  this.generator.reset();
@@ -190,10 +205,13 @@ class Collector {
190
205
  await this.initDocuments();
191
206
  }
192
207
  filePathToBuildRelative(filePath) {
193
- return "./" + this.helper.toBuildRelative(filePath);
208
+ return "./" + this.helper.toModuleBuildRelative(filePath);
194
209
  }
195
210
  filePathToSourceRelative(filePath) {
196
- return "./" + relative(process.cwd(), filePath);
211
+ if (filePath.startsWith("/")) {
212
+ return "./" + relative(process.cwd(), filePath);
213
+ }
214
+ return filePath;
197
215
  }
198
216
  operationToLogEntry(operation, errors) {
199
217
  return {
@@ -203,8 +221,8 @@ class Collector {
203
221
  errors
204
222
  };
205
223
  }
206
- getTemplate(template) {
207
- const content = this.templateResult.get(template);
224
+ getTemplate(template, type) {
225
+ const content = this.templateResult.get(template + "-" + type);
208
226
  if (content === void 0) {
209
227
  throw new Error(`Missing template content: ${template}`);
210
228
  }
@@ -213,23 +231,28 @@ class Collector {
213
231
  /**
214
232
  * Executes code gen and performs validation for operations.
215
233
  */
216
- buildState() {
234
+ async buildState() {
217
235
  const output = this.generator.build();
218
236
  const operations = output.getCollectedOperations();
219
237
  const generatedCode = output.getGeneratedCode();
220
238
  this.templates.forEach((template) => {
239
+ const path = template.options.path;
221
240
  if (template.build) {
222
- const filename = template.options.path + ".js";
223
241
  this.templateResult.set(
224
- filename,
225
- template.build(output, this.helper).trim()
242
+ path + "-default",
243
+ this.helper.processTemplate(
244
+ template.options.path,
245
+ template.build(output, this.helper, this)
246
+ )
226
247
  );
227
248
  }
228
249
  if (template.buildTypes) {
229
- const filename = template.options.path + ".d.ts";
230
250
  this.templateResult.set(
231
- filename,
232
- template.buildTypes(output, this.helper).trim()
251
+ template.options.path + "-types",
252
+ this.helper.processTemplate(
253
+ template.options.path,
254
+ template.buildTypes(output, this.helper, this)
255
+ )
233
256
  );
234
257
  }
235
258
  });
@@ -266,10 +289,15 @@ class Collector {
266
289
  logEntries.push(this.operationToLogEntry(operation, errors));
267
290
  }
268
291
  }
269
- logAllEntries(logEntries);
292
+ logAllEntries(
293
+ logEntries.sort((a, b) => {
294
+ return a.type.localeCompare(b.type) || a.name.localeCompare(b.name);
295
+ })
296
+ );
270
297
  if (hasErrors) {
271
298
  throw new Error("GraphQL errors");
272
299
  }
300
+ await this.helper.nuxt.callHook("nuxt-graphql-middleware:build", { output });
273
301
  if (this.helper.isDev) {
274
302
  for (const code of generatedCode) {
275
303
  const id = `${code.identifier}_${code.graphqlName}`;
@@ -332,12 +360,32 @@ class Collector {
332
360
  );
333
361
  if (shouldRevalidate === "yes") {
334
362
  await this.reset();
335
- return this.init();
363
+ await this.init();
364
+ this.isInitialised = true;
365
+ return;
336
366
  }
337
367
  }
338
368
  throw new Error("Graphql document validation failed.");
339
369
  }
340
370
  }
371
+ addHookDocument(identifier, source) {
372
+ this.hookDocuments.set("hook:" + identifier, source);
373
+ }
374
+ async addOrUpdateHookDocument(identifier, source) {
375
+ const fullIdentifier = "hook:" + identifier;
376
+ const exists = this.hookDocuments.has(fullIdentifier);
377
+ this.hookDocuments.set(fullIdentifier, source);
378
+ if (exists && this.isInitialised) {
379
+ this.generator.update({
380
+ filePath: fullIdentifier,
381
+ document: source
382
+ });
383
+ await this.buildState();
384
+ }
385
+ }
386
+ addHookFile(filePath) {
387
+ this.hookFiles.add(filePath);
388
+ }
341
389
  /**
342
390
  * Initialise the collector.
343
391
  */
@@ -357,7 +405,19 @@ class Collector {
357
405
  documentNode: file.parsed
358
406
  });
359
407
  }
360
- this.buildState();
408
+ const hookDocuments = [...this.hookDocuments.entries()];
409
+ hookDocuments.forEach(([identifier, source]) => {
410
+ const file = new CollectedFile(identifier, source, false);
411
+ this.files.set(identifier, file);
412
+ this.generator.add({
413
+ filePath: identifier,
414
+ documentNode: file.parsed
415
+ });
416
+ });
417
+ for (const filePath of this.hookFiles) {
418
+ await this.addFile(filePath);
419
+ }
420
+ await this.buildState();
361
421
  logger.success("All GraphQL documents are valid.");
362
422
  } catch (e) {
363
423
  this.logError(e);
@@ -369,7 +429,7 @@ class Collector {
369
429
  */
370
430
  async addFile(filePath) {
371
431
  const file = await CollectedFile.fromFilePath(filePath);
372
- if (!file.fileContents) {
432
+ if (!file?.fileContents) {
373
433
  return null;
374
434
  }
375
435
  this.files.set(filePath, file);
@@ -379,15 +439,18 @@ class Collector {
379
439
  });
380
440
  return file;
381
441
  }
442
+ matchesPatternOrExists(filePath) {
443
+ return this.files.has(filePath) || this.hookFiles.has(filePath) || this.hookDocuments.has(filePath) || this.helper.matchesImportPattern(filePath);
444
+ }
382
445
  async handleAdd(filePath) {
383
- if (!this.helper.matchesImportPattern(filePath)) {
446
+ if (!this.matchesPatternOrExists(filePath)) {
384
447
  return false;
385
448
  }
386
449
  const result = await this.addFile(filePath);
387
450
  return !!result;
388
451
  }
389
452
  async handleChange(filePath) {
390
- if (!this.helper.matchesImportPattern(filePath)) {
453
+ if (!this.matchesPatternOrExists(filePath)) {
391
454
  return false;
392
455
  }
393
456
  const file = this.files.get(filePath);
@@ -446,7 +509,7 @@ class Collector {
446
509
  hasChanged = this.handleUnlinkDir(filePath);
447
510
  }
448
511
  if (hasChanged) {
449
- this.buildState();
512
+ await this.buildState();
450
513
  }
451
514
  } catch (e) {
452
515
  this.generator.resetCaches();
@@ -478,7 +541,7 @@ class Collector {
478
541
  */
479
542
  addVirtualTemplate(template) {
480
543
  const filename = template.options.path + ".js";
481
- const getContents = () => this.getTemplate(filename);
544
+ const getContents = () => this.getTemplate(template.options.path, "default");
482
545
  addTemplate({
483
546
  filename,
484
547
  getContents
@@ -503,21 +566,23 @@ class Collector {
503
566
  if (template.options.virtual) {
504
567
  this.addVirtualTemplate(template);
505
568
  } else {
506
- const filename = template.options.path + ".js";
569
+ const path = template.options.path;
570
+ const filename = template.options.isFullPath ? path : path + ".js";
507
571
  addTemplate({
508
572
  filename,
509
573
  write: true,
510
- getContents: () => this.getTemplate(filename)
574
+ getContents: () => this.getTemplate(path, "default")
511
575
  });
512
576
  }
513
577
  }
514
578
  if (template.buildTypes) {
579
+ const path = template.options.path;
515
580
  const filename = template.options.path + ".d.ts";
516
581
  addTypeTemplate(
517
582
  {
518
583
  filename,
519
584
  write: true,
520
- getContents: () => this.getTemplate(filename)
585
+ getContents: () => this.getTemplate(path, "types")
521
586
  },
522
587
  {
523
588
  nuxt: true,
@@ -526,6 +591,23 @@ class Collector {
526
591
  );
527
592
  }
528
593
  }
594
+ /**
595
+ * Get the hook documents.
596
+ */
597
+ getHookDocuments() {
598
+ return [...this.hookDocuments.entries()].map(([identifier, source]) => {
599
+ return {
600
+ identifier,
601
+ source
602
+ };
603
+ });
604
+ }
605
+ /**
606
+ * Get the hook documents.
607
+ */
608
+ getHookFiles() {
609
+ return [...this.hookFiles.values()];
610
+ }
529
611
  }
530
612
 
531
613
  class SchemaProvider {
@@ -730,7 +812,6 @@ ${color.cyan(S_BAR_END)}
730
812
  class ModuleHelper {
731
813
  constructor(nuxt, moduleUrl, options) {
732
814
  this.nuxt = nuxt;
733
- const isModuleBuild = process.env.MODULE_BUILD === "true" && nuxt.options._prepare;
734
815
  const mergedOptions = defu({}, options, defaultOptions);
735
816
  if (!mergedOptions.autoImportPatterns) {
736
817
  mergedOptions.autoImportPatterns = [
@@ -738,17 +819,28 @@ class ModuleHelper {
738
819
  "!node_modules"
739
820
  ];
740
821
  }
741
- if (isModuleBuild) {
742
- mergedOptions.graphqlEndpoint = "http://localhost";
743
- mergedOptions.downloadSchema = false;
744
- mergedOptions.schemaPath = "~~/schema.graphql";
745
- mergedOptions.autoImportPatterns = [
746
- "~~/playground/**/*.{gql,graphql}",
747
- "!node_modules"
748
- ];
749
- }
750
- mergedOptions.autoImportPatterns = (mergedOptions.autoImportPatterns || []).map((pattern) => {
751
- return resolveAlias(pattern);
822
+ const layerAliases = nuxt.options._layers.map((layer) => {
823
+ return {
824
+ "~~": layer.config.rootDir,
825
+ "@@": layer.config.rootDir,
826
+ "~": layer.config.srcDir,
827
+ "@": layer.config.srcDir,
828
+ // Merge any additional aliases defined by the layer.
829
+ // Must be last so that the layer may override the "default" aliases.
830
+ ...layer.config.alias || {}
831
+ };
832
+ });
833
+ const srcResolver = createResolver(nuxt.options.srcDir);
834
+ const rootResolver = createResolver(nuxt.options.rootDir);
835
+ mergedOptions.autoImportPatterns = (mergedOptions.autoImportPatterns || []).flatMap((pattern) => {
836
+ if (pattern.startsWith("!") || pattern.startsWith("/")) {
837
+ return pattern;
838
+ } else if (pattern.startsWith("~") || pattern.startsWith("@")) {
839
+ return layerAliases.map((aliases) => resolveAlias(pattern, aliases));
840
+ }
841
+ return rootResolver.resolve(pattern);
842
+ }).map((pattern) => {
843
+ return pattern.replace(".{graphql}", ".graphql").replace(".{gql}", ".gql");
752
844
  });
753
845
  this.options = mergedOptions;
754
846
  if (!nuxt.options._prepare) {
@@ -758,9 +850,9 @@ class ModuleHelper {
758
850
  this.resolvers = {
759
851
  module: createResolver(moduleUrl),
760
852
  server: createResolver(nuxt.options.serverDir),
761
- src: createResolver(nuxt.options.srcDir),
853
+ src: srcResolver,
762
854
  app: createResolver(nuxt.options.dir.app),
763
- root: createResolver(nuxt.options.rootDir)
855
+ root: rootResolver
764
856
  };
765
857
  this.paths = {
766
858
  runtimeTypes: "",
@@ -786,6 +878,7 @@ class ModuleHelper {
786
878
  options;
787
879
  prompt = new ConsolePrompt();
788
880
  nitroExternals = [];
881
+ tsPaths = {};
789
882
  /**
790
883
  * Find the path to the graphqlMiddleware.serverOptions.ts file.
791
884
  */
@@ -849,35 +942,17 @@ class ModuleHelper {
849
942
  async getImportPatternFiles() {
850
943
  return resolveFiles(
851
944
  this.nuxt.options.srcDir,
852
- this.options.autoImportPatterns,
853
- {
854
- followSymbolicLinks: false
855
- }
945
+ this.options.autoImportPatterns
856
946
  );
857
947
  }
858
948
  matchesImportPattern(filePath) {
859
- return micromatch.isMatch(filePath, this.options.autoImportPatterns);
949
+ return micromatch.isMatch(filePath, this.options.autoImportPatterns) || this.options.autoImportPatterns.includes(filePath);
860
950
  }
861
951
  addAlias(name, path) {
862
952
  this.nuxt.options.alias[name] = path;
863
953
  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
- ];
954
+ this.tsPaths[name] = pathFromName;
955
+ this.tsPaths[name + "/*"] = pathFromName + "/*";
881
956
  this.inlineNitroExternals(name);
882
957
  }
883
958
  inlineNitroExternals(arg) {
@@ -892,10 +967,34 @@ class ModuleHelper {
892
967
  this.nuxt.options.nitro.externals ||= {};
893
968
  this.nuxt.options.nitro.externals.inline ||= [];
894
969
  this.nuxt.options.nitro.externals.inline.push(...this.nitroExternals);
970
+ this.nuxt.options.nitro.typescript ||= {};
971
+ this.nuxt.options.nitro.typescript.tsConfig ||= {};
972
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions ||= {};
973
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths ||= {};
974
+ this.nuxt.options.typescript.tsConfig ||= {};
975
+ this.nuxt.options.typescript.tsConfig.compilerOptions ||= {};
976
+ this.nuxt.options.typescript.tsConfig.compilerOptions.paths ||= {};
977
+ for (const [name, path] of Object.entries(this.tsPaths)) {
978
+ this.nuxt.options.nitro.typescript.tsConfig.compilerOptions.paths[name] = [path];
979
+ this.nuxt.options.typescript.tsConfig.compilerOptions.paths[name] = [path];
980
+ }
981
+ }
982
+ processTemplate(path, content) {
983
+ if (path.includes("graphql-operations/") || path.endsWith(".graphql")) {
984
+ return content.trim();
985
+ }
986
+ const name = path.split("/")[1];
987
+ return `/*
988
+ * @see [Documentation](https://nuxt-graphql-middleware.dulnan.net/advanced/templates#${name})
989
+ */
990
+ ${content.trim()}`;
895
991
  }
896
992
  addTemplate(template) {
897
993
  if (template.build) {
898
- const content = template.build(this).trim();
994
+ const content = this.processTemplate(
995
+ template.options.path,
996
+ template.build(this)
997
+ );
899
998
  addTemplate({
900
999
  filename: template.options.path + ".js",
901
1000
  write: true,
@@ -903,7 +1002,10 @@ class ModuleHelper {
903
1002
  });
904
1003
  }
905
1004
  if (template.buildTypes) {
906
- const content = template.buildTypes(this).trim();
1005
+ const content = this.processTemplate(
1006
+ template.options.path,
1007
+ template.buildTypes(this)
1008
+ );
907
1009
  const filename = template.options.path + ".d.ts";
908
1010
  addTypeTemplate({
909
1011
  filename,
@@ -912,8 +1014,8 @@ class ModuleHelper {
912
1014
  });
913
1015
  }
914
1016
  }
915
- addPlugin(path) {
916
- addPlugin(this.resolvers.module.resolve(path), {
1017
+ addPlugin(name) {
1018
+ addPlugin(this.resolvers.module.resolve("./runtime/plugins/" + name), {
917
1019
  append: false
918
1020
  });
919
1021
  }
@@ -976,16 +1078,16 @@ export { clientOptions }
976
1078
  helper.paths.clientOptions
977
1079
  );
978
1080
  return `import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
979
- import { clientOptions } from '${pathRelative}'
980
-
981
- export type GraphqlClientContext = typeof clientOptions extends GraphqlClientOptions<infer R> ? R : {}
1081
+ import clientOptionsImport from '${pathRelative}'
982
1082
 
983
- export { clientOptions }`;
1083
+ declare export const clientOptions: GraphqlClientOptions
1084
+ export type GraphqlClientContext = typeof clientOptionsImport extends GraphqlClientOptions<infer R> ? R : {}
1085
+ `;
984
1086
  }
985
1087
  return `
986
1088
  import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
987
- export const clientOptions: GraphqlClientOptions
988
1089
 
1090
+ declare export const clientOptions: GraphqlClientOptions
989
1091
  export type GraphqlClientContext = {}
990
1092
  `;
991
1093
  }
@@ -1027,13 +1129,25 @@ const GraphqlConfig = defineStaticTemplate(
1027
1129
  const documents = patterns.filter((v) => !v.includes("!")).map((pattern) => {
1028
1130
  return "./" + relative(configPath, helper.resolvers.root.resolve(pattern));
1029
1131
  });
1030
- return `const schema = ${JSON.stringify(schemaPath)}
1132
+ documents.push(
1133
+ "./" + relative(
1134
+ configPath,
1135
+ join(helper.paths.moduleBuildDir, "hook-documents.graphql")
1136
+ )
1137
+ );
1138
+ return `
1139
+ import { hookFiles } from './hook-files'
1140
+
1141
+ const schema = ${JSON.stringify(schemaPath)}
1031
1142
 
1032
1143
  const documents = ${JSON.stringify(documents, null, 2)};
1033
1144
 
1034
1145
  const config = {
1035
1146
  schema,
1036
- documents,
1147
+ documents: [
1148
+ ...documents,
1149
+ ...hookFiles
1150
+ ]
1037
1151
  }
1038
1152
 
1039
1153
  export default config
@@ -1133,7 +1247,7 @@ import type { GraphqlServerResponse } from '${helper.paths.runtimeTypes}'
1133
1247
 
1134
1248
  declare module '#nuxt-graphql-middleware/response' {
1135
1249
  export type GraphqlMiddlewareResponseUnion =
1136
- | ${allTypes.join("\n | ") || "never"}
1250
+ | ${allTypes.join("\n | ") || "never"}
1137
1251
 
1138
1252
  export type GraphqlResponse<T> = GraphqlServerResponse<T> & GraphqlResponseAdditions
1139
1253
  export type GraphqlResponseTyped = GraphqlResponse<GraphqlMiddlewareResponseUnion>
@@ -1152,16 +1266,27 @@ export { serverOptions }
1152
1266
  `;
1153
1267
  },
1154
1268
  (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 `
1269
+ if (helper.paths.serverOptions) {
1270
+ const resolvedPathRelative = helper.toModuleBuildRelative(
1271
+ helper.paths.serverOptions
1272
+ );
1273
+ return `
1158
1274
  import type { GraphqlMiddlewareServerOptions } from '${helper.paths.runtimeTypes}'
1159
- ${serverOptionsLineTypes}
1275
+ import serverOptionsImport from '${resolvedPathRelative}'
1160
1276
 
1161
1277
  export type GraphqlResponseAdditions =
1162
- typeof serverOptions extends GraphqlMiddlewareServerOptions<infer R, any, any> ? R : {}
1278
+ typeof serverOptionsImport extends GraphqlMiddlewareServerOptions<infer R, any, any> ? R : {}
1279
+
1280
+ declare export const serverOptions: GraphqlMiddlewareServerOptions
1281
+ `;
1282
+ }
1283
+ return `
1284
+ import type { GraphqlMiddlewareServerOptions } from '${helper.paths.runtimeTypes}'
1285
+
1286
+ declare export const serverOptions: GraphqlMiddlewareServerOptions
1163
1287
 
1164
- export { serverOptions }`;
1288
+ export type GraphqlResponseAdditions = object
1289
+ `;
1165
1290
  }
1166
1291
  );
1167
1292
 
@@ -1172,7 +1297,7 @@ const Sources = defineGeneratorTemplate(
1172
1297
  const srcDir = helper.paths.root;
1173
1298
  const lines = [];
1174
1299
  for (const operation of operations) {
1175
- const filePath = relative(srcDir, operation.filePath);
1300
+ const filePath = operation.filePath.startsWith("/") ? relative(srcDir, operation.filePath) : operation.filePath;
1176
1301
  lines.push(
1177
1302
  `${operation.operationType}_${operation.graphqlName}: '${filePath}',`
1178
1303
  );
@@ -1188,6 +1313,43 @@ export const operationSources = {
1188
1313
  }
1189
1314
  );
1190
1315
 
1316
+ const HookDocuments = defineGeneratorTemplate(
1317
+ {
1318
+ path: "nuxt-graphql-middleware/hook-documents.graphql",
1319
+ virtual: false,
1320
+ isFullPath: true
1321
+ },
1322
+ (_output, _helper, collector) => {
1323
+ return collector.getHookDocuments().map((v) => {
1324
+ return `
1325
+ # ${v.identifier}
1326
+ ${v.source}
1327
+ `;
1328
+ }).join("\n\n");
1329
+ },
1330
+ null
1331
+ );
1332
+
1333
+ const HookFiles = defineGeneratorTemplate(
1334
+ {
1335
+ path: "nuxt-graphql-middleware/hook-files",
1336
+ virtual: false
1337
+ },
1338
+ (_output, helper, collector) => {
1339
+ const configPath = helper.resolvers.root.resolve(
1340
+ (helper.options.graphqlConfigFilePath || "").replace(
1341
+ "/graphql.config.ts",
1342
+ ""
1343
+ )
1344
+ );
1345
+ const files = collector.getHookFiles().map((filePath) => {
1346
+ return "./" + relative(configPath, filePath);
1347
+ });
1348
+ return `export const hookFiles = ${JSON.stringify(files, null, 2)}`;
1349
+ },
1350
+ null
1351
+ );
1352
+
1191
1353
  const TEMPLATES = [
1192
1354
  ClientOptions,
1193
1355
  Documents,
@@ -1198,7 +1360,9 @@ const TEMPLATES = [
1198
1360
  Operations,
1199
1361
  Response,
1200
1362
  ServerOptions,
1201
- Sources
1363
+ Sources,
1364
+ HookDocuments,
1365
+ HookFiles
1202
1366
  ];
1203
1367
 
1204
1368
  const DEVTOOLS_UI_ROUTE = "/__nuxt-graphql-middleware";
@@ -1284,11 +1448,12 @@ class DevModeHandler {
1284
1448
  this.nitro = useNitro();
1285
1449
  this.nitro.hooks.hook("compiled", this.onNitroCompiled.bind(this));
1286
1450
  }
1287
- async onBuilderWatch(event, pathAbsolute) {
1288
- if (pathAbsolute === this.helper.paths.schema) {
1451
+ async onBuilderWatch(event, providedFilePath) {
1452
+ if (!providedFilePath.endsWith(".graphql") && !providedFilePath.endsWith(".gql")) {
1289
1453
  return;
1290
1454
  }
1291
- if (!pathAbsolute.match(/\.(gql|graphql)$/)) {
1455
+ const pathAbsolute = providedFilePath.startsWith("/") ? providedFilePath : this.helper.resolvers.src.resolve(providedFilePath);
1456
+ if (pathAbsolute === this.helper.paths.schema) {
1292
1457
  return;
1293
1458
  }
1294
1459
  this.helper.prompt.abort();
@@ -1363,6 +1528,84 @@ class DevModeHandler {
1363
1528
  }
1364
1529
  }
1365
1530
 
1531
+ class ModuleContext {
1532
+ constructor(schemaProvider, collector) {
1533
+ this.schemaProvider = schemaProvider;
1534
+ this.collector = collector;
1535
+ }
1536
+ /**
1537
+ * Return the GraphQL schema.
1538
+ *
1539
+ * Note that the schema may be updated during development, so it can become
1540
+ * stale. Prefer using methods like `schemaHasType()` to query the schema.
1541
+ *
1542
+ * @returns The GraphQL schema.
1543
+ */
1544
+ getSchema() {
1545
+ return this.schemaProvider.getSchema();
1546
+ }
1547
+ /**
1548
+ * Check if the given GraphQL type (interface, concrete type, enum, input type)
1549
+ * exists in the schema.
1550
+ *
1551
+ * @param name - The name of the type.
1552
+ *
1553
+ * @returns True if the type exists in the schema.
1554
+ */
1555
+ schemaHasType(name) {
1556
+ return !!this.schemaProvider.getSchema().getType(name);
1557
+ }
1558
+ /**
1559
+ * Get a type from the schema.
1560
+ *
1561
+ * @param name - The name of the type.
1562
+ *
1563
+ * @returns The type.
1564
+ */
1565
+ schemaGetType(name) {
1566
+ return this.schemaProvider.getSchema().getType(name);
1567
+ }
1568
+ /**
1569
+ * Add an additional static document.
1570
+ *
1571
+ * @param identifier - The unique identifier for your document.
1572
+ * @param source - The document source.
1573
+ */
1574
+ addDocument(identifier, source) {
1575
+ this.collector.addHookDocument(identifier, source);
1576
+ return this;
1577
+ }
1578
+ /**
1579
+ * Add or update an additional static document.
1580
+ *
1581
+ * @param identifier - The unique identifier for your document.
1582
+ * @param source - The document source.
1583
+ */
1584
+ async addOrUpdateDocument(identifier, source) {
1585
+ await this.collector.addOrUpdateHookDocument(identifier, source);
1586
+ return this;
1587
+ }
1588
+ /**
1589
+ * Add an additional GraphQL file to import.
1590
+ *
1591
+ * @param filePath - The absolute path to the file.
1592
+ */
1593
+ addImportFile(filePath) {
1594
+ if (!filePath.startsWith("/")) {
1595
+ throw new Error(
1596
+ `The provided file path "${filePath}" must be an absolute path.`
1597
+ );
1598
+ }
1599
+ if (!filePath.endsWith(".graphql") && !filePath.endsWith(".gql")) {
1600
+ throw new Error(
1601
+ `The provided file path "${filePath}" should have a .graphql or .gql extension.`
1602
+ );
1603
+ }
1604
+ this.collector.addHookFile(filePath);
1605
+ return this;
1606
+ }
1607
+ }
1608
+
1366
1609
  const module = defineNuxtModule({
1367
1610
  meta: {
1368
1611
  name,
@@ -1378,6 +1621,8 @@ const module = defineNuxtModule({
1378
1621
  const schemaProvider = new SchemaProvider(helper);
1379
1622
  await schemaProvider.init();
1380
1623
  const collector = new Collector(schemaProvider.getSchema(), helper);
1624
+ const moduleContext = new ModuleContext(schemaProvider, collector);
1625
+ nuxt._nuxt_graphql_middleware = moduleContext;
1381
1626
  nuxt.options.appConfig.graphqlMiddleware = {
1382
1627
  clientCacheEnabled: !!helper.options.clientCache?.enabled,
1383
1628
  clientCacheMaxSize: helper.options.clientCache?.maxSize ?? 100
@@ -1391,9 +1636,9 @@ const module = defineNuxtModule({
1391
1636
  helper.inlineNitroExternals(helper.paths.moduleTypesDir);
1392
1637
  helper.addAlias("#nuxt-graphql-middleware", helper.paths.moduleBuildDir);
1393
1638
  helper.addAlias("#graphql-operations", helper.paths.moduleTypesDir);
1394
- helper.addPlugin("./runtime/plugins/provideState");
1639
+ helper.addPlugin("provideState");
1395
1640
  if (helper.isDev && helper.options.errorOverlay) {
1396
- helper.addPlugin("./runtime/plugins/devMode");
1641
+ helper.addPlugin("devMode");
1397
1642
  }
1398
1643
  helper.addServerHandler("query", "/query/:name", "get");
1399
1644
  helper.addServerHandler("mutation", "/mutation/:name", "post");
@@ -1423,7 +1668,10 @@ const module = defineNuxtModule({
1423
1668
  }
1424
1669
  });
1425
1670
  helper.applyBuildConfig();
1426
- await collector.init();
1671
+ nuxt.hooks.hookOnce("modules:done", async () => {
1672
+ await nuxt.hooks.callHook("nuxt-graphql-middleware:init", moduleContext);
1673
+ await collector.init();
1674
+ });
1427
1675
  if (!helper.isDev) {
1428
1676
  return;
1429
1677
  }