Skip to content

Commit f85711a

Browse files
authored
refactor(schema): widen types for attributes, default, and foreignKeyFor (#2482)
2 parents 62dfcd1 + c96bdba commit f85711a

File tree

32 files changed

+3252
-3211
lines changed

32 files changed

+3252
-3211
lines changed

packages/clients/tanstack-query/test/schemas/basic/schema-lite.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
/* eslint-disable */
77

8-
import { type SchemaDef, ExpressionUtils } from "@zenstackhq/schema";
8+
import { type SchemaDef, type FieldDefault, ExpressionUtils } from "@zenstackhq/schema";
99
export class SchemaType implements SchemaDef {
1010
provider = {
1111
type: "sqlite"
@@ -18,7 +18,7 @@ export class SchemaType implements SchemaDef {
1818
name: "id",
1919
type: "String",
2020
id: true,
21-
default: ExpressionUtils.call("cuid")
21+
default: ExpressionUtils.call("cuid") as FieldDefault
2222
},
2323
email: {
2424
name: "email",
@@ -50,7 +50,7 @@ export class SchemaType implements SchemaDef {
5050
name: "id",
5151
type: "String",
5252
id: true,
53-
default: ExpressionUtils.call("cuid")
53+
default: ExpressionUtils.call("cuid") as FieldDefault
5454
},
5555
title: {
5656
name: "title",
@@ -68,7 +68,7 @@ export class SchemaType implements SchemaDef {
6868
optional: true,
6969
foreignKeyFor: [
7070
"owner"
71-
]
71+
] as readonly string[]
7272
},
7373
category: {
7474
name: "category",
@@ -82,7 +82,7 @@ export class SchemaType implements SchemaDef {
8282
optional: true,
8383
foreignKeyFor: [
8484
"category"
85-
]
85+
] as readonly string[]
8686
}
8787
},
8888
idFields: ["id"],
@@ -97,7 +97,7 @@ export class SchemaType implements SchemaDef {
9797
name: "id",
9898
type: "String",
9999
id: true,
100-
default: ExpressionUtils.call("cuid")
100+
default: ExpressionUtils.call("cuid") as FieldDefault
101101
},
102102
name: {
103103
name: "name",
@@ -124,7 +124,7 @@ export class SchemaType implements SchemaDef {
124124
name: "id",
125125
type: "String",
126126
id: true,
127-
default: ExpressionUtils.call("cuid")
127+
default: ExpressionUtils.call("cuid") as FieldDefault
128128
},
129129
type: {
130130
name: "type",
@@ -147,7 +147,7 @@ export class SchemaType implements SchemaDef {
147147
name: "id",
148148
type: "String",
149149
id: true,
150-
default: ExpressionUtils.call("cuid")
150+
default: ExpressionUtils.call("cuid") as FieldDefault
151151
},
152152
type: {
153153
name: "type",

packages/schema/src/schema.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ export type UpdatedAtInfo = {
6363
ignore?: readonly string[];
6464
};
6565

66+
export type FieldDefault = MappedBuiltinType | Expression | readonly unknown[];
67+
6668
export type FieldDef = {
6769
name: string;
6870
type: string;
@@ -72,7 +74,7 @@ export type FieldDef = {
7274
unique?: boolean;
7375
updatedAt?: boolean | UpdatedAtInfo;
7476
attributes?: readonly AttributeApplication[];
75-
default?: MappedBuiltinType | Expression | readonly unknown[];
77+
default?: FieldDefault;
7678
omit?: boolean;
7779
relation?: RelationInfo;
7880
foreignKeyFor?: readonly string[];
@@ -285,7 +287,7 @@ export type FieldHasDefault<
285287
Schema extends SchemaDef,
286288
Model extends GetModels<Schema>,
287289
Field extends GetModelFields<Schema, Model>,
288-
> = GetModelField<Schema, Model, Field>['default'] extends object | number | string | boolean
290+
> = 'default' extends keyof GetModelField<Schema, Model, Field>
289291
? true
290292
: GetModelField<Schema, Model, Field>['updatedAt'] extends true | UpdatedAtInfo
291293
? true

packages/sdk/src/ts-schema-generator.ts

Lines changed: 107 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,12 @@ export type TsSchemaGeneratorOptions = {
6464

6565
export class TsSchemaGenerator {
6666
private usedExpressionUtils = false;
67+
private usedAttributeApplication = false;
68+
private usedFieldDefault = false;
6769

6870
async generate(model: Model, options: TsSchemaGeneratorOptions) {
6971
fs.mkdirSync(options.outDir, { recursive: true });
7072

71-
// Reset the flag for each generation
72-
this.usedExpressionUtils = false;
73-
7473
// the schema itself
7574
this.generateSchema(model, options);
7675

@@ -95,6 +94,11 @@ export class TsSchemaGenerator {
9594
}
9695

9796
for (const { lite, file } of targets) {
97+
// Reset per-target import flags so each target tracks its own usage
98+
this.usedExpressionUtils = false;
99+
this.usedAttributeApplication = false;
100+
this.usedFieldDefault = false;
101+
98102
const statements: ts.Statement[] = [];
99103
this.generateSchemaStatements(model, statements, lite);
100104
this.generateBannerComments(statements);
@@ -130,6 +134,24 @@ export class TsSchemaGenerator {
130134
undefined,
131135
ts.factory.createNamedImports([
132136
ts.factory.createImportSpecifier(true, undefined, ts.factory.createIdentifier('SchemaDef')),
137+
...(this.usedAttributeApplication
138+
? [
139+
ts.factory.createImportSpecifier(
140+
true,
141+
undefined,
142+
ts.factory.createIdentifier('AttributeApplication'),
143+
),
144+
]
145+
: []),
146+
...(this.usedFieldDefault
147+
? [
148+
ts.factory.createImportSpecifier(
149+
true,
150+
undefined,
151+
ts.factory.createIdentifier('FieldDefault'),
152+
),
153+
]
154+
: []),
133155
...(this.usedExpressionUtils
134156
? [
135157
ts.factory.createImportSpecifier(
@@ -285,6 +307,22 @@ export class TsSchemaGenerator {
285307
return ts.factory.createAsExpression(expr, ts.factory.createTypeReferenceNode('const'));
286308
}
287309

310+
private createAttributesTypeAssertion(expr: ts.Expression): ts.Expression {
311+
this.usedAttributeApplication = true;
312+
return ts.factory.createAsExpression(
313+
expr,
314+
ts.factory.createTypeOperatorNode(
315+
ts.SyntaxKind.ReadonlyKeyword,
316+
ts.factory.createArrayTypeNode(ts.factory.createTypeReferenceNode('AttributeApplication')),
317+
),
318+
);
319+
}
320+
321+
private createDefaultTypeAssertion(expr: ts.Expression): ts.Expression {
322+
this.usedFieldDefault = true;
323+
return ts.factory.createAsExpression(expr, ts.factory.createTypeReferenceNode('FieldDefault'));
324+
}
325+
288326
private createProviderObject(model: Model): ts.Expression {
289327
const dsProvider = this.getDataSourceProvider(model);
290328
const defaultSchema = this.getDataSourceDefaultSchema(model);
@@ -374,9 +412,11 @@ export class TsSchemaGenerator {
374412
? [
375413
ts.factory.createPropertyAssignment(
376414
'attributes',
377-
ts.factory.createArrayLiteralExpression(
378-
allAttributes.map((attr) => this.createAttributeObject(attr)),
379-
true,
415+
this.createAttributesTypeAssertion(
416+
ts.factory.createArrayLiteralExpression(
417+
allAttributes.map((attr) => this.createAttributeObject(attr)),
418+
true,
419+
),
380420
),
381421
),
382422
]
@@ -458,9 +498,11 @@ export class TsSchemaGenerator {
458498
? [
459499
ts.factory.createPropertyAssignment(
460500
'attributes',
461-
ts.factory.createArrayLiteralExpression(
462-
allAttributes.map((attr) => this.createAttributeObject(attr)),
463-
true,
501+
this.createAttributesTypeAssertion(
502+
ts.factory.createArrayLiteralExpression(
503+
allAttributes.map((attr) => this.createAttributeObject(attr)),
504+
true,
505+
),
464506
),
465507
),
466508
]
@@ -608,71 +650,56 @@ export class TsSchemaGenerator {
608650
objectFields.push(
609651
ts.factory.createPropertyAssignment(
610652
'attributes',
611-
ts.factory.createArrayLiteralExpression(
612-
field.attributes.map((attr) => this.createAttributeObject(attr)),
653+
this.createAttributesTypeAssertion(
654+
ts.factory.createArrayLiteralExpression(
655+
field.attributes.map((attr) => this.createAttributeObject(attr)),
656+
),
613657
),
614658
),
615659
);
616660
}
617661

618662
const defaultValue = this.getFieldMappedDefault(field);
619663
if (defaultValue !== undefined) {
664+
let defaultExpr: ts.Expression;
620665
if (defaultValue === null) {
621-
objectFields.push(
622-
ts.factory.createPropertyAssignment('default', this.createExpressionUtilsCall('_null')),
623-
);
666+
defaultExpr = this.createExpressionUtilsCall('_null');
624667
} else if (typeof defaultValue === 'object' && !Array.isArray(defaultValue)) {
625668
if ('call' in defaultValue) {
626-
objectFields.push(
627-
ts.factory.createPropertyAssignment(
628-
'default',
629-
this.createExpressionUtilsCall('call', [
630-
ts.factory.createStringLiteral(defaultValue.call),
631-
...(defaultValue.args.length > 0
632-
? [
633-
ts.factory.createArrayLiteralExpression(
634-
defaultValue.args.map((arg) =>
635-
this.createExpressionUtilsCall('literal', [
636-
this.createLiteralNode(arg),
637-
]),
638-
),
639-
),
640-
]
641-
: []),
642-
]),
643-
),
644-
);
669+
defaultExpr = this.createExpressionUtilsCall('call', [
670+
ts.factory.createStringLiteral(defaultValue.call),
671+
...(defaultValue.args.length > 0
672+
? [
673+
ts.factory.createArrayLiteralExpression(
674+
defaultValue.args.map((arg) =>
675+
this.createExpressionUtilsCall('literal', [
676+
this.createLiteralNode(arg),
677+
]),
678+
),
679+
),
680+
]
681+
: []),
682+
]);
645683
} else if ('authMember' in defaultValue) {
646-
objectFields.push(
647-
ts.factory.createPropertyAssignment(
648-
'default',
649-
this.createExpressionUtilsCall('member', [
650-
this.createExpressionUtilsCall('call', [ts.factory.createStringLiteral('auth')]),
651-
ts.factory.createArrayLiteralExpression(
652-
defaultValue.authMember.map((m) => ts.factory.createStringLiteral(m)),
653-
),
654-
]),
684+
defaultExpr = this.createExpressionUtilsCall('member', [
685+
this.createExpressionUtilsCall('call', [ts.factory.createStringLiteral('auth')]),
686+
ts.factory.createArrayLiteralExpression(
687+
defaultValue.authMember.map((m) => ts.factory.createStringLiteral(m)),
655688
),
656-
);
689+
]);
657690
} else {
658691
throw new Error(`Unsupported default value type for field ${field.name}`);
659692
}
693+
} else if (Array.isArray(defaultValue)) {
694+
defaultExpr = ts.factory.createArrayLiteralExpression(
695+
defaultValue.map((item) => this.createLiteralNode(item as any)),
696+
);
660697
} else {
661-
if (Array.isArray(defaultValue)) {
662-
objectFields.push(
663-
ts.factory.createPropertyAssignment(
664-
'default',
665-
ts.factory.createArrayLiteralExpression(
666-
defaultValue.map((item) => this.createLiteralNode(item as any)),
667-
),
668-
),
669-
);
670-
} else {
671-
objectFields.push(
672-
ts.factory.createPropertyAssignment('default', this.createLiteralNode(defaultValue)),
673-
);
674-
}
698+
defaultExpr = this.createLiteralNode(defaultValue);
675699
}
700+
objectFields.push(
701+
ts.factory.createPropertyAssignment('default', this.createDefaultTypeAssertion(defaultExpr)),
702+
);
676703
}
677704

678705
if (hasAttribute(field, '@computed')) {
@@ -688,9 +715,17 @@ export class TsSchemaGenerator {
688715
objectFields.push(
689716
ts.factory.createPropertyAssignment(
690717
'foreignKeyFor',
691-
ts.factory.createArrayLiteralExpression(
692-
fkFor.map((fk) => ts.factory.createStringLiteral(fk)),
693-
true,
718+
ts.factory.createAsExpression(
719+
ts.factory.createArrayLiteralExpression(
720+
fkFor.map((fk) => ts.factory.createStringLiteral(fk)),
721+
true,
722+
),
723+
ts.factory.createTypeOperatorNode(
724+
ts.SyntaxKind.ReadonlyKeyword,
725+
ts.factory.createArrayTypeNode(
726+
ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword),
727+
),
728+
),
694729
),
695730
),
696731
);
@@ -1070,11 +1105,13 @@ export class TsSchemaGenerator {
10701105
? [
10711106
ts.factory.createPropertyAssignment(
10721107
'attributes',
1073-
ts.factory.createArrayLiteralExpression(
1074-
field.attributes?.map((attr) =>
1075-
this.createAttributeObject(attr),
1076-
) ?? [],
1077-
true,
1108+
this.createAttributesTypeAssertion(
1109+
ts.factory.createArrayLiteralExpression(
1110+
field.attributes?.map((attr) =>
1111+
this.createAttributeObject(attr),
1112+
) ?? [],
1113+
true,
1114+
),
10781115
),
10791116
),
10801117
]
@@ -1094,9 +1131,11 @@ export class TsSchemaGenerator {
10941131
? [
10951132
ts.factory.createPropertyAssignment(
10961133
'attributes',
1097-
ts.factory.createArrayLiteralExpression(
1098-
e.attributes.map((attr) => this.createAttributeObject(attr)),
1099-
true,
1134+
this.createAttributesTypeAssertion(
1135+
ts.factory.createArrayLiteralExpression(
1136+
e.attributes.map((attr) => this.createAttributeObject(attr)),
1137+
true,
1138+
),
11001139
),
11011140
),
11021141
]

0 commit comments

Comments
 (0)