Properly hijack require, i.e., properly define require hooks and customizations
npx @tessl/cli install tessl/npm-pirates@4.0.0Pirates is a Node.js library that provides functionality to properly hijack the CommonJS require() process, allowing multiple require hooks to coexist without interfering with each other. It solves compatibility issues between tools like Babel and Istanbul by providing a clean API for implementing custom require transformations.
npm install piratesconst { addHook } = require('pirates');For ES modules:
import { addHook } from 'pirates';const { addHook } = require('pirates');
// Create a simple transformation hook
const revert = addHook((code, filename) => {
// Transform the code before it's executed
return code.replace('@@placeholder', 'console.log("transformed!");');
}, {
exts: ['.js'], // Only process .js files
ignoreNodeModules: true // Skip node_modules (default: true)
});
// Later, if you want to remove the hook:
revert();Pirates works by intercepting Node.js's internal Module._extensions mechanism. Key design principles:
The main functionality for adding transformation hooks to the Node.js require process.
/**
* Add a require hook that transforms code before execution
* @param hook - Function that receives code and filename, returns transformed code
* @param opts - Configuration options for the hook
* @returns Function to revert/remove the hook when called
*/
function addHook(hook: Hook, opts?: Options): RevertFunction;
/**
* Hook function signature for transforming module code
* @param code - Source code of the module being required
* @param filename - Absolute path of the file being processed
* @returns Transformed code as a string
*/
type Hook = (code: string, filename: string) => string;
/**
* Function returned by addHook to remove the hook
*/
type RevertFunction = () => void;Usage Example:
const { addHook } = require('pirates');
const revert = addHook((code, filename) => {
// Only transform files containing a specific marker
if (code.includes('// @custom-transform')) {
return code
.replace(/var /g, 'let ')
.replace(/function /g, 'const ');
}
return code;
});
// Use your transformed modules...
require('./my-module'); // Will be transformed
// Clean up when done
revert();Comprehensive options for controlling when and how hooks are applied.
interface Options {
/**
* File extensions to process (takes precedence over other extension options)
* @default ['.js']
*/
extensions?: ReadonlyArray<string> | string;
/**
* File extensions to process (alias for extensions)
* @default ['.js']
*/
exts?: ReadonlyArray<string> | string;
/**
* File extensions to process (alias for extensions)
* @default ['.js']
*/
extension?: ReadonlyArray<string> | string;
/**
* File extensions to process (alias for extensions)
* @default ['.js']
*/
ext?: ReadonlyArray<string> | string;
/**
* Custom function to determine which files should be processed
* @param path - Absolute path of the file being evaluated
* @returns true to process the file, false to skip it
*/
matcher?: Matcher | null;
/**
* Whether to automatically ignore node_modules directories
* @default true
*/
ignoreNodeModules?: boolean;
}
/**
* Custom matcher function for selective file processing
*/
type Matcher = (path: string) => boolean;Configuration Examples:
const { addHook } = require('pirates');
// Process only TypeScript files
addHook(transformTypeScript, {
exts: ['.ts', '.tsx']
});
// Use custom file matching logic
addHook(transformSpecialFiles, {
matcher: (filename) => {
return filename.includes('.special.') &&
!filename.includes('.test.');
}
});
// Process files in node_modules (usually not recommended)
addHook(transformEverything, {
ignoreNodeModules: false
});
// Multiple extensions with array syntax
addHook(transformJavaScript, {
extensions: ['.js', '.jsx', '.mjs']
});Pirates includes built-in validation and clear error messages:
TypeError if extension is not a stringError if hook doesn't return a stringconst { addHook } = require('pirates');
// This will throw an error
addHook((code, filename) => {
// Returning undefined/null breaks the require process
return null; // Error: Hook returned nothing!
});
// This will throw a TypeError
addHook(myHook, {
ext: 123 // Error: Invalid Extension: 123
});const { addHook } = require('pirates');
// Add multiple hooks that work together
const revertBabel = addHook(babelTransform, { exts: ['.jsx'] });
const revertTypeScript = addHook(tsTransform, { exts: ['.ts'] });
const revertCustom = addHook(customTransform, {
matcher: (path) => path.endsWith('.custom.js')
});
// All hooks work independently and can be reverted separately
setTimeout(() => {
revertBabel(); // Only removes Babel hook
}, 5000);
// Or revert all at once
function cleanup() {
revertBabel();
revertTypeScript();
revertCustom();
}