Expand description
Istanbul-compatible JavaScript/TypeScript coverage instrumentation using the Oxc AST.
This crate parses JS/TS source with oxc_parser, identifies statements,
functions, and branches, injects coverage counter expressions, and emits
instrumented code. The coverage map output is compatible with Istanbul’s
coverage-final.json format (consumed by Jest, Vitest, c8, nyc, Codecov).
§Example
use oxc_coverage_instrument::{instrument, InstrumentOptions};
let source = "function add(a, b) { return a + b; }";
let result = instrument(source, "add.js", &InstrumentOptions::default()).unwrap();
println!("Instrumented code:\n{}", result.code);
println!("Functions found: {}", result.coverage_map.fn_map.len());§Coverage model
The coverage map tracks three dimensions:
- Statements: every executable statement gets a counter
- Functions: every function declaration, expression, arrow, and method
- Branches: if/else, ternary, switch cases, logical &&/||
Function names are inferred from the binding an anonymous function is
attached to (declarator, property key, assignment target) where
istanbul-lib-instrument emits (anonymous_N). See the README section
“Differences from istanbul-lib-instrument” for the full list.
§Options
InstrumentOptions defaults to Istanbul-compatible behaviour. The fields
whose semantics do not fit on a single line are expanded below.
§Composing an input source map
InstrumentOptions::compose_input_source_map folds
InstrumentOptions::input_source_map into the coverage map during
instrumentation instead of embedding it for downstream composition.
The resulting FileCoverage (and the coverageData literal baked into
the instrumented code’s preamble, hence the runtime coverage variable)
carries original-source positions, is re-keyed by the original source
path, and has no inputSourceMap field. A subsequent remap_coverage
/ remapCoverageMap on the result is a no-op. This trades the
per-collection remap round-trip (instrument, then walk every entry through
its embedded map at report time) for a one-time composition at instrument
time.
A coverage point whose positions do not remap through the input source map
is not instrumented at all: it gets no statementMap / fnMap /
branchMap entry and no counter in the emitted code, so the runtime
__coverage__ object and the emitted counters cannot disagree. Composition
is then a pure remap of the surviving positions, and never emits past-EOF
entries.
If the input map is unusable (declares no source, fails to parse) the gate
is off and the embedded inputSourceMap is left in place so the lazy remap
path still works.
§TypeScript and decorators
InstrumentOptions::strip_typescript runs oxc_transformer’s
TypeScript-strip pass on the parsed AST before coverage instrumentation.
Set it when passing raw TypeScript that has not been pre-transformed by
Babel / tsc / esbuild. The output is instrumented JavaScript whose
statementMap / branchMap positions reference the original TypeScript
byte offsets, because surviving AST nodes retain their Span through the
strip pass. If it is left off and raw TypeScript is passed, the output
contains TypeScript syntax and is not executable as JavaScript (no error
is returned). JSX is preserved verbatim on .tsx files: the codegen pass
emits it unchanged.
By default, decorator syntax (Stage 3 and legacy experimentalDecorators
alike) flows through unchanged. NestJS / Angular / TypeORM users who need
@Injectable() / @Controller() classes lowered into _decorate(...)
calls, with or without design:type / design:paramtypes metadata, set
InstrumentOptions::decorator_mode to DecoratorMode::Experimental or
DecoratorMode::ExperimentalWithMetadata.
InstrumentOptions::strict_null_checks is only consulted under
DecoratorMode::ExperimentalWithMetadata, where it decides how a
nullable union is written into the emitted design:type metadata. With
true, foo: string | null emits Object, matching what tsc does under
strictNullChecks. With false, null and undefined are elided from
the union first, so the same property emits String. Getting this wrong is
silent: the instrumented code still runs, but NestJS dependency injection,
TypeORM column inference, and class-validator all read that metadata and
will see a different type than tsc would have produced. Set it to match
the tsconfig.json the source is compiled with.
§Naming callback arguments
InstrumentOptions::name_callback_arguments names a function or arrow
expression that is a direct argument of a call or new expression and has
no other inferable name, deriving the name from the callee:
arr.map(x => x) gives "map", el.addEventListener("click", () => {})
gives "addEventListener", new Promise((res) => {}) gives "Promise".
istanbul-lib-instrument leaves these (anonymous_N), so this is an
opt-in enhancement rather than the default. Names inferred from a binding
(variable declarator, property key, assignment target, default value) still
take precedence; only the (anonymous_N) fallback is replaced. Because the
name comes from the callee it is stable across rebuilds, where the
(anonymous_N) counter renumbers as unrelated functions are added, which
matters for downstream tools that key function identity on the name.
Only the callee is used, never a sibling string argument such as a route path or a test description: the traversal ancestor for an argument position exposes the callee but not the other arguments.
§References
Structs§
- Branch
Entry - Branch entry in the coverage map.
- File
Coverage - Coverage data for a single file. Serializes to Istanbul’s
coverage-final.jsonformat. - FnEntry
- Function entry in the coverage map.
- Instrument
Options - Options for the
instrumentfunction. - Instrument
Result - Result of instrumenting a source file.
- Location
- A source location span with start and end positions.
- Position
- A 1-based line, 0-based column position.
- Position
Remapper - A position-remap predicate over a parsed
inputSourceMap. - Remap
Options - Options for the remap helpers.
- Source
MapStore - Stateful map store for the Mode B “continuous remap during collection” flow.
- Unhandled
Pragma - A coverage pragma comment that was found but not handled.
- V8Coverage
Range - A single V8 coverage range.
- V8Function
Coverage - A function’s coverage data as reported by the V8 inspector.
Enums§
- Decorator
Mode - How
strip_typescripthandles decorator syntax. - Instrument
Error - Error type for instrumentation failures.
- V8To
Istanbul Error - Errors produced by the V8-to-Istanbul conversion.
Functions§
- instrument
- Instrument a JavaScript/TypeScript source file for coverage collection.
- parse_
coverage_ map - Parse a
coverage-final.jsonstring into a map of file paths to coverage data. - remap_
coverage - Remap a single
FileCoveragethrough its embeddedinputSourceMap. - remap_
coverage_ map - Remap every
FileCoveragein a coverage map. - remap_
coverage_ map_ with_ loader - Like
remap_coverage_map, but with a disk-read fallback. - remap_
coverage_ map_ with_ loader_ and_ options - Like
remap_coverage_map_with_loader, with aRemapOptionsargument. SeeRemapOptions::drop_unmappedfor the pruning semantics. - remap_
coverage_ map_ with_ options - Like
remap_coverage_map, with aRemapOptionsargument. SeeRemapOptions::drop_unmappedfor the pruning semantics. - remap_
coverage_ with_ loader - Like
remap_coverage, but with a loader fallback for entries that carry no embeddedinputSourceMap. - remap_
coverage_ with_ loader_ and_ options - Like
remap_coverage_with_loader, with aRemapOptionsargument. SeeRemapOptions::drop_unmappedfor the pruning semantics. - remap_
coverage_ with_ options - Like
remap_coverage, but with aRemapOptionsargument. SeeRemapOptions::drop_unmappedfor the pruning semantics. - v8_
to_ istanbul - Convert V8 function coverage into Istanbul
FileCoverage. - v8_
to_ istanbul_ with_ loader - Like
v8_to_istanbul, but with a loader for externalsourceMappingURLreferences.