Skip to content

Commit 7d5e3c6

Browse files
authored
feat(extension): generator extensions (#1196)
1 parent 584dbbe commit 7d5e3c6

File tree

179 files changed

+6590
-1450
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

179 files changed

+6590
-1450
lines changed

eslint.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export default tsEslint.config({
99
'vitest*.config.ts',
1010
'**/generated/**/*',
1111
'tests/_/schemas/*/graffle/**/*',
12+
'**/tests/fixture/graffle/**/*',
13+
'src/layers/1_Schema/Hybrid/types/Scalar/Scalar.ts', // There is an ESLint error that goes away when ignored leading to a circular issue of either lint error or unused lint disable.
1214
'**/$/**/*',
1315
'legacy/**/*',
1416
'build/**/*',

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@
7171
"scripts": {
7272
"serve:pokemon": "tsx tests/_/services/pokemonManual.ts",
7373
"gen:graffle": "pnpm gen:graffle:tests && pnpm build && cd website && pnpm gen:graffle",
74-
"gen:graffle:tests": "tsx tests/_/schemas/generate.ts",
74+
"gen:graffle:tests": "tsx tests/_/schemas/generate.ts && pnpm graffle --project src/extensions/SchemaErrors/tests/fixture",
75+
"gen:graffle:tests2": "tsx tests/_/schemas/generate.ts",
7576
"graffle": "tsx ./src/cli/generate.ts",
7677
"gen:examples": "tsx scripts/generate-examples-derivatives/generate.ts && pnpm format",
7778
"dev": "rm -rf dist && tsc --watch",

src/cli/generate.ts

Lines changed: 57 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#!/usr/bin/env node
22

33
import { Command } from '@molt/command'
4+
import * as Path from 'node:path'
45
import { z } from 'zod'
56
import { Generator } from '../layers/4_generator/__.js'
6-
import { urlParseSafe } from '../lib/prelude.js'
7+
import { toAbsolutePath } from '../lib/fs.js'
8+
import { isError, urlParseSafe } from '../lib/prelude.js'
79

810
const args = Command.create().description(`Generate a type safe GraphQL client.`)
911
.parameter(
@@ -14,25 +16,15 @@ const args = Command.create().description(`Generate a type safe GraphQL client.`
1416
)
1517
.parameter(
1618
`schema`,
17-
z.string().min(1).describe(
18-
`Path to where your GraphQL schema is. If a URL is given it will be introspected. Otherwise assumed to be a path to your GraphQL SDL file. If a directory path is given, then will look for a "schema.graphql" within that directory. Otherwise will attempt to load the exact file path given.`,
19+
z.string().min(1).optional().describe(
20+
`Path to where your GraphQL schema is. If a URL is given it will be introspected. Otherwise assumed to be a path to your GraphQL SDL file. If a directory path is given, then will look for a "schema.graphql" within that directory. Otherwise will attempt to load the exact file path given. If omitted, then your project must have a configuration file which supplies the schema source.`,
1921
),
2022
)
21-
.parametersExclusive(
22-
`schemaErrorType`,
23-
$ =>
24-
$.parameter(
25-
`schemaErrorTypes`,
26-
z.boolean().describe(
27-
`Use the schema error types pattern. All object types whose name starts with "Error" will be considered to be error types. If you want to specify a custom name pattern then use the other parameter "schemaErrorTypePattern".`,
28-
),
29-
)
30-
.parameter(
31-
`schemaErrorTypePattern`,
32-
z.string().min(1).describe(
33-
`Designate objects whose name matches this JS regular expression as being error types in your schema.`,
34-
),
35-
).default(`schemaErrorTypes`, true),
23+
.parameter(
24+
`project`,
25+
z.string().optional().describe(
26+
`Path to your configuration file. By default will look for "graffle.config.{ts,js,mjs,mts}" in the current working directory. If a directory path is given, then will look for "graffle.config.{ts,js,mjs,mts}" in that directory.`,
27+
),
3628
)
3729
.parameter(
3830
`defaultSchemaUrl`,
@@ -43,44 +35,20 @@ const args = Command.create().description(`Generate a type safe GraphQL client.`
4335
z.string().min(1).describe(
4436
`A GraphQL endpoint to be used as the default URL in the generated client for requests.`,
4537
),
46-
]).default(true),
38+
]).optional(),
4739
)
4840
.parameter(
4941
`output`,
50-
z.string().min(1).default(`./graffle`).describe(
51-
`Directory path for where to output the generated TypeScript files.`,
42+
z.string().min(1).optional().describe(
43+
`Directory path for where to output the generated TypeScript files. By default will be './graffle' in the project root.`,
5244
),
5345
)
5446
.parameter(
5547
`format`,
5648
z.boolean().describe(
5749
`Try to format the generated files. At attempt to use dprint will be made. You need to have these dependencies installed in your project: @dprint/formatter, @dprint/typescript.`,
5850
)
59-
.default(true),
60-
)
61-
.parameter(
62-
`libraryPathClient`,
63-
z.string().optional().describe(
64-
`Custom location for where the generated code should import the Graffle "client" module from.`,
65-
),
66-
)
67-
.parameter(
68-
`libraryPathSchema`,
69-
z.string().optional().describe(
70-
`Custom location for where the generated code should import the Graffle "schema" module from.`,
71-
),
72-
)
73-
.parameter(
74-
`libraryPathScalars`,
75-
z.string().optional().describe(
76-
`Custom location for where the generated code should import the Graffle "scalars" module from.`,
77-
),
78-
)
79-
.parameter(
80-
`libraryPathUtilitiesForGenerated`,
81-
z.string().optional().describe(
82-
`Custom location for where the generated code should import the Graffle "utilities-for-generated" module from.`,
83-
),
51+
.optional(),
8452
)
8553
.settings({
8654
parameters: {
@@ -89,33 +57,51 @@ const args = Command.create().description(`Generate a type safe GraphQL client.`
8957
})
9058
.parse()
9159

92-
const url = urlParseSafe(args.schema)
60+
// --- Resolve Config File ---
61+
62+
const configModule = await Generator.Config.load({ filePath: args.project })
63+
if (isError(configModule)) throw configModule
64+
if (!configModule.builder && args.project) {
65+
throw new Error(`Could not find a configuration file at "${configModule.paths.join(`, `)}".`)
66+
}
67+
68+
// --- Resolve Default Schema URL ---
9369

9470
const defaultSchemaUrl = typeof args.defaultSchemaUrl === `string`
9571
? new URL(args.defaultSchemaUrl)
9672
: args.defaultSchemaUrl
9773

98-
const format = args.format
99-
100-
const schemaSource = url
101-
? { type: `url` as const, url }
102-
: { type: `sdl` as const, dirOrFilePath: args.schema }
103-
104-
await Generator.generate({
105-
format,
106-
schemaSource,
107-
defaultSchemaUrl,
108-
name: args.name,
109-
outputDirPath: args.output,
110-
errorTypeNamePattern: args.schemaErrorType._tag === `schemaErrorTypePattern`
111-
? new RegExp(args.schemaErrorType.value)
112-
: args.schemaErrorType.value
113-
? /^Error.+/
114-
: undefined,
115-
libraryPaths: {
116-
client: args.libraryPathClient,
117-
schema: args.libraryPathSchema,
118-
scalars: args.libraryPathScalars,
119-
utilitiesForGenerated: args.libraryPathUtilitiesForGenerated,
120-
},
121-
})
74+
// --- Resolve Schema ---
75+
76+
const url = args.schema ? urlParseSafe(args.schema) : null
77+
78+
const schemaViaCLI = args.schema
79+
? url
80+
? { type: `url` as const, url }
81+
: { type: `sdl` as const, dirOrFilePath: Path.join(process.cwd(), args.schema) }
82+
: undefined
83+
84+
const schema = schemaViaCLI ?? configModule.builder?._.input.schema
85+
86+
if (!schema) {
87+
throw new Error(`No schema source provided. Either specify a schema source in the config file or via the CLI.`)
88+
}
89+
90+
const currentWorkingDirectory = configModule.path ? Path.dirname(configModule.path) : process.cwd()
91+
92+
// --- Merge Inputs ---
93+
94+
const input = {
95+
...configModule.builder?._.input,
96+
currentWorkingDirectory,
97+
schema,
98+
}
99+
100+
if (defaultSchemaUrl !== undefined) input.defaultSchemaUrl = defaultSchemaUrl
101+
if (args.format !== undefined) input.format = args.format
102+
if (args.name !== undefined) input.name = args.name
103+
if (args.output !== undefined) input.outputDirPath = toAbsolutePath(process.cwd(), args.output)
104+
105+
// --- Generate ---
106+
107+
await Generator.generate(input)

src/entrypoints/_Generator.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { generate } from '../layers/4_generator/_.js'
2+
export { create } from '../layers/4_generator/configFile/builder.js'

src/entrypoints/__Generator.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * as Generator from './_Generator.js'

src/entrypoints/extensionkit.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { createExtension as createGeneratorExtension } from '../layers/4_generator/extension/create.js'
2+
export { createExtension } from '../layers/6_client/extension/extension.js'

src/entrypoints/generator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { generate } from '../layers/4_generator/_.js'
1+
export * from './__Generator.js'

src/entrypoints/utilities-for-generated.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
export { type Simplify } from 'type-fest'
2-
export { type SchemaDrivenDataMap } from '../extensions/CustomScalars/schemaDrivenDataMap/types.js'
2+
export { type SchemaDrivenDataMap } from '../extensions/CustomScalars/schemaDrivenDataMap/__.js'
33
export * from '../layers/2_Select/__.js'
4-
export { type SchemaIndex as SchemaIndexBase } from '../layers/4_generator/generators/SchemaIndex.js'
4+
export { type Schema as SchemaIndexBase } from '../layers/4_generator/generators/Schema.js'
5+
export { type GlobalRegistry } from '../layers/4_generator/globalRegistry.js'
56
export type {
67
ConfigGetOutputError,
78
HandleOutput,

0 commit comments

Comments
 (0)