Skip to content

Commit 27f5572

Browse files
authored
4.0.0 (#74)
* 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
1 parent d607529 commit 27f5572

14 files changed

+485
-890
lines changed

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# 4.0.0
2+
3+
## Breaking changes
4+
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.
5+
6+
To update existing code and obtain an array of changed files again, simply convert the results array as follows:
7+
8+
```js
9+
const results = await replace(options);
10+
const changedFiles = results
11+
.filter(result => result.hasChanged)
12+
.map(result => result.file);
13+
```
14+
15+
## New features
16+
- 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)
17+
- Added `--quiet` flag for CLI to suppress success output [#63](https://github.com/adamreisnz/replace-in-file/issues/63)
18+
- Added `cwd` configuration parameter for network drive replacements [#56](https://github.com/adamreisnz/replace-in-file/issues/56)
19+
20+
# 3.0.0
21+
22+
## Breaking changes
23+
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.

README.md

Lines changed: 91 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ A simple utility to quickly replace text in one or more files or globs. Works sy
1616
- [Asynchronous replacement with callback](#asynchronous-replacement-with-callback)
1717
- [Synchronous replacement](#synchronous-replacement)
1818
- [Return value](#return-value)
19+
- [Counting matches and replacements](#counting-matches-and-replacements)
1920
- [Advanced usage](#advanced-usage)
2021
- [Replace a single file or glob](#replace-a-single-file-or-glob)
2122
- [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
3031
- [Ignore multiple files or globs](#ignore-multiple-files-or-globs)
3132
- [Allow empty/invalid paths](#allow-emptyinvalid-paths)
3233
- [Disable globs](#disable-globs)
33-
- [Specify glob configuration](#glob-configuration)
34+
- [Specify glob configuration](#specify-glob-configuration)
35+
- [Making replacements on network drives](#making-replacements-on-network-drives)
3436
- [Specify character encoding](#specify-character-encoding)
3537
- [Dry run](#dry-run)
3638
- [CLI usage](#cli-usage)
@@ -65,8 +67,8 @@ const options = {
6567

6668
```js
6769
try {
68-
const changes = await replace(options)
69-
console.log('Modified files:', changes.join(', '));
70+
const results = await replace(options)
71+
console.log('Replacement results:', results);
7072
}
7173
catch (error) {
7274
console.error('Error occurred:', error);
@@ -77,8 +79,8 @@ catch (error) {
7779

7880
```js
7981
replace(options)
80-
.then(changes => {
81-
console.log('Modified files:', changes.join(', '));
82+
.then(results => {
83+
console.log('Replacement results:', results);
8284
})
8385
.catch(error => {
8486
console.error('Error occurred:', error);
@@ -88,20 +90,20 @@ replace(options)
8890
### Asynchronous replacement with callback
8991

9092
```js
91-
replace(options, (error, changes) => {
93+
replace(options, (error, results) => {
9294
if (error) {
9395
return console.error('Error occurred:', error);
9496
}
95-
console.log('Modified files:', changes.join(', '));
97+
console.log('Replacement results:', results);
9698
});
9799
```
98100

99101
### Synchronous replacement
100102

101103
```js
102104
try {
103-
const changes = replace.sync(options);
104-
console.log('Modified files:', changes.join(', '));
105+
const results = replace.sync(options);
106+
console.log('Replacement results:', results);
105107
}
106108
catch (error) {
107109
console.error('Error occurred:', error);
@@ -110,22 +112,84 @@ catch (error) {
110112

111113
### Return value
112114

113-
The return value of the library is an array of file names of files that were modified (e.g.
114-
had some of the contents replaced). If no replacements were made, the return array will be empty.
115+
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.
116+
117+
Each result contains the following values:
118+
119+
- `file`: The path to the file that was processed
120+
- `hasChanged`: Flag to indicate if the file was changed or not
115121

116122
```js
117-
const changes = replace.sync({
123+
const results = replace.sync({
118124
files: 'path/to/files/*.html',
119-
from: 'foo',
125+
from: /foo/g,
126+
to: 'bar',
127+
});
128+
129+
console.log(results);
130+
131+
// [
132+
// {
133+
// file: 'path/to/files/file1.html',
134+
// hasChanged: true,
135+
// },
136+
// {
137+
// file: 'path/to/files/file2.html',
138+
// hasChanged: true,
139+
// },
140+
// {
141+
// file: 'path/to/files/file3.html',
142+
// hasChanged: false,
143+
// },
144+
// ]
145+
146+
```
147+
148+
To get an array of changed files, simply map the results as follows:
149+
150+
```js
151+
const changedFiles = results
152+
.filter(result => result.hasChanged)
153+
.map(result => result.file);
154+
```
155+
156+
### Counting matches and replacements
157+
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.
158+
159+
- `numMatches`: Indicates the number of times a match was found in the file
160+
- `numReplacements`: Indicates the number of times a replacement was made in the file
161+
162+
Note that the number of matches can be higher than the number of replacements if a match and replacement are the same string.
163+
164+
```js
165+
const results = replace.sync({
166+
files: 'path/to/files/*.html',
167+
from: /foo/g,
120168
to: 'bar',
169+
countMatches: true,
121170
});
122171

123-
console.log(changes);
172+
console.log(results);
124173

125174
// [
126-
// 'path/to/files/file1.html',
127-
// 'path/to/files/file3.html',
128-
// 'path/to/files/file5.html',
175+
// {
176+
// file: 'path/to/files/file1.html',
177+
// hasChanged: true,
178+
// numMatches: 3,
179+
// numReplacements: 3,
180+
// },
181+
// {
182+
// file: 'path/to/files/file2.html',
183+
// hasChanged: true,
184+
// numMatches: 1,
185+
// numReplacements: 1,
186+
// },
187+
// {
188+
// file: 'path/to/files/file3.html',
189+
// hasChanged: false,
190+
// numMatches: 0,
191+
// numReplacements: 0,
192+
// },
129193
// ]
130194
```
131195

@@ -293,6 +357,9 @@ const options = {
293357

294358
Please note that the setting `nodir` will always be passed as `false`.
295359

360+
### Making replacements on network drives
361+
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.
362+
296363
### Specify character encoding
297364
Use a different character encoding for reading/writing files. Defaults to `utf-8`.
298365

@@ -321,6 +388,7 @@ replace-in-file from to some/file.js,some/**/glob.js
321388
[--disableGlobs]
322389
[--isRegex]
323390
[--verbose]
391+
[--quiet]
324392
[--dry]
325393
```
326394

@@ -331,7 +399,9 @@ The flags `--disableGlobs`, `--ignore` and `--encoding` are supported in the CLI
331399
The setting `allowEmptyPaths` is not supported in the CLI as the replacement is
332400
synchronous, and this setting is only relevant for asynchronous replacement.
333401

334-
To list the changed files, use the `--verbose` flag. To do a dry run, use `--dry`.
402+
To list the changed files, use the `--verbose` flag. Success output can be suppressed by using the `--quiet` flag.
403+
404+
To do a dry run without making any actual changes, use `--dry`.
335405

336406
A regular expression may be used for the `from` parameter by specifying the `--isRegex` flag.
337407

@@ -343,7 +413,9 @@ Node’s built in `path.resolve()`, so you can pass in an absolute or relative p
343413
## Version information
344414
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.
345415

416+
See the [Changelog](CHANGELOG.md) for more information.
417+
346418
## License
347419
(MIT License)
348420

349-
Copyright 2015-2018, [Adam Reis](https://adam.reis.nz), co-founder at [Hello Club](https://helloclub.com/?source=npm)
421+
Copyright 2015-2019, [Adam Reis](https://adam.reis.nz), Co-founder at [Hello Club](https://helloclub.com/?source=npm)

bin/cli.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const config = loadConfig(configFile);
2424
const options = combineConfig(config, argv);
2525

2626
//Extract settings
27-
const {from, to, files, isRegex, verbose} = options;
27+
const {from, to, files, isRegex, verbose, quiet} = options;
2828

2929
//Single star globs already get expanded in the command line
3030
options.files = files.reduce((files, file) => {
@@ -44,12 +44,16 @@ if (isRegex) {
4444
}
4545

4646
//Log
47-
console.log(`Replacing '${from}' with '${to}'`);
47+
if (!quiet) {
48+
console.log(`Replacing '${from}' with '${to}'`);
49+
}
4850

4951
//Replace
5052
try {
51-
const changes = replace.sync(options);
52-
successHandler(changes, verbose);
53+
const results = replace.sync(options);
54+
if (!quiet) {
55+
successHandler(results, verbose);
56+
}
5357
}
5458
catch (error) {
5559
errorHandler(error);

lib/helpers/get-paths-async.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ const globAsync = require('./glob-async');
88
/**
99
* Get paths asynchrously
1010
*/
11-
module.exports = function getPathsAsync(
12-
patterns, ignore, disableGlobs, allowEmptyPaths, cfg
13-
) {
11+
module.exports = function getPathsAsync(patterns, config) {
12+
13+
//Extract relevant config
14+
const {ignore, disableGlobs, allowEmptyPaths, glob: cfg} = config;
1415

1516
//Not using globs?
1617
if (disableGlobs) {

lib/helpers/get-paths-sync.js

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,35 @@ const glob = require('glob');
88
/**
99
* Get paths (sync)
1010
*/
11-
module.exports = function getPathsSync(patterns, ignore, disableGlobs, cfg) {
11+
module.exports = function getPathsSync(patterns, config) {
12+
13+
//Extract relevant config
14+
const {ignore, disableGlobs, glob: globConfig, cwd} = config;
1215

1316
//Not using globs?
1417
if (disableGlobs) {
1518
return patterns;
1619
}
1720

1821
//Prepare glob config
19-
cfg = Object.assign({ignore}, cfg, {nodir: true});
22+
const cfg = Object.assign({ignore}, globConfig, {nodir: true});
23+
24+
//Append CWD configuration if given (#56)
25+
//istanbul ignore if
26+
if (cwd) {
27+
cfg.cwd = cwd;
28+
}
2029

2130
//Get paths
22-
const paths = patterns
23-
.map(pattern => glob.sync(pattern, cfg));
31+
const paths = patterns.map(pattern => glob.sync(pattern, cfg));
32+
const flattened = [].concat.apply([], paths);
33+
34+
//Prefix each path with CWD if given (#56)
35+
//istanbul ignore if
36+
if (cwd) {
37+
return flattened.map(path => `${cwd}${path}`);
38+
}
2439

2540
//Return flattened
26-
return [].concat.apply([], paths);
41+
return flattened;
2742
};

lib/helpers/make-replacements.js

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,35 @@ function getReplacement(replace, isArray, i) {
1616
/**
1717
* Helper to make replacements
1818
*/
19-
module.exports = function makeReplacements(contents, from, to, file) {
19+
module.exports = function makeReplacements(contents, from, to, file, count) {
2020

2121
//Turn into array
2222
if (!Array.isArray(from)) {
2323
from = [from];
2424
}
2525

26-
//Check if replace value is an array
26+
//Check if replace value is an array and prepare result
2727
const isArray = Array.isArray(to);
28+
const result = {file};
29+
30+
//Counting? Initialize number of matches
31+
if (count) {
32+
result.numMatches = 0;
33+
result.numReplacements = 0;
34+
}
2835

2936
//Make replacements
30-
from.forEach((item, i) => {
37+
const newContents = from.reduce((contents, item, i) => {
3138

32-
//Call function if given, passing the filename
39+
//Call function if given, passing in the filename
3340
if (typeof item === 'function') {
3441
item = item(file);
3542
}
3643

3744
//Get replacement value
3845
let replacement = getReplacement(to, isArray, i);
3946
if (replacement === null) {
40-
return;
47+
return contents;
4148
}
4249

4350
//Call function if given, appending the filename
@@ -46,10 +53,23 @@ module.exports = function makeReplacements(contents, from, to, file) {
4653
replacement = (...args) => original(...args, file);
4754
}
4855

56+
//Count matches
57+
if (count) {
58+
const matches = contents.match(item);
59+
if (matches) {
60+
const replacements = matches.filter(match => match !== replacement);
61+
result.numMatches += matches.length;
62+
result.numReplacements += replacements.length;
63+
}
64+
}
65+
4966
//Make replacement
50-
contents = contents.replace(item, replacement);
51-
});
67+
return contents.replace(item, replacement);
68+
}, contents);
69+
70+
//Check if changed
71+
result.hasChanged = (newContents !== contents);
5272

53-
//Return modified contents
54-
return contents;
73+
//Return result and new contents
74+
return [result, newContents];
5575
};

0 commit comments

Comments
 (0)