Skip to content

Commit

Permalink
feat(extension): generator extensions (#1196)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonkuhrt authored Oct 18, 2024
1 parent 584dbbe commit 7d5e3c6
Show file tree
Hide file tree
Showing 179 changed files with 6,590 additions and 1,450 deletions.
2 changes: 2 additions & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ export default tsEslint.config({
'vitest*.config.ts',
'**/generated/**/*',
'tests/_/schemas/*/graffle/**/*',
'**/tests/fixture/graffle/**/*',
'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.
'**/$/**/*',
'legacy/**/*',
'build/**/*',
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@
"scripts": {
"serve:pokemon": "tsx tests/_/services/pokemonManual.ts",
"gen:graffle": "pnpm gen:graffle:tests && pnpm build && cd website && pnpm gen:graffle",
"gen:graffle:tests": "tsx tests/_/schemas/generate.ts",
"gen:graffle:tests": "tsx tests/_/schemas/generate.ts && pnpm graffle --project src/extensions/SchemaErrors/tests/fixture",
"gen:graffle:tests2": "tsx tests/_/schemas/generate.ts",
"graffle": "tsx ./src/cli/generate.ts",
"gen:examples": "tsx scripts/generate-examples-derivatives/generate.ts && pnpm format",
"dev": "rm -rf dist && tsc --watch",
Expand Down
128 changes: 57 additions & 71 deletions src/cli/generate.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#!/usr/bin/env node

import { Command } from '@molt/command'
import * as Path from 'node:path'
import { z } from 'zod'
import { Generator } from '../layers/4_generator/__.js'
import { urlParseSafe } from '../lib/prelude.js'
import { toAbsolutePath } from '../lib/fs.js'
import { isError, urlParseSafe } from '../lib/prelude.js'

const args = Command.create().description(`Generate a type safe GraphQL client.`)
.parameter(
Expand All @@ -14,25 +16,15 @@ const args = Command.create().description(`Generate a type safe GraphQL client.`
)
.parameter(
`schema`,
z.string().min(1).describe(
`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.`,
z.string().min(1).optional().describe(
`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.`,
),
)
.parametersExclusive(
`schemaErrorType`,
$ =>
$.parameter(
`schemaErrorTypes`,
z.boolean().describe(
`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".`,
),
)
.parameter(
`schemaErrorTypePattern`,
z.string().min(1).describe(
`Designate objects whose name matches this JS regular expression as being error types in your schema.`,
),
).default(`schemaErrorTypes`, true),
.parameter(
`project`,
z.string().optional().describe(
`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.`,
),
)
.parameter(
`defaultSchemaUrl`,
Expand All @@ -43,44 +35,20 @@ const args = Command.create().description(`Generate a type safe GraphQL client.`
z.string().min(1).describe(
`A GraphQL endpoint to be used as the default URL in the generated client for requests.`,
),
]).default(true),
]).optional(),
)
.parameter(
`output`,
z.string().min(1).default(`./graffle`).describe(
`Directory path for where to output the generated TypeScript files.`,
z.string().min(1).optional().describe(
`Directory path for where to output the generated TypeScript files. By default will be './graffle' in the project root.`,
),
)
.parameter(
`format`,
z.boolean().describe(
`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.`,
)
.default(true),
)
.parameter(
`libraryPathClient`,
z.string().optional().describe(
`Custom location for where the generated code should import the Graffle "client" module from.`,
),
)
.parameter(
`libraryPathSchema`,
z.string().optional().describe(
`Custom location for where the generated code should import the Graffle "schema" module from.`,
),
)
.parameter(
`libraryPathScalars`,
z.string().optional().describe(
`Custom location for where the generated code should import the Graffle "scalars" module from.`,
),
)
.parameter(
`libraryPathUtilitiesForGenerated`,
z.string().optional().describe(
`Custom location for where the generated code should import the Graffle "utilities-for-generated" module from.`,
),
.optional(),
)
.settings({
parameters: {
Expand All @@ -89,33 +57,51 @@ const args = Command.create().description(`Generate a type safe GraphQL client.`
})
.parse()

const url = urlParseSafe(args.schema)
// --- Resolve Config File ---

const configModule = await Generator.Config.load({ filePath: args.project })
if (isError(configModule)) throw configModule
if (!configModule.builder && args.project) {
throw new Error(`Could not find a configuration file at "${configModule.paths.join(`, `)}".`)
}

// --- Resolve Default Schema URL ---

const defaultSchemaUrl = typeof args.defaultSchemaUrl === `string`
? new URL(args.defaultSchemaUrl)
: args.defaultSchemaUrl

const format = args.format

const schemaSource = url
? { type: `url` as const, url }
: { type: `sdl` as const, dirOrFilePath: args.schema }

await Generator.generate({
format,
schemaSource,
defaultSchemaUrl,
name: args.name,
outputDirPath: args.output,
errorTypeNamePattern: args.schemaErrorType._tag === `schemaErrorTypePattern`
? new RegExp(args.schemaErrorType.value)
: args.schemaErrorType.value
? /^Error.+/
: undefined,
libraryPaths: {
client: args.libraryPathClient,
schema: args.libraryPathSchema,
scalars: args.libraryPathScalars,
utilitiesForGenerated: args.libraryPathUtilitiesForGenerated,
},
})
// --- Resolve Schema ---

const url = args.schema ? urlParseSafe(args.schema) : null

const schemaViaCLI = args.schema
? url
? { type: `url` as const, url }
: { type: `sdl` as const, dirOrFilePath: Path.join(process.cwd(), args.schema) }
: undefined

const schema = schemaViaCLI ?? configModule.builder?._.input.schema

if (!schema) {
throw new Error(`No schema source provided. Either specify a schema source in the config file or via the CLI.`)
}

const currentWorkingDirectory = configModule.path ? Path.dirname(configModule.path) : process.cwd()

// --- Merge Inputs ---

const input = {
...configModule.builder?._.input,
currentWorkingDirectory,
schema,
}

if (defaultSchemaUrl !== undefined) input.defaultSchemaUrl = defaultSchemaUrl
if (args.format !== undefined) input.format = args.format
if (args.name !== undefined) input.name = args.name
if (args.output !== undefined) input.outputDirPath = toAbsolutePath(process.cwd(), args.output)

// --- Generate ---

await Generator.generate(input)
2 changes: 2 additions & 0 deletions src/entrypoints/_Generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { generate } from '../layers/4_generator/_.js'
export { create } from '../layers/4_generator/configFile/builder.js'
1 change: 1 addition & 0 deletions src/entrypoints/__Generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * as Generator from './_Generator.js'
2 changes: 2 additions & 0 deletions src/entrypoints/extensionkit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { createExtension as createGeneratorExtension } from '../layers/4_generator/extension/create.js'
export { createExtension } from '../layers/6_client/extension/extension.js'
2 changes: 1 addition & 1 deletion src/entrypoints/generator.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export { generate } from '../layers/4_generator/_.js'
export * from './__Generator.js'
5 changes: 3 additions & 2 deletions src/entrypoints/utilities-for-generated.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
export { type Simplify } from 'type-fest'
export { type SchemaDrivenDataMap } from '../extensions/CustomScalars/schemaDrivenDataMap/types.js'
export { type SchemaDrivenDataMap } from '../extensions/CustomScalars/schemaDrivenDataMap/__.js'
export * from '../layers/2_Select/__.js'
export { type SchemaIndex as SchemaIndexBase } from '../layers/4_generator/generators/SchemaIndex.js'
export { type Schema as SchemaIndexBase } from '../layers/4_generator/generators/Schema.js'
export { type GlobalRegistry } from '../layers/4_generator/globalRegistry.js'
export type {
ConfigGetOutputError,
HandleOutput,
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/CustomScalars/decode.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Kind } from 'graphql'
import { applyCodec } from '../../layers/1_Schema/Hybrid/types/Scalar/Scalar.js'
import type { Grafaid } from '../../lib/grafaid/__.js'
import { SchemaDrivenDataMap } from './schemaDrivenDataMap/types.js'
import { SchemaDrivenDataMap } from './schemaDrivenDataMap/__.js'

/**
* If a document is given then aliases will be decoded as well.
Expand Down
2 changes: 1 addition & 1 deletion src/extensions/CustomScalars/encode.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { applyCodec } from '../../layers/1_Schema/Hybrid/types/Scalar/Scalar.js'
import { Grafaid } from '../../lib/grafaid/__.js'
import { SchemaDrivenDataMap } from './schemaDrivenDataMap/types.js'
import { SchemaDrivenDataMap } from './schemaDrivenDataMap/__.js'

export const encodeRequestVariables = ({ sddm, request }: {
sddm: SchemaDrivenDataMap
Expand Down
5 changes: 5 additions & 0 deletions src/extensions/CustomScalars/schemaDrivenDataMap/__.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * as SchemaDrivenDataMap from './types.js'

import { type SchemaDrivenDataMap as SchemaDrivenDataMap_ } from './types.js'

export type SchemaDrivenDataMap = SchemaDrivenDataMap_
Loading

0 comments on commit 7d5e3c6

Please sign in to comment.