diff --git a/README.md b/README.md index 04470b6..4f56f9f 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Why `kysely-typegen` if there is already `kysely-codegen`? Comparison: | **Dependencies** | 35 total | 0 (no runtime dependencies) | | **Type** | CLI | Library/Programmatic Usage | | **Code Size/Maintainability** | Heavy | Lightweight/Simple and straightforward (string manipulation instead of complex AST) | -| **Database Support** | PostgreSQL, MySQL, SQLite, MSSQL, LibSQL | PostgreSQL, MySQL, SQLite (**can be easily extended to more**) | +| **Database Support** | PostgreSQL, MySQL, SQLite, MSSQL, LibSQL | PostgreSQL, PGlite, MySQL, SQLite (**can be easily extended to more**) | `kysely-typegen` is a **library** (not a CLI), which means you are in control of where and how to run it, and is designed to be **extensible**, easy to add support for more database dialects. @@ -79,6 +79,30 @@ export const database = new Kysely({ dialect }) export const databaseTypegen = new KyselyTypegenPostgresDialect({ database }) ``` +#### PGlite + +[PGlite](https://pglite.dev/) is PostgreSQL compiled to WebAssembly that runs in-process. Use Kysely's built-in [`PGliteDialect`](https://kysely-org.github.io/kysely-apidoc/classes/PGliteDialect.html) with `KyselyTypegenPostgresDialect`: PGlite is introspected exactly like PostgreSQL. Because `kysely-typegen` is a library, it can introspect the PGlite instance your application already uses, which a CLI cannot do. + +```sh +npm install @electric-sql/pglite +``` + +```ts +// database.ts +import { PGlite } from "@electric-sql/pglite" +import { Kysely, PGliteDialect } from "kysely" +import { KyselyTypegenPostgresDialect } from "kysely-typegen/postgres" + +import type { DB } from "./codegen.ts" + +const dialect = new PGliteDialect({ + pglite: new PGlite(process.env["DATABASE_PATH"] ?? "./pgdata"), +}) + +export const database = new Kysely({ dialect }) +export const databaseTypegen = new KyselyTypegenPostgresDialect({ database }) +``` + #### MySQL ```sh diff --git a/package-lock.json b/package-lock.json index 04cb300..44e0c7f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.0-development", "license": "MIT", "devDependencies": { + "@electric-sql/pglite": "0.5.8", "@testcontainers/mysql": "12.2.0", "@testcontainers/postgresql": "12.2.0", "@types/better-sqlite3": "9.6.0", @@ -126,6 +127,13 @@ "node": ">=0.1.90" } }, + "node_modules/@electric-sql/pglite": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.5.8.tgz", + "integrity": "sha512-n9tsbUOhwx2epK1V0ZG9Ar4SHWUju04dhmzZXiSBXwBoleOvIfals33NAaWgagQVAL4Rbvx/Ptsu3P+pA09f6Q==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@grpc/grpc-js": { "version": "1.14.5", "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.5.tgz", diff --git a/package.json b/package.json index e20ca68..05c441a 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "release": "semantic-release" }, "devDependencies": { + "@electric-sql/pglite": "0.5.8", "@testcontainers/mysql": "12.2.0", "@testcontainers/postgresql": "12.2.0", "@types/better-sqlite3": "9.6.0", diff --git a/src/_test/__snapshots__/postgres.test.ts.snapshot b/src/_test/__snapshots__/postgres.test.ts.snapshot index 0a6581d..1000c4a 100644 --- a/src/_test/__snapshots__/postgres.test.ts.snapshot +++ b/src/_test/__snapshots__/postgres.test.ts.snapshot @@ -1,3 +1,121 @@ +exports[`typegen PGlite > 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 extends ColumnType ? ColumnType : ColumnType", + "", + "export type Timestamp = ColumnType", + "", + "export type Numeric = ColumnType", + "", + "export type Int8 = ColumnType", + "", + "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", + " 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", + " 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", + " colTimestamptz: Timestamp", + " colTimetz: string", + " colTsquery: string", + " colTsvector: string", + " colUuid: string", + " colVarbit: string", + " colVarchar: string", + " colXml: string", + " createdAt: Generated", + " id: Generated", + " updatedAt: Timestamp | null", + "}", + "", + "export interface Orders {", + " /**", + " * Total amount in cents.", + " *", + " * Excludes taxes.", + " */", + " amountCents: number", + " createdAt: Generated", + " currency: Generated", + " id: Generated", + " /** Free text, may contain *\\\\/ characters. */", + " note: string | null", + " status: Generated", + " userId: string", + "}", + "", + "export interface Users {", + " createdAt: Generated", + " /** Contact email address. */", + " email: string | null", + " id: Generated", + " isActive: Generated", + " role: Generated", + " username: string", + "}", + "", + "export interface DB {", + " AllTypes: AllTypes", + " Orders: Orders", + " Users: Users", + "}" + ], + "tablesCount": 3, + "enumsCount": 3, + "inlineEnumsCount": 0 +} +`; + exports[`typegen PostgreSQL > generate types matching snapshot (kysely-postgres-js) 1`] = ` { "lines": [ diff --git a/src/_test/postgres.test.ts b/src/_test/postgres.test.ts index e32310a..b742f32 100644 --- a/src/_test/postgres.test.ts +++ b/src/_test/postgres.test.ts @@ -1,6 +1,7 @@ +import { PGlite } from "@electric-sql/pglite" import type { StartedPostgreSqlContainer } from "@testcontainers/postgresql" import { PostgreSqlContainer } from "@testcontainers/postgresql" -import { Kysely, PostgresDialect, sql } from "kysely" +import { Kysely, PGliteDialect, PostgresDialect, sql } from "kysely" import { PostgresJSDialect } from "kysely-postgres-js" import { after, before, describe, it } from "node:test" import pg from "pg" @@ -276,3 +277,36 @@ describe("typegen PostgreSQL", () => { }) }) }) + +describe("typegen PGlite", () => { + let database: Kysely + + before(async () => { + database = new Kysely({ + dialect: new PGliteDialect({ + pglite: new PGlite(), + }), + }) + await createSchema(database) + }) + + after(async () => { + await database.destroy() + }) + + it("generate types matching snapshot", async (testContext) => { + // Arrange - Given + const databaseTypegen = new KyselyTypegenPostgresDialect({ database }) + + // Act - When + const result = await databaseTypegen.typegen() + + // Assert - Then + testContext.assert.snapshot({ + lines: result.lines, + tablesCount: result.tables.length, + enumsCount: result.enums.length, + inlineEnumsCount: result.inlineEnums.size, + }) + }) +})