Comprehensive JavaScript code coverage tool that computes statement, line, function and branch coverage with module loader hooks for transparent instrumentation
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Code instrumentation transforms JavaScript source code to track statement, line, function, and branch coverage during execution. The Instrumenter class provides both synchronous and asynchronous APIs for instrumenting code.
Creates an instrumenter instance for transforming JavaScript code to add coverage tracking.
/**
* Creates an instrumenter for transforming JavaScript code
* @param {InstrumentOptions} options - Configuration options for instrumentation
*/
class Instrumenter {
constructor(options?: InstrumentOptions);
/**
* Synchronously instruments JavaScript code
* @param {string} code - The JavaScript source code to instrument
* @param {string} filename - The filename/path for the code (used in coverage data)
* @returns {string} The instrumented JavaScript code
*/
instrumentSync(code: string, filename: string): string;
/**
* Asynchronously instruments JavaScript code
* @param {string} code - The JavaScript source code to instrument
* @param {string} filename - The filename/path for the code
* @param {Function} callback - Callback function (err, instrumentedCode) => void
*/
instrument(code: string, filename: string, callback: (err: Error | null, code?: string) => void): void;
/**
* Instruments an AST directly (advanced usage)
* @param {Object} program - The parsed AST program node
* @param {string} filename - The filename/path for the code
* @param {string} originalCode - The original source code
* @returns {string} The instrumented JavaScript code
*/
instrumentASTSync(program: Object, filename: string, originalCode: string): string;
/**
* Returns coverage object template for the last instrumented file
* @returns {Object} Zero-coverage object with all statements, functions, branches, and lines
*/
lastFileCoverage(): Object;
/**
* Returns source map for the last instrumented file
* @returns {Object|null} Source map object or null if not available
*/
lastSourceMap(): Object | null;
/**
* Filters comments to extract Istanbul ignore hints
* @param {Object[]} comments - Array of comment objects from esprima
* @returns {Object[]} Filtered array of Istanbul hint comments
*/
filterHints(comments: Object[]): Object[];
/**
* Extracts the current hint for an AST node based on position
* @param {Object} node - AST node to check for hints
* @returns {Object|null} Current hint object or null
*/
extractCurrentHint(node: Object): Object | null;
/**
* Fixes column positions after code wrapping (internal method)
* @param {Object} coverState - Coverage state object to fix
*/
fixColumnPositions(coverState: Object): void;
/**
* Generates the preamble code for coverage tracking (internal method)
* @param {string} sourceCode - Original source code
* @param {boolean} emitUseStrict - Whether to emit 'use strict'
* @returns {string} Preamble code string
*/
getPreamble(sourceCode: string, emitUseStrict: boolean): string;
/**
* Starts ignoring coverage for subsequent code (internal method)
*/
startIgnore(): void;
/**
* Ends ignoring coverage for subsequent code (internal method)
*/
endIgnore(): void;
/**
* Converts AST nodes to block statements (internal method)
* @param {Object} node - AST node to convert
* @returns {Object} Block statement AST node
*/
convertToBlock(node: Object): Object;
/**
* Converts arrow function expressions to block form (internal method)
* @param {Object} node - Arrow function AST node
*/
arrowBlockConverter(node: Object): void;
/**
* Ensures try-catch handler compatibility (internal method)
* @param {Object} node - Try statement AST node
*/
paranoidHandlerCheck(node: Object): void;
}
interface InstrumentOptions {
/** Name of the global variable to store coverage data (default: '__coverage__') */
coverageVariable?: string;
/** Whether to embed original source code in coverage object (default: false) */
embedSource?: boolean;
/** Whether to preserve comments in instrumented output (default: false) */
preserveComments?: boolean;
/** Whether to emit readable code instead of compact (default: false) */
noCompact?: boolean;
/** Whether the code uses ES6 import/export statements (default: false) */
esModules?: boolean;
/** Whether to skip auto-wrapping code in anonymous function (default: false) */
noAutoWrap?: boolean;
/** Options passed directly to escodegen for code generation */
codeGenerationOptions?: Object;
/** Enable debug mode for instrumenter (default: false) */
debug?: boolean;
/** Enable debug mode for AST walker (default: false) */
walkDebug?: boolean;
}Usage Examples:
const fs = require('fs');
const { Instrumenter } = require('istanbul');
// Basic instrumentation
const instrumenter = new Instrumenter();
const code = fs.readFileSync('app.js', 'utf8');
const instrumentedCode = instrumenter.instrumentSync(code, 'app.js');
// Instrumentation with options
const instrumenter2 = new Instrumenter({
coverageVariable: '__myCoverage__',
embedSource: true,
preserveComments: true,
esModules: true
});
// Async instrumentation
instrumenter.instrument(code, 'app.js', (err, instrumentedCode) => {
if (err) throw err;
console.log('Code instrumented successfully');
// Get coverage template
const coverageObject = instrumenter.lastFileCoverage();
console.log('Coverage template:', coverageObject);
});
// Advanced: Direct AST instrumentation
const esprima = require('esprima');
const ast = esprima.parseScript(code);
const instrumentedFromAST = instrumenter.instrumentASTSync(ast, 'app.js', code);The instrumented code populates a global coverage variable (default: __coverage__) with the following structure:
interface CoverageObject {
[filename: string]: FileCoverage;
}
interface FileCoverage {
/** File path */
path: string;
/** Statement coverage data */
s: { [statementId: string]: number };
/** Branch coverage data */
b: { [branchId: string]: number[] };
/** Function coverage data */
f: { [functionId: string]: number };
/** Function name mapping */
fnMap: { [functionId: string]: FunctionMapping };
/** Statement location mapping */
statementMap: { [statementId: string]: Location };
/** Branch location mapping */
branchMap: { [branchId: string]: BranchMapping };
/** Line coverage data (derived) */
l?: { [lineNumber: string]: number };
/** Original source code (if embedSource is true) */
code?: string[];
}
interface Location {
start: { line: number; column: number };
end: { line: number; column: number };
}
interface FunctionMapping {
name: string;
decl: Location;
loc: Location;
line: number;
}
interface BranchMapping {
loc: Location;
type: 'if' | 'switch' | 'cond-expr' | 'default-arg';
locations: Location[];
line: number;
}The instrumentation process involves:
The instrumented code will automatically populate the global coverage variable as it executes, tracking:
try {
const instrumentedCode = instrumenter.instrumentSync(code, filename);
} catch (error) {
if (error.name === 'SyntaxError') {
console.error('JavaScript syntax error in:', filename);
} else {
console.error('Instrumentation failed:', error.message);
}
}Common instrumentation errors include: