From 5f3f5b5d111f6aed7d0d0443b013bbd01bb25c92 Mon Sep 17 00:00:00 2001 From: Daniel Cousens <413395+dcousens@users.noreply.github.com> Date: Thu, 15 Feb 2024 14:35:37 +1100 Subject: [PATCH] fix prismaClientPath outputs --- packages/core/src/artifacts.ts | 6 +- ...inter.tsx => typescript-schema-printer.ts} | 78 +++---- .../__snapshots__/artifacts.test.ts.snap | 198 +++++++++--------- tests/cli-tests/artifacts.test.ts | 2 + 4 files changed, 143 insertions(+), 141 deletions(-) rename packages/core/src/lib/{schema-type-printer.tsx => typescript-schema-printer.ts} (87%) diff --git a/packages/core/src/artifacts.ts b/packages/core/src/artifacts.ts index 85ba2071b3c..362113c0f57 100644 --- a/packages/core/src/artifacts.ts +++ b/packages/core/src/artifacts.ts @@ -4,7 +4,7 @@ import { createRequire } from 'node:module' import { printSchema, GraphQLSchema } from 'graphql' import { getGenerators, formatSchema } from '@prisma/internals' import type { KeystoneConfig } from './types' -import { printGeneratedTypes } from './lib/schema-type-printer' +import { printGeneratedTypes } from './lib/typescript-schema-printer' import { ExitError } from './scripts/utils' import { initialiseLists } from './lib/core/initialise-lists' import { printPrismaSchema } from './lib/core/prisma-schema-printer' @@ -80,8 +80,8 @@ function posixify (s: string) { } export function getSystemPaths (cwd: string, config: KeystoneConfig) { - const prismaClientPath = config.db.prismaClientPath?.startsWith('@') - ? config.db.prismaClientPath + const prismaClientPath = config.db.prismaClientPath === '@prisma/client' + ? null : config.db.prismaClientPath ? path.join(cwd, config.db.prismaClientPath) : null diff --git a/packages/core/src/lib/schema-type-printer.tsx b/packages/core/src/lib/typescript-schema-printer.ts similarity index 87% rename from packages/core/src/lib/schema-type-printer.tsx rename to packages/core/src/lib/typescript-schema-printer.ts index d5167c2baed..65c5e7ff89f 100644 --- a/packages/core/src/lib/schema-type-printer.tsx +++ b/packages/core/src/lib/typescript-schema-printer.ts @@ -23,7 +23,7 @@ function printEnumTypeDefinition (type: GraphQLEnumType) { type .getValues() .map(x => ` | '${stringify(x.name)}'`) - .join('\n') + ';', + .join('\n'), ].join('\n') } @@ -62,17 +62,17 @@ function printInputObjectTypeDefinition ( `export type ${type.name} = {`, ...Object.values(type.getFields()).map(({ type, defaultValue, name }) => { const maybe = type instanceof GraphQLNonNull ? '' : '?' - return ` readonly ${name}${maybe}: ${printTypeReference(type, scalars)};` + return ` readonly ${name}${maybe}: ${printTypeReference(type, scalars)}` }), - '};', + '}', ].join('\n') } function printInputTypesFromSchema (schema: GraphQLSchema, scalars: Record) { const output = [ 'type Scalars = {', - ...Object.keys(scalars).map(scalar => ` readonly ${scalar}: ${scalars[scalar]};`), - '};', + ...Object.keys(scalars).map(scalar => ` readonly ${scalar}: ${scalars[scalar]}`), + '}', ] for (const type of Object.values(schema.getTypeMap())) { @@ -102,19 +102,19 @@ function printInterimType ( return [ `type Resolved${typename} = {`, ...Object.entries(list.fields).map(([fieldKey, { dbField }]) => { - if (dbField.kind === 'none') return ` ${fieldKey}?: undefined;` + if (dbField.kind === 'none') return ` ${fieldKey}?: undefined` // TODO: this could be elsewhere, maybe id-field.ts if (fieldKey === 'id') { // autoincrement doesn't support passing an identifier if ('default' in dbField) { if (dbField.default?.kind === 'autoincrement') { - return ` id?: undefined;` + return ` id?: undefined` } } // soft-block `id` updates for relationship safety - if (operation === 'Update') return ` id?: undefined;` + if (operation === 'Update') return ` id?: undefined` } if (dbField.kind === 'multi') { @@ -122,20 +122,20 @@ function printInterimType ( ` ${fieldKey}: {`, ...Object.entries(dbField.fields).map(([subFieldKey, subDbField]) => { // TODO: untrue if a db defaultValue is set - // const optional = operation === 'Create' && subDbField.mode === 'required' ? '' : '?'; + // const optional = operation === 'Create' && subDbField.mode === 'required' ? '' : '?' const optional = '?' - return ` ${subFieldKey}${optional}: ${prismaType}['${fieldKey}_${subFieldKey}'];` + return ` ${subFieldKey}${optional}: ${prismaType}['${fieldKey}_${subFieldKey}']` }), - ` };`, + ` }`, ].join('\n') } // TODO: untrue if a db defaultValue is set - // const optional = operation === 'Create' && dbField.mode === 'required' ? '' : '?'; + // const optional = operation === 'Create' && dbField.mode === 'required' ? '' : '?' const optional = '?' - return ` ${fieldKey}${optional}: ${prismaType}['${fieldKey}'];` + return ` ${fieldKey}${optional}: ${prismaType}['${fieldKey}']` }), - `};`, + `}`, ].join('\n') } @@ -154,27 +154,27 @@ function printListTypeInfo ( const listTypeInfoName = `Lists.${listKey}.TypeInfo` return [ - `export type ${listKey} = import('@keystone-6/core').ListConfig<${listTypeInfoName}>;`, + `export type ${listKey} = import('@keystone-6/core').ListConfig<${listTypeInfoName}>`, `namespace ${listKey} {`, - ` export type Item = import('${prismaClientPath}').${listKey};`, + ` export type Item = import('${prismaClientPath}').${listKey}`, ` export type TypeInfo = {`, - ` key: '${listKey}';`, - ` isSingleton: ${list.isSingleton};`, + ` key: '${listKey}'`, + ` isSingleton: ${list.isSingleton}`, ` fields: ${Object.keys(list.fields).map(x => `'${x}'`).join(' | ')}`, - ` item: Item;`, + ` item: Item`, ` inputs: {`, - ` where: ${list.graphql.isEnabled.query ? whereInputName : 'never'};`, - ` uniqueWhere: ${list.graphql.isEnabled.query ? whereUniqueInputName : 'never'};`, - ` create: ${list.graphql.isEnabled.create ? createInputName : 'never'};`, - ` update: ${list.graphql.isEnabled.update ? updateInputName : 'never'};`, - ` orderBy: ${list.graphql.isEnabled.query ? listOrderName : 'never'};`, - ` };`, + ` where: ${list.graphql.isEnabled.query ? whereInputName : 'never'}`, + ` uniqueWhere: ${list.graphql.isEnabled.query ? whereUniqueInputName : 'never'}`, + ` create: ${list.graphql.isEnabled.create ? createInputName : 'never'}`, + ` update: ${list.graphql.isEnabled.update ? updateInputName : 'never'}`, + ` orderBy: ${list.graphql.isEnabled.query ? listOrderName : 'never'}`, + ` }`, ` prisma: {`, - ` create: ${list.graphql.isEnabled.create ? `Resolved${createInputName}` : 'never'};`, - ` update: ${list.graphql.isEnabled.update ? `Resolved${updateInputName}` : 'never'};`, - ` };`, - ` all: __TypeInfo;`, - ` };`, + ` create: ${list.graphql.isEnabled.create ? `Resolved${createInputName}` : 'never'}`, + ` update: ${list.graphql.isEnabled.update ? `Resolved${updateInputName}` : 'never'}`, + ` }`, + ` all: __TypeInfo`, + ` }`, `}`, ] .map(line => ` ${line}`) @@ -218,7 +218,7 @@ export function printGeneratedTypes ( ) } - listsTypeInfo.push(` readonly ${listKey}: ${listTypeInfoName};`) + listsTypeInfo.push(` readonly ${listKey}: ${listTypeInfoName}`) listsNamespaces.push(printListTypeInfo(prismaClientPath, listKey, list)) } @@ -240,23 +240,23 @@ export function printGeneratedTypes ( 'export declare namespace Lists {', ...listsNamespaces, '}', - `export type Context = import('@keystone-6/core/types').KeystoneContext>;`, - `export type Config = import('@keystone-6/core/types').KeystoneConfig>;`, + `export type Context = import('@keystone-6/core/types').KeystoneContext>`, + `export type Config = import('@keystone-6/core/types').KeystoneConfig>`, '', 'export type TypeInfo = {', ` lists: {`, ...listsTypeInfo, - ` };`, - ` prisma: import('${prismaClientPath}').PrismaClient;`, - ` session: Session;`, - `};`, + ` }`, + ` prisma: import('${prismaClientPath}').PrismaClient`, + ` session: Session`, + `}`, ``, // we need to reference the `TypeInfo` above in another type that is also called `TypeInfo` - `type __TypeInfo = TypeInfo;`, + `type __TypeInfo = TypeInfo`, ``, `export type Lists = {`, ` [Key in keyof TypeInfo['lists']]?: import('@keystone-6/core').ListConfig['lists'][Key]>`, - `} & Record>;`, + `} & Record>`, ``, `export {}`, ``, diff --git a/tests/cli-tests/__snapshots__/artifacts.test.ts.snap b/tests/cli-tests/__snapshots__/artifacts.test.ts.snap index 8a14169643e..282cbf2b437 100644 --- a/tests/cli-tests/__snapshots__/artifacts.test.ts.snap +++ b/tests/cli-tests/__snapshots__/artifacts.test.ts.snap @@ -5,169 +5,169 @@ exports[`postinstall writes the correct node_modules files 1`] = ` /* eslint-disable */ type Scalars = { - readonly ID: string; - readonly Boolean: boolean; - readonly String: string; - readonly Int: number; - readonly Float: number; - readonly JSON: import('@keystone-6/core/types').JSONValue; - readonly Decimal: import('@keystone-6/core/types').Decimal | string; -}; + readonly ID: string + readonly Boolean: boolean + readonly String: string + readonly Int: number + readonly Float: number + readonly JSON: import('@keystone-6/core/types').JSONValue + readonly Decimal: import('@keystone-6/core/types').Decimal | string +} export type TodoWhereUniqueInput = { - readonly id?: Scalars['ID'] | null; -}; + readonly id?: Scalars['ID'] | null +} export type TodoWhereInput = { - readonly AND?: ReadonlyArray | TodoWhereInput | null; - readonly OR?: ReadonlyArray | TodoWhereInput | null; - readonly NOT?: ReadonlyArray | TodoWhereInput | null; - readonly id?: IDFilter | null; - readonly title?: StringFilter | null; -}; + readonly AND?: ReadonlyArray | TodoWhereInput | null + readonly OR?: ReadonlyArray | TodoWhereInput | null + readonly NOT?: ReadonlyArray | TodoWhereInput | null + readonly id?: IDFilter | null + readonly title?: StringFilter | null +} export type IDFilter = { - readonly equals?: Scalars['ID'] | null; - readonly in?: ReadonlyArray | Scalars['ID'] | null; - readonly notIn?: ReadonlyArray | Scalars['ID'] | null; - readonly lt?: Scalars['ID'] | null; - readonly lte?: Scalars['ID'] | null; - readonly gt?: Scalars['ID'] | null; - readonly gte?: Scalars['ID'] | null; - readonly not?: IDFilter | null; -}; + readonly equals?: Scalars['ID'] | null + readonly in?: ReadonlyArray | Scalars['ID'] | null + readonly notIn?: ReadonlyArray | Scalars['ID'] | null + readonly lt?: Scalars['ID'] | null + readonly lte?: Scalars['ID'] | null + readonly gt?: Scalars['ID'] | null + readonly gte?: Scalars['ID'] | null + readonly not?: IDFilter | null +} export type StringFilter = { - readonly equals?: Scalars['String'] | null; - readonly in?: ReadonlyArray | Scalars['String'] | null; - readonly notIn?: ReadonlyArray | Scalars['String'] | null; - readonly lt?: Scalars['String'] | null; - readonly lte?: Scalars['String'] | null; - readonly gt?: Scalars['String'] | null; - readonly gte?: Scalars['String'] | null; - readonly contains?: Scalars['String'] | null; - readonly startsWith?: Scalars['String'] | null; - readonly endsWith?: Scalars['String'] | null; - readonly not?: NestedStringFilter | null; -}; + readonly equals?: Scalars['String'] | null + readonly in?: ReadonlyArray | Scalars['String'] | null + readonly notIn?: ReadonlyArray | Scalars['String'] | null + readonly lt?: Scalars['String'] | null + readonly lte?: Scalars['String'] | null + readonly gt?: Scalars['String'] | null + readonly gte?: Scalars['String'] | null + readonly contains?: Scalars['String'] | null + readonly startsWith?: Scalars['String'] | null + readonly endsWith?: Scalars['String'] | null + readonly not?: NestedStringFilter | null +} export type NestedStringFilter = { - readonly equals?: Scalars['String'] | null; - readonly in?: ReadonlyArray | Scalars['String'] | null; - readonly notIn?: ReadonlyArray | Scalars['String'] | null; - readonly lt?: Scalars['String'] | null; - readonly lte?: Scalars['String'] | null; - readonly gt?: Scalars['String'] | null; - readonly gte?: Scalars['String'] | null; - readonly contains?: Scalars['String'] | null; - readonly startsWith?: Scalars['String'] | null; - readonly endsWith?: Scalars['String'] | null; - readonly not?: NestedStringFilter | null; -}; + readonly equals?: Scalars['String'] | null + readonly in?: ReadonlyArray | Scalars['String'] | null + readonly notIn?: ReadonlyArray | Scalars['String'] | null + readonly lt?: Scalars['String'] | null + readonly lte?: Scalars['String'] | null + readonly gt?: Scalars['String'] | null + readonly gte?: Scalars['String'] | null + readonly contains?: Scalars['String'] | null + readonly startsWith?: Scalars['String'] | null + readonly endsWith?: Scalars['String'] | null + readonly not?: NestedStringFilter | null +} export type TodoOrderByInput = { - readonly id?: OrderDirection | null; - readonly title?: OrderDirection | null; -}; + readonly id?: OrderDirection | null + readonly title?: OrderDirection | null +} export type OrderDirection = | 'asc' - | 'desc'; + | 'desc' export type TodoUpdateInput = { - readonly title?: Scalars['String'] | null; -}; + readonly title?: Scalars['String'] | null +} export type TodoUpdateArgs = { - readonly where: TodoWhereUniqueInput; - readonly data: TodoUpdateInput; -}; + readonly where: TodoWhereUniqueInput + readonly data: TodoUpdateInput +} export type TodoCreateInput = { - readonly title?: Scalars['String'] | null; -}; + readonly title?: Scalars['String'] | null +} export type KeystoneAdminUIFieldMetaIsNonNull = | 'read' | 'create' - | 'update'; + | 'update' export type KeystoneAdminUIFieldMetaCreateViewFieldMode = | 'edit' - | 'hidden'; + | 'hidden' export type KeystoneAdminUIFieldMetaListViewFieldMode = | 'read' - | 'hidden'; + | 'hidden' export type KeystoneAdminUIFieldMetaItemViewFieldMode = | 'edit' | 'read' - | 'hidden'; + | 'hidden' export type KeystoneAdminUIFieldMetaItemViewFieldPosition = | 'form' - | 'sidebar'; + | 'sidebar' export type QueryMode = | 'default' - | 'insensitive'; + | 'insensitive' export type KeystoneAdminUISortDirection = | 'ASC' - | 'DESC'; + | 'DESC' type ResolvedTodoCreateInput = { - id?: import('@prisma/client').Prisma.TodoCreateInput['id']; - title?: import('@prisma/client').Prisma.TodoCreateInput['title']; -}; + id?: import('@prisma/client').Prisma.TodoCreateInput['id'] + title?: import('@prisma/client').Prisma.TodoCreateInput['title'] +} type ResolvedTodoUpdateInput = { - id?: undefined; - title?: import('@prisma/client').Prisma.TodoUpdateInput['title']; -}; + id?: undefined + title?: import('@prisma/client').Prisma.TodoUpdateInput['title'] +} export declare namespace Lists { - export type Todo = import('@keystone-6/core').ListConfig>; + export type Todo = import('@keystone-6/core').ListConfig> namespace Todo { - export type Item = import('@prisma/client').Todo; + export type Item = import('@prisma/client').Todo export type TypeInfo = { - key: 'Todo'; - isSingleton: false; + key: 'Todo' + isSingleton: false fields: 'id' | 'title' - item: Item; + item: Item inputs: { - where: TodoWhereInput; - uniqueWhere: TodoWhereUniqueInput; - create: TodoCreateInput; - update: TodoUpdateInput; - orderBy: TodoOrderByInput; - }; + where: TodoWhereInput + uniqueWhere: TodoWhereUniqueInput + create: TodoCreateInput + update: TodoUpdateInput + orderBy: TodoOrderByInput + } prisma: { - create: ResolvedTodoCreateInput; - update: ResolvedTodoUpdateInput; - }; - all: __TypeInfo; - }; + create: ResolvedTodoCreateInput + update: ResolvedTodoUpdateInput + } + all: __TypeInfo + } } } -export type Context = import('@keystone-6/core/types').KeystoneContext>; -export type Config = import('@keystone-6/core/types').KeystoneConfig>; +export type Context = import('@keystone-6/core/types').KeystoneContext> +export type Config = import('@keystone-6/core/types').KeystoneConfig> export type TypeInfo = { lists: { - readonly Todo: Lists.Todo.TypeInfo; - }; - prisma: import('@prisma/client').PrismaClient; - session: Session; -}; + readonly Todo: Lists.Todo.TypeInfo + } + prisma: import('@prisma/client').PrismaClient + session: Session +} -type __TypeInfo = TypeInfo; +type __TypeInfo = TypeInfo export type Lists = { [Key in keyof TypeInfo['lists']]?: import('@keystone-6/core').ListConfig['lists'][Key]> -} & Record>; +} & Record> export {} -`; +` diff --git a/tests/cli-tests/artifacts.test.ts b/tests/cli-tests/artifacts.test.ts index 8c101eaa2ab..aae89e431f4 100644 --- a/tests/cli-tests/artifacts.test.ts +++ b/tests/cli-tests/artifacts.test.ts @@ -43,6 +43,7 @@ describe('postinstall', () => { expect(files).toEqual(await getFiles(`${__dirname}/fixtures/basic-project`, schemasMatch)) expect(recording()).toMatchInlineSnapshot(`"? Generated GraphQL and Prisma schemas"`) }) + test("does not prompt, error or modify the schemas if they're already up to date", async () => { const tmp = await testdir({ ...symlinkKeystoneDeps, @@ -55,6 +56,7 @@ describe('postinstall', () => { expect(files).toEqual(await getFiles(`${__dirname}/fixtures/basic-project`, schemasMatch)) expect(recording()).toMatchInlineSnapshot(`"? GraphQL and Prisma schemas are up to date"`) }) + test('writes the correct node_modules files', async () => { const tmp = await testdir({ ...symlinkKeystoneDeps,