nitro-graphql 2.0.0-beta.30 → 2.0.0-beta.32
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 +3 -3
- package/dist/codegen/client-types.d.mts +13 -0
- package/dist/codegen/client-types.mjs +176 -0
- package/dist/codegen/external-types.d.mts +12 -0
- package/dist/codegen/external-types.mjs +129 -0
- package/dist/codegen/index.d.mts +25 -0
- package/dist/codegen/index.mjs +38 -0
- package/dist/codegen/server-types.d.mts +13 -0
- package/dist/codegen/server-types.mjs +76 -0
- package/dist/codegen/validation.d.mts +13 -0
- package/dist/codegen/validation.mjs +96 -0
- package/dist/config/defaults.mjs +36 -0
- package/dist/constants.mjs +91 -0
- package/dist/define.d.mts +3 -3
- package/dist/define.mjs +3 -3
- package/dist/ecosystem/nuxt.mjs +3 -3
- package/dist/rollup.d.mts +7 -7
- package/dist/rollup.mjs +73 -73
- package/dist/routes/apollo-server.d.mts +2 -2
- package/dist/routes/debug.d.mts +2 -2
- package/dist/routes/graphql-yoga.d.mts +2 -2
- package/dist/routes/health.d.mts +2 -2
- package/dist/setup/file-watcher.mjs +80 -0
- package/dist/setup/rollup-integration.mjs +90 -0
- package/dist/setup/scaffold-generator.mjs +109 -0
- package/dist/setup/ts-config.mjs +69 -0
- package/dist/setup.d.mts +2 -2
- package/dist/setup.mjs +127 -290
- package/dist/types/index.d.mts +1 -1
- package/dist/utils/client-codegen.d.mts +1 -1
- package/dist/utils/client-codegen.mjs +5 -2
- package/dist/utils/directive-parser.d.mts +2 -1
- package/dist/utils/directive-parser.mjs +4 -2
- package/dist/utils/file-generator.mjs +1 -1
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.mjs +12 -11
- package/dist/utils/path-resolver.d.mts +2 -2
- package/dist/utils/path-resolver.mjs +2 -2
- package/dist/utils/server-codegen.mjs +4 -1
- package/dist/utils/type-generation.d.mts +6 -12
- package/dist/utils/type-generation.mjs +6 -419
- package/package.json +10 -4
package/dist/utils/index.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import { createDefaultMaskError } from "./errors.mjs";
|
|
|
3
3
|
import { readFile } from "node:fs/promises";
|
|
4
4
|
import { basename, join, relative } from "pathe";
|
|
5
5
|
import { hash } from "ohash";
|
|
6
|
-
import {
|
|
6
|
+
import { parseSync } from "oxc-parser";
|
|
7
7
|
import { glob } from "tinyglobby";
|
|
8
8
|
|
|
9
9
|
//#region src/utils/index.ts
|
|
@@ -70,20 +70,20 @@ async function scanResolvers(nitro) {
|
|
|
70
70
|
"defineResolver",
|
|
71
71
|
"defineQuery",
|
|
72
72
|
"defineMutation",
|
|
73
|
-
"
|
|
73
|
+
"defineField",
|
|
74
74
|
"defineSubscription",
|
|
75
75
|
"defineDirective"
|
|
76
76
|
];
|
|
77
77
|
for (const file of files) try {
|
|
78
78
|
const fileContent = await readFile(file.fullPath, "utf-8");
|
|
79
|
-
const parsed =
|
|
79
|
+
const parsed = parseSync(file.fullPath, fileContent);
|
|
80
80
|
if (parsed.errors && parsed.errors.length > 0) {
|
|
81
81
|
if (nitro.options.dev) {
|
|
82
82
|
const fileName = basename(file.fullPath);
|
|
83
83
|
const firstError = parsed.errors[0];
|
|
84
|
-
const location = firstError
|
|
84
|
+
const location = firstError?.labels?.[0];
|
|
85
85
|
const lineInfo = location ? `:${location.start}` : "";
|
|
86
|
-
const message = firstError
|
|
86
|
+
const message = firstError?.message.split(",")[0] || "Syntax error";
|
|
87
87
|
console.error(`✖ ${fileName}${lineInfo} - ${message}`);
|
|
88
88
|
}
|
|
89
89
|
continue;
|
|
@@ -117,7 +117,7 @@ async function scanResolvers(nitro) {
|
|
|
117
117
|
type: "mutation",
|
|
118
118
|
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
119
119
|
});
|
|
120
|
-
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "
|
|
120
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineField") exports.imports.push({
|
|
121
121
|
name: decl.id.name,
|
|
122
122
|
type: "type",
|
|
123
123
|
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
@@ -137,6 +137,7 @@ async function scanResolvers(nitro) {
|
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
139
|
if (nitro.options.dev) {
|
|
140
|
+
const relPath = relative(nitro.options.rootDir, file.fullPath);
|
|
140
141
|
if (hasDefaultExport && !hasNamedExport) nitro.logger.warn(`[nitro-graphql] ${relPath}: Using default export instead of named export. Resolvers must use named exports like "export const myResolver = defineQuery(...)". Default exports are not detected.`);
|
|
141
142
|
if (exports.imports.length === 0 && hasNamedExport) {
|
|
142
143
|
const validFunctions = VALID_DEFINE_FUNCTIONS.join(", ");
|
|
@@ -146,8 +147,8 @@ async function scanResolvers(nitro) {
|
|
|
146
147
|
}
|
|
147
148
|
if (exports.imports.length > 0) exportName.push(exports);
|
|
148
149
|
} catch (error) {
|
|
149
|
-
const relPath
|
|
150
|
-
nitro.logger.error(`[nitro-graphql] Failed to parse resolver file ${relPath
|
|
150
|
+
const relPath = relative(nitro.options.rootDir, file.fullPath);
|
|
151
|
+
nitro.logger.error(`[nitro-graphql] Failed to parse resolver file ${relPath}:`, error);
|
|
151
152
|
}
|
|
152
153
|
return exportName;
|
|
153
154
|
}
|
|
@@ -166,7 +167,7 @@ async function scanDirectives(nitro) {
|
|
|
166
167
|
const exportName = [];
|
|
167
168
|
for (const file of files) {
|
|
168
169
|
const fileContent = await readFile(file.fullPath, "utf-8");
|
|
169
|
-
const parsed =
|
|
170
|
+
const parsed = parseSync(file.fullPath, fileContent);
|
|
170
171
|
const exports = {
|
|
171
172
|
imports: [],
|
|
172
173
|
specifier: file.fullPath
|
|
@@ -199,7 +200,7 @@ async function scanSchemas(nitro) {
|
|
|
199
200
|
return true;
|
|
200
201
|
}).map((f) => f.fullPath);
|
|
201
202
|
}
|
|
202
|
-
async function
|
|
203
|
+
async function scanDocuments(nitro) {
|
|
203
204
|
const files = await scanDir(nitro, nitro.options.rootDir, nitro.graphql.dir.client, "**/*.graphql");
|
|
204
205
|
const layerAppDirs = getLayerAppDirectories(nitro);
|
|
205
206
|
const layerFiles = await Promise.all(layerAppDirs.map((layerAppDir) => scanDir(nitro, layerAppDir, "graphql", "**/*.graphql"))).then((r) => r.flat());
|
|
@@ -286,4 +287,4 @@ async function scanDir(nitro, dir, name, globPattern = GLOB_SCAN_PATTERN) {
|
|
|
286
287
|
}
|
|
287
288
|
|
|
288
289
|
//#endregion
|
|
289
|
-
export { GLOB_SCAN_PATTERN, createDefaultMaskError, directiveParser, generateDirectiveSchema, generateDirectiveSchemas, generateLayerIgnorePatterns, getImportId, getLayerAppDirectories, getLayerDirectories, getLayerServerDirectories, relativeWithDot, scanDirectives,
|
|
290
|
+
export { GLOB_SCAN_PATTERN, createDefaultMaskError, directiveParser, generateDirectiveSchema, generateDirectiveSchemas, generateLayerIgnorePatterns, getImportId, getLayerAppDirectories, getLayerDirectories, getLayerServerDirectories, relativeWithDot, scanDirectives, scanDocuments, scanExternalServiceDocs, scanGraphql, scanResolvers, scanSchemas, validateExternalServices };
|
|
@@ -53,7 +53,7 @@ declare function getClientUtilsConfig(nitro: Nitro): ClientUtilsConfig;
|
|
|
53
53
|
/**
|
|
54
54
|
* Check if SDK files should be generated (category-level check)
|
|
55
55
|
*/
|
|
56
|
-
declare function
|
|
56
|
+
declare function shouldGenerateSdk(nitro: Nitro): boolean;
|
|
57
57
|
/**
|
|
58
58
|
* Get SDK configuration (handles false case)
|
|
59
59
|
*/
|
|
@@ -67,4 +67,4 @@ declare function shouldGenerateTypes(nitro: Nitro): boolean;
|
|
|
67
67
|
*/
|
|
68
68
|
declare function getTypesConfig(nitro: Nitro): TypesConfig;
|
|
69
69
|
//#endregion
|
|
70
|
-
export { PathPlaceholders, getClientUtilsConfig, getDefaultPaths, getScaffoldConfig, getSdkConfig, getTypesConfig, replacePlaceholders, resolveFilePath, shouldGenerateClientUtils, shouldGenerateFile,
|
|
70
|
+
export { PathPlaceholders, getClientUtilsConfig, getDefaultPaths, getScaffoldConfig, getSdkConfig, getTypesConfig, replacePlaceholders, resolveFilePath, shouldGenerateClientUtils, shouldGenerateFile, shouldGenerateScaffold, shouldGenerateSdk, shouldGenerateTypes };
|
|
@@ -91,7 +91,7 @@ function getClientUtilsConfig(nitro) {
|
|
|
91
91
|
/**
|
|
92
92
|
* Check if SDK files should be generated (category-level check)
|
|
93
93
|
*/
|
|
94
|
-
function
|
|
94
|
+
function shouldGenerateSdk(nitro) {
|
|
95
95
|
const sdkConfig = nitro.options.graphql?.sdk;
|
|
96
96
|
if (sdkConfig === false) return false;
|
|
97
97
|
if (sdkConfig && sdkConfig.enabled === false) return false;
|
|
@@ -124,4 +124,4 @@ function getTypesConfig(nitro) {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
//#endregion
|
|
127
|
-
export { getClientUtilsConfig, getDefaultPaths, getScaffoldConfig, getSdkConfig, getTypesConfig, replacePlaceholders, resolveFilePath, shouldGenerateClientUtils, shouldGenerateFile,
|
|
127
|
+
export { getClientUtilsConfig, getDefaultPaths, getScaffoldConfig, getSdkConfig, getTypesConfig, replacePlaceholders, resolveFilePath, shouldGenerateClientUtils, shouldGenerateFile, shouldGenerateScaffold, shouldGenerateSdk, shouldGenerateTypes };
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { defu as defu$1 } from "defu";
|
|
2
2
|
import consola from "consola";
|
|
3
3
|
import { parse } from "graphql";
|
|
4
|
-
import { printSchemaWithDirectives } from "@graphql-tools/utils";
|
|
5
4
|
import { codegen } from "@graphql-codegen/core";
|
|
6
5
|
import * as typescriptPlugin from "@graphql-codegen/typescript";
|
|
6
|
+
import { printSchemaWithDirectives } from "@graphql-tools/utils";
|
|
7
7
|
import { CurrencyResolver, DateTimeISOResolver, DateTimeResolver, JSONObjectResolver, JSONResolver, NonEmptyStringResolver, UUIDResolver } from "graphql-scalars";
|
|
8
8
|
import * as typescriptResolversPlugin from "@graphql-codegen/typescript-resolvers";
|
|
9
9
|
|
|
10
10
|
//#region src/utils/server-codegen.ts
|
|
11
|
+
/**
|
|
12
|
+
* Plugin to add prepend comments to generated files
|
|
13
|
+
*/
|
|
11
14
|
function pluginContent(_schema, _documents, _config, _info) {
|
|
12
15
|
return {
|
|
13
16
|
prepend: [
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
}
|
|
7
|
-
declare function clientTypeGeneration(nitro: Nitro, options?: {
|
|
8
|
-
silent?: boolean;
|
|
9
|
-
isInitial?: boolean;
|
|
10
|
-
}): Promise<void>;
|
|
11
|
-
//#endregion
|
|
12
|
-
export { clientTypeGeneration, serverTypeGeneration };
|
|
1
|
+
import { generateMainClientTypes } from "../codegen/client-types.mjs";
|
|
2
|
+
import { generateExternalServicesTypes } from "../codegen/external-types.mjs";
|
|
3
|
+
import { generateServerTypes } from "../codegen/server-types.mjs";
|
|
4
|
+
import { validateNoDuplicateTypes } from "../codegen/validation.mjs";
|
|
5
|
+
import { clientTypeGeneration, serverTypeGeneration } from "../codegen/index.mjs";
|
|
6
|
+
export { clientTypeGeneration, generateExternalServicesTypes, generateMainClientTypes, generateServerTypes, serverTypeGeneration, validateNoDuplicateTypes };
|
|
@@ -1,420 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import consola from "consola";
|
|
7
|
-
import { basename, dirname, join, resolve } from "pathe";
|
|
8
|
-
import { buildSchema, parse } from "graphql";
|
|
9
|
-
import { loadFilesSync } from "@graphql-tools/load-files";
|
|
10
|
-
import { mergeTypeDefs } from "@graphql-tools/merge";
|
|
11
|
-
import { printSchemaWithDirectives } from "@graphql-tools/utils";
|
|
1
|
+
import { generateMainClientTypes } from "../codegen/client-types.mjs";
|
|
2
|
+
import { generateExternalServicesTypes } from "../codegen/external-types.mjs";
|
|
3
|
+
import { validateNoDuplicateTypes } from "../codegen/validation.mjs";
|
|
4
|
+
import { generateServerTypes } from "../codegen/server-types.mjs";
|
|
5
|
+
import { clientTypeGeneration, serverTypeGeneration } from "../codegen/index.mjs";
|
|
12
6
|
|
|
13
|
-
|
|
14
|
-
const logger = consola.withTag("nitro-graphql");
|
|
15
|
-
let buildSubgraphSchema = null;
|
|
16
|
-
async function loadFederationSupport() {
|
|
17
|
-
if (buildSubgraphSchema !== null) return buildSubgraphSchema;
|
|
18
|
-
try {
|
|
19
|
-
buildSubgraphSchema = (await import("@apollo/subgraph")).buildSubgraphSchema;
|
|
20
|
-
} catch {
|
|
21
|
-
buildSubgraphSchema = false;
|
|
22
|
-
}
|
|
23
|
-
return buildSubgraphSchema;
|
|
24
|
-
}
|
|
25
|
-
function generateGraphQLIndexFile(nitro, clientDir, externalServices = []) {
|
|
26
|
-
if (!shouldGenerateClientUtils(nitro)) return;
|
|
27
|
-
const placeholders = getDefaultPaths(nitro);
|
|
28
|
-
const clientUtilsConfig = getClientUtilsConfig(nitro);
|
|
29
|
-
const indexPath = resolveFilePath(clientUtilsConfig.index, clientUtilsConfig.enabled, true, "{clientGraphql}/index.ts", placeholders);
|
|
30
|
-
if (!indexPath) return;
|
|
31
|
-
if (!existsSync(indexPath)) {
|
|
32
|
-
let indexContent = `// This file is auto-generated once by nitro-graphql for quick start
|
|
33
|
-
// You can modify this file according to your needs
|
|
34
|
-
//
|
|
35
|
-
// Export your main GraphQL service (auto-generated)
|
|
36
|
-
export * from './default/ofetch'
|
|
37
|
-
|
|
38
|
-
// Export external GraphQL services (auto-generated for existing services)
|
|
39
|
-
// When you add new external services, don't forget to add their exports here:
|
|
40
|
-
// export * from './yourServiceName/ofetch'
|
|
41
|
-
`;
|
|
42
|
-
for (const service of externalServices) indexContent += `export * from './${service.name}/ofetch'\n`;
|
|
43
|
-
writeFileIfNotExists(indexPath, indexContent, "client index.ts");
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
function generateNuxtOfetchClient(nitro, clientDir, serviceName = "default") {
|
|
47
|
-
if (!shouldGenerateClientUtils(nitro)) return;
|
|
48
|
-
const placeholders = {
|
|
49
|
-
...getDefaultPaths(nitro),
|
|
50
|
-
serviceName
|
|
51
|
-
};
|
|
52
|
-
const clientUtilsConfig = getClientUtilsConfig(nitro);
|
|
53
|
-
const ofetchPath = resolveFilePath(clientUtilsConfig.ofetch, clientUtilsConfig.enabled, true, "{clientGraphql}/{serviceName}/ofetch.ts", placeholders);
|
|
54
|
-
if (!ofetchPath) return;
|
|
55
|
-
const serviceDir = dirname(ofetchPath);
|
|
56
|
-
if (!existsSync(serviceDir)) mkdirSync(serviceDir, { recursive: true });
|
|
57
|
-
if (existsSync(ofetchPath)) return;
|
|
58
|
-
writeFileIfNotExists(ofetchPath, nitro.options.framework?.name === "nuxt" ? `// This file is auto-generated once by nitro-graphql for quick start
|
|
59
|
-
// You can modify this file according to your needs
|
|
60
|
-
import type { Requester } from './sdk'
|
|
61
|
-
import { getSdk } from './sdk'
|
|
62
|
-
|
|
63
|
-
export function createGraphQLClient(endpoint: string): Requester {
|
|
64
|
-
return async <R>(doc: string, vars?: any): Promise<R> => {
|
|
65
|
-
const headers = import.meta.server ? useRequestHeaders() : undefined
|
|
66
|
-
|
|
67
|
-
const result = await $fetch(endpoint, {
|
|
68
|
-
method: 'POST',
|
|
69
|
-
body: { query: doc, variables: vars },
|
|
70
|
-
headers: {
|
|
71
|
-
'Content-Type': 'application/json',
|
|
72
|
-
...headers,
|
|
73
|
-
},
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
return result as R
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export const $sdk = getSdk(createGraphQLClient('/api/graphql'))` : `// This file is auto-generated once by nitro-graphql for quick start
|
|
81
|
-
// You can modify this file according to your needs
|
|
82
|
-
import type { Requester } from './sdk'
|
|
83
|
-
import { ofetch } from 'ofetch'
|
|
84
|
-
import { getSdk } from './sdk'
|
|
85
|
-
|
|
86
|
-
export function createGraphQLClient(endpoint: string): Requester {
|
|
87
|
-
return async <R>(doc: string, vars?: any): Promise<R> => {
|
|
88
|
-
const result = await ofetch(endpoint, {
|
|
89
|
-
method: 'POST',
|
|
90
|
-
body: { query: doc, variables: vars },
|
|
91
|
-
headers: {
|
|
92
|
-
'Content-Type': 'application/json',
|
|
93
|
-
},
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
return result as R
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export const $sdk = getSdk(createGraphQLClient('/api/graphql'))`, `${serviceName} ofetch.ts`);
|
|
101
|
-
}
|
|
102
|
-
function generateExternalOfetchClient(nitro, service, endpoint) {
|
|
103
|
-
if (!shouldGenerateClientUtils(nitro)) return;
|
|
104
|
-
const serviceName = service.name;
|
|
105
|
-
const placeholders = {
|
|
106
|
-
...getDefaultPaths(nitro),
|
|
107
|
-
serviceName
|
|
108
|
-
};
|
|
109
|
-
const clientUtilsConfig = getClientUtilsConfig(nitro);
|
|
110
|
-
const ofetchPath = resolveFilePath(service.paths?.ofetch ?? clientUtilsConfig.ofetch, clientUtilsConfig.enabled, true, "{clientGraphql}/{serviceName}/ofetch.ts", placeholders);
|
|
111
|
-
if (!ofetchPath) return;
|
|
112
|
-
const serviceDir = dirname(ofetchPath);
|
|
113
|
-
if (!existsSync(serviceDir)) mkdirSync(serviceDir, { recursive: true });
|
|
114
|
-
if (!existsSync(ofetchPath)) {
|
|
115
|
-
const capitalizedServiceName = serviceName.charAt(0).toUpperCase() + serviceName.slice(1);
|
|
116
|
-
writeFileIfNotExists(ofetchPath, nitro.options.framework?.name === "nuxt" ? `// This file is auto-generated once by nitro-graphql for quick start
|
|
117
|
-
// You can modify this file according to your needs
|
|
118
|
-
import type { Sdk, Requester } from './sdk'
|
|
119
|
-
import { getSdk } from './sdk'
|
|
120
|
-
|
|
121
|
-
export function create${capitalizedServiceName}GraphQLClient(endpoint: string = '${endpoint}'): Requester {
|
|
122
|
-
return async <R>(doc: string, vars?: any): Promise<R> => {
|
|
123
|
-
const headers = import.meta.server ? useRequestHeaders() : undefined
|
|
124
|
-
|
|
125
|
-
const result = await $fetch(endpoint, {
|
|
126
|
-
method: 'POST',
|
|
127
|
-
body: { query: doc, variables: vars },
|
|
128
|
-
headers: {
|
|
129
|
-
'Content-Type': 'application/json',
|
|
130
|
-
...headers,
|
|
131
|
-
},
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
return result as R
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export const $${serviceName}Sdk: Sdk = getSdk(create${capitalizedServiceName}GraphQLClient())` : `// This file is auto-generated once by nitro-graphql for quick start
|
|
139
|
-
// You can modify this file according to your needs
|
|
140
|
-
import type { Sdk, Requester } from './sdk'
|
|
141
|
-
import { ofetch } from 'ofetch'
|
|
142
|
-
import { getSdk } from './sdk'
|
|
143
|
-
|
|
144
|
-
export function create${capitalizedServiceName}GraphQLClient(endpoint: string = '${endpoint}'): Requester {
|
|
145
|
-
return async <R>(doc: string, vars?: any): Promise<R> => {
|
|
146
|
-
const result = await ofetch(endpoint, {
|
|
147
|
-
method: 'POST',
|
|
148
|
-
body: { query: doc, variables: vars },
|
|
149
|
-
headers: {
|
|
150
|
-
'Content-Type': 'application/json',
|
|
151
|
-
},
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
return result as R
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export const $${serviceName}Sdk: Sdk = getSdk(create${capitalizedServiceName}GraphQLClient())`, `${serviceName} external ofetch.ts`);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Check for duplicate type definitions using a simpler approach
|
|
163
|
-
* Try to build each schema individually - if that succeeds but merging fails, we have duplicates
|
|
164
|
-
* @returns true if validation passes, false if duplicates found
|
|
165
|
-
*/
|
|
166
|
-
function validateNoDuplicateTypes(schemas, schemaStrings) {
|
|
167
|
-
const individualSchemasByFile = /* @__PURE__ */ new Map();
|
|
168
|
-
schemaStrings.forEach((schemaContent, index) => {
|
|
169
|
-
const schemaPath = schemas[index];
|
|
170
|
-
const fileName = basename(schemaPath);
|
|
171
|
-
try {
|
|
172
|
-
parse(schemaContent);
|
|
173
|
-
individualSchemasByFile.set(fileName, schemaContent);
|
|
174
|
-
} catch (error) {
|
|
175
|
-
consola.warn(`Invalid GraphQL syntax in ${fileName}:`, error);
|
|
176
|
-
throw error;
|
|
177
|
-
}
|
|
178
|
-
});
|
|
179
|
-
try {
|
|
180
|
-
mergeTypeDefs([schemaStrings.join("\n\n")], {
|
|
181
|
-
throwOnConflict: false,
|
|
182
|
-
commentDescriptions: true,
|
|
183
|
-
sort: true
|
|
184
|
-
});
|
|
185
|
-
mergeTypeDefs([schemaStrings.join("\n\n")], {
|
|
186
|
-
throwOnConflict: true,
|
|
187
|
-
commentDescriptions: true,
|
|
188
|
-
sort: true
|
|
189
|
-
});
|
|
190
|
-
} catch (conflictError) {
|
|
191
|
-
if (conflictError?.message?.includes("already defined with a different type")) throw conflictError;
|
|
192
|
-
}
|
|
193
|
-
const typeNames = /* @__PURE__ */ new Set();
|
|
194
|
-
const duplicateTypes = [];
|
|
195
|
-
schemaStrings.forEach((schemaContent, index) => {
|
|
196
|
-
const fileName = basename(schemas[index]);
|
|
197
|
-
try {
|
|
198
|
-
parse(schemaContent).definitions.forEach((def) => {
|
|
199
|
-
if (def.kind === "ObjectTypeDefinition" || def.kind === "InterfaceTypeDefinition" || def.kind === "UnionTypeDefinition" || def.kind === "EnumTypeDefinition" || def.kind === "InputObjectTypeDefinition" || def.kind === "ScalarTypeDefinition") {
|
|
200
|
-
const typeName = def.name.value;
|
|
201
|
-
if ([
|
|
202
|
-
"String",
|
|
203
|
-
"Int",
|
|
204
|
-
"Float",
|
|
205
|
-
"Boolean",
|
|
206
|
-
"ID",
|
|
207
|
-
"DateTime",
|
|
208
|
-
"JSON"
|
|
209
|
-
].includes(typeName)) return;
|
|
210
|
-
if (typeNames.has(typeName)) {
|
|
211
|
-
const existing = duplicateTypes.find((d) => d.type === typeName);
|
|
212
|
-
if (existing) existing.files.push(fileName);
|
|
213
|
-
else {
|
|
214
|
-
const firstFile = schemas.find((_, i) => {
|
|
215
|
-
const content = schemaStrings[i];
|
|
216
|
-
if (!content) return false;
|
|
217
|
-
try {
|
|
218
|
-
return parse(content).definitions.some((d) => (d.kind === "ObjectTypeDefinition" || d.kind === "InterfaceTypeDefinition" || d.kind === "UnionTypeDefinition" || d.kind === "EnumTypeDefinition" || d.kind === "InputObjectTypeDefinition" || d.kind === "ScalarTypeDefinition") && d.name.value === typeName);
|
|
219
|
-
} catch {
|
|
220
|
-
return false;
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
duplicateTypes.push({
|
|
224
|
-
type: typeName,
|
|
225
|
-
files: [basename(firstFile || ""), fileName]
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
} else typeNames.add(typeName);
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
} catch {}
|
|
232
|
-
});
|
|
233
|
-
if (duplicateTypes.length > 0) {
|
|
234
|
-
let errorMessage = "⚠️ DUPLICATE TYPE DEFINITIONS DETECTED!\n\n";
|
|
235
|
-
duplicateTypes.forEach(({ type, files }) => {
|
|
236
|
-
errorMessage += `❌ Type "${type}" is defined in multiple files:\n`;
|
|
237
|
-
files.forEach((fileName) => {
|
|
238
|
-
const fullPath = schemas.find((path) => basename(path) === fileName) || fileName;
|
|
239
|
-
errorMessage += ` • ${fullPath}\n`;
|
|
240
|
-
});
|
|
241
|
-
errorMessage += "\n";
|
|
242
|
-
});
|
|
243
|
-
errorMessage += "💡 Each GraphQL type should only be defined once.\n";
|
|
244
|
-
errorMessage += " Consider using \"extend type\" syntax instead of duplicate definitions.\n";
|
|
245
|
-
errorMessage += `\n🔍 Found ${duplicateTypes.length} duplicate type(s): ${duplicateTypes.map((d) => d.type).join(", ")}`;
|
|
246
|
-
consola.error(errorMessage);
|
|
247
|
-
return false;
|
|
248
|
-
}
|
|
249
|
-
return true;
|
|
250
|
-
}
|
|
251
|
-
async function serverTypeGeneration(app, options = {}) {
|
|
252
|
-
try {
|
|
253
|
-
if (!shouldGenerateTypes(app)) {
|
|
254
|
-
logger.debug("Server type generation is disabled");
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
const schemas = app.scanSchemas || [];
|
|
258
|
-
if (!schemas.length) {
|
|
259
|
-
if (!options.silent) consola.info("No GraphQL definitions found for server type generation.");
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
const schemaStrings = loadFilesSync(schemas).map((schema$1) => typeof schema$1 === "string" ? schema$1 : schema$1.loc?.source?.body || "").filter(Boolean);
|
|
263
|
-
if (!validateNoDuplicateTypes(schemas, schemaStrings)) return;
|
|
264
|
-
const federationEnabled = app.options.graphql?.federation?.enabled === true;
|
|
265
|
-
const mergedSchemas = mergeTypeDefs([schemaStrings.join("\n\n")], {
|
|
266
|
-
throwOnConflict: true,
|
|
267
|
-
commentDescriptions: true,
|
|
268
|
-
sort: true
|
|
269
|
-
});
|
|
270
|
-
let schema;
|
|
271
|
-
if (federationEnabled) {
|
|
272
|
-
const buildSubgraph = await loadFederationSupport();
|
|
273
|
-
if (!buildSubgraph) throw new Error("Federation is enabled but @apollo/subgraph is not installed. Run: pnpm add @apollo/subgraph");
|
|
274
|
-
schema = buildSubgraph([{ typeDefs: parse(mergedSchemas) }]);
|
|
275
|
-
} else schema = buildSchema(mergedSchemas);
|
|
276
|
-
const data = await generateTypes(app.options.graphql?.framework || "graphql-yoga", schema, app.options.graphql ?? {});
|
|
277
|
-
const printSchema = printSchemaWithDirectives(schema);
|
|
278
|
-
const schemaPath = resolve(app.graphql.buildDir, "schema.graphql");
|
|
279
|
-
mkdirSync(dirname(schemaPath), { recursive: true });
|
|
280
|
-
writeFileSync(schemaPath, printSchema, "utf-8");
|
|
281
|
-
const placeholders = getDefaultPaths(app);
|
|
282
|
-
const typesConfig = getTypesConfig(app);
|
|
283
|
-
const serverTypesPath = resolveFilePath(typesConfig.server, typesConfig.enabled, true, "{typesDir}/nitro-graphql-server.d.ts", placeholders);
|
|
284
|
-
if (serverTypesPath) {
|
|
285
|
-
mkdirSync(dirname(serverTypesPath), { recursive: true });
|
|
286
|
-
writeFileSync(serverTypesPath, data, "utf-8");
|
|
287
|
-
if (!options.silent) logger.success(`Generated server types at: ${serverTypesPath}`);
|
|
288
|
-
}
|
|
289
|
-
} catch (error) {
|
|
290
|
-
logger.error("Server schema generation error:", error);
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
async function clientTypeGeneration(nitro, options = {}) {
|
|
294
|
-
try {
|
|
295
|
-
if (nitro.scanSchemas && nitro.scanSchemas.length > 0) await generateMainClientTypes(nitro, options);
|
|
296
|
-
if (nitro.options.graphql?.externalServices?.length) await generateExternalServicesTypes(nitro, options);
|
|
297
|
-
} catch (error) {
|
|
298
|
-
logger.error("Client schema generation error:", error);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
/**
|
|
302
|
-
* Check for old structure files and warn user about manual migration
|
|
303
|
-
*/
|
|
304
|
-
function checkOldStructure(clientDir) {
|
|
305
|
-
const oldOfetchPath = resolve(clientDir, "ofetch.ts");
|
|
306
|
-
const oldSdkPath = resolve(clientDir, "sdk.ts");
|
|
307
|
-
if (existsSync(oldOfetchPath) || existsSync(oldSdkPath)) {
|
|
308
|
-
const foundFiles = [];
|
|
309
|
-
if (existsSync(oldOfetchPath)) foundFiles.push("app/graphql/ofetch.ts");
|
|
310
|
-
if (existsSync(oldSdkPath)) foundFiles.push("app/graphql/sdk.ts");
|
|
311
|
-
consola.error(`⚠️ OLD GRAPHQL STRUCTURE DETECTED!
|
|
312
|
-
|
|
313
|
-
📁 Found old files in app/graphql/ directory that need to be moved:
|
|
314
|
-
• ${foundFiles.join("\n • ")}
|
|
315
|
-
|
|
316
|
-
🔄 Please manually move these files to the new structure:
|
|
317
|
-
• app/graphql/ofetch.ts → app/graphql/default/ofetch.ts
|
|
318
|
-
• app/graphql/sdk.ts → app/graphql/default/sdk.ts
|
|
319
|
-
|
|
320
|
-
📝 Also update your app/graphql/index.ts to include:
|
|
321
|
-
export * from './default/ofetch'
|
|
322
|
-
|
|
323
|
-
💡 After moving, update your imports to use:
|
|
324
|
-
import { $sdk } from "#graphql/client"
|
|
325
|
-
|
|
326
|
-
🚫 The old files will cause import conflicts until moved!`);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
async function generateMainClientTypes(nitro, options = {}) {
|
|
330
|
-
checkOldStructure(nitro.graphql.clientDir);
|
|
331
|
-
const docs = nitro.scanDocuments;
|
|
332
|
-
const loadDocs = await loadGraphQLDocuments(docs);
|
|
333
|
-
const schemaFilePath = join(nitro.graphql.buildDir, "schema.graphql");
|
|
334
|
-
if (!existsSync(schemaFilePath)) {
|
|
335
|
-
if (!options.silent) consola.info("Schema file not ready yet for client type generation. Server types need to be generated first.");
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
const graphqlString = readFileSync(schemaFilePath, "utf-8");
|
|
339
|
-
const federationEnabled = nitro.options.graphql?.federation?.enabled === true;
|
|
340
|
-
let schema;
|
|
341
|
-
if (federationEnabled) {
|
|
342
|
-
const buildSubgraph = await loadFederationSupport();
|
|
343
|
-
if (!buildSubgraph) throw new Error("Federation is enabled but @apollo/subgraph is not installed. Run: pnpm add @apollo/subgraph");
|
|
344
|
-
schema = buildSubgraph([{ typeDefs: parse(graphqlString) }]);
|
|
345
|
-
} else schema = buildSchema(graphqlString);
|
|
346
|
-
const types = await generateClientTypes(schema, loadDocs, nitro.options.graphql?.codegen?.client ?? {}, nitro.options.graphql?.codegen?.clientSDK ?? {}, void 0, void 0, void 0, options);
|
|
347
|
-
if (types === false) return;
|
|
348
|
-
const placeholders = getDefaultPaths(nitro);
|
|
349
|
-
const typesConfig = getTypesConfig(nitro);
|
|
350
|
-
const sdkConfig = getSdkConfig(nitro);
|
|
351
|
-
const clientTypesPath = resolveFilePath(typesConfig.client, typesConfig.enabled, true, "{typesDir}/nitro-graphql-client.d.ts", placeholders);
|
|
352
|
-
if (clientTypesPath) {
|
|
353
|
-
mkdirSync(dirname(clientTypesPath), { recursive: true });
|
|
354
|
-
writeFileSync(clientTypesPath, types.types, "utf-8");
|
|
355
|
-
if (!options.silent) logger.success(`Generated client types at: ${clientTypesPath}`);
|
|
356
|
-
}
|
|
357
|
-
const sdkPath = resolveFilePath(sdkConfig.main, sdkConfig.enabled, true, "{clientGraphql}/default/sdk.ts", placeholders);
|
|
358
|
-
if (sdkPath) {
|
|
359
|
-
mkdirSync(dirname(sdkPath), { recursive: true });
|
|
360
|
-
writeFileSync(sdkPath, types.sdk, "utf-8");
|
|
361
|
-
if (!options.silent) logger.success(`Generated SDK at: ${sdkPath}`);
|
|
362
|
-
}
|
|
363
|
-
generateNuxtOfetchClient(nitro, nitro.graphql.clientDir, "default");
|
|
364
|
-
const externalServices = nitro.options.graphql?.externalServices || [];
|
|
365
|
-
if (externalServices.length > 0) generateGraphQLIndexFile(nitro, nitro.graphql.clientDir, externalServices);
|
|
366
|
-
}
|
|
367
|
-
async function generateExternalServicesTypes(nitro, options = {}) {
|
|
368
|
-
const externalServices = nitro.options.graphql?.externalServices || [];
|
|
369
|
-
for (const service of externalServices) try {
|
|
370
|
-
if (!options.silent) consola.info(`[graphql:${service.name}] Processing external service`);
|
|
371
|
-
await downloadAndSaveSchema(service, nitro.options.buildDir);
|
|
372
|
-
const schema = await loadExternalSchema(service, nitro.options.buildDir);
|
|
373
|
-
if (!schema) {
|
|
374
|
-
consola.warn(`[graphql:${service.name}] Failed to load schema, skipping`);
|
|
375
|
-
continue;
|
|
376
|
-
}
|
|
377
|
-
const documentPatterns = service.documents || [];
|
|
378
|
-
let loadDocs = [];
|
|
379
|
-
if (documentPatterns.length > 0) try {
|
|
380
|
-
loadDocs = await loadGraphQLDocuments(documentPatterns);
|
|
381
|
-
if (!loadDocs || loadDocs.length === 0) {
|
|
382
|
-
consola.warn(`[graphql:${service.name}] No GraphQL documents found, skipping service generation`);
|
|
383
|
-
continue;
|
|
384
|
-
}
|
|
385
|
-
} catch (error) {
|
|
386
|
-
consola.warn(`[graphql:${service.name}] No documents found, skipping service generation:`, error);
|
|
387
|
-
continue;
|
|
388
|
-
}
|
|
389
|
-
const types = await generateExternalClientTypes(service, schema, loadDocs);
|
|
390
|
-
if (types === false) {
|
|
391
|
-
consola.warn(`[graphql:${service.name}] Type generation failed`);
|
|
392
|
-
continue;
|
|
393
|
-
}
|
|
394
|
-
const placeholders = {
|
|
395
|
-
...getDefaultPaths(nitro),
|
|
396
|
-
serviceName: service.name
|
|
397
|
-
};
|
|
398
|
-
const typesConfig = getTypesConfig(nitro);
|
|
399
|
-
const sdkConfig = getSdkConfig(nitro);
|
|
400
|
-
const serviceTypesPath = resolveFilePath(service.paths?.types ?? typesConfig.external, typesConfig.enabled, true, "{typesDir}/nitro-graphql-client-{serviceName}.d.ts", placeholders);
|
|
401
|
-
if (serviceTypesPath) {
|
|
402
|
-
mkdirSync(dirname(serviceTypesPath), { recursive: true });
|
|
403
|
-
writeFileSync(serviceTypesPath, types.types, "utf-8");
|
|
404
|
-
if (!options.silent) consola.success(`[graphql:${service.name}] Generated types at: ${serviceTypesPath}`);
|
|
405
|
-
}
|
|
406
|
-
const serviceSdkPath = resolveFilePath(service.paths?.sdk ?? sdkConfig.external, sdkConfig.enabled, true, "{clientGraphql}/{serviceName}/sdk.ts", placeholders);
|
|
407
|
-
if (serviceSdkPath) {
|
|
408
|
-
mkdirSync(dirname(serviceSdkPath), { recursive: true });
|
|
409
|
-
writeFileSync(serviceSdkPath, types.sdk, "utf-8");
|
|
410
|
-
if (!options.silent) consola.success(`[graphql:${service.name}] Generated SDK at: ${serviceSdkPath}`);
|
|
411
|
-
}
|
|
412
|
-
generateExternalOfetchClient(nitro, service, service.endpoint);
|
|
413
|
-
if (!options.silent) consola.success(`[graphql:${service.name}] External service types generated successfully`);
|
|
414
|
-
} catch (error) {
|
|
415
|
-
consola.error(`[graphql:${service.name}] External service generation failed:`, error);
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
//#endregion
|
|
420
|
-
export { clientTypeGeneration, serverTypeGeneration };
|
|
7
|
+
export { clientTypeGeneration, generateExternalServicesTypes, generateMainClientTypes, generateServerTypes, serverTypeGeneration, validateNoDuplicateTypes };
|
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.
|
|
4
|
+
"version": "2.0.0-beta.32",
|
|
5
5
|
"description": "GraphQL integration for Nitro",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
"graphql-scalars": "^1.25.0",
|
|
103
103
|
"knitwork": "^1.3.0",
|
|
104
104
|
"ohash": "^2.0.11",
|
|
105
|
-
"oxc-parser": "^0.
|
|
105
|
+
"oxc-parser": "^0.98.0",
|
|
106
106
|
"pathe": "^2.0.3",
|
|
107
107
|
"tinyglobby": "^0.2.15"
|
|
108
108
|
},
|
|
@@ -112,6 +112,7 @@
|
|
|
112
112
|
"@nuxt/schema": "^4.2.1",
|
|
113
113
|
"@types/node": "^24.10.1",
|
|
114
114
|
"@vitejs/devtools": "^0.0.0-alpha.16",
|
|
115
|
+
"@vitest/ui": "^3.0.0",
|
|
115
116
|
"bumpp": "^10.3.1",
|
|
116
117
|
"changelogen": "^0.6.2",
|
|
117
118
|
"crossws": "^0.4.1",
|
|
@@ -119,10 +120,11 @@
|
|
|
119
120
|
"graphql": "16.12.0",
|
|
120
121
|
"graphql-yoga": "5.16.2",
|
|
121
122
|
"nitro": "npm:nitro-nightly@latest",
|
|
122
|
-
"tsdown": "^0.16.
|
|
123
|
+
"tsdown": "^0.16.6",
|
|
123
124
|
"typescript": "^5.9.3",
|
|
124
125
|
"vite": "npm:rolldown-vite@latest",
|
|
125
|
-
"vitepress-plugin-llms": "^1.9.
|
|
126
|
+
"vitepress-plugin-llms": "^1.9.3",
|
|
127
|
+
"vitest": "^3.0.0"
|
|
126
128
|
},
|
|
127
129
|
"resolutions": {
|
|
128
130
|
"nitro-graphql": "link:."
|
|
@@ -140,6 +142,10 @@
|
|
|
140
142
|
"docs:preview": "cd .docs && pnpm preview",
|
|
141
143
|
"lint": "eslint .",
|
|
142
144
|
"lint:fix": "eslint . --fix",
|
|
145
|
+
"test": "vitest",
|
|
146
|
+
"test:ui": "vitest --ui",
|
|
147
|
+
"test:run": "vitest run",
|
|
148
|
+
"test:coverage": "vitest run --coverage",
|
|
143
149
|
"test:types": "tsc --noEmit"
|
|
144
150
|
}
|
|
145
151
|
}
|