Skip to content

Commit 79469a8

Browse files
committed
Default generate to PGlite
1 parent 3039eee commit 79469a8

4 files changed

Lines changed: 81 additions & 58 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
[![npm version](https://badge.fury.io/js/pgstrap.svg)](https://badge.fury.io/js/pgstrap)
44
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
55

6-
pgstrap allows you to easily run typescript migrations or generate a directory that represents your database schemas with `table.sql` files. Run `pgstrap generate` to generate a directory with the structure of your postgres database schemas!
6+
pgstrap allows you to easily run typescript migrations or generate a directory that represents your database schemas with `table.sql` files. Run `pgstrap generate` to generate a directory with the structure of your postgres database schemas. By default, generation runs migrations against an in-memory PGlite database, so you do not need a local Postgres server just to refresh generated types.
77

88
## Features
99

@@ -55,7 +55,7 @@ npm install pgstrap --save-dev
5555

5656
- `npm run db:migrate` - Run pending migrations
5757
- `npm run db:reset` - Drop and recreate the database, then run all migrations
58-
- `npm run db:generate` - Generate types and structure dumps. Use `pgstrap generate --pglite` to run migrations against an in-memory PGlite instance.
58+
- `npm run db:generate` - Generate types and structure dumps with an in-memory PGlite database by default. Use `pgstrap generate --no-pglite` to generate from an external Postgres database configured by `DATABASE_URL`.
5959
- `npm run db:create-migration` - Create a new migration file
6060

6161
### Configuration

src/cli.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,18 @@ import { getProjectContext } from "./get-project-context"
3535
"generate",
3636
"generate types and sql documentation from database",
3737
(yargs) => {
38-
yargs.option("pglite", { type: "boolean", default: false })
38+
yargs.option("pglite", {
39+
type: "boolean",
40+
default: true,
41+
describe:
42+
"run migrations in an in-memory PGlite database before generating types",
43+
})
3944
},
4045
async (argv) => {
41-
generate({ ...(await getProjectContext()), pglite: !!argv.pglite })
46+
generate({
47+
...(await getProjectContext()),
48+
pglite: argv.pglite !== false,
49+
})
4250
},
4351
)
4452
.parse()

src/generate.ts

Lines changed: 66 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export const generate = async ({
1212
schemas,
1313
defaultDatabase,
1414
dbDir,
15-
pglite = false,
15+
pglite = true,
1616
migrationsDir,
1717
}: Pick<Context, "schemas" | "defaultDatabase" | "dbDir"> & {
1818
pglite?: boolean
@@ -27,65 +27,79 @@ export const generate = async ({
2727
const net = await import("node:net")
2828

2929
const db = new PGlite()
30+
let server: any
31+
const prevDbUrl = process.env.DATABASE_URL
3032

31-
await migrate({
32-
client: db as any,
33-
migrationsDir,
34-
defaultDatabase,
35-
cwd: process.cwd(),
36-
schemas,
37-
})
33+
try {
34+
await migrate({
35+
client: db as any,
36+
migrationsDir,
37+
defaultDatabase,
38+
cwd: process.cwd(),
39+
schemas,
40+
})
3841

39-
const server = net.createServer(async (socket) => {
40-
const connection = await fromNodeSocket(socket, {
41-
serverVersion: "16.3 (PGlite)",
42-
auth: {
43-
method: "password",
44-
validateCredentials: ({ username, password }: any) =>
45-
username === "postgres" && password === "postgres",
46-
getClearTextPassword: () => "postgres",
47-
},
48-
async onStartup() {
49-
await (db as any).waitReady
50-
},
51-
async onMessage(data: Uint8Array, { isAuthenticated }: any) {
52-
if (!isAuthenticated) return
53-
try {
54-
const { data: responseData } = await (db as any).execProtocol(data)
55-
return responseData
56-
} catch {
57-
return undefined
58-
}
59-
},
42+
server = net.createServer(async (socket) => {
43+
await fromNodeSocket(socket, {
44+
serverVersion: "16.3 (PGlite)",
45+
auth: {
46+
method: "password",
47+
validateCredentials: ({ username, password }: any) =>
48+
username === "postgres" && password === "postgres",
49+
getClearTextPassword: () => "postgres",
50+
},
51+
async onStartup() {
52+
await (db as any).waitReady
53+
},
54+
async onMessage(data: Uint8Array, { isAuthenticated }: any) {
55+
if (!isAuthenticated) return
56+
try {
57+
const { data: responseData } = await (db as any).execProtocol(
58+
data,
59+
)
60+
return responseData
61+
} catch {
62+
return undefined
63+
}
64+
},
65+
})
6066
})
61-
})
6267

63-
await new Promise<void>((resolve) => server.listen(0, resolve))
64-
const port = (server.address() as any).port
65-
const connectionString = `postgres://postgres:postgres@127.0.0.1:${port}/postgres`
68+
await new Promise<void>((resolve) => server.listen(0, resolve))
69+
const port = (server.address() as any).port
70+
const connectionString = `postgres://postgres:postgres@127.0.0.1:${port}/postgres`
6671

67-
const prevDbUrl = process.env.DATABASE_URL
68-
process.env.DATABASE_URL = connectionString
72+
process.env.DATABASE_URL = connectionString
6973

70-
await zg.generate({
71-
db: {
72-
connectionString,
73-
},
74-
schemas: Object.fromEntries(
75-
schemas.map((s) => [s, { include: "*", exclude: [] }]),
76-
),
77-
outDir: dbDir,
78-
})
74+
await zg.generate({
75+
db: {
76+
connectionString,
77+
},
78+
schemas: Object.fromEntries(
79+
schemas.map((s) => [s, { include: "*", exclude: [] }]),
80+
),
81+
outDir: dbDir,
82+
})
7983

80-
await dumpTree({
81-
targetDir: path.join(dbDir, "structure"),
82-
defaultDatabase: "postgres",
83-
schemas,
84-
})
84+
await dumpTree({
85+
targetDir: path.join(dbDir, "structure"),
86+
defaultDatabase: "postgres",
87+
schemas,
88+
})
89+
} finally {
90+
if (server?.listening) {
91+
await new Promise<void>((resolve, reject) => {
92+
server.close((err: Error | undefined) => {
93+
if (err) reject(err)
94+
else resolve()
95+
})
96+
})
97+
}
98+
await (db as any).close?.()
8599

86-
server.close()
87-
if (prevDbUrl === undefined) delete process.env.DATABASE_URL
88-
else process.env.DATABASE_URL = prevDbUrl
100+
if (prevDbUrl === undefined) delete process.env.DATABASE_URL
101+
else process.env.DATABASE_URL = prevDbUrl
102+
}
89103
return
90104
}
91105

tests/generate.pglite.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ exports.down = async (pgm) => {
1313
}
1414
`
1515

16-
test("generate with pglite runs migrations and dumps structure", async () => {
16+
test("generate defaults to pglite and dumps structure without postgres", async () => {
1717
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "pgstrap-generate-"))
18+
const prevDbUrl = process.env.DATABASE_URL
1819
const migrationsDir = path.join(tmp, "migrations")
1920
fs.mkdirSync(migrationsDir, { recursive: true })
2021
fs.writeFileSync(
@@ -27,7 +28,6 @@ test("generate with pglite runs migrations and dumps structure", async () => {
2728
defaultDatabase: "postgres",
2829
dbDir: path.join(tmp, "db"),
2930
migrationsDir,
30-
pglite: true,
3131
})
3232

3333
const zapatosFile = path.join(tmp, "db", "zapatos", "schema.d.ts")
@@ -42,6 +42,7 @@ test("generate with pglite runs migrations and dumps structure", async () => {
4242

4343
expect(fs.existsSync(zapatosFile)).toBe(true)
4444
expect(fs.existsSync(path.join(structureDir, "table.sql"))).toBe(true)
45+
expect(process.env.DATABASE_URL).toBe(prevDbUrl)
4546

4647
fs.rmSync(tmp, { recursive: true, force: true })
4748
})

0 commit comments

Comments
 (0)