Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Add zod generator #1

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"build": "tsc -p tsconfig.json && cpy 'src/lib/sql/*.sql' dist/lib/sql",
"docs:export": "PG_META_EXPORT_DOCS=true node --loader ts-node/esm src/server/server.ts > openapi.json",
"gen:types:typescript": "PG_META_GENERATE_TYPES=typescript node --loader ts-node/esm src/server/server.ts",
"gen:types:zod": "PG_META_GENERATE_TYPES=zod node --loader ts-node/esm src/server/server.ts",
"start": "node dist/server/server.js",
"dev": "trap 'npm run db:clean' INT && run-s db:clean db:run && nodemon --exec node --loader ts-node/esm src/server/server.ts | pino-pretty --colorize",
"pkg": "run-s clean build && pkg .pkg.config.json",
Expand Down
65 changes: 38 additions & 27 deletions src/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
PG_META_PORT,
} from './constants.js'
import { apply as applyTypescriptTemplate } from './templates/typescript.js'
import { apply as applyZodTemplate } from './templates/zod.js'
import {TemplateProps} from "./types.js";

const logger = pino({
formatters: {
Expand All @@ -33,6 +35,22 @@ if (EXPORT_DOCS) {
console.log(JSON.stringify(app.swagger(), null, 2))
} else if (GENERATE_TYPES === 'typescript') {
// TODO: Move to a separate script.
console.log(
await applyTemplate(applyTypescriptTemplate)
);
} else if (GENERATE_TYPES === "zod") {
console.log(
await applyTemplate(applyZodTemplate)
);
}
else {
app.listen({ port: PG_META_PORT, host: PG_META_HOST }, () => {
const adminPort = PG_META_PORT + 1
adminApp.listen({ port: adminPort, host: PG_META_HOST })
})
}

async function applyTemplate(apply: (props: TemplateProps) => string): Promise<string> {
const pgMeta: PostgresMeta = new PostgresMeta({
...DEFAULT_POOL_CONFIG,
connectionString: PG_CONNECTION,
Expand All @@ -50,27 +68,27 @@ if (EXPORT_DOCS) {
pgMeta.schemas.list(),
pgMeta.tables.list({
includedSchemas:
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
includeColumns: false,
}),
pgMeta.views.list({
includedSchemas:
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
includeColumns: false,
}),
pgMeta.materializedViews.list({
includedSchemas:
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
includeColumns: false,
}),
pgMeta.columns.list({
includedSchemas:
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
}),
pgMeta.relationships.list(),
pgMeta.functions.list({
includedSchemas:
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
GENERATE_TYPES_INCLUDED_SCHEMAS.length > 0 ? GENERATE_TYPES_INCLUDED_SCHEMAS : undefined,
}),
pgMeta.types.list({
includeArrayTypes: true,
Expand Down Expand Up @@ -104,29 +122,22 @@ if (EXPORT_DOCS) {
throw new Error(typesError.message)
}

console.log(
applyTypescriptTemplate({
schemas: schemas!.filter(
return apply({
schemas: schemas!.filter(
({ name }) =>
GENERATE_TYPES_INCLUDED_SCHEMAS.length === 0 ||
GENERATE_TYPES_INCLUDED_SCHEMAS.includes(name)
),
tables: tables!,
views: views!,
materializedViews: materializedViews!,
columns: columns!,
relationships: relationships!,
functions: functions!.filter(
GENERATE_TYPES_INCLUDED_SCHEMAS.length === 0 ||
GENERATE_TYPES_INCLUDED_SCHEMAS.includes(name)
),
tables: tables!,
views: views!,
materializedViews: materializedViews!,
columns: columns!,
relationships: relationships!,
functions: functions!.filter(
({ return_type }) => !['trigger', 'event_trigger'].includes(return_type)
),
types: types!.filter(({ name }) => name[0] !== '_'),
arrayTypes: types!.filter(({ name }) => name[0] === '_'),
detectOneToOneRelationships: GENERATE_TYPES_DETECT_ONE_TO_ONE_RELATIONSHIPS,
})
)
} else {
app.listen({ port: PG_META_PORT, host: PG_META_HOST }, () => {
const adminPort = PG_META_PORT + 1
adminApp.listen({ port: adminPort, host: PG_META_HOST })
),
types: types!.filter(({ name }) => name[0] !== '_'),
arrayTypes: types!.filter(({ name }) => name[0] === '_'),
detectOneToOneRelationships: GENERATE_TYPES_DETECT_ONE_TO_ONE_RELATIONSHIPS,
})
}
28 changes: 28 additions & 0 deletions src/server/templates/_common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {PostgresFunction, PostgresType} from "../../lib/index.js";

export const filterFromSchema = <T extends {schema: string, name: string}>(items: T[], schemaName: string): T[] => {
return items.filter((item) => item.schema === schemaName).sort(({name: a}, {name: b}) => a.localeCompare(b))
}

export const filterSchemaFunctions = (functions: PostgresFunction[], schemaName: string): PostgresFunction[] => {
return functions
.filter((func) => {
if (func.schema !== schemaName) {
return false
}

// Either:
// 1. All input args are be named, or
// 2. There is only one input arg which is unnamed
const inArgs = func.args.filter(({ mode }) => ['in', 'inout', 'variadic'].includes(mode))

return inArgs.length === 1 || !inArgs.some(({ name }) => name === '')
})
.sort(({ name: a }, { name: b }) => a.localeCompare(b))
}

export const filterSchemaEnums = (types: PostgresType[], schemaName: string): PostgresType[] =>
types
.filter((type) => type.schema === schemaName && type.enums.length > 0)
.sort(({ name: a }, { name: b }) => a.localeCompare(b))

36 changes: 5 additions & 31 deletions src/server/templates/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
PostgresType,
PostgresView,
} from '../../lib/index.js'
import {filterFromSchema, filterSchemaEnums, filterSchemaFunctions} from "./_common.js";

export const apply = ({
schemas,
Expand Down Expand Up @@ -48,37 +49,10 @@ export interface Database {
${schemas
.sort(({ name: a }, { name: b }) => a.localeCompare(b))
.map((schema) => {
const schemaTables = tables
.filter((table) => table.schema === schema.name)
.sort(({ name: a }, { name: b }) => a.localeCompare(b))
const schemaViews = [...views, ...materializedViews]
.filter((view) => view.schema === schema.name)
.sort(({ name: a }, { name: b }) => a.localeCompare(b))
const schemaFunctions = functions
.filter((func) => {
if (func.schema !== schema.name) {
return false
}

// Either:
// 1. All input args are be named, or
// 2. There is only one input arg which is unnamed
const inArgs = func.args.filter(({ mode }) => ['in', 'inout', 'variadic'].includes(mode))

if (!inArgs.some(({ name }) => name === '')) {
return true
}

if (inArgs.length === 1) {
return true
}

return false
})
.sort(({ name: a }, { name: b }) => a.localeCompare(b))
const schemaEnums = types
.filter((type) => type.schema === schema.name && type.enums.length > 0)
.sort(({ name: a }, { name: b }) => a.localeCompare(b))
const schemaTables = filterFromSchema(tables, schema.name)
const schemaViews = filterFromSchema([...views, ...materializedViews], schema.name)
const schemaFunctions = filterSchemaFunctions(functions, schema.name)
const schemaEnums = filterSchemaEnums(types, schema.name)
const schemaCompositeTypes = types
.filter((type) => type.schema === schema.name && type.attributes.length > 0)
.sort(({ name: a }, { name: b }) => a.localeCompare(b))
Expand Down
Loading