Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion graphile/node-type-registry/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"build:dev": "makage build --dev",
"lint": "eslint . --fix",
"test": "jest",
"test:watch": "jest --watch"
"test:watch": "jest --watch",
"generate:seed": "ts-node src/codegen/generate-seed.ts"
},
"dependencies": {
"@pgsql/types": "^17.6.2",
Expand Down
135 changes: 128 additions & 7 deletions graphile/node-type-registry/src/codegen/generate-seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,16 @@
* suitable for use as separate pgpm migration files.
*
* Usage:
* npx ts-node src/codegen/generate-seed.ts [--outdir <dir>] [--single]
* npx ts-node src/codegen/generate-seed.ts [--outdir <dir>] [--single] [--pgpm <dir>]
*
* --outdir <dir> Directory to write individual SQL files (default: stdout)
* --single Emit a single combined seed.sql instead of per-node files
* --pgpm <dir> Generate deploy/revert/verify files in pgpm package layout.
* <dir> is the pgpm package root (e.g. packages/metaschema).
* Files are written relative to this root at:
* deploy/schemas/metaschema_public/tables/node_type_registry/data/seed.sql
* revert/schemas/metaschema_public/tables/node_type_registry/data/seed.sql
* verify/schemas/metaschema_public/tables/node_type_registry/data/seed.sql
*
* Examples:
* # Print all INSERT statements to stdout
Expand All @@ -19,6 +25,9 @@
*
* # Generate a single combined seed file
* npx ts-node src/codegen/generate-seed.ts --single --outdir ./deploy
*
* # Generate pgpm deploy/revert/verify files
* npx ts-node src/codegen/generate-seed.ts --pgpm ../../constructive-db/packages/metaschema
*/

import { writeFileSync, mkdirSync, existsSync } from 'fs';
Expand All @@ -29,6 +38,13 @@ import type { Node } from '@pgsql/types';
import { allNodeTypes } from '../index';
import type { NodeTypeDefinition } from '../types';

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

const MIGRATION_PATH =
'schemas/metaschema_public/tables/node_type_registry/data/seed';

// ---------------------------------------------------------------------------
// AST helpers
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -119,20 +135,123 @@ function buildInsertStmt(nt: NodeTypeDefinition): Node {
};
}

// ---------------------------------------------------------------------------
// pgpm file generators
// ---------------------------------------------------------------------------

const GENERATED_HEADER = [
'-- GENERATED FILE — DO NOT EDIT',
'--',
'-- Regenerate with:',
'-- cd graphile/node-type-registry && pnpm generate:seed --pgpm ../../constructive-db/packages/metaschema',
'--',
'',
].join('\n');

async function buildDeploySql(): Promise<string> {
const header = [
`-- Deploy ${MIGRATION_PATH} to pg`,
'',
GENERATED_HEADER,
'-- requires: schemas/metaschema_public/tables/node_type_registry/table',
'',
].join('\n');

const stmts = allNodeTypes.map(buildInsertStmt);
const body = await deparse(stmts);

return header + body + '\n';
}

function buildRevertSql(): string {
const names = allNodeTypes.map((nt) => ` '${nt.name}'`);

// Wrap names at ~4 per line for readability
const chunks: string[] = [];
for (let i = 0; i < names.length; i += 4) {
chunks.push(names.slice(i, i + 4).join(', '));
}

return [
`-- Revert ${MIGRATION_PATH} from pg`,
'',
GENERATED_HEADER,
'DELETE FROM metaschema_public.node_type_registry',
'WHERE name IN (',
chunks.join(',\n'),
');',
'',
].join('\n');
}

function buildVerifySql(): string {
// Pick one representative from each category
const categories = new Map<string, NodeTypeDefinition>();
for (const nt of allNodeTypes) {
if (!categories.has(nt.category)) {
categories.set(nt.category, nt);
}
}

const checks = Array.from(categories.values()).map(
(nt) =>
`SELECT 1 FROM metaschema_public.node_type_registry WHERE name = '${nt.name}';`
);

return [
`-- Verify ${MIGRATION_PATH} on pg`,
'',
GENERATED_HEADER,
...checks,
'',
].join('\n');
}

// ---------------------------------------------------------------------------
// File writer helper
// ---------------------------------------------------------------------------

function writeFile(filePath: string, content: string): void {
const dir = join(filePath, '..');
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
writeFileSync(filePath, content);
}

// ---------------------------------------------------------------------------
// CLI
// ---------------------------------------------------------------------------

function main() {
async function main() {
const args = process.argv.slice(2);
const outdirIdx = args.indexOf('--outdir');
const outdir = outdirIdx !== -1 ? args[outdirIdx + 1] : undefined;
const single = args.includes('--single');
const pgpmIdx = args.indexOf('--pgpm');
const pgpmRoot = pgpmIdx !== -1 ? args[pgpmIdx + 1] : undefined;

// --pgpm mode: generate deploy/revert/verify in pgpm package layout
if (pgpmRoot) {
const relPath = 'schemas/metaschema_public/tables/node_type_registry/data/seed.sql';

const deployPath = join(pgpmRoot, 'deploy', relPath);
const revertPath = join(pgpmRoot, 'revert', relPath);
const verifyPath = join(pgpmRoot, 'verify', relPath);

writeFile(deployPath, await buildDeploySql());
writeFile(revertPath, buildRevertSql());
writeFile(verifyPath, buildVerifySql());

console.log(`Wrote ${allNodeTypes.length} node types to pgpm layout:`);
console.log(` deploy: ${deployPath}`);
console.log(` revert: ${revertPath}`);
console.log(` verify: ${verifyPath}`);
return;
}

if (single) {
// Emit all INSERT statements as a single SQL string
const stmts = allNodeTypes.map(buildInsertStmt);
const sql = deparse(stmts);
const sql = await deparse(stmts);
if (outdir) {
if (!existsSync(outdir)) mkdirSync(outdir, { recursive: true });
writeFileSync(join(outdir, 'seed.sql'), sql + '\n');
Expand All @@ -144,10 +263,12 @@ function main() {
}

// Emit individual SQL files per node type
const stmts = allNodeTypes.map((nt) => ({
nt,
sql: deparse([buildInsertStmt(nt)]),
}));
const stmts = await Promise.all(
allNodeTypes.map(async (nt) => ({
nt,
sql: await deparse([buildInsertStmt(nt)]),
}))
);

if (outdir) {
if (!existsSync(outdir)) mkdirSync(outdir, { recursive: true });
Expand Down
Loading