require-cjs-transformer
API documentation for the require-cjs-transformer plugin
require-cjs-transformer
Transform ESM imports of CommonJS-only packages to require() calls for better Node.js performance.
Installation
The plugin is included with @visulima/packem-rollup:
npm install @visulima/packemUsage
import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'
export default defineConfig({
transformer,
rollup: {
requireCJS: {
builtinNodeModules: true
}
}
})API Reference
requireCJSTransformerPlugin
function requireCJSTransformerPlugin(
options?: RequireCJSPluginOptions,
logger?: Pail
): PluginCreates a Rollup plugin that transforms ESM imports of CJS packages.
Parameters
- options (
RequireCJSPluginOptions?): Plugin configuration options - logger (
Pail?): Logger instance for debug output
Returns
A Rollup plugin instance.
Options
RequireCJSPluginOptions
interface RequireCJSPluginOptions {
/**
* Transform Node.js built-in modules (fs, path, etc.) to use runtime helpers that check Node.js version compatibility
* @default false
*/
builtinNodeModules?: boolean
/**
* Custom working directory for module resolution
* @default process.cwd()
*/
cwd?: string
/**
* Additional packages to consider as CJS-only (force transformation)
* @default []
*/
additionalPackages?: string[]
/**
* Packages to exclude from transformation
* @default []
*/
exclude?: string[]
/**
* Include patterns for files to process
* @default ['**/*.{js,jsx,ts,tsx,mjs,mts,cjs,cts}']
*/
include?: string[]
/**
* Exclude patterns for files to skip
* @default ['**/node_modules/**']
*/
excludePatterns?: string[]
}builtinNodeModules
Type: boolean
Default: false
When enabled, transforms Node.js built-in modules to use runtime helpers that check Node.js version compatibility and fallback to require() when needed.
Example:
// Input
import { readFileSync } from 'fs'
import { join } from 'path'
// Output (with builtinNodeModules: true)
const { readFileSync } = __cjs_getBuiltinModule("fs")
const { join } = __cjs_getBuiltinModule("path")cwd
Type: string
Default: process.cwd()
Working directory for module resolution. Useful when the plugin runs in a different directory than the project root.
additionalPackages
Type: string[]
Default: []
Additional packages to force transformation for, even if they appear to be ESM-compatible.
Example:
requireCJS: {
additionalPackages: ['my-cjs-package', '@myorg/cjs-lib']
}exclude
Type: string[]
Default: []
Packages to exclude from transformation, even if they are detected as CJS-only.
Example:
requireCJS: {
exclude: ['some-package', '@myorg/esm-package']
}include
Type: string[]
Default: ['**/*.{js,jsx,ts,tsx,mjs,mts,cjs,cts}']
File patterns to include for transformation processing.
excludePatterns
Type: string[]
Default: ['**/node_modules/**']
File patterns to exclude from transformation processing.
Examples
Basic Configuration
import { defineConfig } from '@visulima/packem/config'
import transformer from '@visulima/packem/transformer/esbuild'
export default defineConfig({
transformer,
rollup: {
requireCJS: {
builtinNodeModules: true
}
}
})Advanced Configuration
export default defineConfig({
transformer,
rollup: {
requireCJS: {
builtinNodeModules: true,
additionalPackages: ['my-cjs-lib'],
exclude: ['some-esm-package'],
include: ['src/**/*.{ts,tsx}'],
excludePatterns: ['src/vendor/**']
}
}
})Programmatic Usage
import { requireCJSTransformerPlugin } from '@visulima/packem-rollup'
const plugin = requireCJSTransformerPlugin({
builtinNodeModules: true,
additionalPackages: ['typescript']
})Transformation Examples
Named Imports
// Input
import { parse } from '@babel/parser'
import { transpile } from 'typescript'
// Output
const { parse } = require("@babel/parser")
const { transpile } = require("typescript")Default Imports
// Input
import typescript from 'typescript'
import parser from '@babel/parser'
// Output
const typescript = require("typescript")
const parser = require("@babel/parser")Namespace Imports
// Input
import * as ts from 'typescript'
import * as babel from '@babel/core'
// Output
const ts = require("typescript")
const babel = require("@babel/core")Node.js Built-ins
// Input (with builtinNodeModules: true)
import { readFileSync } from 'fs'
import { join, dirname } from 'path'
// Output
const { readFileSync } = __cjs_getBuiltinModule("fs")
const { join, dirname } = __cjs_getBuiltinModule("path")Mixed Imports
// Input
import fs, { readFileSync } from 'fs'
import * as path from 'path'
import { join } from 'path'
// Output (with builtinNodeModules: true)
const fs = __cjs_getBuiltinModule("fs")
const { readFileSync } = __cjs_getBuiltinModule("fs")
const path = __cjs_getBuiltinModule("path")
const { join } = __cjs_getBuiltinModule("path")Detection Logic
The plugin automatically detects CJS-only packages by:
- Package.json Analysis: Checks if
type: "commonjs"is set - Export Conditions: Analyzes available export conditions
- Entry Points: Verifies if only CommonJS entry points exist
- Additional Packages: Includes user-specified packages
- Exclusions: Respects user-defined exclusions
Automatic Detection Examples
CJS-only package (transformed):
{
"name": "cjs-package",
"main": "index.js",
"type": "commonjs"
}ESM package (not transformed):
{
"name": "esm-package",
"type": "module",
"exports": {
".": {
"import": "./index.js",
"types": "./index.d.ts"
}
}
}Dual package (not transformed - has ESM exports):
{
"name": "dual-package",
"type": "module",
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"exports": {
".": {
"import": "./dist/index.js",
"require": "./dist/index.cjs"
}
}
}TypeScript Support
The plugin generates TypeScript declaration files that reflect the transformed imports:
// Input types (from package)
export declare function parse(code: string): AST
// Generated declaration (transformed)
declare const parse: (code: string) => AST
export { parse }Rollup Integration
The plugin integrates with Rollup's plugin system and provides:
- Transform Hook: Processes individual chunks during bundling
- ResolveId Hook: Handles module resolution for transformed imports
- Logger Integration: Uses Rollup's logging system for debug output
Plugin Hooks
interface Plugin {
name: 'packem:require-cjs-transformer'
version: string
renderChunk: (code: string, chunk: RenderedChunk) => { code: string } | null
resolveId: (id: string, importer?: string) => Promise<string | null>
}Performance Considerations
When to Use
- ✅ CLI tools with heavy CJS package usage
- ✅ Build tools and bundlers
- ✅ Applications with measurable startup time issues
- ✅ Development servers with frequent restarts
When NOT to Use
- ❌ Simple applications with few CJS imports
- ❌ Libraries consumed by other packages
- ❌ When performance gain is negligible
- ❌ If you prefer simpler build configuration
Benchmarking
// Measure startup time impact
const start = Date.now()
await import('./dist/index.js')
const end = Date.now()
console.log(`Startup time: ${end - start}ms`)Error Handling
Common Errors
"Cannot resolve module"
- Ensure the package is installed
- Check if the package name is spelled correctly
- Verify the package exports the expected modules
"Plugin only works with ESM output"
- Ensure your Rollup output format is ESM-compatible
"Module not found in exclude list"
- Check your
excludeconfiguration - Verify package names match exactly
Debug Logging
Enable debug logging to troubleshoot transformation issues:
export default defineConfig({
// ... other config
rollup: {
requireCJS: {
// ... options
}
}
})The plugin logs transformation details with the prefix packem:plugin-require-cjs.
Related APIs
defineConfig- Main configuration functionPackemConfig- Configuration interfaceisPureCJS- Utility for detecting CJS packages