diff --git a/package.json b/package.json index 322e58d3..2a1a66d7 100644 --- a/package.json +++ b/package.json @@ -22,10 +22,10 @@ "build": "yarn build-only", "build-only": "swc src/ -d dist/ && bun build --target node src/env/node.ts --outfile bin/rexreplace.cli.js ", "build-minify": "bun build --target node src/env/node.ts --outfile bin/rexreplace.cli.min.js --minify", - "prebuild": "rm -fr bin # && yarn format", + "prebuild": "rm -fr bin dist # && yarn format", "test-js": "echo todo: async mocha", "test-minify": "yarn build-minify && yarn test-cli && yarn test-js", - "test-cli": "npm uninstall -g rexreplace && npm -g install ./ && yarn test-cli-only", + "test-cli": "(true || (npm uninstall -g rexreplace && npm -g install ./)) && yarn test-cli-only", "test-cli-only": "bash test/cli/run.sh", "test-speed": "bash test/speed/run.sh", "prepublishOnly": "yarn is-git-clean && git fetch && git rebase origin/main && yarn test-minify && yarn load-options && yarn bump", @@ -80,6 +80,7 @@ "test": "test" }, "dependencies": { + "chalk": "^5.3.0", "fast-glob": "^3.3.1", "fs-extra": "^11.1.1", "globs": "0.1.4", diff --git a/src/cli.ts b/src/cli.ts index 44602c96..ec58f84f 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -4,7 +4,7 @@ import yargs from 'yargs'; import * as rexreplace from './engine.ts'; -import {chat, debug, die, error, info, outputConfig, step} from './output.ts'; +import {chat, debug, die, warn, info, outputConfig, step} from './output.ts'; const re = { nl: /\r?\n/, @@ -31,17 +31,16 @@ export function cli2conf(runtime: Runtime, args: string[]) { } const argv = yargs(args) - //.strict() - /* .usage( - 'RexReplace v' + + .usage( + '$0 RexReplace v' + rexreplace.version + '\n\nRegexp 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 pattern replacement [fileGlob|option]+' ) - .usage(`> rexreplace 'Foo' 'xxx' myfile.md`, `'foobar' in myfile.md will become 'xxxbar'`) - .usage(`> rr xxx Foo myfile.md`, `The alias 'rr' can be used instead of 'rexreplace'`) - */ + .usage(`$0 > rexreplace 'Foo' 'xxx' myfile.md`, `'foobar' in myfile.md will become 'xxxbar'`) + .usage(`$0 > rr xxx Foo myfile.md`, `The alias 'rr' can be used instead of 'rexreplace'`) + .example( `> rexreplace '(f?(o))o(.*)' '$3$1€2' myfile.md`, `'foobar' in myfile.md will become 'barfoo'` @@ -287,7 +286,7 @@ export function cli2conf(runtime: Runtime, args: string[]) { .describe('h', 'Display help.') .alias('h', 'help') .epilog(`Inspiration: .oO(What should 'sed' have been by now?)`) - .parseSync(); + .strict().argv; // All options into one big config object for the rexreplace engine let conf: any = {}; @@ -324,6 +323,7 @@ function unescapeString(str = '') { export function executeReplacement(runtime: Runtime, conf, pipeData: string = null) { if (0 < conf.needHelp) { + conf.showHelp(); runtime.exit(conf.needHelp - 1); } @@ -368,9 +368,10 @@ export function executeReplacement(runtime: Runtime, conf, pipeData: string = nu } if (conf.includeGlob.length) { - return die( - 'Data is being piped in, but no flag indicating what to use it for. If you want to do replacements in the pipe then avoid having files/globs in the parameters' - ); + if ('' !== pipeData && '\n' !== pipeData) + return warn( + `${conf.includeGlob.length} file paths / globs provided. \n Data is also being piped to the command with no flags indicating how to treat it.\n Pipe data will be ignored, but check if this is actually what you intended to do.` + ); } step('Content being piped'); diff --git a/src/engine.ts b/src/engine.ts index 6addb84b..0783ce79 100644 --- a/src/engine.ts +++ b/src/engine.ts @@ -105,8 +105,9 @@ function doReplacement(filePath: string, conf: any, content: string) { } if (conf.output) { - debug('Output result from: ' + filePath); - + if (conf.verbose || conf.debug) { + console.error(filePath); + } return process.stdout.write(result); } @@ -129,7 +130,7 @@ function doReplacement(filePath: string, conf: any, content: string) { if (err) { return error(err); } - info(filePath); + return info(filePath); }); } @@ -163,11 +164,11 @@ function doReplacement(filePath: string, conf: any, content: string) { } if (!conf.keepBackup) { - fs.unlink(backupFile, (err) => { + return fs.unlink(backupFile, (err) => { if (err) { return error(err); } - info(filePath); + return info(filePath); }); } diff --git a/src/output.ts b/src/output.ts index 802a4aa0..fdc7c924 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,7 +1,7 @@ let font: any = {}; -font.red = font.green = font.gray = (str) => str; +font.yellow = font.red = font.green = font.gray = (str) => str; // check for node version supporting chalk - if so overwrite `font` -//const font = import('chalk'); +//font = import('chalk'); let conf: any = null; @@ -10,19 +10,17 @@ export const outputConfig = function (_conf) { }; export const info = function (msg, data = '') { - if (conf.quiet || conf.quietTotal) { + if (conf?.quiet || conf?.quietTotal) { return; } - - if (conf.output || conf.outputMatch) { + if (conf?.output || conf?.outputMatch) { return console.error.apply(this, [font.gray(msg), data].filter(Boolean)); } - console.log.apply(this, [msg, data].filter(Boolean)); }; export const chat = function (msg, data = '') { - if (conf.verbose) { + if (conf?.verbose && !(conf?.output || conf?.outputMatch)) { info(msg, data); } else { debug([msg, data].filter(Boolean).join(' ')); @@ -30,37 +28,47 @@ export const chat = function (msg, data = '') { }; export const error = function (msg, data = '') { - if (conf.bail) { - return die(msg, data); - } - if (!conf.quietTotal) { + if (!conf?.quietTotal) { console.error.apply(this, [' ❌', font.red(msg), data].filter(Boolean)); } + if (conf?.bail) { + return kill(); + } + + return false; +}; + +export const warn = function (msg, data = '') { + if (!conf?.quiet && !conf?.quietTotal) { + console.error.apply(this, [' 🟡', font.yellow(msg), data].filter(Boolean)); + } return false; }; export const die = function (msg = '', data = '', displayHelp = false) { - if (displayHelp && !conf.quietTotal) { - conf.showHelp(); + if (displayHelp && !conf?.quietTotal) { + conf?.showHelp(); } - msg && error(' ❌ ' + msg, data); + msg && error(msg, data); kill(); }; export function debug(...data) { - if (conf.debug) { + if (conf?.debug) { console.error(font.gray(JSON.stringify(data, null, 4))); } } export function step(data) { - if (conf?.verbose) { + if (conf?.verbose && !(conf?.output || conf?.quiet || !conf?.quiet || !conf?.quietTotal)) { console.error(font.gray(data)); } } function kill(error = 1, msg = '') { - msg && console.error(+msg); + if (!conf?.quietTotal && msg) { + console.error(+msg); + } process.exit(+error); } diff --git a/test/cli/aserta.sh b/test/cli/aserta.sh index bf0b0066..4a6951c3 100644 --- a/test/cli/aserta.sh +++ b/test/cli/aserta.sh @@ -433,7 +433,7 @@ assert() { # shellcheck disable=SC2059 expected=$(printf "${2:-}") # shellcheck disable=SC2059 - result=$(printf "$(eval 2>/dev/null "$1" <<< "${3:-}")") + [[ -n "$DEBUG" ]] && result=$(printf "$(eval "$1" <<< "${3:-}")") || result=$(printf "$(eval 2>/dev/null "$1" <<< "${3:-}")") if [[ "$result" == "$expected" ]]; then [[ -z "$DEBUG" ]] || printf '.' return diff --git a/test/cli/run.sh b/test/cli/run.sh index c5f4487f..ace8b297 100644 --- a/test/cli/run.sh +++ b/test/cli/run.sh @@ -13,8 +13,12 @@ echo RexReplace v$(rexreplace -v) DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" export STOP=1 +#export DEBUG=1 source $DIR/aserta.sh + + + # # Command exit codes # assert_success "true" # assert_failure "false" @@ -108,7 +112,7 @@ assert_success "rexreplace -h" echo echo '>' --output reset -assert "rexreplace x x my.file --output" "foobar" +assert "rexreplace x x my.file --output --verbose --debug" "foobar" assert "rexreplace x x my.file -o" "foobar" @@ -149,11 +153,20 @@ assert "cat stderr.log" "" echo echo '>' --engine -reset +#reset # assert "rexreplace o x my.file --output --engine RE2" "fxxbar" # RE2 depricated + + +reset assert "rexreplace o x my.file --output --engine V8" "fxxbar" -assert "rexreplace o x my.file --output -e V8" "fxxbar" -assert_failure "rexreplace o x my.file --output -e xxxyyyzzz" + + +reset +assert "rexreplace o x my.file --output -E V8" "fxxbar" + + +reset +assert_failure "rexreplace o x my.file --output -E xxxyyyzzz" @@ -195,8 +208,14 @@ assert "rexreplace Foo xxx my.file -o --I" "foobar" echo echo '>' --void-global reset -assert "rexreplace o x my.file -o " "foobar" +assert "rexreplace o x my.file -o " "fxxbar" + + +reset assert "rexreplace o x my.file -o --void-global" "fxobar" + + +reset assert "rexreplace o x my.file -o -G" "fxobar" @@ -206,6 +225,9 @@ echo echo '>' --output-match reset assert "rexreplace [fb]. _ my.file --output-match" "fo\\nba" + + +reset assert "rexreplace [fb]. _ my.file -O" "fo\\nba" @@ -219,6 +241,9 @@ echo echo ": Combine multiple flags (-GO)" reset assert "rexreplace [fb]. _ my.file --output-match --voidGlobal" "fo" + + +reset assert "rexreplace [fb]. _ my.file -GO" "fo" diff --git a/yarn.lock b/yarn.lock index b2090c7b..b990d2a5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -828,6 +828,11 @@ chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"