nitro-graphql 2.0.0-beta.4 → 2.0.0-beta.6
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 +785 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +80 -26
- package/dist/rollup.js +155 -28
- package/dist/routes/debug.d.ts +15 -0
- package/dist/routes/debug.js +449 -0
- package/dist/types/index.d.ts +119 -1
- package/dist/types/standard-schema.d.ts +2 -2
- package/dist/utils/file-generator.d.ts +37 -0
- package/dist/utils/file-generator.js +72 -0
- package/dist/utils/index.js +62 -34
- package/dist/utils/path-resolver.d.ts +70 -0
- package/dist/utils/path-resolver.js +127 -0
- package/dist/utils/type-generation.js +111 -38
- package/package.json +9 -5
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
+
import consola from "consola";
|
|
3
|
+
import { dirname } from "pathe";
|
|
4
|
+
|
|
5
|
+
//#region src/utils/file-generator.ts
|
|
6
|
+
/**
|
|
7
|
+
* Safely write a file to disk, creating parent directories if needed
|
|
8
|
+
*/
|
|
9
|
+
function writeFile(filePath, content, description) {
|
|
10
|
+
try {
|
|
11
|
+
const dir = dirname(filePath);
|
|
12
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
13
|
+
writeFileSync(filePath, content, "utf-8");
|
|
14
|
+
if (description) consola.success(`[nitro-graphql] Generated: ${description}`);
|
|
15
|
+
} catch (error) {
|
|
16
|
+
consola.error(`[nitro-graphql] Failed to write file: ${filePath}`, error);
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Write a file only if it doesn't already exist
|
|
22
|
+
* Returns true if file was created, false if it already existed
|
|
23
|
+
*/
|
|
24
|
+
function writeFileIfNotExists(filePath, content, description) {
|
|
25
|
+
if (existsSync(filePath)) return false;
|
|
26
|
+
writeFile(filePath, content, description);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Write a file and always overwrite (used for generated files like SDK)
|
|
31
|
+
* This is for files that are auto-generated and should be updated on every build
|
|
32
|
+
*/
|
|
33
|
+
function writeGeneratedFile(filePath, content, description) {
|
|
34
|
+
writeFile(filePath, content, description);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Check if a path is configured and should be generated
|
|
38
|
+
* Returns the resolved path if should generate, null otherwise
|
|
39
|
+
*/
|
|
40
|
+
function getGenerationPath(resolvedPath, description) {
|
|
41
|
+
if (!resolvedPath) {
|
|
42
|
+
if (description) consola.debug(`[nitro-graphql] Skipping generation: ${description} (disabled in config)`);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
return resolvedPath;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Log skipped file generation (helpful for debugging library mode)
|
|
49
|
+
*/
|
|
50
|
+
function logSkipped(fileName, reason) {
|
|
51
|
+
const message = reason ? `Skipped ${fileName}: ${reason}` : `Skipped ${fileName}`;
|
|
52
|
+
consola.debug(`[nitro-graphql] ${message}`);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Log generated file path
|
|
56
|
+
*/
|
|
57
|
+
function logGenerated(fileName, path) {
|
|
58
|
+
consola.info(`[nitro-graphql] Generated ${fileName} at: ${path}`);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Validate that a Nitro instance has the required GraphQL configuration
|
|
62
|
+
*/
|
|
63
|
+
function validateGraphQLConfig(nitro) {
|
|
64
|
+
if (!nitro.options.graphql?.framework) {
|
|
65
|
+
consola.warn("[nitro-graphql] No GraphQL framework specified. Some features may not work correctly.");
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
//#endregion
|
|
72
|
+
export { getGenerationPath, logGenerated, logSkipped, validateGraphQLConfig, writeFile, writeFileIfNotExists, writeGeneratedFile };
|
package/dist/utils/index.js
CHANGED
|
@@ -65,50 +65,78 @@ async function scanResolvers(nitro) {
|
|
|
65
65
|
return true;
|
|
66
66
|
});
|
|
67
67
|
const exportName = [];
|
|
68
|
-
|
|
68
|
+
const VALID_DEFINE_FUNCTIONS = [
|
|
69
|
+
"defineResolver",
|
|
70
|
+
"defineQuery",
|
|
71
|
+
"defineMutation",
|
|
72
|
+
"defineType",
|
|
73
|
+
"defineSubscription",
|
|
74
|
+
"defineDirective"
|
|
75
|
+
];
|
|
76
|
+
for (const file of files) try {
|
|
69
77
|
const fileContent = await readFile(file.fullPath, "utf-8");
|
|
70
78
|
const parsed = await parseAsync(file.fullPath, fileContent);
|
|
71
79
|
const exports = {
|
|
72
80
|
imports: [],
|
|
73
81
|
specifier: file.fullPath
|
|
74
82
|
};
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
type
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
type
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
type
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
type
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
type
|
|
106
|
-
|
|
107
|
-
|
|
83
|
+
let hasDefaultExport = false;
|
|
84
|
+
let hasNamedExport = false;
|
|
85
|
+
const namedExports = [];
|
|
86
|
+
for (const node of parsed.program.body) {
|
|
87
|
+
if (node.type === "ExportDefaultDeclaration") hasDefaultExport = true;
|
|
88
|
+
if (node.type === "ExportNamedDeclaration" && node.declaration && node.declaration.type === "VariableDeclaration") {
|
|
89
|
+
for (const decl of node.declaration.declarations) if (decl.type === "VariableDeclarator" && decl.init && decl.id.type === "Identifier") {
|
|
90
|
+
hasNamedExport = true;
|
|
91
|
+
namedExports.push(decl.id.name);
|
|
92
|
+
if (decl.init && decl.init.type === "CallExpression") {
|
|
93
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineResolver") exports.imports.push({
|
|
94
|
+
name: decl.id.name,
|
|
95
|
+
type: "resolver",
|
|
96
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
97
|
+
});
|
|
98
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineQuery") exports.imports.push({
|
|
99
|
+
name: decl.id.name,
|
|
100
|
+
type: "query",
|
|
101
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
102
|
+
});
|
|
103
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineMutation") exports.imports.push({
|
|
104
|
+
name: decl.id.name,
|
|
105
|
+
type: "mutation",
|
|
106
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
107
|
+
});
|
|
108
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineType") exports.imports.push({
|
|
109
|
+
name: decl.id.name,
|
|
110
|
+
type: "type",
|
|
111
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
112
|
+
});
|
|
113
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineSubscription") exports.imports.push({
|
|
114
|
+
name: decl.id.name,
|
|
115
|
+
type: "subscription",
|
|
116
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
117
|
+
});
|
|
118
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineDirective") exports.imports.push({
|
|
119
|
+
name: decl.id.name,
|
|
120
|
+
type: "directive",
|
|
121
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
122
|
+
});
|
|
123
|
+
}
|
|
108
124
|
}
|
|
109
125
|
}
|
|
110
126
|
}
|
|
127
|
+
if (nitro.options.dev) {
|
|
128
|
+
const relPath = relative(nitro.options.rootDir, file.fullPath);
|
|
129
|
+
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.`);
|
|
130
|
+
if (exports.imports.length === 0 && hasNamedExport) {
|
|
131
|
+
const validFunctions = VALID_DEFINE_FUNCTIONS.join(", ");
|
|
132
|
+
nitro.logger.warn(`[nitro-graphql] ${relPath}: File has named exports [${namedExports.join(", ")}] but none use the required define functions (${validFunctions}). Exports will not be registered.`);
|
|
133
|
+
}
|
|
134
|
+
if (!hasDefaultExport && !hasNamedExport) nitro.logger.warn(`[nitro-graphql] ${relPath}: No exports found. Resolver files must export resolvers using defineResolver, defineQuery, defineMutation, etc.`);
|
|
135
|
+
}
|
|
111
136
|
if (exports.imports.length > 0) exportName.push(exports);
|
|
137
|
+
} catch (error) {
|
|
138
|
+
const relPath = relative(nitro.options.rootDir, file.fullPath);
|
|
139
|
+
nitro.logger.error(`[nitro-graphql] Failed to parse resolver file ${relPath}:`, error);
|
|
112
140
|
}
|
|
113
141
|
return exportName;
|
|
114
142
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { ClientUtilsConfig, FileGenerationConfig, ScaffoldConfig, SdkConfig, TypesConfig } from "../types/index.js";
|
|
2
|
+
import { Nitro } from "nitropack/types";
|
|
3
|
+
|
|
4
|
+
//#region src/utils/path-resolver.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Placeholder values for path resolution
|
|
8
|
+
*/
|
|
9
|
+
interface PathPlaceholders {
|
|
10
|
+
serviceName?: string;
|
|
11
|
+
buildDir: string;
|
|
12
|
+
rootDir: string;
|
|
13
|
+
framework: 'nuxt' | 'nitro';
|
|
14
|
+
typesDir: string;
|
|
15
|
+
serverGraphql: string;
|
|
16
|
+
clientGraphql: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Replace placeholders in a path string
|
|
20
|
+
* Supports: {serviceName}, {buildDir}, {rootDir}, {framework}, {typesDir}, {serverGraphql}, {clientGraphql}
|
|
21
|
+
*/
|
|
22
|
+
declare function replacePlaceholders(path: string, placeholders: PathPlaceholders): string;
|
|
23
|
+
/**
|
|
24
|
+
* Get default paths based on framework and user configuration
|
|
25
|
+
*/
|
|
26
|
+
declare function getDefaultPaths(nitro: Nitro): Required<PathPlaceholders>;
|
|
27
|
+
/**
|
|
28
|
+
* Check if a file should be generated based on config
|
|
29
|
+
* Returns: true if should generate, false if should skip
|
|
30
|
+
*/
|
|
31
|
+
declare function shouldGenerateFile(config: FileGenerationConfig | undefined, categoryEnabled: boolean | undefined, topLevelEnabled: boolean): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the file path based on configuration
|
|
34
|
+
* Returns: resolved absolute path or null if file should not be generated
|
|
35
|
+
*/
|
|
36
|
+
declare function resolveFilePath(config: FileGenerationConfig | undefined, categoryEnabled: boolean | undefined, topLevelEnabled: boolean, defaultPath: string, placeholders: PathPlaceholders): string | null;
|
|
37
|
+
/**
|
|
38
|
+
* Check if scaffold files should be generated (category-level check)
|
|
39
|
+
*/
|
|
40
|
+
declare function shouldGenerateScaffold(nitro: Nitro): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Get scaffold configuration (handles false case)
|
|
43
|
+
*/
|
|
44
|
+
declare function getScaffoldConfig(nitro: Nitro): ScaffoldConfig;
|
|
45
|
+
/**
|
|
46
|
+
* Check if client utilities should be generated (category-level check)
|
|
47
|
+
*/
|
|
48
|
+
declare function shouldGenerateClientUtils(nitro: Nitro): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Get client utilities configuration (handles false case)
|
|
51
|
+
*/
|
|
52
|
+
declare function getClientUtilsConfig(nitro: Nitro): ClientUtilsConfig;
|
|
53
|
+
/**
|
|
54
|
+
* Check if SDK files should be generated (category-level check)
|
|
55
|
+
*/
|
|
56
|
+
declare function shouldGenerateSDK(nitro: Nitro): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Get SDK configuration (handles false case)
|
|
59
|
+
*/
|
|
60
|
+
declare function getSdkConfig(nitro: Nitro): SdkConfig;
|
|
61
|
+
/**
|
|
62
|
+
* Check if type files should be generated (category-level check)
|
|
63
|
+
*/
|
|
64
|
+
declare function shouldGenerateTypes(nitro: Nitro): boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Get types configuration (handles false case)
|
|
67
|
+
*/
|
|
68
|
+
declare function getTypesConfig(nitro: Nitro): TypesConfig;
|
|
69
|
+
//#endregion
|
|
70
|
+
export { PathPlaceholders, getClientUtilsConfig, getDefaultPaths, getScaffoldConfig, getSdkConfig, getTypesConfig, replacePlaceholders, resolveFilePath, shouldGenerateClientUtils, shouldGenerateFile, shouldGenerateSDK, shouldGenerateScaffold, shouldGenerateTypes };
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { resolve } from "pathe";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/path-resolver.ts
|
|
4
|
+
/**
|
|
5
|
+
* Replace placeholders in a path string
|
|
6
|
+
* Supports: {serviceName}, {buildDir}, {rootDir}, {framework}, {typesDir}, {serverGraphql}, {clientGraphql}
|
|
7
|
+
*/
|
|
8
|
+
function replacePlaceholders(path, placeholders) {
|
|
9
|
+
return path.replace(/\{serviceName\}/g, placeholders.serviceName || "default").replace(/\{buildDir\}/g, placeholders.buildDir).replace(/\{rootDir\}/g, placeholders.rootDir).replace(/\{framework\}/g, placeholders.framework).replace(/\{typesDir\}/g, placeholders.typesDir).replace(/\{serverGraphql\}/g, placeholders.serverGraphql).replace(/\{clientGraphql\}/g, placeholders.clientGraphql);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get default paths based on framework and user configuration
|
|
13
|
+
*/
|
|
14
|
+
function getDefaultPaths(nitro) {
|
|
15
|
+
const isNuxt = nitro.options.framework?.name === "nuxt";
|
|
16
|
+
const rootDir = nitro.options.rootDir;
|
|
17
|
+
const buildDir = nitro.options.buildDir;
|
|
18
|
+
const pathsConfig = nitro.options.graphql?.paths || {};
|
|
19
|
+
const defaultServerGraphql = pathsConfig.serverGraphql || resolve(rootDir, "server", "graphql");
|
|
20
|
+
const defaultClientGraphql = pathsConfig.clientGraphql || resolve(rootDir, isNuxt ? "app/graphql" : "graphql");
|
|
21
|
+
const defaultBuildDir = pathsConfig.buildDir || buildDir;
|
|
22
|
+
const defaultTypesDir = pathsConfig.typesDir || resolve(defaultBuildDir, "types");
|
|
23
|
+
return {
|
|
24
|
+
serviceName: "default",
|
|
25
|
+
buildDir: defaultBuildDir,
|
|
26
|
+
rootDir,
|
|
27
|
+
framework: isNuxt ? "nuxt" : "nitro",
|
|
28
|
+
typesDir: defaultTypesDir,
|
|
29
|
+
serverGraphql: defaultServerGraphql,
|
|
30
|
+
clientGraphql: defaultClientGraphql
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if a file should be generated based on config
|
|
35
|
+
* Returns: true if should generate, false if should skip
|
|
36
|
+
*/
|
|
37
|
+
function shouldGenerateFile(config, categoryEnabled, topLevelEnabled) {
|
|
38
|
+
if (config === false) return false;
|
|
39
|
+
if (config === true || typeof config === "string") return true;
|
|
40
|
+
if (categoryEnabled === false) return false;
|
|
41
|
+
if (categoryEnabled === true) return true;
|
|
42
|
+
return topLevelEnabled;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Resolve the file path based on configuration
|
|
46
|
+
* Returns: resolved absolute path or null if file should not be generated
|
|
47
|
+
*/
|
|
48
|
+
function resolveFilePath(config, categoryEnabled, topLevelEnabled, defaultPath, placeholders) {
|
|
49
|
+
if (!shouldGenerateFile(config, categoryEnabled, topLevelEnabled)) return null;
|
|
50
|
+
if (typeof config === "string") {
|
|
51
|
+
const customPath = replacePlaceholders(config, placeholders);
|
|
52
|
+
return resolve(placeholders.rootDir, customPath);
|
|
53
|
+
}
|
|
54
|
+
const resolvedDefault = replacePlaceholders(defaultPath, placeholders);
|
|
55
|
+
return resolve(placeholders.rootDir, resolvedDefault);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check if scaffold files should be generated (category-level check)
|
|
59
|
+
*/
|
|
60
|
+
function shouldGenerateScaffold(nitro) {
|
|
61
|
+
const scaffoldConfig = nitro.options.graphql?.scaffold;
|
|
62
|
+
if (scaffoldConfig === false) return false;
|
|
63
|
+
if (scaffoldConfig && scaffoldConfig.enabled === false) return false;
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get scaffold configuration (handles false case)
|
|
68
|
+
*/
|
|
69
|
+
function getScaffoldConfig(nitro) {
|
|
70
|
+
const scaffoldConfig = nitro.options.graphql?.scaffold;
|
|
71
|
+
if (scaffoldConfig === false) return { enabled: false };
|
|
72
|
+
return scaffoldConfig || {};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Check if client utilities should be generated (category-level check)
|
|
76
|
+
*/
|
|
77
|
+
function shouldGenerateClientUtils(nitro) {
|
|
78
|
+
const clientUtilsConfig = nitro.options.graphql?.clientUtils;
|
|
79
|
+
if (clientUtilsConfig === false) return false;
|
|
80
|
+
if (clientUtilsConfig && clientUtilsConfig.enabled === false) return false;
|
|
81
|
+
return nitro.options.framework?.name === "nuxt";
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get client utilities configuration (handles false case)
|
|
85
|
+
*/
|
|
86
|
+
function getClientUtilsConfig(nitro) {
|
|
87
|
+
const clientUtilsConfig = nitro.options.graphql?.clientUtils;
|
|
88
|
+
if (clientUtilsConfig === false) return { enabled: false };
|
|
89
|
+
return clientUtilsConfig || {};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Check if SDK files should be generated (category-level check)
|
|
93
|
+
*/
|
|
94
|
+
function shouldGenerateSDK(nitro) {
|
|
95
|
+
const sdkConfig = nitro.options.graphql?.sdk;
|
|
96
|
+
if (sdkConfig === false) return false;
|
|
97
|
+
if (sdkConfig && sdkConfig.enabled === false) return false;
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Get SDK configuration (handles false case)
|
|
102
|
+
*/
|
|
103
|
+
function getSdkConfig(nitro) {
|
|
104
|
+
const sdkConfig = nitro.options.graphql?.sdk;
|
|
105
|
+
if (sdkConfig === false) return { enabled: false };
|
|
106
|
+
return sdkConfig || {};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Check if type files should be generated (category-level check)
|
|
110
|
+
*/
|
|
111
|
+
function shouldGenerateTypes(nitro) {
|
|
112
|
+
const typesConfig = nitro.options.graphql?.types;
|
|
113
|
+
if (typesConfig === false) return false;
|
|
114
|
+
if (typesConfig && typesConfig.enabled === false) return false;
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get types configuration (handles false case)
|
|
119
|
+
*/
|
|
120
|
+
function getTypesConfig(nitro) {
|
|
121
|
+
const typesConfig = nitro.options.graphql?.types;
|
|
122
|
+
if (typesConfig === false) return { enabled: false };
|
|
123
|
+
return typesConfig || {};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
//#endregion
|
|
127
|
+
export { getClientUtilsConfig, getDefaultPaths, getScaffoldConfig, getSdkConfig, getTypesConfig, replacePlaceholders, resolveFilePath, shouldGenerateClientUtils, shouldGenerateFile, shouldGenerateSDK, shouldGenerateScaffold, shouldGenerateTypes };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { downloadAndSaveSchema, generateClientTypes, generateExternalClientTypes, loadExternalSchema, loadGraphQLDocuments } from "./client-codegen.js";
|
|
2
|
+
import { writeFileIfNotExists } from "./file-generator.js";
|
|
3
|
+
import { getClientUtilsConfig, getDefaultPaths, getSdkConfig, getTypesConfig, resolveFilePath, shouldGenerateClientUtils, shouldGenerateTypes } from "./path-resolver.js";
|
|
2
4
|
import { generateTypes } from "./server-codegen.js";
|
|
3
5
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
6
|
import consola from "consola";
|
|
@@ -10,12 +12,16 @@ import { mergeTypeDefs } from "@graphql-tools/merge";
|
|
|
10
12
|
import { printSchemaWithDirectives } from "@graphql-tools/utils";
|
|
11
13
|
|
|
12
14
|
//#region src/utils/type-generation.ts
|
|
13
|
-
function generateGraphQLIndexFile(clientDir, externalServices = []) {
|
|
14
|
-
|
|
15
|
+
function generateGraphQLIndexFile(nitro, clientDir, externalServices = []) {
|
|
16
|
+
if (!shouldGenerateClientUtils(nitro)) return;
|
|
17
|
+
const placeholders = getDefaultPaths(nitro);
|
|
18
|
+
const clientUtilsConfig = getClientUtilsConfig(nitro);
|
|
19
|
+
const indexPath = resolveFilePath(clientUtilsConfig.index, clientUtilsConfig.enabled, true, "{clientGraphql}/index.ts", placeholders);
|
|
20
|
+
if (!indexPath) return;
|
|
15
21
|
if (!existsSync(indexPath)) {
|
|
16
22
|
let indexContent = `// This file is auto-generated once by nitro-graphql for quick start
|
|
17
23
|
// You can modify this file according to your needs
|
|
18
|
-
//
|
|
24
|
+
//
|
|
19
25
|
// Export your main GraphQL service (auto-generated)
|
|
20
26
|
export * from './default/ofetch'
|
|
21
27
|
|
|
@@ -24,23 +30,27 @@ export * from './default/ofetch'
|
|
|
24
30
|
// export * from './yourServiceName/ofetch'
|
|
25
31
|
`;
|
|
26
32
|
for (const service of externalServices) indexContent += `export * from './${service.name}/ofetch'\n`;
|
|
27
|
-
|
|
33
|
+
writeFileIfNotExists(indexPath, indexContent, "client index.ts");
|
|
28
34
|
}
|
|
29
35
|
}
|
|
30
|
-
function
|
|
31
|
-
|
|
32
|
-
const
|
|
36
|
+
function generateNuxtOfetchClient(nitro, clientDir, serviceName = "default") {
|
|
37
|
+
if (!shouldGenerateClientUtils(nitro)) return;
|
|
38
|
+
const placeholders = {
|
|
39
|
+
...getDefaultPaths(nitro),
|
|
40
|
+
serviceName
|
|
41
|
+
};
|
|
42
|
+
const clientUtilsConfig = getClientUtilsConfig(nitro);
|
|
43
|
+
const ofetchPath = resolveFilePath(clientUtilsConfig.ofetch, clientUtilsConfig.enabled, true, "{clientGraphql}/{serviceName}/ofetch.ts", placeholders);
|
|
44
|
+
if (!ofetchPath) return;
|
|
45
|
+
const serviceDir = dirname(ofetchPath);
|
|
33
46
|
if (!existsSync(serviceDir)) mkdirSync(serviceDir, { recursive: true });
|
|
34
47
|
if (existsSync(ofetchPath)) return;
|
|
35
|
-
|
|
36
|
-
const functionName = isDefault ? "createGraphQLClient" : `create${capitalizedServiceName}GraphQLClient`;
|
|
37
|
-
const exportName = isDefault ? "$sdk" : `$${serviceName}Sdk`;
|
|
38
|
-
writeFileSync(ofetchPath, `// This file is auto-generated once by nitro-graphql for quick start
|
|
48
|
+
writeFileIfNotExists(ofetchPath, `// This file is auto-generated once by nitro-graphql for quick start
|
|
39
49
|
// You can modify this file according to your needs
|
|
40
|
-
import type {
|
|
50
|
+
import type { Requester } from './sdk'
|
|
41
51
|
import { getSdk } from './sdk'
|
|
42
52
|
|
|
43
|
-
export function
|
|
53
|
+
export function createGraphQLClient(endpoint: string): Requester {
|
|
44
54
|
return async <R>(doc: string, vars?: any): Promise<R> => {
|
|
45
55
|
const headers = import.meta.server ? useRequestHeaders() : undefined
|
|
46
56
|
|
|
@@ -57,7 +67,46 @@ export function ${functionName}(endpoint: string${isDefault ? "" : ` = '${endpoi
|
|
|
57
67
|
}
|
|
58
68
|
}
|
|
59
69
|
|
|
60
|
-
export const $
|
|
70
|
+
export const $sdk = getSdk(createGraphQLClient('/api/graphql'))`, `${serviceName} ofetch.ts`);
|
|
71
|
+
}
|
|
72
|
+
function generateExternalOfetchClient(nitro, service, endpoint) {
|
|
73
|
+
if (!shouldGenerateClientUtils(nitro)) return;
|
|
74
|
+
const serviceName = service.name;
|
|
75
|
+
const placeholders = {
|
|
76
|
+
...getDefaultPaths(nitro),
|
|
77
|
+
serviceName
|
|
78
|
+
};
|
|
79
|
+
const clientUtilsConfig = getClientUtilsConfig(nitro);
|
|
80
|
+
const ofetchPath = resolveFilePath(service.paths?.ofetch ?? clientUtilsConfig.ofetch, clientUtilsConfig.enabled, true, "{clientGraphql}/{serviceName}/ofetch.ts", placeholders);
|
|
81
|
+
if (!ofetchPath) return;
|
|
82
|
+
const serviceDir = dirname(ofetchPath);
|
|
83
|
+
if (!existsSync(serviceDir)) mkdirSync(serviceDir, { recursive: true });
|
|
84
|
+
if (!existsSync(ofetchPath)) {
|
|
85
|
+
const capitalizedServiceName = serviceName.charAt(0).toUpperCase() + serviceName.slice(1);
|
|
86
|
+
writeFileIfNotExists(ofetchPath, `// This file is auto-generated once by nitro-graphql for quick start
|
|
87
|
+
// You can modify this file according to your needs
|
|
88
|
+
import type { Sdk, Requester } from './sdk'
|
|
89
|
+
import { getSdk } from './sdk'
|
|
90
|
+
|
|
91
|
+
export function create${capitalizedServiceName}GraphQLClient(endpoint: string = '${endpoint}'): Requester {
|
|
92
|
+
return async <R>(doc: string, vars?: any): Promise<R> => {
|
|
93
|
+
const headers = import.meta.server ? useRequestHeaders() : undefined
|
|
94
|
+
|
|
95
|
+
const result = await $fetch(endpoint, {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
body: { query: doc, variables: vars },
|
|
98
|
+
headers: {
|
|
99
|
+
'Content-Type': 'application/json',
|
|
100
|
+
...headers,
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
return result as R
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const $${serviceName}Sdk: Sdk = getSdk(create${capitalizedServiceName}GraphQLClient())`, `${serviceName} external ofetch.ts`);
|
|
109
|
+
}
|
|
61
110
|
}
|
|
62
111
|
/**
|
|
63
112
|
* Check for duplicate type definitions using a simpler approach
|
|
@@ -151,6 +200,10 @@ function validateNoDuplicateTypes(schemas, schemaStrings) {
|
|
|
151
200
|
}
|
|
152
201
|
async function serverTypeGeneration(app) {
|
|
153
202
|
try {
|
|
203
|
+
if (!shouldGenerateTypes(app)) {
|
|
204
|
+
consola.debug("[nitro-graphql] Server type generation is disabled");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
154
207
|
const schemas = app.scanSchemas || [];
|
|
155
208
|
if (!schemas.length) {
|
|
156
209
|
consola.info("No GraphQL definitions found for server type generation.");
|
|
@@ -170,9 +223,14 @@ async function serverTypeGeneration(app) {
|
|
|
170
223
|
const schemaPath = resolve(app.graphql.buildDir, "schema.graphql");
|
|
171
224
|
mkdirSync(dirname(schemaPath), { recursive: true });
|
|
172
225
|
writeFileSync(schemaPath, printSchema, "utf-8");
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
226
|
+
const placeholders = getDefaultPaths(app);
|
|
227
|
+
const typesConfig = getTypesConfig(app);
|
|
228
|
+
const serverTypesPath = resolveFilePath(typesConfig.server, typesConfig.enabled, true, "{typesDir}/nitro-graphql-server.d.ts", placeholders);
|
|
229
|
+
if (serverTypesPath) {
|
|
230
|
+
mkdirSync(dirname(serverTypesPath), { recursive: true });
|
|
231
|
+
writeFileSync(serverTypesPath, data, "utf-8");
|
|
232
|
+
consola.success(`[nitro-graphql] Generated server types at: ${serverTypesPath}`);
|
|
233
|
+
}
|
|
176
234
|
} catch (error) {
|
|
177
235
|
consola.error("Server schema generation error:", error);
|
|
178
236
|
}
|
|
@@ -225,19 +283,25 @@ async function generateMainClientTypes(nitro) {
|
|
|
225
283
|
const graphqlString = readFileSync(schemaFilePath, "utf-8");
|
|
226
284
|
const types = await generateClientTypes(nitro.options.graphql?.federation?.enabled === true ? buildSubgraphSchema([{ typeDefs: parse(graphqlString) }]) : buildSchema(graphqlString), loadDocs, nitro.options.graphql?.codegen?.client ?? {}, nitro.options.graphql?.codegen?.clientSDK ?? {});
|
|
227
285
|
if (types === false) return;
|
|
228
|
-
const
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
286
|
+
const placeholders = getDefaultPaths(nitro);
|
|
287
|
+
const typesConfig = getTypesConfig(nitro);
|
|
288
|
+
const sdkConfig = getSdkConfig(nitro);
|
|
289
|
+
const clientTypesPath = resolveFilePath(typesConfig.client, typesConfig.enabled, true, "{typesDir}/nitro-graphql-client.d.ts", placeholders);
|
|
290
|
+
if (clientTypesPath) {
|
|
291
|
+
mkdirSync(dirname(clientTypesPath), { recursive: true });
|
|
292
|
+
writeFileSync(clientTypesPath, types.types, "utf-8");
|
|
293
|
+
consola.success(`[nitro-graphql] Generated client types at: ${clientTypesPath}`);
|
|
294
|
+
}
|
|
295
|
+
const sdkPath = resolveFilePath(sdkConfig.main, sdkConfig.enabled, true, "{clientGraphql}/default/sdk.ts", placeholders);
|
|
296
|
+
if (sdkPath) {
|
|
297
|
+
mkdirSync(dirname(sdkPath), { recursive: true });
|
|
298
|
+
writeFileSync(sdkPath, types.sdk, "utf-8");
|
|
299
|
+
consola.success(`[nitro-graphql] Generated SDK at: ${sdkPath}`);
|
|
300
|
+
}
|
|
237
301
|
if (nitro.options.framework?.name === "nuxt") {
|
|
238
|
-
|
|
302
|
+
generateNuxtOfetchClient(nitro, nitro.graphql.clientDir, "default");
|
|
239
303
|
const externalServices = nitro.options.graphql?.externalServices || [];
|
|
240
|
-
generateGraphQLIndexFile(nitro.graphql.clientDir, externalServices);
|
|
304
|
+
generateGraphQLIndexFile(nitro, nitro.graphql.clientDir, externalServices);
|
|
241
305
|
}
|
|
242
306
|
}
|
|
243
307
|
async function generateExternalServicesTypes(nitro) {
|
|
@@ -267,16 +331,25 @@ async function generateExternalServicesTypes(nitro) {
|
|
|
267
331
|
consola.warn(`[graphql:${service.name}] Type generation failed`);
|
|
268
332
|
continue;
|
|
269
333
|
}
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
if (
|
|
278
|
-
|
|
279
|
-
|
|
334
|
+
const placeholders = {
|
|
335
|
+
...getDefaultPaths(nitro),
|
|
336
|
+
serviceName: service.name
|
|
337
|
+
};
|
|
338
|
+
const typesConfig = getTypesConfig(nitro);
|
|
339
|
+
const sdkConfig = getSdkConfig(nitro);
|
|
340
|
+
const serviceTypesPath = resolveFilePath(service.paths?.types ?? typesConfig.external, typesConfig.enabled, true, "{typesDir}/nitro-graphql-client-{serviceName}.d.ts", placeholders);
|
|
341
|
+
if (serviceTypesPath) {
|
|
342
|
+
mkdirSync(dirname(serviceTypesPath), { recursive: true });
|
|
343
|
+
writeFileSync(serviceTypesPath, types.types, "utf-8");
|
|
344
|
+
consola.success(`[graphql:${service.name}] Generated types at: ${serviceTypesPath}`);
|
|
345
|
+
}
|
|
346
|
+
const serviceSdkPath = resolveFilePath(service.paths?.sdk ?? sdkConfig.external, sdkConfig.enabled, true, "{clientGraphql}/{serviceName}/sdk.ts", placeholders);
|
|
347
|
+
if (serviceSdkPath) {
|
|
348
|
+
mkdirSync(dirname(serviceSdkPath), { recursive: true });
|
|
349
|
+
writeFileSync(serviceSdkPath, types.sdk, "utf-8");
|
|
350
|
+
consola.success(`[graphql:${service.name}] Generated SDK at: ${serviceSdkPath}`);
|
|
351
|
+
}
|
|
352
|
+
if (nitro.options.framework?.name === "nuxt") generateExternalOfetchClient(nitro, service, service.endpoint);
|
|
280
353
|
consola.success(`[graphql:${service.name}] External service types generated successfully`);
|
|
281
354
|
} catch (error) {
|
|
282
355
|
consola.error(`[graphql:${service.name}] External service generation failed:`, error);
|
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.6",
|
|
5
5
|
"description": "GraphQL integration for Nitro",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -111,17 +111,18 @@
|
|
|
111
111
|
"@antfu/eslint-config": "^6.0.0",
|
|
112
112
|
"@nuxt/kit": "^4.1.3",
|
|
113
113
|
"@nuxt/schema": "^4.1.3",
|
|
114
|
-
"@types/node": "^24.
|
|
114
|
+
"@types/node": "^24.9.1",
|
|
115
115
|
"bumpp": "^10.3.1",
|
|
116
116
|
"changelogen": "^0.6.2",
|
|
117
117
|
"crossws": "0.3.5",
|
|
118
|
-
"eslint": "^9.
|
|
118
|
+
"eslint": "^9.38.0",
|
|
119
119
|
"graphql": "16.11.0",
|
|
120
120
|
"graphql-yoga": "^5.16.0",
|
|
121
121
|
"h3": "^2.0.1-rc.2",
|
|
122
122
|
"nitro": "^3.0.1-alpha.0",
|
|
123
|
-
"tsdown": "^0.15.
|
|
124
|
-
"typescript": "^5.9.3"
|
|
123
|
+
"tsdown": "^0.15.9",
|
|
124
|
+
"typescript": "^5.9.3",
|
|
125
|
+
"vitepress-plugin-llms": "^1.8.1"
|
|
125
126
|
},
|
|
126
127
|
"resolutions": {
|
|
127
128
|
"nitro-graphql": "link:."
|
|
@@ -134,6 +135,9 @@
|
|
|
134
135
|
"playground:nitro": "cd playgrounds/nitro && pnpm install && pnpm dev",
|
|
135
136
|
"playground:nuxt": "cd playgrounds/nuxt && pnpm install && pnpm dev",
|
|
136
137
|
"playground:federation": "cd playgrounds/federation && pnpm install && pnpm dev",
|
|
138
|
+
"docs:dev": "cd .docs && pnpm install && pnpm dev",
|
|
139
|
+
"docs:build": "cd .docs && pnpm install && pnpm build",
|
|
140
|
+
"docs:preview": "cd .docs && pnpm preview",
|
|
137
141
|
"lint": "eslint .",
|
|
138
142
|
"lint:fix": "eslint . --fix"
|
|
139
143
|
}
|