Replace in file
A simple utility to quickly replace text in one or more files or globs. Works synchronously or asynchronously with either promises or callbacks. Make a single replacement or multiple replacements at once.
Index
Installation
# Using npm, installing to local project
npm i --save replace-in-file
# Using npm, installing globally for global cli usage
npm i -g replace-in-file
# Using yarn
yarn add replace-in-file
Basic usage
const replace = require('replace-in-file');
const options = {
files: 'path/to/file',
from: /foo/g,
to: 'bar',
};
Asynchronous replacement with async
/await
try {
const results = await replace(options)
console.log('Replacement results:', results);
}
catch (error) {
console.error('Error occurred:', error);
}
Asynchronous replacement with promises
replace(options)
.then(results => {
console.log('Replacement results:', results);
})
.catch(error => {
console.error('Error occurred:', error);
});
Asynchronous replacement with callback
replace(options, (error, results) => {
if (error) {
return console.error('Error occurred:', error);
}
console.log('Replacement results:', results);
});
Synchronous replacement
try {
const results = replace.sync(options);
console.log('Replacement results:', results);
}
catch (error) {
console.error('Error occurred:', error);
}
Return value
The return value of the library is an array of replacement results against each file that was processed. This includes files in which no replacements were made.
Each result contains the following values:
file
: The path to the file that was processedhasChanged
: Flag to indicate if the file was changed or not
const results = replace.sync({
files: 'path/to/files/*.html',
from: /foo/g,
to: 'bar',
});
console.log(results);
To get an array of changed files, simply map the results as follows:
const changedFiles = results
.filter(result => result.hasChanged)
.map(result => result.file);
Counting matches and replacements
By setting the countMatches
configuration flag to true
, the number of matches and replacements per file will be counted and present in the results array.
numMatches
: Indicates the number of times a match was found in the filenumReplacements
: Indicates the number of times a replacement was made in the file
Note that the number of matches can be higher than the number of replacements if a match and replacement are the same string.
const results = replace.sync({
files: 'path/to/files/*.html',
from: /foo/g,
to: 'bar',
countMatches: true,
});
console.log(results);
Custom processor
For advanced usage where complex processing is needed it's possible to use a callback that will receive content as an argument and should return it processed.
const results = replace.sync({
files: 'path/to/files/*.html',
processor: (input) => input.replace(/foo/g, 'bar'),
});
The custom processor will receive the path of the file being processed as a second parameter:
const results = replace.sync({
files: 'path/to/files/*.html',
processor: (input, file) => input.replace(/foo/g, file),
});
Array of custom processors
Passing processor function also supports passing an array of functions that will be executed sequentially
function someProcessingA(input) {
const chapters = input.split('###')
chapters[1] = chapters[1].replace(/foo/g, 'bar')
return chapters.join('###')
}
function someProcessingB(input) {
return input.replace(/foo/g, 'bar')
}
const results = replace.sync({
files: 'path/to/files/*.html',
processor: [someProcessingA, someProcessingB],
});
Advanced usage
Replace a single file or glob
const options = {
files: 'path/to/file',
};
Replace multiple files or globs
const options = {
files: [
'path/to/file',
'path/to/other/file',
'path/to/files/*.html',
'another/**/*.path',
],
};
Replace first occurrence only
const options = {
from: 'foo',
to: 'bar',
};
Replace all occurrences
Please note that the value specified in the from
parameter is passed straight to the native String replace method. As such, if you pass a string as the from
parameter, it will only replace the first occurrence.
To replace multiple occurrences at once, you must use a regular expression for the from
parameter with the global flag enabled, e.g. /foo/g
.
const options = {
from: /foo/g,
to: 'bar',
};
Multiple values with the same replacement
These will be replaced sequentially.
const options = {
from: [/foo/g, /baz/g],
to: 'bar',
};
Multiple values with different replacements
These will be replaced sequentially.
const options = {
from: [/foo/g, /baz/g],
to: ['bar', 'bax'],
};
Custom regular expressions
Use the RegExp constructor to create any regular expression.
const str = 'foo';
const regex = new RegExp('^' + str + 'bar', 'i');
const options = {
from: regex,
to: 'bar',
};
Using callbacks for from
You can also specify a callback that returns a string or a regular expression. The callback receives the name of the file in which the replacement is being performed, thereby allowing the user to tailor the search string. The following example uses a callback to produce a search string dependent on the filename:
const options = {
files: 'path/to/file',
from: (file) => new RegExp(file, 'g'),
to: 'bar',
};
Using callbacks for to
As the to
parameter is passed to the native String replace method, you can also specify a callback. The following example uses a callback to convert matching strings to lowercase:
const options = {
files: 'path/to/file',
from: /SomePattern[A-Za-z-]+/g,
to: (match) => match.toLowerCase(),
};
This callback provides for an extra argument above the String replace method, which is the name of the file in which the replacement is being performed. The following example replaces the matched string with the filename:
const options = {
files: 'path/to/file',
from: /SomePattern[A-Za-z-]+/g,
to: (...args) => args.pop(),
};
Ignore a single file or glob
const options = {
ignore: 'path/to/ignored/file',
};
Ignore multiple files or globs
const options = {
ignore: [
'path/to/ignored/file',
'path/to/other/ignored_file',
'path/to/ignored_files/*.html',
'another/**/*.ignore',
],
};
Please note that there is an open issue with Glob that causes ignored patterns to be ignored when using a ./
prefix in your files glob. To work around this, simply remove the prefix, e.g. use **/*
instead of ./**/*
.
Allow empty/invalid paths
If set to true, empty or invalid paths will fail silently and no error will be thrown. For asynchronous replacement only. Defaults to false
.
const options = {
allowEmptyPaths: true,
};
Disable globs
You can disable globs if needed using this flag. Use this when you run into issues with file paths like files like //SERVER/share/file.txt
. Defaults to false
.
const options = {
disableGlobs: true,
};
Specify glob configuration
Specify configuration passed to the glob call:
const options = {
glob: {
dot: true,
windowsPathsNoEscape: true,
},
};
Please note that the setting nodir
will always be passed as false
.
Making replacements on network drives
To make replacements in files on network drives, you may need to specify the UNC path as the cwd
config option. This will then be passed to glob and prefixed to your paths accordingly. See #56 for more details.
Specify character encoding
Use a different character encoding for reading/writing files. Defaults to utf-8
.
const options = {
encoding: 'utf8',
};
Dry run
To do a dry run without actually making replacements, for testing purposes. Defaults to false
.
const options = {
dry: true,
};
File system
replace-in-file
defaults to using require('fs')
to provide file reading and write APIs.
You can provide an fs
object of your own to switch to a different file system, such as a mock file system for unit tests.
- If using asynchronous APIs, the provided
fs
must provide readFile
and writeFile
methods - If using synchronous APIs, the provided
fs
must provide readFileSync
and writeFileSync
methods
Custom fs
methods should have the same parameters and returned values as their built-in Node fs
equivalents.
replaceInFile({
from: 'a',
fs: {
readFile: (file, encoding, callback) => {
console.log(`Reading ${file} with encoding ${encoding}...`);
callback(null, 'fake file contents');
},
writeFile: (file, newContents, encoding, callback) => {
console.log(`Writing ${file} with encoding ${encoding}: ${newContents}`);
callback(null);
},
},
to: 'b',
})
CLI usage
replace-in-file from to some/file.js,some/**/glob.js
[--configFile=replace-config.js]
[--ignore=ignore/files.js,ignore/**/glob.js]
[--encoding=utf-8]
[--disableGlobs]
[--isRegex]
[--verbose]
[--quiet]
[--dry]
Multiple files or globs can be replaced by providing a comma separated list.
The flags --disableGlobs
, --ignore
and --encoding
are supported in the CLI.
The setting allowEmptyPaths
is not supported in the CLI as the replacement is
synchronous, and this setting is only relevant for asynchronous replacement.
To list the changed files, use the --verbose
flag. Success output can be suppressed by using the --quiet
flag.
To do a dry run without making any actual changes, use --dry
.
A regular expression may be used for the from
parameter by specifying the --isRegex
flag.
The from
and to
parameters, as well as the files list, can be omitted if you provide this
information in a configuration file. You can provide a path to a configuration file
(either Javascript or JSON) with the --configFile
flag. This path will be resolved using
Node’s built in path.resolve()
, so you can pass in an absolute or relative path.
A note on using globs with the CLI
When using the CLI, the glob pattern is handled by the operating system. But if you specify the glob pattern in the configuration file, the package will use the glob module from the Node modules, and this can lead to different behaviour despite using the same pattern.
For example, the following will only look at top level files:
module.exports = {
from: /cat/g,
to: 'dog',
};
replace-in-file ** --configFile=config.js
However, this example is recursive:
module.exports = {
files: '**',
from: /cat/g,
to: 'dog',
};
replace-in-file --configFile=config.js
If you want to do a recursive file search as an argument you must use:
replace-in-file $(ls l {,**/}*) --configFile=config.js
Version information
From version 3.0.0 onwards, replace in file requires Node 6 or higher. If you need support for Node 4 or 5, please use version 2.x.x.
From version 5.0.0 onwards, replace in file requires Node 8 or higher. If you need support for Node 6, please use version 4.x.x.
See the Changelog for more information.
License
(MIT License)
Copyright 2015-2023, Adam Reis, Co-founder at Hello Club and Frello