Rich-Harris / magic-string

Manipulate strings like a wizard
MIT License
2.34k stars 113 forks source link

`update` and `overwrite` don't take into account newlines in the replacement #248

Closed bmeurer closed 1 year ago

bmeurer commented 1 year ago

Here's a tiny repro for the problem discovered in https://github.com/sveltejs/svelte/pull/8339/files#diff-0982cdc4ef56dbd22e3d1474590a2110feb82177e17a5c50ca60a806fdb0ce0d by @benmccann:

import MagicString from 'magic-string';
import open from 'open';

function magic_string_replace_all(src, search, replace) {
  let idx = src.original.indexOf(search);
  if (idx == -1) throw new Error('search not found in src');
  do {
    src.update(idx, idx + search.length, replace, {storeName: true});
  } while ((idx = src.original.indexOf(search, idx + 1)) != -1);
}

const input = `h1 {
  --replace-me-once: red;
}
h2 {
  --replace-me-twice: green;
}
div {
  --keep-me: blue;
}`;

const src = new MagicString(input);

magic_string_replace_all(src, '--replace-me-once', '\n --done-replace-once');
magic_string_replace_all(
    src, '--replace-me-twice', '\n--almost-done-replace-twice');

const map = src.generateMap({hires: true});
const code = src.toString();
const url = 'https://sokra.github.io/source-map-visualization/#base64,' + [
  code, JSON.stringify(map)
].concat(input).map(s => btoa(unescape(encodeURIComponent(s))));
open(url);

It will open the generated source map in Tobias' source map visualizer. You'll see that the mapping for the replaced words is broken now. Avoiding the newlines in the replacement strings fixes the problem and yields a proper mapping. The root cause here is that update (and overwrite) don't pay attention to potential newlines in the replacement strings, for adjusting the mapping at the point where the new string is inserted.