Files
T
tmu 400fe902e0 ♻️ Run doc-test generation as an explicit CI step
Drop the `pretest:ci` lifecycle hook and call `create:doc-tests` from the
`build` job before `test:ci`. The hook hid the step from the job log and
made `test:ci` behave differently under npm than when run directly.
2026-09-24 13:08:45 +00:00

3.1 KiB

Docs

Why the prose documentation is maintained the way it is. The actionable rules are in CONTRIBUTING.md; this file records the rationale.

Validating Markdown code fences

Decision (2026-11)

Compile every ts / typescript fence in the prose docs into a real node:test case under src/doc-test/__generated__/, typechecked by a scoped tsc project and executed by node --test.

Why

  • A documented example is a promise about the API. Left unchecked it drifts the moment a signature changes, and a reader copies broken code.
  • The repo runs TypeScript 7, the native/Go compiler. It exposes no legacy JS compiler API (ts.createProgram, ts.transpileModule, ts.createSourceFile are all undefined; Object.keys(require("typescript")) is ["version", "versionMajorMinor"]). So @typescript/vfs, the type-aware eslint-plugin-markdown and docs-ts / @effect/docgen cannot run here.
  • node --check parses as JS and rejects valid TS type annotations, so it is not a gate. The only faithful validator is the tsc CLI, which means emitting real .ts files and letting the existing check:tsc / node --test pipeline judge them.

Rejected

  • A packaged doc-test tool (see above) — no usable compiler API on TS 7.
  • Embedding a typecheck in the generator — duplicates the gate, and would not exercise the repo's own resolution.
  • node --check — wrong language level.

Known issue

  • Scanned sources are hard-coded to README.md and CONTRIBUTING.md. A docs/ + examples/ list is the natural extension; development/ must never be scanned (its fences are illustrative, not compilable).
  • The generator hoists and merges leading imports, rewrites tiny-pattern-ts to the #test-tiny-pattern-ts source alias, and rejects an example that imports node:assert / node:test (the prelude already binds both). Titles are the immediately preceding paragraph; a fence with no such paragraph is a fatal error, which keeps every example described.
  • oxlint src/doc-test reports "No files found" because the generated *.test.ts are gitignored. That is cosmetic: the files are still typechecked and run.
  • The generated tests are *.test.ts, which c8's default excludes already keep out of the --100 gate. Do not add an --exclude for them: passing any --exclude replaces the defaults, so every hand-written test file and __tests__/ helper re-enters coverage and the gate fails.
  • Generated examples must not run under c8: an example could cover a line no hand-written test reaches, so the coverage gate would pass on documentation alone. test:coverage therefore runs c8 over the tracked tests only (git ls-files 'src/*.test.ts' — the generated files are untracked), and test:doc runs the examples in a separate process without c8. test:ci chains the two, so correctness and coverage stay independent.
  • The CI build job runs npm run create:doc-tests as an explicit step before npm run test:ci, not a pretest:ci lifecycle hook: the hook hides the step from the job log and makes test:ci behave differently under npm than when run directly.