mirror of
https://github.com/theoludwig/kysely-typegen.git
synced 2026-05-22 16:23:25 +02:00
feat: add KyselyTypegenPostgresDialect
This commit is contained in:
@@ -0,0 +1,608 @@
|
||||
exports[`typegen > generate types matching snapshot 1`] = `
|
||||
{
|
||||
"lines": [
|
||||
"// This file was automatically generated by \`kysely-typegen\`.",
|
||||
"// Do not edit this file manually.",
|
||||
"",
|
||||
"import type { ColumnType } from \\"kysely\\"",
|
||||
"",
|
||||
"export type Generated<T> = T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>",
|
||||
"",
|
||||
"export type Timestamp = ColumnType<Date, Date | string, Date | string>",
|
||||
"",
|
||||
"export type Numeric = ColumnType<string, number | string, number | string>",
|
||||
"",
|
||||
"export type Int8 = ColumnType<string, bigint | number | string, bigint | number | string>",
|
||||
"",
|
||||
"export type Json = JsonValue",
|
||||
"",
|
||||
"export type JsonArray = JsonValue[]",
|
||||
"",
|
||||
"export interface JsonObject {",
|
||||
" [x: string]: JsonValue | undefined",
|
||||
"}",
|
||||
"",
|
||||
"export type JsonPrimitive = boolean | number | string | null",
|
||||
"",
|
||||
"export type JsonValue = JsonArray | JsonObject | JsonPrimitive",
|
||||
"",
|
||||
"export type Currency = \\"EUR\\" | \\"GBP\\" | \\"USD\\"",
|
||||
"",
|
||||
"export type OrderStatus = \\"cancelled\\" | \\"paid\\" | \\"pending\\" | \\"shipped\\"",
|
||||
"",
|
||||
"export type UserRole = \\"admin\\" | \\"guest\\" | \\"member\\"",
|
||||
"",
|
||||
"export interface AllTypes {",
|
||||
" colBit: string",
|
||||
" colBool: boolean",
|
||||
" colBoolDefault: Generated<boolean>",
|
||||
" colBoolNullable: boolean | null",
|
||||
" colBox: string",
|
||||
" colBpchar: string",
|
||||
" colBytea: Buffer",
|
||||
" colCidr: string",
|
||||
" colDate: Timestamp",
|
||||
" colFloat4: number",
|
||||
" colFloat8: number",
|
||||
" colInet: string",
|
||||
" colInt2: number",
|
||||
" colInt4: number",
|
||||
" colInt8: Int8",
|
||||
" colJson: Json",
|
||||
" colJsonb: Json",
|
||||
" colJsonbDefault: Generated<Json>",
|
||||
" colLine: string",
|
||||
" colLseg: string",
|
||||
" colMacaddr: string",
|
||||
" colMoney: string",
|
||||
" colNumeric: Numeric",
|
||||
" colOid: number",
|
||||
" colPath: string",
|
||||
" colPoint: unknown",
|
||||
" colPolygon: string",
|
||||
" colText: string",
|
||||
" colTextNullable: string | null",
|
||||
" colTime: string",
|
||||
" colTimestamp: Timestamp",
|
||||
" colTimestampDefault: Generated<Timestamp>",
|
||||
" colTimestamptz: Timestamp",
|
||||
" colTimetz: string",
|
||||
" colTsquery: string",
|
||||
" colTsvector: string",
|
||||
" colUuid: string",
|
||||
" colVarbit: string",
|
||||
" colVarchar: string",
|
||||
" colXml: string",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" id: Generated<string>",
|
||||
" updatedAt: Timestamp | null",
|
||||
"}",
|
||||
"",
|
||||
"export interface Orders {",
|
||||
" amountCents: number",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" currency: Generated<Currency>",
|
||||
" id: Generated<Int8>",
|
||||
" note: string | null",
|
||||
" status: Generated<OrderStatus>",
|
||||
" userId: string",
|
||||
"}",
|
||||
"",
|
||||
"export interface Users {",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" email: string | null",
|
||||
" id: Generated<string>",
|
||||
" isActive: Generated<boolean>",
|
||||
" role: Generated<UserRole>",
|
||||
" username: string",
|
||||
"}",
|
||||
"",
|
||||
"export interface DB {",
|
||||
" AllTypes: AllTypes",
|
||||
" Orders: Orders",
|
||||
" Users: Users",
|
||||
"}"
|
||||
],
|
||||
"enums": [
|
||||
{
|
||||
"name": "Currency",
|
||||
"values": [
|
||||
"EUR",
|
||||
"GBP",
|
||||
"USD"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OrderStatus",
|
||||
"values": [
|
||||
"cancelled",
|
||||
"paid",
|
||||
"pending",
|
||||
"shipped"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "UserRole",
|
||||
"values": [
|
||||
"admin",
|
||||
"guest",
|
||||
"member"
|
||||
]
|
||||
}
|
||||
],
|
||||
"tables": [
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"dataType": "uuid",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "id"
|
||||
},
|
||||
{
|
||||
"dataType": "bool",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colBool"
|
||||
},
|
||||
{
|
||||
"dataType": "bool",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": true,
|
||||
"name": "colBoolNullable"
|
||||
},
|
||||
{
|
||||
"dataType": "bool",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colBoolDefault"
|
||||
},
|
||||
{
|
||||
"dataType": "int2",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colInt2"
|
||||
},
|
||||
{
|
||||
"dataType": "int4",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colInt4"
|
||||
},
|
||||
{
|
||||
"dataType": "int8",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colInt8"
|
||||
},
|
||||
{
|
||||
"dataType": "float4",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colFloat4"
|
||||
},
|
||||
{
|
||||
"dataType": "float8",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colFloat8"
|
||||
},
|
||||
{
|
||||
"dataType": "numeric",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colNumeric"
|
||||
},
|
||||
{
|
||||
"dataType": "money",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colMoney"
|
||||
},
|
||||
{
|
||||
"dataType": "text",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colText"
|
||||
},
|
||||
{
|
||||
"dataType": "varchar",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colVarchar"
|
||||
},
|
||||
{
|
||||
"dataType": "bpchar",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colBpchar"
|
||||
},
|
||||
{
|
||||
"dataType": "bytea",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colBytea"
|
||||
},
|
||||
{
|
||||
"dataType": "date",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colDate"
|
||||
},
|
||||
{
|
||||
"dataType": "time",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colTime"
|
||||
},
|
||||
{
|
||||
"dataType": "timetz",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colTimetz"
|
||||
},
|
||||
{
|
||||
"dataType": "timestamp",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colTimestamp"
|
||||
},
|
||||
{
|
||||
"dataType": "timestamptz",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colTimestamptz"
|
||||
},
|
||||
{
|
||||
"dataType": "json",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colJson"
|
||||
},
|
||||
{
|
||||
"dataType": "jsonb",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colJsonb"
|
||||
},
|
||||
{
|
||||
"dataType": "uuid",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colUuid"
|
||||
},
|
||||
{
|
||||
"dataType": "inet",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colInet"
|
||||
},
|
||||
{
|
||||
"dataType": "cidr",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colCidr"
|
||||
},
|
||||
{
|
||||
"dataType": "macaddr",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colMacaddr"
|
||||
},
|
||||
{
|
||||
"dataType": "bit",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colBit"
|
||||
},
|
||||
{
|
||||
"dataType": "varbit",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colVarbit"
|
||||
},
|
||||
{
|
||||
"dataType": "xml",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colXml"
|
||||
},
|
||||
{
|
||||
"dataType": "tsvector",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colTsvector"
|
||||
},
|
||||
{
|
||||
"dataType": "tsquery",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colTsquery"
|
||||
},
|
||||
{
|
||||
"dataType": "point",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colPoint"
|
||||
},
|
||||
{
|
||||
"dataType": "line",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colLine"
|
||||
},
|
||||
{
|
||||
"dataType": "lseg",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colLseg"
|
||||
},
|
||||
{
|
||||
"dataType": "box",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colBox"
|
||||
},
|
||||
{
|
||||
"dataType": "path",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colPath"
|
||||
},
|
||||
{
|
||||
"dataType": "polygon",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colPolygon"
|
||||
},
|
||||
{
|
||||
"dataType": "oid",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colOid"
|
||||
},
|
||||
{
|
||||
"dataType": "text",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": true,
|
||||
"name": "colTextNullable"
|
||||
},
|
||||
{
|
||||
"dataType": "jsonb",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colJsonbDefault"
|
||||
},
|
||||
{
|
||||
"dataType": "timestamp",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "colTimestampDefault"
|
||||
},
|
||||
{
|
||||
"dataType": "timestamptz",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "createdAt"
|
||||
},
|
||||
{
|
||||
"dataType": "timestamptz",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": true,
|
||||
"name": "updatedAt"
|
||||
}
|
||||
],
|
||||
"isForeign": false,
|
||||
"isView": false,
|
||||
"name": "AllTypes",
|
||||
"schema": "public"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"dataType": "int8",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": true,
|
||||
"isNullable": false,
|
||||
"name": "id"
|
||||
},
|
||||
{
|
||||
"dataType": "uuid",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "userId"
|
||||
},
|
||||
{
|
||||
"dataType": "OrderStatus",
|
||||
"dataTypeSchema": "public",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "status"
|
||||
},
|
||||
{
|
||||
"dataType": "Currency",
|
||||
"dataTypeSchema": "public",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "currency"
|
||||
},
|
||||
{
|
||||
"dataType": "int4",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "amountCents"
|
||||
},
|
||||
{
|
||||
"dataType": "text",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": true,
|
||||
"name": "note"
|
||||
},
|
||||
{
|
||||
"dataType": "timestamptz",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "createdAt"
|
||||
}
|
||||
],
|
||||
"isForeign": false,
|
||||
"isView": false,
|
||||
"name": "Orders",
|
||||
"schema": "public"
|
||||
},
|
||||
{
|
||||
"columns": [
|
||||
{
|
||||
"dataType": "uuid",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "id"
|
||||
},
|
||||
{
|
||||
"dataType": "varchar",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "username"
|
||||
},
|
||||
{
|
||||
"dataType": "text",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": false,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": true,
|
||||
"name": "email"
|
||||
},
|
||||
{
|
||||
"dataType": "UserRole",
|
||||
"dataTypeSchema": "public",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "role"
|
||||
},
|
||||
{
|
||||
"dataType": "bool",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "isActive"
|
||||
},
|
||||
{
|
||||
"dataType": "timestamptz",
|
||||
"dataTypeSchema": "pg_catalog",
|
||||
"hasDefaultValue": true,
|
||||
"isAutoIncrementing": false,
|
||||
"isNullable": false,
|
||||
"name": "createdAt"
|
||||
}
|
||||
],
|
||||
"isForeign": false,
|
||||
"isView": false,
|
||||
"name": "Users",
|
||||
"schema": "public"
|
||||
}
|
||||
]
|
||||
}
|
||||
`;
|
||||
+234
-9
@@ -1,17 +1,242 @@
|
||||
import assert from "node:assert/strict"
|
||||
import { describe, it } from "node:test"
|
||||
import { fn } from "../index.ts"
|
||||
import type { StartedPostgreSqlContainer } from "@testcontainers/postgresql"
|
||||
import { PostgreSqlContainer } from "@testcontainers/postgresql"
|
||||
import { Kysely, sql } from "kysely"
|
||||
import { PostgresJSDialect } from "kysely-postgres-js"
|
||||
import path from "node:path"
|
||||
import { after, before, describe, it, snapshot } from "node:test"
|
||||
import postgres from "postgres"
|
||||
import { KyselyTypegenPostgresDialect } from "../index.ts"
|
||||
|
||||
describe("index", () => {
|
||||
it('should return "Hello, tsdown!"', () => {
|
||||
snapshot.setResolveSnapshotPath((testFilePath) => {
|
||||
if (testFilePath == null) {
|
||||
throw new Error('"testFilePath" is null.')
|
||||
}
|
||||
const dir = path.dirname(testFilePath)
|
||||
const base = path.basename(testFilePath)
|
||||
return path.join(dir, "__snapshots__", `${base}.snapshot`)
|
||||
})
|
||||
|
||||
const POSTGRES_IMAGE =
|
||||
"docker.io/postgres:18.4@sha256:f7ce845ee6873dd84be93c9828fe0d1fab0f9707dc9ac569694657398b290bce"
|
||||
|
||||
const createSchema = async (database: Kysely<any>): Promise<void> => {
|
||||
await database.schema.createType("Currency").asEnum(["EUR", "USD", "GBP"]).execute()
|
||||
await database.schema.createType("UserRole").asEnum(["admin", "member", "guest"]).execute()
|
||||
await database.schema
|
||||
.createType("OrderStatus")
|
||||
.asEnum(["pending", "paid", "shipped", "cancelled"])
|
||||
.execute()
|
||||
|
||||
await database.schema
|
||||
.createTable("AllTypes")
|
||||
.addColumn("id", "uuid", (column) => {
|
||||
return column
|
||||
.notNull()
|
||||
.primaryKey()
|
||||
.defaultTo(sql`gen_random_uuid()`)
|
||||
})
|
||||
.addColumn("colBool", "boolean", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colBoolNullable", "boolean")
|
||||
.addColumn("colBoolDefault", "boolean", (column) => {
|
||||
return column.notNull().defaultTo(false)
|
||||
})
|
||||
.addColumn("colInt2", "smallint", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colInt4", "integer", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colInt8", "bigint", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colFloat4", "real", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colFloat8", "double precision", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colNumeric", sql`numeric(12, 2)`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colMoney", sql`money`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colText", "text", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colVarchar", "varchar(255)", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colBpchar", sql`char(10)`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colBytea", "bytea", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colDate", "date", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTime", "time", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTimetz", sql`timetz`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTimestamp", "timestamp", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTimestamptz", "timestamptz", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colJson", "json", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colJsonb", "jsonb", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colUuid", "uuid", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colInet", sql`inet`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colCidr", sql`cidr`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colMacaddr", sql`macaddr`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colBit", sql`bit(8)`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colVarbit", sql`varbit(16)`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colXml", sql`xml`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTsvector", sql`tsvector`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTsquery", sql`tsquery`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colPoint", sql`point`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colLine", sql`line`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colLseg", sql`lseg`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colBox", sql`box`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colPath", sql`path`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colPolygon", sql`polygon`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colOid", sql`oid`, (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("colTextNullable", "text")
|
||||
.addColumn("colJsonbDefault", "jsonb", (column) => {
|
||||
return column.notNull().defaultTo(sql`'{}'::jsonb`)
|
||||
})
|
||||
.addColumn("colTimestampDefault", "timestamp", (column) => {
|
||||
return column.notNull().defaultTo(sql`now()`)
|
||||
})
|
||||
.addColumn("createdAt", "timestamptz", (column) => {
|
||||
return column.notNull().defaultTo(sql`now()`)
|
||||
})
|
||||
.addColumn("updatedAt", "timestamptz")
|
||||
.execute()
|
||||
|
||||
await database.schema
|
||||
.createTable("Users")
|
||||
.addColumn("id", "uuid", (column) => {
|
||||
return column
|
||||
.notNull()
|
||||
.primaryKey()
|
||||
.defaultTo(sql`gen_random_uuid()`)
|
||||
})
|
||||
.addColumn("username", "varchar(50)", (column) => {
|
||||
return column.notNull().unique()
|
||||
})
|
||||
.addColumn("email", "text")
|
||||
.addColumn("role", sql`"UserRole"`, (column) => {
|
||||
return column.notNull().defaultTo("member")
|
||||
})
|
||||
.addColumn("isActive", "boolean", (column) => {
|
||||
return column.notNull().defaultTo(true)
|
||||
})
|
||||
.addColumn("createdAt", "timestamptz", (column) => {
|
||||
return column.notNull().defaultTo(sql`now()`)
|
||||
})
|
||||
.execute()
|
||||
|
||||
await database.schema
|
||||
.createTable("Orders")
|
||||
.addColumn("id", "bigserial", (column) => {
|
||||
return column.notNull().primaryKey()
|
||||
})
|
||||
.addColumn("userId", "uuid", (column) => {
|
||||
return column.notNull().references("Users.id")
|
||||
})
|
||||
.addColumn("status", sql`"OrderStatus"`, (column) => {
|
||||
return column.notNull().defaultTo("pending")
|
||||
})
|
||||
.addColumn("currency", sql`"Currency"`, (column) => {
|
||||
return column.notNull().defaultTo("EUR")
|
||||
})
|
||||
.addColumn("amountCents", "integer", (column) => {
|
||||
return column.notNull()
|
||||
})
|
||||
.addColumn("note", "text")
|
||||
.addColumn("createdAt", "timestamptz", (column) => {
|
||||
return column.notNull().defaultTo(sql`now()`)
|
||||
})
|
||||
.execute()
|
||||
}
|
||||
|
||||
describe("typegen", () => {
|
||||
let container: StartedPostgreSqlContainer
|
||||
let database: Kysely<any>
|
||||
|
||||
before(async () => {
|
||||
container = await new PostgreSqlContainer(POSTGRES_IMAGE).start()
|
||||
database = new Kysely<any>({
|
||||
dialect: new PostgresJSDialect({
|
||||
postgres: postgres({
|
||||
database: container.getDatabase(),
|
||||
host: container.getHost(),
|
||||
port: container.getPort(),
|
||||
user: container.getUsername(),
|
||||
password: container.getPassword(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
await createSchema(database)
|
||||
})
|
||||
|
||||
after(async () => {
|
||||
await database.destroy()
|
||||
await container.stop()
|
||||
})
|
||||
|
||||
it("generate types matching snapshot", async (testContext) => {
|
||||
// Arrange - Given
|
||||
const input = "tsdown"
|
||||
const databaseTypegen = new KyselyTypegenPostgresDialect({ database })
|
||||
|
||||
// Act - When
|
||||
const output = fn(input)
|
||||
const result = await databaseTypegen.typegen()
|
||||
|
||||
// Assert - Then
|
||||
const expected = "Hello, tsdown!"
|
||||
assert.strictEqual(output, expected)
|
||||
testContext.assert.snapshot(result)
|
||||
})
|
||||
})
|
||||
|
||||
+190
-2
@@ -1,3 +1,191 @@
|
||||
export const fn = (input: string): string => {
|
||||
return `Hello, ${input}!`
|
||||
import type { Kysely, TableMetadata as KyselyTableMetadata } from "kysely"
|
||||
|
||||
export type TableMetadata = KyselyTableMetadata
|
||||
export interface EnumMetadata {
|
||||
name: string
|
||||
values: string[]
|
||||
}
|
||||
|
||||
export abstract class KyselyTypegenDialect {
|
||||
public abstract readonly database: Kysely<any>
|
||||
public abstract readonly scalars: Record<string, string>
|
||||
|
||||
public async getTables(): Promise<TableMetadata[]> {
|
||||
const tables = await this.database.introspection.getTables()
|
||||
return tables.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name)
|
||||
})
|
||||
}
|
||||
public getTablesTypegen(tables: TableMetadata[], enums: EnumMetadata[]): string[] {
|
||||
const enumNames = new Set(
|
||||
enums.map((enumMetadata) => {
|
||||
return enumMetadata.name
|
||||
}),
|
||||
)
|
||||
const result: string[] = []
|
||||
for (const table of tables) {
|
||||
const interfaceName = table.name
|
||||
result.push(`export interface ${interfaceName} {`)
|
||||
const columns = [...table.columns].sort((a, b) => {
|
||||
return a.name.localeCompare(b.name)
|
||||
})
|
||||
for (const column of columns) {
|
||||
const isEnum = enumNames.has(column.dataType)
|
||||
const baseType = isEnum ? column.dataType : (this.scalars[column.dataType] ?? "unknown")
|
||||
let columnType = column.isNullable ? `${baseType} | null` : baseType
|
||||
if (column.hasDefaultValue || column.isAutoIncrementing) {
|
||||
columnType = `Generated<${columnType}>`
|
||||
}
|
||||
result.push(` ${column.name}: ${columnType}`)
|
||||
}
|
||||
result.push("}", "")
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
protected abstract getEnumsMap(): Promise<Map<string, string[]>>
|
||||
public async getEnums(): Promise<EnumMetadata[]> {
|
||||
const enumsMap = await this.getEnumsMap()
|
||||
const enums: EnumMetadata[] = []
|
||||
for (const [name, values] of enumsMap) {
|
||||
enums.push({ name, values })
|
||||
}
|
||||
return enums.sort((a, b) => {
|
||||
return a.name.localeCompare(b.name)
|
||||
})
|
||||
}
|
||||
public getEnumsTypegen(enums: EnumMetadata[]): string[] {
|
||||
const result: string[] = []
|
||||
for (const enumMetadata of enums) {
|
||||
const enumName = enumMetadata.name
|
||||
result.push(
|
||||
`export type ${enumName} = ${enumMetadata.values
|
||||
.map((value) => {
|
||||
return `"${value}"`
|
||||
})
|
||||
.join(" | ")}`,
|
||||
"",
|
||||
)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
public abstract typegen(): Promise<{
|
||||
lines: string[]
|
||||
enums: EnumMetadata[]
|
||||
tables: TableMetadata[]
|
||||
}>
|
||||
}
|
||||
export class KyselyTypegenPostgresDialect extends KyselyTypegenDialect {
|
||||
public database: KyselyTypegenDialect["database"]
|
||||
|
||||
// These types have been found through experimentation in Adminer and in the 'pg' source code.
|
||||
public readonly scalars: Record<string, string> = {
|
||||
bit: "string",
|
||||
bool: "boolean",
|
||||
box: "string",
|
||||
bpchar: "string",
|
||||
bytea: "Buffer",
|
||||
cidr: "string",
|
||||
date: "Timestamp",
|
||||
float4: "number",
|
||||
float8: "number",
|
||||
inet: "string",
|
||||
int2: "number",
|
||||
int4: "number",
|
||||
int8: "Int8",
|
||||
json: "Json",
|
||||
jsonb: "Json",
|
||||
line: "string",
|
||||
lseg: "string",
|
||||
macaddr: "string",
|
||||
money: "string",
|
||||
numeric: "Numeric",
|
||||
oid: "number",
|
||||
path: "string",
|
||||
polygon: "string",
|
||||
text: "string",
|
||||
time: "string",
|
||||
timestamp: "Timestamp",
|
||||
timestamptz: "Timestamp",
|
||||
timetz: "string",
|
||||
tsquery: "string",
|
||||
tsvector: "string",
|
||||
uuid: "string",
|
||||
varbit: "string",
|
||||
varchar: "string",
|
||||
xml: "string",
|
||||
}
|
||||
|
||||
public constructor(input: { database: KyselyTypegenDialect["database"] }) {
|
||||
super()
|
||||
this.database = input.database
|
||||
}
|
||||
|
||||
protected async getEnumsMap(): Promise<Map<string, string[]>> {
|
||||
const rows = await this.database
|
||||
.withoutPlugins()
|
||||
.selectFrom("pg_type as type")
|
||||
.innerJoin("pg_enum as enum", "type.oid", "enum.enumtypid")
|
||||
.innerJoin("pg_catalog.pg_namespace as namespace", "namespace.oid", "type.typnamespace")
|
||||
.select(["type.typname as enumName", "enum.enumlabel as enumValue"])
|
||||
.execute()
|
||||
const enums = new Map<string, string[]>()
|
||||
for (const row of rows) {
|
||||
const data = row as { enumName: string; enumValue: string }
|
||||
enums.set(
|
||||
data.enumName,
|
||||
[...(enums.get(data.enumName) ?? []), data.enumValue].sort((a, b) => {
|
||||
return a.localeCompare(b)
|
||||
}),
|
||||
)
|
||||
}
|
||||
return enums
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate TypeScript types based on the database schema, including tables and enums.
|
||||
*/
|
||||
public async typegen(): Promise<{
|
||||
lines: string[]
|
||||
enums: EnumMetadata[]
|
||||
tables: TableMetadata[]
|
||||
}> {
|
||||
const [tables, enums] = await Promise.all([this.getTables(), this.getEnums()])
|
||||
const lines: string[] = [
|
||||
`// This file was automatically generated by \`kysely-typegen\`.`,
|
||||
"// Do not edit this file manually.",
|
||||
"",
|
||||
'import type { ColumnType } from "kysely"',
|
||||
"",
|
||||
"export type Generated<T> = T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>",
|
||||
"",
|
||||
"export type Timestamp = ColumnType<Date, Date | string, Date | string>",
|
||||
"",
|
||||
"export type Numeric = ColumnType<string, number | string, number | string>",
|
||||
"",
|
||||
"export type Int8 = ColumnType<string, bigint | number | string, bigint | number | string>",
|
||||
"",
|
||||
"export type Json = JsonValue",
|
||||
"",
|
||||
"export type JsonArray = JsonValue[]",
|
||||
"",
|
||||
"export interface JsonObject {",
|
||||
" [x: string]: JsonValue | undefined",
|
||||
"}",
|
||||
"",
|
||||
"export type JsonPrimitive = boolean | number | string | null",
|
||||
"",
|
||||
"export type JsonValue = JsonArray | JsonObject | JsonPrimitive",
|
||||
"",
|
||||
]
|
||||
lines.push(...this.getEnumsTypegen(enums))
|
||||
lines.push(...this.getTablesTypegen(tables, enums))
|
||||
lines.push("export interface DB {")
|
||||
for (const table of tables) {
|
||||
lines.push(` ${table.name}: ${table.name}`)
|
||||
}
|
||||
lines.push("}")
|
||||
return { lines, enums, tables }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user