From fdca0a7b24d366aa995f31b8df22449b8aefe4a1 Mon Sep 17 00:00:00 2001 From: Jason Kuhrt Date: Thu, 11 Apr 2024 20:24:19 -0400 Subject: [PATCH] feat(ts-client): index sans dollar namespace (#767) --- package.json | 4 +- pnpm-lock.yaml | 68 +++++++++---------- src/ResultSet/ResultSet.test-d.ts | 4 +- src/SelectionSet/SelectionSet.test-d.ts | 4 +- .../toGraphQLDocumentString.test.ts | 6 +- src/SelectionSet/toGraphQLDocumentString.ts | 4 +- src/client.test.ts | 6 +- src/generator/code/code.ts | 4 ++ src/generator/code/index.ts | 39 +++++------ src/generator/files.ts | 31 +++++++-- tests/ts/_/schema/generated/Index.ts | 46 ++++++------- 11 files changed, 115 insertions(+), 101 deletions(-) diff --git a/package.json b/package.json index 4531f2ef8..2bb611ded 100644 --- a/package.json +++ b/package.json @@ -79,7 +79,7 @@ }, "dependencies": { "@dprint/formatter": "^0.2.1", - "@dprint/typescript": "^0.90.0", + "@dprint/typescript": "^0.90.1", "@graphql-typed-document-node/core": "^3.2.0", "@molt/command": "^0.9.0", "dprint": "^0.45.1", @@ -118,6 +118,6 @@ "type-fest": "^4.15.0", "typescript": "^5.4.5", "typescript-eslint": "^7.6.0", - "vitest": "^1.4.0" + "vitest": "^1.5.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 15709f6da..cdba4aa0c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,8 +9,8 @@ dependencies: specifier: ^0.2.1 version: 0.2.1 '@dprint/typescript': - specifier: ^0.90.0 - version: 0.90.0 + specifier: ^0.90.1 + version: 0.90.1 '@graphql-typed-document-node/core': specifier: ^3.2.0 version: 3.2.0(graphql@16.8.1) @@ -113,8 +113,8 @@ devDependencies: specifier: ^7.6.0 version: 7.6.0(eslint@9.0.0)(typescript@5.4.5) vitest: - specifier: ^1.4.0 - version: 1.4.0(@types/node@20.12.7)(happy-dom@14.7.1) + specifier: ^1.5.0 + version: 1.5.0(@types/node@20.12.7)(happy-dom@14.7.1) packages: @@ -307,8 +307,8 @@ packages: dev: false optional: true - /@dprint/typescript@0.90.0: - resolution: {integrity: sha512-8+iMqWEk0qKwSEj8qQCLoJHt29WY9q+EOG4rmemnVZtJmiqJyKliryegiIJ4xspThG05WZRLWwckVNorLWjEJA==} + /@dprint/typescript@0.90.1: + resolution: {integrity: sha512-ZHrg5U1rjNatxM0vqeGNB1roO/IjWuONP9MQr4U1CrybZj868O7d4ps0qM/ERqOleOZjfS4MBUUtWVXJt5AUUw==} dev: false /@dprint/win32-x64@0.45.1: @@ -1457,8 +1457,8 @@ packages: resolution: {integrity: sha512-3wXCiM8croUnhg9LdtZUJQwNcQYGWxxdOWDjPe1ykCqJFPVpzAKfs/2dgSoCtAvdPeaponcWPI7mPcGGp9dkKQ==} dev: true - /@types/eslint@8.56.7: - resolution: {integrity: sha512-SjDvI/x3zsZnOkYZ3lCt9lOZWZLB2jIlNKz+LBgCtDurK0JZcwucxYHn1w2BJkD34dgX9Tjnak0txtq4WTggEA==} + /@types/eslint@8.56.8: + resolution: {integrity: sha512-LdDdQVDzDXf3ijhhMnE27C5vc0QEknD8GiMR/Hi+fVbdZNfAfCy2j69m0LjUd2MAy0+kIgnOtd5ndTmDk/VWCA==} dependencies: '@types/estree': 1.0.5 '@types/json-schema': 7.0.15 @@ -1467,7 +1467,7 @@ packages: /@types/eslint__js@8.42.3: resolution: {integrity: sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==} dependencies: - '@types/eslint': 8.56.7 + '@types/eslint': 8.56.8 dev: true /@types/estree@1.0.5: @@ -1894,38 +1894,38 @@ packages: eslint-visitor-keys: 3.4.3 dev: true - /@vitest/expect@1.4.0: - resolution: {integrity: sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==} + /@vitest/expect@1.5.0: + resolution: {integrity: sha512-0pzuCI6KYi2SIC3LQezmxujU9RK/vwC1U9R0rLuGlNGcOuDWxqWKu6nUdFsX9tH1WU0SXtAxToOsEjeUn1s3hA==} dependencies: - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 + '@vitest/spy': 1.5.0 + '@vitest/utils': 1.5.0 chai: 4.4.1 dev: true - /@vitest/runner@1.4.0: - resolution: {integrity: sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==} + /@vitest/runner@1.5.0: + resolution: {integrity: sha512-7HWwdxXP5yDoe7DTpbif9l6ZmDwCzcSIK38kTSIt6CFEpMjX4EpCgT6wUmS0xTXqMI6E/ONmfgRKmaujpabjZQ==} dependencies: - '@vitest/utils': 1.4.0 + '@vitest/utils': 1.5.0 p-limit: 5.0.0 pathe: 1.1.2 dev: true - /@vitest/snapshot@1.4.0: - resolution: {integrity: sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==} + /@vitest/snapshot@1.5.0: + resolution: {integrity: sha512-qpv3fSEuNrhAO3FpH6YYRdaECnnRjg9VxbhdtPwPRnzSfHVXnNzzrpX4cJxqiwgRMo7uRMWDFBlsBq4Cr+rO3A==} dependencies: magic-string: 0.30.9 pathe: 1.1.2 pretty-format: 29.7.0 dev: true - /@vitest/spy@1.4.0: - resolution: {integrity: sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==} + /@vitest/spy@1.5.0: + resolution: {integrity: sha512-vu6vi6ew5N5MMHJjD5PoakMRKYdmIrNJmyfkhRpQt5d9Ewhw9nZ5Aqynbi3N61bvk9UvZ5UysMT6ayIrZ8GA9w==} dependencies: tinyspy: 2.2.1 dev: true - /@vitest/utils@1.4.0: - resolution: {integrity: sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==} + /@vitest/utils@1.5.0: + resolution: {integrity: sha512-BDU0GNL8MWkRkSRdNFvCUCAVOeHaUlVJ9Tx0TYBZyXaaOTmGtUFObzchCivIBrIwKzvZA7A9sCejVhXM2aY98A==} dependencies: diff-sequences: 29.6.3 estree-walker: 3.0.3 @@ -5872,8 +5872,8 @@ packages: vfile-message: 2.0.4 dev: true - /vite-node@1.4.0(@types/node@20.12.7): - resolution: {integrity: sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==} + /vite-node@1.5.0(@types/node@20.12.7): + resolution: {integrity: sha512-tV8h6gMj6vPzVCa7l+VGq9lwoJjW8Y79vst8QZZGiuRAfijU+EEWuc0kFpmndQrWhMMhet1jdSF+40KSZUqIIw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true dependencies: @@ -5929,15 +5929,15 @@ packages: fsevents: 2.3.3 dev: true - /vitest@1.4.0(@types/node@20.12.7)(happy-dom@14.7.1): - resolution: {integrity: sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==} + /vitest@1.5.0(@types/node@20.12.7)(happy-dom@14.7.1): + resolution: {integrity: sha512-d8UKgR0m2kjdxDWX6911uwxout6GHS0XaGH1cksSIVVG8kRlE7G7aBw7myKQCvDI5dT4j7ZMa+l706BIORMDLw==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 - '@vitest/browser': 1.4.0 - '@vitest/ui': 1.4.0 + '@vitest/browser': 1.5.0 + '@vitest/ui': 1.5.0 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -5955,11 +5955,11 @@ packages: optional: true dependencies: '@types/node': 20.12.7 - '@vitest/expect': 1.4.0 - '@vitest/runner': 1.4.0 - '@vitest/snapshot': 1.4.0 - '@vitest/spy': 1.4.0 - '@vitest/utils': 1.4.0 + '@vitest/expect': 1.5.0 + '@vitest/runner': 1.5.0 + '@vitest/snapshot': 1.5.0 + '@vitest/spy': 1.5.0 + '@vitest/utils': 1.5.0 acorn-walk: 8.3.2 chai: 4.4.1 debug: 4.3.4 @@ -5974,7 +5974,7 @@ packages: tinybench: 2.6.0 tinypool: 0.8.3 vite: 5.2.8(@types/node@20.12.7) - vite-node: 1.4.0(@types/node@20.12.7) + vite-node: 1.5.0(@types/node@20.12.7) why-is-node-running: 2.2.2 transitivePeerDependencies: - less diff --git a/src/ResultSet/ResultSet.test-d.ts b/src/ResultSet/ResultSet.test-d.ts index 35ed7a49e..deb179b65 100644 --- a/src/ResultSet/ResultSet.test-d.ts +++ b/src/ResultSet/ResultSet.test-d.ts @@ -1,12 +1,12 @@ /* eslint-disable @typescript-eslint/ban-types */ import { expectTypeOf, test } from 'vitest' -import type { $ } from '../../tests/ts/_/schema/generated/Index.js' +import type { Index } from '../../tests/ts/_/schema/generated/Index.js' import type * as Schema from '../../tests/ts/_/schema/generated/SchemaBuildtime.js' import type { SelectionSet } from '../SelectionSet/__.js' import type { ResultSet } from './__.js' -type I = $.Index +type I = Index type RS<$selectionSet extends SelectionSet.Query> = ResultSet.Query<$selectionSet, I> // dprint-ignore diff --git a/src/SelectionSet/SelectionSet.test-d.ts b/src/SelectionSet/SelectionSet.test-d.ts index b87cbd5aa..11168df24 100644 --- a/src/SelectionSet/SelectionSet.test-d.ts +++ b/src/SelectionSet/SelectionSet.test-d.ts @@ -1,8 +1,8 @@ import { assertType, expectTypeOf, test } from 'vitest' -import type { $ } from '../../tests/ts/_/schema/generated/Index.js' +import type { Index } from '../../tests/ts/_/schema/generated/Index.js' import type { SelectionSet } from './__.js' -type Q = SelectionSet.Query<$.Index> +type Q = SelectionSet.Query test(`ParseAliasExpression`, () => { expectTypeOf>().toEqualTypeOf>() diff --git a/src/SelectionSet/toGraphQLDocumentString.test.ts b/src/SelectionSet/toGraphQLDocumentString.test.ts index 7b6c87e6f..6f652d06b 100644 --- a/src/SelectionSet/toGraphQLDocumentString.test.ts +++ b/src/SelectionSet/toGraphQLDocumentString.test.ts @@ -1,10 +1,12 @@ import { parse, print } from 'graphql' import { describe, expect, test } from 'vitest' -import type { $ } from '../../tests/ts/_/schema/generated/Index.js' +import type { Index } from '../../tests/ts/_/schema/generated/Index.js' import type { SelectionSet } from './__.js' import { toGraphQLDocumentString } from './toGraphQLDocumentString.js' -type Q = SelectionSet.Query<$.Index> +// eslint-disable-next-line +// @ts-ignore +type Q = SelectionSet.Query const s = (selectionSet: Q) => selectionSet const prepareResult = (ss: Q) => { const graphqlDocumentString = toGraphQLDocumentString(ss as any) diff --git a/src/SelectionSet/toGraphQLDocumentString.ts b/src/SelectionSet/toGraphQLDocumentString.ts index 325ced9d6..d29d5c194 100644 --- a/src/SelectionSet/toGraphQLDocumentString.ts +++ b/src/SelectionSet/toGraphQLDocumentString.ts @@ -17,9 +17,7 @@ type Args_ = string | boolean | null | number | Args type Indicator = 0 | 1 | boolean -export type GraphQLDocumentObject = { - [k: string]: Indicator | SS -} +export type GraphQLDocumentObject = Record type SS = { [k: string]: Indicator | SS diff --git a/src/client.test.ts b/src/client.test.ts index d3f5f721d..6487b4ee7 100644 --- a/src/client.test.ts +++ b/src/client.test.ts @@ -1,7 +1,7 @@ /* eslint-disable */ import { beforeEach, describe, expect, test } from 'vitest' import { setupMockServer } from '../tests/raw/__helpers.js' -import type { $ } from '../tests/ts/_/schema/generated/Index.js' +import type { Index } from '../tests/ts/_/schema/generated/Index.js' import { $Index as schemaIndex } from '../tests/ts/_/schema/generated/SchemaRuntime.js' import { create } from './client.js' @@ -9,7 +9,7 @@ const ctx = setupMockServer() const data = { fooBarUnion: { int: 1 } } // @ts-ignore infinite depth -const client = () => create<$.Index>({ url: ctx.url, schemaIndex }) +const client = () => create({ url: ctx.url, schemaIndex }) test.todo(`query`, async () => { const mockRes = ctx.res({ body: { data } }).spec.body! @@ -75,7 +75,7 @@ describe(`custom scalar`, () => { ctx.res({ body: { data: {} } }) }) const clientExpected = (expectedDocument: (document: any) => void) => { - const client = create<$.Index>({ + const client = create({ url: ctx.url, schemaIndex, hooks: { diff --git a/src/generator/code/code.ts b/src/generator/code/code.ts index fd3cfe4d7..90af67ca8 100644 --- a/src/generator/code/code.ts +++ b/src/generator/code/code.ts @@ -19,6 +19,10 @@ export interface Input { } schemaSource: string options?: { + /** + * Should custom scalars definitions be imported into the generated output? + */ + customScalars?: boolean formatter?: Formatter TSDoc?: { noDocPolicy?: 'message' | 'ignore' diff --git a/src/generator/code/index.ts b/src/generator/code/index.ts index 00b1bb2dd..578539335 100644 --- a/src/generator/code/index.ts +++ b/src/generator/code/index.ts @@ -7,30 +7,23 @@ export const generateIndex = (config: Config) => { let code = `` code += `import type * as ${namespace} from './SchemaBuildtime.js'\n\n` code += Code.export$( - Code.namespace( - `$`, - Code.group( - Code.export$( - Code.interface$( - `Index`, - Code.objectFrom({ - Root: { - type: Code.objectFrom({ - Query: hasQuery(config.typeMapByKind) ? `${namespace}.Root.Query` : null, - Mutation: hasMutation(config.typeMapByKind) ? `${namespace}.Root.Mutation` : null, - Subscription: hasSubscription(config.typeMapByKind) ? `${namespace}.Root.Subscription` : null, - }), - }, - objects: Code.objectFromEntries( - config.typeMapByKind.GraphQLObjectType.map(_ => [_.name, `${namespace}.Object.${_.name}`]), - ), - unions: Code.objectFromEntries( - config.typeMapByKind.GraphQLUnionType.map(_ => [_.name, `${namespace}.Union.${_.name}`]), - ), - }), - ), + Code.interface$( + `Index`, + Code.objectFrom({ + Root: { + type: Code.objectFrom({ + Query: hasQuery(config.typeMapByKind) ? `${namespace}.Root.Query` : null, + Mutation: hasMutation(config.typeMapByKind) ? `${namespace}.Root.Mutation` : null, + Subscription: hasSubscription(config.typeMapByKind) ? `${namespace}.Root.Subscription` : null, + }), + }, + objects: Code.objectFromEntries( + config.typeMapByKind.GraphQLObjectType.map(_ => [_.name, `${namespace}.Object.${_.name}`]), ), - ), + unions: Code.objectFromEntries( + config.typeMapByKind.GraphQLUnionType.map(_ => [_.name, `${namespace}.Union.${_.name}`]), + ), + }), ), ) diff --git a/src/generator/files.ts b/src/generator/files.ts index e0c473560..3eb4f67f2 100644 --- a/src/generator/files.ts +++ b/src/generator/files.ts @@ -1,7 +1,9 @@ import { createFromBuffer } from '@dprint/formatter' import { getPath } from '@dprint/typescript' +import _ from 'json-bigint' import fs from 'node:fs/promises' import * as Path from 'node:path' +import { errorFromMaybeError } from '../lib/prelude.js' import { generateCode, type Input as GenerateInput } from './code/code.js' export interface Input { @@ -16,15 +18,21 @@ export const generateFiles = async (input: Input) => { const sourceDirPath = input.sourceDirPath ?? process.cwd() const schemaPath = input.schemaPath ?? Path.join(sourceDirPath, `schema.graphql`) const schemaSource = await fs.readFile(schemaPath, `utf8`) - const options = (input.format ?? true) - ? { - formatter: createFromBuffer(await fs.readFile(getPath())), - } - : undefined + + const customScalarCodecsPath = Path.relative(input.outputDirPath, Path.join(sourceDirPath, `customScalarCodecs.js`)) + // todo support other extensions: .tsx,.js,.mjs,.cjs + const customScalarCodecsPathExists = await fileExists(customScalarCodecsPath.replace(`.js`, `.ts`)) + const formatter = (input.format ?? true) ? createFromBuffer(await fs.readFile(getPath())) : undefined + + const options: GenerateInput['options'] = { + formatter, + customScalars: customScalarCodecsPathExists, + } + const code = generateCode({ schemaSource, importPaths: { - customScalarCodecs: Path.relative(input.outputDirPath, Path.join(sourceDirPath, `customScalarCodecs.js`)), + customScalarCodecs: customScalarCodecsPath, }, ...input.code, options, @@ -35,3 +43,14 @@ export const generateFiles = async (input: Input) => { await fs.writeFile(`${input.outputDirPath}/Scalar.ts`, code.scalars, { encoding: `utf8` }) await fs.writeFile(`${input.outputDirPath}/SchemaRuntime.ts`, code.schemaRuntime, { encoding: `utf8` }) } + +const fileExists = async (path: string) => { + return Boolean( + await fs.stat(path).catch((_: unknown) => { + const error = errorFromMaybeError(_) + return `code` in error && typeof error.code === `string` && error.code === `ENOENT` + ? null + : Promise.reject(error) + }), + ) +} diff --git a/tests/ts/_/schema/generated/Index.ts b/tests/ts/_/schema/generated/Index.ts index 4bb934778..717061934 100644 --- a/tests/ts/_/schema/generated/Index.ts +++ b/tests/ts/_/schema/generated/Index.ts @@ -1,28 +1,26 @@ import type * as Schema from './SchemaBuildtime.js' -export namespace $ { - export interface Index { - Root: { - Query: Schema.Root.Query - Mutation: null - Subscription: null - } - objects: { - DateObject1: Schema.Object.DateObject1 - DateObject2: Schema.Object.DateObject2 - Foo: Schema.Object.Foo - Bar: Schema.Object.Bar - ObjectNested: Schema.Object.ObjectNested - lowerCaseObject: Schema.Object.lowerCaseObject - lowerCaseObject2: Schema.Object.lowerCaseObject2 - Object1: Schema.Object.Object1 - Object1ImplementingInterface: Schema.Object.Object1ImplementingInterface - Object2ImplementingInterface: Schema.Object.Object2ImplementingInterface - } - unions: { - DateUnion: Schema.Union.DateUnion - FooBarUnion: Schema.Union.FooBarUnion - lowerCaseUnion: Schema.Union.lowerCaseUnion - } +export interface Index { + Root: { + Query: Schema.Root.Query + Mutation: null + Subscription: null + } + objects: { + DateObject1: Schema.Object.DateObject1 + DateObject2: Schema.Object.DateObject2 + Foo: Schema.Object.Foo + Bar: Schema.Object.Bar + ObjectNested: Schema.Object.ObjectNested + lowerCaseObject: Schema.Object.lowerCaseObject + lowerCaseObject2: Schema.Object.lowerCaseObject2 + Object1: Schema.Object.Object1 + Object1ImplementingInterface: Schema.Object.Object1ImplementingInterface + Object2ImplementingInterface: Schema.Object.Object2ImplementingInterface + } + unions: { + DateUnion: Schema.Union.DateUnion + FooBarUnion: Schema.Union.FooBarUnion + lowerCaseUnion: Schema.Union.lowerCaseUnion } }