nitro-graphql 2.0.0-beta.32 → 2.0.0-beta.33
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/dist/codegen/client-types.mjs +9 -54
- package/dist/codegen/external-types.mjs +7 -48
- package/dist/codegen/index.d.mts +4 -11
- package/dist/codegen/index.mjs +1 -15
- package/dist/codegen/server-types.mjs +2 -14
- package/dist/constants/scalars.mjs +27 -0
- package/dist/constants.mjs +16 -1
- package/dist/rollup.d.mts +1 -7
- package/dist/rollup.mjs +12 -187
- package/dist/routes/apollo-server.d.mts +2 -2
- package/dist/routes/apollo-server.mjs +7 -51
- package/dist/routes/debug-template.d.mts +15 -0
- package/dist/routes/debug-template.mjs +385 -0
- package/dist/routes/debug.d.mts +2 -2
- package/dist/routes/debug.mjs +1 -344
- package/dist/routes/graphql-yoga.d.mts +2 -2
- package/dist/routes/graphql-yoga.mjs +7 -51
- package/dist/routes/health.d.mts +2 -2
- package/dist/setup/file-watcher.mjs +9 -5
- package/dist/setup/graphql-scanner.mjs +25 -0
- package/dist/setup/scaffold-generator.mjs +1 -1
- package/dist/setup/ts-config.mjs +1 -1
- package/dist/setup.mjs +38 -47
- package/dist/types/index.d.mts +1 -1
- package/dist/utils/client-codegen.mjs +4 -30
- package/dist/utils/codegen-plugin.d.mts +20 -0
- package/dist/utils/codegen-plugin.mjs +30 -0
- package/dist/utils/federation.d.mts +29 -0
- package/dist/utils/federation.mjs +40 -0
- package/dist/utils/file-writer.d.mts +35 -0
- package/dist/utils/file-writer.mjs +32 -0
- package/dist/utils/imports.d.mts +15 -0
- package/dist/utils/imports.mjs +25 -0
- package/dist/utils/index.d.mts +11 -38
- package/dist/utils/index.mjs +10 -287
- package/dist/utils/layers.d.mts +22 -0
- package/dist/utils/layers.mjs +28 -0
- package/dist/utils/ofetch-templates.d.mts +30 -0
- package/dist/utils/ofetch-templates.mjs +135 -0
- package/dist/utils/scanning/common.d.mts +23 -0
- package/dist/utils/scanning/common.mjs +39 -0
- package/dist/utils/scanning/directives.d.mts +11 -0
- package/dist/utils/scanning/directives.mjs +43 -0
- package/dist/utils/scanning/documents.d.mts +15 -0
- package/dist/utils/scanning/documents.mjs +46 -0
- package/dist/utils/scanning/index.d.mts +6 -0
- package/dist/utils/scanning/index.mjs +7 -0
- package/dist/utils/scanning/resolvers.d.mts +11 -0
- package/dist/utils/scanning/resolvers.mjs +100 -0
- package/dist/utils/scanning/schemas.d.mts +15 -0
- package/dist/utils/scanning/schemas.mjs +29 -0
- package/dist/utils/schema-builder.d.mts +48 -0
- package/dist/utils/schema-builder.mjs +51 -0
- package/dist/utils/server-codegen.mjs +3 -29
- package/dist/utils/type-generation.d.mts +2 -2
- package/dist/utils/type-generation.mjs +2 -2
- package/dist/utils/validation.d.mts +11 -0
- package/dist/utils/validation.mjs +34 -0
- package/dist/virtual/generators/config.d.mts +22 -0
- package/dist/virtual/generators/config.mjs +36 -0
- package/dist/virtual/generators/debug.d.mts +14 -0
- package/dist/virtual/generators/debug.mjs +53 -0
- package/dist/virtual/generators/directives.d.mts +14 -0
- package/dist/virtual/generators/directives.mjs +52 -0
- package/dist/virtual/generators/index.d.mts +6 -0
- package/dist/virtual/generators/index.mjs +7 -0
- package/dist/virtual/generators/resolvers.d.mts +14 -0
- package/dist/virtual/generators/resolvers.mjs +55 -0
- package/dist/virtual/generators/schemas.d.mts +14 -0
- package/dist/virtual/generators/schemas.mjs +43 -0
- package/package.json +73 -61
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Nitro } from "nitro/types";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/layers.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Get all Nuxt layer directories from Nitro config
|
|
7
|
+
*/
|
|
8
|
+
declare function getLayerDirectories(nitro: Nitro): string[];
|
|
9
|
+
/**
|
|
10
|
+
* Get all Nuxt layer server directories from Nitro config
|
|
11
|
+
*/
|
|
12
|
+
declare function getLayerServerDirectories(nitro: Nitro): string[];
|
|
13
|
+
/**
|
|
14
|
+
* Get all Nuxt layer app directories from Nitro config
|
|
15
|
+
*/
|
|
16
|
+
declare function getLayerAppDirectories(nitro: Nitro): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Generate layer-aware ignore patterns for auto-generated files
|
|
19
|
+
*/
|
|
20
|
+
declare function generateLayerIgnorePatterns(): string[];
|
|
21
|
+
//#endregion
|
|
22
|
+
export { generateLayerIgnorePatterns, getLayerAppDirectories, getLayerDirectories, getLayerServerDirectories };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/utils/layers.ts
|
|
2
|
+
/**
|
|
3
|
+
* Get all Nuxt layer directories from Nitro config
|
|
4
|
+
*/
|
|
5
|
+
function getLayerDirectories(nitro) {
|
|
6
|
+
return nitro.options.graphql?.layerDirectories || [];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Get all Nuxt layer server directories from Nitro config
|
|
10
|
+
*/
|
|
11
|
+
function getLayerServerDirectories(nitro) {
|
|
12
|
+
return nitro.options.graphql?.layerServerDirs || [];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Get all Nuxt layer app directories from Nitro config
|
|
16
|
+
*/
|
|
17
|
+
function getLayerAppDirectories(nitro) {
|
|
18
|
+
return nitro.options.graphql?.layerAppDirs || [];
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Generate layer-aware ignore patterns for auto-generated files
|
|
22
|
+
*/
|
|
23
|
+
function generateLayerIgnorePatterns() {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { generateLayerIgnorePatterns, getLayerAppDirectories, getLayerDirectories, getLayerServerDirectories };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/utils/ofetch-templates.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Shared ofetch template generation utilities
|
|
4
|
+
* Used by both main client and external service code generation
|
|
5
|
+
*/
|
|
6
|
+
interface OfetchTemplateOptions {
|
|
7
|
+
/** Service name (e.g., 'default', 'github') */
|
|
8
|
+
serviceName: string;
|
|
9
|
+
/** Whether to use Nuxt composables ($fetch, useRequestHeaders) */
|
|
10
|
+
isNuxt: boolean;
|
|
11
|
+
/** Endpoint URL for the GraphQL service */
|
|
12
|
+
endpoint: string;
|
|
13
|
+
/** Whether this is an external service (affects naming convention) */
|
|
14
|
+
isExternal?: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Generate ofetch client template content
|
|
18
|
+
*
|
|
19
|
+
* For main (default) service:
|
|
20
|
+
* - SDK exported as `$sdk`
|
|
21
|
+
* - Client function is `createGraphQLClient`
|
|
22
|
+
*
|
|
23
|
+
* For external services:
|
|
24
|
+
* - SDK exported as `$${serviceName}Sdk`
|
|
25
|
+
* - Client function is `create${ServiceName}GraphQLClient`
|
|
26
|
+
* - Endpoint has default value
|
|
27
|
+
*/
|
|
28
|
+
declare function generateOfetchTemplate(options: OfetchTemplateOptions): string;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { OfetchTemplateOptions, generateOfetchTemplate };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
//#region src/utils/ofetch-templates.ts
|
|
2
|
+
/**
|
|
3
|
+
* Capitalize first letter of a string
|
|
4
|
+
*/
|
|
5
|
+
function capitalize(str) {
|
|
6
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Generate ofetch client template content
|
|
10
|
+
*
|
|
11
|
+
* For main (default) service:
|
|
12
|
+
* - SDK exported as `$sdk`
|
|
13
|
+
* - Client function is `createGraphQLClient`
|
|
14
|
+
*
|
|
15
|
+
* For external services:
|
|
16
|
+
* - SDK exported as `$${serviceName}Sdk`
|
|
17
|
+
* - Client function is `create${ServiceName}GraphQLClient`
|
|
18
|
+
* - Endpoint has default value
|
|
19
|
+
*/
|
|
20
|
+
function generateOfetchTemplate(options) {
|
|
21
|
+
const { serviceName, isNuxt, endpoint, isExternal = false } = options;
|
|
22
|
+
if (isExternal) return generateExternalOfetchTemplate({
|
|
23
|
+
serviceName,
|
|
24
|
+
isNuxt,
|
|
25
|
+
endpoint
|
|
26
|
+
});
|
|
27
|
+
return generateMainOfetchTemplate({
|
|
28
|
+
isNuxt,
|
|
29
|
+
endpoint
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Generate ofetch template for main (default) GraphQL service
|
|
34
|
+
*/
|
|
35
|
+
function generateMainOfetchTemplate(options) {
|
|
36
|
+
const { isNuxt, endpoint } = options;
|
|
37
|
+
if (isNuxt) return `// This file is auto-generated once by nitro-graphql for quick start
|
|
38
|
+
// You can modify this file according to your needs
|
|
39
|
+
import type { Requester } from './sdk'
|
|
40
|
+
import { getSdk } from './sdk'
|
|
41
|
+
|
|
42
|
+
export function createGraphQLClient(endpoint: string): Requester {
|
|
43
|
+
return async <R>(doc: string, vars?: any): Promise<R> => {
|
|
44
|
+
const headers = import.meta.server ? useRequestHeaders() : undefined
|
|
45
|
+
|
|
46
|
+
const result = await $fetch(endpoint, {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
body: { query: doc, variables: vars },
|
|
49
|
+
headers: {
|
|
50
|
+
'Content-Type': 'application/json',
|
|
51
|
+
...headers,
|
|
52
|
+
},
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
return result as R
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const $sdk = getSdk(createGraphQLClient('${endpoint}'))`;
|
|
60
|
+
return `// This file is auto-generated once by nitro-graphql for quick start
|
|
61
|
+
// You can modify this file according to your needs
|
|
62
|
+
import type { Requester } from './sdk'
|
|
63
|
+
import { ofetch } from 'ofetch'
|
|
64
|
+
import { getSdk } from './sdk'
|
|
65
|
+
|
|
66
|
+
export function createGraphQLClient(endpoint: string): Requester {
|
|
67
|
+
return async <R>(doc: string, vars?: any): Promise<R> => {
|
|
68
|
+
const result = await ofetch(endpoint, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
body: { query: doc, variables: vars },
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
},
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
return result as R
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const $sdk = getSdk(createGraphQLClient('${endpoint}'))`;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Generate ofetch template for external GraphQL service
|
|
84
|
+
*/
|
|
85
|
+
function generateExternalOfetchTemplate(options) {
|
|
86
|
+
const { serviceName, isNuxt, endpoint } = options;
|
|
87
|
+
const capitalizedName = capitalize(serviceName);
|
|
88
|
+
if (isNuxt) return `// This file is auto-generated once by nitro-graphql for quick start
|
|
89
|
+
// You can modify this file according to your needs
|
|
90
|
+
import type { Sdk, Requester } from './sdk'
|
|
91
|
+
import { getSdk } from './sdk'
|
|
92
|
+
|
|
93
|
+
export function create${capitalizedName}GraphQLClient(endpoint: string = '${endpoint}'): Requester {
|
|
94
|
+
return async <R>(doc: string, vars?: any): Promise<R> => {
|
|
95
|
+
const headers = import.meta.server ? useRequestHeaders() : undefined
|
|
96
|
+
|
|
97
|
+
const result = await $fetch(endpoint, {
|
|
98
|
+
method: 'POST',
|
|
99
|
+
body: { query: doc, variables: vars },
|
|
100
|
+
headers: {
|
|
101
|
+
'Content-Type': 'application/json',
|
|
102
|
+
...headers,
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
return result as R
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export const $${serviceName}Sdk: Sdk = getSdk(create${capitalizedName}GraphQLClient())`;
|
|
111
|
+
return `// This file is auto-generated once by nitro-graphql for quick start
|
|
112
|
+
// You can modify this file according to your needs
|
|
113
|
+
import type { Sdk, Requester } from './sdk'
|
|
114
|
+
import { ofetch } from 'ofetch'
|
|
115
|
+
import { getSdk } from './sdk'
|
|
116
|
+
|
|
117
|
+
export function create${capitalizedName}GraphQLClient(endpoint: string = '${endpoint}'): Requester {
|
|
118
|
+
return async <R>(doc: string, vars?: any): Promise<R> => {
|
|
119
|
+
const result = await ofetch(endpoint, {
|
|
120
|
+
method: 'POST',
|
|
121
|
+
body: { query: doc, variables: vars },
|
|
122
|
+
headers: {
|
|
123
|
+
'Content-Type': 'application/json',
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
return result as R
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export const $${serviceName}Sdk: Sdk = getSdk(create${capitalizedName}GraphQLClient())`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
//#endregion
|
|
135
|
+
export { generateOfetchTemplate };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Nitro } from "nitro/types";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/scanning/common.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* File information returned from scanning operations
|
|
7
|
+
*/
|
|
8
|
+
interface FileInfo {
|
|
9
|
+
path: string;
|
|
10
|
+
fullPath: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Scan a directory for files matching a glob pattern
|
|
14
|
+
*/
|
|
15
|
+
declare function scanDir(nitro: Nitro, dir: string, name: string, globPattern?: string): Promise<FileInfo[]>;
|
|
16
|
+
/**
|
|
17
|
+
* Deduplicate files by fullPath
|
|
18
|
+
*/
|
|
19
|
+
declare function deduplicateFiles<T extends {
|
|
20
|
+
fullPath: string;
|
|
21
|
+
}>(files: T[]): T[];
|
|
22
|
+
//#endregion
|
|
23
|
+
export { FileInfo, deduplicateFiles, scanDir };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { GLOB_SCAN_PATTERN } from "../../constants.mjs";
|
|
2
|
+
import { join, relative } from "pathe";
|
|
3
|
+
import { glob } from "tinyglobby";
|
|
4
|
+
|
|
5
|
+
//#region src/utils/scanning/common.ts
|
|
6
|
+
/**
|
|
7
|
+
* Scan a directory for files matching a glob pattern
|
|
8
|
+
*/
|
|
9
|
+
async function scanDir(nitro, dir, name, globPattern = GLOB_SCAN_PATTERN) {
|
|
10
|
+
return (await glob(join(name, globPattern), {
|
|
11
|
+
cwd: dir,
|
|
12
|
+
dot: true,
|
|
13
|
+
ignore: nitro.options.ignore,
|
|
14
|
+
absolute: true
|
|
15
|
+
}).catch((error) => {
|
|
16
|
+
if (error?.code === "ENOTDIR") {
|
|
17
|
+
nitro.logger.warn(`Ignoring \`${join(dir, name)}\`. It must be a directory.`);
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
throw error;
|
|
21
|
+
})).map((fullPath) => ({
|
|
22
|
+
fullPath,
|
|
23
|
+
path: relative(join(dir, name), fullPath)
|
|
24
|
+
})).sort((a, b) => a.path.localeCompare(b.path));
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Deduplicate files by fullPath
|
|
28
|
+
*/
|
|
29
|
+
function deduplicateFiles(files) {
|
|
30
|
+
const seenPaths = /* @__PURE__ */ new Set();
|
|
31
|
+
return files.filter((file) => {
|
|
32
|
+
if (seenPaths.has(file.fullPath)) return false;
|
|
33
|
+
seenPaths.add(file.fullPath);
|
|
34
|
+
return true;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { deduplicateFiles, scanDir };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GenImport } from "../../types/index.mjs";
|
|
2
|
+
import { Nitro } from "nitro/types";
|
|
3
|
+
|
|
4
|
+
//#region src/utils/scanning/directives.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Scan for directive files and parse their exports
|
|
8
|
+
*/
|
|
9
|
+
declare function scanDirectives(nitro: Nitro): Promise<GenImport[]>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { scanDirectives };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getLayerServerDirectories } from "../layers.mjs";
|
|
2
|
+
import { deduplicateFiles, scanDir } from "./common.mjs";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { relative } from "pathe";
|
|
5
|
+
import { hash } from "ohash";
|
|
6
|
+
import { parseSync } from "oxc-parser";
|
|
7
|
+
|
|
8
|
+
//#region src/utils/scanning/directives.ts
|
|
9
|
+
/**
|
|
10
|
+
* Scan for directive files and parse their exports
|
|
11
|
+
*/
|
|
12
|
+
async function scanDirectives(nitro) {
|
|
13
|
+
const serverDirRelative = relative(nitro.options.rootDir, nitro.graphql.serverDir);
|
|
14
|
+
const regularFiles = await scanDir(nitro, nitro.options.rootDir, serverDirRelative, "**/*.directive.{ts,js}");
|
|
15
|
+
const layerServerDirs = getLayerServerDirectories(nitro);
|
|
16
|
+
const layerFiles = await Promise.all(layerServerDirs.map((layerServerDir) => scanDir(nitro, layerServerDir, "graphql", "**/*.directive.{ts,js}"))).then((r) => r.flat());
|
|
17
|
+
const files = deduplicateFiles([...regularFiles, ...layerFiles]);
|
|
18
|
+
const exportName = [];
|
|
19
|
+
for (const file of files) {
|
|
20
|
+
const fileContent = await readFile(file.fullPath, "utf-8");
|
|
21
|
+
const parsed = parseSync(file.fullPath, fileContent);
|
|
22
|
+
const exports = {
|
|
23
|
+
imports: [],
|
|
24
|
+
specifier: file.fullPath
|
|
25
|
+
};
|
|
26
|
+
for (const node of parsed.program.body) if (node.type === "ExportNamedDeclaration" && node.declaration && node.declaration.type === "VariableDeclaration") {
|
|
27
|
+
for (const decl of node.declaration.declarations) if (decl.type === "VariableDeclarator" && decl.init && decl.id.type === "Identifier") {
|
|
28
|
+
if (decl.init && decl.init.type === "CallExpression") {
|
|
29
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineDirective") exports.imports.push({
|
|
30
|
+
name: decl.id.name,
|
|
31
|
+
type: "directive",
|
|
32
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (exports.imports.length > 0) exportName.push(exports);
|
|
38
|
+
}
|
|
39
|
+
return exportName;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
//#endregion
|
|
43
|
+
export { scanDirectives };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Nitro } from "nitro/types";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/scanning/documents.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Scan for GraphQL client documents (.graphql) in client directory
|
|
7
|
+
* Excludes files from external service directories
|
|
8
|
+
*/
|
|
9
|
+
declare function scanDocuments(nitro: Nitro): Promise<string[]>;
|
|
10
|
+
/**
|
|
11
|
+
* Scan documents for a specific external service
|
|
12
|
+
*/
|
|
13
|
+
declare function scanExternalServiceDocs(nitro: Nitro, serviceName: string, patterns: string[]): Promise<string[]>;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { scanDocuments, scanExternalServiceDocs };
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getLayerAppDirectories } from "../layers.mjs";
|
|
2
|
+
import { deduplicateFiles, scanDir } from "./common.mjs";
|
|
3
|
+
import { glob } from "tinyglobby";
|
|
4
|
+
|
|
5
|
+
//#region src/utils/scanning/documents.ts
|
|
6
|
+
/**
|
|
7
|
+
* Scan for GraphQL client documents (.graphql) in client directory
|
|
8
|
+
* Excludes files from external service directories
|
|
9
|
+
*/
|
|
10
|
+
async function scanDocuments(nitro) {
|
|
11
|
+
const files = await scanDir(nitro, nitro.options.rootDir, nitro.graphql.dir.client, "**/*.graphql");
|
|
12
|
+
const layerAppDirs = getLayerAppDirectories(nitro);
|
|
13
|
+
const layerFiles = await Promise.all(layerAppDirs.map((layerAppDir) => scanDir(nitro, layerAppDir, "graphql", "**/*.graphql"))).then((r) => r.flat());
|
|
14
|
+
const allFiles = deduplicateFiles([...files, ...layerFiles]);
|
|
15
|
+
const externalPatterns = (nitro.options.graphql?.externalServices || []).flatMap((service) => service.documents || []);
|
|
16
|
+
return allFiles.filter((f) => !f.path.startsWith("external/")).filter((f) => {
|
|
17
|
+
const relativePath = f.path;
|
|
18
|
+
for (const pattern of externalPatterns) {
|
|
19
|
+
const clientDirPattern = `${nitro.graphql.dir.client}/`;
|
|
20
|
+
if (pattern.replace(/* @__PURE__ */ new RegExp(`^${clientDirPattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`), "").split("/")[0] === relativePath.split("/")[0]) return false;
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}).map((f) => f.fullPath);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Scan documents for a specific external service
|
|
27
|
+
*/
|
|
28
|
+
async function scanExternalServiceDocs(nitro, serviceName, patterns) {
|
|
29
|
+
if (!patterns.length) return [];
|
|
30
|
+
const files = [];
|
|
31
|
+
for (const pattern of patterns) try {
|
|
32
|
+
const serviceFiles = await glob(pattern, {
|
|
33
|
+
cwd: nitro.options.rootDir,
|
|
34
|
+
dot: true,
|
|
35
|
+
ignore: nitro.options.ignore,
|
|
36
|
+
absolute: true
|
|
37
|
+
});
|
|
38
|
+
files.push(...serviceFiles);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
nitro.logger.warn(`[graphql:${serviceName}] Error scanning documents with pattern "${pattern}":`, error);
|
|
41
|
+
}
|
|
42
|
+
return files.filter((file, index, self) => self.indexOf(file) === index);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
export { scanDocuments, scanExternalServiceDocs };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { FileInfo, deduplicateFiles, scanDir } from "./common.mjs";
|
|
2
|
+
import { scanDirectives } from "./directives.mjs";
|
|
3
|
+
import { scanDocuments, scanExternalServiceDocs } from "./documents.mjs";
|
|
4
|
+
import { scanResolvers } from "./resolvers.mjs";
|
|
5
|
+
import { scanGraphql, scanSchemas } from "./schemas.mjs";
|
|
6
|
+
export { type FileInfo, deduplicateFiles, scanDir, scanDirectives, scanDocuments, scanExternalServiceDocs, scanGraphql, scanResolvers, scanSchemas };
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { deduplicateFiles, scanDir } from "./common.mjs";
|
|
2
|
+
import { scanDirectives } from "./directives.mjs";
|
|
3
|
+
import { scanDocuments, scanExternalServiceDocs } from "./documents.mjs";
|
|
4
|
+
import { scanResolvers } from "./resolvers.mjs";
|
|
5
|
+
import { scanGraphql, scanSchemas } from "./schemas.mjs";
|
|
6
|
+
|
|
7
|
+
export { deduplicateFiles, scanDir, scanDirectives, scanDocuments, scanExternalServiceDocs, scanGraphql, scanResolvers, scanSchemas };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GenImport } from "../../types/index.mjs";
|
|
2
|
+
import { Nitro } from "nitro/types";
|
|
3
|
+
|
|
4
|
+
//#region src/utils/scanning/resolvers.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Scan for resolver files and parse their exports
|
|
8
|
+
*/
|
|
9
|
+
declare function scanResolvers(nitro: Nitro): Promise<GenImport[]>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { scanResolvers };
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { DEFINE_FUNCTIONS } from "../../constants.mjs";
|
|
2
|
+
import { getLayerServerDirectories } from "../layers.mjs";
|
|
3
|
+
import { deduplicateFiles, scanDir } from "./common.mjs";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import { basename, relative } from "pathe";
|
|
6
|
+
import { hash } from "ohash";
|
|
7
|
+
import { parseSync } from "oxc-parser";
|
|
8
|
+
|
|
9
|
+
//#region src/utils/scanning/resolvers.ts
|
|
10
|
+
/**
|
|
11
|
+
* Scan for resolver files and parse their exports
|
|
12
|
+
*/
|
|
13
|
+
async function scanResolvers(nitro) {
|
|
14
|
+
const serverDirRelative = relative(nitro.options.rootDir, nitro.graphql.serverDir);
|
|
15
|
+
const regularFiles = await scanDir(nitro, nitro.options.rootDir, serverDirRelative, "**/*.resolver.{ts,js}");
|
|
16
|
+
const layerServerDirs = getLayerServerDirectories(nitro);
|
|
17
|
+
const layerFiles = await Promise.all(layerServerDirs.map((layerServerDir) => scanDir(nitro, layerServerDir, "graphql", "**/*.resolver.{ts,js}"))).then((r) => r.flat());
|
|
18
|
+
const files = deduplicateFiles([...regularFiles, ...layerFiles]);
|
|
19
|
+
const exportName = [];
|
|
20
|
+
for (const file of files) try {
|
|
21
|
+
const fileContent = await readFile(file.fullPath, "utf-8");
|
|
22
|
+
const parsed = parseSync(file.fullPath, fileContent);
|
|
23
|
+
if (parsed.errors && parsed.errors.length > 0) {
|
|
24
|
+
if (nitro.options.dev) {
|
|
25
|
+
const fileName = basename(file.fullPath);
|
|
26
|
+
const firstError = parsed.errors[0];
|
|
27
|
+
const location = firstError?.labels?.[0];
|
|
28
|
+
const lineInfo = location ? `:${location.start}` : "";
|
|
29
|
+
const message = firstError?.message.split(",")[0] || "Syntax error";
|
|
30
|
+
console.error(`✖ ${fileName}${lineInfo} - ${message}`);
|
|
31
|
+
}
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
const exports = {
|
|
35
|
+
imports: [],
|
|
36
|
+
specifier: file.fullPath
|
|
37
|
+
};
|
|
38
|
+
let hasDefaultExport = false;
|
|
39
|
+
let hasNamedExport = false;
|
|
40
|
+
const namedExports = [];
|
|
41
|
+
for (const node of parsed.program.body) {
|
|
42
|
+
if (node.type === "ExportDefaultDeclaration") hasDefaultExport = true;
|
|
43
|
+
if (node.type === "ExportNamedDeclaration" && node.declaration && node.declaration.type === "VariableDeclaration") {
|
|
44
|
+
for (const decl of node.declaration.declarations) if (decl.type === "VariableDeclarator" && decl.init && decl.id.type === "Identifier") {
|
|
45
|
+
hasNamedExport = true;
|
|
46
|
+
namedExports.push(decl.id.name);
|
|
47
|
+
if (decl.init && decl.init.type === "CallExpression") {
|
|
48
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineResolver") exports.imports.push({
|
|
49
|
+
name: decl.id.name,
|
|
50
|
+
type: "resolver",
|
|
51
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
52
|
+
});
|
|
53
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineQuery") exports.imports.push({
|
|
54
|
+
name: decl.id.name,
|
|
55
|
+
type: "query",
|
|
56
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
57
|
+
});
|
|
58
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineMutation") exports.imports.push({
|
|
59
|
+
name: decl.id.name,
|
|
60
|
+
type: "mutation",
|
|
61
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
62
|
+
});
|
|
63
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineField") exports.imports.push({
|
|
64
|
+
name: decl.id.name,
|
|
65
|
+
type: "type",
|
|
66
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
67
|
+
});
|
|
68
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineSubscription") exports.imports.push({
|
|
69
|
+
name: decl.id.name,
|
|
70
|
+
type: "subscription",
|
|
71
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
72
|
+
});
|
|
73
|
+
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineDirective") exports.imports.push({
|
|
74
|
+
name: decl.id.name,
|
|
75
|
+
type: "directive",
|
|
76
|
+
as: `_${hash(decl.id.name + file.fullPath).replace(/-/g, "").slice(0, 6)}`
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if (nitro.options.dev) {
|
|
83
|
+
const relPath = relative(nitro.options.rootDir, file.fullPath);
|
|
84
|
+
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.`);
|
|
85
|
+
if (exports.imports.length === 0 && hasNamedExport) {
|
|
86
|
+
const validFunctions = DEFINE_FUNCTIONS.join(", ");
|
|
87
|
+
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.`);
|
|
88
|
+
}
|
|
89
|
+
if (!hasDefaultExport && !hasNamedExport) nitro.logger.warn(`[nitro-graphql] ${relPath}: No exports found. Resolver files must export resolvers using defineResolver, defineQuery, defineMutation, etc.`);
|
|
90
|
+
}
|
|
91
|
+
if (exports.imports.length > 0) exportName.push(exports);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
const relPath = relative(nitro.options.rootDir, file.fullPath);
|
|
94
|
+
nitro.logger.error(`[nitro-graphql] Failed to parse resolver file ${relPath}:`, error);
|
|
95
|
+
}
|
|
96
|
+
return exportName;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
//#endregion
|
|
100
|
+
export { scanResolvers };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Nitro } from "nitro/types";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/scanning/schemas.d.ts
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Scan for GraphQL schema files (.graphql) in server directory
|
|
7
|
+
*/
|
|
8
|
+
declare function scanSchemas(nitro: Nitro): Promise<string[]>;
|
|
9
|
+
/**
|
|
10
|
+
* Scan for GraphQL files (.graphql, .gql) in server directory
|
|
11
|
+
* Similar to scanSchemas but includes .gql extension
|
|
12
|
+
*/
|
|
13
|
+
declare function scanGraphql(nitro: Nitro): Promise<string[]>;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { scanGraphql, scanSchemas };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getLayerServerDirectories } from "../layers.mjs";
|
|
2
|
+
import { deduplicateFiles, scanDir } from "./common.mjs";
|
|
3
|
+
import { relative } from "pathe";
|
|
4
|
+
|
|
5
|
+
//#region src/utils/scanning/schemas.ts
|
|
6
|
+
/**
|
|
7
|
+
* Scan for GraphQL schema files (.graphql) in server directory
|
|
8
|
+
*/
|
|
9
|
+
async function scanSchemas(nitro) {
|
|
10
|
+
const serverDirRelative = relative(nitro.options.rootDir, nitro.graphql.serverDir);
|
|
11
|
+
const files = await scanDir(nitro, nitro.options.rootDir, serverDirRelative, "**/*.graphql");
|
|
12
|
+
const layerServerDirs = getLayerServerDirectories(nitro);
|
|
13
|
+
const layerFiles = await Promise.all(layerServerDirs.map((layerServerDir) => scanDir(nitro, layerServerDir, "graphql", "**/*.graphql"))).then((r) => r.flat());
|
|
14
|
+
return deduplicateFiles([...files, ...layerFiles]).map((f) => f.fullPath);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Scan for GraphQL files (.graphql, .gql) in server directory
|
|
18
|
+
* Similar to scanSchemas but includes .gql extension
|
|
19
|
+
*/
|
|
20
|
+
async function scanGraphql(nitro) {
|
|
21
|
+
const serverDirRelative = relative(nitro.options.rootDir, nitro.graphql.serverDir);
|
|
22
|
+
const files = await scanDir(nitro, nitro.options.rootDir, serverDirRelative, "**/*.{graphql,gql}");
|
|
23
|
+
const layerServerDirs = getLayerServerDirectories(nitro);
|
|
24
|
+
const layerFiles = await Promise.all(layerServerDirs.map((layerServerDir) => scanDir(nitro, layerServerDir, "graphql", "**/*.{graphql,gql}"))).then((r) => r.flat());
|
|
25
|
+
return deduplicateFiles([...files, ...layerFiles]).map((f) => f.fullPath);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { scanGraphql, scanSchemas };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { DirectiveDefinition } from "../types/define.mjs";
|
|
2
|
+
import { GraphQLSchema } from "graphql";
|
|
3
|
+
|
|
4
|
+
//#region src/utils/schema-builder.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Schema definition from virtual module
|
|
8
|
+
*/
|
|
9
|
+
interface SchemaDefinition {
|
|
10
|
+
def: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Resolver definition from virtual module
|
|
14
|
+
*/
|
|
15
|
+
interface ResolverDefinition {
|
|
16
|
+
resolver: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Directive wrapper from virtual module
|
|
20
|
+
*/
|
|
21
|
+
interface DirectiveWrapper {
|
|
22
|
+
directive: DirectiveDefinition;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Module configuration for federation
|
|
26
|
+
*/
|
|
27
|
+
interface ModuleConfig {
|
|
28
|
+
federation?: {
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
serviceName?: string;
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Options for creating a merged schema
|
|
35
|
+
*/
|
|
36
|
+
interface CreateMergedSchemaOptions {
|
|
37
|
+
schemas: SchemaDefinition[];
|
|
38
|
+
resolvers: ResolverDefinition[];
|
|
39
|
+
directives?: DirectiveWrapper[];
|
|
40
|
+
moduleConfig: ModuleConfig;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Create a merged GraphQL schema from schemas, resolvers, and directives
|
|
44
|
+
* Supports Apollo Federation when enabled
|
|
45
|
+
*/
|
|
46
|
+
declare function createMergedSchema(options: CreateMergedSchemaOptions): Promise<GraphQLSchema>;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { CreateMergedSchemaOptions, DirectiveWrapper, ModuleConfig, ResolverDefinition, SchemaDefinition, createMergedSchema };
|