Install magic-string
masterYou can install magic-string via npm for Node.js environments. It is an ESM-only package.
npm i magic-stringrepository·master·Indexed 25 days ago
https://github.com/rich-harris/magic-stringA small, fast utility for manipulating strings and generating version 3 sourcemaps. It allows for light modifications—such as replacing characters, wrapping code, or adding headers and footers—while maintaining character indices relative to the original string. It includes a Bundle class for concatenating multiple MagicString instances into a single output with a combined sourcemap.
You can install magic-string via npm for Node.js environments. It is an ESM-only package.
npm i magic-stringIn browser environments, you can import magic-string from an ESM CDN using a module script.
<script type="module">
import MagicString from 'https://unpkg.com/magic-string/dist/index.mjs';
</script>magic-string allows you to perform light modifications to a string (like replacing characters, wrapping content, or prepending/appending) while maintaining the ability to generate source maps. Character indices always refer to the original string.
Most methods are chainable.
import MagicString from 'magic-string'
const s = new MagicString('problems = 99')
s.update(0, 8, 'answer')
s.toString() // 'answer = 99'
s.update(11, 13, '42') // character indices always refer to the original string
s.toString() // 'answer = 42'
s.prepend('var ').append(';') // most methods are chainable
s.toString() // 'var answer = 42;'
const map = s.generateMap({
source: 'source.js',
file: 'converted.js.map',
includeContent: true,
}) // generates a v3 sourcemapIf you have already configured a MagicString instance with a filename or indentExclusionRanges during its construction, you can pass the instance directly to bundle.addSource() instead of using an object wrapper.
const bundle = new Bundle()
const source = new MagicString(someCode, {
filename: 'foo.js',
})
bundle.addSource(source)Use the Bundle class to concatenate several MagicString instances into a single output. You can add sources by providing an object containing filename, content, and optional properties like ignoreList or indentExclusionRanges.
filename: The name of the source file.content: A MagicString instance.ignoreList: A boolean hint for debuggers (set to false to ensure the source is not ignored).indentExclusionRanges: Passed to s.indent() to define specific ranges that should not be indented.After adding sources, you can use methods like .indent(), .prepend(), and .append() on the bundle itself. To get the final string, use .toString(). To generate a source map for the entire bundle, use .generateMap() with options similar to s.generateMap().
import MagicString, { Bundle } from 'magic-string'
const bundle = new Bundle()
bundle.addSource({
filename: 'foo.js',
content: new MagicString('var answer = 42;'),
})
bundle.addSource({
filename: 'bar.js',
content: new MagicString('console.log( answer )'),
})
// Sources can be marked as ignore-listed
bundle.addSource({
filename: 'some-3rdparty-library.js',
content: new MagicString('function myLib(){}'),
ignoreList: false,
})
bundle
.indent()
.prepend('(function () {
')
.append('}());')
const output = bundle.toString()
// (function () {
// var answer = 42;
// console.log( answer );
// }());
const map = bundle.generateMap({
file: 'bundle.js',
includeContent: true,
hires: true,
})Use these methods to change the structure of the generated string:
s.append(content): Appends content to the end.s.prepend(content): Prepends content to the start.s.appendLeft(index, content): Appends content at index in the original string. If a range ending with index is moved, the insert moves with it.s.appendRight(index, content): Appends content at index in the original string. If a range starting with index is moved, the insert moves with it.s.prependLeft(index, content): Same as appendLeft, but inserted before previous appends/prepends at index.s.prependRight(index, content): Same as appendRight, but inserted before previous appends/prepends at index.s.move(start, end, index): Moves characters from start to end to index in the original string.Perform string replacements using RegExp or strings. Unlike String.prototype.replace, these methods always match against the original string and mutate the MagicString state.
s.replace(regexpOrString, substitution): Replaces the first match.s.replaceAll(regexpOrString, substitution): Replaces all matches. If using a RegExp, it must have the global (g) flag set, otherwise a TypeError is thrown.The substitution parameter supports strings and functions.
import MagicString from 'magic-string'
const s = new MagicString(source)
s.replace('foo', 'bar')
s.replace('foo', (str, index, s) => `${str}-${index}`)
s.replace(/foo/g, 'bar')
s.replace(/(\w)(\d+)/g, (_, $1, $2) => $1.toUpperCase() + $2)
s.replaceAll('foo', 'bar')Generates a version 3 sourcemap. The returned sourcemap object includes two convenience methods:
toString(): Returns the equivalent of JSON.stringify(map).toUrl(): Returns a DataURI containing the sourcemap, useful for appending //# sourceMappingURL= to your code.Options:
file: The filename where you plan to write the sourcemap.source: The filename of the file containing the original source.includeContent: Whether to include the original content in the map's sourcesContent array.hires: Whether the mapping should be high-resolution (maps every character) or low-resolution (maps line/word boundaries). Use "boundary" for semi-hi-res mappings segmented per word boundary.When creating a new MagicString instance, you can pass an options object to configure behavior for subsequent operations or bundling.
Options:
filename: The filename of the source.indentExclusionRanges: An array of [start, end] character ranges to exclude from indentation.ignoreList: Boolean to mark source as ignore in DevTools.offset: An integer to adjust the incoming position for various APIs (like slice, update, remove, etc.).const s = new MagicString(someCode, {
filename: 'foo.js',
indentExclusionRanges: [
/* ... */
],
ignoreList: false,
offset: 0,
})Both methods allow replacing a range of the original string with new content.
s.update(start, end, content, [options]): Replaces characters from start to end. The options object can include:storeName: If true, the original name is stored for the sourcemap names array.overwrite: Defaults to false. If true, it overwrites appended/prepended content in that range. (Note: s.update is equivalent to s.overwrite with contentOnly: true).s.overwrite(start, end, content, [options]): Similar to update, but allows controlling whether appended/prepended content is overwritten via the contentOnly property.Note: Use s.update if you wish to avoid overwriting the appended/prepended content.
When instantiating a Bundle, you can provide an optional BundleOptions object to set the initial intro and separator used when joining sources.
intro (string): Content to prepend to the beginning of the bundle.separator (string): The string used to separate sources. Defaults to \n if not specified.When generating a sourcemap, you can provide a SourceMapOptions object to control the resolution, file metadata, and content inclusion.
hires: Controls mapping resolution.true: High-resolution (maps every character; precise but larger).false: Low-resolution (maps lines; faster and smaller).'boundary': Semi-high-resolution (segments per word boundary).file: The filename where the sourcemap will be written.source: The filename of the original source file.includeContent: Whether to include the original source code in the sourcesContent array.