Skip to content

Use tsconfig project references so typecheck is a single incremental build #17

Description

@AlaskanTuna

Root typecheck is four sequential compilations plus a manual pre-build:

pnpm --filter @shared/types build && backend tsc --noEmit && frontend tsc --noEmit && pdf-service tsc --noEmit

Every one of those starts cold, and the pnpm --filter @shared/types build prefix is hand-copied into five scripts (typecheck, test, build, dev:backend, dev:frontend) — the build order lives in string concatenation instead of in the compiler. The scaffolding for the real fix is already half-present: shared/tsconfig.json is composite: true, and both backend and frontend already declare references: [{ path: "../shared" }]. What is missing is a root solution file, composite on the projects that can carry it, and a typecheck script that actually uses build mode.

Scope: tsconfig.json, tsconfig.build.json (new, if a separate solution file is cleaner than reusing root), shared/tsconfig.json, backend/tsconfig.json, frontend/tsconfig.json, package.json

Depends on: #15 — land after CI enforces typecheck, so the change is validated by a green pipeline rather than by assertion

Checklist

  • Add composite: true to backend/tsconfig.json; leave frontend non-composite (TS5053 — noEmit and composite are mutually exclusive, and the frontend genuinely wants noEmit)
  • Add a root solution config with "files": [] and "references" to shared, backend, and frontend, so build order is expressed once, in the compiler, rather than five times in npm scripts
  • Decide and record whether the root paths mapping "@shared/types": ["./shared/index.ts"] stays. It is compatible with project references — TypeScript redirects an import that resolves into a referenced composite project to that project's declaration output — but it means two resolution mechanisms coexist (paths at compile time, the workspace symlink plus exports.types at runtime). Recommendation: keep it (smaller change, preserves go-to-definition landing on source), and prove it works from cold rather than assuming
  • Replace the four-command typecheck script with build mode. Verify empirically which form TypeScript 5.9.3 accepts — tsc -b --noEmit was historically rejected as TS5094 and support was added later, so test it rather than trusting either this plan or memory. If -b --noEmit is unavailable, plain tsc -b is an acceptable substitute: it emits exactly the artifacts build needs anyway
  • Keep the services/pdf-service leg as its own explicit invocation and leave a one-line comment saying why it is outside the solution — the next reader will otherwise "fix" it by folding it in and break the isolated Vercel deploy
  • Remove the now-redundant pnpm --filter @shared/types build prefixes from typecheck, test, and build once references make them unnecessary. Leave dev:backend / dev:frontend alone unless watch mode is verified end to end — those exist so a cold dev has shared/dist present, and breaking hot reload to save a string is a bad trade
  • verify: from a genuinely cold tree (rm -rf shared/dist backend/dist **/*.tsbuildinfo), one typecheck command typechecks all three workspaces in dependency order and exits 0
  • verify: the second consecutive run is measurably faster than the first — record both wall-clock numbers in the issue. This is the entire point of the task; if the numbers are the same, incrementality is not actually on
  • verify: a deliberate type error introduced in shared/index.ts fails both the backend and frontend legs — this proves the reference graph is wired, not just the paths alias
  • verify: a deliberate type error in backend/src fails, and one in services/pdf-service/api still fails — the pdf-service leg must not be silently dropped while restructuring
  • verify: build still produces a working backend/dist/server.js and frontend/dist SPA, test stays green with unchanged counts, and dev still hot-reloads a shared/ edit through to both backend and frontend

Priority: Low · Phase 12 task 20 of the maintainer’s migration plan.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3-lowNice to havetoolingBuild, lint, monorepo, developer workflow

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions