@tryinget/ts-mutate
v0.1.0
Published
Mutation testing for TypeScript and JavaScript using AST-discovered mutation sites.
Downloads
40
Readme
summary: "Package README for ts-mutate, the TypeScript/JavaScript mutation testing tool." read_when:
- "You are using ts-mutate from the monorepo"
- "You need package-level CLI behavior, worker model, or manifest semantics" type: "reference"
ts-mutate
Published as @tryinget/ts-mutate.
Mutation testing for TypeScript and JavaScript, inspired by clj-mutate but implemented with the TypeScript compiler API and sidecar manifests.
ts-mutate stands on its own as a publishable CLI, but it works especially well with @tryinget/crap4ts, which ranks the next hotspot from real LCOV.
The repo keeps them as separate packages so teams can adopt mutation testing without forcing the full companion workflow.
Features
- Discovers mutation sites from the AST
- Supports arithmetic, increment/decrement, comparison, equality, boolean, conditional, logical, and
0/1constant mutations - Records mutation index, original text, mutant text, line, column, category, and top-level declaration anchor
- Optionally uses LCOV to skip uncovered lines
- Runs a configurable test command per mutant
- Supports isolated parallel workers
- Supports differential mutation via a persisted manifest of top-level declarations/functions
Usage
npm exec --yes --package @tryinget/ts-mutate -- ts-mutate src/foo.ts
npm exec --yes --package @tryinget/ts-mutate -- ts-mutate src/foo.ts --scan
npm exec --yes --package @tryinget/ts-mutate -- ts-mutate src/foo.ts --lines 12,48
npm exec --yes --package @tryinget/ts-mutate -- ts-mutate src/foo.ts --max-workers 4
npm exec --yes --package @tryinget/ts-mutate -- ts-mutate src/foo.ts --test-command "npm test -- --runInBand"
npm exec --yes --package @tryinget/ts-mutate -- ts-mutate src/foo.ts --since-last-run
npm exec --yes --package @tryinget/ts-mutate -- ts-mutate src/foo.ts --mutate-allMonorepo root workflow
From this repo's root, prefer the wrapper for explicit targeted runs. The normal path is a src/ file, but you can also point it at a prebuilt dist/ JS file when you need to work from existing artifacts.
just mutate ts-mutate src/mutate-text.ts
npm run mutate:target -- --package ts-mutate --file src/mutate-text.ts --lines 4 --max-workers 1The wrapper forwards normal ts-mutate flags and defaults the current Node-based packages to:
npm run build --workspace packages/ts-mutate && cd packages/ts-mutate && node --preserve-symlinks --preserve-symlinks-main --test dist/__tests__/*.test.jsOverride that with --test-command "..." when you want a narrower suite.
If the custom command still uses Node's test runner, keep both preserve-symlinks flags.
If package-local LCOV exists at packages/<package>/coverage/lcov.info and you do not pass --lcov, the root wrapper forwards that file automatically.
Coverage-aware workflow
Generate source-mapped LCOV from the package test suite:
npm run coverage --workspace packages/ts-mutateThen either run ts-mutate directly with that LCOV or let the root wrapper auto-detect it:
npm run mutate:target -- --package ts-mutate --file src/workflow.ts
npm run mutate:target -- --package ts-mutate --file src/workflow.ts --lcov packages/ts-mutate/coverage/lcov.infoFor an opinionated next-step suggestion, generate a repo-level quality receipt:
just quality ts-mutate
# npm equivalent
npm run quality -- --package ts-mutateThat flow uses crap4ts as the companion ranking step and then prints the exact next ts-mutate command.
CLI options
--scan: structural scan only, no tests--update-manifest: rewrite the sidecar manifest without running tests--lines <L1,L2,...>: restrict mutations to selected source lines--since-last-run: mutate only changed top-level declarations/functions--mutate-all: disable differential filtering and test all covered mutations--lcov <path>: explicit LCOV file path--timeout-factor <n>: timeout multiplier relative to the baseline run. Defaults to10--test-command <command>: test command to run per mutant. Defaults tonpm test -- --runInBand--max-workers <n>: worker limit--mutation-warning <n>: warning threshold for large files. Defaults to50--cwd <path>: override working directory--help: print usage
Manifest behavior
Only clean full mutation passes write a sidecar JSON manifest under .ts-mutate/manifests/.
A run is considered clean when:
- the baseline test command passes,
- the mutation run is not line-filtered,
- no mutants survive,
- no mutants time out.
The manifest stores a module hash plus hashes of each top-level declaration or statement. If a manifest exists, default runs become differential unless --mutate-all is passed.
Dirty runs do not advance the differential receipt:
- baseline failures,
- survivor runs,
- timeout runs,
--linesruns,- runs that did not execute a full mutation pass.
Exit behavior
ts-mutate exits non-zero when:
- the baseline test command fails,
- one or more mutants survive,
- one or more mutants time out,
- CLI argument parsing fails.
