diff --git a/README.md b/README.md index d228709..e7e1eef 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,24 @@ Same as `s.appendLeft(...)`, except that the inserted content will go *before* a Same as `s.appendRight(...)`, except that the inserted content will go *before* any previous appends or prepends at `index` +### s.replace( regexp, substitution ) + +String replacement with RegExp or string, a replacer function is also supported. Returns `this`. + +```ts +import MagicString from 'magic-string' + +const s = new MagicString(source) + +s.replace(foo, 'bar') +s.replace(/foo/g, 'bar') +s.replace(/(\w)(\d+)/g, (_, $1, $2) => $1.toUpperCase() + $2) +``` + +The differences from [`String.replace`]((https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace)): +- It will always match against the **original string** +- It mutates the magic string state (use `.clone()` to be immutable) + ### s.remove( start, end ) Removes the characters from `start` to `end` (of the original string, **not** the generated string). Removing the same content twice, or making removals that partially overlap, will cause an error. Returns `this`. diff --git a/index.d.ts b/index.d.ts index 343c49d..fd62cd8 100644 --- a/index.d.ts +++ b/index.d.ts @@ -204,6 +204,10 @@ export default class MagicString { * Removes empty lines from the start and end. */ trimLines(): MagicString; + /** + * String replacement with RegExp or string. + */ + replace(regex: RegExp | string, replacement: string | ((substring: string, ...args: any[]) => string)): MagicString; lastChar(): string; lastLine(): string; diff --git a/src/MagicString.js b/src/MagicString.js index 2cccc0d..6632043 100644 --- a/src/MagicString.js +++ b/src/MagicString.js @@ -712,4 +712,46 @@ export default class MagicString { this.trimStartAborted(charType); return this; } + + replace(searchValue, replacement) { + function getReplacement(match) { + if (typeof replacement === 'string') { + return replacement.replace(/\$(\$|&|\d+)/g, (_, i) => { + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#specifying_a_string_as_a_parameter + if (i === '$') + return '$'; + if (i === '&') + return match[0]; + const num = +i; + if (num < match.length) + return match[+i]; + return `$${i}`; + }); + } + else { + return replacement(...match); + } + } + function matchAll(re, str) { + let match; + const matches = []; + while (match = re.exec(str)) { + matches.push(match); + } + return matches; + } + if (typeof searchValue !== 'string' && searchValue.global) { + const matches = matchAll(searchValue, this.original); + matches.forEach((match) => { + if (match.index != null) + this.overwrite(match.index, match.index + match[0].length, getReplacement(match)); + }); + } + else { + const match = this.original.match(searchValue); + if (match && match.index != null) + this.overwrite(match.index, match.index + match[0].length, getReplacement(match)); + } + return this; + } } diff --git a/test/MagicString.js b/test/MagicString.js index 83ad2aa..a97f157 100644 --- a/test/MagicString.js +++ b/test/MagicString.js @@ -1277,4 +1277,40 @@ describe('MagicString', () => { assert.equal(s.lastLine(), '//lastline'); }); }); + + describe('replace', () => { + it('works with string replace', () => { + const code = '1 2 1 2'; + const s = new MagicString(code); + + s.replace('2', '3'); + + assert.strictEqual(s.toString(), '1 3 1 2'); + }); + + it('works with global regex replace', () => { + const s = new MagicString('1 2 3 4 a b c'); + + s.replace(/(\d)/g, 'xx$1$10'); + + assert.strictEqual(s.toString(), 'xx1$10 xx2$10 xx3$10 xx4$10 a b c'); + }); + + it('works with global regex replace $$', () => { + const s = new MagicString('1 2 3 4 a b c'); + + s.replace(/(\d)/g, '$$'); + + assert.strictEqual(s.toString(),'$ $ $ $ a b c'); + }); + + it('works with global regex replace function', () => { + const code = 'hey this is magic'; + const s = new MagicString(code); + + s.replace(/(\w)(\w+)/g, (_, $1, $2) => `${$1.toUpperCase()}${$2}`); + + assert.strictEqual(s.toString(),'Hey This Is Magic'); + }); + }); });