Skip to content
This repository was archived by the owner on Jun 26, 2021. It is now read-only.

Commit 05d08c7

Browse files
committed
Cli wip
1 parent 330b894 commit 05d08c7

File tree

6 files changed

+101
-53
lines changed

6 files changed

+101
-53
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"gostek-generate": "./lib/cli.js"
1212
},
1313
"scripts": {
14-
"build": "tsc && chmod +x ./lib/cli.js",
14+
"build": "tsc -p tsconfig.build.json && chmod +x ./lib/cli.js",
1515
"test": "jest",
1616
"lint": "eslint src --ext .js,.jsx,.ts,.tsx"
1717
},

src/__tests__/test.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import 'jest-extended';
2-
import { db, sql, pgp } from '../generator/db';
2+
import { sql, pgp, getDbConnection } from '../generator/db';
33
import {
44
getTablesSchemas,
55
schemaToTableObj,
@@ -107,27 +107,31 @@ export const User = {
107107
});
108108

109109
describe('integration tests', () => {
110+
const db = getDbConnection({
111+
user: 'test',
112+
database: 'test',
113+
password: 'test',
114+
});
115+
110116
const drop = async () => {
111117
await db.none(sql(Path.join(__dirname, 'drop_user.sql')));
112118
await db.none(sql(Path.join(__dirname, 'drop_invoice.sql')));
113119
};
114120
const create = async () => {
115121
await db.none(sql(Path.join(__dirname, 'create_user.sql')));
116122
await db.none(sql(Path.join(__dirname, 'create_invoice.sql')));
117-
// const x = await db.one('SELECT * FROM "user";');
118-
// console.log(x, typeof x.editTime);
119123
};
120124
beforeAll(drop);
121125
beforeEach(create);
122126
afterEach(drop);
123127
afterAll(() => pgp.end());
124128

125129
it('reads postgres version', async () => {
126-
expect(await getPostgresVersion()).toBeGreaterThanOrEqual(120000);
130+
expect(await getPostgresVersion(db)).toBeGreaterThanOrEqual(120000);
127131
});
128132

129133
it('correctly reads schema for table user', async () => {
130-
const result = await getTablesSchemas();
134+
const result = await getTablesSchemas(db);
131135

132136
const userSchema = result.find((i) => i.tableName === 'user');
133137
expect(userSchema).toBeDefined();
@@ -153,7 +157,7 @@ describe('integration tests', () => {
153157
});
154158

155159
it('generates valid TS code for all schemas', async () => {
156-
const code = await generateTSCodeForAllSchemas();
160+
const code = await generateTSCodeForAllSchemas(db);
157161

158162
expect(code).toEqual(
159163
`

src/cli.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/usr/bin/env node
22
import Path from 'path';
33
import Fs from 'fs';
4+
import { getDbConnection, pgp } from './generator/db';
5+
import { generateTSCodeForAllSchemas } from './generator';
46

57
const pkgPath = Path.resolve(process.cwd(), 'package.json');
68
if (!Fs.existsSync(pkgPath)) {
@@ -9,6 +11,32 @@ if (!Fs.existsSync(pkgPath)) {
911
);
1012
}
1113

12-
// const pkg = require(pkgPath);
14+
const connectionOptions = {
15+
user: 'test',
16+
database: 'test',
17+
password: 'test',
18+
};
1319

14-
// const config = {};
20+
const outputFilename = 'generated/models.ts';
21+
22+
function ensureDirectoryExistence(filePath: string) {
23+
const dirname = Path.dirname(filePath);
24+
if (Fs.existsSync(dirname)) {
25+
return;
26+
}
27+
ensureDirectoryExistence(dirname);
28+
Fs.mkdirSync(dirname);
29+
}
30+
31+
(async () => {
32+
const db = getDbConnection(connectionOptions);
33+
const schemas = await generateTSCodeForAllSchemas(db);
34+
pgp.end();
35+
console.log(schemas);
36+
37+
ensureDirectoryExistence(outputFilename);
38+
Fs.writeFileSync(outputFilename, schemas, 'utf8');
39+
})().catch((err) => {
40+
console.error(err);
41+
process.exitCode = 1;
42+
});

src/generator/db.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,21 @@ import PgPromise, { QueryFile } from 'pg-promise';
22

33
export const pgp = PgPromise();
44

5-
export const db = pgp({
6-
user: 'test',
7-
database: 'test',
8-
password: 'test',
9-
});
5+
type ConnectionOptions =
6+
| {
7+
connectionString: string;
8+
}
9+
| {
10+
host?: string;
11+
database: string;
12+
user: string;
13+
password: string;
14+
port?: number;
15+
};
16+
17+
export const getDbConnection = (options: ConnectionOptions) => {
18+
return pgp(options);
19+
};
1020

1121
const sqlCache = new Map<string, QueryFile>();
1222

src/generator/index.ts

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { db } from './db';
21
import { TableSchema, ColumnType } from './types';
32
import { Table } from '..';
43
import Prettier from 'prettier';
54
import { defaults, flatMap } from 'lodash';
65
import { makeIntrospectionQuery } from './introspectionQuery';
76
import { xByY, xByYAndZ, parseTags } from './utils';
7+
import { IDatabase } from 'pg-promise';
88

99
// @ts-ignore
1010
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -215,13 +215,44 @@ type PgIntrospectionResultsByKind = PgIntrospectionOriginalResultsByKind & {
215215
typeById: { [typeId: string]: PgType };
216216
};
217217

218-
export async function getPostgresVersion(): Promise<number> {
218+
export function schemaToTableObj(schema: TableSchema): Table {
219+
return {
220+
name: schema.tableName,
221+
columns: Object.fromEntries(
222+
schema.schema.map((s) => {
223+
return [s.column_name, { type: s.udt_name, notNull: s.is_nullable === 'NO' }];
224+
}),
225+
),
226+
};
227+
}
228+
229+
export function tableObjToTSCode(table: Table, opts?: Prettier.Options): string {
230+
const typeName = table.name.slice(0, 1).toLocaleUpperCase() + table.name.slice(1);
231+
const code = `export const ${typeName} = ${JSON.stringify(table)} as const;`;
232+
const defaultOptions: Prettier.Options = {
233+
semi: true,
234+
singleQuote: true,
235+
trailingComma: 'all',
236+
printWidth: 100,
237+
tabWidth: 2,
238+
useTabs: false,
239+
} as const;
240+
const options = defaults({}, opts, defaultOptions);
241+
return Prettier.format(code, {
242+
...options,
243+
parser: 'typescript',
244+
});
245+
}
246+
247+
export async function getPostgresVersion(db: IDatabase<any>): Promise<number> {
219248
const versionResult = await db.one('show server_version_num;');
220249
return Number.parseInt(versionResult.server_version_num, 10);
221250
}
222251

223-
export async function runIntrospectionQuery(): Promise<PgIntrospectionResultsByKind> {
224-
const version = await getPostgresVersion();
252+
export async function runIntrospectionQuery(
253+
db: IDatabase<any>,
254+
): Promise<PgIntrospectionResultsByKind> {
255+
const version = await getPostgresVersion(db);
225256
const sql = makeIntrospectionQuery(version);
226257
const kinds = [
227258
'namespace',
@@ -293,15 +324,15 @@ export async function runIntrospectionQuery(): Promise<PgIntrospectionResultsByK
293324
return Object.freeze(result);
294325
}
295326

296-
export async function introspectSchemas() {
297-
const intro = await runIntrospectionQuery();
327+
export async function introspectSchemas(db: IDatabase<any>) {
328+
const intro = await runIntrospectionQuery(db);
298329
const namespaceIds = intro.namespace.map((n) => n.id);
299330
const classes = intro.class.filter((c) => namespaceIds.includes(c.namespaceId));
300331
return { classes, intro };
301332
}
302333

303-
export async function getTablesSchemas(): Promise<Array<TableSchema>> {
304-
const { classes, intro } = await introspectSchemas();
334+
export async function getTablesSchemas(db: IDatabase<any>): Promise<Array<TableSchema>> {
335+
const { classes, intro } = await introspectSchemas(db);
305336

306337
return classes.map((klass) => {
307338
return {
@@ -317,39 +348,10 @@ export async function getTablesSchemas(): Promise<Array<TableSchema>> {
317348
});
318349
}
319350

320-
export function schemaToTableObj(schema: TableSchema): Table {
321-
return {
322-
name: schema.tableName,
323-
columns: Object.fromEntries(
324-
schema.schema.map((s) => {
325-
return [s.column_name, { type: s.udt_name, notNull: s.is_nullable === 'NO' }];
326-
}),
327-
),
328-
};
329-
}
330-
331-
export function tableObjToTSCode(table: Table, opts?: Prettier.Options): string {
332-
const typeName = table.name.slice(0, 1).toLocaleUpperCase() + table.name.slice(1);
333-
const code = `export const ${typeName} = ${JSON.stringify(table)} as const;`;
334-
const defaultOptions: Prettier.Options = {
335-
semi: true,
336-
singleQuote: true,
337-
trailingComma: 'all',
338-
printWidth: 100,
339-
tabWidth: 2,
340-
useTabs: false,
341-
} as const;
342-
const options = defaults({}, opts, defaultOptions);
343-
return Prettier.format(code, {
344-
...options,
345-
parser: 'typescript',
346-
});
347-
}
348-
349-
export async function generateTSCodeForAllSchemas() {
351+
export async function generateTSCodeForAllSchemas(db: IDatabase<any>) {
350352
// @ts-ignore
351353
// eslint-disable-next-line @typescript-eslint/no-unused-vars
352-
const schemas = await getTablesSchemas();
354+
const schemas = await getTablesSchemas(db);
353355
const allModelsCode = schemas
354356
.map(schemaToTableObj)
355357
.map((obj) => tableObjToTSCode(obj))

tsconfig.build.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"extends": "./tsconfig.json",
3+
"exclude": ["src/__tests__", "src/index.test-d.ts"]
4+
}

0 commit comments

Comments
 (0)