From 999fdde9d049c76c0a7bd90ea2d37cca27db7115 Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Tue, 8 Jul 2025 00:12:28 -0400 Subject: [PATCH 1/4] chore: Improve performance for prisma-client generation --- .../src/plugins/enhancer/enhance/index.ts | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/schema/src/plugins/enhancer/enhance/index.ts b/packages/schema/src/plugins/enhancer/enhance/index.ts index b439a61fb..e09bb7c00 100644 --- a/packages/schema/src/plugins/enhancer/enhance/index.ts +++ b/packages/schema/src/plugins/enhancer/enhance/index.ts @@ -503,16 +503,13 @@ export type Enhanced = for (const d of this.model.declarations.filter(isDataModel)) { const fileName = `${prismaClientDir}/models/${d.name}.ts`; const sf = project.addSourceFileAtPath(fileName); - const sfNew = project.createSourceFile(`${prismaClientDir}/models/${d.name}-fixed.ts`, undefined, { - overwrite: true, - }); const syntaxList = sf.getChildren()[0]; if (!Node.isSyntaxList(syntaxList)) { throw new PluginError(name, `Unexpected syntax list structure in ${fileName}`); } - sfNew.addStatements('import $Types = runtime.Types;'); + const statements = ['import $Types = runtime.Types;']; // Add import for json-types if this model has JSON type fields const modelWithJsonFields = this.modelsWithJsonTypeFields.find((m) => m.name === d.name); @@ -525,23 +522,35 @@ export type Enhanced = const typeNames = [...new Set(jsonFieldTypes.map((field) => field.type.reference!.$refText))]; if (typeNames.length > 0) { - sfNew.addStatements(`import type { ${typeNames.join(', ')} } from "../../json-types";`); + statements.push(`import type { ${typeNames.join(', ')} } from "../../json-types";`); } } syntaxList.getChildren().forEach((node) => { if (Node.isInterfaceDeclaration(node)) { - sfNew.addInterface(this.transformInterface(node, delegateInfo)); + statements.push(this.transformInterface(node, delegateInfo)); } else if (Node.isTypeAliasDeclaration(node)) { - sfNew.addTypeAlias(this.transformTypeAlias(node, delegateInfo)); + statements.push(this.transformTypeAlias(node, delegateInfo)); } else { - sfNew.addStatements(node.getText()); + statements.push(node.getText()); } }); - await sfNew.move(sf.getFilePath(), { overwrite: true }); + const structure = sf.getStructure(); + structure.statements = statements; + + const sfNew = project.createSourceFile(`${prismaClientDir}/models/${d.name}-fixed.ts`, structure, { + overwrite: true, + }); await sfNew.save(); } + + for (const d of this.model.declarations.filter(isDataModel)) { + const fixedFileName = `${prismaClientDir}/models/${d.name}-fixed.ts`; + const fileName = `${prismaClientDir}/models/${d.name}.ts`; + + fs.renameSync(fixedFileName, fileName); + } } private transformPrismaTypes(sf: SourceFile, sfNew: SourceFile, delegateInfo: DelegateInfo) { From 3bfa67c4357201a9d87d4704c907654b6ed7cfcf Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Tue, 8 Jul 2025 02:21:44 -0400 Subject: [PATCH 2/4] Additional fixes for generated types in Prisma namespace --- .../src/plugins/enhancer/enhance/index.ts | 57 ++++++++++++++++++- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/packages/schema/src/plugins/enhancer/enhance/index.ts b/packages/schema/src/plugins/enhancer/enhance/index.ts index e09bb7c00..5b5076577 100644 --- a/packages/schema/src/plugins/enhancer/enhance/index.ts +++ b/packages/schema/src/plugins/enhancer/enhance/index.ts @@ -40,6 +40,7 @@ import { SyntaxKind, TypeAliasDeclaration, VariableStatement, + type StatementStructures, } from 'ts-morph'; import { name } from '..'; import { getConcreteModels, getDiscriminatorField } from '../../../utils/ast-utils'; @@ -492,6 +493,32 @@ export type Enhanced = private async processClientTypesNewPrismaGenerator(prismaClientDir: string, delegateInfo: DelegateInfo) { const project = new Project(); + // remove delegate_aux_* fields from the prismaNamespace.ts + const internalFilename = `${prismaClientDir}/internal/prismaNamespace.ts` + const internalFilenameFixed = `${prismaClientDir}/internal/prismaNamespace-fixed.ts` + const internalSf = project.addSourceFileAtPath(internalFilename); + const syntaxList = internalSf.getChildren()[0]; + if (!Node.isSyntaxList(syntaxList)) { + throw new PluginError(name, `Unexpected syntax list structure in ${internalFilename}`); + } + const statements: (string | StatementStructures)[] = []; + + syntaxList.getChildren().forEach((node) => { + if (Node.isVariableStatement(node)) { + statements.push(this.transformVariableStatementProps(node)); + } else { + statements.push(node.getText()); + } + }); + const structure = internalSf.getStructure(); + structure.statements = statements; + + const internalSfNew = project.createSourceFile(internalFilenameFixed, structure, { + overwrite: true, + }); + await internalSfNew.save(); + fs.renameSync(internalFilenameFixed, internalFilename); + // Create a shared file for all JSON fields type definitions const jsonFieldsFile = project.createSourceFile(path.join(this.outDir, 'json-types.ts'), undefined, { overwrite: true, @@ -509,7 +536,7 @@ export type Enhanced = throw new PluginError(name, `Unexpected syntax list structure in ${fileName}`); } - const statements = ['import $Types = runtime.Types;']; + const statements: (string | StatementStructures)[] = ['import $Types = runtime.Types;']; // Add import for json-types if this model has JSON type fields const modelWithJsonFields = this.modelsWithJsonTypeFields.find((m) => m.name === d.name); @@ -650,6 +677,26 @@ export type Enhanced = return structure; } + private transformVariableStatementProps(variable: VariableStatement) { + const structure = variable.getStructure(); + + // remove `delegate_aux_*` fields from the variable's initializer + const auxFields = this.findAuxProps(variable); + if (auxFields.length > 0) { + structure.declarations.forEach((variable) => { + if (variable.initializer) { + let source = variable.initializer; + auxFields.forEach((f) => { + source = this.removeFromSource(source, f.getText()); + }); + variable.initializer = source; + } + }); + } + + return structure; + } + private transformInterface(iface: InterfaceDeclaration, delegateInfo: DelegateInfo) { const structure = iface.getStructure(); @@ -967,6 +1014,12 @@ export type Enhanced = .filter((n) => n.getName().includes(DELEGATE_AUX_RELATION_PREFIX)); } + private findAuxProps(node: Node) { + return node + .getDescendantsOfKind(SyntaxKind.PropertyAssignment) + .filter((n) => n.getName().includes(DELEGATE_AUX_RELATION_PREFIX)); + } + private saveSourceFile(sf: SourceFile) { if (this.options.preserveTsFiles) { saveSourceFile(sf); @@ -983,7 +1036,7 @@ export type Enhanced = } private trimEmptyLines(source: string): string { - return source.replace(/^\s*[\r\n]/gm, ''); + return source.replace(/^\s*\,?[\r\n]/gm, ''); } private get isNewPrismaClientGenerator() { From c882c18bf540aa952877f7d82959ba8d80c65f05 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Mon, 14 Jul 2025 10:34:57 +0800 Subject: [PATCH 3/4] several fixes - Unnecessary regex escape - Type assertion - Removed logic that updates `prismaNamespace.ts` which seems to be unnecessary --- .../src/plugins/enhancer/enhance/index.ts | 31 ++----------------- 1 file changed, 3 insertions(+), 28 deletions(-) diff --git a/packages/schema/src/plugins/enhancer/enhance/index.ts b/packages/schema/src/plugins/enhancer/enhance/index.ts index 5b5076577..d69c21e4d 100644 --- a/packages/schema/src/plugins/enhancer/enhance/index.ts +++ b/packages/schema/src/plugins/enhancer/enhance/index.ts @@ -26,7 +26,7 @@ import { type Model, } from '@zenstackhq/sdk/ast'; import { getDMMF, getPrismaClientImportSpec, getPrismaVersion, type DMMF } from '@zenstackhq/sdk/prisma'; -import { upperCaseFirst } from '@zenstackhq/runtime/local-helpers'; +import { invariant, upperCaseFirst } from '@zenstackhq/runtime/local-helpers'; import fs from 'fs'; import path from 'path'; import semver from 'semver'; @@ -493,32 +493,6 @@ export type Enhanced = private async processClientTypesNewPrismaGenerator(prismaClientDir: string, delegateInfo: DelegateInfo) { const project = new Project(); - // remove delegate_aux_* fields from the prismaNamespace.ts - const internalFilename = `${prismaClientDir}/internal/prismaNamespace.ts` - const internalFilenameFixed = `${prismaClientDir}/internal/prismaNamespace-fixed.ts` - const internalSf = project.addSourceFileAtPath(internalFilename); - const syntaxList = internalSf.getChildren()[0]; - if (!Node.isSyntaxList(syntaxList)) { - throw new PluginError(name, `Unexpected syntax list structure in ${internalFilename}`); - } - const statements: (string | StatementStructures)[] = []; - - syntaxList.getChildren().forEach((node) => { - if (Node.isVariableStatement(node)) { - statements.push(this.transformVariableStatementProps(node)); - } else { - statements.push(node.getText()); - } - }); - const structure = internalSf.getStructure(); - structure.statements = statements; - - const internalSfNew = project.createSourceFile(internalFilenameFixed, structure, { - overwrite: true, - }); - await internalSfNew.save(); - fs.renameSync(internalFilenameFixed, internalFilename); - // Create a shared file for all JSON fields type definitions const jsonFieldsFile = project.createSourceFile(path.join(this.outDir, 'json-types.ts'), undefined, { overwrite: true, @@ -687,6 +661,7 @@ export type Enhanced = if (variable.initializer) { let source = variable.initializer; auxFields.forEach((f) => { + invariant(typeof source === 'string'); source = this.removeFromSource(source, f.getText()); }); variable.initializer = source; @@ -1036,7 +1011,7 @@ export type Enhanced = } private trimEmptyLines(source: string): string { - return source.replace(/^\s*\,?[\r\n]/gm, ''); + return source.replace(/^\s*,?[\r\n]/gm, ''); } private get isNewPrismaClientGenerator() { From 6def505ba533343fbfcc658ff7c6d7cf7e90c7e2 Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Mon, 14 Jul 2025 10:59:22 +0800 Subject: [PATCH 4/4] pin test projects to zod3 --- tests/integration/tests/cli/generate.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/tests/cli/generate.test.ts b/tests/integration/tests/cli/generate.test.ts index 857c4fed1..39e1611d5 100644 --- a/tests/integration/tests/cli/generate.test.ts +++ b/tests/integration/tests/cli/generate.test.ts @@ -45,7 +45,7 @@ model Post { // set up project fs.writeFileSync('package.json', JSON.stringify({ name: 'my app', version: '1.0.0' })); createNpmrc(); - installPackage('prisma @prisma/client zod'); + installPackage('prisma @prisma/client zod@3'); installPackage(path.join(__dirname, '../../../../packages/runtime/dist')); // set up schema