Skip to content

Commit

Permalink
Put 'value' into the codegen instead.
Browse files Browse the repository at this point in the history
  • Loading branch information
stephenh committed Apr 11, 2024
1 parent b560246 commit bfedc92
Show file tree
Hide file tree
Showing 47 changed files with 488 additions and 1,352 deletions.
43 changes: 22 additions & 21 deletions packages/codegen/src/generateEntityCodegenFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
EntityGraphQLFilter,
EntityManager,
EntityMetadata,
FieldType,
FieldsOf,
FilterOf,
Flavor,
Expand All @@ -45,7 +44,6 @@ import {
ReactiveField,
ReactiveReference,
SSAssert,
SettableFields,
TaggedId,
ValueFilter,
ValueGraphQLFilter,
Expand All @@ -64,12 +62,12 @@ import {
hasOneToOne,
isEntity,
isLoaded,
setFieldValue,
loadLens,
mustBeSubType,
newChangesProxy,
newRequiredRule,
setField,
setFieldValue,
setOpts,
toIdOf,
} from "./symbols";
Expand Down Expand Up @@ -404,11 +402,13 @@ export function generateEntityCodegenFile(config: Config, dbMeta: DbMetadata, me
const baseEntity = dbMeta.entities.find((e) => e.name === meta.baseClassName);
const subEntities = dbMeta.entities.filter((e) => e.baseClassName === meta.name);
const base = baseEntity?.entity.type ?? code`${BaseEntity}<${EntityManager}, ${idType}>`;
const maybeBaseFields = baseEntity ? code`extends ${imp('t:' + baseEntity.name + "Fields@./entities.ts")}` : "";
const maybeBaseFields = baseEntity ? code`extends ${imp("t:" + baseEntity.name + "Fields@./entities.ts")}` : "";
const maybeBaseOpts = baseEntity ? code`extends ${baseEntity.entity.optsType}` : "";
const maybeBaseIdOpts = baseEntity ? code`extends ${imp('t:' + baseEntity.name + "IdsOpts@./entities.ts")}` : "";
const maybeBaseFilter = baseEntity ? code`extends ${imp('t:' + baseEntity.name + "Filter@./entities.ts")}` : "";
const maybeBaseGqlFilter = baseEntity ? code`extends ${imp('t:' + baseEntity.name + "GraphQLFilter@./entities.ts")}` : "";
const maybeBaseIdOpts = baseEntity ? code`extends ${imp("t:" + baseEntity.name + "IdsOpts@./entities.ts")}` : "";
const maybeBaseFilter = baseEntity ? code`extends ${imp("t:" + baseEntity.name + "Filter@./entities.ts")}` : "";
const maybeBaseGqlFilter = baseEntity
? code`extends ${imp("t:" + baseEntity.name + "GraphQLFilter@./entities.ts")}`
: "";
const maybeBaseOrder = baseEntity ? code`extends ${baseEntity.entity.orderType}` : "";
const maybeBaseId = baseEntity ? code` & Flavor<${idType}, "${baseEntity.name}">` : "";
const maybePreventBaseTypeInstantiation = meta.abstract
Expand Down Expand Up @@ -532,13 +532,13 @@ export function generateEntityCodegenFile(config: Config, dbMeta: DbMetadata, me
getFieldValue<K extends keyof ${entityName}Fields>(
key: K
): ${FieldType}<${entityName}Fields, K> {
): ${entityName}Fields[K]["value"] {
return ${getField}(this as any, key);
}
setFieldValue<K extends keyof ${SettableFields}<${entityName}Fields> & keyof ${entityName}Fields>(
setFieldValue<K extends keyof ${entityName}Fields>(
key: K,
value: ${FieldType}<${entityName}Fields, K>,
value: ${entityName}Fields[K]["value"],
): void {
${setFieldValue}(this, key, value);
}
Expand Down Expand Up @@ -717,33 +717,34 @@ function generateOptsFields(config: Config, meta: EntityDbMetadata): Code[] {

// Make our fields type
function generateFieldsType(config: Config, meta: EntityDbMetadata): Code[] {
const id = code`id: { kind: "primitive"; type: ${meta.primaryKey.fieldType}; unique: ${true}; nullable: never };`;
const id = code`id: { kind: "primitive"; type: ${meta.primaryKey.fieldType}; unique: ${true}; nullable: never; value: never };`;
const primitives = meta.primitives.map((field) => {
const { fieldName, fieldType, notNull, unique, derived } = field;
return code`${fieldName}: { kind: "primitive"; type: ${fieldType}; unique: ${unique}; nullable: ${undefinedOrNever(
notNull,
)}, derived: ${derived !== false} };`;
const uOrNever = undefinedOrNever(notNull);
return code`${fieldName}: { kind: "primitive"; type: ${fieldType}; unique: ${unique}; nullable: ${uOrNever}; value: ${fieldType} | ${uOrNever}; derived: ${derived !== false} };`;
});
const enums = meta.enums.map((field) => {
const { fieldName, enumType, notNull, isArray } = field;
if (isArray) {
// Arrays are always optional and we'll default to `[]`
return code`${fieldName}: { kind: "enum"; type: ${enumType}[]; nullable: never };`;
return code`${fieldName}: { kind: "enum"; type: ${enumType}[]; nullable: never; value: never };`;
} else {
return code`${fieldName}: { kind: "enum"; type: ${enumType}; nullable: ${undefinedOrNever(notNull)} };`;
const uOrNever = undefinedOrNever(notNull);
return code`${fieldName}: { kind: "enum"; type: ${enumType}; nullable: ${uOrNever}; value: ${enumType} | ${uOrNever} };`;
}
});
const pgEnums = meta.pgEnums.map(({ fieldName, enumType, notNull }) => {
const nullable = undefinedOrNever(notNull);
return code`${fieldName}: { kind: "enum"; type: ${enumType}; nullable: ${nullable}; native: true };`;
return code`${fieldName}: { kind: "enum"; type: ${enumType}; nullable: ${nullable}; native: true; value: never };`;
});
const m2o = meta.manyToOnes.map(({ fieldName, otherEntity, notNull, derived }) => {
return code`${fieldName}: { kind: "m2o"; type: ${otherEntity.type}; nullable: ${undefinedOrNever(
notNull,
)}, derived: ${derived !== false} };`;
const uOrNever = undefinedOrNever(notNull);
return code`${fieldName}: { kind: "m2o"; type: ${otherEntity.type}; nullable: ${uOrNever}; value: ${otherEntity.idType} | ${uOrNever}; derived: ${derived !== false} };`;
});
const polys = meta.polymorphics.map(({ fieldName, notNull, fieldType }) => {
return code`${fieldName}: { kind: "poly"; type: ${fieldType}; nullable: ${undefinedOrNever(notNull)} };`;
const uOrNever = undefinedOrNever(notNull);
const genericIdType = config.idType === "number" ? "number" : "string";
return code`${fieldName}: { kind: "poly"; type: ${fieldType}; nullable: ${uOrNever}; value: ${genericIdType} | ${uOrNever}; }`;
});
return [id, ...primitives, ...enums, ...pgEnums, ...m2o, ...polys];
}
Expand Down
5 changes: 4 additions & 1 deletion packages/codegen/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ export async function generateAndSaveFiles(config: Config, dbMeta: DbMetadata):
toolName: "joist-codegen",
directory: config.entitiesDirectory,
files,
toStringOpts: { importExtensions: config.esm ? 'js' : false }
toStringOpts: {
dprintOptions: { lineWidth: 150 },
importExtensions: config.esm ? "js" : false,
},
});
}

Expand Down
1 change: 0 additions & 1 deletion packages/codegen/src/symbols.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ export const PrimitiveSerde = imp("PrimitiveSerde@joist-orm");
export const BigIntSerde = imp("BigIntSerde@joist-orm");
export const JsonSerde = imp("JsonSerde@joist-orm");
export const SettableFields = imp("SettableFields@joist-orm");
export const FieldType = imp("FieldType@joist-orm");
export const SuperstructSerde = imp("SuperstructSerde@joist-orm");
export const TaggedId = imp("t:TaggedId@joist-orm");
export const ZodSerde = imp("ZodSerde@joist-orm");
Expand Down
2 changes: 1 addition & 1 deletion packages/orm/src/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export interface Entity {
readonly isNewEntity: boolean;
readonly isDeletedEntity: boolean;
readonly isDirtyEntity: boolean;
getFieldValue(fieldName: string): any;
getFieldValue(fieldName: string): unknown;
setFieldValue(fieldName: string, value: unknown): void;
set(opts: Partial<OptsOf<this>>): void;
setPartial(values: PartialOrNull<OptsOf<this>>): void;
Expand Down
10 changes: 0 additions & 10 deletions packages/orm/src/EntityFields.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { IdOf } from "./EntityManager";

/** All the fields for an entity in the `FieldsOf` / `EntityField` format. */
export type EntityFields<T> = {
[K in keyof T]: EntityField;
Expand All @@ -21,11 +19,3 @@ export type SettableFields<F> = {
? F[K]
: never;
};

export type FieldType<F, K extends keyof F> = F[K] extends { kind: "primitive"; type: infer T; nullable: infer N }
? T | N
: F[K] extends { kind: "enum"; type: infer T; nullable: infer N }
? T | N
: F[K] extends { kind: "m2o"; type: infer E; nullable: infer N }
? IdOf<E> | N
: never;
31 changes: 10 additions & 21 deletions packages/tests/esm-misc/src/entities/codegen/AuthorCodegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
cleanStringValue,
ConfigApi,
failNoIdYet,
FieldType,
getField,
getInstanceData,
hasMany,
Expand All @@ -14,7 +13,6 @@ import {
setField,
setFieldValue,
setOpts,
SettableFields,
toIdOf,
} from "joist-orm";
import type {
Expand Down Expand Up @@ -45,12 +43,12 @@ import type { BookId, Entity } from "../entities.js";
export type AuthorId = Flavor<string, Author>;

export interface AuthorFields {
id: { kind: "primitive"; type: number; unique: true; nullable: never };
firstName: { kind: "primitive"; type: string; unique: false; nullable: never; derived: false };
lastName: { kind: "primitive"; type: string; unique: false; nullable: undefined; derived: false };
delete: { kind: "primitive"; type: boolean; unique: false; nullable: undefined; derived: false };
createdAt: { kind: "primitive"; type: Date; unique: false; nullable: never; derived: true };
updatedAt: { kind: "primitive"; type: Date; unique: false; nullable: never; derived: true };
id: { kind: "primitive"; type: number; unique: true; nullable: never; value: never };
firstName: { kind: "primitive"; type: string; unique: false; nullable: never; value: string | never; derived: false };
lastName: { kind: "primitive"; type: string; unique: false; nullable: undefined; value: string | undefined; derived: false };
delete: { kind: "primitive"; type: boolean; unique: false; nullable: undefined; value: boolean | undefined; derived: false };
createdAt: { kind: "primitive"; type: Date; unique: false; nullable: never; value: Date | never; derived: true };
updatedAt: { kind: "primitive"; type: Date; unique: false; nullable: never; value: Date | never; derived: true };
}

export interface AuthorOpts {
Expand Down Expand Up @@ -166,14 +164,11 @@ export abstract class AuthorCodegen extends BaseEntity<EntityManager, string> im
return getField(this, "updatedAt");
}

getFieldValue<K extends keyof AuthorFields>(key: K): FieldType<AuthorFields, K> {
getFieldValue<K extends keyof AuthorFields>(key: K): AuthorFields[K]["value"] {
return getField(this as any, key);
}

setFieldValue<K extends keyof SettableFields<AuthorFields> & keyof AuthorFields>(
key: K,
value: FieldType<AuthorFields, K>,
): void {
setFieldValue<K extends keyof AuthorFields>(key: K, value: AuthorFields[K]["value"]): void {
setFieldValue(this, key, value);
}

Expand All @@ -196,14 +191,8 @@ export abstract class AuthorCodegen extends BaseEntity<EntityManager, string> im
populate<H extends LoadHint<Author>>(hint: H): Promise<Loaded<Author, H>>;
populate<H extends LoadHint<Author>>(opts: { hint: H; forceReload?: boolean }): Promise<Loaded<Author, H>>;
populate<H extends LoadHint<Author>, V>(hint: H, fn: (a: Loaded<Author, H>) => V): Promise<V>;
populate<H extends LoadHint<Author>, V>(
opts: { hint: H; forceReload?: boolean },
fn: (a: Loaded<Author, H>) => V,
): Promise<V>;
populate<H extends LoadHint<Author>, V>(
hintOrOpts: any,
fn?: (a: Loaded<Author, H>) => V,
): Promise<Loaded<Author, H> | V> {
populate<H extends LoadHint<Author>, V>(opts: { hint: H; forceReload?: boolean }, fn: (a: Loaded<Author, H>) => V): Promise<V>;
populate<H extends LoadHint<Author>, V>(hintOrOpts: any, fn?: (a: Loaded<Author, H>) => V): Promise<Loaded<Author, H> | V> {
return this.em.populate(this as any as Author, hintOrOpts, fn);
}

Expand Down
20 changes: 6 additions & 14 deletions packages/tests/esm-misc/src/entities/codegen/BookCodegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
cleanStringValue,
ConfigApi,
failNoIdYet,
FieldType,
getField,
getInstanceData,
hasOne,
Expand All @@ -14,7 +13,6 @@ import {
setField,
setFieldValue,
setOpts,
SettableFields,
toIdOf,
} from "joist-orm";
import type {
Expand Down Expand Up @@ -43,9 +41,9 @@ import type { AuthorId, AuthorOrder, Entity } from "../entities.js";
export type BookId = Flavor<string, Book>;

export interface BookFields {
id: { kind: "primitive"; type: number; unique: true; nullable: never };
title: { kind: "primitive"; type: string; unique: false; nullable: never; derived: false };
author: { kind: "m2o"; type: Author; nullable: never; derived: false };
id: { kind: "primitive"; type: number; unique: true; nullable: never; value: never };
title: { kind: "primitive"; type: string; unique: false; nullable: never; value: string | never; derived: false };
author: { kind: "m2o"; type: Author; nullable: never; value: AuthorId | never; derived: false };
}

export interface BookOpts {
Expand Down Expand Up @@ -123,14 +121,11 @@ export abstract class BookCodegen extends BaseEntity<EntityManager, string> impl
setField(this, "title", cleanStringValue(title));
}

getFieldValue<K extends keyof BookFields>(key: K): FieldType<BookFields, K> {
getFieldValue<K extends keyof BookFields>(key: K): BookFields[K]["value"] {
return getField(this as any, key);
}

setFieldValue<K extends keyof SettableFields<BookFields> & keyof BookFields>(
key: K,
value: FieldType<BookFields, K>,
): void {
setFieldValue<K extends keyof BookFields>(key: K, value: BookFields[K]["value"]): void {
setFieldValue(this, key, value);
}

Expand All @@ -153,10 +148,7 @@ export abstract class BookCodegen extends BaseEntity<EntityManager, string> impl
populate<H extends LoadHint<Book>>(hint: H): Promise<Loaded<Book, H>>;
populate<H extends LoadHint<Book>>(opts: { hint: H; forceReload?: boolean }): Promise<Loaded<Book, H>>;
populate<H extends LoadHint<Book>, V>(hint: H, fn: (b: Loaded<Book, H>) => V): Promise<V>;
populate<H extends LoadHint<Book>, V>(
opts: { hint: H; forceReload?: boolean },
fn: (b: Loaded<Book, H>) => V,
): Promise<V>;
populate<H extends LoadHint<Book>, V>(opts: { hint: H; forceReload?: boolean }, fn: (b: Loaded<Book, H>) => V): Promise<V>;
populate<H extends LoadHint<Book>, V>(hintOrOpts: any, fn?: (b: Loaded<Book, H>) => V): Promise<Loaded<Book, H> | V> {
return this.em.populate(this as any as Book, hintOrOpts, fn);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
cleanStringValue,
ConfigApi,
failNoIdYet,
FieldType,
getField,
isLoaded,
loadLens,
Expand All @@ -11,7 +10,6 @@ import {
setField,
setFieldValue,
setOpts,
SettableFields,
toIdOf,
} from "joist-orm";
import type {
Expand All @@ -35,8 +33,8 @@ import type { Entity, UserFields, UserFilter, UserGraphQLFilter, UserIdsOpts, Us
export type AdminUserId = Flavor<string, AdminUser> & Flavor<string, "User">;

export interface AdminUserFields extends UserFields {
id: { kind: "primitive"; type: number; unique: true; nullable: never };
role: { kind: "primitive"; type: string; unique: false; nullable: never; derived: false };
id: { kind: "primitive"; type: number; unique: true; nullable: never; value: never };
role: { kind: "primitive"; type: string; unique: false; nullable: never; value: string | never; derived: false };
}

export interface AdminUserOpts extends UserOpts {
Expand Down Expand Up @@ -105,14 +103,11 @@ export abstract class AdminUserCodegen extends User implements Entity {
setField(this, "role", cleanStringValue(role));
}

getFieldValue<K extends keyof AdminUserFields>(key: K): FieldType<AdminUserFields, K> {
getFieldValue<K extends keyof AdminUserFields>(key: K): AdminUserFields[K]["value"] {
return getField(this as any, key);
}

setFieldValue<K extends keyof SettableFields<AdminUserFields> & keyof AdminUserFields>(
key: K,
value: FieldType<AdminUserFields, K>,
): void {
setFieldValue<K extends keyof AdminUserFields>(key: K, value: AdminUserFields[K]["value"]): void {
setFieldValue(this, key, value);
}

Expand All @@ -135,14 +130,8 @@ export abstract class AdminUserCodegen extends User implements Entity {
populate<H extends LoadHint<AdminUser>>(hint: H): Promise<Loaded<AdminUser, H>>;
populate<H extends LoadHint<AdminUser>>(opts: { hint: H; forceReload?: boolean }): Promise<Loaded<AdminUser, H>>;
populate<H extends LoadHint<AdminUser>, V>(hint: H, fn: (u: Loaded<AdminUser, H>) => V): Promise<V>;
populate<H extends LoadHint<AdminUser>, V>(
opts: { hint: H; forceReload?: boolean },
fn: (u: Loaded<AdminUser, H>) => V,
): Promise<V>;
populate<H extends LoadHint<AdminUser>, V>(
hintOrOpts: any,
fn?: (u: Loaded<AdminUser, H>) => V,
): Promise<Loaded<AdminUser, H> | V> {
populate<H extends LoadHint<AdminUser>, V>(opts: { hint: H; forceReload?: boolean }, fn: (u: Loaded<AdminUser, H>) => V): Promise<V>;
populate<H extends LoadHint<AdminUser>, V>(hintOrOpts: any, fn?: (u: Loaded<AdminUser, H>) => V): Promise<Loaded<AdminUser, H> | V> {
return this.em.populate(this as any as AdminUser, hintOrOpts, fn);
}

Expand Down

0 comments on commit bfedc92

Please sign in to comment.