-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
224 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{ "presets": ["es2015"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,5 +2,10 @@ language: node_js | |
node_js: | ||
- "node" | ||
- "6" | ||
|
||
|
||
- "5" | ||
- "4" | ||
- "0.12" | ||
- "0.11" | ||
- "0.10" | ||
- "0.8" | ||
- "0.6" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
#!/usr/bin/env node | ||
'use strict'; | ||
|
||
var version = '2.0.3'; | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var font = require('chalk'); | ||
var globs = require('globs'); | ||
|
||
var yargs = require('yargs').strict().usage('RexReplace ' + version + ': Regexp search and replace for files using lookahead and backreference to matching groups in the replacement. Defaults to global multiline case-insensitive search.\n\n> rexreplace searchFor replaceWith filename').example("> rexreplace '(f?(o))o(.*)' '$3$1$2' myfile.md", "'foobar' in myfile.md will become 'barfoo'").example('').example("> rexreplace -I 'Foo' 'xxx' myfile.md", "'foobar' in myfile.md will remain 'foobar'").example('').example('> rexreplace \'^#\' \'##\' *.md', 'All markdown files in this dir got all headlines moved one level deeper').version('v', 'Echo rexreplace version', version).alias('v', 'version').boolean('I').describe('I', 'Void case insensitive search pattern.').alias('I', 'void-ignore-case').boolean('M').describe('M', 'Void multiline search pattern. Makes ^ and $ match start/end of whole content rather than each line.').alias('M', 'void-multiline').boolean('u').describe('u', 'Treat pattern as a sequence of unicode code points.').alias('u', 'unicode').describe('e', 'Encoding of files.').alias('e', 'encoding').default('e', 'utf8').boolean('o').describe('o', 'Output the result instead of saving to file. Will also output content even if no replacement have taken place.').alias('o', 'output') | ||
//.conflicts('o', 'd') | ||
|
||
.boolean('q').describe('q', "Only display erros (no other info)").alias('q', 'quiet').boolean('Q').describe('Q', "Never display erros or info").alias('Q', 'quiet-total').boolean('H').describe('H', "Halt on first error").alias('H', 'halt').default('H', false).boolean('d').describe('d', "Print debug info").alias('d', 'debug').boolean('€').describe('€', "Void having '€' as alias for '$' in pattern and replacement").alias('€', 'void-euro') | ||
|
||
/* // Ideas | ||
.boolean('n') | ||
.describe('n', "Do replacement on file names instead of file content (rename the files)") | ||
.alias('n', 'name') | ||
.boolean('v') | ||
.describe('v', "More chatty output") | ||
.alias('v', 'verbose') | ||
.boolean('p') | ||
.describe('p', "Pattern will be piped in. Note that replacement must then be first argument. Other elements like -P and -€ will be applyed afterwards.") | ||
.alias('p', 'pattern-pipe') | ||
.boolean('r') | ||
.describe('r', "Replacement will be piped in. Note that filename/globs must then be second argument") | ||
.alias('r', 'replacement-pipe') | ||
.boolean('P') | ||
.describe('P', "Pattern is a filename from where the pattern will be generated. Pattern will be defined by each line trimmed and having newlines removed followed by other other rules (like -€).)") | ||
.alias('P', 'pattern-file') | ||
.boolean('R') | ||
.describe('R', "Replacement is a filename from where the replacement will be generated. Replacement will be defined by each line trimmed and having newlines removed followed by other other rules (like -€).") | ||
.alias('R', 'replacement-file') | ||
.boolean('G') | ||
.describe('G', "filename/globas are filename(s) for files containing one filename/globs on each line to be search/replaced") | ||
.alias('G', 'globs-file') | ||
.boolean('g') | ||
.describe('g', "filename/globs will be piped in. If any filename/globs are given in command the piped data will be prepened") | ||
.alias('g', 'glob-pipe') | ||
.boolean('j') | ||
.describe('j', "Pattern is javascript source that will return a string giving the pattern to use") | ||
.alias('j', 'pattern-js') | ||
.boolean('J') | ||
.describe('J', "Replacement is javascript source that will return a string giving the replacement to use to use") | ||
.alias('j', 'replacement-js') | ||
.boolean('glob-js') | ||
.describe('glob-js', "filename/globs are javascript source that will return a string with newline seperating each glob to work on") | ||
*/ | ||
|
||
.help('h').alias('h', 'help').epilog('What "sed" should have been...'); | ||
|
||
var args = yargs.argv; | ||
|
||
debug(args); | ||
|
||
if (args._.length < 3) { | ||
die('Need more than 2 arguments', args._.length + ' was found', true); | ||
} | ||
|
||
if (!args['€']) { | ||
args._[0] = args._[0].replace('€', '$'); | ||
args._[1] = args._[1].replace('€', '$'); | ||
} | ||
|
||
var flags = 'g'; | ||
if (!args['void-ignore-case']) { | ||
flags += 'i'; | ||
} | ||
if (!args['void-multiline']) { | ||
flags += 'm'; | ||
} | ||
if (args.unicode) { | ||
flags += 'u'; | ||
} | ||
|
||
debug(flags); | ||
|
||
// Get regex pattern | ||
var regex = args._.shift(); | ||
try { | ||
regex = new RegExp(regex, flags); | ||
} catch (err) { | ||
die('Wrong formatted regexp', regex); | ||
} | ||
|
||
// Get replacement | ||
var replace = args._.shift(); | ||
|
||
// The rest are files | ||
var files = globs.sync(args._); | ||
|
||
files | ||
// Correct filepath | ||
.map(function (filepath) { | ||
return path.normalize(process.cwd() + '/' + filepath); | ||
}) | ||
|
||
// Find out if any filepaths are invalid | ||
.filter(function (filepath) { | ||
return fs.existsSync(filepath) ? true : error('File not found:', filepath); | ||
}) | ||
|
||
// Do the replacement | ||
.forEach(function (filepath) { | ||
return replaceInFile(filepath, regex, replace, args.encoding); | ||
}); | ||
|
||
function replaceInFile(file, regex, replace, encoding) { | ||
fs.readFile(file, encoding, function (err, data) { | ||
if (err) { | ||
return error(err); | ||
} | ||
debug('About to replace in: ' + file); | ||
var result = data.replace(regex, replace); | ||
|
||
if (args.output) { | ||
debug('Outputting result from: ' + file); | ||
return process.stdout.write(result); | ||
} | ||
|
||
// Nothing replaced = no need for writing file again | ||
if (result === data) { | ||
debug('Nothing changed in: ' + file); | ||
return; | ||
} | ||
|
||
debug('About to write to: ' + file); | ||
fs.writeFile(file, result, encoding, function (err) { | ||
if (err) { | ||
return error(err); | ||
} | ||
info(file); | ||
}); | ||
}); | ||
} | ||
|
||
function info(msg) { | ||
var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; | ||
|
||
if (args.quiet || args['quiet-total']) { | ||
return; | ||
} | ||
console.log(font.green(msg), data); | ||
} | ||
|
||
function die(msg) { | ||
var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; | ||
var displayHelp = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; | ||
|
||
if (displayHelp) { | ||
yargs.showHelp(); | ||
} | ||
error(msg, data); | ||
kill(); | ||
} | ||
|
||
function error(msg) { | ||
var data = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; | ||
|
||
if (!args.quiet && !args['quiet-total']) { | ||
console.error(font.red(msg), data); | ||
} | ||
if (args.halt) { | ||
kill(); | ||
} | ||
return false; | ||
} | ||
|
||
function debug(data) { | ||
if (args.debug) { | ||
console.log(font.gray(JSON.stringify(data, null, 4))); | ||
} | ||
} | ||
|
||
function kill() { | ||
var error = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; | ||
|
||
setTimeout(function () { | ||
return process.exit(error); | ||
}, 10); // give stdout a bit of time to finish | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
File renamed without changes.