Skip to content

Commit 0d89d81

Browse files
committed
fix: Emit metadata and enabled emitSingle cause TDZ issue
closes: #16
1 parent 1c5d727 commit 0d89d81

9 files changed

+91
-12
lines changed

Taskfile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@ compatibilty_check() {
2222

2323
# emitSingle and emitCompiled
2424
sed -i 's/emitSingle = false/emitSingle = true/g' prisma/schema.prisma
25-
rm -rf src/@generated && npm run prisma:g && npx tsc --noEmit --skipLibCheck --experimentalDecorators src/@generated/index.ts
25+
rm -rf src/@generated && npm run prisma:g
26+
npx tsc --noEmit --skipLibCheck --experimentalDecorators src/@generated/index.ts
27+
npx ts-node src/@generated/index.ts
2628

2729
sed -i 's/emitCompiled = false/emitCompiled = true/g' prisma/schema.prisma
28-
rm -rf src/@generated && npm run prisma:g && node src/@generated/index.js
30+
rm -rf src/@generated && npm run prisma:g
31+
node src/@generated/index.js
2932

3033
sed -i 's/emitCompiled = true/emitCompiled = false/g' prisma/schema.prisma
3134
sed -i 's/emitSingle = true/emitSingle = false/g' prisma/schema.prisma

src/generate.spec.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
SourceFile,
99
} from 'ts-morph';
1010

