Files
pn-new-crm/scripts/tsc-staged.mjs

68 lines
2.1 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/**
* Pre-commit type check for staged TS files.
*
* Writes a temp tsconfig that extends the project root and pins
* `files` to whatever lint-staged passed in. `tsc -p` then compiles
* the whole dep graph from those entrypoints catches errors in
* the staged code AND in anything it imports while still skipping
* the 22s full-project pass.
*
* Replaces `tsc-files` (npm), which silently fails under pnpm because
* its tsc-resolution path (typescript/../.bin/tsc) doesn't exist in
* pnpm's virtual store layout.
*/
import { spawnSync } from 'node:child_process';
feat(audit-wave-1): real db:migrate runner with CONCURRENTLY support Closes Wave 1.1 (CRITICAL): the production-grade migration runner the audit flagged as missing. Why drizzle-kit migrate alone wasn't enough: - Wraps every migration in a single transaction. Postgres forbids CREATE INDEX CONCURRENTLY inside a transaction (25001), so the 6 composite indexes in 0052_audit_critical_fixes.sql never landed in prod. - db:push silently diverges from migration-tracked truth on DDL the kit can't infer from the schema (CHECK constraints, partial unique indexes, the berth-pdf circular FK). scripts/db-migrate.ts: - Reads journal-ordered migrations from src/lib/db/migrations. - Tracks applied state in drizzle.__drizzle_migrations (same schema Drizzle's own tools use). - Splits each migration on `--> statement-breakpoint`. - Classifies each statement: CREATE/REINDEX/DROP INDEX CONCURRENTLY → outside transaction; everything else → batched in one tx per migration. Transactional batch runs first, CONCURRENTLY second. Three modes: - `pnpm db:migrate` — apply pending migrations - `pnpm db:migrate:status` — diff applied vs disk - `pnpm db:migrate:baseline` — mark all as applied without running them. Use ONCE per env when schema was bootstrapped via db:push. Also fixes scripts/tsc-staged.mjs: temp tsconfig now lives in `node_modules/.cache/tsc-staged/` (was /tmp) AND explicitly lists `types: [node, react, react-dom]` so @types/* auto-resolution works when `include: []` short-circuits TS's default discovery. For the existing prod cutover: After `db:migrate:baseline`, manually verify 0052's composite indexes exist: SELECT indexname FROM pg_indexes WHERE indexname IN ('idx_files_port_client', 'idx_files_port_company', 'idx_files_port_yacht', 'idx_docs_port_client', 'idx_docs_port_company', 'idx_docs_port_yacht'); If missing, paste 0052's CREATE INDEX CONCURRENTLY statements into a `psql` session directly (each runs OUTSIDE a transaction). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 00:04:52 +02:00
import { mkdtempSync, rmSync, writeFileSync, mkdirSync } from 'node:fs';
import { join, relative, resolve } from 'node:path';
const cwd = process.cwd();
const args = process.argv.slice(2);
const files = args.filter((a) => /\.(ts|tsx)$/.test(a));
if (files.length === 0) {
process.exit(0);
}
feat(audit-wave-1): real db:migrate runner with CONCURRENTLY support Closes Wave 1.1 (CRITICAL): the production-grade migration runner the audit flagged as missing. Why drizzle-kit migrate alone wasn't enough: - Wraps every migration in a single transaction. Postgres forbids CREATE INDEX CONCURRENTLY inside a transaction (25001), so the 6 composite indexes in 0052_audit_critical_fixes.sql never landed in prod. - db:push silently diverges from migration-tracked truth on DDL the kit can't infer from the schema (CHECK constraints, partial unique indexes, the berth-pdf circular FK). scripts/db-migrate.ts: - Reads journal-ordered migrations from src/lib/db/migrations. - Tracks applied state in drizzle.__drizzle_migrations (same schema Drizzle's own tools use). - Splits each migration on `--> statement-breakpoint`. - Classifies each statement: CREATE/REINDEX/DROP INDEX CONCURRENTLY → outside transaction; everything else → batched in one tx per migration. Transactional batch runs first, CONCURRENTLY second. Three modes: - `pnpm db:migrate` — apply pending migrations - `pnpm db:migrate:status` — diff applied vs disk - `pnpm db:migrate:baseline` — mark all as applied without running them. Use ONCE per env when schema was bootstrapped via db:push. Also fixes scripts/tsc-staged.mjs: temp tsconfig now lives in `node_modules/.cache/tsc-staged/` (was /tmp) AND explicitly lists `types: [node, react, react-dom]` so @types/* auto-resolution works when `include: []` short-circuits TS's default discovery. For the existing prod cutover: After `db:migrate:baseline`, manually verify 0052's composite indexes exist: SELECT indexname FROM pg_indexes WHERE indexname IN ('idx_files_port_client', 'idx_files_port_company', 'idx_files_port_yacht', 'idx_docs_port_client', 'idx_docs_port_company', 'idx_docs_port_yacht'); If missing, paste 0052's CREATE INDEX CONCURRENTLY statements into a `psql` session directly (each runs OUTSIDE a transaction). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 00:04:52 +02:00
// Temp tsconfig lives inside the project tree (not /tmp) so @types/*
// resolution walks up to node_modules. tsc's "atTypes" auto-discovery
// is anchored to the tsconfig's directory, so a temp config in /tmp
// would miss our @types/node, @types/react, etc.
const baseDir = join(cwd, 'node_modules/.cache/tsc-staged');
mkdirSync(baseDir, { recursive: true });
const tmpDir = mkdtempSync(join(baseDir, 'run-'));
const tmpConfig = join(tmpDir, 'tsconfig.json');
const relFiles = files.map((f) => relative(tmpDir, resolve(cwd, f)));
writeFileSync(
tmpConfig,
JSON.stringify(
{
extends: relative(tmpDir, join(cwd, 'tsconfig.json')),
feat(audit-wave-1): real db:migrate runner with CONCURRENTLY support Closes Wave 1.1 (CRITICAL): the production-grade migration runner the audit flagged as missing. Why drizzle-kit migrate alone wasn't enough: - Wraps every migration in a single transaction. Postgres forbids CREATE INDEX CONCURRENTLY inside a transaction (25001), so the 6 composite indexes in 0052_audit_critical_fixes.sql never landed in prod. - db:push silently diverges from migration-tracked truth on DDL the kit can't infer from the schema (CHECK constraints, partial unique indexes, the berth-pdf circular FK). scripts/db-migrate.ts: - Reads journal-ordered migrations from src/lib/db/migrations. - Tracks applied state in drizzle.__drizzle_migrations (same schema Drizzle's own tools use). - Splits each migration on `--> statement-breakpoint`. - Classifies each statement: CREATE/REINDEX/DROP INDEX CONCURRENTLY → outside transaction; everything else → batched in one tx per migration. Transactional batch runs first, CONCURRENTLY second. Three modes: - `pnpm db:migrate` — apply pending migrations - `pnpm db:migrate:status` — diff applied vs disk - `pnpm db:migrate:baseline` — mark all as applied without running them. Use ONCE per env when schema was bootstrapped via db:push. Also fixes scripts/tsc-staged.mjs: temp tsconfig now lives in `node_modules/.cache/tsc-staged/` (was /tmp) AND explicitly lists `types: [node, react, react-dom]` so @types/* auto-resolution works when `include: []` short-circuits TS's default discovery. For the existing prod cutover: After `db:migrate:baseline`, manually verify 0052's composite indexes exist: SELECT indexname FROM pg_indexes WHERE indexname IN ('idx_files_port_client', 'idx_files_port_company', 'idx_files_port_yacht', 'idx_docs_port_client', 'idx_docs_port_company', 'idx_docs_port_yacht'); If missing, paste 0052's CREATE INDEX CONCURRENTLY statements into a `psql` session directly (each runs OUTSIDE a transaction). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 00:04:52 +02:00
compilerOptions: {
noEmit: true,
skipLibCheck: true,
// Explicitly list `types` so the @types/* auto-discovery
// finds them — without this, the temp-tsconfig location
// anchors discovery to .cache/ and misses node/react/etc.
types: ['node', 'react', 'react-dom'],
},
files: relFiles,
include: [],
},
null,
2,
),
);
const tsc = spawnSync('pnpm', ['exec', 'tsc', '-p', tmpConfig, '--pretty'], {
cwd,
stdio: 'inherit',
});
rmSync(tmpDir, { recursive: true, force: true });
process.exit(tsc.status ?? 1);