diff --git a/README.md b/README.md index 4287d941..f2139b8d 100644 --- a/README.md +++ b/README.md @@ -364,7 +364,6 @@ _.oO(What should "sed" have looked like by now?)_ - Let Pattern, replacement, and globs come from a file - Let pattern and glob be javascript code returning a string as the result - Auto string search / replace if no regex magic is used (and verify that speed is better) -- Error != warning - Flag for String-literal (no regex, no special chars, no escape chars) to avoid backslashes or remembering which characters needs to be escaped - Check if https://github.com/eugeneware/replacestream is good to rely on - Check if regex engine from spider monkey can be wrapped in something that does not need node diff --git a/src/engine.ts b/src/engine.ts index 73b04528..15ceacda 100644 --- a/src/engine.ts +++ b/src/engine.ts @@ -6,7 +6,7 @@ const globs = require('fast-glob'); const now = new Date(); -import {outputConfig, step, debug, chat, info, error, die} from './output.ts'; +import {outputConfig, step, debug, chat, info, warn, error, die} from './output.ts'; const re = { euro: /€/g, @@ -156,6 +156,18 @@ function handlePipedData(config) { step('Check Piped Data'); if (config.includeGlob.length) { + if ( + !config.replacementJs && + !config.replacementPipe && + !config.pipedDataUsed && + !config.voidPipe && + config.pipedData && + config.pipedData !== '\n' && + config.pipedData !== '\r\n' + ) { + warn('Data is piped in, but no flag tells what to do with it. It is not used.'); + } + if (!config.replacementJs && config.pipedData) { chat('Piped data never used.'); } diff --git a/src/output.ts b/src/output.ts index e3f8d6ab..9a1b67d1 100644 --- a/src/output.ts +++ b/src/output.ts @@ -1,5 +1,5 @@ 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'); @@ -42,6 +42,14 @@ export const error = function (msg, data = '') { return false; }; +export const warn = function (msg, data = '') { + if (config?.quiet || config?.quietTotal) { + return false; + } + console.error(...[' 🟡', font.yellow(msg), data].filter(Boolean)); + return false; +}; + export function debug(data) { if (config.debug) { console.error(font.gray(JSON.stringify(data, null, 4))); diff --git a/test/cli/run.sh b/test/cli/run.sh index bca55aaa..5462d834 100644 --- a/test/cli/run.sh +++ b/test/cli/run.sh @@ -293,6 +293,28 @@ assert "printf x | rexreplace 'foo' 'pipe' my.file -o --replacement-js --voi assert_failure "printf x | rexreplace 'b' _ my.file -P -R" assert_failure "printf x | rexreplace 'b' _ my.file -P -T" +# Warn when piped data is not used (#719) +reset +assert "printf notes | rexreplace foo bar my.file 2>&1 1>/dev/null" " 🟡 Data is piped in, but no flag tells what to do with it. It is not used.\nmy.file " +assert "cat my.file" 'barbar' + +reset +assert "printf notes | rexreplace foo bar my.file -q 2>&1 1>/dev/null" '' + +reset +assert "printf notes | rexreplace foo bar my.file -Q 2>&1 1>/dev/null" '' + +reset +assert "printf notes | rexreplace foo bar my.file -P 2>&1 1>/dev/null" 'my.file ' + +reset +assert "printf '' | rexreplace foo bar my.file 2>&1 1>/dev/null" 'my.file ' + +reset +assert "printf '\n' | rexreplace foo bar my.file 2>&1 1>/dev/null" 'my.file ' + + + diff --git a/test/js/fix/719.test.js b/test/js/fix/719.test.js new file mode 100644 index 00000000..4a93cec8 --- /dev/null +++ b/test/js/fix/719.test.js @@ -0,0 +1,130 @@ +// https://github.com/mathiasrw/rexreplace/issues/719 +// Warn when data is piped in and files are also given, but the piped data is not used. +import {describe, test, expect} from 'bun:test'; +import fs from 'fs'; +import {rr} from '../helper.js'; + +const warnMsg = ' 🟡 Data is piped in, but no flag tells what to do with it. It is not used.\n'; + +describe('#719 warn when piped data is not used', () => { + test('warns when data is piped in and files are given', async () => { + fs.writeFileSync('my.file', 'foobar'); + const result = await rr('foo', 'bar', { + includeGlob: ['my.file'], + pipedData: 'notes', + }); + expect(result.err).toContain(warnMsg); + expect(result.exit).toBe(null); + expect(fs.readFileSync('my.file', 'utf8')).toBe('barbar'); + }); + + test('warning starts with 🟡 and goes to stderr', async () => { + fs.writeFileSync('my.file', 'foobar'); + const result = await rr('foo', 'bar', { + includeGlob: ['my.file'], + pipedData: 'notes', + }); + expect(result.err.startsWith(' 🟡')).toBe(true); + expect(result.out).toBe(''); + }); + + test('--quiet hides the warning', async () => { + fs.writeFileSync('my.file', 'foobar'); + const result = await rr('foo', 'bar', { + includeGlob: ['my.file'], + pipedData: 'notes', + quiet: true, + }); + expect(result.err).toBe(''); + expect(fs.readFileSync('my.file', 'utf8')).toBe('barbar'); + }); + + test('--quiet-total hides the warning', async () => { + fs.writeFileSync('my.file', 'foobar'); + const result = await rr('foo', 'bar', { + includeGlob: ['my.file'], + pipedData: 'notes', + quietTotal: true, + }); + expect(result.err).toBe(''); + expect(fs.readFileSync('my.file', 'utf8')).toBe('barbar'); + }); + + test('warning never stops the run, also not with --halt', async () => { + fs.writeFileSync('my.file', 'foobar'); + const result = await rr('foo', 'bar', { + includeGlob: ['my.file'], + pipedData: 'notes', + halt: true, + }); + expect(result.err).toContain(warnMsg); + expect(result.exit).toBe(null); + expect(fs.readFileSync('my.file', 'utf8')).toBe('barbar'); + }); + + test('--void-pipe suppresses the warning', async () => { + fs.writeFileSync('my.file', 'foobar'); + const result = await rr('foo', 'bar', { + includeGlob: ['my.file'], + pipedData: 'notes', + voidPipe: true, + }); + expect(result.err).not.toContain(warnMsg); + expect(fs.readFileSync('my.file', 'utf8')).toBe('barbar'); + }); + + test('--replacement-pipe suppresses the warning', async () => { + fs.writeFileSync('my.file', 'foobar'); + const result = await rr('foo', '_', { + includeGlob: ['my.file'], + pipedData: 'bar', + replacementPipe: true, + }); + expect(result.err).not.toContain(warnMsg); + expect(fs.readFileSync('my.file', 'utf8')).toBe('barbar'); + }); + + test('--replacement-js suppresses the warning', async () => { + fs.writeFileSync('my.file', 'foobar'); + const result = await rr('foo', 'pipe', { + includeGlob: ['my.file'], + pipedData: 'bar', + replacementJs: true, + }); + expect(result.err).not.toContain(warnMsg); + expect(fs.readFileSync('my.file', 'utf8')).toBe('barbar'); + }); + + test('empty piped data does not trigger a warning', async () => { + fs.writeFileSync('my.file', 'foobar'); + const result = await rr('foo', 'bar', { + includeGlob: ['my.file'], + pipedData: '', + }); + expect(result.err).not.toContain(warnMsg); + }); + + test('piped data with only a newline does not trigger a warning', async () => { + fs.writeFileSync('my.file', 'foobar'); + const resultN = await rr('foo', 'bar', { + includeGlob: ['my.file'], + pipedData: '\n', + }); + expect(resultN.err).not.toContain(warnMsg); + + const resultRN = await rr('foo', 'bar', { + includeGlob: ['my.file'], + pipedData: '\r\n', + }); + expect(resultRN.err).not.toContain(warnMsg); + }); + + test('piped data when no files are given does not trigger a warning', async () => { + const result = await rr('foo', 'bar', { + includeGlob: [], + pipedData: 'foobar', + }); + expect(result.err).not.toContain(warnMsg); + expect(result.out).toBe('barbar'); + }); +});