✨ Compile doc code fences into tests
Every `ts`-tagged fence in README.md / CONTRIBUTING.md now becomes an executed `node:test` case in a gitignored generated file, so a documented example cannot drift from the API. The generator hoists and merges the leading imports, rewrites the library specifier to the `#test-tiny-pattern-ts` alias, and rejects a fence with no describing paragraph or one that re-imports a prelude module. CI regenerates via `pretest:ci`; `create:doc-tests` runs the generator, formats, then typechecks the output against a scoped tsconfig that relaxes `noUnusedLocals`. c8's default excludes already omit the generated `*.test.ts`, so `test:ci` is unchanged.
This commit is contained in:
1 parent
d1963b0329
commit
30d97a9209
15 files changed
+1180
-8
No files matched your search
@@ -24,6 +24,7 @@ One file per category:
|
||||
| [workflow.md](./workflow.md) | Branching and merging, script prefixes, feedback tiers, commit messages |
|
||||
| [tooling.md](./tooling.md) | Toolchain choices and configuration, editor setup |
|
||||
| [testing.md](./testing.md) | Test strategy and type-driven development |
|
||||
| [docs.md](./docs.md) | Validating the Markdown code fences in the prose docs |
|
||||
| [ci.md](./ci.md) | CI pipeline, runner image, coverage serving |
|
||||
| [publishing.md](./publishing.md) | Release and npm publishing |
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
# Docs
|
||||
|
||||
Why the prose documentation is maintained the way it is. The actionable rules
|
||||
are in [CONTRIBUTING.md](../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.
|
||||
@@ -96,7 +96,8 @@ aggregator.
|
||||
|
||||
#### Why
|
||||
|
||||
- Both members create something real: a branch, a release.
|
||||
- Every member creates something real: a branch, a release, the compiled
|
||||
doc-tests.
|
||||
- It joined both lists in [CONTRIBUTING.md](../CONTRIBUTING.md) alongside its
|
||||
first members, so it could not go invisible the way the retired `use:` prefix
|
||||
did.
|
||||
|
||||
Reference in new issue
Block a user