nitro-graphql 2.0.0-beta.11 โ†’ 2.0.0-beta.13

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.
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  # Nitro GraphQL
6
6
 
7
7
  [![npm version][npm-version-src]][npm-version-href]
8
+ [![Beta Status][beta-src]][beta-href]
8
9
  [![npm downloads][npm-downloads-src]][npm-downloads-href]
9
10
  [![bundle][bundle-src]][bundle-href]
10
11
  [![License][license-src]][license-href]
@@ -18,6 +19,13 @@
18
19
 
19
20
  </div>
20
21
 
22
+ > [!IMPORTANT]
23
+ > **v2.0 Beta (Current - Main Branch)**
24
+ > This is the **v2.0** beta branch with Nitro v3 and H3 v2 support. Includes Rolldown optimization, improved chunking, and enhanced Vite integration.
25
+ >
26
+ > **Looking for v1.x?**
27
+ > For the stable v1 version (Nitro v2), see the [`v1` branch](https://github.com/productdevbook/nitro-graphql/tree/v1).
28
+
21
29
  ---
22
30
 
23
31
  ## ๐ŸŽฅ Watch & Learn
@@ -34,6 +42,14 @@
34
42
  - ๐ŸŽฎ **Developer-friendly** - Built-in Apollo Sandbox for testing
35
43
  - ๐Ÿ”ง **Zero config** - Sensible defaults, customize when needed
36
44
 
45
+ ### ๐Ÿ†• What's New in v2.0 Beta
46
+
47
+ - ๐Ÿš€ **Nitro v3 & H3 v2** - Full compatibility with the latest Nitro and H3
48
+ - โš™๏ธ **Rolldown Support** - Optimized for both Rolldown (Vite 7+) and Rollup
49
+ - ๐Ÿ“ฆ **Smart Chunking** - GraphQL code split into separate chunks (~98% size reduction)
50
+ - ๐Ÿ” **Debug Dashboard** - Built-in diagnostics at `/_nitro/graphql/debug` (dev only)
51
+ - ๐ŸŽจ **Enhanced Vite Integration** - Better custom path support and virtual module resolution
52
+
37
53
  ## ๐Ÿš€ Quick Start
38
54
 
39
55
  ### 1. Install
@@ -147,6 +163,7 @@ pnpm dev
147
163
  - **Endpoint**: `http://localhost:3000/api/graphql`
148
164
  - **Playground**: `http://localhost:3000/api/graphql` (browser)
149
165
  - **Health**: `http://localhost:3000/api/graphql/health`
166
+ - **Debug Dashboard**: `http://localhost:3000/_nitro/graphql/debug` (dev mode only)
150
167
 
151
168
  ## ๐ŸŽฎ Examples
152
169
 
@@ -159,6 +176,13 @@ Try these working examples:
159
176
  | [**Nuxt Integration**](./playgrounds/nuxt/) | Full Nuxt app with client types | `pnpm playground:nuxt` |
160
177
  | [**Apollo Federation**](./playgrounds/federation/) | Federated GraphQL services | `pnpm playground:federation` |
161
178
 
179
+ ### ๐Ÿงช Test Projects
180
+
181
+ Real-world test projects using nitro-graphql v2:
182
+
183
+ - [**Vite + Nitro + Rolldown**](https://github.com/productdevbook/vite-nitro-graphql/tree/rolldown) - Testing with Rolldown bundler (Vite 7+)
184
+ - [**Vite + Nitro (Main)**](https://github.com/productdevbook/vite-nitro-graphql) - Standard Vite integration tests
185
+
162
186
  ## ๐Ÿ—๏ธ Building Your First Feature
163
187
 
164
188
  Let's create a complete user management system:
@@ -1248,4 +1272,6 @@ pnpm lint
1248
1272
  [license-src]: https://img.shields.io/github/license/productdevbook/nitro-graphql.svg?style=flat&colorA=080f12&colorB=1fa669
1249
1273
  [license-href]: https://github.com/productdevbook/nitro-graphql/blob/main/LICENSE
1250
1274
  [docs-src]: https://img.shields.io/badge/docs-read-blue?style=flat&colorA=080f12&colorB=1fa669
1251
- [docs-href]: https://nitro-graphql.pages.dev
1275
+ [docs-href]: https://nitro-graphql.pages.dev
1276
+ [beta-src]: https://img.shields.io/npm/v/nitro-graphql/beta?style=flat&logo=rocket&logoColor=white&label=beta&color=7c3aed&colorA=080f12
1277
+ [beta-href]: https://github.com/productdevbook/nitro-graphql/releases
@@ -1,5 +1,6 @@
1
+ import { getDefaultPaths, getTypesConfig, resolveFilePath } from "../utils/path-resolver.js";
1
2
  import { existsSync, mkdirSync, writeFileSync } from "node:fs";
2
- import { join, resolve } from "pathe";
3
+ import { dirname, join, relative, resolve } from "pathe";
3
4
  import { defineNuxtModule, getLayerDirectories } from "@nuxt/kit";
4
5
 
5
6
  //#region src/ecosystem/nuxt.ts
@@ -11,24 +12,65 @@ var nuxt_default = defineNuxtModule({
11
12
  },
12
13
  setup: async (_options, nuxt) => {
13
14
  nuxt.hooks.hook("prepare:types", (options) => {
14
- options.references.push({ path: "types/nitro-graphql-client.d.ts" });
15
- options.tsConfig ??= {};
16
- options.tsConfig.compilerOptions ??= {};
17
- options.tsConfig.compilerOptions.paths ??= {};
18
- options.tsConfig.compilerOptions.paths["#graphql/client"] = ["./types/nitro-graphql-client.d.ts"];
15
+ const mockNitro$1 = { options: {
16
+ rootDir: nuxt.options.rootDir,
17
+ buildDir: nuxt.options.buildDir,
18
+ framework: { name: "nuxt" },
19
+ graphql: nuxt.options.nitro?.graphql
20
+ } };
21
+ const placeholders$1 = getDefaultPaths(mockNitro$1);
22
+ const typesConfig$1 = getTypesConfig(mockNitro$1);
23
+ const tsconfigDir = dirname(join(nuxt.options.buildDir, "tsconfig.json"));
24
+ const relativeWithDot = (from, to) => {
25
+ const rel = relative(from, to);
26
+ return rel.startsWith(".") ? rel : `./${rel}`;
27
+ };
28
+ const clientTypesPath$1 = resolveFilePath(typesConfig$1.client, typesConfig$1.enabled, true, "{typesDir}/nitro-graphql-client.d.ts", placeholders$1);
29
+ if (clientTypesPath$1) {
30
+ const relativePath = relativeWithDot(tsconfigDir, clientTypesPath$1);
31
+ options.references.push({ path: relativePath });
32
+ options.tsConfig ??= {};
33
+ options.tsConfig.compilerOptions ??= {};
34
+ options.tsConfig.compilerOptions.paths ??= {};
35
+ options.tsConfig.compilerOptions.paths["#graphql/client"] = [relativePath];
36
+ options.tsConfig.include = options.tsConfig.include || [];
37
+ options.tsConfig.include.push(relativePath);
38
+ }
19
39
  const externalServices$1 = nuxt.options.nitro?.graphql?.externalServices || [];
20
40
  for (const service of externalServices$1) {
21
- options.references.push({ path: `types/nitro-graphql-client-${service.name}.d.ts` });
22
- options.tsConfig.compilerOptions.paths[`#graphql/client/${service.name}`] = [`./types/nitro-graphql-client-${service.name}.d.ts`];
41
+ const servicePlaceholders = {
42
+ ...placeholders$1,
43
+ serviceName: service.name
44
+ };
45
+ const externalTypesPath = resolveFilePath(service.paths?.types ?? typesConfig$1.external, typesConfig$1.enabled, true, "{typesDir}/nitro-graphql-client-{serviceName}.d.ts", servicePlaceholders);
46
+ if (externalTypesPath) {
47
+ const relativePath = relativeWithDot(tsconfigDir, externalTypesPath);
48
+ options.references.push({ path: relativePath });
49
+ options.tsConfig.compilerOptions.paths[`#graphql/client/${service.name}`] = [relativePath];
50
+ options.tsConfig.include.push(relativePath);
51
+ }
23
52
  }
24
- options.tsConfig.include = options.tsConfig.include || [];
25
- options.tsConfig.include.push("./types/nitro-graphql-client.d.ts");
26
- for (const service of externalServices$1) options.tsConfig.include.push(`./types/nitro-graphql-client-${service.name}.d.ts`);
27
53
  });
28
54
  nuxt.options.alias = nuxt.options.alias || {};
29
- nuxt.options.alias["#graphql/client"] = join(nuxt.options.buildDir, "types/nitro-graphql-client.d.ts");
55
+ const mockNitro = { options: {
56
+ rootDir: nuxt.options.rootDir,
57
+ buildDir: nuxt.options.buildDir,
58
+ framework: { name: "nuxt" },
59
+ graphql: nuxt.options.nitro?.graphql
60
+ } };
61
+ const placeholders = getDefaultPaths(mockNitro);
62
+ const typesConfig = getTypesConfig(mockNitro);
63
+ const clientTypesPath = resolveFilePath(typesConfig.client, typesConfig.enabled, true, "{typesDir}/nitro-graphql-client.d.ts", placeholders);
64
+ if (clientTypesPath) nuxt.options.alias["#graphql/client"] = clientTypesPath;
30
65
  const externalServices = nuxt.options.nitro?.graphql?.externalServices || [];
31
- for (const service of externalServices) nuxt.options.alias[`#graphql/client/${service.name}`] = join(nuxt.options.buildDir, `types/nitro-graphql-client-${service.name}.d.ts`);
66
+ for (const service of externalServices) {
67
+ const servicePlaceholders = {
68
+ ...placeholders,
69
+ serviceName: service.name
70
+ };
71
+ const externalTypesPath = resolveFilePath(service.paths?.types ?? typesConfig.external, typesConfig.enabled, true, "{typesDir}/nitro-graphql-client-{serviceName}.d.ts", servicePlaceholders);
72
+ if (externalTypesPath) nuxt.options.alias[`#graphql/client/${service.name}`] = externalTypesPath;
73
+ }
32
74
  nuxt.hook("imports:dirs", (dirs) => {
33
75
  const graphqlServerPath = nuxt.options.nitro?.graphql?.serverDir || resolve(nuxt.options.srcDir, "graphql");
34
76
  dirs.push(graphqlServerPath);
package/dist/index.d.ts CHANGED
@@ -3,6 +3,10 @@ import { ClientUtilsConfig, CodegenClientConfig, CodegenServerConfig, ExternalGr
3
3
  import * as nitro_types0 from "nitro/types";
4
4
 
5
5
  //#region src/index.d.ts
6
+ /**
7
+ * Nitro GraphQL module
8
+ * Can be used via modules array in nitro.config.ts
9
+ */
6
10
  declare const _default: nitro_types0.NitroModule;
7
11
  //#endregion
8
12
  export { ClientUtilsConfig, CodegenClientConfig, CodegenServerConfig, ExternalGraphQLService, ExternalServicePaths, FederationConfig, FileGenerationConfig, GenImport, GenericSdkConfig, NitroGraphQLOptions, PathsConfig, ScaffoldConfig, SdkConfig, StandardSchemaV1, TypesConfig, _default as default };
package/dist/index.js CHANGED
@@ -1,323 +1,14 @@
1
- import { generateDirectiveSchemas } from "./utils/directive-parser.js";
2
- import { generateLayerIgnorePatterns, getLayerAppDirectories, getLayerServerDirectories, relativeWithDot, scanDirectives, scanDocs, scanResolvers, scanSchemas, validateExternalServices } from "./utils/index.js";
3
- import { writeFileIfNotExists } from "./utils/file-generator.js";
4
- import { getScaffoldConfig, resolveFilePath, shouldGenerateScaffold } from "./utils/path-resolver.js";
5
- import { clientTypeGeneration, serverTypeGeneration } from "./utils/type-generation.js";
6
- import { rollupConfig } from "./rollup.js";
7
- import { existsSync, mkdirSync } from "node:fs";
8
- import { fileURLToPath } from "node:url";
9
- import { watch } from "chokidar";
10
- import consola from "consola";
11
- import defu from "defu";
12
- import { dirname, join, relative, resolve } from "pathe";
1
+ import { setupNitroGraphQL } from "./setup.js";
13
2
 
14
3
  //#region src/index.ts
4
+ /**
5
+ * Nitro GraphQL module
6
+ * Can be used via modules array in nitro.config.ts
7
+ */
15
8
  var src_default = defineNitroModule({
16
9
  name: "nitro-graphql",
17
10
  async setup(nitro) {
18
- if (!nitro.options.graphql?.framework) consola.warn("No GraphQL framework specified. Please set graphql.framework to \"graphql-yoga\" or \"apollo-server\".");
19
- if (nitro.options.graphql?.externalServices?.length) {
20
- const validationErrors = validateExternalServices(nitro.options.graphql.externalServices);
21
- if (validationErrors.length > 0) {
22
- consola.error("External services configuration errors:");
23
- for (const error of validationErrors) consola.error(` - ${error}`);
24
- throw new Error("Invalid external services configuration");
25
- }
26
- consola.info(`Configured ${nitro.options.graphql.externalServices.length} external GraphQL services`);
27
- }
28
- const { getDefaultPaths: getDefaultPaths$1 } = await import("./utils/path-resolver.js");
29
- const defaultPaths = getDefaultPaths$1(nitro);
30
- nitro.graphql ||= {
31
- buildDir: "",
32
- watchDirs: [],
33
- clientDir: defaultPaths.clientGraphql,
34
- serverDir: defaultPaths.serverGraphql,
35
- dir: {
36
- build: relative(nitro.options.rootDir, nitro.options.buildDir),
37
- client: "graphql",
38
- server: "server"
39
- }
40
- };
41
- nitro.hooks.hook("rollup:before", (_, rollupConfig$1) => {
42
- rollupConfig$1.external = rollupConfig$1.external || [];
43
- const codegenExternals = ["oxc-parser", "@oxc-parser"];
44
- if (Array.isArray(rollupConfig$1.external)) rollupConfig$1.external.push(...codegenExternals);
45
- else if (typeof rollupConfig$1.external === "function") {
46
- const originalExternal = rollupConfig$1.external;
47
- rollupConfig$1.external = (id, parent, isResolved) => {
48
- if (codegenExternals.some((external) => id.includes(external))) return true;
49
- return originalExternal(id, parent, isResolved);
50
- };
51
- }
52
- });
53
- nitro.options.runtimeConfig.graphql = defu(nitro.options.runtimeConfig.graphql || {}, {
54
- endpoint: {
55
- graphql: "/api/graphql",
56
- healthCheck: "/api/graphql/health"
57
- },
58
- playground: true
59
- });
60
- if (nitro.options.graphql?.federation?.enabled) consola.info(`Apollo Federation enabled for service: ${nitro.options.graphql.federation.serviceName || "unnamed"}`);
61
- const graphqlBuildDir = resolve(nitro.options.buildDir, "graphql");
62
- nitro.graphql.buildDir = graphqlBuildDir;
63
- const watchDirs = [];
64
- switch (nitro.options.framework.name) {
65
- case "nuxt": {
66
- nitro.graphql.dir.client = relative(nitro.options.rootDir, nitro.graphql.clientDir);
67
- nitro.graphql.dir.server = relative(nitro.options.rootDir, nitro.graphql.serverDir);
68
- watchDirs.push(nitro.graphql.clientDir);
69
- const layerServerDirs = getLayerServerDirectories(nitro);
70
- const layerAppDirs = getLayerAppDirectories(nitro);
71
- for (const layerServerDir of layerServerDirs) watchDirs.push(join(layerServerDir, "graphql"));
72
- for (const layerAppDir of layerAppDirs) watchDirs.push(join(layerAppDir, "graphql"));
73
- break;
74
- }
75
- case "nitro":
76
- nitro.graphql.dir.client = relative(nitro.options.rootDir, nitro.graphql.clientDir);
77
- nitro.graphql.dir.server = relative(nitro.options.rootDir, nitro.graphql.serverDir);
78
- watchDirs.push(nitro.graphql.clientDir);
79
- watchDirs.push(nitro.graphql.serverDir);
80
- break;
81
- default:
82
- }
83
- if (nitro.options.graphql?.externalServices?.length) {
84
- for (const service of nitro.options.graphql.externalServices) if (service.documents?.length) for (const pattern of service.documents) {
85
- if (!pattern) continue;
86
- const baseDir = pattern.split("**")[0]?.replace(/\/$/, "") || ".";
87
- const resolvedDir = resolve(nitro.options.rootDir, baseDir);
88
- if (!watchDirs.includes(resolvedDir)) watchDirs.push(resolvedDir);
89
- }
90
- }
91
- const watcher = watch(watchDirs, {
92
- persistent: true,
93
- ignoreInitial: true,
94
- ignored: [...nitro.options.ignore, ...generateLayerIgnorePatterns(nitro)]
95
- }).on("all", async (_, path) => {
96
- if (path.endsWith(".graphql") || path.endsWith(".gql")) if (path.includes(nitro.graphql.serverDir) || path.includes("server/graphql") || path.includes("server\\graphql")) {
97
- await serverTypeGeneration(nitro);
98
- await clientTypeGeneration(nitro);
99
- await nitro.hooks.callHook("dev:reload");
100
- } else await clientTypeGeneration(nitro);
101
- });
102
- nitro.hooks.hook("close", () => {
103
- watcher.close();
104
- });
105
- const tsconfigDir = dirname(resolve(nitro.options.buildDir, nitro.options.typescript.tsconfigPath));
106
- const typesDir = resolve(nitro.options.buildDir, "types");
107
- nitro.scanSchemas = await scanSchemas(nitro);
108
- nitro.scanDocuments = await scanDocs(nitro);
109
- nitro.scanResolvers = await scanResolvers(nitro);
110
- const directives = await scanDirectives(nitro);
111
- nitro.scanDirectives = directives;
112
- await generateDirectiveSchemas(nitro, directives);
113
- nitro.hooks.hook("dev:start", async () => {
114
- const schemas = await scanSchemas(nitro);
115
- nitro.scanSchemas = schemas;
116
- const resolvers = await scanResolvers(nitro);
117
- nitro.scanResolvers = resolvers;
118
- const directives$1 = await scanDirectives(nitro);
119
- nitro.scanDirectives = directives$1;
120
- await generateDirectiveSchemas(nitro, directives$1);
121
- const docs = await scanDocs(nitro);
122
- nitro.scanDocuments = docs;
123
- if (nitro.options.dev) {
124
- consola.box({
125
- title: "Nitro GraphQL",
126
- message: [
127
- `Framework: ${nitro.options.graphql?.framework || "Not configured"}`,
128
- `Schemas: ${schemas.length}`,
129
- `Resolvers: ${resolvers.length}`,
130
- `Directives: ${directives$1.length}`,
131
- `Documents: ${docs.length}`,
132
- "",
133
- "Debug Dashboard: /_nitro/graphql/debug"
134
- ].join("\n"),
135
- style: {
136
- borderColor: "cyan",
137
- borderStyle: "rounded"
138
- }
139
- });
140
- if (resolvers.length > 0) {
141
- const totalExports = resolvers.reduce((sum, r) => sum + r.imports.length, 0);
142
- const typeCount = {
143
- query: 0,
144
- mutation: 0,
145
- resolver: 0,
146
- type: 0,
147
- subscription: 0,
148
- directive: 0
149
- };
150
- for (const resolver of resolvers) for (const imp of resolver.imports) if (imp.type in typeCount) typeCount[imp.type]++;
151
- const breakdown = [];
152
- if (typeCount.query > 0) breakdown.push(`${typeCount.query} query`);
153
- if (typeCount.mutation > 0) breakdown.push(`${typeCount.mutation} mutation`);
154
- if (typeCount.resolver > 0) breakdown.push(`${typeCount.resolver} resolver`);
155
- if (typeCount.type > 0) breakdown.push(`${typeCount.type} type`);
156
- if (typeCount.subscription > 0) breakdown.push(`${typeCount.subscription} subscription`);
157
- if (typeCount.directive > 0) breakdown.push(`${typeCount.directive} directive`);
158
- if (breakdown.length > 0) consola.success(`[nitro-graphql] ${totalExports} resolver export(s): ${breakdown.join(", ")}`);
159
- } else consola.warn("[nitro-graphql] No resolvers found. Check /_nitro/graphql/debug for details.");
160
- }
161
- });
162
- await rollupConfig(nitro);
163
- await serverTypeGeneration(nitro);
164
- await clientTypeGeneration(nitro);
165
- nitro.hooks.hook("close", async () => {
166
- await serverTypeGeneration(nitro);
167
- await clientTypeGeneration(nitro);
168
- });
169
- const runtime = fileURLToPath(new URL("routes", import.meta.url));
170
- const methods = [
171
- "GET",
172
- "POST",
173
- "OPTIONS"
174
- ];
175
- if (nitro.options.graphql?.framework === "graphql-yoga") for (const method of methods) nitro.options.handlers.push({
176
- route: nitro.options.runtimeConfig.graphql?.endpoint?.graphql || "/api/graphql",
177
- handler: join(runtime, "graphql-yoga"),
178
- method
179
- });
180
- if (nitro.options.graphql?.framework === "apollo-server") for (const method of methods) nitro.options.handlers.push({
181
- route: nitro.options.runtimeConfig.graphql?.endpoint?.graphql || "/api/graphql",
182
- handler: join(runtime, "apollo-server"),
183
- method
184
- });
185
- nitro.options.handlers.push({
186
- route: nitro.options.runtimeConfig.graphql?.endpoint?.healthCheck || "/api/graphql/health",
187
- handler: join(runtime, "health"),
188
- method: "GET"
189
- });
190
- if (nitro.options.dev) {
191
- nitro.options.handlers.push({
192
- route: "/_nitro/graphql/debug",
193
- handler: join(runtime, "debug"),
194
- method: "GET"
195
- });
196
- consola.info("[nitro-graphql] Debug dashboard available at: /_nitro/graphql/debug");
197
- }
198
- if (nitro.options.imports) {
199
- nitro.options.imports.presets ??= [];
200
- nitro.options.imports.presets.push({
201
- from: fileURLToPath(new URL("utils/define", import.meta.url)),
202
- imports: [
203
- "defineResolver",
204
- "defineMutation",
205
- "defineQuery",
206
- "defineSubscription",
207
- "defineType",
208
- "defineGraphQLConfig",
209
- "defineSchema",
210
- "defineDirective"
211
- ]
212
- });
213
- }
214
- nitro.hooks.hook("rollup:before", (_, rollupConfig$1) => {
215
- const manualChunks = rollupConfig$1.output?.manualChunks;
216
- const chunkFiles = rollupConfig$1.output?.chunkFileNames;
217
- if (!rollupConfig$1.output.inlineDynamicImports) {
218
- rollupConfig$1.output.manualChunks = (id, meta) => {
219
- if (id.endsWith(".graphql") || id.endsWith(".gql")) return "schemas";
220
- if (id.endsWith(".resolver.ts")) return "resolvers";
221
- if (typeof manualChunks === "function") return manualChunks(id, meta);
222
- };
223
- rollupConfig$1.output.advancedChunks = { groups: [{
224
- name: "schemas",
225
- test: /\.(?:graphql|gql)$/
226
- }, {
227
- name: "resolvers",
228
- test: /\.resolver\.ts$/
229
- }] };
230
- }
231
- rollupConfig$1.output.chunkFileNames = (chunkInfo) => {
232
- if (chunkInfo.moduleIds && chunkInfo.moduleIds.some((id) => id.endsWith(".graphql") || id.endsWith(".resolver.ts") || id.endsWith(".gql"))) return `chunks/graphql/[name].mjs`;
233
- if (typeof chunkFiles === "function") return chunkFiles(chunkInfo);
234
- return `chunks/_/[name].mjs`;
235
- };
236
- });
237
- nitro.options.typescript.strict = true;
238
- nitro.hooks.hook("types:extend", (types) => {
239
- types.tsConfig ||= {};
240
- types.tsConfig.compilerOptions ??= {};
241
- types.tsConfig.compilerOptions.paths ??= {};
242
- types.tsConfig.compilerOptions.paths["#graphql/server"] = [relativeWithDot(tsconfigDir, join(typesDir, "nitro-graphql-server.d.ts"))];
243
- types.tsConfig.compilerOptions.paths["#graphql/client"] = [relativeWithDot(tsconfigDir, join(typesDir, "nitro-graphql-client.d.ts"))];
244
- types.tsConfig.compilerOptions.paths["#graphql/schema"] = [relativeWithDot(tsconfigDir, join(nitro.graphql.serverDir, "schema.ts"))];
245
- if (nitro.options.graphql?.externalServices?.length) for (const service of nitro.options.graphql.externalServices) types.tsConfig.compilerOptions.paths[`#graphql/client/${service.name}`] = [relativeWithDot(tsconfigDir, join(typesDir, `nitro-graphql-client-${service.name}.d.ts`))];
246
- types.tsConfig.include = types.tsConfig.include || [];
247
- types.tsConfig.include.push(relativeWithDot(tsconfigDir, join(typesDir, "nitro-graphql-server.d.ts")), relativeWithDot(tsconfigDir, join(typesDir, "nitro-graphql-client.d.ts")), relativeWithDot(tsconfigDir, join(typesDir, "graphql.d.ts")));
248
- if (nitro.options.graphql?.externalServices?.length) for (const service of nitro.options.graphql.externalServices) types.tsConfig.include.push(relativeWithDot(tsconfigDir, join(typesDir, `nitro-graphql-client-${service.name}.d.ts`)));
249
- });
250
- if (nitro.options.framework?.name === "nuxt" && nitro.options.graphql?.externalServices?.length) nitro.hooks.hook("build:before", () => {
251
- const nuxtOptions = nitro._nuxt?.options;
252
- if (nuxtOptions) nuxtOptions.nitroGraphqlExternalServices = nitro.options.graphql?.externalServices || [];
253
- });
254
- if (shouldGenerateScaffold(nitro)) {
255
- const placeholders = getDefaultPaths$1(nitro);
256
- const scaffoldConfig = getScaffoldConfig(nitro);
257
- const graphqlConfigPath = resolveFilePath(scaffoldConfig.graphqlConfig, scaffoldConfig.enabled, true, "graphql.config.ts", placeholders);
258
- if (graphqlConfigPath) writeFileIfNotExists(graphqlConfigPath, `
259
- import type { IGraphQLConfig } from 'graphql-config'
260
-
261
- export default <IGraphQLConfig> {
262
- projects: {
263
- default: {
264
- schema: [
265
- '${relativeWithDot(nitro.options.rootDir, resolve(nitro.graphql.buildDir, "schema.graphql"))}',
266
- ],
267
- documents: [
268
- '${relativeWithDot(nitro.options.rootDir, resolve(nitro.graphql.clientDir, "**/*.{graphql,js,ts,jsx,tsx}"))}',
269
- ],
270
- },
271
- },
272
- }`, "graphql.config.ts");
273
- const serverSchemaPath = resolveFilePath(scaffoldConfig.serverSchema, scaffoldConfig.enabled, true, "{serverGraphql}/schema.ts", placeholders);
274
- const serverConfigPath = resolveFilePath(scaffoldConfig.serverConfig, scaffoldConfig.enabled, true, "{serverGraphql}/config.ts", placeholders);
275
- const serverContextPath = resolveFilePath(scaffoldConfig.serverContext, scaffoldConfig.enabled, true, "{serverGraphql}/context.ts", placeholders);
276
- if (serverSchemaPath || serverConfigPath || serverContextPath) {
277
- if (!existsSync(nitro.graphql.serverDir)) mkdirSync(nitro.graphql.serverDir, { recursive: true });
278
- }
279
- if (serverSchemaPath) writeFileIfNotExists(serverSchemaPath, `export default defineSchema({
280
-
281
- })
282
- `, "server schema.ts");
283
- if (serverConfigPath) writeFileIfNotExists(serverConfigPath, `// Example GraphQL config file please change it to your needs
284
- // import * as tables from '../drizzle/schema/index'
285
- // import { useDatabase } from '../utils/useDb'
286
- import { defineGraphQLConfig } from 'nitro-graphql/utils/define'
287
-
288
- export default defineGraphQLConfig({
289
- // graphql-yoga example config
290
- // context: () => {
291
- // return {
292
- // context: {
293
- // useDatabase,
294
- // tables,
295
- // },
296
- // }
297
- // },
298
- })
299
- `, "server config.ts");
300
- if (serverContextPath) writeFileIfNotExists(serverContextPath, `// Example context definition - please change it to your needs
301
- // import type { Database } from '../utils/useDb'
302
-
303
- declare module 'h3' {
304
- interface H3EventContext {
305
- // Add your custom context properties here
306
- // useDatabase: () => Database
307
- // tables: typeof import('../drizzle/schema')
308
- // auth?: {
309
- // user?: {
310
- // id: string
311
- // role: 'admin' | 'user'
312
- // }
313
- // }
314
- }
315
- }`, "server context.ts");
316
- if (existsSync(join(nitro.graphql.serverDir, "context.d.ts"))) {
317
- consola.warn("nitro-graphql: Found context.d.ts file. Please rename it to context.ts for the new structure.");
318
- consola.info("The context file should now be context.ts instead of context.d.ts");
319
- }
320
- } else consola.info("[nitro-graphql] Scaffold file generation is disabled (library mode)");
11
+ await setupNitroGraphQL(nitro);
321
12
  }
322
13
  });
323
14
 
@@ -1,6 +1,6 @@
1
- import * as h31 from "h3";
1
+ import * as h30 from "h3";
2
2
 
3
3
  //#region src/routes/apollo-server.d.ts
4
- declare const _default: h31.EventHandlerWithFetch<h31.EventHandlerRequest, Promise<any>>;
4
+ declare const _default: h30.EventHandlerWithFetch<h30.EventHandlerRequest, Promise<any>>;
5
5
  //#endregion
6
6
  export { _default as default };
@@ -1,4 +1,4 @@
1
- import * as h35 from "h3";
1
+ import * as h33 from "h3";
2
2
 
3
3
  //#region src/routes/debug.d.ts
4
4
 
@@ -10,7 +10,7 @@ import * as h35 from "h3";
10
10
  * - /_nitro/graphql/debug - HTML dashboard
11
11
  * - /_nitro/graphql/debug?format=json - JSON API
12
12
  */
13
- declare const _default: h35.EventHandlerWithFetch<h35.EventHandlerRequest, Promise<string | {
13
+ declare const _default: h33.EventHandlerWithFetch<h33.EventHandlerRequest, Promise<string | {
14
14
  timestamp: string;
15
15
  environment: {
16
16
  dev: any;
@@ -2,7 +2,7 @@ import { moduleConfig } from "#nitro-graphql/module-config";
2
2
  import { directives } from "#nitro-graphql/server-directives";
3
3
  import { resolvers } from "#nitro-graphql/server-resolvers";
4
4
  import { schemas } from "#nitro-graphql/server-schemas";
5
- import { defineEventHandler, getQuery, setResponseHeader } from "h3";
5
+ import { defineEventHandler, getQuery } from "h3";
6
6
  import { debugInfo } from "#nitro-graphql/debug-info";
7
7
 
8
8
  //#region src/routes/debug.ts
@@ -15,10 +15,6 @@ import { debugInfo } from "#nitro-graphql/debug-info";
15
15
  * - /_nitro/graphql/debug?format=json - JSON API
16
16
  */
17
17
  var debug_default = defineEventHandler(async (event) => {
18
- if (!debugInfo.isDev) {
19
- setResponseHeader(event, "Content-Type", "text/plain");
20
- return "Debug endpoint is only available in development mode";
21
- }
22
18
  const format = getQuery(event).format || "html";
23
19
  const processedResolverFiles = debugInfo.scanned.resolverFiles.map((r) => {
24
20
  const parts = r.specifier.split("/");
@@ -94,10 +90,10 @@ var debug_default = defineEventHandler(async (event) => {
94
90
  }
95
91
  };
96
92
  if (format === "json") {
97
- setResponseHeader(event, "Content-Type", "application/json");
93
+ event.res.headers.set("Content-Type", "application/json");
98
94
  return fullDebugInfo;
99
95
  }
100
- setResponseHeader(event, "Content-Type", "text/html");
96
+ event.res.headers.set("Content-Type", "text/html");
101
97
  return generateHtmlDashboard(fullDebugInfo);
102
98
  });
103
99
  function generateHtmlDashboard(debugInfo$1) {
@@ -1,6 +1,6 @@
1
- import * as h33 from "h3";
1
+ import * as h35 from "h3";
2
2
 
3
3
  //#region src/routes/graphql-yoga.d.ts
4
- declare const _default: h33.EventHandlerWithFetch<h33.EventHandlerRequest, Promise<Response>>;
4
+ declare const _default: h35.EventHandlerWithFetch<h35.EventHandlerRequest, Promise<Response>>;
5
5
  //#endregion
6
6
  export { _default as default };
@@ -1,7 +1,7 @@
1
- import * as h30 from "h3";
1
+ import * as h31 from "h3";
2
2
 
3
3
  //#region src/routes/health.d.ts
4
- declare const _default: h30.EventHandlerWithFetch<h30.EventHandlerRequest, Promise<{
4
+ declare const _default: h31.EventHandlerWithFetch<h31.EventHandlerRequest, Promise<{
5
5
  status: string;
6
6
  message: string;
7
7
  timestamp: string;
package/dist/setup.js ADDED
@@ -0,0 +1,345 @@
1
+ import { generateDirectiveSchemas } from "./utils/directive-parser.js";
2
+ import { generateLayerIgnorePatterns, getLayerAppDirectories, getLayerServerDirectories, relativeWithDot, scanDirectives, scanDocs, scanResolvers, scanSchemas, validateExternalServices } from "./utils/index.js";
3
+ import { writeFileIfNotExists } from "./utils/file-generator.js";
4
+ import { getScaffoldConfig, getTypesConfig, resolveFilePath, shouldGenerateScaffold } from "./utils/path-resolver.js";
5
+ import { clientTypeGeneration, serverTypeGeneration } from "./utils/type-generation.js";
6
+ import { rollupConfig } from "./rollup.js";
7
+ import { existsSync, mkdirSync } from "node:fs";
8
+ import { fileURLToPath } from "node:url";
9
+ import { watch } from "chokidar";
10
+ import consola from "consola";
11
+ import defu from "defu";
12
+ import { dirname, join, relative, resolve } from "pathe";
13
+
14
+ //#region src/setup.ts
15
+ /**
16
+ * Shared setup logic for nitro-graphql module
17
+ * Used by both the direct Nitro module export and the Vite plugin's nitro: hook
18
+ */
19
+ async function setupNitroGraphQL(nitro) {
20
+ if (!nitro.options.graphql?.framework) consola.warn("No GraphQL framework specified. Please set graphql.framework to \"graphql-yoga\" or \"apollo-server\".");
21
+ if (nitro.options.graphql?.externalServices?.length) {
22
+ const validationErrors = validateExternalServices(nitro.options.graphql.externalServices);
23
+ if (validationErrors.length > 0) {
24
+ consola.error("External services configuration errors:");
25
+ for (const error of validationErrors) consola.error(` - ${error}`);
26
+ throw new Error("Invalid external services configuration");
27
+ }
28
+ consola.info(`Configured ${nitro.options.graphql.externalServices.length} external GraphQL services`);
29
+ }
30
+ const { getDefaultPaths } = await import("./utils/path-resolver.js");
31
+ const defaultPaths = getDefaultPaths(nitro);
32
+ nitro.graphql ||= {
33
+ buildDir: "",
34
+ watchDirs: [],
35
+ clientDir: defaultPaths.clientGraphql,
36
+ serverDir: defaultPaths.serverGraphql,
37
+ dir: {
38
+ build: relative(nitro.options.rootDir, nitro.options.buildDir),
39
+ client: "graphql",
40
+ server: "server"
41
+ }
42
+ };
43
+ nitro.hooks.hook("rollup:before", (_, rollupConfig$1) => {
44
+ rollupConfig$1.external = rollupConfig$1.external || [];
45
+ const codegenExternals = ["oxc-parser", "@oxc-parser"];
46
+ if (Array.isArray(rollupConfig$1.external)) rollupConfig$1.external.push(...codegenExternals);
47
+ else if (typeof rollupConfig$1.external === "function") {
48
+ const originalExternal = rollupConfig$1.external;
49
+ rollupConfig$1.external = (id, parent, isResolved) => {
50
+ if (codegenExternals.some((external) => id.includes(external))) return true;
51
+ return originalExternal(id, parent, isResolved);
52
+ };
53
+ }
54
+ });
55
+ nitro.options.runtimeConfig.graphql = defu(nitro.options.runtimeConfig.graphql || {}, {
56
+ endpoint: {
57
+ graphql: "/api/graphql",
58
+ healthCheck: "/api/graphql/health"
59
+ },
60
+ playground: true
61
+ });
62
+ if (nitro.options.graphql?.federation?.enabled) consola.info(`Apollo Federation enabled for service: ${nitro.options.graphql.federation.serviceName || "unnamed"}`);
63
+ const graphqlBuildDir = resolve(nitro.options.buildDir, "graphql");
64
+ nitro.graphql.buildDir = graphqlBuildDir;
65
+ const watchDirs = [];
66
+ switch (nitro.options.framework.name) {
67
+ case "nuxt": {
68
+ nitro.graphql.dir.client = relative(nitro.options.rootDir, nitro.graphql.clientDir);
69
+ nitro.graphql.dir.server = relative(nitro.options.rootDir, nitro.graphql.serverDir);
70
+ watchDirs.push(nitro.graphql.clientDir);
71
+ const layerServerDirs = getLayerServerDirectories(nitro);
72
+ const layerAppDirs = getLayerAppDirectories(nitro);
73
+ for (const layerServerDir of layerServerDirs) watchDirs.push(join(layerServerDir, "graphql"));
74
+ for (const layerAppDir of layerAppDirs) watchDirs.push(join(layerAppDir, "graphql"));
75
+ break;
76
+ }
77
+ case "nitro":
78
+ nitro.graphql.dir.client = relative(nitro.options.rootDir, nitro.graphql.clientDir);
79
+ nitro.graphql.dir.server = relative(nitro.options.rootDir, nitro.graphql.serverDir);
80
+ watchDirs.push(nitro.graphql.clientDir);
81
+ watchDirs.push(nitro.graphql.serverDir);
82
+ break;
83
+ default:
84
+ }
85
+ if (nitro.options.graphql?.externalServices?.length) {
86
+ for (const service of nitro.options.graphql.externalServices) if (service.documents?.length) for (const pattern of service.documents) {
87
+ if (!pattern) continue;
88
+ const baseDir = pattern.split("**")[0]?.replace(/\/$/, "") || ".";
89
+ const resolvedDir = resolve(nitro.options.rootDir, baseDir);
90
+ if (!watchDirs.includes(resolvedDir)) watchDirs.push(resolvedDir);
91
+ }
92
+ }
93
+ const watcher = watch(watchDirs, {
94
+ persistent: true,
95
+ ignoreInitial: true,
96
+ ignored: [...nitro.options.ignore, ...generateLayerIgnorePatterns(nitro)]
97
+ }).on("all", async (_, path) => {
98
+ if (path.endsWith(".graphql") || path.endsWith(".gql")) if (path.includes(nitro.graphql.serverDir) || path.includes("server/graphql") || path.includes("server\\graphql")) {
99
+ await serverTypeGeneration(nitro);
100
+ await clientTypeGeneration(nitro);
101
+ await nitro.hooks.callHook("dev:reload");
102
+ } else await clientTypeGeneration(nitro);
103
+ });
104
+ nitro.hooks.hook("close", () => {
105
+ watcher.close();
106
+ });
107
+ const tsconfigDir = dirname(resolve(nitro.options.buildDir, nitro.options.typescript.tsconfigPath));
108
+ nitro.scanSchemas = await scanSchemas(nitro);
109
+ nitro.scanDocuments = await scanDocs(nitro);
110
+ nitro.scanResolvers = await scanResolvers(nitro);
111
+ const directives = await scanDirectives(nitro);
112
+ nitro.scanDirectives = directives;
113
+ await generateDirectiveSchemas(nitro, directives);
114
+ nitro.hooks.hook("dev:start", async () => {
115
+ const schemas = await scanSchemas(nitro);
116
+ nitro.scanSchemas = schemas;
117
+ const resolvers = await scanResolvers(nitro);
118
+ nitro.scanResolvers = resolvers;
119
+ const directives$1 = await scanDirectives(nitro);
120
+ nitro.scanDirectives = directives$1;
121
+ await generateDirectiveSchemas(nitro, directives$1);
122
+ const docs = await scanDocs(nitro);
123
+ nitro.scanDocuments = docs;
124
+ if (nitro.options.dev) {
125
+ consola.box({
126
+ title: "Nitro GraphQL",
127
+ message: [
128
+ `Framework: ${nitro.options.graphql?.framework || "Not configured"}`,
129
+ `Schemas: ${schemas.length}`,
130
+ `Resolvers: ${resolvers.length}`,
131
+ `Directives: ${directives$1.length}`,
132
+ `Documents: ${docs.length}`,
133
+ "",
134
+ "Debug Dashboard: /_nitro/graphql/debug"
135
+ ].join("\n"),
136
+ style: {
137
+ borderColor: "cyan",
138
+ borderStyle: "rounded"
139
+ }
140
+ });
141
+ if (resolvers.length > 0) {
142
+ const totalExports = resolvers.reduce((sum, r) => sum + r.imports.length, 0);
143
+ const typeCount = {
144
+ query: 0,
145
+ mutation: 0,
146
+ resolver: 0,
147
+ type: 0,
148
+ subscription: 0,
149
+ directive: 0
150
+ };
151
+ for (const resolver of resolvers) for (const imp of resolver.imports) if (imp.type in typeCount) typeCount[imp.type]++;
152
+ const breakdown = [];
153
+ if (typeCount.query > 0) breakdown.push(`${typeCount.query} query`);
154
+ if (typeCount.mutation > 0) breakdown.push(`${typeCount.mutation} mutation`);
155
+ if (typeCount.resolver > 0) breakdown.push(`${typeCount.resolver} resolver`);
156
+ if (typeCount.type > 0) breakdown.push(`${typeCount.type} type`);
157
+ if (typeCount.subscription > 0) breakdown.push(`${typeCount.subscription} subscription`);
158
+ if (typeCount.directive > 0) breakdown.push(`${typeCount.directive} directive`);
159
+ if (breakdown.length > 0) consola.success(`[nitro-graphql] ${totalExports} resolver export(s): ${breakdown.join(", ")}`);
160
+ } else consola.warn("[nitro-graphql] No resolvers found. Check /_nitro/graphql/debug for details.");
161
+ }
162
+ });
163
+ await rollupConfig(nitro);
164
+ await serverTypeGeneration(nitro);
165
+ await clientTypeGeneration(nitro);
166
+ nitro.hooks.hook("close", async () => {
167
+ await serverTypeGeneration(nitro);
168
+ await clientTypeGeneration(nitro);
169
+ });
170
+ const runtime = fileURLToPath(new URL("routes", import.meta.url));
171
+ const methods = [
172
+ "GET",
173
+ "POST",
174
+ "OPTIONS"
175
+ ];
176
+ if (nitro.options.graphql?.framework === "graphql-yoga") for (const method of methods) nitro.options.handlers.push({
177
+ route: nitro.options.runtimeConfig.graphql?.endpoint?.graphql || "/api/graphql",
178
+ handler: join(runtime, "graphql-yoga"),
179
+ method
180
+ });
181
+ if (nitro.options.graphql?.framework === "apollo-server") for (const method of methods) nitro.options.handlers.push({
182
+ route: nitro.options.runtimeConfig.graphql?.endpoint?.graphql || "/api/graphql",
183
+ handler: join(runtime, "apollo-server"),
184
+ method
185
+ });
186
+ nitro.options.handlers.push({
187
+ route: nitro.options.runtimeConfig.graphql?.endpoint?.healthCheck || "/api/graphql/health",
188
+ handler: join(runtime, "health"),
189
+ method: "GET"
190
+ });
191
+ if (nitro.options.dev) {
192
+ nitro.options.handlers.push({
193
+ route: "/_nitro/graphql/debug",
194
+ handler: join(runtime, "debug"),
195
+ method: "GET"
196
+ });
197
+ consola.info("[nitro-graphql] Debug dashboard available at: /_nitro/graphql/debug");
198
+ }
199
+ if (nitro.options.imports) {
200
+ nitro.options.imports.presets ??= [];
201
+ nitro.options.imports.presets.push({
202
+ from: fileURLToPath(new URL("utils/define", import.meta.url)),
203
+ imports: [
204
+ "defineResolver",
205
+ "defineMutation",
206
+ "defineQuery",
207
+ "defineSubscription",
208
+ "defineType",
209
+ "defineGraphQLConfig",
210
+ "defineSchema",
211
+ "defineDirective"
212
+ ]
213
+ });
214
+ }
215
+ nitro.hooks.hook("rollup:before", (_, rollupConfig$1) => {
216
+ const manualChunks = rollupConfig$1.output?.manualChunks;
217
+ const chunkFiles = rollupConfig$1.output?.chunkFileNames;
218
+ if (!rollupConfig$1.output.inlineDynamicImports) {
219
+ rollupConfig$1.output.manualChunks = (id, meta) => {
220
+ if (id.endsWith(".graphql") || id.endsWith(".gql")) return "schemas";
221
+ if (id.endsWith(".resolver.ts")) return "resolvers";
222
+ if (typeof manualChunks === "function") return manualChunks(id, meta);
223
+ };
224
+ rollupConfig$1.output.advancedChunks = { groups: [{
225
+ name: "schemas",
226
+ test: /\.(?:graphql|gql)$/
227
+ }, {
228
+ name: "resolvers",
229
+ test: /\.resolver\.ts$/
230
+ }] };
231
+ }
232
+ rollupConfig$1.output.chunkFileNames = (chunkInfo) => {
233
+ if (chunkInfo.moduleIds && chunkInfo.moduleIds.some((id) => id.endsWith(".graphql") || id.endsWith(".resolver.ts") || id.endsWith(".gql"))) return `chunks/graphql/[name].mjs`;
234
+ if (typeof chunkFiles === "function") return chunkFiles(chunkInfo);
235
+ return `chunks/_/[name].mjs`;
236
+ };
237
+ });
238
+ nitro.options.typescript.strict = true;
239
+ nitro.hooks.hook("types:extend", (types) => {
240
+ types.tsConfig ||= {};
241
+ types.tsConfig.compilerOptions ??= {};
242
+ types.tsConfig.compilerOptions.paths ??= {};
243
+ const placeholders = getDefaultPaths(nitro);
244
+ const typesConfig = getTypesConfig(nitro);
245
+ const serverTypesPath = resolveFilePath(typesConfig.server, typesConfig.enabled, true, "{typesDir}/nitro-graphql-server.d.ts", placeholders);
246
+ if (serverTypesPath) types.tsConfig.compilerOptions.paths["#graphql/server"] = [relativeWithDot(tsconfigDir, serverTypesPath)];
247
+ const clientTypesPath = resolveFilePath(typesConfig.client, typesConfig.enabled, true, "{typesDir}/nitro-graphql-client.d.ts", placeholders);
248
+ if (clientTypesPath) types.tsConfig.compilerOptions.paths["#graphql/client"] = [relativeWithDot(tsconfigDir, clientTypesPath)];
249
+ types.tsConfig.compilerOptions.paths["#graphql/schema"] = [relativeWithDot(tsconfigDir, join(nitro.graphql.serverDir, "schema.ts"))];
250
+ if (nitro.options.graphql?.externalServices?.length) for (const service of nitro.options.graphql.externalServices) {
251
+ const servicePlaceholders = {
252
+ ...placeholders,
253
+ serviceName: service.name
254
+ };
255
+ const externalTypesPath = resolveFilePath(service.paths?.types ?? typesConfig.external, typesConfig.enabled, true, "{typesDir}/nitro-graphql-client-{serviceName}.d.ts", servicePlaceholders);
256
+ if (externalTypesPath) types.tsConfig.compilerOptions.paths[`#graphql/client/${service.name}`] = [relativeWithDot(tsconfigDir, externalTypesPath)];
257
+ }
258
+ types.tsConfig.include = types.tsConfig.include || [];
259
+ if (serverTypesPath) types.tsConfig.include.push(relativeWithDot(tsconfigDir, serverTypesPath));
260
+ if (clientTypesPath) types.tsConfig.include.push(relativeWithDot(tsconfigDir, clientTypesPath));
261
+ types.tsConfig.include.push(relativeWithDot(tsconfigDir, join(placeholders.typesDir, "graphql.d.ts")));
262
+ if (nitro.options.graphql?.externalServices?.length) for (const service of nitro.options.graphql.externalServices) {
263
+ const servicePlaceholders = {
264
+ ...placeholders,
265
+ serviceName: service.name
266
+ };
267
+ const externalTypesPath = resolveFilePath(service.paths?.types ?? typesConfig.external, typesConfig.enabled, true, "{typesDir}/nitro-graphql-client-{serviceName}.d.ts", servicePlaceholders);
268
+ if (externalTypesPath) types.tsConfig.include.push(relativeWithDot(tsconfigDir, externalTypesPath));
269
+ }
270
+ });
271
+ if (nitro.options.framework?.name === "nuxt" && nitro.options.graphql?.externalServices?.length) nitro.hooks.hook("build:before", () => {
272
+ const nuxtOptions = nitro._nuxt?.options;
273
+ if (nuxtOptions) nuxtOptions.nitroGraphqlExternalServices = nitro.options.graphql?.externalServices || [];
274
+ });
275
+ if (shouldGenerateScaffold(nitro)) {
276
+ const placeholders = getDefaultPaths(nitro);
277
+ const scaffoldConfig = getScaffoldConfig(nitro);
278
+ const graphqlConfigPath = resolveFilePath(scaffoldConfig.graphqlConfig, scaffoldConfig.enabled, true, "graphql.config.ts", placeholders);
279
+ if (graphqlConfigPath) writeFileIfNotExists(graphqlConfigPath, `
280
+ import type { IGraphQLConfig } from 'graphql-config'
281
+
282
+ export default <IGraphQLConfig> {
283
+ projects: {
284
+ default: {
285
+ schema: [
286
+ '${relativeWithDot(nitro.options.rootDir, resolve(nitro.graphql.buildDir, "schema.graphql"))}',
287
+ ],
288
+ documents: [
289
+ '${relativeWithDot(nitro.options.rootDir, resolve(nitro.graphql.clientDir, "**/*.{graphql,js,ts,jsx,tsx}"))}',
290
+ ],
291
+ },
292
+ },
293
+ }`, "graphql.config.ts");
294
+ const serverSchemaPath = resolveFilePath(scaffoldConfig.serverSchema, scaffoldConfig.enabled, true, "{serverGraphql}/schema.ts", placeholders);
295
+ const serverConfigPath = resolveFilePath(scaffoldConfig.serverConfig, scaffoldConfig.enabled, true, "{serverGraphql}/config.ts", placeholders);
296
+ const serverContextPath = resolveFilePath(scaffoldConfig.serverContext, scaffoldConfig.enabled, true, "{serverGraphql}/context.ts", placeholders);
297
+ if (serverSchemaPath || serverConfigPath || serverContextPath) {
298
+ if (!existsSync(nitro.graphql.serverDir)) mkdirSync(nitro.graphql.serverDir, { recursive: true });
299
+ }
300
+ if (serverSchemaPath) writeFileIfNotExists(serverSchemaPath, `export default defineSchema({
301
+
302
+ })
303
+ `, "server schema.ts");
304
+ if (serverConfigPath) writeFileIfNotExists(serverConfigPath, `// Example GraphQL config file please change it to your needs
305
+ // import * as tables from '../drizzle/schema/index'
306
+ // import { useDatabase } from '../utils/useDb'
307
+ import { defineGraphQLConfig } from 'nitro-graphql/utils/define'
308
+
309
+ export default defineGraphQLConfig({
310
+ // graphql-yoga example config
311
+ // context: () => {
312
+ // return {
313
+ // context: {
314
+ // useDatabase,
315
+ // tables,
316
+ // },
317
+ // }
318
+ // },
319
+ })
320
+ `, "server config.ts");
321
+ if (serverContextPath) writeFileIfNotExists(serverContextPath, `// Example context definition - please change it to your needs
322
+ // import type { Database } from '../utils/useDb'
323
+
324
+ declare module 'h3' {
325
+ interface H3EventContext {
326
+ // Add your custom context properties here
327
+ // useDatabase: () => Database
328
+ // tables: typeof import('../drizzle/schema')
329
+ // auth?: {
330
+ // user?: {
331
+ // id: string
332
+ // role: 'admin' | 'user'
333
+ // }
334
+ // }
335
+ }
336
+ }`, "server context.ts");
337
+ if (existsSync(join(nitro.graphql.serverDir, "context.d.ts"))) {
338
+ consola.warn("nitro-graphql: Found context.d.ts file. Please rename it to context.ts for the new structure.");
339
+ consola.info("The context file should now be context.ts instead of context.d.ts");
340
+ }
341
+ } else consola.info("[nitro-graphql] Scaffold file generation is disabled (library mode)");
342
+ }
343
+
344
+ //#endregion
345
+ export { setupNitroGraphQL };
@@ -23,14 +23,14 @@ declare function loadExternalSchema(service: ExternalGraphQLService, buildDir?:
23
23
  */
24
24
  declare function downloadAndSaveSchema(service: ExternalGraphQLService, buildDir: string): Promise<string | undefined>;
25
25
  declare function loadGraphQLDocuments(patterns: string | string[]): Promise<Source[]>;
26
- declare function generateClientTypes(schema: GraphQLSchema, docs: Source[], config?: CodegenClientConfig, sdkConfig?: GenericSdkConfig, outputPath?: string, serviceName?: string): Promise<false | {
26
+ declare function generateClientTypes(schema: GraphQLSchema, docs: Source[], config?: CodegenClientConfig, sdkConfig?: GenericSdkConfig, outputPath?: string, serviceName?: string, virtualTypesPath?: string): Promise<false | {
27
27
  types: string;
28
28
  sdk: string;
29
29
  }>;
30
30
  /**
31
31
  * Generate client types for external GraphQL service
32
32
  */
33
- declare function generateExternalClientTypes(service: ExternalGraphQLService, schema: GraphQLSchema, docs: Source[]): Promise<{
33
+ declare function generateExternalClientTypes(service: ExternalGraphQLService, schema: GraphQLSchema, docs: Source[], virtualTypesPath?: string): Promise<{
34
34
  types: string;
35
35
  sdk: string;
36
36
  } | false>;
@@ -165,7 +165,7 @@ async function loadGraphQLDocuments(patterns) {
165
165
  else throw e;
166
166
  }
167
167
  }
168
- async function generateClientTypes(schema, docs, config = {}, sdkConfig = {}, outputPath, serviceName) {
168
+ async function generateClientTypes(schema, docs, config = {}, sdkConfig = {}, outputPath, serviceName, virtualTypesPath) {
169
169
  if (docs.length === 0 && !serviceName) {
170
170
  consola$1.info("No client GraphQL files found. Skipping client type generation.");
171
171
  return false;
@@ -252,7 +252,7 @@ export function getSdk(requester: Requester): Sdk {
252
252
  typescriptOperations: { plugin: plugin$2 }
253
253
  }
254
254
  });
255
- const typesPath = serviceName ? `#graphql/client/${serviceName}` : "#graphql/client";
255
+ const typesPath = virtualTypesPath || (serviceName ? `#graphql/client/${serviceName}` : "#graphql/client");
256
256
  const sdkOutput = await preset.buildGeneratesSection({
257
257
  baseOutputDir: outputPath || "client-types.generated.ts",
258
258
  schema: parse(printSchemaWithDirectives(schema)),
@@ -282,8 +282,8 @@ export function getSdk(requester: Requester): Sdk {
282
282
  /**
283
283
  * Generate client types for external GraphQL service
284
284
  */
285
- async function generateExternalClientTypes(service, schema, docs) {
286
- return generateClientTypes(schema, docs, service.codegen?.client || {}, service.codegen?.clientSDK || {}, void 0, service.name);
285
+ async function generateExternalClientTypes(service, schema, docs, virtualTypesPath) {
286
+ return generateClientTypes(schema, docs, service.codegen?.client || {}, service.codegen?.clientSDK || {}, void 0, service.name, virtualTypesPath);
287
287
  }
288
288
 
289
289
  //#endregion
@@ -1,4 +1,4 @@
1
- import { resolve } from "pathe";
1
+ import { isAbsolute, resolve } from "pathe";
2
2
 
3
3
  //#region src/utils/path-resolver.ts
4
4
  /**
@@ -49,7 +49,7 @@ function resolveFilePath(config, categoryEnabled, topLevelEnabled, defaultPath,
49
49
  if (!shouldGenerateFile(config, categoryEnabled, topLevelEnabled)) return null;
50
50
  if (typeof config === "string") {
51
51
  const customPath = replacePlaceholders(config, placeholders);
52
- return resolve(placeholders.rootDir, customPath);
52
+ return isAbsolute(customPath) ? customPath : resolve(placeholders.rootDir, customPath);
53
53
  }
54
54
  const resolvedDefault = replacePlaceholders(defaultPath, placeholders);
55
55
  return resolve(placeholders.rootDir, resolvedDefault);
package/dist/vite.d.ts CHANGED
@@ -1,10 +1,12 @@
1
+ import { NitroGraphQLOptions } from "./types/index.js";
1
2
  import { Plugin } from "vite";
2
3
 
3
4
  //#region src/vite.d.ts
4
5
 
5
6
  /**
6
- * Vite plugin to load GraphQL files as strings
7
+ * Vite plugin to load GraphQL files as strings AND auto-register Nitro module
7
8
  * This prevents Vite from trying to parse .graphql/.gql files as JavaScript
9
+ * and automatically sets up the nitro-graphql module via the nitro: hook
8
10
  *
9
11
  * @example
10
12
  * ```ts
@@ -14,12 +16,12 @@ import { Plugin } from "vite";
14
16
  *
15
17
  * export default defineConfig({
16
18
  * plugins: [
17
- * graphql(), // Must be before nitro()
19
+ * graphql({ framework: 'graphql-yoga' }), // Auto-registers Nitro module
18
20
  * nitro()
19
21
  * ]
20
22
  * })
21
23
  * ```
22
24
  */
23
- declare function graphql(): Plugin;
25
+ declare function graphql(options?: NitroGraphQLOptions): Plugin;
24
26
  //#endregion
25
27
  export { graphql };
package/dist/vite.js CHANGED
@@ -1,9 +1,12 @@
1
+ import { setupNitroGraphQL } from "./setup.js";
2
+ import defu from "defu";
1
3
  import { readFile } from "node:fs/promises";
2
4
 
3
5
  //#region src/vite.ts
4
6
  /**
5
- * Vite plugin to load GraphQL files as strings
7
+ * Vite plugin to load GraphQL files as strings AND auto-register Nitro module
6
8
  * This prevents Vite from trying to parse .graphql/.gql files as JavaScript
9
+ * and automatically sets up the nitro-graphql module via the nitro: hook
7
10
  *
8
11
  * @example
9
12
  * ```ts
@@ -13,13 +16,13 @@ import { readFile } from "node:fs/promises";
13
16
  *
14
17
  * export default defineConfig({
15
18
  * plugins: [
16
- * graphql(), // Must be before nitro()
19
+ * graphql({ framework: 'graphql-yoga' }), // Auto-registers Nitro module
17
20
  * nitro()
18
21
  * ]
19
22
  * })
20
23
  * ```
21
24
  */
22
- function graphql() {
25
+ function graphql(options) {
23
26
  return {
24
27
  name: "nitro-graphql:vite",
25
28
  enforce: "pre",
@@ -32,7 +35,11 @@ function graphql() {
32
35
  if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return null;
33
36
  throw error;
34
37
  }
35
- }
38
+ },
39
+ nitro: { async setup(nitro) {
40
+ if (options) nitro.options.graphql = defu(nitro.options.graphql || {}, options);
41
+ await setupNitroGraphQL(nitro);
42
+ } }
36
43
  };
37
44
  }
38
45
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nitro-graphql",
3
3
  "type": "module",
4
- "version": "2.0.0-beta.11",
4
+ "version": "2.0.0-beta.13",
5
5
  "description": "GraphQL integration for Nitro",
6
6
  "license": "MIT",
7
7
  "sideEffects": false,