From 5e555fbb0798c07cfad581a0554221cfc379ded9 Mon Sep 17 00:00:00 2001 From: Sandro Circi Date: Fri, 24 Jan 2025 20:18:36 +0100 Subject: [PATCH] build: generate drivers meta (#136) --- .github/workflows/autofix.yml | 1 + .prettierignore | 3 + package.json | 5 +- pnpm-lock.yaml | 27 ++++----- scripts/gen-connectors.ts | 111 ++++++++++++++++++++++++++++++++++ src/_connectors.ts | 55 +++++++++++++++++ src/connectors/mysql2.ts | 4 +- src/connectors/planetscale.ts | 4 +- src/index.ts | 26 -------- tsconfig.json | 5 +- 10 files changed, 196 insertions(+), 45 deletions(-) create mode 100644 scripts/gen-connectors.ts create mode 100644 src/_connectors.ts diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml index f87c66a..e4c6108 100644 --- a/.github/workflows/autofix.yml +++ b/.github/workflows/autofix.yml @@ -19,6 +19,7 @@ jobs: node-version: 20 cache: "pnpm" - run: pnpm install + - run: pnpm run gen-connectors - run: pnpm automd - run: pnpm lint:fix - uses: autofix-ci/action@551dded8c6cc8a1054039c8bc0b8b48c51dfc6ef diff --git a/.prettierignore b/.prettierignore index 3e0f93a..84e9d61 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,6 @@ connectors integrations +src/_connectors.ts dist +node_modules +.output diff --git a/package.json b/package.json index eb9a82d..66a41f7 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,8 @@ "integrations" ], "scripts": { - "build": "unbuild", + "build": "pnpm gen-connectors && unbuild", + "gen-connectors": "jiti scripts/gen-connectors.ts", "db0": "pnpm jiti src/cli", "dev": "vitest", "lint": "eslint . && prettier -c src test", @@ -66,9 +67,11 @@ "eslint": "^9.18.0", "eslint-config-unjs": "^0.4.2", "jiti": "^2.4.2", + "mlly": "^1.7.4", "mysql2": "^3.12.0", "pg": "^8.13.1", "prettier": "^3.4.2", + "scule": "^1.3.0", "typescript": "^5.7.3", "unbuild": "^3.3.1", "vitest": "^3.0.4" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5434071..70a1df8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,6 +56,9 @@ importers: jiti: specifier: ^2.4.2 version: 2.4.2 + mlly: + specifier: ^1.7.4 + version: 1.7.4 mysql2: specifier: ^3.12.0 version: 3.12.0 @@ -65,6 +68,9 @@ importers: prettier: specifier: ^3.4.2 version: 3.4.2 + scule: + specifier: ^1.3.0 + version: 1.3.0 typescript: specifier: ^5.7.3 version: 5.7.3 @@ -2438,6 +2444,7 @@ packages: libsql@0.4.7: resolution: {integrity: sha512-T9eIRCs6b0J1SHKYIvD8+KCJMcWZ900iZyxdnSCdqxN12Z1ijzT+jY5nrk72Jw4B0HGzms2NgpryArlJqvc3Lw==} + cpu: [x64, arm64, wasm32] os: [darwin, linux, win32] lilconfig@3.1.3: @@ -2611,9 +2618,6 @@ packages: vue-tsc: optional: true - mlly@1.7.3: - resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==} - mlly@1.7.4: resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} @@ -4559,7 +4563,7 @@ snapshots: globby: 14.0.2 magic-string: 0.30.17 mdbox: 0.1.1 - mlly: 1.7.3 + mlly: 1.7.4 ofetch: 1.4.1 pathe: 1.1.2 perfect-debounce: 1.0.0 @@ -4658,7 +4662,7 @@ snapshots: dotenv: 16.4.7 giget: 1.2.3 jiti: 1.21.7 - mlly: 1.7.3 + mlly: 1.7.4 ohash: 1.1.4 pathe: 1.1.2 perfect-debounce: 1.0.0 @@ -4675,7 +4679,7 @@ snapshots: dotenv: 16.4.7 giget: 1.2.3 jiti: 2.4.2 - mlly: 1.7.3 + mlly: 1.7.4 ohash: 1.1.4 pathe: 1.1.2 perfect-debounce: 1.0.0 @@ -5866,13 +5870,6 @@ snapshots: optionalDependencies: typescript: 5.7.3 - mlly@1.7.3: - dependencies: - acorn: 8.14.0 - pathe: 1.1.2 - pkg-types: 1.2.1 - ufo: 1.5.4 - mlly@1.7.4: dependencies: acorn: 8.14.0 @@ -5951,7 +5948,7 @@ snapshots: consola: 3.2.3 execa: 8.0.1 pathe: 1.1.2 - pkg-types: 1.2.1 + pkg-types: 1.3.1 ufo: 1.5.4 obuf@1.1.2: {} @@ -6107,7 +6104,7 @@ snapshots: pkg-types@1.2.1: dependencies: confbox: 0.1.8 - mlly: 1.7.3 + mlly: 1.7.4 pathe: 1.1.2 pkg-types@1.3.1: diff --git a/scripts/gen-connectors.ts b/scripts/gen-connectors.ts new file mode 100644 index 0000000..49ad418 --- /dev/null +++ b/scripts/gen-connectors.ts @@ -0,0 +1,111 @@ +import { readFile, readdir, writeFile } from "node:fs/promises"; +import { join } from "node:path"; +import { fileURLToPath } from "node:url"; +import { findTypeExports } from "mlly"; +import { camelCase, upperFirst } from "scule"; + +const connectorsDir = fileURLToPath( + new URL("../src/connectors", import.meta.url), +); + +const connectorsMetaFile = fileURLToPath( + new URL("../src/_connectors.ts", import.meta.url), +); + +const aliases = { + "better-sqlite3": ["sqlite"], + "bun-sqlite": ["bun"], + "libsql-node": ["libsql"], +} as const; + +async function getConnectorFiles(dir: string): Promise { + const files: string[] = []; + const entries = await readdir(dir, { withFileTypes: true }); + + for (const entry of entries) { + if (entry.isDirectory()) { + files.push(...(await getConnectorFiles(join(dir, entry.name)))); + } else if (entry.isFile()) { + files.push(join(dir, entry.name)); + } + } + + return files; +} + +const connectorFiles = await getConnectorFiles(connectorsDir); +const connectorEntries = connectorFiles.map((file) => + file.replace(connectorsDir + "/", ""), +); + +const connectors: { + name: string; + safeName: string; + names: string[]; + subpath: string; + optionsTExport?: string; + optionsTName?: string; +}[] = []; + +for (const entry of connectorEntries) { + const pathName = entry.replace(/\.ts$/, ""); + const name = pathName.replace(/\/|\\/g, "-"); + const subpath = `db0/connectors/${pathName}`; + const fullPath = join(connectorsDir, `${pathName}.ts`); + + const contents = await readFile(fullPath, "utf8"); + const optionsTExport = findTypeExports(contents).find((type) => + type.name?.endsWith("Options"), + )?.name; + + const safeName = camelCase(name).replace(/db/i, "DB").replace(/sql/i, "SQL"); + + const alternativeNames: string[] = aliases[name] || []; + + const names = [...new Set([name, ...alternativeNames])]; + + const optionsTName = upperFirst(safeName) + "Options"; + + connectors.push({ + name, + safeName, + names, + subpath, + optionsTExport, + optionsTName, + }); +} + +connectors.sort((a, b) => a.name.localeCompare(b.name)); + +const genCode = /* ts */ `// Auto-generated using scripts/gen-connectors. +// Do not manually edit! +${connectors + .filter((d) => d.optionsTExport) + .map( + (d) => + /* ts */ `import type { ${d.optionsTExport} as ${d.optionsTName} } from "${d.subpath}";`, + ) + .join("\n")} + +export type BuiltinConnectorName = ${connectors.flatMap((d) => d.names.map((name) => `"${name}"`)).join(" | ")}; + +export type BuiltinConnectorOptions = { + ${connectors + .filter((d) => d.optionsTExport) + .flatMap((d) => + d.names.map( + (name, i) => + `${i === 0 ? "" : `/** @deprecated Alias of ${d.name} */\n `}"${name}": ${d.optionsTName};`, + ), + ) + .join("\n ")} +}; + +export const builtinConnectors = Object.freeze({ + ${connectors.flatMap((d) => d.names.map((name, i) => `${i === 0 ? "" : `/** @deprecated Alias of ${d.name} */\n `}"${name}": "${d.subpath}"`)).join(",\n ")}, +} as const); +`; + +await writeFile(connectorsMetaFile, genCode, "utf8"); +console.log("Generated connectors metadata file to", connectorsMetaFile); diff --git a/src/_connectors.ts b/src/_connectors.ts new file mode 100644 index 0000000..4e0b680 --- /dev/null +++ b/src/_connectors.ts @@ -0,0 +1,55 @@ +// Auto-generated using scripts/gen-connectors. +// Do not manually edit! +import type { ConnectorOptions as BetterSQLite3Options } from "db0/connectors/better-sqlite3"; +import type { ConnectorOptions as BunSQLiteOptions } from "db0/connectors/bun-sqlite"; +import type { ConnectorOptions as CloudflareD1Options } from "db0/connectors/cloudflare-d1"; +import type { ConnectorOptions as LibSQLCoreOptions } from "db0/connectors/libsql/core"; +import type { ConnectorOptions as LibSQLHttpOptions } from "db0/connectors/libsql/http"; +import type { ConnectorOptions as LibSQLNodeOptions } from "db0/connectors/libsql/node"; +import type { ConnectorOptions as LibSQLWebOptions } from "db0/connectors/libsql/web"; +import type { ConnectorOptions as MySQL2Options } from "db0/connectors/mysql2"; +import type { ConnectorOptions as PgliteOptions } from "db0/connectors/pglite"; +import type { ConnectorOptions as PlanetscaleOptions } from "db0/connectors/planetscale"; +import type { ConnectorOptions as PostgreSQLOptions } from "db0/connectors/postgresql"; + +export type BuiltinConnectorName = "better-sqlite3" | "sqlite" | "bun-sqlite" | "bun" | "cloudflare-d1" | "libsql-core" | "libsql-http" | "libsql-node" | "libsql" | "libsql-web" | "mysql2" | "pglite" | "planetscale" | "postgresql"; + +export type BuiltinConnectorOptions = { + "better-sqlite3": BetterSQLite3Options; + /** @deprecated Alias of better-sqlite3 */ + "sqlite": BetterSQLite3Options; + "bun-sqlite": BunSQLiteOptions; + /** @deprecated Alias of bun-sqlite */ + "bun": BunSQLiteOptions; + "cloudflare-d1": CloudflareD1Options; + "libsql-core": LibSQLCoreOptions; + "libsql-http": LibSQLHttpOptions; + "libsql-node": LibSQLNodeOptions; + /** @deprecated Alias of libsql-node */ + "libsql": LibSQLNodeOptions; + "libsql-web": LibSQLWebOptions; + "mysql2": MySQL2Options; + "pglite": PgliteOptions; + "planetscale": PlanetscaleOptions; + "postgresql": PostgreSQLOptions; +}; + +export const builtinConnectors = Object.freeze({ + "better-sqlite3": "db0/connectors/better-sqlite3", + /** @deprecated Alias of better-sqlite3 */ + "sqlite": "db0/connectors/better-sqlite3", + "bun-sqlite": "db0/connectors/bun-sqlite", + /** @deprecated Alias of bun-sqlite */ + "bun": "db0/connectors/bun-sqlite", + "cloudflare-d1": "db0/connectors/cloudflare-d1", + "libsql-core": "db0/connectors/libsql/core", + "libsql-http": "db0/connectors/libsql/http", + "libsql-node": "db0/connectors/libsql/node", + /** @deprecated Alias of libsql-node */ + "libsql": "db0/connectors/libsql/node", + "libsql-web": "db0/connectors/libsql/web", + "mysql2": "db0/connectors/mysql2", + "pglite": "db0/connectors/pglite", + "planetscale": "db0/connectors/planetscale", + "postgresql": "db0/connectors/postgresql", +} as const); diff --git a/src/connectors/mysql2.ts b/src/connectors/mysql2.ts index 14fa17d..ca99ac4 100644 --- a/src/connectors/mysql2.ts +++ b/src/connectors/mysql2.ts @@ -2,7 +2,9 @@ import mysql from "mysql2/promise"; import type { Connector, Statement } from "../types"; -export default function mysqlConnector(opts: mysql.ConnectionOptions) { +export type ConnectorOptions = mysql.ConnectionOptions + +export default function mysqlConnector(opts: ConnectorOptions) { let _connection: mysql.Connection | undefined; const getConnection = async () => { if (_connection) { diff --git a/src/connectors/planetscale.ts b/src/connectors/planetscale.ts index 83fb6f0..ea252ac 100644 --- a/src/connectors/planetscale.ts +++ b/src/connectors/planetscale.ts @@ -2,7 +2,9 @@ import { Client, type Config } from "@planetscale/database"; import type { Connector, Statement } from "../types"; -export default function planetscaleConnector(opts: Config) { +export type ConnectorOptions = Config + +export default function planetscaleConnector(opts: ConnectorOptions) { let _client: undefined | Client; function getClient() { if (_client) { diff --git a/src/index.ts b/src/index.ts index 38700e7..e24302a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,29 +8,3 @@ export type { SQLDialect, Statement, } from "./types"; - -/** - * A mapping of available database connector identifiers to their module paths. - * This constant facilitates the use of different database connectors by providing easy access to the - * by providing easy access to the connector's specific import paths. - */ -export const connectors = { - sqlite: "db0/connectors/better-sqlite3", - postgresql: "db0/connectors/postgresql", - pglite: "db0/connectors/pglite", - "cloudflare-d1": "db0/connectors/cloudflare-d1", - libsql: "db0/connectors/libsql/node", - "libsql-node": "db0/connectors/libsql/node", - "libsql-http": "db0/connectors/libsql/http", - "libsql-web": "db0/connectors/libsql/web", - bun: "db0/connectors/bun-sqlite", - "bun-sqlite": "db0/connectors/bun-sqlite", - planetscale: "db0/connectors/planetscale", - mysql: "db0/connectors/mysql2", -} as const; - -/** - * Type alias for the keys of the {@link connectors} map. - * Represents the names of available database connectors. - */ -export type ConnectorName = keyof typeof connectors; diff --git a/tsconfig.json b/tsconfig.json index 7ed9c46..ff69594 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,10 @@ "target": "ESNext", "module": "ESNext", "moduleResolution": "Node", - "esModuleInterop": true + "esModuleInterop": true, + "paths": { + "db0/connectors/*": ["./src/connectors/*"] + } }, "include": [ "src"