Skip to content

Commit

Permalink
fix(client): Fix model-vs-model type clashes
Browse files Browse the repository at this point in the history
In some cases, generated types for one of the models will clash with
generated types for another. In that case, we would generate duplicate
type.
This PR fixes this: approach is very different to namespaces proposal I
shared earlier. Turns out, most of the conflicts are solved by just
renaming default args types from `ModelArgs` to `ModelDefaultArgs`.
Deperecated alias for an old name would still be created if it does not
conflict.
Somewhat different case is `Payload` type. First, they are created at
top level of the file, rather then in `Prisma` namespace. @millsp and I
agreed that thouse types are not public, so it is safe to move them to
namespace and rename them. So, `UserPayload` would now become
`Prisma.$UserPayload`. This allows to both use `UserPayload` as a model
name as well as use `Batch` as a model name (we have built-in
`BatchPayload` type that is unrelated to aforementioned `Payload`
types).

Fix #19967
Fix #18902
Fix #16940
Fix #9568
Fix #7518
  • Loading branch information
SevInf committed Jul 12, 2023
1 parent 99a96e8 commit b27a996
Show file tree
Hide file tree
Showing 21 changed files with 4,398 additions and 3,878 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions packages/client/src/generation/TSClient/Args.ts
@@ -1,10 +1,10 @@
import indent from 'indent-string'

import { DMMF } from '../dmmf-types'
import { GenericArgsInfo } from '../GenericsArgsInfo'
import { getIncludeName, getModelArgName, getSelectName } from '../utils'
import { TAB_SIZE } from './constants'
import type { Generatable } from './Generatable'
import { GenerateContext } from './GenerateContext'
import { getArgFieldJSDoc } from './helpers'
import { InputField } from './Input'

