nuxt-graphql-middleware 5.0.0-alpha.8 → 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 +340 -91
  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/c16b1fe3-c3fb-4c15-952d-ba148d4025cb.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.8";
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: "",
@@ -770,7 +862,7 @@ class ModuleHelper {
770
862
  schema: this.resolvers.root.resolve(
771
863
  resolveAlias(this.options.schemaPath)
772
864
  ),
773
- serverOptions: this.findServerOptions(),
865
+ serverOptions: "",
774
866
  clientOptions: this.findClientOptions(),
775
867
  moduleBuildDir: nuxt.options.buildDir + "/nuxt-graphql-middleware",
776
868
  moduleTypesDir: nuxt.options.buildDir + "/graphql-operations"
@@ -778,6 +870,7 @@ class ModuleHelper {
778
870
  this.paths.runtimeTypes = this.toModuleBuildRelative(
779
871
  this.resolvers.module.resolve("./runtime/types.ts")
780
872
  );
873
+ this.paths.serverOptions = this.findServerOptions();
781
874
  }
782
875
  resolvers;
783
876
  paths;
@@ -785,6 +878,7 @@ class ModuleHelper {
785
878
  options;
786
879
  prompt = new ConsolePrompt();
787
880
  nitroExternals = [];
881
+ tsPaths = {};
788
882
  /**
789
883
  * Find the path to the graphqlMiddleware.serverOptions.ts file.
790
884
  */
@@ -848,35 +942,17 @@ class ModuleHelper {
848
942
  async getImportPatternFiles() {
849
943
  return resolveFiles(
850
944
  this.nuxt.options.srcDir,
851
- this.options.autoImportPatterns,
852
- {
853
- followSymbolicLinks: false
854
- }
945
+ this.options.autoImportPatterns
855
946
  );
856
947
  }
857
948
  matchesImportPattern(filePath) {
858
- return micromatch.isMatch(filePath, this.options.autoImportPatterns);
949
+ return micromatch.isMatch(filePath, this.options.autoImportPatterns) || this.options.autoImportPatterns.includes(filePath);
859
950
  }
860
951
  addAlias(name, path) {
861
952
  this.nuxt.options.alias[name] = path;
862
953
  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.nuxt.options.typescript.tsConfig ||= {};
872
- this.nuxt.options.typescript.tsConfig.compilerOptions ||= {};
873
- this.nuxt.options.typescript.tsConfig.compilerOptions.paths ||= {};
874
- this.nuxt.options.typescript.tsConfig.compilerOptions.paths[name] = [
875
- pathFromName
876
- ];
877
- this.nuxt.options.typescript.tsConfig.compilerOptions.paths[name + "/*"] = [
878
- pathFromName + "/*"
879
- ];
954
+ this.tsPaths[name] = pathFromName;
955
+ this.tsPaths[name + "/*"] = pathFromName + "/*";
880
956
  this.inlineNitroExternals(name);
881
957
  }
882
958
  inlineNitroExternals(arg) {
@@ -891,10 +967,34 @@ class ModuleHelper {
891
967
  this.nuxt.options.nitro.externals ||= {};
892
968
  this.nuxt.options.nitro.externals.inline ||= [];
893
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()}`;
894
991
  }
895
992
  addTemplate(template) {
896
993
  if (template.build) {
897
- const content = template.build(this).trim();
994
+ const content = this.processTemplate(
995
+ template.options.path,
996
+ template.build(this)
997
+ );
898
998
  addTemplate({
899
999
  filename: template.options.path + ".js",
900
1000
  write: true,
@@ -902,7 +1002,10 @@ class ModuleHelper {
902
1002
  });
903
1003
  }
904
1004
  if (template.buildTypes) {
905
- const content = template.buildTypes(this).trim();
1005
+ const content = this.processTemplate(
1006
+ template.options.path,
1007
+ template.buildTypes(this)
1008
+ );
906
1009
  const filename = template.options.path + ".d.ts";
907
1010
  addTypeTemplate({
908
1011
  filename,
@@ -911,8 +1014,8 @@ class ModuleHelper {
911
1014
  });
912
1015
  }
913
1016
  }
914
- addPlugin(path) {
915
- addPlugin(this.resolvers.module.resolve(path), {
1017
+ addPlugin(name) {
1018
+ addPlugin(this.resolvers.module.resolve("./runtime/plugins/" + name), {
916
1019
  append: false
917
1020
  });
918
1021
  }
@@ -975,16 +1078,16 @@ export { clientOptions }
975
1078
  helper.paths.clientOptions
976
1079
  );
977
1080
  return `import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
978
- import { clientOptions } from '${pathRelative}'
979
-
980
- export type GraphqlClientContext = typeof clientOptions extends GraphqlClientOptions<infer R> ? R : {}
1081
+ import clientOptionsImport from '${pathRelative}'
981
1082
 
982
- export { clientOptions }`;
1083
+ declare export const clientOptions: GraphqlClientOptions
1084
+ export type GraphqlClientContext = typeof clientOptionsImport extends GraphqlClientOptions<infer R> ? R : {}
1085
+ `;
983
1086
  }
984
1087
  return `
985
1088
  import type { GraphqlClientOptions } from '${helper.paths.runtimeTypes}'
986
- export const clientOptions: GraphqlClientOptions
987
1089
 
1090
+ declare export const clientOptions: GraphqlClientOptions
988
1091
  export type GraphqlClientContext = {}
989
1092
  `;
990
1093
  }
@@ -1026,13 +1129,25 @@ const GraphqlConfig = defineStaticTemplate(
1026
1129
  const documents = patterns.filter((v) => !v.includes("!")).map((pattern) => {
1027
1130
  return "./" + relative(configPath, helper.resolvers.root.resolve(pattern));
1028
1131
  });
1029
- 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)}
1030
1142
 
1031
1143
  const documents = ${JSON.stringify(documents, null, 2)};
1032
1144
 
1033
1145
  const config = {
1034
1146
  schema,
1035
- documents,
1147
+ documents: [
1148
+ ...documents,
1149
+ ...hookFiles
1150
+ ]
1036
1151
  }
1037
1152
 
1038
1153
  export default config
@@ -1132,7 +1247,7 @@ import type { GraphqlServerResponse } from '${helper.paths.runtimeTypes}'
1132
1247
 
1133
1248
  declare module '#nuxt-graphql-middleware/response' {
1134
1249
  export type GraphqlMiddlewareResponseUnion =
1135
- | ${allTypes.join("\n | ") || "never"}
1250
+ | ${allTypes.join("\n | ") || "never"}
1136
1251
 
1137
1252
  export type GraphqlResponse<T> = GraphqlServerResponse<T> & GraphqlResponseAdditions
1138
1253
  export type GraphqlResponseTyped = GraphqlResponse<GraphqlMiddlewareResponseUnion>
@@ -1151,16 +1266,27 @@ export { serverOptions }
1151
1266
  `;
1152
1267
  },
1153
1268
  (helper) => {
1154
- const resolvedPathRelative = helper.paths.serverOptions ? helper.toModuleBuildRelative(helper.paths.serverOptions) : null;
1155
- const serverOptionsLineTypes = resolvedPathRelative ? `import serverOptions from '${resolvedPathRelative}'` : `const serverOptions: GraphqlMiddlewareServerOptions = {}`;
1156
- return `
1269
+ if (helper.paths.serverOptions) {
1270
+ const resolvedPathRelative = helper.toModuleBuildRelative(
1271
+ helper.paths.serverOptions
1272
+ );
1273
+ return `
1157
1274
  import type { GraphqlMiddlewareServerOptions } from '${helper.paths.runtimeTypes}'
1158
- ${serverOptionsLineTypes}
1275
+ import serverOptionsImport from '${resolvedPathRelative}'
1159
1276
 
1160
1277
  export type GraphqlResponseAdditions =
1161
- 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
1162
1287
 
1163
- export { serverOptions }`;
1288
+ export type GraphqlResponseAdditions = object
1289
+ `;
1164
1290
  }
1165
1291
  );
1166
1292
 
@@ -1171,7 +1297,7 @@ const Sources = defineGeneratorTemplate(
1171
1297
  const srcDir = helper.paths.root;
1172
1298
  const lines = [];
1173
1299
  for (const operation of operations) {
1174
- const filePath = relative(srcDir, operation.filePath);
1300
+ const filePath = operation.filePath.startsWith("/") ? relative(srcDir, operation.filePath) : operation.filePath;
1175
1301
  lines.push(
1176
1302
  `${operation.operationType}_${operation.graphqlName}: '${filePath}',`
1177
1303
  );
@@ -1187,6 +1313,43 @@ export const operationSources = {
1187
1313
  }
1188
1314
  );
1189
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
+
1190
1353
  const TEMPLATES = [
1191
1354
  ClientOptions,
1192
1355
  Documents,
@@ -1197,7 +1360,9 @@ const TEMPLATES = [
1197
1360
  Operations,
1198
1361
  Response,
1199
1362
  ServerOptions,
1200
- Sources
1363
+ Sources,
1364
+ HookDocuments,
1365
+ HookFiles
1201
1366
  ];
1202
1367
 
1203
1368
  const DEVTOOLS_UI_ROUTE = "/__nuxt-graphql-middleware";
@@ -1283,11 +1448,12 @@ class DevModeHandler {
1283
1448
  this.nitro = useNitro();
1284
1449
  this.nitro.hooks.hook("compiled", this.onNitroCompiled.bind(this));
1285
1450
  }
1286
- async onBuilderWatch(event, pathAbsolute) {
1287
- if (pathAbsolute === this.helper.paths.schema) {
1451
+ async onBuilderWatch(event, providedFilePath) {
1452
+ if (!providedFilePath.endsWith(".graphql") && !providedFilePath.endsWith(".gql")) {
1288
1453
  return;
1289
1454
  }
1290
- if (!pathAbsolute.match(/\.(gql|graphql)$/)) {
1455
+ const pathAbsolute = providedFilePath.startsWith("/") ? providedFilePath : this.helper.resolvers.src.resolve(providedFilePath);
1456
+ if (pathAbsolute === this.helper.paths.schema) {
1291
1457
  return;
1292
1458
  }
1293
1459
  this.helper.prompt.abort();
@@ -1362,6 +1528,84 @@ class DevModeHandler {
1362
1528
  }
1363
1529
  }
1364
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
+
1365
1609
  const module = defineNuxtModule({
1366
1610
  meta: {
1367
1611
  name,
@@ -1377,6 +1621,8 @@ const module = defineNuxtModule({
1377
1621
  const schemaProvider = new SchemaProvider(helper);
1378
1622
  await schemaProvider.init();
1379
1623
  const collector = new Collector(schemaProvider.getSchema(), helper);
1624
+ const moduleContext = new ModuleContext(schemaProvider, collector);
1625
+ nuxt._nuxt_graphql_middleware = moduleContext;
1380
1626
  nuxt.options.appConfig.graphqlMiddleware = {
1381
1627
  clientCacheEnabled: !!helper.options.clientCache?.enabled,
1382
1628
  clientCacheMaxSize: helper.options.clientCache?.maxSize ?? 100
@@ -1390,9 +1636,9 @@ const module = defineNuxtModule({
1390
1636
  helper.inlineNitroExternals(helper.paths.moduleTypesDir);
1391
1637
  helper.addAlias("#nuxt-graphql-middleware", helper.paths.moduleBuildDir);
1392
1638
  helper.addAlias("#graphql-operations", helper.paths.moduleTypesDir);
1393
- helper.addPlugin("./runtime/plugins/provideState");
1639
+ helper.addPlugin("provideState");
1394
1640
  if (helper.isDev && helper.options.errorOverlay) {
1395
- helper.addPlugin("./runtime/plugins/devMode");
1641
+ helper.addPlugin("devMode");
1396
1642
  }
1397
1643
  helper.addServerHandler("query", "/query/:name", "get");
1398
1644
  helper.addServerHandler("mutation", "/mutation/:name", "post");
@@ -1422,7 +1668,10 @@ const module = defineNuxtModule({
1422
1668
  }
1423
1669
  });
1424
1670
  helper.applyBuildConfig();
1425
- 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
+ });
1426
1675
  if (!helper.isDev) {
1427
1676
  return;
1428
1677
  }