595759ca7a4b421a90b42137607f16ebe880db60
tiny-pattern-ts
Pattern matching for TypeScript/ESM environments (F#-style, not regex).
Development
- Build:
npm run build - Test:
npm run test,npm run test:ci - Watch:
npm run watch - Checks:
npm run check,npm run fix - Verify:
npm run verify— the definition of done - Maintenance:
npm run maintain— advisory only - Individual fixes:
npm run fix:oxfmt,npm run fix:oxlint
What each tier runs, when it fires and what it costs:
CONTRIBUTING.md § Feedback tiers. How the
prefix: in a script name is chosen:
§ Script prefix convention.
Tooling
- TypeScript 7 — type checker and build (
tsc). - node --test +
--strip-types— test runner. - c8 — code coverage for
test:ci. - oxlint — Rust-based linter, with type-aware rules powered by oxlint-tsgolint (typescript-go).
- oxfmt — Rust-based formatter (Prettier-compatible). Formats JS/TS, JSON/JSONC, YAML, Markdown, MDX, and more; built-in
package.jsonkey sorting replacessort-package-json. - cspell — spell checking.
- knip — finds unused dependencies, exports, and files.
- check-outdated — reports dependencies behind the registry; it exits non-zero whenever any dependency is outdated.
- publint — validates
package.jsonfor ESM publishing correctness. - @arethetypeswrong/cli (
attw) — validates.d.tsdeclarations against multiple module-resolution scenarios. - lefthook — git hooks.
- @spences10/pi-lsp — read-only LSP code intelligence for AI coding agents (project-local
.pi/settings.json). Talks to this repo's TypeScript 7 viatsc --lsp --stdio.
Each tool's configuration trade-off is recorded in Tooling decisions; when it runs is in CONTRIBUTING.md § Feedback tiers.
Tooling decisions
The choice and configuration of each tool above is the result of deliberate trade-offs, not defaults. The non-obvious ones:
tsconfig.jsonextends@tsconfig/strictest+@tsconfig/node26;tsconfig.build.jsonextends it to add the emit-only options (declaration,sourceMap,inlineSources,outDir,target: es2024,rewriteRelativeImportExtensions: true) and to exclude test files.inlineSourcesembeds the original TypeScript indist/*.js.map, so debuggers can map intosrc/without it being shipped;declarationMapis intentionally off because a.d.ts.mapcannot embed source and would dangle. This separation lets the editor and CI type-check from one config while the build emits from the other.npm run buildfirst runs aprebuildhook that emptiesdist/.tscdoes not prune orphaned emit output — droppingdeclarationMap, for example, left stale*.d.ts.mapfiles behind — so the build must start from an emptydist/to be reproducible.prebuildremoves onlydist; the manualcleanstill resetsdist+coverage, so a local coverage report survives a build.- Source imports use
.tsextensions sonode --strip-typesresolves them at test time.rewriteRelativeImportExtensions: trueintsconfig.build.jsonrewrites them to.jsin the emitted JavaScript; the emitted.d.tskeep the.tsspecifier, which TypeScript >= 5.0 resolves (see Requirements), so no post-processing step is needed. - Type-aware oxlint is enabled declaratively via
options.typeAware: truein.oxlintrc.json(powered byoxlint-tsgolint). The script commands stay clean — no CLI flag — and type-aware mode is a property of the config, not the invocation. - Source-level
oxlint-disabledirectives are used for known type-aware false positives (seesrc/pattern.ts,src/match.ts,src/index.test.ts). The disable lives next to the code it silences, not in.oxlintrc.json, so the trade-off is visible to anyone reading the source. knip --include dependencies,exports,filesintentionally omits thetypescategory, which produces systematic false positives for libraries whose exported types are part of the public API. The targeted scope keeps the signal high without config-file boilerplate.attw --profile esm-onlyis semantically correct: this package is intentionally ESM-only (no CommonJS shim), so CJS resolution scenarios are out of scope by design, not a bug.check:tscruns first in thenpm run checkchain so a type error short-circuits the rest (faster feedback than letting oxlint/oxfmt run and then failing on tsc at the end).- The pre-commit hook sets the
LEFTHOOK_FILESenv var to the staged-files list, and the affected scripts use${LEFTHOOK_FILES:-<default>}to default to the whole project when invoked manually. This keepspackage.json#scriptsas the single source of truth for the underlying commands —lefthook.ymlonly describes what to run on which files. tslibandtype-festare deliberately not used.tslibis a runtime helper for old ES3/ES5 targets (the project targets ES2024);type-festwas never imported. knip caught both.@spences10/pi-lspis pinned to0.0.46and is read-only by design. The package inspectsnode_modules/typescript, sees major ≥ 7 with nolib/tsserver.js(true of thetypescript-go/tsgoport), and spawns the repo's owntsc --lsp --stdiobinary — notypescript-language-serverdependency is required. Earlier releases (≤ 0.0.10) hard-wire totypescript-language-server --stdioand are TS6-only. The tool is intermediate agent feedback (hover, references, definition, symbols, diagnostics); it has no rename / code-action / apply-edit surface, and never a correctness gate —npm run check/verifyremain that..pi/settings.jsonis the shared, committed declaration;.pi/npm/is a gitignored install cache that pi recreates automatically on a trusted startup (it runsnpm installfor any missing project package), so the cache is deliberately not tracked.
Requirements
- Node.js >= 26 (engines field; pinned via
.node-version). - TypeScript >= 5.0 to consume the published declarations. The emitted
.d.tsuseconsttype parameters (TS 5.0) and keep their relative.tsspecifiers; both resolve on TS >= 5.0 innode10/node16/nodenext/bundler.
VSCode integration
- Recommended extensions: see
.vscode/extensions.json(oxc, cspell, TypeScript native-preview, EditorConfig, todo-tasks). - TypeScript 7 is used via the
typescriptteam.native-previewextension. - oxc extension provides oxlint squiggles and oxfmt format-on-save;
.vscode/settings.jsonpins it per language so a user's local[language]formatter settings cannot override the project's choice.
Contributing
For maintainer and contributor docs — the script prefix convention, the feedback-tier system, the rules the tools don't enforce, and the publishing workflow — see CONTRIBUTING.md. AI coding agents: your entry point is AGENTS.md, which points back to CONTRIBUTING.md.
- Commit signing (GPG).
- Type-only tests use
expect-type'sexpectTypeOf(...)insidenode --testcases.