Core istanbul API for JS code coverage instrumentation and analysis
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Integration functionality for incorporating coverage instrumentation directly into Babel transformation pipelines. This approach is used by babel-plugin-istanbul and enables seamless coverage tracking during existing build processes.
Creates a Babel AST visitor for instrumenting code during Babel transformation. Returns an object with enter and exit methods for Program-level AST manipulation.
/**
* Creates a babel adaptor for instrumentation
* @param types - An instance of babel-types (typically from Babel's API)
* @param sourceFilePath - The path to source file for coverage tracking
* @param opts - Additional instrumentation options
* @returns Object with enter and exit functions for Babel Program visitor
*/
function programVisitor(
types: object,
sourceFilePath?: string,
opts?: ProgramVisitorOptions
): ProgramVisitorResult;
interface ProgramVisitorOptions {
/** The global coverage variable name (default: "__coverage__") */
coverageVariable?: string;
/** Report boolean value of logical expressions (default: false) */
reportLogic?: boolean;
/** The global coverage variable scope (default: "this") */
coverageGlobalScope?: string;
/** Use an evaluated function to find coverageGlobalScope (default: true) */
coverageGlobalScopeFunc?: boolean;
/** Names of methods to ignore by default on classes (default: []) */
ignoreClassMethods?: string[];
/** The input source map that maps uninstrumented code back to original code */
inputSourceMap?: object;
}
interface ProgramVisitorResult {
/** Function to call when entering Program AST node */
enter(path: object): void;
/** Function to call when exiting Program AST node */
exit(path: object): ExitResult;
}
interface ExitResult {
/** The file coverage object created for the source file */
fileCoverage: object;
/** Any source mapping URL found when processing the file */
sourceMappingURL?: string;
}Usage Examples:
const { programVisitor } = require("istanbul-lib-instrument");
const { transformSync } = require("@babel/core");
const types = require("@babel/types");
// Create a simple Babel plugin using programVisitor
function createCoveragePlugin() {
return function({ types }) {
const visitor = programVisitor(types, 'example.js', {
coverageVariable: '__coverage__',
reportLogic: true
});
return {
visitor: {
Program: {
enter: visitor.enter,
exit(path) {
const result = visitor.exit(path);
console.log('Coverage data:', result.fileCoverage);
if (result.sourceMappingURL) {
console.log('Source map URL:', result.sourceMappingURL);
}
}
}
}
};
};
}
// Use the plugin with Babel
const code = `
function greet(name) {
if (name) {
return "Hello, " + name;
}
return "Hello, World!";
}
`;
const result = transformSync(code, {
plugins: [createCoveragePlugin()],
filename: 'greet.js'
});
console.log(result.code);The programVisitor is designed to integrate with existing Babel pipelines without requiring separate instrumentation steps.
// Typical babel-plugin-istanbul usage pattern
function babelPluginIstanbul(babel) {
const { types } = babel;
return {
visitor: {
Program: {
enter(path, state) {
const visitor = programVisitor(types, state.filename, state.opts);
visitor.enter(path);
},
exit(path, state) {
const visitor = programVisitor(types, state.filename, state.opts);
const result = visitor.exit(path);
// Store coverage data for later use
state.coverageData = result.fileCoverage;
}
}
}
};
}Usage Examples:
const { programVisitor } = require("istanbul-lib-instrument");
// Custom Babel transformation with coverage
function transformWithCoverage(code, filename, options = {}) {
const { transformSync } = require("@babel/core");
let coverageData = null;
const result = transformSync(code, {
filename,
plugins: [
function({ types }) {
const visitor = programVisitor(types, filename, options);
return {
visitor: {
Program: {
enter: visitor.enter,
exit(path) {
const exitResult = visitor.exit(path);
coverageData = exitResult.fileCoverage;
}
}
}
};
}
]
});
return {
code: result.code,
coverage: coverageData,
map: result.map
};
}
// Usage
const { code, coverage } = transformWithCoverage(
'function add(a, b) { return a + b; }',
'math.js',
{ reportLogic: true }
);Integration with complex Babel transformation pipelines and build tools.
// Integration with multiple Babel plugins
function createInstrumentationPipeline(options = {}) {
return [
// Pre-instrumentation plugins (e.g., TypeScript, JSX)
'@babel/plugin-transform-typescript',
'@babel/plugin-transform-react-jsx',
// Coverage instrumentation
function({ types }) {
const visitor = programVisitor(types, options.filename, {
coverageVariable: options.coverageVariable,
reportLogic: options.reportLogic,
inputSourceMap: options.inputSourceMap
});
return {
visitor: {
Program: {
enter: visitor.enter,
exit: visitor.exit
}
}
};
},
// Post-instrumentation plugins
'@babel/plugin-transform-modules-commonjs'
];
}Usage Examples:
const { transformSync } = require("@babel/core");
// Transform TypeScript with coverage
const tsCode = `
interface User {
name: string;
age: number;
}
function createUser(name: string, age: number): User {
if (age < 0) {
throw new Error("Age cannot be negative");
}
return { name, age };
}
`;
const result = transformSync(tsCode, {
filename: 'user.ts',
plugins: createInstrumentationPipeline({
filename: 'user.ts',
reportLogic: true,
coverageVariable: '__coverage__'
})
});Handling source maps during Babel transformation with coverage instrumentation.
// Source map preservation with instrumentation
function instrumentWithSourceMap(code, filename, existingSourceMap) {
const { transformSync } = require("@babel/core");
const { programVisitor } = require("istanbul-lib-instrument");
let finalCoverage = null;
const result = transformSync(code, {
filename,
inputSourceMap: existingSourceMap,
sourceMaps: true,
plugins: [
function({ types }) {
const visitor = programVisitor(types, filename, {
inputSourceMap: existingSourceMap
});
return {
visitor: {
Program: {
enter: visitor.enter,
exit(path) {
const exitResult = visitor.exit(path);
finalCoverage = exitResult.fileCoverage;
}
}
}
};
}
]
});
return {
code: result.code,
map: result.map,
coverage: finalCoverage
};
}// Webpack integration example
module.exports = {
module: {
rules: [{
test: /\.js$/,
use: {
loader: 'babel-loader',
options: {
plugins: [
function({ types }) {
return {
visitor: {
Program: {
enter(path, state) {
const visitor = programVisitor(types, state.filename);
visitor.enter(path);
},
exit(path, state) {
const visitor = programVisitor(types, state.filename);
visitor.exit(path);
}
}
}
};
}
]
}
}
}]
}
};// Conditional instrumentation based on environment
function createConditionalPlugin() {
if (process.env.NODE_ENV === 'test') {
return function({ types }) {
const visitor = programVisitor(types, 'unknown.js', {
reportLogic: true,
debug: true
});
return {
visitor: {
Program: {
enter: visitor.enter,
exit: visitor.exit
}
}
};
};
}
// Return no-op plugin for production
return function() {
return { visitor: {} };
};
}The programVisitor handles errors during AST transformation and provides meaningful error messages.
const { programVisitor } = require("istanbul-lib-instrument");
function safeInstrumentation(types, filename, options) {
try {
return programVisitor(types, filename, options);
} catch (error) {
console.error(`Failed to create program visitor for ${filename}:`, error);
// Return no-op visitor
return {
enter() {},
exit() {
return { fileCoverage: null };
}
};
}
}