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
Storage system provides pluggable backends for coverage data with memory, filesystem, and temporary storage options. The Store system enables flexible data persistence during coverage collection and processing.
Factory class for creating and registering storage implementations.
/**
* Store factory and base class
*/
class Store {
/**
* Creates a store of the specified type
* @param {string} type - Store type ('memory', 'fslookup', 'tmp')
* @param {Object} opts - Store-specific options
* @returns {Store} Store instance
*/
static create(type: string, opts?: Object): Store;
/**
* Registers a new store implementation
* @param {Function} constructor - Store constructor function
*/
static register(constructor: Function): void;
}All store implementations provide a consistent interface for data persistence.
interface Store {
/**
* Sets content for a key
* @param {string} key - Storage key
* @param {string} contents - Content to store
*/
set(key: string, contents: string): void;
/**
* Gets content for a key
* @param {string} key - Storage key
* @returns {string} Stored content
*/
get(key: string): string;
/**
* Returns array of all known keys
* @returns {string[]} Array of keys
*/
keys(): string[];
/**
* Checks if a key exists in storage
* @param {string} key - Storage key
* @returns {boolean} True if key exists
*/
hasKey(key: string): boolean;
/**
* Disposes store and reclaims temporary resources
*/
dispose(): void;
/**
* Gets JSON object for a key
* @param {string} key - Storage key
* @returns {Object} Parsed JSON object
*/
getObject(key: string): Object;
/**
* Sets JSON object for a key
* @param {string} key - Storage key
* @param {Object} object - Object to serialize and store
*/
setObject(key: string, object: Object): void;
}Usage Examples:
const { Store } = require('istanbul');
// Create different store types
const memoryStore = Store.create('memory');
const fsStore = Store.create('fslookup', {
dir: './coverage-data'
});
const tmpStore = Store.create('tmp');
// Basic operations
memoryStore.set('coverage-1', JSON.stringify(coverageData));
const retrieved = JSON.parse(memoryStore.get('coverage-1'));
// Object operations (automatic JSON handling)
memoryStore.setObject('coverage-2', coverageData);
const coverageObj = memoryStore.getObject('coverage-2');
// Key management
console.log('All keys:', memoryStore.keys());
console.log('Has coverage-1:', memoryStore.hasKey('coverage-1'));
// Cleanup
memoryStore.dispose();In-memory storage for coverage data with fast access and automatic cleanup.
/**
* In-memory store implementation
*/
class MemoryStore extends Store {
constructor(opts?: MemoryStoreOptions);
}
interface MemoryStoreOptions {
/** No specific options for memory store */
}Usage Examples:
// Create memory store
const memoryStore = Store.create('memory');
// Store coverage data from multiple files
memoryStore.setObject('file1.js', {
path: 'file1.js',
s: { '1': 1, '2': 0 },
b: { '1': [1, 0] },
f: { '1': 1 }
});
memoryStore.setObject('file2.js', {
path: 'file2.js',
s: { '1': 2, '2': 1 },
b: { '1': [2, 1] },
f: { '1': 2 }
});
// Retrieve and process
const allKeys = memoryStore.keys();
allKeys.forEach(key => {
const coverage = memoryStore.getObject(key);
console.log(`Coverage for ${key}:`, coverage);
});
// Memory stores are automatically cleaned up
memoryStore.dispose();File system-based storage for persistent coverage data with directory organization.
/**
* Filesystem-based store implementation
*/
class FsLookupStore extends Store {
constructor(opts: FsStoreOptions);
}
interface FsStoreOptions {
/** Directory to store files */
dir: string;
/** Whether to create directory if it doesn't exist */
create?: boolean;
}Usage Examples:
const path = require('path');
// Create filesystem store
const fsStore = Store.create('fslookup', {
dir: path.resolve('./coverage-store'),
create: true
});
// Store coverage data (persisted to disk)
fsStore.setObject('src/app.js', {
path: 'src/app.js',
s: { '1': 5, '2': 3, '3': 1 },
b: { '1': [5, 2], '2': [3, 0] },
f: { '1': 5, '2': 3 }
});
// Data persists across program restarts
const storedCoverage = fsStore.getObject('src/app.js');
console.log('Persisted coverage:', storedCoverage);
// Cleanup removes temporary files
fsStore.dispose();Temporary directory storage with automatic cleanup for intermediate processing.
/**
* Temporary directory store implementation
*/
class TmpStore extends Store {
constructor(opts?: TmpStoreOptions);
}
interface TmpStoreOptions {
/** Prefix for temporary directory name */
prefix?: string;
}Usage Examples:
const os = require('os');
// Create temporary store
const tmpStore = Store.create('tmp', {
prefix: 'istanbul-coverage-'
});
// Store data in temporary location
tmpStore.set('intermediate-data', JSON.stringify({
processed: true,
timestamp: Date.now()
}));
// Temporary directory location
console.log('Temp store keys:', tmpStore.keys());
// Automatic cleanup on dispose
tmpStore.dispose(); // Removes temporary directory and all filesStores are commonly used with Collector for large datasets:
const { Collector, Store } = require('istanbul');
// Use filesystem store for large coverage collection
const collector = new Collector({
store: Store.create('fslookup', {
dir: './coverage-temp',
create: true
})
});
// Add multiple coverage objects
collector.add(coverage1);
collector.add(coverage2);
collector.add(coverage3);
// Store handles persistence automatically
const finalCoverage = collector.getFinalCoverage();
// Cleanup
collector.dispose(); // Also disposes the storeUse stores for batch processing large coverage datasets:
async function processCoverageFiles(coverageFiles) {
const store = Store.create('tmp', { prefix: 'batch-coverage-' });
try {
// Store all coverage data
for (let i = 0; i < coverageFiles.length; i++) {
const coverage = require(coverageFiles[i]);
store.setObject(`batch-${i}`, coverage);
}
// Process stored data
const allKeys = store.keys();
const processedResults = allKeys.map(key => {
const coverage = store.getObject(key);
return processCoverage(coverage);
});
return processedResults;
} finally {
// Always cleanup
store.dispose();
}
}Create custom store implementations for specialized storage needs:
class DatabaseStore extends Store {
constructor(opts) {
super();
this.db = opts.database;
this.table = opts.table || 'coverage_data';
}
set(key, contents) {
this.db.query(
`INSERT OR REPLACE INTO ${this.table} (key, contents) VALUES (?, ?)`,
[key, contents]
);
}
get(key) {
const result = this.db.query(
`SELECT contents FROM ${this.table} WHERE key = ?`,
[key]
);
return result.length > 0 ? result[0].contents : null;
}
keys() {
const results = this.db.query(`SELECT key FROM ${this.table}`);
return results.map(row => row.key);
}
hasKey(key) {
const result = this.db.query(
`SELECT 1 FROM ${this.table} WHERE key = ? LIMIT 1`,
[key]
);
return result.length > 0;
}
dispose() {
// Clean up database resources
if (this.db) {
this.db.close();
}
}
}
// Register custom store
Store.register(DatabaseStore);
// Use custom store
const dbStore = Store.create('DatabaseStore', {
database: myDatabase,
table: 'istanbul_coverage'
});Handle storage errors appropriately:
const store = Store.create('fslookup', { dir: './coverage-data' });
try {
store.setObject('test-coverage', coverageData);
} catch (error) {
if (error.code === 'ENOENT') {
console.error('Storage directory does not exist');
} else if (error.code === 'EACCES') {
console.error('Permission denied writing to storage');
} else {
console.error('Storage error:', error.message);
}
}
// Safe retrieval
function safeCoverageGet(store, key) {
try {
if (store.hasKey(key)) {
return store.getObject(key);
}
return null;
} catch (error) {
console.warn(`Failed to retrieve coverage for ${key}:`, error.message);
return null;
}
}Choose appropriate store types based on usage patterns:
// For small datasets or short-lived processes
const quickStore = Store.create('memory');
// For large datasets that need persistence
const persistentStore = Store.create('fslookup', {
dir: './coverage-cache'
});
// For intermediate processing with automatic cleanup
const tempStore = Store.create('tmp');
// Monitor store performance
function benchmarkStore(store, operations = 1000) {
const start = Date.now();
for (let i = 0; i < operations; i++) {
store.set(`key-${i}`, `value-${i}`);
}
for (let i = 0; i < operations; i++) {
store.get(`key-${i}`);
}
const elapsed = Date.now() - start;
console.log(`${operations} operations took ${elapsed}ms`);
store.dispose();
}Proper lifecycle management for stores:
class CoverageProcessor {
constructor(storeType = 'memory', storeOpts = {}) {
this.store = Store.create(storeType, storeOpts);
this.disposed = false;
}
addCoverage(key, coverage) {
if (this.disposed) {
throw new Error('Cannot use disposed store');
}
this.store.setObject(key, coverage);
}
getCoverage(key) {
if (this.disposed) {
throw new Error('Cannot use disposed store');
}
return this.store.getObject(key);
}
getAllCoverage() {
if (this.disposed) {
throw new Error('Cannot use disposed store');
}
const coverage = {};
this.store.keys().forEach(key => {
coverage[key] = this.store.getObject(key);
});
return coverage;
}
dispose() {
if (!this.disposed) {
this.store.dispose();
this.disposed = true;
}
}
}
// Usage with proper cleanup
const processor = new CoverageProcessor('tmp');
try {
processor.addCoverage('file1.js', coverage1);
processor.addCoverage('file2.js', coverage2);
const allCoverage = processor.getAllCoverage();
// Process coverage...
} finally {
processor.dispose(); // Always cleanup
}