Native MagicString
Overview
experimental.nativeMagicString is an optimization feature that replaces the JavaScript-based MagicString implementation with a native Rust version, enabling source map generation in background threads for improving performance.
What is MagicString?
MagicString is a JavaScript library developed by Rich Harris (the creator of Rollup, Svelte, and Vite) that provides efficient string manipulation with automatic source map generation. It's commonly used by bundlers and build tools for:
- Code transformation in plugins
- Source map generation
- Precise line/column tracking
- Efficient string operations (replace, prepend, append, etc.)
The JavaScript Implementation vs Native Rust
Traditional JavaScript MagicString
The original MagicString implementation is written in JavaScript and runs in the Node.js environment. When bundlers perform code transformations, they typically:
- Load source code as JavaScript strings
- Apply transformations using MagicString API
- Generate source maps for the transformed code
- Process everything in the main JavaScript thread
Native Rust Implementation
Rolldown's native MagicString implementation rewrites the core functionality in Rust, providing several advantages:
- Performance: Rust's memory safety and zero-cost abstractions make string operations faster
- Parallel Processing: Source map generation can happen in background threads
- Memory Efficiency: Better memory management for large codebases
- Integration: Seamless integration with Rolldown's Rust-based architecture
How It Works
When experimental.nativeMagicString is enabled, Rolldown modifies the transformation pipeline. The diagrams below show the architectural differences:
INFO
Some technical details are simplified for better illustration. The native MagicString implementation provides a magicString object in the meta parameter of transform hooks, which plugins can use just like the JavaScript version.
Without Native MagicString
(Correction in the image: "rolldown without js magic-string" should be "rolldown without native magic-string")
With Native MagicString
Key Difference: The native implementation is written in Rust, providing both Rust's performance advantages and background thread source map generation. Offloading to background threads improves overall CPU usage and enables significant performance improvements.
API Compatibility
The native implementation maintains API compatibility with the JavaScript version. The most commonly used APIs are already implemented, with the remaining APIs planned for completion in future releases.
Real-World Performance
use rolldown/benchmarks as benchmark cases
Build time
| Runs | oxc raw transfer + js magicString | oxc raw transfer + native magicString | Time Saved | Speedup |
|---|---|---|---|---|
| apps/1000 | 497.6 ms | 431.1 ms | 66.5 ms | 1.15x |
| apps/5000 | 1.100 s | 894.5 ms | 205.5 ms | 1.23x |
| apps/10000 | 1.814 s | 1.368 s | 446.0 ms | 1.33x |
Plugin transform time (build time - noop plugin build time)
| Runs | Transform Time (oxc raw transfer + js magicString) | Transform Time (oxc raw transfer + native magicString) | Time Saved | Speedup |
|---|---|---|---|---|
| 1000 | 172.0 ms | 105.5 ms | 66.5 ms | 1.63x |
| 5000 | 455.4 ms | 249.9 ms | 205.5 ms | 1.82x |
| 10000 | 799.0 ms | 353.0 ms | 446.0 ms | 2.26x |
For detailed benchmark results, see the benchmark pull request.
Usage Examples
Basic Plugin with Native MagicString
import { defineConfig } from 'rolldown';
export default defineConfig({
experimental: {
nativeMagicString: true,
},
output: {
sourcemap: true,
},
plugins: [
{
name: 'transform-example',
transform(code, id, meta) {
if (!meta?.magicString) {
// Fallback when nativeMagicString is not available
return null;
}
const { magicString } = meta;
// Example transformation: Add debug comments
if (code.includes('console.log')) {
magicString.replace(
/console\.log\(/g,
'console.log("[DEBUG]", ',
);
}
// Example: Add file header
magicString.prepend(`// Transformed from: ${id}\n`);
return {
code: magicString,
};
},
},
],
});Compatibility and Fallbacks
Checking for Native MagicString Availability
transform(code, id, meta) {
if (meta?.magicString) {
// Native MagicString is available
const { magicString } = meta;
// Use the native implementation
// Note: Return the magicString object directly, not a string
return {
code: magicString
};
} else {
// Fallback to regular string manipulation
// or use the JavaScript MagicString library
const MagicString = require('magic-string');
const ms = new MagicString(code);
// Your transformations here...
return {
code: ms.toString(),
map: ms.generateMap()
};
}
}Rollup Compatibility
This feature is Rolldown-specific and not available in Rollup. For plugins that need to work with both bundlers:
function createTransform() {
return function(code, id, meta) {
if (meta?.magicString) {
// Rolldown with native MagicString
return transformWithNativeMagicString(code, id, meta);
} else {
// Rollup or Rolldown without native MagicString
return transformWithJsMagicString(code, id);
}
};
}When to Use Native MagicString
Recommended Scenarios
- Large Codebases: Projects with hundreds or thousands of files
- Complex Transformations: Plugins that perform extensive code manipulation
- Source Map Intensive: Projects requiring detailed source maps
- Performance-Critical: Build processes where speed is crucial
- Development Mode: Faster rebuild times during development
When to Be Cautious
- Experimental Feature: As an experimental feature, API may change
- Plugin Compatibility: Some plugins may expect specific JavaScript MagicString behavior
- Debugging: Native implementation may have different error messages
Migration Guide
Enabling Native MagicString
- Update Configuration:
export default {
experimental: {
nativeMagicString: true,
},
output: {
sourcemap: true, // Required for source map generation
},
};- Update Plugins:
// Before
transform(code, id) {
const ms = new MagicString(code);
// ... transformations
return { code: ms.toString(), map: ms.generateMap() };
}
// After
transform(code, id, meta) {
if (meta?.magicString) {
const { magicString } = meta;
// ... transformations (same API)
return { code: magicString };
}
// Fallback logic
}Limitations and Considerations
Current Limitations
- Experimental Status: API may change in future versions
- Edge Cases: Some edge cases may behave differently from JavaScript version
- Debugging: Error messages may be less familiar
Best Practices
- Always Check Availability: Verify
meta?.magicStringexists before using - Provide Fallbacks: Include fallback logic for compatibility
- Test Thoroughly: Test transformations with both implementations
- Report Issues: Report any behavior differences to the Rolldown team
Conclusion
experimental.nativeMagicString represents a significant performance optimization for Rolldown by leveraging Rust's efficiency for code transformation tasks. While it requires some considerations for compatibility, the performance benefits make it an attractive option for large-scale projects and performance-critical build processes.
As an experimental feature, it's recommended to test thoroughly in development environments before adopting in production workflows. The Rolldown team is actively working on this feature, and feedback from the community is valuable for its continued development.