Skip to content

Commit 2b88296

Browse files
committed
Use postgres.js
- Remove the bundled version of node-postgres - Bundle a modified version of postgres.js instead, and use it for sqltyper's own purposes - Add code generation for postgres.js with the `-t postgres` CLI option - Add tests for the CLI
1 parent 2e420cf commit 2b88296

54 files changed

Lines changed: 2967 additions & 2596 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.circleci/config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ jobs:
1616
POSTGRES_PASSWORD: sqltyper
1717

1818
steps:
19+
# Install psql
20+
- run: sudo apt-get update
21+
- run: sudo apt-get install postgresql-client-9.6
22+
1923
- checkout
2024

2125
# Restore bundle cache

.eslintignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
dist/
2+
src/postgres/

.eslintrc.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,13 @@ module.exports = {
88
'plugin:@typescript-eslint/recommended',
99
],
1010
rules: {
11+
'@typescript-eslint/ban-types': [
12+
'error',
13+
{
14+
types: { '{}': false },
15+
extendDefaults: true,
16+
}
17+
],
1118
'@typescript-eslint/explicit-function-return-type': 'off',
1219
'@typescript-eslint/member-delimiter-style': [
1320
'error',

.prettierignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
package.json
12
dist/
3+
src/postgres/

README.md

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,20 @@ perfectly safe to use it with any query.
6666
## Installation
6767

6868
```
69-
npm install --save pg
7069
npm install --save-dev sqltyper
7170
```
7271

73-
Or:
72+
The generated TypeScript code uses [node-postgres] or [postgres.js] to execute
73+
the queries, so either `pg` or `postgres` is a required runtime dependency:
7474

7575
```
76-
yarn add pg
77-
yarn add --dev sqltyper
76+
npm install --save pg
77+
# or
78+
npm install --save postgres@beta
7879
```
7980

80-
sqltyper generates TypeScript code, so it isn't needed on
81-
application runtime. However, the generated TypeScript code uses
82-
[node-postgres] to execute the queries, so `pg` is a required runtime
83-
dependency.
84-
85-
[node-postgres]: https://node-postgres.com/
81+
At the time of writing, you need to install the `@beta` verson of postgres.js to
82+
get TypeScript support.
8683

8784
## Tutorial
8885

@@ -101,9 +98,9 @@ src/
10198
Run sqltyper on the `sqls` directory:
10299

103100
```
104-
yarn sqltyper --database postgres://user:pass@host/dbname src/sqls
101+
npx sqltyper --database postgres://user:pass@host/dbname src/sqls
105102
106-
# or npx sqltyper, or ./node_modules/.bin/sqltyper, ...
103+
# or yarn sqltyper, or ./node_modules/.bin/sqltyper, ...
107104
```
108105

109106
sqltyper connects to the PostgreSQL database you give in the
@@ -194,7 +191,21 @@ nullability. Default: `false`.
194191
Watch files and run the conversion when something changes. Default:
195192
`false`.
196193

197-
`--check`, `-c`
194+
`--target`, `-t`
195+
196+
Whether to generate code for `pg` ([node-postgres]) or `postgres`
197+
([postgres.js]). Default: `pg`.
198+
199+
`--module`, `-m`
200+
201+
Where to import node-postgres or postgres.js from. Default: `pg` for
202+
node-postgres, `postgres` for postgres.js.
203+
204+
`--pg-module` (deprecated)
205+
206+
Alias of `--module`.
207+
208+
`--check`,`-c`
198209

199210
Check whether all output files are up-to-date without actually
200211
updating them. If they are, exit with status 0, otherwise exit with
@@ -210,10 +221,6 @@ installed and configured for your project. Default: `false`.
210221
Whether to generate and `index.ts` file that re-exports all the
211222
generated functions. Default: `true`.
212223

213-
`--pg-module`
214-
215-
Where to import node-postgres from. Default: `pg`.
216-
217224
[connecting logic]: https://node-postgres.com/features/connecting
218225
[libpq environment variables]: https://www.postgresql.org/docs/current/libpq-envars.html
219226
[prettier]: https://prettier.io/
@@ -259,4 +266,6 @@ does more or less the same as sqltyper, but for Scala, and is designed
259266
to be used with MySQL. It uses JDBC, and is implemented as a Scala
260267
macro rather than an offline code generation tool.
261268

269+
[node-postgres]: https://node-postgres.com/
270+
[postgres.js]: https://github.com/porsager/postgres
262271
[sqlτyped]: https://github.com/jonifreeman/sqltyped

jest.config.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
module.exports = {
22
preset: 'ts-jest',
33
testEnvironment: 'node',
4-
testPathIgnorePatterns: ['/node_modules/', '/dist/'],
4+
testPathIgnorePatterns: [
5+
'/node_modules/',
6+
'<rootDir>/dist/',
7+
'<rootDir>/src/postgres/',
8+
],
9+
modulePathIgnorePatterns: ['<rootDir>/dist'],
510
}

package.json

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,18 @@
66
"repository": "https://github.com/akheron/sqltyper",
77
"author": "Petri Lehtinen <petri@digip.org>",
88
"license": "MIT",
9-
"keywords": [
10-
"database",
11-
"postgres",
12-
"postgresql",
13-
"typescript"
14-
],
15-
"files": [
16-
"dist"
17-
],
9+
"keywords": ["database", "postgres", "postgresql", "typescript"],
10+
"files": ["dist"],
1811
"bin": {
1912
"sqltyper": "dist/src/cli.js"
2013
},
2114
"scripts": {
22-
"sql": "ts-node src/cli.ts --prettify --pg-module ../pg src/sql/",
15+
"sql": "ts-node src/cli.ts --target postgres --module '../postgres' --prettify src/sql/",
2316
"sql:check": "yarn run sql --check",
2417
"sql:watch": "yarn run sql --watch",
2518
"lint": "eslint '**/*.ts' && prettier --check \"**/*.{json,md}\"",
2619
"lint:fix": "eslint --fix '**/*.ts' && prettier --write '**/*.{json,md}'",
27-
"build": "tsc",
20+
"build": "tsc -p tsconfig.dist.json",
2821
"clean": "rm -rf dist/",
2922
"prepublishOnly": "yarn clean && yarn build",
3023
"test": "jest"
@@ -51,10 +44,6 @@
5144
"fp-ts": "^2.5.3",
5245
"node-watch": "^0.6.3",
5346
"packet-reader": "^1.0.0",
54-
"pg-connection-string": "^2.2.0",
55-
"pg-pool": "^3.0.0",
56-
"pg-types": "^3.0.0",
57-
"pgpass": "^1.0.2",
5847
"ramda": "^0.27.0",
5948
"typed-parser": "^0.1.4",
6049
"typescript": "^3.8.3",

src/cli.ts

Lines changed: 43 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { pipe } from 'fp-ts/lib/pipeable'
1717
import * as yargs from 'yargs'
1818

1919
import { Clients, connect, disconnect } from './clients'
20+
import { CodegenTarget, codegenTargets } from './codegen'
2021
import {
2122
sqlToStatementDescription,
2223
generateTSCode,
@@ -31,7 +32,8 @@ type Options = {
3132
verbose: boolean
3233
index: boolean
3334
prettify: boolean
34-
pgModule: string
35+
target: CodegenTarget
36+
module: string
3537
terminalColumns: number | undefined
3638
}
3739

@@ -51,7 +53,8 @@ async function main(): Promise<number> {
5153
verbose: args.verbose,
5254
index: args.index,
5355
prettify: args.prettify,
54-
pgModule: args['pg-module'],
56+
target: args.target,
57+
module: args.module ?? args['pg-module'] ?? args.target,
5558
terminalColumns: process.stdout.columns,
5659
}
5760

@@ -68,7 +71,7 @@ async function main(): Promise<number> {
6871
const clients = await connect(args.database)
6972
if (Either.isLeft(clients)) {
7073
console.error(clients.left)
71-
throw process.exit(1)
74+
return 1
7275
}
7376

7477
let status = 0
@@ -87,7 +90,13 @@ Some files are out of date!`)
8790
status = 1
8891
}
8992
} else {
90-
await processDirectories(clients.right, fileExtensions, dirPaths, options)()
93+
const moduleDirs = await processDirectories(
94+
clients.right,
95+
fileExtensions,
96+
dirPaths,
97+
options
98+
)()
99+
if (moduleDirs.some(moduleDir => moduleDir.hasErrors)) status = 1
91100
}
92101

93102
await disconnect(clients.right)
@@ -130,6 +139,18 @@ function parseArgs() {
130139
type: 'boolean',
131140
default: false,
132141
})
142+
.option('target', {
143+
alias: 't',
144+
description:
145+
'Postgres client library to use in generated TypeScript code',
146+
choices: codegenTargets,
147+
default: codegenTargets[0],
148+
})
149+
.option('module', {
150+
alias: 'm',
151+
description: 'Where to import node-postgres or postgres.js from.',
152+
type: 'string',
153+
})
133154
.option('check', {
134155
alias: 'c',
135156
description:
@@ -146,9 +167,9 @@ function parseArgs() {
146167
default: false,
147168
})
148169
.option('pg-module', {
149-
description: 'Where to import node-postgres from.',
170+
description:
171+
'Where to import node-postgres from. (deprecated, use --module instead)',
150172
type: 'string',
151-
default: 'pg',
152173
})
153174
.epilogue(
154175
`\
@@ -246,7 +267,11 @@ async function handleWatchEvents(
246267
moduleDirs = pipe(
247268
modifyWhere(
248269
moduleDir => moduleDir.dirPath === dirPath,
249-
moduleDir => ({ dirPath: moduleDir.dirPath, modules: newModules }),
270+
moduleDir => ({
271+
dirPath: moduleDir.dirPath,
272+
modules: newModules,
273+
hasErrors: moduleDir.hasErrors,
274+
}),
250275
moduleDirs
251276
),
252277
Option.getOrElse(() => moduleDirs)
@@ -362,9 +387,13 @@ function processDirectories(
362387
(dirPath, tsModules) => processSQLDirectory(dirPath, tsModules, options)
363388
)
364389
),
365-
Task.map(result => {
366-
console.log('done.')
367-
return result
390+
Task.map(moduleDirs => {
391+
if (moduleDirs.some(moduleDir => moduleDir.hasErrors)) {
392+
console.log('Compilation failed.')
393+
} else {
394+
console.log('done.')
395+
}
396+
return moduleDirs
368397
})
369398
)
370399
}
@@ -436,7 +465,8 @@ function processSQLFile(
436465
TaskEither.chain(source =>
437466
generateTSCode(clients, path.basename(filePath), source, fnName, {
438467
prettierFileName: options.prettify ? tsPath : undefined,
439-
pgModule: options.pgModule,
468+
target: options.target,
469+
module: options.module,
440470
})
441471
),
442472
TaskEither.chain(tsCode => async () => {
@@ -474,14 +504,15 @@ function processSQLDirectory(
474504
options: Options
475505
): Task.Task<TsModuleDir> {
476506
const successfulModules = pipe(modules, Array.filterMap(identity))
507+
const hasErrors = modules.some(Option.isNone)
477508
return pipe(
478509
maybeWriteIndexModule(
479510
options.index,
480511
dirPath,
481512
successfulModules,
482513
options.prettify
483514
),
484-
Task.map(modules => ({ dirPath, modules }))
515+
Task.map(modules => ({ hasErrors, dirPath, modules }))
485516
)
486517
}
487518

src/clients.ts

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,40 @@
11
import * as Either from 'fp-ts/lib/Either'
22

3-
import * as pg from './pg'
3+
import * as postgres from './postgres'
44
import { SchemaClient, schemaClient } from './schema'
55
import { TypeClient, typeClient } from './tstype'
66

77
export type Clients = {
8-
pg: pg.Client
8+
postgres: postgres.Sql<{}>
99
schema: SchemaClient
1010
types: TypeClient
1111
}
1212

1313
export async function connect(
1414
connectionString?: string | undefined
1515
): Promise<Either.Either<string, Clients>> {
16-
const pgClient = new pg.Client(
17-
connectionString == null ? undefined : { connectionString }
18-
)
19-
try {
20-
await pgClient.connect()
21-
} catch (err) {
22-
return Either.left(`Error connecting to database: ${err.message}`)
23-
}
16+
const postgresClient = connectionString
17+
? postgres(connectionString)
18+
: postgres()
2419

25-
const schema = schemaClient(pgClient)
26-
const types = await typeClient(pgClient)
20+
return Either.right(await clients(postgresClient))
21+
}
2722

28-
return Either.right({ pg: pgClient, schema, types })
23+
export async function clients(
24+
postgresClient: postgres.Sql<{}>
25+
): Promise<Clients> {
26+
const schema = schemaClient(postgresClient)
27+
const types = await typeClient(postgresClient)
28+
return { postgres: postgresClient, schema, types }
2929
}
3030

3131
export async function disconnect(clients: Clients): Promise<void> {
32-
await clients.pg.end()
32+
await clients.postgres.end({ timeout: 5 })
3333
}
3434

3535
export async function clearCache(clients: Clients): Promise<Clients> {
3636
// The type client caches stuff about user-defined SQL types.
3737
// Recreate it to clear the cache.
38-
const types = await typeClient(clients.pg)
38+
const types = await typeClient(clients.postgres)
3939
return { ...clients, types }
4040
}

0 commit comments

Comments
 (0)