CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-istanbul

Comprehensive JavaScript code coverage tool that computes statement, line, function and branch coverage with module loader hooks for transparent instrumentation

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

instrumentation.mddocs/

Code Instrumentation

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.

Capabilities

Instrumenter Class

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);

Coverage Variable Structure

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;
}

Instrumentation Process

The instrumentation process involves:

  1. Parse: Code is parsed into an Abstract Syntax Tree (AST)
  2. Transform: AST is traversed and modified to add coverage tracking
  3. Generate: Modified AST is converted back to JavaScript code
  4. Template: Coverage template is created with all trackable elements

The instrumented code will automatically populate the global coverage variable as it executes, tracking:

  • Statement coverage: Which statements were executed
  • Branch coverage: Which conditional branches were taken
  • Function coverage: Which functions were called
  • Line coverage: Which lines contained executed code

Error Handling

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:

  • SyntaxError: Invalid JavaScript syntax in source code
  • TypeError: Invalid options passed to instrumenter
  • Error: AST transformation failures or code generation issues

docs

cli.md

collection.md

configuration.md

hooks.md

index.md

instrumentation.md

reporting.md

storage.md

tree-summarizer.md

utilities.md

tile.json