✨ Add watch tier with watch:test child
Adds the earliest feedback tier to the ladder: `npm run watch` re-runs tests on file save, started manually in a dedicated terminal pane. Built on Node 26's native `node --test --watch` (no new dependency). Structure follows the existing prefix convention: - `watch:test` — runs the test suite in watch mode - `watch` — umbrella that aggregates `watch:*` children (currently just `watch:test`; future `watch:oxlint` etc. would aggregate here, switching to concurrent execution) CONTRIBUTING.md documents the new tier in three places: the prefix convention list, the feedback tiers table (new top row), and a 'Why these splits?' bullet explaining why watch is a manual tier rather than a hook. README's Development section gains a one-line pointer.
This commit is contained in:
1 parent
538272c222
commit
8924dd67d3
3 files changed
+6
No files matched your search
@@ -9,6 +9,7 @@ Script names in `package.json` use a prefix that signals _when_ the script is in
|
|||||||
- `check:*` — read-only verification. Aggregated by `npm run check`. Used in pre-commit hooks (on staged files) and CI's build job (on the whole project). Read-only; never modifies files.
|
- `check:*` — read-only verification. Aggregated by `npm run check`. Used in pre-commit hooks (on staged files) and CI's build job (on the whole project). Read-only; never modifies files.
|
||||||
- `fix:*` — mutating counterpart of a `check:*` script. Aggregated by `npm run fix`. Use after `npm run check` to auto-resolve issues; the diff is the review surface.
|
- `fix:*` — mutating counterpart of a `check:*` script. Aggregated by `npm run fix`. Use after `npm run check` to auto-resolve issues; the diff is the review surface.
|
||||||
- `test:*` — test scripts. `npm run test` is the canonical entry point (`check:tsc` + unit tests); `test:unit` skips the typecheck for fast local iteration; `test:ci` adds c8 coverage and is the CI variant.
|
- `test:*` — test scripts. `npm run test` is the canonical entry point (`check:tsc` + unit tests); `test:unit` skips the typecheck for fast local iteration; `test:ci` adds c8 coverage and is the CI variant.
|
||||||
|
- `watch:*` — long-running watchers, started manually via `npm run watch` for the inner dev loop. Sits "before" pre-commit in the feedback ladder (see Feedback tiers below). Currently a single child (`watch:test`); future `watch:oxlint` / `watch:tsc` would aggregate under the same `watch` umbrella.
|
||||||
- `publish:*` — runs only at publish time, in the CI `publish` job (immediately before `npm publish`). There is **no** local `npm run publish` script — publishing is CI-only by policy. The `publish:` prefix still documents intent: this script validates the _publishable artifact_ (e.g., `dist/`) rather than the source.
|
- `publish:*` — runs only at publish time, in the CI `publish` job (immediately before `npm publish`). There is **no** local `npm run publish` script — publishing is CI-only by policy. The `publish:` prefix still documents intent: this script validates the _publishable artifact_ (e.g., `dist/`) rather than the source.
|
||||||
|
|
||||||
A new script should pick the prefix that matches its lifecycle, not invent a new one. If no existing prefix fits, that's a signal the script doesn't belong in the standard pipeline.
|
A new script should pick the prefix that matches its lifecycle, not invent a new one. If no existing prefix fits, that's a signal the script doesn't belong in the standard pipeline.
|
||||||
@@ -19,6 +20,7 @@ The tools are organized into a feedback ladder. Each tier catches different thin
|
|||||||
|
|
||||||
| Tier | When | What it runs | Time |
|
| Tier | When | What it runs | Time |
|
||||||
| ----------------- | ---------- | ----------------------------------------------------------- | ----- |
|
| ----------------- | ---------- | ----------------------------------------------------------- | ----- |
|
||||||
|
| `npm run watch` | manual | `watch:test` — re-runs tests on file save | ~0.1s |
|
||||||
| Pre-commit (auto) | on stage | tsc + oxlint + oxfmt + cspell (staged files only) | ~1.3s |
|
| Pre-commit (auto) | on stage | tsc + oxlint + oxfmt + cspell (staged files only) | ~1.3s |
|
||||||
| Pre-push (auto) | on push | `npm test` (full tsc + unit tests) | ~3.5s |
|
| Pre-push (auto) | on push | `npm test` (full tsc + unit tests) | ~3.5s |
|
||||||
| `npm run check` | manual | Full project audit: all `check:*` scripts + knip + outdated | ~10s |
|
| `npm run check` | manual | Full project audit: all `check:*` scripts + knip + outdated | ~10s |
|
||||||
@@ -28,6 +30,7 @@ The tools are organized into a feedback ladder. Each tier catches different thin
|
|||||||
|
|
||||||
### Why these splits?
|
### Why these splits?
|
||||||
|
|
||||||
|
- **`watch:*` is a manual tier, not a hook.** The developer starts it on demand (it has to be killed with Ctrl-C) and it runs in a dedicated terminal pane. It sits as the earliest tier in the feedback ladder, catching failures the moment a file is saved — before staging, before commit. The umbrella `watch` script is designed to aggregate multiple `watch:*` children (currently just `watch:test`); if more watchers are added later (e.g. `watch:oxlint`), the umbrella would switch to running them concurrently rather than sequentially.
|
||||||
- **`check:tsc`, `check:oxlint`, `check:oxfmt`, `check:cspell`** are in pre-commit because they are fast (~0.2–0.5s each), fully offline, and naturally scope to staged files via the `LEFTHOOK_FILES` env var convention. They give instant feedback on what you typed.
|
- **`check:tsc`, `check:oxlint`, `check:oxfmt`, `check:cspell`** are in pre-commit because they are fast (~0.2–0.5s each), fully offline, and naturally scope to staged files via the `LEFTHOOK_FILES` env var convention. They give instant feedback on what you typed.
|
||||||
- **`test` (and the `tsc` it includes) is in pre-push** because it runs the whole test suite across the whole project. The pre-commit `LEFTHOOK_FILES` convention doesn't apply to the test runner, so pre-commit isn't the right home. Pre-push runs after all commits are made but before the push leaves the machine, catching regressions that span multiple commits.
|
- **`test` (and the `tsc` it includes) is in pre-push** because it runs the whole test suite across the whole project. The pre-commit `LEFTHOOK_FILES` convention doesn't apply to the test runner, so pre-commit isn't the right home. Pre-push runs after all commits are made but before the push leaves the machine, catching regressions that span multiple commits.
|
||||||
- **`check:knip` and `check:outdated` are NOT in pre-commit** — knip scans the whole project (~4s, would noticeably slow the hook), and `check-outdated` queries the npm registry (~6.5s, network-dependent, advisory not correctness). Both run in `npm run check` and CI; pre-commit stays fast and offline.
|
- **`check:knip` and `check:outdated` are NOT in pre-commit** — knip scans the whole project (~4s, would noticeably slow the hook), and `check-outdated` queries the npm registry (~6.5s, network-dependent, advisory not correctness). Both run in `npm run check` and CI; pre-commit stays fast and offline.
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ Pattern matching for TypeScript/ESM environments (F#-style, not regex).
|
|||||||
|
|
||||||
- **Build:** `npm run build`
|
- **Build:** `npm run build`
|
||||||
- **Test:** `npm run test`, `npm run test:ci`
|
- **Test:** `npm run test`, `npm run test:ci`
|
||||||
|
- **Watch:** `npm run watch` (re-runs tests on file save; the earliest feedback tier)
|
||||||
- **Checks:** `npm run check`, `npm run fix`
|
- **Checks:** `npm run check`, `npm run fix`
|
||||||
- **Individual fixes:** `npm run fix:oxfmt`, `npm run fix:oxlint`
|
- **Individual fixes:** `npm run fix:oxfmt`, `npm run fix:oxlint`
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,8 @@
|
|||||||
"test": "npm run check:tsc && node --test --strip-types \"src/**/*.test.ts\"",
|
"test": "npm run check:tsc && node --test --strip-types \"src/**/*.test.ts\"",
|
||||||
"test:ci": "c8 --reporter=text --reporter=lcov --reporter=html node --test --strip-types \"src/**/*.test.ts\"",
|
"test:ci": "c8 --reporter=text --reporter=lcov --reporter=html node --test --strip-types \"src/**/*.test.ts\"",
|
||||||
"test:unit": "node --test --strip-types \"src/**/*.test.ts\"",
|
"test:unit": "node --test --strip-types \"src/**/*.test.ts\"",
|
||||||
|
"watch": "npm run watch:test",
|
||||||
|
"watch:test": "node --test --watch --strip-types \"src/**/*.test.ts\"",
|
||||||
"publish:attw": "attw . --pack --profile esm-only",
|
"publish:attw": "attw . --pack --profile esm-only",
|
||||||
"publish:publint": "publint",
|
"publish:publint": "publint",
|
||||||
"use:git-commit-message": "cp commit-message-template .git/COMMIT_EDITMSG || true"
|
"use:git-commit-message": "cp commit-message-template .git/COMMIT_EDITMSG || true"
|
||||||
|
|||||||
Reference in new issue
Block a user