* Bump dependencies

* [feature] #38 Count number of matches

* Update make-replacements.js

* [feature] #42 Differentiate number of matches and number of replacements

* [enhance] #56 Support for CWD parameter

* Default config value

* [enhance] #63 Add --quiet flag to supress console output in CLI

* Update success-handler.js

* Update readme and add change log
This commit is contained in:
Adam Reis
2019-04-21 13:00:33 +12:00
committed by GitHub
parent d607529227
commit 27f55721b7
14 changed files with 487 additions and 892 deletions

23
CHANGELOG.md Normal file
View File

@@ -0,0 +1,23 @@
# 4.0.0
## Breaking changes
The return value is now a results array instead of an array with changed files. The new results array includes each file that was processed, with a flag to indicate whether or not the file was changed, and optionally information about the number of matches and replacements that were made. See the readme for more details.
To update existing code and obtain an array of changed files again, simply convert the results array as follows:
```js
const results = await replace(options);
const changedFiles = results
.filter(result => result.hasChanged)
.map(result => result.file);
```
## New features
- Added `countMatches` flag to count the number of matches and replacements per file [#38](https://github.com/adamreisnz/replace-in-file/issues/38), [#42](https://github.com/adamreisnz/replace-in-file/issues/42), [#61](https://github.com/adamreisnz/replace-in-file/issues/61)
- Added `--quiet` flag for CLI to suppress success output [#63](https://github.com/adamreisnz/replace-in-file/issues/63)
- Added `cwd` configuration parameter for network drive replacements [#56](https://github.com/adamreisnz/replace-in-file/issues/56)
# 3.0.0
## Breaking changes
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.

110
README.md
View File

@@ -16,6 +16,7 @@ A simple utility to quickly replace text in one or more files or globs. Works sy
- [Asynchronous replacement with callback](#asynchronous-replacement-with-callback) - [Asynchronous replacement with callback](#asynchronous-replacement-with-callback)
- [Synchronous replacement](#synchronous-replacement) - [Synchronous replacement](#synchronous-replacement)
- [Return value](#return-value) - [Return value](#return-value)
- [Counting matches and replacements](#counting-matches-and-replacements)
- [Advanced usage](#advanced-usage) - [Advanced usage](#advanced-usage)
- [Replace a single file or glob](#replace-a-single-file-or-glob) - [Replace a single file or glob](#replace-a-single-file-or-glob)
- [Replace multiple files or globs](#replace-multiple-files-or-globs) - [Replace multiple files or globs](#replace-multiple-files-or-globs)
@@ -30,7 +31,8 @@ A simple utility to quickly replace text in one or more files or globs. Works sy
- [Ignore multiple files or globs](#ignore-multiple-files-or-globs) - [Ignore multiple files or globs](#ignore-multiple-files-or-globs)
- [Allow empty/invalid paths](#allow-emptyinvalid-paths) - [Allow empty/invalid paths](#allow-emptyinvalid-paths)
- [Disable globs](#disable-globs) - [Disable globs](#disable-globs)
- [Specify glob configuration](#glob-configuration) - [Specify glob configuration](#specify-glob-configuration)
- [Making replacements on network drives](#making-replacements-on-network-drives)
- [Specify character encoding](#specify-character-encoding) - [Specify character encoding](#specify-character-encoding)
- [Dry run](#dry-run) - [Dry run](#dry-run)
- [CLI usage](#cli-usage) - [CLI usage](#cli-usage)
@@ -65,8 +67,8 @@ const options = {
```js ```js
try { try {
const changes = await replace(options) const results = await replace(options)
console.log('Modified files:', changes.join(', ')); console.log('Replacement results:', results);
} }
catch (error) { catch (error) {
console.error('Error occurred:', error); console.error('Error occurred:', error);
@@ -77,8 +79,8 @@ catch (error) {
```js ```js
replace(options) replace(options)
.then(changes => { .then(results => {
console.log('Modified files:', changes.join(', ')); console.log('Replacement results:', results);
}) })
.catch(error => { .catch(error => {
console.error('Error occurred:', error); console.error('Error occurred:', error);
@@ -88,11 +90,11 @@ replace(options)
### Asynchronous replacement with callback ### Asynchronous replacement with callback
```js ```js
replace(options, (error, changes) => { replace(options, (error, results) => {
if (error) { if (error) {
return console.error('Error occurred:', error); return console.error('Error occurred:', error);
} }
console.log('Modified files:', changes.join(', ')); console.log('Replacement results:', results);
}); });
``` ```
@@ -100,8 +102,8 @@ replace(options, (error, changes) => {
```js ```js
try { try {
const changes = replace.sync(options); const results = replace.sync(options);
console.log('Modified files:', changes.join(', ')); console.log('Replacement results:', results);
} }
catch (error) { catch (error) {
console.error('Error occurred:', error); console.error('Error occurred:', error);
@@ -110,22 +112,84 @@ catch (error) {
### Return value ### Return value
The return value of the library is an array of file names of files that were modified (e.g. 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.
had some of the contents replaced). If no replacements were made, the return array will be empty.
Each result contains the following values:
- `file`: The path to the file that was processed
- `hasChanged`: Flag to indicate if the file was changed or not
```js ```js
const changes = replace.sync({ const results = replace.sync({
files: 'path/to/files/*.html', files: 'path/to/files/*.html',
from: 'foo', from: /foo/g,
to: 'bar', to: 'bar',
}); });
console.log(changes); console.log(results);
// [ // [
// 'path/to/files/file1.html', // {
// 'path/to/files/file3.html', // file: 'path/to/files/file1.html',
// 'path/to/files/file5.html', // hasChanged: true,
// },
// {
// file: 'path/to/files/file2.html',
// hasChanged: true,
// },
// {
// file: 'path/to/files/file3.html',
// hasChanged: false,
// },
// ]
```
To get an array of changed files, simply map the results as follows:
```js
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 file
- `numReplacements`: 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.
```js
const results = replace.sync({
files: 'path/to/files/*.html',
from: /foo/g,
to: 'bar',
countMatches: true,
});
console.log(results);
// [
// {
// file: 'path/to/files/file1.html',
// hasChanged: true,
// numMatches: 3,
// numReplacements: 3,
// },
// {
// file: 'path/to/files/file2.html',
// hasChanged: true,
// numMatches: 1,
// numReplacements: 1,
// },
// {
// file: 'path/to/files/file3.html',
// hasChanged: false,
// numMatches: 0,
// numReplacements: 0,
// },
// ] // ]
``` ```
@@ -293,6 +357,9 @@ const options = {
Please note that the setting `nodir` will always be passed as `false`. 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](https://github.com/adamreisnz/replace-in-file/issues/56) for more details.
### Specify character encoding ### Specify character encoding
Use a different character encoding for reading/writing files. Defaults to `utf-8`. Use a different character encoding for reading/writing files. Defaults to `utf-8`.
@@ -321,6 +388,7 @@ replace-in-file from to some/file.js,some/**/glob.js
[--disableGlobs] [--disableGlobs]
[--isRegex] [--isRegex]
[--verbose] [--verbose]
[--quiet]
[--dry] [--dry]
``` ```
@@ -331,7 +399,9 @@ The flags `--disableGlobs`, `--ignore` and `--encoding` are supported in the CLI
The setting `allowEmptyPaths` is not supported in the CLI as the replacement is The setting `allowEmptyPaths` is not supported in the CLI as the replacement is
synchronous, and this setting is only relevant for asynchronous replacement. synchronous, and this setting is only relevant for asynchronous replacement.
To list the changed files, use the `--verbose` flag. To do a dry run, use `--dry`. 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. A regular expression may be used for the `from` parameter by specifying the `--isRegex` flag.
@@ -343,7 +413,9 @@ Nodes built in `path.resolve()`, so you can pass in an absolute or relative p
## Version information ## 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 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.
See the [Changelog](CHANGELOG.md) for more information.
## License ## License
(MIT License) (MIT License)
Copyright 2015-2018, [Adam Reis](https://adam.reis.nz), co-founder at [Hello Club](https://helloclub.com/?source=npm) Copyright 2015-2019, [Adam Reis](https://adam.reis.nz), Co-founder at [Hello Club](https://helloclub.com/?source=npm)

View File

@@ -24,7 +24,7 @@ const config = loadConfig(configFile);
const options = combineConfig(config, argv); const options = combineConfig(config, argv);
//Extract settings //Extract settings
const {from, to, files, isRegex, verbose} = options; const {from, to, files, isRegex, verbose, quiet} = options;
//Single star globs already get expanded in the command line //Single star globs already get expanded in the command line
options.files = files.reduce((files, file) => { options.files = files.reduce((files, file) => {
@@ -44,12 +44,16 @@ if (isRegex) {
} }
//Log //Log
console.log(`Replacing '${from}' with '${to}'`); if (!quiet) {
console.log(`Replacing '${from}' with '${to}'`);
}
//Replace //Replace
try { try {
const changes = replace.sync(options); const results = replace.sync(options);
successHandler(changes, verbose); if (!quiet) {
successHandler(results, verbose);
}
} }
catch (error) { catch (error) {
errorHandler(error); errorHandler(error);

View File

@@ -8,9 +8,10 @@ const globAsync = require('./glob-async');
/** /**
* Get paths asynchrously * Get paths asynchrously
*/ */
module.exports = function getPathsAsync( module.exports = function getPathsAsync(patterns, config) {
patterns, ignore, disableGlobs, allowEmptyPaths, cfg
) { //Extract relevant config
const {ignore, disableGlobs, allowEmptyPaths, glob: cfg} = config;
//Not using globs? //Not using globs?
if (disableGlobs) { if (disableGlobs) {

View File

@@ -8,7 +8,10 @@ const glob = require('glob');
/** /**
* Get paths (sync) * Get paths (sync)
*/ */
module.exports = function getPathsSync(patterns, ignore, disableGlobs, cfg) { module.exports = function getPathsSync(patterns, config) {
//Extract relevant config
const {ignore, disableGlobs, glob: globConfig, cwd} = config;
//Not using globs? //Not using globs?
if (disableGlobs) { if (disableGlobs) {
@@ -16,12 +19,24 @@ module.exports = function getPathsSync(patterns, ignore, disableGlobs, cfg) {
} }
//Prepare glob config //Prepare glob config
cfg = Object.assign({ignore}, cfg, {nodir: true}); const cfg = Object.assign({ignore}, globConfig, {nodir: true});
//Append CWD configuration if given (#56)
//istanbul ignore if
if (cwd) {
cfg.cwd = cwd;
}
//Get paths //Get paths
const paths = patterns const paths = patterns.map(pattern => glob.sync(pattern, cfg));
.map(pattern => glob.sync(pattern, cfg)); const flattened = [].concat.apply([], paths);
//Prefix each path with CWD if given (#56)
//istanbul ignore if
if (cwd) {
return flattened.map(path => `${cwd}${path}`);
}
//Return flattened //Return flattened
return [].concat.apply([], paths); return flattened;
}; };

View File

@@ -16,20 +16,27 @@ function getReplacement(replace, isArray, i) {
/** /**
* Helper to make replacements * Helper to make replacements
*/ */
module.exports = function makeReplacements(contents, from, to, file) { module.exports = function makeReplacements(contents, from, to, file, count) {
//Turn into array //Turn into array
if (!Array.isArray(from)) { if (!Array.isArray(from)) {
from = [from]; from = [from];
} }
//Check if replace value is an array //Check if replace value is an array and prepare result
const isArray = Array.isArray(to); const isArray = Array.isArray(to);
const result = {file};
//Counting? Initialize number of matches
if (count) {
result.numMatches = 0;
result.numReplacements = 0;
}
//Make replacements //Make replacements
from.forEach((item, i) => { const newContents = from.reduce((contents, item, i) => {
//Call function if given, passing the filename //Call function if given, passing in the filename
if (typeof item === 'function') { if (typeof item === 'function') {
item = item(file); item = item(file);
} }
@@ -37,7 +44,7 @@ module.exports = function makeReplacements(contents, from, to, file) {
//Get replacement value //Get replacement value
let replacement = getReplacement(to, isArray, i); let replacement = getReplacement(to, isArray, i);
if (replacement === null) { if (replacement === null) {
return; return contents;
} }
//Call function if given, appending the filename //Call function if given, appending the filename
@@ -46,10 +53,23 @@ module.exports = function makeReplacements(contents, from, to, file) {
replacement = (...args) => original(...args, file); replacement = (...args) => original(...args, file);
} }
//Make replacement //Count matches
contents = contents.replace(item, replacement); if (count) {
}); const matches = contents.match(item);
if (matches) {
const replacements = matches.filter(match => match !== replacement);
result.numMatches += matches.length;
result.numReplacements += replacements.length;
}
}
//Return modified contents //Make replacement
return contents; return contents.replace(item, replacement);
}, contents);
//Check if changed
result.hasChanged = (newContents !== contents);
//Return result and new contents
return [result, newContents];
}; };

View File

@@ -8,10 +8,13 @@ const defaults = {
encoding: 'utf-8', encoding: 'utf-8',
disableGlobs: false, disableGlobs: false,
allowEmptyPaths: false, allowEmptyPaths: false,
countMatches: false,
isRegex: false, isRegex: false,
verbose: false, verbose: false,
quiet: false,
dry: false, dry: false,
glob: {}, glob: {},
cwd: null,
}; };
/** /**
@@ -28,7 +31,7 @@ module.exports = function parseConfig(config) {
config.glob = config.glob || {}; config.glob = config.glob || {};
//Extract data //Extract data
const {files, from, to, ignore, encoding, glob} = config; const {files, from, to, ignore, encoding} = config;
//Validate values //Validate values
if (typeof files === 'undefined') { if (typeof files === 'undefined') {
@@ -40,9 +43,6 @@ module.exports = function parseConfig(config) {
if (typeof to === 'undefined') { if (typeof to === 'undefined') {
throw new Error('Must specify a replacement (can be blank string)'); throw new Error('Must specify a replacement (can be blank string)');
} }
if (typeof glob !== 'object') {
throw new Error('Invalid glob config');
}
//Ensure arrays //Ensure arrays
if (!Array.isArray(files)) { if (!Array.isArray(files)) {

View File

@@ -9,32 +9,36 @@ const makeReplacements = require('./make-replacements');
/** /**
* Helper to replace in a single file (async) * Helper to replace in a single file (async)
*/ */
module.exports = function replaceAsync(file, from, to, enc, dry) { module.exports = function replaceAsync(file, from, to, config) {
//Extract relevant config
const {encoding, dry, countMatches} = config;
//Wrap in promise
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fs.readFile(file, enc, (error, contents) => { fs.readFile(file, encoding, (error, contents) => {
//istanbul ignore if //istanbul ignore if
if (error) { if (error) {
return reject(error); return reject(error);
} }
//Replace contents and check if anything changed //Make replacements
let newContents = makeReplacements(contents, from, to, file); const [result, newContents] = makeReplacements(
if (newContents === contents) { contents, from, to, file, countMatches
return resolve({file, hasChanged: false}); );
}
//Dry run, resolve //Not changed or dry run?
if (dry) { if (!result.hasChanged || dry) {
return resolve({file, hasChanged: true}); return resolve(result);
} }
//Write to file //Write to file
fs.writeFile(file, newContents, enc, error => { fs.writeFile(file, newContents, encoding, error => {
//istanbul ignore if //istanbul ignore if
if (error) { if (error) {
return reject(error); return reject(error);
} }
resolve({file, hasChanged: true}); resolve(result);
}); });
}); });
}); });

View File

@@ -9,23 +9,22 @@ const makeReplacements = require('./make-replacements');
/** /**
* Helper to replace in a single file (sync) * Helper to replace in a single file (sync)
*/ */
module.exports = function replaceSync(file, from, to, enc, dry) { module.exports = function replaceSync(file, from, to, config) {
//Read contents //Extract relevant config and read file contents
const contents = fs.readFileSync(file, enc); const {encoding, dry, countMatches} = config;
const contents = fs.readFileSync(file, encoding);
//Replace contents and check if anything changed //Replace contents and check if anything changed
const newContents = makeReplacements(contents, from, to, file); const [result, newContents] = makeReplacements(
if (newContents === contents) { contents, from, to, file, countMatches
return false; );
//Contents changed and not a dry run? Write to file
if (result.hasChanged && !dry) {
fs.writeFileSync(file, newContents, encoding);
} }
//Dry run? //Return result
if (dry) { return result;
return true;
}
//Write to file
fs.writeFileSync(file, newContents, enc);
return true;
}; };

View File

@@ -8,11 +8,13 @@ const chalk = require('chalk');
/** /**
* Success handler * Success handler
*/ */
module.exports = function successHandler(changes, verbose = false) { module.exports = function successHandler(results, verbose) {
if (changes.length > 0) { const changed = results.filter(result => result.hasChanged);
console.log(chalk.green(changes.length, 'file(s) were changed')); const numChanges = changed.length;
if (numChanges > 0) {
console.log(chalk.green(`${numChanges} file(s) were changed`));
if (verbose) { if (verbose) {
changes.forEach(file => console.log(chalk.grey('-', file))); changed.forEach(result => console.log(chalk.grey('-', result.file)));
} }
} }
else { else {

View File

@@ -27,10 +27,7 @@ function replaceInFile(config, cb) {
} }
//Get config //Get config
const { const {files, from, to, dry, verbose} = config;
files, from, to, encoding, ignore, allowEmptyPaths, disableGlobs,
dry, verbose, glob,
} = config;
//Dry run? //Dry run?
//istanbul ignore if: No need to test console logs //istanbul ignore if: No need to test console logs
@@ -39,26 +36,19 @@ function replaceInFile(config, cb) {
} }
//Find paths //Find paths
return getPathsAsync(files, ignore, disableGlobs, allowEmptyPaths, glob) return getPathsAsync(files, config)
//Make replacements //Make replacements
.then(paths => Promise.all(paths.map(file => { .then(paths => Promise.all(
return replaceAsync(file, from, to, encoding, dry); paths.map(file => replaceAsync(file, from, to, config))
}))) ))
//Convert results to array of changed files
.then(results => {
return results
.filter(result => result.hasChanged)
.map(result => result.file);
})
//Success handler //Success handler
.then(changedFiles => { .then(results => {
if (cb) { if (cb) {
cb(null, changedFiles); cb(null, results);
} }
return changedFiles; return results;
}) })
//Error handler //Error handler
@@ -81,27 +71,17 @@ replaceInFile.sync = function(config) {
config = parseConfig(config); config = parseConfig(config);
//Get config, paths, and initialize changed files //Get config, paths, and initialize changed files
const { const {files, from, to, dry, verbose} = config;
files, from, to, encoding, ignore, disableGlobs, dry, verbose, glob, const paths = getPathsSync(files, config);
} = config;
const paths = getPathsSync(files, ignore, disableGlobs, glob);
const changedFiles = [];
//Dry run? //Dry run?
//istanbul ignore if: No need to test console logs //istanbul ignore if: No need to test console logs
if (dry && verbose) { if (dry && verbose) {
console.log(chalk.yellow('Dry run, not making any changes')); console.log(chalk.yellow('Dry run, not making actual changes'));
} }
//Process synchronously //Process synchronously and return results
paths.forEach(path => { return paths.map(path => replaceSync(path, from, to, config));
if (replaceSync(path, from, to, encoding, dry)) {
changedFiles.push(path);
}
});
//Return changed files
return changedFiles;
}; };
//Export //Export

View File

@@ -68,15 +68,6 @@ describe('Replace in file', () => {
})).to.eventually.be.rejectedWith(Error); })).to.eventually.be.rejectedWith(Error);
}); });
it('should throw an error when invalid `glob` config defined', () => {
return expect(replace({
files: 'test1',
from: /re\splace/g,
to: 'test',
glob: 'invalid',
})).to.eventually.be.rejectedWith(Error);
});
it('should replace contents in a single file with regex', done => { it('should replace contents in a single file with regex', done => {
replace({ replace({
files: 'test1', files: 'test1',
@@ -236,49 +227,54 @@ describe('Replace in file', () => {
}).should.be.fulfilled; }).should.be.fulfilled;
}); });
it('should return a changed files array', done => { it('should return a results array', done => {
replace({ replace({
files: 'test1', files: 'test1',
from: /re\splace/g, from: /re\splace/g,
to: 'b', to: 'b',
}).then(changedFiles => { }).then(results => {
expect(changedFiles).to.be.instanceof(Array); expect(results).to.be.instanceof(Array);
expect(results).to.have.length(1);
expect(results[0].file).to.equal('test1');
done(); done();
}); });
}); });
it('should return in files if something was replaced', done => { it('should mark if something was replaced', done => {
replace({ replace({
files: 'test1', files: 'test1',
from: /re\splace/g, from: /re\splace/g,
to: 'b', to: 'b',
}).then(changedFiles => { }).then(results => {
expect(changedFiles).to.have.length(1); expect(results[0].hasChanged).to.equal(true);
expect(changedFiles[0]).to.equal('test1');
done(); done();
}); });
}); });
it('should not return in files if nothing replaced', done => { it('should not mark if nothing was replaced', done => {
replace({ replace({
files: 'test1', files: 'test1',
from: 'nope', from: 'nope',
to: 'b', to: 'b',
}).then(changedFiles => { }).then(results => {
expect(changedFiles).to.have.length(0); expect(results[0].hasChanged).to.equal(false);
done(); done();
}); });
}); });
it('should return changed files for multiple files', done => { it('should return correct results for multiple files', done => {
replace({ replace({
files: ['test1', 'test2', 'test3'], files: ['test1', 'test2', 'test3'],
from: /re\splace/g, from: /re\splace/g,
to: 'b', to: 'b',
}).then(changedFiles => { }).then(results => {
expect(changedFiles).to.have.length(2); expect(results).to.have.length(3);
expect(changedFiles).to.contain('test1'); expect(results[0].file).to.equal('test1');
expect(changedFiles).to.contain('test2'); expect(results[0].hasChanged).to.equal(true);
expect(results[1].file).to.equal('test2');
expect(results[1].hasChanged).to.equal(true);
expect(results[2].file).to.equal('test3');
expect(results[2].hasChanged).to.equal(false);
done(); done();
}); });
}); });
@@ -346,10 +342,14 @@ describe('Replace in file', () => {
from: /re\splace/g, from: /re\splace/g,
to: 'b', to: 'b',
dry: true, dry: true,
}).then(changedFiles => { }).then(results => {
expect(changedFiles).to.have.length(2); expect(results).to.have.length(3);
expect(changedFiles).to.contain('test1'); expect(results[0].file).to.equal('test1');
expect(changedFiles).to.contain('test2'); expect(results[0].hasChanged).to.equal(true);
expect(results[1].file).to.equal('test2');
expect(results[1].hasChanged).to.equal(true);
expect(results[2].file).to.equal('test3');
expect(results[2].hasChanged).to.equal(false);
done(); done();
}); });
}); });
@@ -382,6 +382,41 @@ describe('Replace in file', () => {
done(); done();
}); });
}); });
it('should count matches if specified in config', done => {
replace({
files: 'test1',
from: [/re/g, /place/g],
to: 'test',
countMatches: true,
}).then(results => {
expect(results[0].numMatches).to.equal(2);
done();
});
});
it('should not count matches if not specified in config', done => {
replace({
files: 'test1',
from: [/re/g, /place/g],
to: 'test',
}).then(results => {
expect(results[0].numMatches).to.be.undefined;
done();
});
});
it('should return 0 matches if match not found', done => {
replace({
files: 'test1',
from: 'nope',
to: 'test',
countMatches: true,
}).then(results => {
expect(results[0].numMatches).to.equal(0);
done();
});
});
}); });
/** /**
@@ -589,49 +624,54 @@ describe('Replace in file', () => {
}); });
}); });
it('should return a changed files array', done => { it('should return a results array', done => {
replace({ replace({
files: 'test1', files: 'test1',
from: /re\splace/g, from: /re\splace/g,
to: 'b', to: 'b',
}, (error, changedFiles) => { }, (error, results) => {
expect(changedFiles).to.be.instanceof(Array); expect(results).to.be.instanceof(Array);
expect(results).to.have.length(1);
expect(results[0].file).to.equal('test1');
done(); done();
}); });
}); });
it('should return in files if something was replaced', done => { it('should mark if something was replaced', done => {
replace({ replace({
files: 'test1', files: 'test1',
from: /re\splace/g, from: /re\splace/g,
to: 'b', to: 'b',
}, (error, changedFiles) => { }, (error, results) => {
expect(changedFiles).to.have.length(1); expect(results[0].hasChanged).to.equal(true);
expect(changedFiles[0]).to.equal('test1');
done(); done();
}); });
}); });
it('should not return in files if nothing replaced', done => { it('should not mark if nothing was replaced', done => {
replace({ replace({
files: 'test1', files: 'test1',
from: 'nope', from: 'nope',
to: 'b', to: 'b',
}, (error, changedFiles) => { }, (error, results) => {
expect(changedFiles).to.have.length(0); expect(results[0].hasChanged).to.equal(false);
done(); done();
}); });
}); });
it('should return changed files for multiple files', done => { it('should return correct results for multiple files', done => {
replace({ replace({
files: ['test1', 'test2', 'test3'], files: ['test1', 'test2', 'test3'],
from: /re\splace/g, from: /re\splace/g,
to: 'b', to: 'b',
}, (error, changedFiles) => { }, (error, results) => {
expect(changedFiles).to.have.length(2); expect(results).to.have.length(3);
expect(changedFiles).to.contain('test1'); expect(results[0].file).to.equal('test1');
expect(changedFiles).to.contain('test2'); expect(results[0].hasChanged).to.equal(true);
expect(results[1].file).to.equal('test2');
expect(results[1].hasChanged).to.equal(true);
expect(results[2].file).to.equal('test3');
expect(results[2].hasChanged).to.equal(false);
done(); done();
}); });
}); });
@@ -692,6 +732,41 @@ describe('Replace in file', () => {
done(); done();
}); });
}); });
it('should count matches if specified in config', done => {
replace({
files: 'test1',
from: [/re/g, /place/g],
to: 'test',
countMatches: true,
}, (error, results) => {
expect(results[0].numMatches).to.equal(2);
done();
});
});
it('should not count matches if not specified in config', done => {
replace({
files: 'test1',
from: [/re/g, /place/g],
to: 'test',
}, (error, results) => {
expect(results[0].numMatches).to.be.undefined;
done();
});
});
it('should return 0 matches if match not found', done => {
replace({
files: 'test1',
from: 'nope',
to: 'test',
countMatches: true,
}, (error, results) => {
expect(results[0].numMatches).to.equal(0);
done();
});
});
}); });
/** /**
@@ -861,43 +936,48 @@ describe('Replace in file', () => {
expect(test2).to.equal('a b c'); expect(test2).to.equal('a b c');
}); });
it('should return a changed files array', function() { it('should return a results array', function() {
const changedFiles = replace.sync({ const results = replace.sync({
files: 'test1', files: 'test1',
from: /re\splace/g, from: /re\splace/g,
to: 'b', to: 'b',
}); });
expect(changedFiles).to.be.instanceof(Array); expect(results).to.be.instanceof(Array);
expect(results).to.have.length(1);
expect(results[0].file).to.equal('test1');
}); });
it('should return in changed files if something was replaced', function() { it('should mark if something was replaced', function() {
const changedFiles = replace.sync({ const results = replace.sync({
files: 'test1', files: 'test1',
from: /re\splace/g, from: /re\splace/g,
to: 'b', to: 'b',
}); });
expect(changedFiles).to.have.length(1); expect(results[0].hasChanged).to.equal(true);
expect(changedFiles[0]).to.equal('test1');
}); });
it('should not return in changed files if nothing replaced', function() { it('should not mark if nothing was replaced', function() {
const changedFiles = replace.sync({ const results = replace.sync({
files: 'test1', files: 'test1',
from: 'nope', from: 'nope',
to: 'b', to: 'b',
}); });
expect(changedFiles).to.have.length(0); expect(results[0].hasChanged).to.equal(false);
}); });
it('should return changed files for multiple files', function() { it('should return corret results for multiple files', function() {
const changedFiles = replace.sync({ const results = replace.sync({
files: ['test1', 'test2', 'test3'], files: ['test1', 'test2', 'test3'],
from: /re\splace/g, from: /re\splace/g,
to: 'b', to: 'b',
}); });
expect(changedFiles).to.have.length(2); expect(results).to.have.length(3);
expect(changedFiles).to.contain('test1'); expect(results[0].file).to.equal('test1');
expect(changedFiles).to.contain('test2'); expect(results[0].hasChanged).to.equal(true);
expect(results[1].file).to.equal('test2');
expect(results[1].hasChanged).to.equal(true);
expect(results[2].file).to.equal('test3');
expect(results[2].hasChanged).to.equal(false);
}); });
it('should make multiple replacements with the same string', () => { it('should make multiple replacements with the same string', () => {
@@ -1002,15 +1082,73 @@ describe('Replace in file', () => {
}); });
it('should return changed files for a dry run', () => { it('should return changed files for a dry run', () => {
const changedFiles = replace.sync({ const results = replace.sync({
files: ['test1', 'test2', 'test3'], files: ['test1', 'test2', 'test3'],
from: /re\splace/g, from: /re\splace/g,
to: 'b', to: 'b',
dry: true, dry: true,
}); });
expect(changedFiles).to.have.length(2); expect(results).to.have.length(3);
expect(changedFiles).to.contain('test1'); expect(results[0].file).to.equal('test1');
expect(changedFiles).to.contain('test2'); expect(results[0].hasChanged).to.equal(true);
expect(results[1].file).to.equal('test2');
expect(results[1].hasChanged).to.equal(true);
expect(results[2].file).to.equal('test3');
expect(results[2].hasChanged).to.equal(false);
});
it('should count matches and replacements if specified in config', () => {
const results = replace.sync({
files: 'test1',
from: [/re/g, /place/g],
to: 'test',
countMatches: true,
});
expect(results[0].numMatches).to.equal(2);
expect(results[0].numReplacements).to.equal(2);
});
it('should differentiate between matches and replacements', () => {
const results = replace.sync({
files: 'test1',
from: [/re/g, /place/g],
to: 're',
countMatches: true,
});
expect(results[0].numMatches).to.equal(2);
expect(results[0].numReplacements).to.equal(1);
});
it('should count multiple replacements correctly', () => {
const results = replace.sync({
files: 'test1',
from: [/re/g, /place/g],
to: 'place',
countMatches: true,
});
expect(results[0].numMatches).to.equal(3);
expect(results[0].numReplacements).to.equal(1);
});
it('should not count matches or replacements if not specified in config', () => {
const results = replace.sync({
files: 'test1',
from: [/re/g, /place/g],
to: 'test',
});
expect(results[0].numMatches).to.be.undefined;
expect(results[0].numReplacements).to.be.undefined;
});
it('should return 0 matches and replacements if match not found', () => {
const results = replace.sync({
files: 'test1',
from: 'nope',
to: 'test',
countMatches: true,
});
expect(results[0].numMatches).to.equal(0);
expect(results[0].numReplacements).to.equal(0);
}); });
}); });
}); });

View File

@@ -38,12 +38,12 @@
"devDependencies": { "devDependencies": {
"babel-cli": "^6.26.0", "babel-cli": "^6.26.0",
"babel-preset-es2015": "^6.24.1", "babel-preset-es2015": "^6.24.1",
"bluebird": "^3.5.3", "bluebird": "^3.5.4",
"chai": "^4.2.0", "chai": "^4.2.0",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"dirty-chai": "^2.0.1", "dirty-chai": "^2.0.1",
"istanbul": "^1.0.0-alpha.2", "istanbul": "^1.0.0-alpha.2",
"mocha": "^6.0.2" "mocha": "^6.1.4"
}, },
"browserify": { "browserify": {
"transform": [ "transform": [

777
yarn.lock

File diff suppressed because it is too large Load Diff