graphql-request
Advanced tools
Comparing version 7.2.0-next.11 to 7.2.0-next.12
#!/usr/bin/env node | ||
import { Command } from '@molt/command'; | ||
import { buildClientSchema } from 'graphql'; | ||
import * as Path from 'node:path'; | ||
import { z } from 'zod'; | ||
import { generateFiles } from '../layers/2_generator/files.js'; | ||
import { safeParseUrl } from '../lib/prelude.js'; | ||
import { introspectionQuery } from './_helpers.js'; | ||
const args = Command.create().description(`Generate a type safe GraphQL client.`) | ||
.parameter(`schema`, z.string().min(1).describe(`File path to where your GraphQL schema is.`)) | ||
.parameter(`name`, z.string().min(1).optional().describe(`The name of your client. If you are not generating multiple clients you probably do not need this. Otherwise you need to differentiate the clients so that their global type registrations do not conflict. It is possible to leave one client unnamed which will become the default client at the type level (e.g. in configuration etc.)`)) | ||
.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 file path to your GraphQL SDL file.`)) | ||
.parameter(`output`, z.string().min(1).describe(`Directory path for where to output the generated TypeScript files.`)) | ||
@@ -12,2 +16,5 @@ .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(`format`, z.boolean().describe(`Format the generated files using dprint.`).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.`)) | ||
.settings({ | ||
@@ -19,12 +26,41 @@ parameters: { | ||
.parse(); | ||
await generateFiles({ | ||
sourceDirPath: Path.dirname(args.schema), | ||
outputDirPath: args.output, | ||
format: args.format, | ||
errorTypeNamePattern: args.schemaErrorType._tag === `schemaErrorTypePattern` | ||
? new RegExp(args.schemaErrorType.value) | ||
: args.schemaErrorType.value | ||
? /^Error.+/ | ||
: undefined, | ||
}); | ||
const url = safeParseUrl(args.schema); | ||
if (url) { | ||
const data = await introspectionQuery(url); | ||
const schemaSource = buildClientSchema(data); | ||
await generateFiles({ | ||
name: args.name, | ||
libraryPaths: { | ||
client: args.libraryPathClient, | ||
schema: args.libraryPathSchema, | ||
scalars: args.libraryPathScalars, | ||
}, | ||
schemaSource: schemaSource, | ||
outputDirPath: args.output, | ||
format: args.format, | ||
errorTypeNamePattern: args.schemaErrorType._tag === `schemaErrorTypePattern` | ||
? new RegExp(args.schemaErrorType.value) | ||
: args.schemaErrorType.value | ||
? /^Error.+/ | ||
: undefined, | ||
}); | ||
} | ||
else { | ||
await generateFiles({ | ||
name: args.name, | ||
libraryPaths: { | ||
client: args.libraryPathClient, | ||
schema: args.libraryPathSchema, | ||
scalars: args.libraryPathScalars, | ||
}, | ||
sourceDirPath: Path.dirname(args.schema), | ||
outputDirPath: args.output, | ||
format: args.format, | ||
errorTypeNamePattern: args.schemaErrorType._tag === `schemaErrorTypePattern` | ||
? new RegExp(args.schemaErrorType.value) | ||
: args.schemaErrorType.value | ||
? /^Error.+/ | ||
: undefined, | ||
}); | ||
} | ||
//# sourceMappingURL=generate.js.map |
@@ -1,2 +0,2 @@ | ||
export { create } from '../../layers/1_Schema/Hybrid/types/Scalar/Scalar.js'; | ||
export { Boolean, create, Float, ID, Int, String } from '../../layers/1_Schema/Hybrid/types/Scalar/Scalar.js'; | ||
//# sourceMappingURL=scalars.d.ts.map |
@@ -1,2 +0,4 @@ | ||
export { create } from '../../layers/1_Schema/Hybrid/types/Scalar/Scalar.js'; | ||
// todo this module should be tailored to users defining their own customer scalars. | ||
// todo ... so, consider a different entrypoint for these scalar types that are just for the generated clients. | ||
export { Boolean, create, Float, ID, Int, String } from '../../layers/1_Schema/Hybrid/types/Scalar/Scalar.js'; | ||
//# sourceMappingURL=scalars.js.map |
@@ -0,1 +1,2 @@ | ||
import { GraphQLList, GraphQLNonNull, GraphQLScalarType, } from 'graphql'; | ||
import { isEnumType, isInputObjectType, isInterfaceType, isListType, isNamedType, isObjectType, isScalarType, isUnionType, } from 'graphql'; | ||
@@ -58,2 +59,3 @@ import { hasMutation, hasQuery, hasSubscription, unwrapToNamed, unwrapToNonNull } from '../../../lib/graphql.js'; | ||
}; | ||
const commentTsIgnoreCircDep = `// @ts-ignore - circular types cannot infer. Ignore in case there are any. This comment is always added, it does not indicate if this particular type could infer or not.`; | ||
const union = (_config, type) => { | ||
@@ -63,4 +65,4 @@ // todo probably need thunks here | ||
return ` | ||
// @ts-ignore - circular types cannot infer. Ignore in case there are any. This comment is always added, it does not indicate if this particular type could infer or not. | ||
export const ${type.name} = $.Union(\`${type.name}\`, [${members}])\n`; | ||
${commentTsIgnoreCircDep} | ||
export const ${type.name} = $.Union(\`${type.name}\`, [${members}])\n`; | ||
}; | ||
@@ -71,3 +73,5 @@ const interface$ = (config, type) => { | ||
const fields = Object.values(type.getFields()).map((field) => { | ||
return `${field.name}: ${outputField(config, field)}`; | ||
// todo test case for this directive being present | ||
const maybeCommentTsIgnoreCircDep = isScalarTypeDirectOrNested(field.type) ? `` : commentTsIgnoreCircDep + `\n`; | ||
return `${maybeCommentTsIgnoreCircDep}${field.name}: ${outputField(config, field)}`; | ||
}).join(`,\n`); | ||
@@ -84,6 +88,8 @@ return `export const ${type.name} = $.Interface(\`${type.name}\`, {${fields}}, [${implementors}])`; | ||
const fields = Object.values(type.getFields()).map((field) => { | ||
return `${field.name}: ${outputField(config, field)}`; | ||
// todo test case for this directive being present | ||
const maybeCommentTsIgnoreCircDep = isScalarTypeDirectOrNested(field.type) ? `` : commentTsIgnoreCircDep + `\n`; | ||
return `${maybeCommentTsIgnoreCircDep}${field.name}: ${outputField(config, field)}`; | ||
}).join(`,\n`); | ||
return ` | ||
// @ts-ignore - circular types cannot infer. Ignore in case there are any. This comment is always added, it does not indicate if this particular type could infer or not. | ||
${commentTsIgnoreCircDep} | ||
export const ${type.name} = $.Object$(\`${type.name}\`, { | ||
@@ -158,2 +164,7 @@ ${fields} | ||
}; | ||
const isScalarTypeDirectOrNested = (type) => { | ||
return type instanceof GraphQLScalarType | ||
|| type instanceof GraphQLNonNull && isScalarTypeDirectOrNested(type.ofType) | ||
|| type instanceof GraphQLList && isScalarTypeDirectOrNested(type.ofType); | ||
}; | ||
//# sourceMappingURL=SchemaRuntime.js.map |
@@ -0,14 +1,21 @@ | ||
import { type GraphQLSchema } from 'graphql'; | ||
import type { OptionsInput } from './generateCode.js'; | ||
import { type Input as GenerateInput } from './generateCode.js'; | ||
export interface Input { | ||
outputDirPath: string; | ||
name?: string; | ||
outputDirPath: string; | ||
code?: Omit<GenerateInput, 'schemaSource' | 'sourceDirPath' | 'options'>; | ||
sourceDirPath?: string; | ||
sourceCustomScalarCodecsFilePath?: string; | ||
schemaSource?: GraphQLSchema; | ||
schemaPath?: string; | ||
format?: boolean; | ||
errorTypeNamePattern?: OptionsInput['errorTypeNamePattern']; | ||
libraryPaths?: { | ||
client?: string; | ||
schema?: string; | ||
scalars?: string; | ||
}; | ||
} | ||
export declare const generateFiles: (input: Input) => Promise<void>; | ||
//# sourceMappingURL=files.d.ts.map |
@@ -0,1 +1,2 @@ | ||
import { printSchema } from 'graphql'; | ||
import _ from 'json-bigint'; | ||
@@ -19,3 +20,3 @@ import fs from 'node:fs/promises'; | ||
const schemaPath = input.schemaPath ?? Path.join(sourceDirPath, `schema.graphql`); | ||
const schemaSource = await fs.readFile(schemaPath, `utf8`); | ||
const schemaSource = input.schemaSource ? printSchema(input.schemaSource) : await fs.readFile(schemaPath, `utf8`); | ||
// todo support other extensions: .tsx,.js,.mjs,.cjs | ||
@@ -28,2 +29,5 @@ const customScalarCodecsFilePath = input.sourceCustomScalarCodecsFilePath | ||
const codes = generateCode({ | ||
libraryPaths: { | ||
...input.libraryPaths, | ||
}, | ||
name: input.name, | ||
@@ -30,0 +34,0 @@ schemaSource, |
@@ -91,3 +91,4 @@ import type { Simplify } from 'type-fest'; | ||
} | ||
export declare const safeParseUrl: (url: string) => false | URL; | ||
export {}; | ||
//# sourceMappingURL=prelude.d.ts.map |
@@ -131,2 +131,10 @@ export const uppercase = (str) => str.toUpperCase(); | ||
// type AsBoolean<T> = T extends boolean ? T : never | ||
export const safeParseUrl = (url) => { | ||
try { | ||
return new URL(url); | ||
} | ||
catch { | ||
return false; | ||
} | ||
}; | ||
//# sourceMappingURL=prelude.js.map |
{ | ||
"name": "graphql-request", | ||
"version": "7.2.0-next.11", | ||
"version": "7.2.0-next.12", | ||
"packageManager": "pnpm@9.6.0", | ||
@@ -60,3 +60,5 @@ "type": "module", | ||
"scripts": { | ||
"graffle": "tsx ./src/cli/generate.ts", | ||
"gen:test:schema": "tsx tests/_/schemaGenerate.ts", | ||
"gen:docs:examples:clients:countries": "pnpm graffle --name countriesClient --schema https://countries.trevorblades.com/graphql --output ./examples/generated-clients/countries --libraryPathClient ../../../src/entrypoints/alpha/client.js --libraryPathSchema ../../../src/entrypoints/alpha/schema.js --libraryPathScalars ../../../src/entrypoints/alpha/scalars.js", | ||
"demo": "tsx src/cli/generateSchema.ts && dprint fmt src/demo.ts", | ||
@@ -63,0 +65,0 @@ "dev": "rm -rf dist && tsc --watch", |
#!/usr/bin/env node | ||
import { Command } from '@molt/command' | ||
import { buildClientSchema } from 'graphql' | ||
import * as Path from 'node:path' | ||
import { z } from 'zod' | ||
import { generateFiles } from '../layers/2_generator/files.js' | ||
import { safeParseUrl } from '../lib/prelude.js' | ||
import { introspectionQuery } from './_helpers.js' | ||
const args = Command.create().description(`Generate a type safe GraphQL client.`) | ||
.parameter(`schema`, z.string().min(1).describe(`File path to where your GraphQL schema is.`)) | ||
.parameter( | ||
`name`, | ||
z.string().min(1).optional().describe( | ||
`The name of your client. If you are not generating multiple clients you probably do not need this. Otherwise you need to differentiate the clients so that their global type registrations do not conflict. It is possible to leave one client unnamed which will become the default client at the type level (e.g. in configuration etc.)`, | ||
), | ||
) | ||
.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 file path to your GraphQL SDL file.`, | ||
), | ||
) | ||
.parameter( | ||
`output`, | ||
@@ -33,2 +47,20 @@ z.string().min(1).describe( | ||
.parameter(`format`, z.boolean().describe(`Format the generated files using dprint.`).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.`, | ||
), | ||
) | ||
.settings({ | ||
@@ -41,11 +73,40 @@ parameters: { | ||
await generateFiles({ | ||
sourceDirPath: Path.dirname(args.schema), | ||
outputDirPath: args.output, | ||
format: args.format, | ||
errorTypeNamePattern: args.schemaErrorType._tag === `schemaErrorTypePattern` | ||
? new RegExp(args.schemaErrorType.value) | ||
: args.schemaErrorType.value | ||
? /^Error.+/ | ||
: undefined, | ||
}) | ||
const url = safeParseUrl(args.schema) | ||
if (url) { | ||
const data = await introspectionQuery(url) | ||
const schemaSource = buildClientSchema(data) | ||
await generateFiles({ | ||
name: args.name, | ||
libraryPaths: { | ||
client: args.libraryPathClient, | ||
schema: args.libraryPathSchema, | ||
scalars: args.libraryPathScalars, | ||
}, | ||
schemaSource: schemaSource, | ||
outputDirPath: args.output, | ||
format: args.format, | ||
errorTypeNamePattern: args.schemaErrorType._tag === `schemaErrorTypePattern` | ||
? new RegExp(args.schemaErrorType.value) | ||
: args.schemaErrorType.value | ||
? /^Error.+/ | ||
: undefined, | ||
}) | ||
} else { | ||
await generateFiles({ | ||
name: args.name, | ||
libraryPaths: { | ||
client: args.libraryPathClient, | ||
schema: args.libraryPathSchema, | ||
scalars: args.libraryPathScalars, | ||
}, | ||
sourceDirPath: Path.dirname(args.schema), | ||
outputDirPath: args.output, | ||
format: args.format, | ||
errorTypeNamePattern: args.schemaErrorType._tag === `schemaErrorTypePattern` | ||
? new RegExp(args.schemaErrorType.value) | ||
: args.schemaErrorType.value | ||
? /^Error.+/ | ||
: undefined, | ||
}) | ||
} |
@@ -1,1 +0,3 @@ | ||
export { create } from '../../layers/1_Schema/Hybrid/types/Scalar/Scalar.js' | ||
// todo this module should be tailored to users defining their own customer scalars. | ||
// todo ... so, consider a different entrypoint for these scalar types that are just for the generated clients. | ||
export { Boolean, create, Float, ID, Int, String } from '../../layers/1_Schema/Hybrid/types/Scalar/Scalar.js' |
@@ -1,9 +0,12 @@ | ||
import type { | ||
GraphQLArgument, | ||
GraphQLEnumType, | ||
GraphQLInputField, | ||
GraphQLInputObjectType, | ||
GraphQLInterfaceType, | ||
import { | ||
type GraphQLArgument, | ||
type GraphQLEnumType, | ||
type GraphQLInputField, | ||
type GraphQLInputObjectType, | ||
type GraphQLInterfaceType, | ||
GraphQLList, | ||
GraphQLNonNull, | ||
type GraphQLOutputType, | ||
GraphQLScalarType, | ||
GraphQLUnionType, | ||
type GraphQLUnionType, | ||
} from 'graphql' | ||
@@ -105,2 +108,5 @@ import { | ||
const commentTsIgnoreCircDep = | ||
`// @ts-ignore - circular types cannot infer. Ignore in case there are any. This comment is always added, it does not indicate if this particular type could infer or not.` | ||
const union = (_config: Config, type: GraphQLUnionType) => { | ||
@@ -110,4 +116,4 @@ // todo probably need thunks here | ||
return ` | ||
// @ts-ignore - circular types cannot infer. Ignore in case there are any. This comment is always added, it does not indicate if this particular type could infer or not. | ||
export const ${type.name} = $.Union(\`${type.name}\`, [${members}])\n` | ||
${commentTsIgnoreCircDep} | ||
export const ${type.name} = $.Union(\`${type.name}\`, [${members}])\n` | ||
} | ||
@@ -121,3 +127,5 @@ | ||
const fields = Object.values(type.getFields()).map((field) => { | ||
return `${field.name}: ${outputField(config, field)}` | ||
// todo test case for this directive being present | ||
const maybeCommentTsIgnoreCircDep = isScalarTypeDirectOrNested(field.type) ? `` : commentTsIgnoreCircDep + `\n` | ||
return `${maybeCommentTsIgnoreCircDep}${field.name}: ${outputField(config, field)}` | ||
}).join(`,\n`) | ||
@@ -136,6 +144,8 @@ return `export const ${type.name} = $.Interface(\`${type.name}\`, {${fields}}, [${implementors}])` | ||
const fields = Object.values(type.getFields()).map((field) => { | ||
return `${field.name}: ${outputField(config, field)}` | ||
// todo test case for this directive being present | ||
const maybeCommentTsIgnoreCircDep = isScalarTypeDirectOrNested(field.type) ? `` : commentTsIgnoreCircDep + `\n` | ||
return `${maybeCommentTsIgnoreCircDep}${field.name}: ${outputField(config, field)}` | ||
}).join(`,\n`) | ||
return ` | ||
// @ts-ignore - circular types cannot infer. Ignore in case there are any. This comment is always added, it does not indicate if this particular type could infer or not. | ||
${commentTsIgnoreCircDep} | ||
export const ${type.name} = $.Object$(\`${type.name}\`, { | ||
@@ -219,1 +229,7 @@ ${fields} | ||
} | ||
const isScalarTypeDirectOrNested = (type: GraphQLOutputType): boolean => { | ||
return type instanceof GraphQLScalarType | ||
|| type instanceof GraphQLNonNull && isScalarTypeDirectOrNested(type.ofType) | ||
|| type instanceof GraphQLList && isScalarTypeDirectOrNested(type.ofType) | ||
} |
import type { Formatter } from '@dprint/formatter' | ||
import { type GraphQLSchema, printSchema } from 'graphql' | ||
import _ from 'json-bigint' | ||
@@ -10,10 +11,16 @@ import fs from 'node:fs/promises' | ||
export interface Input { | ||
outputDirPath: string | ||
name?: string | ||
outputDirPath: string | ||
code?: Omit<GenerateInput, 'schemaSource' | 'sourceDirPath' | 'options'> | ||
sourceDirPath?: string | ||
sourceCustomScalarCodecsFilePath?: string | ||
schemaSource?: GraphQLSchema | ||
schemaPath?: string | ||
format?: boolean | ||
errorTypeNamePattern?: OptionsInput['errorTypeNamePattern'] | ||
libraryPaths?: { | ||
client?: string | ||
schema?: string | ||
scalars?: string | ||
} | ||
} | ||
@@ -34,3 +41,3 @@ | ||
const schemaPath = input.schemaPath ?? Path.join(sourceDirPath, `schema.graphql`) | ||
const schemaSource = await fs.readFile(schemaPath, `utf8`) | ||
const schemaSource = input.schemaSource ? printSchema(input.schemaSource) : await fs.readFile(schemaPath, `utf8`) | ||
@@ -48,2 +55,5 @@ // todo support other extensions: .tsx,.js,.mjs,.cjs | ||
const codes = generateCode({ | ||
libraryPaths: { | ||
...input.libraryPaths, | ||
}, | ||
name: input.name, | ||
@@ -50,0 +60,0 @@ schemaSource, |
@@ -384,1 +384,9 @@ import type { Simplify } from 'type-fest' | ||
// type AsBoolean<T> = T extends boolean ? T : never | ||
export const safeParseUrl = (url: string) => { | ||
try { | ||
return new URL(url) | ||
} catch { | ||
return false | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
966012
635
15043