📝 Reconcile project-specs.md and cspell.json with the feature/setup branch
This commit is contained in:
1 parent
1fe3dbe28b
commit
1a749b13d6
2 files changed
+77
-44
No files matched your search
@@ -2,8 +2,6 @@
|
|||||||
"version": "0.2",
|
"version": "0.2",
|
||||||
"language": "en",
|
"language": "en",
|
||||||
"words": [
|
"words": [
|
||||||
"tslib",
|
|
||||||
"typefest",
|
|
||||||
"lefthook",
|
"lefthook",
|
||||||
"oxlint",
|
"oxlint",
|
||||||
"oxfmt",
|
"oxfmt",
|
||||||
|
|||||||
+77
-42
@@ -8,13 +8,28 @@ typescript-lib-starter-tiny => https://github.com/tmueller/typescript-lib-starte
|
|||||||
|
|
||||||
## 1. Development Environment
|
## 1. Development Environment
|
||||||
|
|
||||||
- **TypeScript**: Use strictest practical rules. Inline in `tsconfig.json` (not
|
- **TypeScript**: Use strictest practical rules via `@tsconfig/strictest`
|
||||||
via `@tsconfig/strictest`). The rule set is: `strict`, `noImplicitAny`,
|
(extended from `tsconfig.json`). The project also extends
|
||||||
`noImplicitThis`, `alwaysStrict`, `strictNullChecks`, `strictFunctionTypes`,
|
`@tsconfig/node26` for Node 26's `target`/`module`/`lib` defaults;
|
||||||
`strictBindCallApply`, `strictPropertyInitialization`, `noImplicitReturns`,
|
`tsconfig.json` is the type-check base (`noEmit: true`, includes
|
||||||
`noFallthroughCasesInSwitch`, `noUncheckedIndexedAccess`, `noImplicitOverride`,
|
`src/`), and `tsconfig.build.json` extends it to add the emit-only
|
||||||
`noUnusedLocals`, `noUnusedParameters`, `forceConsistentCasingInFileNames`,
|
options (`declaration`, `sourceMap`, `outDir`, `target: es2024`,
|
||||||
`isolatedModules`, `verbatimModuleSyntax`.
|
`rewriteRelativeImportExtensions: true`) and to exclude test files
|
||||||
|
from emission. The `verbatimModuleSyntax: true` and
|
||||||
|
`allowImportingTsExtensions: true` overrides live in
|
||||||
|
`tsconfig.json` (the latter only works because the base has
|
||||||
|
`noEmit: true`). The strictest flags the project relies on (inherited
|
||||||
|
from `@tsconfig/strictest`) are: `strict`, `noImplicitAny`,
|
||||||
|
`noImplicitThis`, `alwaysStrict`, `strictNullChecks`,
|
||||||
|
`strictFunctionTypes`, `strictBindCallApply`,
|
||||||
|
`strictPropertyInitialization`, `noImplicitReturns`,
|
||||||
|
`noFallthroughCasesInSwitch`, `noUncheckedIndexedAccess`,
|
||||||
|
`noImplicitOverride`, `noUnusedLocals`, `noUnusedParameters`,
|
||||||
|
`forceConsistentCasingInFileNames`, `isolatedModules`,
|
||||||
|
`verbatimModuleSyntax`, plus the additional strictest flags
|
||||||
|
(`exactOptionalPropertyTypes`, `noPropertyAccessFromIndexSignature`,
|
||||||
|
`noUncheckedIndexedAccess` etc.) that `@tsconfig/strictest` enables
|
||||||
|
beyond a plain `strict: true`.
|
||||||
- **EditorConfig**: Use `.editorconfig` from typescript-lib-starter-tiny.
|
- **EditorConfig**: Use `.editorconfig` from typescript-lib-starter-tiny.
|
||||||
- **oxfmt**: Rust-based formatter, Prettier-compatible. Replaces Prettier.
|
- **oxfmt**: Rust-based formatter, Prettier-compatible. Replaces Prettier.
|
||||||
Config in `.oxfmtrc.json` (same shape as `.prettierrc`).
|
Config in `.oxfmtrc.json` (same shape as `.prettierrc`).
|
||||||
@@ -34,6 +49,8 @@ typescript-lib-starter-tiny => https://github.com/tmueller/typescript-lib-starte
|
|||||||
- `eslint/sort-keys` — handler/case order is semantic, not alphabetical.
|
- `eslint/sort-keys` — handler/case order is semantic, not alphabetical.
|
||||||
- `eslint/id-length` — `T`, `R`, `U`, `V` are standard TS generics.
|
- `eslint/id-length` — `T`, `R`, `U`, `V` are standard TS generics.
|
||||||
- `import/no-named-export` — false positive on library entry re-exports.
|
- `import/no-named-export` — false positive on library entry re-exports.
|
||||||
|
- `typescript/method-signature-style` — method signatures in
|
||||||
|
`interface` declarations are conventional TS ergonomics.
|
||||||
- Stylistic rules superseded by oxfmt (oxfmt is the canonical
|
- Stylistic rules superseded by oxfmt (oxfmt is the canonical
|
||||||
formatter; these rules either conflict with its output or
|
formatter; these rules either conflict with its output or
|
||||||
duplicate features oxfmt already provides):
|
duplicate features oxfmt already provides):
|
||||||
@@ -48,8 +65,6 @@ typescript-lib-starter-tiny => https://github.com/tmueller/typescript-lib-starte
|
|||||||
`import { type X, Y }` is intentional for grouping.
|
`import { type X, Y }` is intentional for grouping.
|
||||||
- `unicorn/prefer-export-from` — conflicts with how the
|
- `unicorn/prefer-export-from` — conflicts with how the
|
||||||
barrel `src/index.ts` re-exports through `src/match.ts`.
|
barrel `src/index.ts` re-exports through `src/match.ts`.
|
||||||
- `typescript/method-signature-style` — method signatures
|
|
||||||
in `interface` are conventional TS ergonomics.
|
|
||||||
For `*.test.ts` files additionally: `no-unused-expressions` (for
|
For `*.test.ts` files additionally: `no-unused-expressions` (for
|
||||||
`expectTypeOf(...)` calls), `no-empty-file` (we have a single import
|
`expectTypeOf(...)` calls), `no-empty-file` (we have a single import
|
||||||
per test file in some cases), `import/no-nodejs-modules` (we use
|
per test file in some cases), `import/no-nodejs-modules` (we use
|
||||||
@@ -58,9 +73,9 @@ typescript-lib-starter-tiny => https://github.com/tmueller/typescript-lib-starte
|
|||||||
- **Import sorting**: built into oxfmt (no separate plugin).
|
- **Import sorting**: built into oxfmt (no separate plugin).
|
||||||
- **cspell**: Basic spelling configuration. Words dictionary in `cspell.json`
|
- **cspell**: Basic spelling configuration. Words dictionary in `cspell.json`
|
||||||
covers tooling names (`oxlint`, `oxfmt`, `oxc`, `nodenext`,
|
covers tooling names (`oxlint`, `oxfmt`, `oxc`, `nodenext`,
|
||||||
`oxfmtrc`, `oxlintrc`, `EDITMSG`, `typescriptteam`,
|
`oxfmtrc`, `oxlintrc`, `tsgolint`, `EDITMSG`, `typescriptteam`,
|
||||||
`gitmoji`, `dbaeumer`, `msvc`) and a few library names (`tslib`,
|
`gitmoji`, `dbaeumer`, `msvc`, `publint`, `attw`, `arethetypeswrong`,
|
||||||
`typefest`, `lefthook`).
|
`knip`) and a few library names (`lefthook`).
|
||||||
- **Lefthook**: Pre-commit checks (run in parallel, lefthook v2 schema).
|
- **Lefthook**: Pre-commit checks (run in parallel, lefthook v2 schema).
|
||||||
Single source of truth for the underlying commands is in
|
Single source of truth for the underlying commands is in
|
||||||
`package.json#scripts`; the lefthook config only describes _what to
|
`package.json#scripts`; the lefthook config only describes _what to
|
||||||
@@ -82,16 +97,27 @@ typescript-lib-starter-tiny => https://github.com/tmueller/typescript-lib-starte
|
|||||||
CI and via the explicit `lefthook run outdated` command.
|
CI and via the explicit `lefthook run outdated` command.
|
||||||
|
|
||||||
- **Additional Dev Dependencies**:
|
- **Additional Dev Dependencies**:
|
||||||
- lefthook
|
- lefthook (git hook manager)
|
||||||
- check-outdated
|
- check-outdated (outdated dependency warnings)
|
||||||
|
- knip (unused dependency / export / file detection)
|
||||||
|
- publint (publish-time `package.json` validation)
|
||||||
|
- @arethetypeswrong/cli (attw, publish-time `.d.ts` validation)
|
||||||
|
- @tsconfig/strictest, @tsconfig/node26 (extended from in
|
||||||
|
`tsconfig.json`)
|
||||||
- c8 (coverage for `node --test`)
|
- c8 (coverage for `node --test`)
|
||||||
- expect-type (type-level assertions in tests)
|
- expect-type (type-level assertions in tests)
|
||||||
- oxfmt, oxlint (with their native bindings as `optionalDependencies`
|
- oxfmt, oxlint (with their native bindings as `optionalDependencies`
|
||||||
so the correct binding is selected per platform automatically)
|
so the correct binding is selected per platform automatically)
|
||||||
|
- oxlint-tsgolint (type-aware linter backend, also with native
|
||||||
|
bindings as `optionalDependencies`)
|
||||||
|
- cspell (spell checking)
|
||||||
- typescript (TS 7)
|
- typescript (TS 7)
|
||||||
- @types/node
|
- @types/node
|
||||||
- tslib (available for future runtime helper imports)
|
|
||||||
- type-fest (utility types)
|
`tslib` and `type-fest` were removed (commit `ec98223`): knip
|
||||||
|
flagged them as unused, and they were not appropriate for an
|
||||||
|
ES2024-targeting ESM-only library (`tslib` is a runtime helper for
|
||||||
|
old ES3/ES5 targets; `type-fest` was never imported).
|
||||||
|
|
||||||
## 2. Build & Test
|
## 2. Build & Test
|
||||||
|
|
||||||
@@ -121,10 +147,6 @@ typescript-lib-starter-tiny => https://github.com/tmueller/typescript-lib-starte
|
|||||||
- `target: es2024`, `lib: ["es2024"]` (Node 26 supports all ES2024
|
- `target: es2024`, `lib: ["es2024"]` (Node 26 supports all ES2024
|
||||||
features natively).
|
features natively).
|
||||||
- **Additional Dev Dependencies** (for types and tests):
|
- **Additional Dev Dependencies** (for types and tests):
|
||||||
- `tslib` — available for runtime helper imports; not currently
|
|
||||||
used by source (kept for future use per the original spec).
|
|
||||||
- `type-fest` — utility types; not currently used by source
|
|
||||||
(kept for future use per the original spec).
|
|
||||||
- `@types/node` — types for `node:test`, `node:assert`, `node:fs`.
|
- `@types/node` — types for `node:test`, `node:assert`, `node:fs`.
|
||||||
- **Node Version**: `>=26` (engines field). Pinned via `.node-version`
|
- **Node Version**: `>=26` (engines field). Pinned via `.node-version`
|
||||||
for fnm/nvm/volta/mise auto-switching and CI
|
for fnm/nvm/volta/mise auto-switching and CI
|
||||||
@@ -207,7 +229,10 @@ script does not belong in the standard pipeline.
|
|||||||
- `test:unit`: Run unit tests with `node --test --strip-types "src/**/*.test.ts"`
|
- `test:unit`: Run unit tests with `node --test --strip-types "src/**/*.test.ts"`
|
||||||
(no preceding typecheck).
|
(no preceding typecheck).
|
||||||
- `test:ci`: Run tests in CI mode with c8 coverage (text + lcov + html
|
- `test:ci`: Run tests in CI mode with c8 coverage (text + lcov + html
|
||||||
reporters), uploading `coverage/` as an artifact.
|
reporters), uploading `coverage/` as an artifact. The command is
|
||||||
|
`c8 --reporter=text --reporter=lcov --reporter=html node --test
|
||||||
|
--strip-types "src/**/*.test.ts"` (the glob is required on Node 26
|
||||||
|
for the same reason `test` uses one — see below).
|
||||||
|
|
||||||
### BUILD
|
### BUILD
|
||||||
|
|
||||||
@@ -223,8 +248,11 @@ script does not belong in the standard pipeline.
|
|||||||
|
|
||||||
### CHECK
|
### CHECK
|
||||||
|
|
||||||
- `check`: Run all checks in order — `check:oxlint`, `check:oxfmt`,
|
- `check`: Run all checks in order — `check:tsc`, `check:oxlint`,
|
||||||
`check:tsc`, `check:cspell`, `check:knip`, `check:outdated`.
|
`check:oxfmt`, `check:cspell`, `check:knip`, `check:outdated`.
|
||||||
|
`check:tsc` is first so a type error short-circuits the rest
|
||||||
|
(faster feedback than letting oxlint/oxfmt run and then failing on
|
||||||
|
tsc at the end).
|
||||||
- `check:oxlint`: `oxlint ${LEFTHOOK_FILES:-src}` — lints `src/` by
|
- `check:oxlint`: `oxlint ${LEFTHOOK_FILES:-src}` — lints `src/` by
|
||||||
default; when invoked from the lefthook pre-commit hook with
|
default; when invoked from the lefthook pre-commit hook with
|
||||||
`LEFTHOOK_FILES` set to the staged-files list, lints only those
|
`LEFTHOOK_FILES` set to the staged-files list, lints only those
|
||||||
@@ -256,7 +284,8 @@ script does not belong in the standard pipeline.
|
|||||||
- `check:tsc`: `tsc` (noEmit is set in `tsconfig.json`).
|
- `check:tsc`: `tsc` (noEmit is set in `tsconfig.json`).
|
||||||
- `check:cspell`: `cspell lint ${LEFTHOOK_FILES:-.}` — walks the
|
- `check:cspell`: `cspell lint ${LEFTHOOK_FILES:-.}` — walks the
|
||||||
project root by default; from lefthook, only the staged files.
|
project root by default; from lefthook, only the staged files.
|
||||||
- `check:outdated`: `check-outdated --ignore-pre-releases --ignore-packages @oxfmt/binding-*,@oxlint/binding-*`. The oxc native bindings are declared as `optionalDependencies` so the correct one is selected per platform automatically; the `*`-platform bindings show as "not installed" on the current platform and are explicitly ignored here.
|
- `check:outdated`: `check-outdated --ignore-pre-releases
|
||||||
|
--ignore-packages @oxfmt/binding-darwin-arm64,@oxfmt/binding-darwin-x64,@oxfmt/binding-linux-arm64-gnu,@oxfmt/binding-linux-arm64-musl,@oxfmt/binding-linux-x64-gnu,@oxfmt/binding-linux-x64-musl,@oxfmt/binding-win32-x64-msvc,@oxlint/binding-darwin-arm64,@oxlint/binding-darwin-x64,@oxlint/binding-linux-arm64-gnu,@oxlint/binding-linux-arm64-musl,@oxlint/binding-linux-x64-gnu,@oxlint/binding-linux-x64-musl,@oxlint/binding-win32-x64-msvc,@oxlint-tsgolint/darwin-arm64,@oxlint-tsgolint/darwin-x64,@oxlint-tsgolint/linux-arm64,@oxlint-tsgolint/linux-x64,@oxlint-tsgolint/win32-arm64,@oxlint-tsgolint/win32-x64`. The oxfmt, oxlint, and oxlint-tsgolint native bindings are declared as `optionalDependencies` so the correct one is selected per platform automatically; bindings for non-current platforms show as "not installed" and are explicitly ignored here.
|
||||||
- `check:knip`: `knip --include dependencies,exports,files` — finds
|
- `check:knip`: `knip --include dependencies,exports,files` — finds
|
||||||
unused dependencies, value exports, and source files. The
|
unused dependencies, value exports, and source files. The
|
||||||
scoped `--include` list intentionally omits the `types`
|
scoped `--include` list intentionally omits the `types`
|
||||||
@@ -311,7 +340,7 @@ deliberate design choice; the rule of thumb is:
|
|||||||
- **pre-commit hook**: fast, file-scoped, offline, deterministic.
|
- **pre-commit hook**: fast, file-scoped, offline, deterministic.
|
||||||
Catches what _you just changed_ in under a second or two.
|
Catches what _you just changed_ in under a second or two.
|
||||||
- **pre-push hook**: runs the unit test suite (`npm test`,
|
- **pre-push hook**: runs the unit test suite (`npm test`,
|
||||||
including a full `tsc` over the project). ~500ms. Catches
|
including a full `tsc` over the project). ~3.5s. Catches
|
||||||
runtime/logic bugs across the whole codebase before the push
|
runtime/logic bugs across the whole codebase before the push
|
||||||
leaves your machine. The `tsc` step is technically redundant
|
leaves your machine. The `tsc` step is technically redundant
|
||||||
with pre-commit but validates against unstaged changes too.
|
with pre-commit but validates against unstaged changes too.
|
||||||
@@ -321,17 +350,22 @@ deliberate design choice; the rule of thumb is:
|
|||||||
`npm run test:ci` on every push and PR. The ground truth —
|
`npm run test:ci` on every push and PR. The ground truth —
|
||||||
if CI passes, the codebase is clean.
|
if CI passes, the codebase is clean.
|
||||||
|
|
||||||
| Script | pre-commit | pre-push | `npm run check` | CI build | CI publish |
|
| Script | pre-commit | pre-push | `npm run check` | `npm run fix` | CI build | CI publish |
|
||||||
| ----------------- | :--------: | :------: | :-------------: | :------: | :--------: |
|
| ----------------- | :--------: | :------: | :-------------: | :-----------: | :------: | :--------: |
|
||||||
| `check:tsc` | ✅ | ✅ | ✅ | ✅ | — |
|
| `check:tsc` | ✅ | ✅ | ✅ | — | ✅ | — |
|
||||||
| `check:oxlint` | ✅ | — | ✅ | ✅ | — |
|
| `check:oxlint` | ✅ | — | ✅ | — | ✅ | — |
|
||||||
| `check:oxfmt` | ✅ | — | ✅ | ✅ | — |
|
| `check:oxfmt` | ✅ | — | ✅ | — | ✅ | — |
|
||||||
| `check:cspell` | ✅ | — | ✅ | ✅ | — |
|
| `check:cspell` | ✅ | — | ✅ | — | ✅ | — |
|
||||||
| `test` | — | ✅ | — | ✅ | — |
|
| `test` | — | ✅ | — | — | ✅ | — |
|
||||||
| `check:knip` | — | — | ✅ | ✅ | — |
|
| `check:knip` | — | — | ✅ | — | ✅ | — |
|
||||||
| `check:outdated` | — | — | ✅ | ✅ | — |
|
| `check:outdated` | — | — | ✅ | — | ✅ | — |
|
||||||
| `publish:publint` | — | — | — | — | ✅ |
|
| `fix:oxlint` | — | — | — | ✅ | — | — |
|
||||||
| `publish:attw` | — | — | — | — | ✅ |
|
| `fix:oxfmt` | — | — | — | ✅ | — | — |
|
||||||
|
| `publish:publint` | — | — | — | — | — | ✅ |
|
||||||
|
| `publish:attw` | — | — | — | — | — | ✅ |
|
||||||
|
|
||||||
|
(Each `test` row also runs `check:tsc` internally, so pre-push gets a
|
||||||
|
full `tsc` for free.)
|
||||||
|
|
||||||
#### Why these splits?
|
#### Why these splits?
|
||||||
|
|
||||||
@@ -342,12 +376,13 @@ deliberate design choice; the rule of thumb is:
|
|||||||
on what you typed.
|
on what you typed.
|
||||||
|
|
||||||
- **`test` (and the `tsc` it includes) is in pre-push** because it
|
- **`test` (and the `tsc` it includes) is in pre-push** because it
|
||||||
runs the whole test suite across the whole project (~500ms
|
runs the whole test suite across the whole project (~3.5s
|
||||||
for the current 6 tests). The pre-commit `LEFTHOOK_FILES`
|
for the current 6 tests, including a full `tsc`). The pre-commit
|
||||||
convention doesn't apply to the test runner, so pre-commit
|
`LEFTHOOK_FILES` convention doesn't apply to the test runner,
|
||||||
isn't the right home. Pre-push is the natural place: it runs
|
so pre-commit isn't the right home. Pre-push is the natural
|
||||||
after all commits are made but before the push leaves the
|
place: it runs after all commits are made but before the push
|
||||||
machine, catching regressions that span multiple commits.
|
leaves the machine, catching regressions that span multiple
|
||||||
|
commits.
|
||||||
|
|
||||||
- **`check:knip` is NOT in pre-commit or pre-push** because it
|
- **`check:knip` is NOT in pre-commit or pre-push** because it
|
||||||
scans the whole project (not staged files) and takes ~4s.
|
scans the whole project (not staged files) and takes ~4s.
|
||||||
|
|||||||
Reference in new issue
Block a user