mirror of
https://github.com/theoludwig/kysely-typegen.git
synced 2026-10-16 19:16:47 +02:00
Compare commits
2
Commits
f956a31c92
...
ff16be6787
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff16be6787
|
||
|
|
3849b14615
|
No files matched your search
@@ -15,10 +15,10 @@ jobs:
|
||||
TZ: "Europe/Paris"
|
||||
DO_NOT_TRACK: "1"
|
||||
steps:
|
||||
- uses: "actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10" # v6.0.3
|
||||
- uses: "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1" # v7.0.1
|
||||
|
||||
- name: "Setup Node.js"
|
||||
uses: "actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e" # v6.4.0
|
||||
uses: "actions/setup-node@820762786026740c76f36085b0efc47a31fe5020" # v7.0.0
|
||||
with:
|
||||
node-version: "24.16.0"
|
||||
cache: "npm"
|
||||
|
||||
@@ -17,13 +17,13 @@ jobs:
|
||||
pull-requests: "write"
|
||||
id-token: "write"
|
||||
steps:
|
||||
- uses: "actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10" # v6.0.3
|
||||
- uses: "actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1" # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Setup Node.js"
|
||||
uses: "actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e" # v6.4.0
|
||||
uses: "actions/setup-node@820762786026740c76f36085b0efc47a31fe5020" # v7.0.0
|
||||
with:
|
||||
node-version: "24.16.0"
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
@@ -168,6 +168,19 @@ const rows = await database.selectFrom("User").selectAll().execute()
|
||||
|
||||
Fully type-safe queries derived from your actual database schema.
|
||||
|
||||
### Column comments
|
||||
|
||||
Column comments (`COMMENT ON COLUMN` in PostgreSQL, `COMMENT '...'` in MySQL) are emitted as JSDoc above the column, so they show up in your editor on hover:
|
||||
|
||||
```ts
|
||||
export interface Users {
|
||||
/** Contact email address. */
|
||||
email: string | null
|
||||
}
|
||||
```
|
||||
|
||||
SQLite has no column comments, so none are generated.
|
||||
|
||||
## Extending to other database dialects
|
||||
|
||||
`kysely-typegen` ships with `KyselyTypegenPostgresDialect`, `KyselyTypegenMySQLDialect`, and `KyselyTypegenSQLiteDialect`, but you can add support for any database by extending the abstract `KyselyTypegenDialect` class.
|
||||
|
||||
Generated
+1446
-1196
File diff suppressed because it is too large.
Load diff
+16
-16
@@ -41,23 +41,23 @@
|
||||
"release": "semantic-release"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testcontainers/mysql": "12.0.1",
|
||||
"@testcontainers/postgresql": "12.0.1",
|
||||
"@types/better-sqlite3": "7.6.13",
|
||||
"@types/node": "25.9.2",
|
||||
"@types/pg": "8.20.0",
|
||||
"better-sqlite3": "12.10.0",
|
||||
"kysely": "0.29.2",
|
||||
"kysely-postgres-js": "3.0.0",
|
||||
"mysql2": "3.22.5",
|
||||
"oxfmt": "0.54.0",
|
||||
"oxlint": "1.69.0",
|
||||
"oxlint-tsgolint": "0.23.0",
|
||||
"pg": "8.21.0",
|
||||
"@testcontainers/mysql": "12.2.0",
|
||||
"@testcontainers/postgresql": "12.2.0",
|
||||
"@types/better-sqlite3": "9.6.0",
|
||||
"@types/node": "26.6.4",
|
||||
"@types/pg": "8.23.1",
|
||||
"better-sqlite3": "13.0.3",
|
||||
"kysely": "0.29.6",
|
||||
"kysely-postgres-js": "5.0.1",
|
||||
"mysql2": "3.24.5",
|
||||
"oxfmt": "0.71.0",
|
||||
"oxlint": "1.86.0",
|
||||
"oxlint-tsgolint": "7.0.2003",
|
||||
"pg": "8.23.1",
|
||||
"postgres": "3.4.9",
|
||||
"semantic-release": "25.0.3",
|
||||
"tsdown": "0.22.2",
|
||||
"typescript": "6.0.3"
|
||||
"semantic-release": "25.0.9",
|
||||
"tsdown": "0.23.0",
|
||||
"typescript": "7.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"kysely": ">=0.29.0"
|
||||
|
||||
@@ -62,10 +62,16 @@ exports[`typegen MySQL > generate types matching snapshot 1`] = `
|
||||
"}",
|
||||
"",
|
||||
"export interface Orders {",
|
||||
" /**",
|
||||
" * Total amount in cents.",
|
||||
" *",
|
||||
" * Excludes taxes.",
|
||||
" */",
|
||||
" amountCents: number",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" currency: Generated<\\"EUR\\" | \\"GBP\\" | \\"USD\\">",
|
||||
" id: Generated<Int8>",
|
||||
" /** Free text, may contain *\\\\/ characters. */",
|
||||
" note: string | null",
|
||||
" status: Generated<\\"cancelled\\" | \\"paid\\" | \\"pending\\" | \\"shipped\\">",
|
||||
" userId: number",
|
||||
@@ -73,6 +79,7 @@ exports[`typegen MySQL > generate types matching snapshot 1`] = `
|
||||
"",
|
||||
"export interface Users {",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" /** Contact email address. */",
|
||||
" email: string | null",
|
||||
" id: Generated<number>",
|
||||
" isActive: Generated<number>",
|
||||
|
||||
@@ -79,10 +79,16 @@ exports[`typegen PostgreSQL > generate types matching snapshot (kysely-postgres-
|
||||
"}",
|
||||
"",
|
||||
"export interface Orders {",
|
||||
" /**",
|
||||
" * Total amount in cents.",
|
||||
" *",
|
||||
" * Excludes taxes.",
|
||||
" */",
|
||||
" amountCents: number",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" currency: Generated<Currency>",
|
||||
" id: Generated<Int8>",
|
||||
" /** Free text, may contain *\\\\/ characters. */",
|
||||
" note: string | null",
|
||||
" status: Generated<OrderStatus>",
|
||||
" userId: string",
|
||||
@@ -90,6 +96,7 @@ exports[`typegen PostgreSQL > generate types matching snapshot (kysely-postgres-
|
||||
"",
|
||||
"export interface Users {",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" /** Contact email address. */",
|
||||
" email: string | null",
|
||||
" id: Generated<string>",
|
||||
" isActive: Generated<boolean>",
|
||||
@@ -190,10 +197,16 @@ exports[`typegen PostgreSQL > generate types matching snapshot (pg) 1`] = `
|
||||
"}",
|
||||
"",
|
||||
"export interface Orders {",
|
||||
" /**",
|
||||
" * Total amount in cents.",
|
||||
" *",
|
||||
" * Excludes taxes.",
|
||||
" */",
|
||||
" amountCents: number",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" currency: Generated<Currency>",
|
||||
" id: Generated<Int8>",
|
||||
" /** Free text, may contain *\\\\/ characters. */",
|
||||
" note: string | null",
|
||||
" status: Generated<OrderStatus>",
|
||||
" userId: string",
|
||||
@@ -201,6 +214,7 @@ exports[`typegen PostgreSQL > generate types matching snapshot (pg) 1`] = `
|
||||
"",
|
||||
"export interface Users {",
|
||||
" createdAt: Generated<Timestamp>",
|
||||
" /** Contact email address. */",
|
||||
" email: string | null",
|
||||
" id: Generated<string>",
|
||||
" isActive: Generated<boolean>",
|
||||
|
||||
@@ -117,7 +117,9 @@ const createSchema = async (database: Kysely<any>): Promise<void> => {
|
||||
.addColumn("username", "varchar(50)", (column) => {
|
||||
return column.notNull().unique()
|
||||
})
|
||||
.addColumn("email", "text")
|
||||
.addColumn("email", "text", (column) => {
|
||||
return column.modifyEnd(sql`comment ${sql.lit("Contact email address.")}`)
|
||||
})
|
||||
.addColumn("role", sql`enum('admin','member','guest')`, (column) => {
|
||||
return column.notNull().defaultTo("member")
|
||||
})
|
||||
@@ -144,9 +146,13 @@ const createSchema = async (database: Kysely<any>): Promise<void> => {
|
||||
return column.notNull().defaultTo("EUR")
|
||||
})
|
||||
.addColumn("amountCents", "integer", (column) => {
|
||||
return column.notNull()
|
||||
return column
|
||||
.notNull()
|
||||
.modifyEnd(sql`comment ${sql.lit("Total amount in cents.\n\nExcludes taxes.")}`)
|
||||
})
|
||||
.addColumn("note", "text", (column) => {
|
||||
return column.modifyEnd(sql`comment ${sql.lit("Free text, may contain */ characters.")}`)
|
||||
})
|
||||
.addColumn("note", "text")
|
||||
.addColumn("createdAt", "timestamp", (column) => {
|
||||
return column.notNull().defaultTo(sql`current_timestamp`)
|
||||
})
|
||||
|
||||
@@ -195,6 +195,16 @@ const createSchema = async (database: Kysely<any>): Promise<void> => {
|
||||
return column.notNull().defaultTo(sql`now()`)
|
||||
})
|
||||
.execute()
|
||||
|
||||
await sql`comment on column ${sql.ref("Users.email")} is ${sql.lit("Contact email address.")}`.execute(
|
||||
database,
|
||||
)
|
||||
await sql`comment on column ${sql.ref("Orders.amountCents")} is ${sql.lit("Total amount in cents.\n\nExcludes taxes.")}`.execute(
|
||||
database,
|
||||
)
|
||||
await sql`comment on column ${sql.ref("Orders.note")} is ${sql.lit("Free text, may contain */ characters.")}`.execute(
|
||||
database,
|
||||
)
|
||||
}
|
||||
|
||||
describe("typegen PostgreSQL", () => {
|
||||
|
||||
+31
-8
@@ -73,6 +73,24 @@ export abstract class KyselyTypegenDialect {
|
||||
return scalars[dataType] ?? "unknown"
|
||||
}
|
||||
|
||||
protected formatColumnComment(comment: string): string[] {
|
||||
const trimmedComment = comment.trim()
|
||||
if (trimmedComment.length === 0) {
|
||||
return []
|
||||
}
|
||||
const lines = trimmedComment.replaceAll("*/", "*\\/").split(/\r?\n/u)
|
||||
if (lines.length === 1) {
|
||||
return [` /** ${lines[0]} */`]
|
||||
}
|
||||
return [
|
||||
" /**",
|
||||
...lines.map((line) => {
|
||||
return line.length === 0 ? " *" : ` * ${line}`
|
||||
}),
|
||||
" */",
|
||||
]
|
||||
}
|
||||
|
||||
public getTablesTypegen(
|
||||
tables: TableMetadata[],
|
||||
enums: EnumMetadata[],
|
||||
@@ -94,6 +112,9 @@ export abstract class KyselyTypegenDialect {
|
||||
if (column.hasDefaultValue || column.isAutoIncrementing) {
|
||||
columnType = `Generated<${columnType}>`
|
||||
}
|
||||
if (column.comment != null) {
|
||||
result.push(...this.formatColumnComment(column.comment))
|
||||
}
|
||||
result.push(` ${column.name}: ${columnType}`)
|
||||
}
|
||||
result.push("}", "")
|
||||
@@ -146,14 +167,16 @@ export abstract class KyselyTypegenDialect {
|
||||
tables: TableMetadata[]
|
||||
}> {
|
||||
const [tables, { enums, inlineEnums }] = await Promise.all([this.getTables(), this.getEnums()])
|
||||
const lines: string[] = [...PREAMBLE_LINES]
|
||||
lines.push(...this.getEnumsTypegen(enums))
|
||||
lines.push(...this.getTablesTypegen(tables, enums, inlineEnums))
|
||||
lines.push("export interface DB {")
|
||||
for (const table of tables) {
|
||||
lines.push(` ${table.name}: ${table.name}`)
|
||||
}
|
||||
lines.push("}")
|
||||
const lines: string[] = [
|
||||
...PREAMBLE_LINES,
|
||||
...this.getEnumsTypegen(enums),
|
||||
...this.getTablesTypegen(tables, enums, inlineEnums),
|
||||
"export interface DB {",
|
||||
...tables.map((table) => {
|
||||
return ` ${table.name}: ${table.name}`
|
||||
}),
|
||||
"}",
|
||||
]
|
||||
return { lines, enums, inlineEnums, tables }
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -52,12 +52,12 @@ export class KyselyTypegenMySQLDialect extends KyselyTypegenDialect {
|
||||
])
|
||||
.where("columns.TABLE_SCHEMA", "=", sql`database()`)
|
||||
.where("columns.DATA_TYPE", "in", ["enum", "set"])
|
||||
.$castTo<{ tableName: string; columnName: string; columnType: string }>()
|
||||
.execute()
|
||||
|
||||
const inline = new Map<string, string[]>()
|
||||
for (const row of rows) {
|
||||
const data = row as { tableName: string; columnName: string; columnType: string }
|
||||
inline.set(`${data.tableName}.${data.columnName}`, parseMysqlEnumColumnType(data.columnType))
|
||||
inline.set(`${row.tableName}.${row.columnName}`, parseMysqlEnumColumnType(row.columnType))
|
||||
}
|
||||
return { named: [], inline }
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user