Files
ersatztv/web/tsconfig.node.json
T
timothy 63afc5f743 fix(819): derive the SPA page-size guard's population from the git index
`pageSizeCallSites.guard.test.ts` asserts EXACT completeness over the files it
discovers, but discovered them with `import.meta.glob` over `web/src`. A
directory walk is not an authoritative source: an untracked `.ts`/`.tsx` — a
scratch file, a half-finished component, an editor dropping — entered the
population and failed as unregistered on that checkout, while CI, which only
ever checks out tracked files, stayed green. Developer-red / CI-green, the #778
shape #806 removed from every other guard here.

Witnessed before the fix: planting an untracked `Scratch819Untracked.tsx` with a
`pageSize: 25` literal reddened the guard with
`UNREGISTERED (1) — screens/Scratch819Untracked.tsx:literal:25`.

The glob stays, as the source of file CONTENT. The POPULATION is now
`git ls-files`, read by `web/vite-plugins/trackedSourceFiles.ts` in Vite's own
Node context and handed to the app project as the virtual module
`virtual:etv-tracked-source-files`. That is what reaches the index without
admitting `@types/node` to `tsconfig.app.json` — the obstacle that deferred this
in #818, since wiring those types in was tried and reverted (it leaked Node's
ambient `setTimeout` into the app project and broke three unrelated
`window.setTimeout` mocks).

The plugin fails rather than degrading: a failed or empty `git ls-files` throws,
because answering "could not tell" with the very population the index replaced
is a permanent no-op that reports green (#772's shape). It reads the index
lazily, inside `load()`, so `vite build` — including the Docker image build,
whose context carries no `.git` — never shells out to git. Verified by building
in a copy of `web/` outside any repository.

Proofs, all executed:
- clause mutation: deleting `if (!isTrackedSourcePath(...))` from
  `listSourceFilesFrom` reddens exactly one named test, `EXCLUDES an untracked
  file that matches the scope in every other way (#819)`, and nothing else. The
  pipeline was extracted to take injected inputs precisely so that clause is
  provable — over real source on a clean checkout it removes nothing, so a
  plant-a-real-file check pins nothing, the same argument
  `isScannableSourceFileName` already records.
- negative control: the same planted file, once `git add`ed, is caught again.
  Untracked is ignored, tracked is caught — the guard did not go blind.
- positive control against a clause mutated to reject everything.
- anti-vacuity on the consuming side: the real index is >50 paths and contains
  named known files, so a plugin that stopped being registered cannot pass an
  empty population off as a satisfied one.

Residual, stated rather than implied: a file tracked but DELETED in the working
tree has no content and drops out. Not asserted — an unstaged deletion is a
normal developer state, and a guard red on one gets ignored (#806) — and a
REGISTERED site in such a file still fails loudly on the STALE direction.

fixes #819
2026-08-29 05:12:23 +02:00

19 lines
743 B
JSON

{
"compilerOptions": {
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
"target": "ES2023",
"lib": ["ES2023"],
"module": "ESNext",
"moduleResolution": "Bundler",
"allowSyntheticDefaultImports": true,
"strict": true,
"noEmit": true,
"skipLibCheck": true,
"types": ["node"]
},
// The Playwright UI-E2E config + specs (ersatztv#445) belong here, not in tsconfig.app.json: they
// run in Node (not the bundled browser app), and `tsc -b` then type-checks them as part of
// `npm run build`, so a broken spec fails the build instead of only failing at test time.
"include": ["vite.config.ts", "eslint.config.js", "playwright.config.ts", "e2e/**/*.ts", "vite-plugins/**/*.ts"]
}