Expand All @@ -14,7 +14,7 @@ export class ArgsType implements Generatable {
constructor(
protected readonly args: DMMF.SchemaArg[],
protected readonly type: DMMF.OutputType,
protected readonly genericsInfo: GenericArgsInfo,
protected readonly context: GenerateContext,
protected readonly action?: DMMF.ModelAction,
) {}
public setGeneratedName(name: string): this {
Expand Down Expand Up @@ -83,13 +83,14 @@ export class ArgsType implements Generatable {

argsToGenerate.push(...args)
const generatedName = this.generatedName ?? getModelArgName(name, action)
this.context.defaultArgsAliases.registerArgName(generatedName)

return `
/**
* ${this.getGeneratedComment()}
*/
export type ${generatedName}<ExtArgs extends $Extensions.Args = $Extensions.DefaultArgs> = {
${indent(argsToGenerate.map((arg) => new InputField(arg, this.genericsInfo).toTS()).join('\n'), TAB_SIZE)}
${indent(argsToGenerate.map((arg) => new InputField(arg, this.context.genericArgsInfo).toTS()).join('\n'), TAB_SIZE)}
}
`
}
Expand All @@ -103,7 +104,7 @@ export class MinimalArgsType implements Generatable {
constructor(
protected readonly args: DMMF.SchemaArg[],
protected readonly type: DMMF.OutputType,
protected readonly genericsInfo: GenericArgsInfo,
protected readonly context: GenerateContext,
protected readonly action?: DMMF.ModelAction,
protected readonly generatedTypeName = getModelArgName(type.name, action),
) {}
Expand All @@ -115,6 +116,7 @@ export class MinimalArgsType implements Generatable {
arg.comment = getArgFieldJSDoc(this.type, action, arg)
}

this.context.defaultArgsAliases.registerArgName(this.generatedTypeName)
return `
/**
* ${name} ${action ? action : 'without action'}
Expand All @@ -123,7 +125,7 @@ export type ${this.generatedTypeName}<ExtArgs extends $Extensions.Args = $Extens
${indent(
args
.map((arg) => {
return new InputField(arg, this.genericsInfo).toTS()
return new InputField(arg, this.context.genericArgsInfo).toTS()
})
.join('\n'),
TAB_SIZE,
Expand Down
17 changes: 5 additions & 12 deletions packages/client/src/generation/TSClient/Count.ts
@@ -1,35 +1,28 @@
import type { GeneratorConfig } from '@prisma/generator-helper'
import indent from 'indent-string'

import type { DMMFHelper } from '../dmmf'
import { DMMF } from '../dmmf-types'
import { GenericArgsInfo } from '../GenericsArgsInfo'
import { capitalize, getFieldArgName, getSelectName } from '../utils'
import { ArgsType, MinimalArgsType } from './Args'
import { TAB_SIZE } from './constants'
import type { Generatable } from './Generatable'
import { TS } from './Generatable'
import { GenerateContext } from './GenerateContext'
import { OutputType } from './Output'

export class Count implements Generatable {
constructor(
protected readonly type: DMMF.OutputType,
protected readonly dmmf: DMMFHelper,
protected readonly genericsInfo: GenericArgsInfo,
protected readonly generator?: GeneratorConfig,
) {}
constructor(protected readonly type: DMMF.OutputType, protected readonly context: GenerateContext) {}
protected get argsTypes(): Generatable[] {
const argsTypes: Generatable[] = []

argsTypes.push(new ArgsType([], this.type, this.genericsInfo))
argsTypes.push(new ArgsType([], this.type, this.context))

for (const field of this.type.fields) {
if (field.args.length > 0) {
argsTypes.push(
new MinimalArgsType(
field.args,
this.type,
this.genericsInfo,
this.context,
undefined,
getCountArgsType(this.type.name, field.name),
),
Expand All @@ -42,7 +35,7 @@ export class Count implements Generatable {
public toTS(): string {
const { type } = this
const { name } = type
const outputType = new OutputType(this.dmmf, this.type)
const outputType = new OutputType(this.context.dmmf, this.type)

return `
/**
Expand Down
40 changes: 40 additions & 0 deletions packages/client/src/generation/TSClient/DefaultArgsAliases.ts
@@ -0,0 +1,40 @@
import { DMMFHelper } from '../dmmf'
import * as ts from '../ts-builders'
import { extArgsParam, getLegacyModelArgName, getModelArgName } from '../utils'

export class DefaultArgsAliases {
private existingArgTypes = new Set<string>()

registerArgName(name: string) {
this.existingArgTypes.add(name)
}

generateAliases(dmmf: DMMFHelper) {
const aliases: string[] = []
for (const modelName of Object.keys(dmmf.typeAndModelMap)) {
const legacyName = getLegacyModelArgName(modelName)
if (this.existingArgTypes.has(legacyName)) {
// alias to and old name is not created if there
// is already existing arg type with the same name
continue
}

const newName = getModelArgName(modelName)

aliases.push(
ts.stringify(
ts
.moduleExport(
ts
.typeDeclaration(legacyName, ts.namedType(newName).addGenericArgument(extArgsParam.toArgument()))
.addGenericParameter(extArgsParam),
)
.setDocComment(ts.docComment(`@deprecated Use ${newName} instead`)),
{ indentLevel: 1 },
),
)
}

return aliases.join('\n')
}
}
12 changes: 12 additions & 0 deletions packages/client/src/generation/TSClient/GenerateContext.ts
@@ -0,0 +1,12 @@
import { GeneratorConfig } from '@prisma/generator-helper'

import { DMMFHelper } from '../dmmf'
import { GenericArgsInfo } from '../GenericsArgsInfo'
import { DefaultArgsAliases } from './DefaultArgsAliases'

export interface GenerateContext {
dmmf: DMMFHelper
genericArgsInfo: GenericArgsInfo
defaultArgsAliases: DefaultArgsAliases
generator?: GeneratorConfig
}

0 comments on commit b27a996

Please sign in to comment.