11+
import { User } from './@generated/user/user.model';
1112
import { generate } from './generate';
1213
import { generateFileName } from './helpers/generate-file-name';
1314
import {
@@ -1459,7 +1460,7 @@ describe('emit single', () => {
14591460
}
14601461
model Post {
14611462
id Int @id
1462-
User User? @relation(fields: [userId], references: [id])
1463+
user User? @relation(fields: [userId], references: [id])
14631464
userId Int?
14641465
}
14651466
`;
@@ -1492,6 +1493,28 @@ describe('emit single', () => {
14921493
expect(sourceText).toMatch(/export class Post {/);
14931494
});
14941495

1496+
it('should use InstanceType trick to avoid tdz', () => {
1497+
const type = sourceFile
1498+
.getClass('Post')
1499+
?.getProperty('user')
1500+
?.getStructure().type;
1501+
expect(type).toEqual('InstanceType<typeof User>');
1502+
});
1503+
1504+
it('type for all properties should use InstanceType trick to avoid tdz', () => {
1505+
const types = sourceFile
1506+
.getClasses()
1507+
.flatMap(c => c.getProperties().map(p => ({ c, p })))
1508+
.map(({ c, p }) => ({ c, p, type: p.getType() }))
1509+
.filter(({ type }) => type.isClass())
1510+
.map(({ c, p }) => ({
1511+
class: c.getName(),
1512+
property: p.getStructure().name,
1513+
type: p.getStructure().type,
1514+
}));
1515+
expect(types).toHaveLength(0);
1516+
});
1517+
14951518
// it('^', () => console.log(sourceFile.getText()));
14961519
});
14971520

src/generate.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { Project, QuoteKind } from 'ts-morph';
77
import { argsType } from './handlers/args-type';
88
import { combineScalarFilters } from './handlers/combine-scalar-filters';
99
import { createAggregateInput } from './handlers/create-aggregate-input';
10+
import { emitSingle } from './handlers/emit-single';
1011
import { beforeGenerateFiles, generateFiles } from './handlers/generate-files';
1112
import { inputType } from './handlers/input-type';
1213
import { modelData } from './handlers/model-data';
@@ -81,13 +82,18 @@ export async function generate(
8182
quoteKind: QuoteKind.Single,
8283
},
8384
});
85+
8486
if (!skipAddOutputSourceFiles) {
85-
project.addSourceFilesAtPaths(`${generatorOutputValue}/**/*.ts`);
87+
project.addSourceFilesAtPaths([
88+
`${generatorOutputValue}/**/*.ts`,
89+
`!${generatorOutputValue}/**/*.d.ts`,
90+
]);
8691
}
8792

8893
config.combineScalarFilters && combineScalarFilters(eventEmitter);
8994
config.noAtomicOperations && noAtomicOperations(eventEmitter);
9095
config.reExport !== ReExport.None && reExport(eventEmitter);
96+
config.emitSingle && emitSingle(eventEmitter);
9197

9298
const models = new Map<string, Model>();
9399
const modelNames: string[] = [];

src/handlers/emit-single.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import AwaitEventEmitter from 'await-event-emitter';
2+
import { PropertyDeclarationStructure } from 'ts-morph';
3+
4+
import { DMMF } from '../types';
5+
6+
export function emitSingle(emitter: AwaitEventEmitter) {
7+
emitter.on('ClassProperty', classProperty);
8+
}
9+
10+
function classProperty(
11+
property: PropertyDeclarationStructure,
12+
eventArguments: { location: DMMF.FieldLocation; isList: boolean },
13+
) {
14+
const { location, isList } = eventArguments;
15+
if (['inputObjectTypes', 'outputObjectTypes'].includes(location) && !isList) {
16+
property.type = `InstanceType<typeof ${String(property.type)}>`;
17+
}
18+
}

src/handlers/generate-files.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
ClassDeclarationStructure,
33
ImportSpecifierStructure,
4-
ScriptKind,
54
StatementStructures,
65
StructureKind,
76
} from 'ts-morph';

src/handlers/input-type.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ export function inputType(
122122
}),
123123
],
124124
});
125+
126+
eventEmitter.emitSync('ClassProperty', property, { location, isList });
125127
}
126128

127129
sourceFile.set({

src/handlers/model-output-type.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,14 @@ import { propertyStructure } from '../helpers/property-structure';
1717
import { EventArguments, OutputType } from '../types';
1818

1919
export function modelOutputType(outputType: OutputType, args: EventArguments) {
20-
const { getSourceFile, models, config, modelFields, fieldSettings } = args;
20+
const {
21+
getSourceFile,
22+
models,
23+
config,
24+
modelFields,
25+
fieldSettings,
26+
eventEmitter,
27+
} = args;
2128
const model = models.get(outputType.name);
2229
assert(model, `Cannot find model by name ${outputType.name}`);
2330
const sourceFile = getSourceFile({
@@ -149,6 +156,8 @@ export function modelOutputType(outputType: OutputType, args: EventArguments) {
149156
],
150157
});
151158
}
159+
160+
eventEmitter.emitSync('ClassProperty', property, { location, isList });
152161
}
153162

154163
const hasExportDeclaration = (sourceFileStructure.statements as StatementStructures[]).some(

src/handlers/output-type.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ export function outputType(outputType: OutputType, args: EventArguments) {
129129
],
130130
});
131131
}
132+
133+
eventEmitter.emitSync('ClassProperty', property, { location, isList });
132134
}
133135

134136
sourceFile.set({

src/testing/compatibility.ts

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Prisma, PrismaClient } from '@prisma/client';
2+
import * as P from '@prisma/client';
23
import { Decimal } from '@prisma/client/runtime';
34

45
import { DummyCreateInput } from '../@generated/dummy/dummy-create.input';
@@ -9,6 +10,7 @@ import { StringFilter } from '../@generated/prisma/string-filter.input';
910
import { AggregateUserArgs } from '../@generated/user/aggregate-user.args';
1011
import { FindManyUserArgs } from '../@generated/user/find-many-user.args';
1112
import { GroupByUserArgs } from '../@generated/user/group-by-user.args';
13+
import { User } from '../@generated/user/user.model';
1214
import { UserCreateInput } from '../@generated/user/user-create.input';
1315
import { UserCreateWithoutArticlesInput } from '../@generated/user/user-create-without-articles.input';
1416
import { UserCreateWithoutCommentsInput } from '../@generated/user/user-create-without-comments.input';
@@ -119,12 +121,6 @@ const $prisma = new PrismaClient();
119121
let p: Prisma.DummyCreateInput['json'] = {};
120122
p = x;
121123
}
122-
// {
123-
// // todo: get property type should return ['Decimal', 'number', 'string']; where Decimal is import from
124-
// const x: DummyCreateInput['decimal'] = new Decimal(1);
125-
// let p: Prisma.DummyCreateInput['decimal'];
126-
// p = x;
127-
// }
128124
{
129125
const x: UserCreateInput = {
130126
email: '',
@@ -138,3 +134,24 @@ const $prisma = new PrismaClient();
138134
};
139135
p = x;
140136
}
137+
// {
138+
// // incompatible
139+
// let x: User = {
140+
// id: '',
141+
// email: '',
142+
// name: '',
143+
// password: '',
144+
// };
145+
// let p: P.User = {
146+
// id: '',
147+
// email: '',
148+
// name: '',
149+
// password: '',
150+
// bio: null,
151+
// image: null,
152+
// countComments: null,
153+
// rating: null,
154+
// role: null,
155+
// };
156+
// x = p;
157+
// }

0 commit comments

Comments
 (0)