-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
160 lines (141 loc) · 4.51 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
const fs = require('fs');
/*
* Read the input file, line by line
* @param {ReadableStream} a Readable Stream created from the input path
* @param {String} outPath -> the path of the output file
* @param {Function} fn -> callback function to run after every line of data
*/
const readLines = (input, outPath, fn) => {
let remaining = '';
input.on('data', (data) => {
remaining += data;
let index = remaining.indexOf('\n');
let last = 0;
while (index > -1) {
let line = remaining.substring(last, index);
last = index + 1;
fn(line).then(newContent => {
write(outPath, newContent)
});
index = remaining.indexOf('\n', last);
}
remaining = remaining.substring(last);
});
input.on('end', () => {
if (remaining.length > 0) {
fn(remaining).then(newContent => {
write(outPath, newContent)
});
}
});
}
/*
* An object to store lowerCase & upperCase related utilities
*/
const caseOps = {
lowerCase: {
is: str => str === str.toLowerCase(),
fn: str => str.toLowerCase()
},
upperCase: {
is: str => str === str.toUpperCase(),
fn: str => str.toUpperCase()
}
}
/*
* Write (append) to an output file, line by line
* @param {String} str -> the content to write
* @param {String} outPath -> the path of the output file
*/
const write = (outPath, str) => {
fs.appendFile(outPath, str + '\n', err => {
if (err) {
return console.log(err);
}
});
}
/*
* Clone the casing of the string-to-be-replaced on the replacing string
* @param {Array} caseArr -> array holding the casing of the string-to-be-replaced
* @param {String} replacement -> the replacing string
* @return {String} -> the replacement string, with the same casing as the particular instance of the string its replacing
*/
const cloneCase = (caseArr, replacement) => {
let splitTgt = replacement.split('');
let caseClonedTgt = '';
let i = 0;
caseArr.forEach(value => {
if (i < splitTgt.length) {
caseClonedTgt += caseOps[value]['fn'](splitTgt[i++]);
}
})
return caseClonedTgt + replacement.substring(i, replacement.length);
}
/*
* Store the casing of each character of the string-to-be-replaced in an array
* @param {String} str -> string-to-be-replaced
* @return {Array}
*/
const buildCaseArr = (str) => {
return str.split('').map(char => {
const isLowerCase = caseOps['lowerCase']['is'](char);
return isLowerCase ? 'lowerCase' : 'upperCase'
})
}
/*
* Remove punctuation from string
* @param {String} str
* @return {String}
*/
const removePunc = (str) => {
return (
str
.replace(/[.,\/#"'?!$%\^&\*;:{}=\-_`~()]/g, "")
.replace(/\s{2,}/g, " ")
)
}
/*
* Replace the target with the desired string, while preserving casing & punctuation
* @param {String} cand -> the particular string replacement candidate
* @param {String} replacement
* @param {String} replaceable
* @return {String}
*/
const localReplace = (cand, replacement, replaceable) => {
const punclessCand = removePunc(cand);
if (cand.includes(punclessCand) && punclessCand.toLowerCase() === replaceable.toLowerCase()) {
const caseArr = buildCaseArr(punclessCand);
const casedReplacement = cloneCase(caseArr, replacement);
return cand.replace(punclessCand, casedReplacement);
}
return cand;
}
/*
* Replace the target with the desired string, while preserving casing & punctuation
* @param {String} cand -> the particular string replacement candidate
* @param {String} replacement
* @param {String} replaceable
* @return {Promise}
*/
const replace = (replacement, replaceable, cand) => new Promise(resolve => {
const lineArr = cand.split(' ');
const newArr = lineArr.map(el => {
return localReplace(el, replacement, replaceable);
});
resolve(newArr.join(' '));
})
/*
* Replace all instances of a string with another, while preserving casing & punctuation
* @param {String} inPath -> the path of the input file
* @param {String} outPath -> the path of the output file
* @param {String} replacement -> the replacing string
* @param {String} replaceable -> the string-to-be-replaced
*/
const swichr = (inPath, outPath, replacement, replaceable) => {
readLines(
fs.createReadStream(inPath),
outPath,
replace.bind(null, replacement, replaceable)
);
}
module.exports = swichr;