nitro-graphql 1.2.1 → 1.2.3
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 +0 -158
- package/dist/ecosystem/nuxt.d.ts +2 -2
- package/dist/ecosystem/nuxt.js +22 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -28
- package/dist/rollup.js +0 -24
- package/dist/routes/apollo-server.d.ts +2 -2
- package/dist/routes/apollo-server.js +6 -11
- package/dist/routes/graphql-yoga.d.ts +2 -2
- package/dist/routes/graphql-yoga.js +3 -9
- package/dist/routes/health.d.ts +2 -2
- package/dist/types/index.d.ts +1 -2
- package/dist/utils/client-codegen.js +104 -32
- package/dist/utils/define.d.ts +1 -52
- package/dist/utils/define.js +1 -33
- package/dist/utils/index.d.ts +2 -3
- package/dist/utils/index.js +2 -29
- package/dist/utils/type-generation.js +8 -2
- package/package.json +1 -1
- package/dist/utils/directive-parser.d.ts +0 -80
- package/dist/utils/directive-parser.js +0 -235
package/README.md
CHANGED
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
- 🔄 **Hot Reload**: Development mode with automatic schema and resolver updates
|
|
31
31
|
- 📦 **Optimized Bundling**: Smart chunking and dynamic imports for production
|
|
32
32
|
- 🌐 **Nuxt Integration**: First-class Nuxt.js support with dedicated module
|
|
33
|
-
- 🎭 **Custom Directives**: Create reusable GraphQL directives with automatic schema generation
|
|
34
33
|
- 🔗 **Multi-Service Support**: Connect to multiple external GraphQL APIs alongside your main server
|
|
35
34
|
|
|
36
35
|
## 🎯 Used Projects
|
|
@@ -196,10 +195,6 @@ server/
|
|
|
196
195
|
├── graphql/
|
|
197
196
|
│ ├── schema.graphql # Main schema with scalars and base types
|
|
198
197
|
│ ├── hello.resolver.ts # Global resolvers (use named exports)
|
|
199
|
-
│ ├── directives/ # Custom GraphQL directives
|
|
200
|
-
│ │ ├── auth.directive.ts # Authentication directive
|
|
201
|
-
│ │ ├── cache.directive.ts # Caching directive
|
|
202
|
-
│ │ └── validate.directive.ts # Validation directive
|
|
203
198
|
│ ├── users/
|
|
204
199
|
│ │ ├── user.graphql # User schema definitions
|
|
205
200
|
│ │ ├── user-queries.resolver.ts # User query resolvers (use named exports)
|
|
@@ -634,90 +629,6 @@ export const postTypes = defineType({
|
|
|
634
629
|
|
|
635
630
|
</details>
|
|
636
631
|
|
|
637
|
-
<details>
|
|
638
|
-
<summary><strong>defineDirective</strong> - Create custom GraphQL directives</summary>
|
|
639
|
-
|
|
640
|
-
```ts
|
|
641
|
-
import { defineDirective } from 'nitro-graphql/utils/define'
|
|
642
|
-
import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils'
|
|
643
|
-
import { defaultFieldResolver, GraphQLError } from 'graphql'
|
|
644
|
-
|
|
645
|
-
export const authDirective = defineDirective({
|
|
646
|
-
name: 'auth',
|
|
647
|
-
locations: ['FIELD_DEFINITION', 'OBJECT'],
|
|
648
|
-
args: {
|
|
649
|
-
requires: {
|
|
650
|
-
type: 'String',
|
|
651
|
-
defaultValue: 'USER',
|
|
652
|
-
description: 'Required role to access this field',
|
|
653
|
-
},
|
|
654
|
-
},
|
|
655
|
-
description: 'Directive to check authentication and authorization',
|
|
656
|
-
transformer: (schema) => {
|
|
657
|
-
return mapSchema(schema, {
|
|
658
|
-
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
|
|
659
|
-
const authDirectiveConfig = getDirective(schema, fieldConfig, 'auth')?.[0]
|
|
660
|
-
|
|
661
|
-
if (authDirectiveConfig) {
|
|
662
|
-
const { resolve = defaultFieldResolver } = fieldConfig
|
|
663
|
-
|
|
664
|
-
fieldConfig.resolve = async function (source, args, context, info) {
|
|
665
|
-
if (!context.user) {
|
|
666
|
-
throw new GraphQLError('You must be logged in')
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
if (context.user.role !== authDirectiveConfig.requires) {
|
|
670
|
-
throw new GraphQLError('Insufficient permissions')
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
return resolve(source, args, context, info)
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
return fieldConfig
|
|
678
|
-
},
|
|
679
|
-
})
|
|
680
|
-
},
|
|
681
|
-
})
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
**Usage in Schema:**
|
|
685
|
-
```graphql
|
|
686
|
-
type User {
|
|
687
|
-
id: ID!
|
|
688
|
-
name: String!
|
|
689
|
-
email: String! @auth(requires: "ADMIN")
|
|
690
|
-
secretData: String @auth(requires: "SUPER_ADMIN")
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
type Query {
|
|
694
|
-
users: [User!]! @auth
|
|
695
|
-
adminStats: AdminStats @auth(requires: "ADMIN")
|
|
696
|
-
}
|
|
697
|
-
```
|
|
698
|
-
|
|
699
|
-
**Available Argument Types:**
|
|
700
|
-
- Basic scalars: `String`, `Int`, `Float`, `Boolean`, `ID`, `JSON`, `DateTime`
|
|
701
|
-
- Non-nullable: `String!`, `Int!`, `Float!`, `Boolean!`, `ID!`, `JSON!`, `DateTime!`
|
|
702
|
-
- Arrays: `[String]`, `[String!]`, `[String]!`, `[String!]!` (and all combinations for other types)
|
|
703
|
-
- Custom types: Any string for your custom GraphQL types
|
|
704
|
-
|
|
705
|
-
**Helper Function:**
|
|
706
|
-
```ts
|
|
707
|
-
export const validateDirective = defineDirective({
|
|
708
|
-
name: 'validate',
|
|
709
|
-
locations: ['FIELD_DEFINITION', 'ARGUMENT_DEFINITION'],
|
|
710
|
-
args: {
|
|
711
|
-
minLength: arg('Int', { description: 'Minimum length' }),
|
|
712
|
-
maxLength: arg('Int', { description: 'Maximum length' }),
|
|
713
|
-
pattern: arg('String', { description: 'Regex pattern' }),
|
|
714
|
-
},
|
|
715
|
-
// ... transformer implementation
|
|
716
|
-
})
|
|
717
|
-
```
|
|
718
|
-
|
|
719
|
-
</details>
|
|
720
|
-
|
|
721
632
|
<details>
|
|
722
633
|
<summary><strong>defineSchema</strong> - Define custom schema with validation</summary>
|
|
723
634
|
|
|
@@ -844,75 +755,6 @@ export default defineNitroConfig({
|
|
|
844
755
|
|
|
845
756
|
## 🔥 Advanced Features
|
|
846
757
|
|
|
847
|
-
<details>
|
|
848
|
-
<summary><strong>Custom Directives</strong></summary>
|
|
849
|
-
|
|
850
|
-
Create reusable GraphQL directives with automatic schema generation:
|
|
851
|
-
|
|
852
|
-
```ts
|
|
853
|
-
// server/graphql/directives/auth.directive.ts
|
|
854
|
-
import { defineDirective } from 'nitro-graphql/utils/define'
|
|
855
|
-
import { getDirective, MapperKind, mapSchema } from '@graphql-tools/utils'
|
|
856
|
-
|
|
857
|
-
export const authDirective = defineDirective({
|
|
858
|
-
name: 'auth',
|
|
859
|
-
locations: ['FIELD_DEFINITION', 'OBJECT'],
|
|
860
|
-
args: {
|
|
861
|
-
requires: {
|
|
862
|
-
type: 'String',
|
|
863
|
-
defaultValue: 'USER',
|
|
864
|
-
description: 'Required role to access this field',
|
|
865
|
-
},
|
|
866
|
-
},
|
|
867
|
-
description: 'Authentication and authorization directive',
|
|
868
|
-
transformer: (schema) => {
|
|
869
|
-
return mapSchema(schema, {
|
|
870
|
-
[MapperKind.OBJECT_FIELD]: (fieldConfig) => {
|
|
871
|
-
const authConfig = getDirective(schema, fieldConfig, 'auth')?.[0]
|
|
872
|
-
if (authConfig) {
|
|
873
|
-
// Transform field resolvers to check authentication
|
|
874
|
-
const { resolve = defaultFieldResolver } = fieldConfig
|
|
875
|
-
fieldConfig.resolve = async (source, args, context, info) => {
|
|
876
|
-
if (!context.user || context.user.role !== authConfig.requires) {
|
|
877
|
-
throw new GraphQLError('Access denied')
|
|
878
|
-
}
|
|
879
|
-
return resolve(source, args, context, info)
|
|
880
|
-
}
|
|
881
|
-
}
|
|
882
|
-
return fieldConfig
|
|
883
|
-
},
|
|
884
|
-
})
|
|
885
|
-
},
|
|
886
|
-
})
|
|
887
|
-
```
|
|
888
|
-
|
|
889
|
-
**Common Directive Examples:**
|
|
890
|
-
- `@auth(requires: "ADMIN")` - Role-based authentication
|
|
891
|
-
- `@cache(ttl: 300, scope: "PUBLIC")` - Field-level caching
|
|
892
|
-
- `@rateLimit(limit: 10, window: 60)` - Rate limiting
|
|
893
|
-
- `@validate(minLength: 5, maxLength: 100)` - Input validation
|
|
894
|
-
- `@transform(upper: true, trim: true)` - Data transformation
|
|
895
|
-
- `@permission(roles: ["ADMIN", "MODERATOR"])` - Multi-role permissions
|
|
896
|
-
|
|
897
|
-
**Usage in Schema:**
|
|
898
|
-
```graphql
|
|
899
|
-
type User {
|
|
900
|
-
id: ID!
|
|
901
|
-
name: String!
|
|
902
|
-
email: String! @auth(requires: "ADMIN")
|
|
903
|
-
posts: [Post!]! @cache(ttl: 300)
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
type Query {
|
|
907
|
-
users: [User!]! @rateLimit(limit: 100, window: 3600)
|
|
908
|
-
sensitiveData: String @auth(requires: "SUPER_ADMIN")
|
|
909
|
-
}
|
|
910
|
-
```
|
|
911
|
-
|
|
912
|
-
The module automatically generates the directive schema definitions and integrates them with both GraphQL Yoga and Apollo Server.
|
|
913
|
-
|
|
914
|
-
</details>
|
|
915
|
-
|
|
916
758
|
<details>
|
|
917
759
|
<summary><strong>Custom Scalars</strong></summary>
|
|
918
760
|
|
package/dist/ecosystem/nuxt.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as _nuxt_schema0 from "@nuxt/schema";
|
|
2
2
|
|
|
3
3
|
//#region src/ecosystem/nuxt.d.ts
|
|
4
4
|
interface ModuleOptions {}
|
|
5
|
-
declare const _default:
|
|
5
|
+
declare const _default: _nuxt_schema0.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { ModuleOptions, _default as default };
|
package/dist/ecosystem/nuxt.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
1
2
|
import { join, resolve } from "pathe";
|
|
2
3
|
import { defineNuxtModule } from "@nuxt/kit";
|
|
3
4
|
|
|
@@ -31,6 +32,27 @@ var nuxt_default = defineNuxtModule({
|
|
|
31
32
|
nuxt.hook("imports:dirs", (dirs) => {
|
|
32
33
|
dirs.push(resolve(nuxt.options.srcDir, "graphql"));
|
|
33
34
|
});
|
|
35
|
+
nuxt.hook("nitro:config", () => {
|
|
36
|
+
const clientDir = join(nuxt.options.buildDir, "graphql");
|
|
37
|
+
const appGraphqlDir = resolve(nuxt.options.rootDir, "app/graphql");
|
|
38
|
+
const hasAppGraphqlDir = existsSync(appGraphqlDir);
|
|
39
|
+
if (!hasAppGraphqlDir) {
|
|
40
|
+
const defaultDir = join(clientDir, "default");
|
|
41
|
+
if (!existsSync(defaultDir)) mkdirSync(defaultDir, { recursive: true });
|
|
42
|
+
const sampleQueryFile = join(defaultDir, "queries.graphql");
|
|
43
|
+
if (!existsSync(sampleQueryFile)) writeFileSync(sampleQueryFile, `# Example GraphQL queries
|
|
44
|
+
# Add your GraphQL queries here
|
|
45
|
+
|
|
46
|
+
# query GetUser($id: ID!) {
|
|
47
|
+
# user(id: $id) {
|
|
48
|
+
# id
|
|
49
|
+
# name
|
|
50
|
+
# email
|
|
51
|
+
# }
|
|
52
|
+
# }
|
|
53
|
+
`, "utf-8");
|
|
54
|
+
}
|
|
55
|
+
});
|
|
34
56
|
}
|
|
35
57
|
});
|
|
36
58
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { StandardSchemaV1 } from "./types/standard-schema.js";
|
|
2
2
|
import { CodegenClientConfig, CodegenServerConfig, ExternalGraphQLService, GenImport, GenericSdkConfig, NitroGraphQLOptions } from "./types/index.js";
|
|
3
|
-
import * as
|
|
3
|
+
import * as nitropack5 from "nitropack";
|
|
4
4
|
|
|
5
5
|
//#region src/index.d.ts
|
|
6
|
-
declare const _default:
|
|
6
|
+
declare const _default: nitropack5.NitroModule;
|
|
7
7
|
//#endregion
|
|
8
8
|
export { CodegenClientConfig, CodegenServerConfig, ExternalGraphQLService, GenImport, GenericSdkConfig, NitroGraphQLOptions, StandardSchemaV1, _default as default };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { relativeWithDot, scanDirectives, scanDocs, scanResolvers, scanSchemas, validateExternalServices } from "./utils/index.js";
|
|
1
|
+
import { relativeWithDot, scanDocs, scanResolvers, scanSchemas, validateExternalServices } from "./utils/index.js";
|
|
3
2
|
import { clientTypeGeneration, serverTypeGeneration } from "./utils/type-generation.js";
|
|
4
3
|
import { rollupConfig } from "./rollup.js";
|
|
5
4
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
@@ -80,7 +79,7 @@ var src_default = defineNitroModule({
|
|
|
80
79
|
const watcher = watch(watchDirs, {
|
|
81
80
|
persistent: true,
|
|
82
81
|
ignoreInitial: true,
|
|
83
|
-
ignored:
|
|
82
|
+
ignored: nitro.options.ignore
|
|
84
83
|
}).on("all", async (event, path) => {
|
|
85
84
|
if (path.endsWith(".graphql") || path.endsWith(".gql")) await clientTypeGeneration(nitro);
|
|
86
85
|
});
|
|
@@ -96,17 +95,11 @@ var src_default = defineNitroModule({
|
|
|
96
95
|
nitro.scanDocuments = docs;
|
|
97
96
|
const resolvers = await scanResolvers(nitro);
|
|
98
97
|
nitro.scanResolvers = resolvers;
|
|
99
|
-
const directives = await scanDirectives(nitro);
|
|
100
|
-
nitro.scanDirectives = directives;
|
|
101
|
-
await generateDirectiveSchemas(nitro, directives);
|
|
102
98
|
nitro.hooks.hook("dev:start", async () => {
|
|
103
99
|
const schemas$1 = await scanSchemas(nitro);
|
|
104
100
|
nitro.scanSchemas = schemas$1;
|
|
105
101
|
const resolvers$1 = await scanResolvers(nitro);
|
|
106
102
|
nitro.scanResolvers = resolvers$1;
|
|
107
|
-
const directives$1 = await scanDirectives(nitro);
|
|
108
|
-
nitro.scanDirectives = directives$1;
|
|
109
|
-
await generateDirectiveSchemas(nitro, directives$1);
|
|
110
103
|
const docs$1 = await scanDocs(nitro);
|
|
111
104
|
nitro.scanDocuments = docs$1;
|
|
112
105
|
});
|
|
@@ -149,8 +142,7 @@ var src_default = defineNitroModule({
|
|
|
149
142
|
"defineSubscription",
|
|
150
143
|
"defineType",
|
|
151
144
|
"defineGraphQLConfig",
|
|
152
|
-
"defineSchema"
|
|
153
|
-
"defineDirective"
|
|
145
|
+
"defineSchema"
|
|
154
146
|
]
|
|
155
147
|
});
|
|
156
148
|
}
|
|
@@ -246,23 +238,6 @@ declare module 'h3' {
|
|
|
246
238
|
consola.warn("nitro-graphql: Found context.d.ts file. Please rename it to context.ts for the new structure.");
|
|
247
239
|
consola.info("The context file should now be context.ts instead of context.d.ts");
|
|
248
240
|
}
|
|
249
|
-
if (nitro.options.framework.name === "nuxt") {
|
|
250
|
-
if (!existsSync(nitro.graphql.clientDir)) mkdirSync(nitro.graphql.clientDir, { recursive: true });
|
|
251
|
-
const defaultDir = join(nitro.graphql.clientDir, "default");
|
|
252
|
-
if (!existsSync(defaultDir)) mkdirSync(defaultDir, { recursive: true });
|
|
253
|
-
const sampleQueryFile = join(defaultDir, "queries.graphql");
|
|
254
|
-
if (!existsSync(sampleQueryFile)) writeFileSync(sampleQueryFile, `# Example GraphQL queries
|
|
255
|
-
# Add your GraphQL queries here
|
|
256
|
-
|
|
257
|
-
# query GetUser($id: ID!) {
|
|
258
|
-
# user(id: $id) {
|
|
259
|
-
# id
|
|
260
|
-
# name
|
|
261
|
-
# email
|
|
262
|
-
# }
|
|
263
|
-
# }
|
|
264
|
-
`, "utf-8");
|
|
265
|
-
}
|
|
266
241
|
}
|
|
267
242
|
});
|
|
268
243
|
|
package/dist/rollup.js
CHANGED
|
@@ -9,7 +9,6 @@ import { genImport } from "knitwork";
|
|
|
9
9
|
async function rollupConfig(app) {
|
|
10
10
|
virtualSchemas(app);
|
|
11
11
|
virtualResolvers(app);
|
|
12
|
-
virtualDirectives(app);
|
|
13
12
|
getGraphQLConfig(app);
|
|
14
13
|
app.hooks.hook("rollup:before", (nitro, rollupConfig$1) => {
|
|
15
14
|
rollupConfig$1.plugins = rollupConfig$1.plugins || [];
|
|
@@ -86,29 +85,6 @@ function virtualResolvers(app) {
|
|
|
86
85
|
return code;
|
|
87
86
|
};
|
|
88
87
|
}
|
|
89
|
-
function virtualDirectives(app) {
|
|
90
|
-
const getDirectives = () => {
|
|
91
|
-
const directives = [...app.scanDirectives || []];
|
|
92
|
-
return directives;
|
|
93
|
-
};
|
|
94
|
-
app.options.virtual ??= {};
|
|
95
|
-
app.options.virtual["#nitro-internal-virtual/server-directives"] = () => {
|
|
96
|
-
const imports = getDirectives();
|
|
97
|
-
const importsContent = [...imports.map(({ specifier, imports: imports$1, options }) => {
|
|
98
|
-
return genImport(specifier, imports$1, options);
|
|
99
|
-
})];
|
|
100
|
-
const data = imports.map(({ imports: imports$1 }) => imports$1.map((i) => `{ directive: ${i.as} }`).join(",\n")).filter(Boolean).join(",\n");
|
|
101
|
-
const content = [
|
|
102
|
-
"export const directives = [",
|
|
103
|
-
data,
|
|
104
|
-
"]",
|
|
105
|
-
""
|
|
106
|
-
];
|
|
107
|
-
content.unshift(...importsContent);
|
|
108
|
-
const code = content.join("\n");
|
|
109
|
-
return code;
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
88
|
function getGraphQLConfig(app) {
|
|
113
89
|
const configPath = resolve(app.graphql.serverDir, "config.ts");
|
|
114
90
|
app.options.virtual ??= {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as h31 from "h3";
|
|
2
2
|
|
|
3
3
|
//#region src/routes/apollo-server.d.ts
|
|
4
|
-
declare const _default:
|
|
4
|
+
declare const _default: h31.EventHandler<h31.EventHandlerRequest, any>;
|
|
5
5
|
//#endregion
|
|
6
6
|
export { _default as default };
|
|
@@ -2,27 +2,21 @@ import { startServerAndCreateH3Handler } from "../utils/apollo.js";
|
|
|
2
2
|
import defu from "defu";
|
|
3
3
|
import { mergeResolvers, mergeTypeDefs } from "@graphql-tools/merge";
|
|
4
4
|
import { importedConfig } from "#nitro-internal-virtual/graphql-config";
|
|
5
|
-
import { directives } from "#nitro-internal-virtual/server-directives";
|
|
6
5
|
import { resolvers } from "#nitro-internal-virtual/server-resolvers";
|
|
7
6
|
import { schemas } from "#nitro-internal-virtual/server-schemas";
|
|
8
7
|
import { ApolloServer } from "@apollo/server";
|
|
9
8
|
import { ApolloServerPluginLandingPageLocalDefault } from "@apollo/server/plugin/landingPage/default";
|
|
10
|
-
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
11
9
|
|
|
12
10
|
//#region src/routes/apollo-server.ts
|
|
13
11
|
function createMergedSchema() {
|
|
14
12
|
try {
|
|
15
|
-
const mergedSchemas = schemas.map((schema
|
|
13
|
+
const mergedSchemas = schemas.map((schema) => schema.def).join("\n\n");
|
|
16
14
|
const typeDefs = mergeTypeDefs([mergedSchemas]);
|
|
17
15
|
const mergedResolvers = mergeResolvers(resolvers.map((r) => r.resolver));
|
|
18
|
-
|
|
16
|
+
return {
|
|
19
17
|
typeDefs,
|
|
20
18
|
resolvers: mergedResolvers
|
|
21
|
-
}
|
|
22
|
-
if (directives && directives.length > 0) {
|
|
23
|
-
for (const { directive } of directives) if (directive.transformer) schema = directive.transformer(schema);
|
|
24
|
-
}
|
|
25
|
-
return schema;
|
|
19
|
+
};
|
|
26
20
|
} catch (error) {
|
|
27
21
|
console.error("Schema merge error:", error);
|
|
28
22
|
throw error;
|
|
@@ -31,9 +25,10 @@ function createMergedSchema() {
|
|
|
31
25
|
let apolloServer = null;
|
|
32
26
|
function createApolloServer() {
|
|
33
27
|
if (!apolloServer) {
|
|
34
|
-
const
|
|
28
|
+
const { typeDefs, resolvers: mergedResolvers } = createMergedSchema();
|
|
35
29
|
apolloServer = new ApolloServer(defu({
|
|
36
|
-
|
|
30
|
+
typeDefs,
|
|
31
|
+
resolvers: mergedResolvers,
|
|
37
32
|
introspection: true,
|
|
38
33
|
plugins: [ApolloServerPluginLandingPageLocalDefault({ embed: true })]
|
|
39
34
|
}, importedConfig));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as h33 from "h3";
|
|
2
2
|
|
|
3
3
|
//#region src/routes/graphql-yoga.d.ts
|
|
4
|
-
declare const _default:
|
|
4
|
+
declare const _default: h33.EventHandler<h33.EventHandlerRequest, Promise<Response>>;
|
|
5
5
|
//#endregion
|
|
6
6
|
export { _default as default };
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import defu from "defu";
|
|
2
2
|
import { mergeResolvers, mergeTypeDefs } from "@graphql-tools/merge";
|
|
3
3
|
import { importedConfig } from "#nitro-internal-virtual/graphql-config";
|
|
4
|
-
import { directives } from "#nitro-internal-virtual/server-directives";
|
|
5
4
|
import { resolvers } from "#nitro-internal-virtual/server-resolvers";
|
|
6
5
|
import { schemas } from "#nitro-internal-virtual/server-schemas";
|
|
7
|
-
import { makeExecutableSchema } from "@graphql-tools/schema";
|
|
8
6
|
import { defineEventHandler, toWebRequest } from "h3";
|
|
9
|
-
import { createYoga } from "graphql-yoga";
|
|
7
|
+
import { createSchema, createYoga } from "graphql-yoga";
|
|
10
8
|
|
|
11
9
|
//#region src/routes/graphql-yoga.ts
|
|
12
10
|
const apolloSandboxHtml = `<!DOCTYPE html>
|
|
@@ -28,17 +26,13 @@ new window.EmbeddedSandbox({
|
|
|
28
26
|
</html>`;
|
|
29
27
|
function createMergedSchema() {
|
|
30
28
|
try {
|
|
31
|
-
const mergedSchemas = schemas.map((schema
|
|
29
|
+
const mergedSchemas = schemas.map((schema) => schema.def).join("\n\n");
|
|
32
30
|
const typeDefs = mergeTypeDefs([mergedSchemas]);
|
|
33
31
|
const mergedResolvers = mergeResolvers(resolvers.map((r) => r.resolver));
|
|
34
|
-
|
|
32
|
+
return createSchema({
|
|
35
33
|
typeDefs,
|
|
36
34
|
resolvers: mergedResolvers
|
|
37
35
|
});
|
|
38
|
-
if (directives && directives.length > 0) {
|
|
39
|
-
for (const { directive } of directives) if (directive.transformer) schema = directive.transformer(schema);
|
|
40
|
-
}
|
|
41
|
-
return schema;
|
|
42
36
|
} catch (error) {
|
|
43
37
|
console.error("Schema merge error:", error);
|
|
44
38
|
throw error;
|
package/dist/routes/health.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as h36 from "h3";
|
|
2
2
|
|
|
3
3
|
//#region src/routes/health.d.ts
|
|
4
|
-
declare const _default:
|
|
4
|
+
declare const _default: h36.EventHandler<h36.EventHandlerRequest, Promise<{
|
|
5
5
|
status: string;
|
|
6
6
|
message: string;
|
|
7
7
|
timestamp: string;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -20,7 +20,7 @@ type CodegenClientConfig = TypeScriptPluginConfig & TypeScriptDocumentsPluginCon
|
|
|
20
20
|
interface IESMImport {
|
|
21
21
|
name: string;
|
|
22
22
|
as?: string;
|
|
23
|
-
type: 'resolver' | 'query' | 'mutation' | 'type' | 'subscription'
|
|
23
|
+
type: 'resolver' | 'query' | 'mutation' | 'type' | 'subscription';
|
|
24
24
|
}
|
|
25
25
|
interface GenImport {
|
|
26
26
|
specifier: string;
|
|
@@ -32,7 +32,6 @@ declare module 'nitropack/types' {
|
|
|
32
32
|
scanSchemas: string[];
|
|
33
33
|
scanDocuments: string[];
|
|
34
34
|
scanResolvers: GenImport[];
|
|
35
|
-
scanDirectives: GenImport[];
|
|
36
35
|
graphql: {
|
|
37
36
|
buildDir: string;
|
|
38
37
|
watchDirs: string[];
|
|
@@ -57,23 +57,23 @@ async function loadExternalSchema(service, buildDir) {
|
|
|
57
57
|
if (service.downloadSchema && buildDir) {
|
|
58
58
|
const defaultPath = resolve(buildDir, "graphql", "schemas", `${service.name}.graphql`);
|
|
59
59
|
const schemaFilePath = service.downloadPath ? resolve(service.downloadPath) : defaultPath;
|
|
60
|
-
if (existsSync(schemaFilePath)) {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return result$1;
|
|
66
|
-
} catch (localError) {
|
|
67
|
-
consola$1.warn(`[graphql:${service.name}] Failed to load local schema, falling back to remote:`, localError);
|
|
68
|
-
}
|
|
60
|
+
if (existsSync(schemaFilePath)) try {
|
|
61
|
+
const result$1 = loadSchemaSync([schemaFilePath], { loaders: [new GraphQLFileLoader()] });
|
|
62
|
+
return result$1;
|
|
63
|
+
} catch {
|
|
64
|
+
consola$1.warn(`[graphql:${service.name}] Cached schema invalid, loading from source`);
|
|
69
65
|
}
|
|
70
66
|
}
|
|
71
|
-
|
|
67
|
+
const hasUrls = schemas.some((schema) => isUrl(schema));
|
|
68
|
+
const hasLocalFiles = schemas.some((schema) => !isUrl(schema));
|
|
69
|
+
const loaders = [];
|
|
70
|
+
if (hasLocalFiles) loaders.push(new GraphQLFileLoader());
|
|
71
|
+
if (hasUrls) loaders.push(new UrlLoader());
|
|
72
|
+
if (loaders.length === 0) throw new Error("No appropriate loaders found for schema sources");
|
|
72
73
|
const result = loadSchemaSync(schemas, {
|
|
73
|
-
loaders
|
|
74
|
+
loaders,
|
|
74
75
|
...Object.keys(headers).length > 0 && { headers }
|
|
75
76
|
});
|
|
76
|
-
consola$1.info(`[graphql:${service.name}] External schema loaded successfully`);
|
|
77
77
|
return result;
|
|
78
78
|
} catch (error) {
|
|
79
79
|
consola$1.error(`[graphql:${service.name}] Failed to load external schema:`, error);
|
|
@@ -81,6 +81,12 @@ async function loadExternalSchema(service, buildDir) {
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
/**
|
|
84
|
+
* Check if a path is a URL (http/https)
|
|
85
|
+
*/
|
|
86
|
+
function isUrl(path) {
|
|
87
|
+
return path.startsWith("http://") || path.startsWith("https://");
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
84
90
|
* Download and save schema from external service
|
|
85
91
|
*/
|
|
86
92
|
async function downloadAndSaveSchema(service, buildDir) {
|
|
@@ -91,12 +97,14 @@ async function downloadAndSaveSchema(service, buildDir) {
|
|
|
91
97
|
try {
|
|
92
98
|
const headers = typeof service.headers === "function" ? service.headers() : service.headers || {};
|
|
93
99
|
const schemas = Array.isArray(service.schema) ? service.schema : [service.schema];
|
|
100
|
+
const hasUrlSchemas = schemas.some((schema) => isUrl(schema));
|
|
101
|
+
const hasLocalSchemas = schemas.some((schema) => !isUrl(schema));
|
|
94
102
|
let shouldDownload = false;
|
|
95
103
|
const fileExists = existsSync(schemaFilePath);
|
|
96
104
|
if (downloadMode === "always") {
|
|
97
105
|
shouldDownload = true;
|
|
98
|
-
if (fileExists) try {
|
|
99
|
-
const remoteSchema = loadSchemaSync(schemas, {
|
|
106
|
+
if (fileExists && hasUrlSchemas) try {
|
|
107
|
+
const remoteSchema = loadSchemaSync(schemas.filter(isUrl), {
|
|
100
108
|
loaders: [new UrlLoader()],
|
|
101
109
|
...Object.keys(headers).length > 0 && { headers }
|
|
102
110
|
});
|
|
@@ -112,24 +120,48 @@ async function downloadAndSaveSchema(service, buildDir) {
|
|
|
112
120
|
consola$1.warn(`[graphql:${service.name}] Unable to compare with remote schema, updating local cache`);
|
|
113
121
|
shouldDownload = true;
|
|
114
122
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
else if (fileExists && hasLocalSchemas) {
|
|
124
|
+
const localFiles = schemas.filter((schema) => !isUrl(schema));
|
|
125
|
+
let sourceIsNewer = false;
|
|
126
|
+
for (const localFile of localFiles) if (existsSync(localFile)) {
|
|
127
|
+
const { statSync } = await import("node:fs");
|
|
128
|
+
const sourceStats = statSync(localFile);
|
|
129
|
+
const cachedStats = statSync(schemaFilePath);
|
|
130
|
+
if (sourceStats.mtime > cachedStats.mtime) {
|
|
131
|
+
sourceIsNewer = true;
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (!sourceIsNewer) shouldDownload = false;
|
|
136
|
+
}
|
|
137
|
+
} else if (downloadMode === true || downloadMode === "once") shouldDownload = !fileExists;
|
|
119
138
|
if (shouldDownload) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
139
|
+
if (hasUrlSchemas && hasLocalSchemas) {
|
|
140
|
+
const schema = loadSchemaSync(schemas, {
|
|
141
|
+
loaders: [new GraphQLFileLoader(), new UrlLoader()],
|
|
142
|
+
...Object.keys(headers).length > 0 && { headers }
|
|
143
|
+
});
|
|
144
|
+
const schemaString = printSchemaWithDirectives(schema);
|
|
145
|
+
mkdirSync(dirname(schemaFilePath), { recursive: true });
|
|
146
|
+
writeFileSync(schemaFilePath, schemaString, "utf-8");
|
|
147
|
+
} else if (hasUrlSchemas) {
|
|
148
|
+
const schema = loadSchemaSync(schemas, {
|
|
149
|
+
loaders: [new UrlLoader()],
|
|
150
|
+
...Object.keys(headers).length > 0 && { headers }
|
|
151
|
+
});
|
|
152
|
+
const schemaString = printSchemaWithDirectives(schema);
|
|
153
|
+
mkdirSync(dirname(schemaFilePath), { recursive: true });
|
|
154
|
+
writeFileSync(schemaFilePath, schemaString, "utf-8");
|
|
155
|
+
} else if (hasLocalSchemas) {
|
|
156
|
+
const schema = loadSchemaSync(schemas, { loaders: [new GraphQLFileLoader()] });
|
|
157
|
+
const schemaString = printSchemaWithDirectives(schema);
|
|
158
|
+
mkdirSync(dirname(schemaFilePath), { recursive: true });
|
|
159
|
+
writeFileSync(schemaFilePath, schemaString, "utf-8");
|
|
160
|
+
}
|
|
129
161
|
}
|
|
130
162
|
return schemaFilePath;
|
|
131
163
|
} catch (error) {
|
|
132
|
-
consola$1.error(`[graphql:${service.name}] Failed to download schema:`, error);
|
|
164
|
+
consola$1.error(`[graphql:${service.name}] Failed to download/copy schema:`, error);
|
|
133
165
|
return void 0;
|
|
134
166
|
}
|
|
135
167
|
}
|
|
@@ -143,13 +175,11 @@ async function loadGraphQLDocuments(patterns) {
|
|
|
143
175
|
}
|
|
144
176
|
}
|
|
145
177
|
async function generateClientTypes(schema, docs, config = {}, sdkConfig = {}, outputPath, serviceName) {
|
|
146
|
-
if (docs.length === 0) {
|
|
147
|
-
|
|
148
|
-
consola$1.info(`[graphql${serviceLabel$1}] No client GraphQL files found. Skipping client type generation.`);
|
|
178
|
+
if (docs.length === 0 && !serviceName) {
|
|
179
|
+
consola$1.info("No client GraphQL files found. Skipping client type generation.");
|
|
149
180
|
return false;
|
|
150
181
|
}
|
|
151
182
|
const serviceLabel = serviceName ? `:${serviceName}` : "";
|
|
152
|
-
consola$1.info(`[graphql${serviceLabel}] Found ${docs.length} client GraphQL documents`);
|
|
153
183
|
const defaultConfig = {
|
|
154
184
|
emitLegacyCommonJSImports: false,
|
|
155
185
|
useTypeImports: true,
|
|
@@ -178,6 +208,48 @@ async function generateClientTypes(schema, docs, config = {}, sdkConfig = {}, ou
|
|
|
178
208
|
const mergedConfig = defu$1(defaultConfig, config);
|
|
179
209
|
const mergedSdkConfig = defu$1(mergedConfig, sdkConfig);
|
|
180
210
|
try {
|
|
211
|
+
if (docs.length === 0) {
|
|
212
|
+
const output$1 = await codegen({
|
|
213
|
+
filename: outputPath || "client-types.generated.ts",
|
|
214
|
+
schema: parse(printSchemaWithDirectives(schema)),
|
|
215
|
+
documents: [],
|
|
216
|
+
config: mergedConfig,
|
|
217
|
+
plugins: [{ pluginContent: {} }, { typescript: {} }],
|
|
218
|
+
pluginMap: {
|
|
219
|
+
pluginContent: { plugin: pluginContent },
|
|
220
|
+
typescript: { plugin }
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
const sdkContent$1 = `// THIS FILE IS GENERATED, DO NOT EDIT!
|
|
224
|
+
/* eslint-disable eslint-comments/no-unlimited-disable */
|
|
225
|
+
/* tslint:disable */
|
|
226
|
+
/* eslint-disable */
|
|
227
|
+
/* prettier-ignore */
|
|
228
|
+
|
|
229
|
+
import type { GraphQLResolveInfo } from 'graphql'
|
|
230
|
+
export type RequireFields<T, K extends keyof T> = Omit<T, K> & { [P in K]-?: NonNullable<T[P]> }
|
|
231
|
+
|
|
232
|
+
export interface Requester<C = {}, E = unknown> {
|
|
233
|
+
<R, V>(doc: string, vars?: V, options?: C): Promise<R> | AsyncIterable<R>
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export type Sdk = {
|
|
237
|
+
request: <R, V = Record<string, any>>(document: string, variables?: V) => Promise<R>
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
export function getSdk(requester: Requester): Sdk {
|
|
241
|
+
return {
|
|
242
|
+
request: <R, V = Record<string, any>>(document: string, variables?: V): Promise<R> => {
|
|
243
|
+
return requester<R, V>(document, variables)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
`;
|
|
248
|
+
return {
|
|
249
|
+
types: output$1,
|
|
250
|
+
sdk: sdkContent$1
|
|
251
|
+
};
|
|
252
|
+
}
|
|
181
253
|
const output = await codegen({
|
|
182
254
|
filename: outputPath || "client-types.generated.ts",
|
|
183
255
|
schema: parse(printSchemaWithDirectives(schema)),
|
package/dist/utils/define.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { GraphQLSchema } from "graphql";
|
|
2
1
|
import { ApolloServerOptions } from "@apollo/server";
|
|
3
2
|
import { H3Event } from "h3";
|
|
4
3
|
import { YogaServerOptions } from "graphql-yoga";
|
|
@@ -18,55 +17,5 @@ declare function defineSubscription(resolvers?: Resolvers['Subscription']): Reso
|
|
|
18
17
|
declare function defineType(resolvers: Resolvers): Resolvers;
|
|
19
18
|
type DefineServerConfig<T extends NPMConfig = NPMConfig> = T['framework'] extends 'graphql-yoga' ? Partial<YogaServerOptions<H3Event, Partial<H3Event>>> : T['framework'] extends 'apollo-server' ? Partial<ApolloServerOptions<H3Event>> : Partial<YogaServerOptions<H3Event, Partial<H3Event>>> | Partial<ApolloServerOptions<H3Event>>;
|
|
20
19
|
declare function defineGraphQLConfig<T extends NPMConfig = NPMConfig>(config: Partial<DefineServerConfig<T>>): Partial<DefineServerConfig<T>>;
|
|
21
|
-
type DirectiveLocationName = 'QUERY' | 'MUTATION' | 'SUBSCRIPTION' | 'FIELD' | 'FRAGMENT_DEFINITION' | 'FRAGMENT_SPREAD' | 'INLINE_FRAGMENT' | 'VARIABLE_DEFINITION' | 'SCHEMA' | 'SCALAR' | 'OBJECT' | 'FIELD_DEFINITION' | 'ARGUMENT_DEFINITION' | 'INTERFACE' | 'UNION' | 'ENUM' | 'ENUM_VALUE' | 'INPUT_OBJECT' | 'INPUT_FIELD_DEFINITION';
|
|
22
|
-
type GraphQLScalarType = 'String' | 'Int' | 'Float' | 'Boolean' | 'ID' | 'JSON' | 'DateTime';
|
|
23
|
-
type GraphQLBaseType = GraphQLScalarType | (string & {});
|
|
24
|
-
type GraphQLArgumentType = 'String' | 'Int' | 'Float' | 'Boolean' | 'ID' | 'JSON' | 'DateTime' | 'String!' | 'Int!' | 'Float!' | 'Boolean!' | 'ID!' | 'JSON!' | 'DateTime!' | '[String]' | '[String!]' | '[String]!' | '[String!]!' | '[Int]' | '[Int!]' | '[Int]!' | '[Int!]!' | '[Float]' | '[Float!]' | '[Float]!' | '[Float!]!' | '[Boolean]' | '[Boolean!]' | '[Boolean]!' | '[Boolean!]!' | '[ID]' | '[ID!]' | '[ID]!' | '[ID!]!' | '[JSON]' | '[JSON!]' | '[JSON]!' | '[JSON!]!' | '[DateTime]' | '[DateTime!]' | '[DateTime]!' | '[DateTime!]!' | (string & {});
|
|
25
|
-
interface DirectiveArgument<T extends GraphQLArgumentType = GraphQLArgumentType> {
|
|
26
|
-
/**
|
|
27
|
-
* GraphQL type for the argument
|
|
28
|
-
* @example 'String', 'Int!', '[String!]!', 'DateTime', 'JSON'
|
|
29
|
-
*/
|
|
30
|
-
type: T;
|
|
31
|
-
defaultValue?: any;
|
|
32
|
-
description?: string;
|
|
33
|
-
}
|
|
34
|
-
interface DirectiveArg {
|
|
35
|
-
type: GraphQLArgumentType;
|
|
36
|
-
defaultValue?: any;
|
|
37
|
-
description?: string;
|
|
38
|
-
}
|
|
39
|
-
interface DirectiveDefinition {
|
|
40
|
-
name: string;
|
|
41
|
-
locations: DirectiveLocationName[];
|
|
42
|
-
args?: Record<string, DirectiveArg>;
|
|
43
|
-
description?: string;
|
|
44
|
-
isRepeatable?: boolean;
|
|
45
|
-
transformer?: (schema: GraphQLSchema) => GraphQLSchema;
|
|
46
|
-
}
|
|
47
|
-
interface DefineDirectiveConfig {
|
|
48
|
-
name: string;
|
|
49
|
-
locations: ReadonlyArray<DirectiveLocationName>;
|
|
50
|
-
args?: Record<string, {
|
|
51
|
-
type: GraphQLArgumentType;
|
|
52
|
-
defaultValue?: any;
|
|
53
|
-
description?: string;
|
|
54
|
-
}>;
|
|
55
|
-
description?: string;
|
|
56
|
-
isRepeatable?: boolean;
|
|
57
|
-
transformer?: (schema: GraphQLSchema) => GraphQLSchema;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Helper function to create directive arguments with proper type inference
|
|
61
|
-
* @example
|
|
62
|
-
* args: {
|
|
63
|
-
* myArg: arg('String!', { defaultValue: 'hello' })
|
|
64
|
-
* }
|
|
65
|
-
*/
|
|
66
|
-
declare function arg<T extends GraphQLArgumentType>(type: T, options?: {
|
|
67
|
-
defaultValue?: any;
|
|
68
|
-
description?: string;
|
|
69
|
-
}): DirectiveArgument<T>;
|
|
70
|
-
declare function defineDirective(config: DefineDirectiveConfig): DirectiveDefinition;
|
|
71
20
|
//#endregion
|
|
72
|
-
export {
|
|
21
|
+
export { DefineServerConfig, ResolverQuery, defineGraphQLConfig, defineMutation, defineQuery, defineResolver, defineSchema, defineSubscription, defineType };
|
package/dist/utils/define.js
CHANGED
|
@@ -20,38 +20,6 @@ function defineType(resolvers) {
|
|
|
20
20
|
function defineGraphQLConfig(config) {
|
|
21
21
|
return config;
|
|
22
22
|
}
|
|
23
|
-
/**
|
|
24
|
-
* Helper function to create directive arguments with proper type inference
|
|
25
|
-
* @example
|
|
26
|
-
* args: {
|
|
27
|
-
* myArg: arg('String!', { defaultValue: 'hello' })
|
|
28
|
-
* }
|
|
29
|
-
*/
|
|
30
|
-
function arg(type, options) {
|
|
31
|
-
return {
|
|
32
|
-
type,
|
|
33
|
-
...options
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
function defineDirective(config) {
|
|
37
|
-
const args = config.args ? Object.entries(config.args).map(([name, arg$1]) => {
|
|
38
|
-
const defaultValue = arg$1.defaultValue !== void 0 ? ` = ${JSON.stringify(arg$1.defaultValue)}` : "";
|
|
39
|
-
return `${name}: ${arg$1.type}${defaultValue}`;
|
|
40
|
-
}).join(", ") : "";
|
|
41
|
-
const argsString = args ? `(${args})` : "";
|
|
42
|
-
const locations = config.locations.join(" | ");
|
|
43
|
-
const schemaDefinition = `directive @${config.name}${argsString} on ${locations}`;
|
|
44
|
-
Object.defineProperty(config, "__schema", {
|
|
45
|
-
value: schemaDefinition,
|
|
46
|
-
enumerable: false,
|
|
47
|
-
configurable: false,
|
|
48
|
-
writable: false
|
|
49
|
-
});
|
|
50
|
-
return {
|
|
51
|
-
...config,
|
|
52
|
-
locations: [...config.locations]
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
23
|
|
|
56
24
|
//#endregion
|
|
57
|
-
export {
|
|
25
|
+
export { defineGraphQLConfig, defineMutation, defineQuery, defineResolver, defineSchema, defineSubscription, defineType };
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { GenImport } from "../types/index.js";
|
|
2
|
-
import { directiveParser, generateDirectiveSchema, generateDirectiveSchemas } from "./directive-parser.js";
|
|
3
2
|
import { Nitro } from "nitropack";
|
|
4
3
|
|
|
5
4
|
//#region src/utils/index.d.ts
|
|
@@ -8,7 +7,7 @@ declare function getImportId(p: string, lazy?: boolean): string;
|
|
|
8
7
|
declare function relativeWithDot(from: string, to: string): string;
|
|
9
8
|
declare function scanGraphql(nitro: Nitro): Promise<string[]>;
|
|
10
9
|
declare function scanResolvers(nitro: Nitro): Promise<GenImport[]>;
|
|
11
|
-
declare function scanDirectives(nitro: Nitro): Promise<
|
|
10
|
+
declare function scanDirectives(nitro: Nitro): Promise<string[]>;
|
|
12
11
|
declare function scanTypeDefs(nitro: Nitro): Promise<string[]>;
|
|
13
12
|
declare function scanSchemas(nitro: Nitro): Promise<string[]>;
|
|
14
13
|
declare function scanDocs(nitro: Nitro): Promise<string[]>;
|
|
@@ -21,4 +20,4 @@ declare function scanExternalServiceDocs(nitro: Nitro, serviceName: string, patt
|
|
|
21
20
|
*/
|
|
22
21
|
declare function validateExternalServices(services: any[]): string[];
|
|
23
22
|
//#endregion
|
|
24
|
-
export { GLOB_SCAN_PATTERN,
|
|
23
|
+
export { GLOB_SCAN_PATTERN, getImportId, relativeWithDot, scanDirectives, scanDocs, scanExternalServiceDocs, scanGraphql, scanResolvers, scanSchemas, scanTypeDefs, validateExternalServices };
|
package/dist/utils/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { directiveParser, generateDirectiveSchema, generateDirectiveSchemas } from "./directive-parser.js";
|
|
2
1
|
import { join, relative } from "pathe";
|
|
3
2
|
import { readFile } from "node:fs/promises";
|
|
4
3
|
import { hash } from "ohash";
|
|
@@ -57,11 +56,6 @@ async function scanResolvers(nitro) {
|
|
|
57
56
|
type: "subscription",
|
|
58
57
|
as: `_${hash(decl.id.name + file.path).replace(/-/g, "").slice(0, 6)}`
|
|
59
58
|
});
|
|
60
|
-
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineDirective") exports.imports.push({
|
|
61
|
-
name: decl.id.name,
|
|
62
|
-
type: "directive",
|
|
63
|
-
as: `_${hash(decl.id.name + file.path).replace(/-/g, "").slice(0, 6)}`
|
|
64
|
-
});
|
|
65
59
|
}
|
|
66
60
|
}
|
|
67
61
|
}
|
|
@@ -71,28 +65,7 @@ async function scanResolvers(nitro) {
|
|
|
71
65
|
}
|
|
72
66
|
async function scanDirectives(nitro) {
|
|
73
67
|
const files = await scanFiles(nitro, "graphql", "**/*.directive.{ts,js}");
|
|
74
|
-
|
|
75
|
-
for (const file of files) {
|
|
76
|
-
const fileContent = await readFile(file.fullPath, "utf-8");
|
|
77
|
-
const parsed = await parseAsync(file.fullPath, fileContent);
|
|
78
|
-
const exports = {
|
|
79
|
-
imports: [],
|
|
80
|
-
specifier: file.fullPath
|
|
81
|
-
};
|
|
82
|
-
for (const node of parsed.program.body) if (node.type === "ExportNamedDeclaration" && node.declaration && node.declaration.type === "VariableDeclaration") {
|
|
83
|
-
for (const decl of node.declaration.declarations) if (decl.type === "VariableDeclarator" && decl.init && decl.id.type === "Identifier") {
|
|
84
|
-
if (decl.init && decl.init.type === "CallExpression") {
|
|
85
|
-
if (decl.init.callee.type === "Identifier" && decl.init.callee.name === "defineDirective") exports.imports.push({
|
|
86
|
-
name: decl.id.name,
|
|
87
|
-
type: "directive",
|
|
88
|
-
as: `_${hash(decl.id.name + file.path).replace(/-/g, "").slice(0, 6)}`
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (exports.imports.length > 0) exportName.push(exports);
|
|
94
|
-
}
|
|
95
|
-
return exportName;
|
|
68
|
+
return files.map((f) => f.fullPath);
|
|
96
69
|
}
|
|
97
70
|
async function scanTypeDefs(nitro) {
|
|
98
71
|
const files = await scanFiles(nitro, "graphql", "**/*.typedef.{ts,js}");
|
|
@@ -173,4 +146,4 @@ async function scanDir(nitro, dir, name, globPattern = GLOB_SCAN_PATTERN) {
|
|
|
173
146
|
}
|
|
174
147
|
|
|
175
148
|
//#endregion
|
|
176
|
-
export { GLOB_SCAN_PATTERN,
|
|
149
|
+
export { GLOB_SCAN_PATTERN, getImportId, relativeWithDot, scanDirectives, scanDocs, scanExternalServiceDocs, scanGraphql, scanResolvers, scanSchemas, scanTypeDefs, validateExternalServices };
|
|
@@ -116,7 +116,8 @@ async function serverTypeGeneration(app) {
|
|
|
116
116
|
}
|
|
117
117
|
async function clientTypeGeneration(nitro) {
|
|
118
118
|
try {
|
|
119
|
-
|
|
119
|
+
const hasServerSchema = nitro.scanSchemas && nitro.scanSchemas.length > 0;
|
|
120
|
+
if (hasServerSchema) await generateMainClientTypes(nitro);
|
|
120
121
|
if (nitro.options.graphql?.externalServices?.length) await generateExternalServicesTypes(nitro);
|
|
121
122
|
} catch (error) {
|
|
122
123
|
consola.error("Client schema generation error:", error);
|
|
@@ -190,8 +191,13 @@ async function generateExternalServicesTypes(nitro) {
|
|
|
190
191
|
let loadDocs = [];
|
|
191
192
|
if (documentPatterns.length > 0) try {
|
|
192
193
|
loadDocs = await loadGraphQLDocuments(documentPatterns);
|
|
194
|
+
if (!loadDocs || loadDocs.length === 0) {
|
|
195
|
+
consola.warn(`[graphql:${service.name}] No GraphQL documents found, skipping service generation`);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
193
198
|
} catch (error) {
|
|
194
|
-
consola.warn(`[graphql:${service.name}] No documents found:`, error);
|
|
199
|
+
consola.warn(`[graphql:${service.name}] No documents found, skipping service generation:`, error);
|
|
200
|
+
continue;
|
|
195
201
|
}
|
|
196
202
|
const types = await generateExternalClientTypes(service, schema, loadDocs);
|
|
197
203
|
if (types === false) {
|
package/package.json
CHANGED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
//#region src/utils/directive-parser.d.ts
|
|
2
|
-
interface ParsedDirective {
|
|
3
|
-
name: string;
|
|
4
|
-
locations: string[];
|
|
5
|
-
args?: Record<string, {
|
|
6
|
-
type: string;
|
|
7
|
-
defaultValue?: any;
|
|
8
|
-
}>;
|
|
9
|
-
description?: string;
|
|
10
|
-
isRepeatable?: boolean;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Clean AST-based directive parser using oxc-parser
|
|
14
|
-
*/
|
|
15
|
-
declare class DirectiveParser {
|
|
16
|
-
private oxc;
|
|
17
|
-
init(): Promise<void>;
|
|
18
|
-
/**
|
|
19
|
-
* Parse directives from a TypeScript/JavaScript file
|
|
20
|
-
*/
|
|
21
|
-
parseDirectives(fileContent: string, filePath: string): Promise<ParsedDirective[]>;
|
|
22
|
-
/**
|
|
23
|
-
* Extract directive definitions from AST
|
|
24
|
-
*/
|
|
25
|
-
private extractDirectiveDefinitions;
|
|
26
|
-
/**
|
|
27
|
-
* Traverse AST nodes recursively
|
|
28
|
-
*/
|
|
29
|
-
private traverse;
|
|
30
|
-
/**
|
|
31
|
-
* Check if node is a defineDirective call
|
|
32
|
-
*/
|
|
33
|
-
private isDefineDirectiveCall;
|
|
34
|
-
/**
|
|
35
|
-
* Extract directive configuration from defineDirective call
|
|
36
|
-
*/
|
|
37
|
-
private extractDirectiveFromCall;
|
|
38
|
-
/**
|
|
39
|
-
* Extract directive properties from object expression
|
|
40
|
-
*/
|
|
41
|
-
private extractDirectiveFromObject;
|
|
42
|
-
/**
|
|
43
|
-
* Extract string literal value
|
|
44
|
-
*/
|
|
45
|
-
private extractStringLiteral;
|
|
46
|
-
/**
|
|
47
|
-
* Extract boolean literal value
|
|
48
|
-
*/
|
|
49
|
-
private extractBooleanLiteral;
|
|
50
|
-
/**
|
|
51
|
-
* Extract array of strings
|
|
52
|
-
*/
|
|
53
|
-
private extractStringArray;
|
|
54
|
-
/**
|
|
55
|
-
* Extract arguments object
|
|
56
|
-
*/
|
|
57
|
-
private extractArgsObject;
|
|
58
|
-
/**
|
|
59
|
-
* Extract argument configuration
|
|
60
|
-
*/
|
|
61
|
-
private extractArgConfig;
|
|
62
|
-
/**
|
|
63
|
-
* Extract literal value (string, number, boolean)
|
|
64
|
-
*/
|
|
65
|
-
private extractLiteralValue;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Generate GraphQL directive schema from parsed directive
|
|
69
|
-
*/
|
|
70
|
-
declare function generateDirectiveSchema(directive: ParsedDirective): string;
|
|
71
|
-
/**
|
|
72
|
-
* Generate directive schemas file from scanned directives
|
|
73
|
-
*/
|
|
74
|
-
declare function generateDirectiveSchemas(nitro: any, directives: any[]): Promise<void>;
|
|
75
|
-
/**
|
|
76
|
-
* Singleton instance for reuse
|
|
77
|
-
*/
|
|
78
|
-
declare const directiveParser: DirectiveParser;
|
|
79
|
-
//#endregion
|
|
80
|
-
export { DirectiveParser, ParsedDirective, directiveParser, generateDirectiveSchema, generateDirectiveSchemas };
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
//#region src/utils/directive-parser.ts
|
|
2
|
-
/**
|
|
3
|
-
* Clean AST-based directive parser using oxc-parser
|
|
4
|
-
*/
|
|
5
|
-
var DirectiveParser = class {
|
|
6
|
-
oxc;
|
|
7
|
-
async init() {
|
|
8
|
-
if (!this.oxc) this.oxc = await import("oxc-parser");
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Parse directives from a TypeScript/JavaScript file
|
|
12
|
-
*/
|
|
13
|
-
async parseDirectives(fileContent, filePath) {
|
|
14
|
-
await this.init();
|
|
15
|
-
try {
|
|
16
|
-
const result = this.oxc.parseSync(filePath, fileContent, {
|
|
17
|
-
lang: filePath.endsWith(".ts") ? "ts" : "js",
|
|
18
|
-
sourceType: "module",
|
|
19
|
-
astType: "ts"
|
|
20
|
-
});
|
|
21
|
-
if (result.errors.length > 0) {
|
|
22
|
-
console.warn(`Parse errors in ${filePath}:`, result.errors.map((e) => e.message));
|
|
23
|
-
return [];
|
|
24
|
-
}
|
|
25
|
-
return this.extractDirectiveDefinitions(result.program);
|
|
26
|
-
} catch (error) {
|
|
27
|
-
console.warn(`Failed to parse ${filePath} with oxc:`, error);
|
|
28
|
-
return [];
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Extract directive definitions from AST
|
|
33
|
-
*/
|
|
34
|
-
extractDirectiveDefinitions(program) {
|
|
35
|
-
const directives = [];
|
|
36
|
-
this.traverse(program, (node) => {
|
|
37
|
-
if (this.isDefineDirectiveCall(node)) {
|
|
38
|
-
const directive = this.extractDirectiveFromCall(node);
|
|
39
|
-
if (directive) directives.push(directive);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
return directives;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Traverse AST nodes recursively
|
|
46
|
-
*/
|
|
47
|
-
traverse(node, visitor) {
|
|
48
|
-
if (!node || typeof node !== "object") return;
|
|
49
|
-
visitor(node);
|
|
50
|
-
for (const key in node) {
|
|
51
|
-
const child = node[key];
|
|
52
|
-
if (Array.isArray(child)) child.forEach((item) => this.traverse(item, visitor));
|
|
53
|
-
else if (child && typeof child === "object") this.traverse(child, visitor);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Check if node is a defineDirective call
|
|
58
|
-
*/
|
|
59
|
-
isDefineDirectiveCall(node) {
|
|
60
|
-
return node.type === "CallExpression" && node.callee?.type === "Identifier" && node.callee.name === "defineDirective" && node.arguments?.length > 0;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Extract directive configuration from defineDirective call
|
|
64
|
-
*/
|
|
65
|
-
extractDirectiveFromCall(node) {
|
|
66
|
-
const arg = node.arguments[0];
|
|
67
|
-
if (arg?.type !== "ObjectExpression") return null;
|
|
68
|
-
return this.extractDirectiveFromObject(arg);
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Extract directive properties from object expression
|
|
72
|
-
*/
|
|
73
|
-
extractDirectiveFromObject(objNode) {
|
|
74
|
-
let name = "";
|
|
75
|
-
let locations = [];
|
|
76
|
-
let args = {};
|
|
77
|
-
let description;
|
|
78
|
-
let isRepeatable;
|
|
79
|
-
for (const prop of objNode.properties || []) {
|
|
80
|
-
if (prop.type !== "Property" || prop.key?.type !== "Identifier") continue;
|
|
81
|
-
switch (prop.key.name) {
|
|
82
|
-
case "name":
|
|
83
|
-
name = this.extractStringLiteral(prop.value) || "";
|
|
84
|
-
break;
|
|
85
|
-
case "locations":
|
|
86
|
-
locations = this.extractStringArray(prop.value);
|
|
87
|
-
break;
|
|
88
|
-
case "args":
|
|
89
|
-
args = this.extractArgsObject(prop.value);
|
|
90
|
-
break;
|
|
91
|
-
case "description":
|
|
92
|
-
description = this.extractStringLiteral(prop.value);
|
|
93
|
-
break;
|
|
94
|
-
case "isRepeatable":
|
|
95
|
-
isRepeatable = this.extractBooleanLiteral(prop.value);
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return name && locations.length > 0 ? {
|
|
100
|
-
name,
|
|
101
|
-
locations,
|
|
102
|
-
args,
|
|
103
|
-
description,
|
|
104
|
-
isRepeatable
|
|
105
|
-
} : null;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Extract string literal value
|
|
109
|
-
*/
|
|
110
|
-
extractStringLiteral(node) {
|
|
111
|
-
if (node?.type === "Literal" && typeof node.value === "string") return node.value;
|
|
112
|
-
return void 0;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Extract boolean literal value
|
|
116
|
-
*/
|
|
117
|
-
extractBooleanLiteral(node) {
|
|
118
|
-
if (node?.type === "Literal" && typeof node.value === "boolean") return node.value;
|
|
119
|
-
return void 0;
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Extract array of strings
|
|
123
|
-
*/
|
|
124
|
-
extractStringArray(node) {
|
|
125
|
-
if (node?.type !== "ArrayExpression") return [];
|
|
126
|
-
return (node.elements || []).filter((el) => el?.type === "Literal" && typeof el.value === "string").map((el) => el.value);
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Extract arguments object
|
|
130
|
-
*/
|
|
131
|
-
extractArgsObject(node) {
|
|
132
|
-
if (node?.type !== "ObjectExpression") return {};
|
|
133
|
-
const args = {};
|
|
134
|
-
for (const prop of node.properties || []) {
|
|
135
|
-
if (prop.type !== "Property" || prop.key?.type !== "Identifier") continue;
|
|
136
|
-
const argName = prop.key.name;
|
|
137
|
-
const argConfig = this.extractArgConfig(prop.value);
|
|
138
|
-
if (argConfig) args[argName] = argConfig;
|
|
139
|
-
}
|
|
140
|
-
return args;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Extract argument configuration
|
|
144
|
-
*/
|
|
145
|
-
extractArgConfig(node) {
|
|
146
|
-
if (node?.type !== "ObjectExpression") return null;
|
|
147
|
-
let type = "String";
|
|
148
|
-
let defaultValue;
|
|
149
|
-
for (const prop of node.properties || []) {
|
|
150
|
-
if (prop.type !== "Property" || prop.key?.type !== "Identifier") continue;
|
|
151
|
-
switch (prop.key.name) {
|
|
152
|
-
case "type": {
|
|
153
|
-
const typeValue = this.extractStringLiteral(prop.value);
|
|
154
|
-
if (typeValue) type = typeValue;
|
|
155
|
-
break;
|
|
156
|
-
}
|
|
157
|
-
case "defaultValue":
|
|
158
|
-
defaultValue = this.extractLiteralValue(prop.value);
|
|
159
|
-
break;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return {
|
|
163
|
-
type,
|
|
164
|
-
...defaultValue !== void 0 && { defaultValue }
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Extract literal value (string, number, boolean)
|
|
169
|
-
*/
|
|
170
|
-
extractLiteralValue(node) {
|
|
171
|
-
if (node?.type === "Literal") return node.value;
|
|
172
|
-
return void 0;
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
/**
|
|
176
|
-
* Generate GraphQL directive schema from parsed directive
|
|
177
|
-
*/
|
|
178
|
-
function generateDirectiveSchema(directive) {
|
|
179
|
-
let args = "";
|
|
180
|
-
if (directive.args && Object.keys(directive.args).length > 0) {
|
|
181
|
-
const argDefs = Object.entries(directive.args).map(([name, arg]) => {
|
|
182
|
-
let defaultValue = "";
|
|
183
|
-
if (arg.defaultValue !== void 0) if (typeof arg.defaultValue === "string") defaultValue = ` = "${arg.defaultValue}"`;
|
|
184
|
-
else defaultValue = ` = ${arg.defaultValue}`;
|
|
185
|
-
return `${name}: ${arg.type}${defaultValue}`;
|
|
186
|
-
});
|
|
187
|
-
args = `(${argDefs.join(", ")})`;
|
|
188
|
-
}
|
|
189
|
-
const locations = directive.locations.join(" | ");
|
|
190
|
-
return `directive @${directive.name}${args} on ${locations}`;
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Generate directive schemas file from scanned directives
|
|
194
|
-
*/
|
|
195
|
-
async function generateDirectiveSchemas(nitro, directives) {
|
|
196
|
-
if (directives.length === 0) return;
|
|
197
|
-
const { existsSync, readFileSync, writeFileSync } = await import("node:fs");
|
|
198
|
-
const { readFile } = await import("node:fs/promises");
|
|
199
|
-
const { resolve } = await import("pathe");
|
|
200
|
-
const directiveSchemas = [];
|
|
201
|
-
const seenDirectives = /* @__PURE__ */ new Set();
|
|
202
|
-
const parser = new DirectiveParser();
|
|
203
|
-
for (const dir of directives) for (const _imp of dir.imports) {
|
|
204
|
-
const fileContent = await readFile(dir.specifier, "utf-8");
|
|
205
|
-
const directiveDefs = await parser.parseDirectives(fileContent, dir.specifier);
|
|
206
|
-
for (const def of directiveDefs) {
|
|
207
|
-
if (seenDirectives.has(def.name)) continue;
|
|
208
|
-
seenDirectives.add(def.name);
|
|
209
|
-
const schema = generateDirectiveSchema(def);
|
|
210
|
-
directiveSchemas.push(schema);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
if (directiveSchemas.length > 0) {
|
|
214
|
-
const directivesPath = resolve(nitro.graphql.serverDir, "_directives.graphql");
|
|
215
|
-
const content = `# WARNING: This file is auto-generated by nitro-graphql
|
|
216
|
-
# Do not modify this file directly. It will be overwritten.
|
|
217
|
-
# To define custom directives, create .directive.ts files using defineDirective()
|
|
218
|
-
|
|
219
|
-
${directiveSchemas.join("\n\n")}`;
|
|
220
|
-
let shouldWrite = true;
|
|
221
|
-
if (existsSync(directivesPath)) {
|
|
222
|
-
const existingContent = readFileSync(directivesPath, "utf-8");
|
|
223
|
-
shouldWrite = existingContent !== content;
|
|
224
|
-
}
|
|
225
|
-
if (shouldWrite) writeFileSync(directivesPath, content, "utf-8");
|
|
226
|
-
if (!nitro.scanSchemas.includes(directivesPath)) nitro.scanSchemas.push(directivesPath);
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Singleton instance for reuse
|
|
231
|
-
*/
|
|
232
|
-
const directiveParser = new DirectiveParser();
|
|
233
|
-
|
|
234
|
-
//#endregion
|
|
235
|
-
export { DirectiveParser, directiveParser, generateDirectiveSchema, generateDirectiveSchemas };
|