Replace strings in files while bundling with Rollup
npx @tessl/cli install tessl/npm-rollup--plugin-replace@6.0.0A Rollup plugin that replaces targeted strings in files during the bundling process. It provides comprehensive configuration options for precise string replacement, including customizable delimiters, word boundary matching, object guard replacement, and assignment prevention to enable build-time code transformations, environment variable injection, and preprocessing workflows.
npm install @rollup/plugin-replace --save-devimport replace from '@rollup/plugin-replace';For CommonJS:
const replace = require('@rollup/plugin-replace');import replace from '@rollup/plugin-replace';
export default {
input: 'src/index.js',
output: {
dir: 'output',
format: 'cjs'
},
plugins: [
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
__buildDate__: () => JSON.stringify(new Date()),
__buildVersion: 15
})
]
};Creates a Rollup plugin instance for string replacement during bundling.
/**
* Creates a Rollup plugin for string replacement during bundling
* @param options - Configuration options for replacement patterns and behavior
* @returns Rollup Plugin object with transform and renderChunk hooks
*/
function replace(options?: RollupReplaceOptions): Plugin;
interface RollupReplaceOptions {
/**
* All other options are treated as string: replacement replacers,
* or string: (id) => replacement functions.
*/
[str: string]:
| Replacement
| RollupReplaceOptions['include']
| RollupReplaceOptions['values']
| RollupReplaceOptions['objectGuards']
| RollupReplaceOptions['preventAssignment']
| RollupReplaceOptions['sourceMap']
| RollupReplaceOptions['sourcemap'];
/**
* A picomatch pattern, or array of patterns, of files that should be
* processed by this plugin (if omitted, all files are included by default)
*/
include?: FilterPattern;
/**
* Files that should be excluded, if include is otherwise too permissive.
*/
exclude?: FilterPattern;
/**
* If false, skips source map generation. This will improve performance.
* @default true
*/
sourceMap?: boolean;
/**
* If false, skips source map generation. This will improve performance.
* Alternative lowercase spelling of sourceMap.
* @default true
*/
sourcemap?: boolean;
/**
* To replace every occurrence of <@foo@> instead of every occurrence
* of foo, supply delimiters
* @default ['\\b', '\\b(?!\\.)']
*/
delimiters?: [string, string];
/**
* When replacing dot-separated object properties like process.env.NODE_ENV,
* will also replace typeof process object guard checks against the objects
* with the string "object".
* @default false
*/
objectGuards?: boolean;
/**
* Prevents replacing strings where they are followed by a single equals
* sign.
* @default false
*/
preventAssignment?: boolean;
/**
* You can separate values to replace from other options.
*/
values?: { [str: string]: Replacement };
}
type Replacement = string | ((id: string) => string);
type FilterPattern = string | RegExp | (string | RegExp)[];
interface Plugin {
name: string;
buildStart(): void;
transform(code: string, id: string): TransformResult | null;
renderChunk(code: string, chunk: ChunkInfo): TransformResult | null;
}
interface TransformResult {
code: string;
map?: SourceMap;
}
interface ChunkInfo {
fileName: string;
// ... other chunk properties
}
interface SourceMap {
// Standard source map object
// Details depend on source-map library implementation
}Usage Examples:
// Simple string replacement
replace({
'process.env.NODE_ENV': JSON.stringify('production'),
__VERSION__: JSON.stringify(process.env.npm_package_version)
})
// Function-based replacement
replace({
__buildDate__: () => JSON.stringify(new Date()),
__buildUser__: (id) => JSON.stringify(process.env.USER)
})
// Using values object to separate replacements
replace({
include: ['src/**/*.js'],
exclude: ['src/vendor/**'],
values: {
'DEBUG': 'false',
'API_URL': JSON.stringify('https://api.prod.example.com')
}
})
// Custom delimiters for precise matching
replace({
delimiters: ['<@', '@>'],
'<@VERSION@>': '1.0.0',
'<@BUILD_ID@>': process.env.BUILD_ID
})
// Object guards for typeof checks
replace({
objectGuards: true,
'process.env.NODE_ENV': '"production"'
})
// Transforms: typeof process !== 'undefined' && process.env.NODE_ENV === 'production'
// Into: 'object' !== 'undefined' && 'production' === 'production'
// Prevent assignment replacement
replace({
preventAssignment: true,
'process.env.DEBUG': 'false'
})
// Leaves: process.env.DEBUG = false (assignment)
// Replaces: if (process.env.DEBUG === true) (comparison)
// File filtering
replace({
include: ['src/**/*.{js,ts}', 'lib/**/*.js'],
exclude: ['**/*.test.*', 'src/legacy/**'],
'OLD_API': 'NEW_API'
})
// Source map control
replace({
sourceMap: false, // Disable for better performance
'DEBUG_MODE': 'false'
})
// Alternative sourcemap spelling
replace({
sourcemap: process.env.NODE_ENV === 'development', // Enable in dev only
'FEATURE_FLAG': 'true'
})Advanced Configuration:
// Complex replacement with multiple patterns
replace({
// File filtering
include: ['src/**/*.{js,ts,jsx,tsx}'],
exclude: ['**/*.test.*', '**/*.spec.*'],
// Replacement options
preventAssignment: true,
objectGuards: true,
delimiters: ['\\b', '\\b(?!\\.)'],
// Environment-based replacements
values: {
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
'process.env.API_URL': JSON.stringify(process.env.API_URL),
'__VERSION__': JSON.stringify(require('./package.json').version),
'__BUILD_TIME__': () => JSON.stringify(new Date().toISOString()),
'__GIT_HASH__': () => {
try {
return JSON.stringify(require('child_process')
.execSync('git rev-parse HEAD', { encoding: 'utf8' }).trim());
} catch {
return '"unknown"';
}
}
},
// Source maps for debugging (sourceMap or sourcemap both work)
sourceMap: process.env.NODE_ENV === 'development'
})The plugin will emit warnings in the following cases:
preventAssignment is not explicitly set to true or false, a deprecation warning is shown during buildStartdelimiters will cause runtime errors during plugin initializationCommon issues and solutions:
// Problem: Replacement not working due to word boundaries
replace({ 'API': 'newAPI' }) // Won't replace 'myAPI'
// Solution: Use empty delimiters or adjust pattern
replace({
delimiters: ['', ''],
'API': 'newAPI'
}) // Will replace 'myAPI' -> 'mynewAPI'
// Problem: Accidental assignment replacement
replace({ 'DEBUG': 'false' }) // Converts 'DEBUG = true' to 'false = true' (error)
// Solution: Enable preventAssignment
replace({
preventAssignment: true,
'DEBUG': 'false'
}) // Leaves assignments alone@rollup/plugin-replace before other plugins so they can apply optimizations like dead code removal