Skip to content

Commit ad4ddd4

Browse files
[Drizzle Kit]: Extend api (#4999)
* feat: Add external kit api to start Drizzle Studio * feat: Separate API build process and fix ESM dynamic require handling * fix: Update import paths for Relations in api.ts, pgImports.ts, and studio.ts * dprint * fix: Remove '-pooler' suffix from NEON_CONNECTION_STRING in Vercel client tests * dprint * chore: update version to 0.31.6 and add changelog for bug fixes
1 parent 11ff664 commit ad4ddd4

File tree

9 files changed

+253
-38
lines changed

9 files changed

+253
-38
lines changed

changelogs/drizzle-kit/0.31.6.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
### Bug fixes
2+
3+
- [[BUG]: Importing drizzle-kit/api fails in ESM modules](https://github.com/drizzle-team/drizzle-orm/issues/2853)

drizzle-kit/build.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ esbuild.buildSync({
8282

8383
const main = async () => {
8484
await tsup.build({
85-
entryPoints: ['./src/index.ts', './src/api.ts'],
85+
entryPoints: ['./src/index.ts'],
8686
outDir: './dist',
8787
external: ['bun:sqlite'],
8888
splitting: false,
@@ -102,6 +102,39 @@ const main = async () => {
102102
},
103103
});
104104

105+
await tsup.build({
106+
entryPoints: ['./src/api.ts'],
107+
outDir: './dist',
108+
external: ['bun:sqlite'],
109+
splitting: false,
110+
dts: true,
111+
format: ['cjs', 'esm'],
112+
banner: (ctx) => {
113+
/**
114+
* fix dynamic require in ESM ("glob" -> "fs.realpath" requires 'fs' module)
115+
* @link https://github.com/drizzle-team/drizzle-orm/issues/2853
116+
*/
117+
if (ctx.format === 'esm') {
118+
return {
119+
js: "import { createRequire } from 'module'; const require = createRequire(import.meta.url);",
120+
};
121+
}
122+
return undefined;
123+
},
124+
outExtension: (ctx) => {
125+
if (ctx.format === 'cjs') {
126+
return {
127+
dts: '.d.ts',
128+
js: '.js',
129+
};
130+
}
131+
return {
132+
dts: '.d.mts',
133+
js: '.mjs',
134+
};
135+
},
136+
});
137+
105138
const apiCjs = readFileSync('./dist/api.js', 'utf8').replace(/await import\(/g, 'require(');
106139
writeFileSync('./dist/api.js', apiCjs);
107140
};

drizzle-kit/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "drizzle-kit",
3-
"version": "0.31.5",
3+
"version": "0.31.6",
44
"homepage": "https://orm.drizzle.team",
55
"keywords": [
66
"drizzle",

drizzle-kit/src/api.ts

Lines changed: 170 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,18 @@
1+
import type { PGlite } from '@electric-sql/pglite';
12
import { randomUUID } from 'crypto';
3+
import { is } from 'drizzle-orm';
24
import { LibSQLDatabase } from 'drizzle-orm/libsql';
5+
import { AnyMySqlTable, getTableConfig as mysqlTableConfig, MySqlTable } from 'drizzle-orm/mysql-core';
36
import type { MySql2Database } from 'drizzle-orm/mysql2';
4-
import { PgDatabase } from 'drizzle-orm/pg-core';
7+
import { AnyPgTable, getTableConfig as pgTableConfig, PgDatabase, PgTable } from 'drizzle-orm/pg-core';
8+
import { Relations } from 'drizzle-orm/relations';
59
import { SingleStoreDriverDatabase } from 'drizzle-orm/singlestore';
10+
import {
11+
AnySingleStoreTable,
12+
getTableConfig as singlestoreTableConfig,
13+
SingleStoreTable,
14+
} from 'drizzle-orm/singlestore-core';
15+
import { AnySQLiteTable, SQLiteTable } from 'drizzle-orm/sqlite-core';
616
import {
717
columnsResolver,
818
enumsResolver,
@@ -22,10 +32,13 @@ import { updateUpToV6 as upPgV6, updateUpToV7 as upPgV7 } from './cli/commands/p
2232
import { sqlitePushIntrospect } from './cli/commands/sqliteIntrospect';
2333
import { logSuggestionsAndReturn } from './cli/commands/sqlitePushUtils';
2434
import type { CasingType } from './cli/validations/common';
35+
import type { MysqlCredentials } from './cli/validations/mysql';
36+
import type { PostgresCredentials } from './cli/validations/postgres';
37+
import type { SingleStoreCredentials } from './cli/validations/singlestore';
38+
import type { SqliteCredentials } from './cli/validations/sqlite';
2539
import { getTablesFilterByExtensions } from './extensions/getTablesFilterByExtensions';
2640
import { originUUID } from './global';
2741
import type { Config } from './index';
28-
import { fillPgSnapshot } from './migrationPreparator';
2942
import { MySqlSchema as MySQLSchemaKit, mysqlSchema, squashMysqlScheme } from './serializer/mysqlSchema';
3043
import { generateMySqlSnapshot } from './serializer/mysqlSerializer';
3144
import { prepareFromExports } from './serializer/pgImports';
@@ -39,7 +52,9 @@ import {
3952
import { generateSingleStoreSnapshot } from './serializer/singlestoreSerializer';
4053
import { SQLiteSchema as SQLiteSchemaKit, sqliteSchema, squashSqliteScheme } from './serializer/sqliteSchema';
4154
import { generateSqliteSnapshot } from './serializer/sqliteSerializer';
55+
import type { Setup } from './serializer/studio';
4256
import type { DB, SQLiteDB } from './utils';
57+
import { certs } from './utils/certs';
4358
export type DrizzleSnapshotJSON = PgSchemaKit;
4459
export type DrizzleSQLiteSnapshotJSON = SQLiteSchemaKit;
4560
export type DrizzleMySQLSnapshotJSON = MySQLSchemaKit;
@@ -68,11 +83,11 @@ export const generateDrizzleJson = (
6883
schemaFilters,
6984
);
7085

71-
return fillPgSnapshot({
72-
serialized: snapshot,
86+
return {
87+
...snapshot,
7388
id,
74-
idPrev: prevId ?? originUUID,
75-
});
89+
prevId: prevId ?? originUUID,
90+
};
7691
};
7792

7893
export const generateMigration = async (
@@ -171,6 +186,39 @@ export const pushSchema = async (
171186
};
172187
};
173188

189+
export const startStudioPostgresServer = async (
190+
imports: Record<string, unknown>,
191+
credentials: PostgresCredentials | {
192+
driver: 'pglite';
193+
client: PGlite;
194+
},
195+
options?: {
196+
host?: string;
197+
port?: number;
198+
casing?: CasingType;
199+
},
200+
) => {
201+
const { drizzleForPostgres } = await import('./serializer/studio');
202+
203+
const pgSchema: Record<string, Record<string, AnyPgTable>> = {};
204+
const relations: Record<string, Relations> = {};
205+
206+
Object.entries(imports).forEach(([k, t]) => {
207+
if (is(t, PgTable)) {
208+
const schema = pgTableConfig(t).schema || 'public';
209+
pgSchema[schema] = pgSchema[schema] || {};
210+
pgSchema[schema][k] = t;
211+
}
212+
213+
if (is(t, Relations)) {
214+
relations[k] = t;
215+
}
216+
});
217+
218+
const setup = await drizzleForPostgres(credentials, pgSchema, relations, [], options?.casing);
219+
await startServerFromSetup(setup, options);
220+
};
221+
174222
// SQLite
175223

176224
export const generateSQLiteDrizzleJson = async (
@@ -277,6 +325,36 @@ export const pushSQLiteSchema = async (
277325
};
278326
};
279327

328+
export const startStudioSQLiteServer = async (
329+
imports: Record<string, unknown>,
330+
credentials: SqliteCredentials,
331+
options?: {
332+
host?: string;
333+
port?: number;
334+
casing?: CasingType;
335+
},
336+
) => {
337+
const { drizzleForSQLite } = await import('./serializer/studio');
338+
339+
const sqliteSchema: Record<string, Record<string, AnySQLiteTable>> = {};
340+
const relations: Record<string, Relations> = {};
341+
342+
Object.entries(imports).forEach(([k, t]) => {
343+
if (is(t, SQLiteTable)) {
344+
const schema = 'public'; // sqlite does not have schemas
345+
sqliteSchema[schema] = sqliteSchema[schema] || {};
346+
sqliteSchema[schema][k] = t;
347+
}
348+
349+
if (is(t, Relations)) {
350+
relations[k] = t;
351+
}
352+
});
353+
354+
const setup = await drizzleForSQLite(credentials, sqliteSchema, relations, [], options?.casing);
355+
await startServerFromSetup(setup, options);
356+
};
357+
280358
// MySQL
281359

282360
export const generateMySQLDrizzleJson = async (
@@ -382,6 +460,36 @@ export const pushMySQLSchema = async (
382460
};
383461
};
384462

463+
export const startStudioMySQLServer = async (
464+
imports: Record<string, unknown>,
465+
credentials: MysqlCredentials,
466+
options?: {
467+
host?: string;
468+
port?: number;
469+
casing?: CasingType;
470+
},
471+
) => {
472+
const { drizzleForMySQL } = await import('./serializer/studio');
473+
474+
const mysqlSchema: Record<string, Record<string, AnyMySqlTable>> = {};
475+
const relations: Record<string, Relations> = {};
476+
477+
Object.entries(imports).forEach(([k, t]) => {
478+
if (is(t, MySqlTable)) {
479+
const schema = mysqlTableConfig(t).schema || 'public';
480+
mysqlSchema[schema] = mysqlSchema[schema] || {};
481+
mysqlSchema[schema][k] = t;
482+
}
483+
484+
if (is(t, Relations)) {
485+
relations[k] = t;
486+
}
487+
});
488+
489+
const setup = await drizzleForMySQL(credentials, mysqlSchema, relations, [], options?.casing);
490+
await startServerFromSetup(setup, options);
491+
};
492+
385493
// SingleStore
386494

387495
export const generateSingleStoreDrizzleJson = async (
@@ -489,6 +597,62 @@ export const pushSingleStoreSchema = async (
489597
};
490598
};
491599

600+
export const startStudioSingleStoreServer = async (
601+
imports: Record<string, unknown>,
602+
credentials: SingleStoreCredentials,
603+
options?: {
604+
host?: string;
605+
port?: number;
606+
casing?: CasingType;
607+
},
608+
) => {
609+
const { drizzleForSingleStore } = await import('./serializer/studio');
610+
611+
const singleStoreSchema: Record<string, Record<string, AnySingleStoreTable>> = {};
612+
const relations: Record<string, Relations> = {};
613+
614+
Object.entries(imports).forEach(([k, t]) => {
615+
if (is(t, SingleStoreTable)) {
616+
const schema = singlestoreTableConfig(t).schema || 'public';
617+
singleStoreSchema[schema] = singleStoreSchema[schema] || {};
618+
singleStoreSchema[schema][k] = t;
619+
}
620+
621+
if (is(t, Relations)) {
622+
relations[k] = t;
623+
}
624+
});
625+
626+
const setup = await drizzleForSingleStore(credentials, singleStoreSchema, relations, [], options?.casing);
627+
await startServerFromSetup(setup, options);
628+
};
629+
630+
const startServerFromSetup = async (setup: Setup, options?: {
631+
host?: string;
632+
port?: number;
633+
}) => {
634+
const { prepareServer } = await import('./serializer/studio');
635+
636+
const server = await prepareServer(setup);
637+
638+
const host = options?.host || '127.0.0.1';
639+
const port = options?.port || 4983;
640+
const { key, cert } = (await certs()) || {};
641+
server.start({
642+
host,
643+
port,
644+
key,
645+
cert,
646+
cb: (err) => {
647+
if (err) {
648+
console.error(err);
649+
} else {
650+
console.log(`Studio is running at ${key ? 'https' : 'http'}://${host}:${port}`);
651+
}
652+
},
653+
});
654+
};
655+
492656
export const upPgSnapshot = (snapshot: Record<string, unknown>) => {
493657
if (snapshot.version === '5') {
494658
return upPgV7(upPgV6(snapshot));

drizzle-kit/src/cli/connections.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import type { PGlite } from '@electric-sql/pglite';
12
import type { AwsDataApiPgQueryResult, AwsDataApiSessionOptions } from 'drizzle-orm/aws-data-api/pg';
23
import type { MigrationConfig } from 'drizzle-orm/migrator';
34
import type { PreparedQueryConfig } from 'drizzle-orm/pg-core';
@@ -24,7 +25,10 @@ import { SingleStoreCredentials } from './validations/singlestore';
2425
import type { SqliteCredentials } from './validations/sqlite';
2526

2627
export const preparePostgresDB = async (
27-
credentials: PostgresCredentials,
28+
credentials: PostgresCredentials | {
29+
driver: 'pglite';
30+
client: PGlite;
31+
},
2832
): Promise<
2933
DB & {
3034
packageName:
@@ -123,7 +127,7 @@ export const preparePostgresDB = async (
123127
const { drizzle } = await import('drizzle-orm/pglite');
124128
const { migrate } = await import('drizzle-orm/pglite/migrator');
125129

126-
const pglite = new PGlite(normalisePGliteUrl(credentials.url));
130+
const pglite = 'client' in credentials ? credentials.client : new PGlite(normalisePGliteUrl(credentials.url));
127131
await pglite.waitReady;
128132
const drzl = drizzle(pglite);
129133
const migrateFn = async (config: MigrationConfig) => {

drizzle-kit/src/migrationPreparator.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import fs from 'fs';
33
import { CasingType } from './cli/validations/common';
44
import { serializeMySql, serializePg, serializeSingleStore, serializeSQLite } from './serializer';
55
import { dryMySql, MySqlSchema, mysqlSchema } from './serializer/mysqlSchema';
6-
import { dryPg, PgSchema, pgSchema, PgSchemaInternal } from './serializer/pgSchema';
6+
import { dryPg, PgSchema, pgSchema } from './serializer/pgSchema';
77
import { drySingleStore, SingleStoreSchema, singlestoreSchema } from './serializer/singlestoreSchema';
88
import { drySQLite, SQLiteSchema, sqliteSchema } from './serializer/sqliteSchema';
99

@@ -168,19 +168,6 @@ export const prepareSqliteMigrationSnapshot = async (
168168
return { prev: prevSnapshot, cur: result, custom };
169169
};
170170

171-
export const fillPgSnapshot = ({
172-
serialized,
173-
id,
174-
idPrev,
175-
}: {
176-
serialized: PgSchemaInternal;
177-
id: string;
178-
idPrev: string;
179-
}): PgSchema => {
180-
// const id = randomUUID();
181-
return { id, prevId: idPrev, ...serialized };
182-
};
183-
184171
export const preparePgMigrationSnapshot = async (
185172
snapshots: string[],
186173
schemaPath: string | string[],
@@ -199,7 +186,11 @@ export const preparePgMigrationSnapshot = async (
199186
const { id: _ignoredId, prevId: _ignoredPrevId, ...prevRest } = prevSnapshot;
200187

201188
// that's for custom migrations, when we need new IDs, but old snapshot
202-
const custom: PgSchema = fillPgSnapshot({ serialized: prevRest, id, idPrev });
189+
const custom: PgSchema = {
190+
id,
191+
prevId: idPrev,
192+
...prevRest,
193+
};
203194

204195
return { prev: prevSnapshot, cur: result, custom };
205196
};

0 commit comments

Comments
 (0)