Find & Replace — JavaScript source
Find and replace text with literal or regular-expression matching, global replace, case sensitivity, whole-word, and capture-group substitution. Live match counter.
This is the JavaScript implementation — the same logic the interactive tool runs, in a shareable, citable form.
/**
* Find & replace with literal or regex matching, $-substitution
* ($1 backrefs, $&, $$), case sensitivity, whole-word, and global modes.
*
* Language: JavaScript (ES module)
* CosmoDev polyglot showcase port of the `find-replace` tool.
* Ported from src/lib/findReplace.ts - display source, part of CosmoDev's
* polyglot tool pages.
*
* Mirrors the live lib exactly. A literal find string is regex-escaped and
* matched verbatim; an isRegex find is compiled as-is. \b wraps the pattern
* when wholeWord is set, and the flags string composes g (global), i
* (case-insensitive), and m (multiline, regex mode only). Invalid patterns
* are reported as an error string instead of throwing, and an empty find is
* a no-op. Replacement delegates to String.prototype.replace, so $1, $&,
* and $$ expand exactly as the engine defines them - this is the reference
* implementation every other language in the polyglot set is measured
* against.
*/
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const DEFAULTS = {
isRegex: false,
caseSensitive: true,
wholeWord: false,
global: true,
multiline: false,
};
/**
* Compile the find expression with flag + whole-word modifiers.
* Returns a RegExp on success, or { error } on invalid syntax.
*/
function buildRegex(find, opts) {
let pattern = opts.isRegex ? find : escapeRegExp(find);
if (opts.wholeWord) pattern = `\\b${pattern}\\b`;
let flags = opts.global ? 'g' : '';
if (!opts.caseSensitive) flags += 'i';
if (opts.isRegex && opts.multiline) flags += 'm';
try {
return new RegExp(pattern, flags);
} catch (e) {
return { error: e.message };
}
}
/**
* Count occurrences of `find` in `input`.
* In non-global mode JS String.match returns [whole, g1..gN], so the count
* is 1 + (capture-group count) when a match exists.
*/
export function countMatches(input, find, optsIn = {}) {
const opts = { ...DEFAULTS, ...optsIn };
if (find === '') return { matches: 0, error: null };
const re = buildRegex(find, opts);
if (re instanceof RegExp) {
const m = input.match(re);
return { matches: m ? m.length : 0, error: null };
}
return { matches: 0, error: re.error };
}
/**
* Replace occurrences of `find` with `replacement`.
* Never throws: invalid patterns return the input unchanged plus an error.
*/
export function findReplace(input, find, replacement, optsIn = {}) {
const opts = { ...DEFAULTS, ...optsIn };
if (find === '') return { result: input, matches: 0, error: null };
const re = buildRegex(find, opts);
if (!(re instanceof RegExp)) {
return { result: input, matches: 0, error: re.error };
}
const matches = input.match(re)?.length ?? 0;
// Rebuild a fresh RegExp: a global match() above advanced lastIndex.
const re2 = new RegExp(re.source, re.flags);
const result = input.replace(re2, replacement);
return { result, matches, error: null };
}
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →