What's next for srcmap, and why.
Interop work, not part of published ECMA-426 core today — bloomberg.github.io/js-blog/post/standardizing-source-maps
A "sourcesFunctionMappings" array parallel to "sources", where each entry is a VLQ-encoded string mapping generated positions to original function names. Previously the x_com_bloomberg_sourcesFunctionMappings extension, and worth tracking for ecosystem interoperability.
Note: this repository tracks it as forward-looking ecosystem interoperability work. It is not currently in the published ECMA-426 core standard or the official tc39/ecma426 proposal set.
This is a simpler alternative to the full scopes proposal for resolving minified function names in stack traces. Tools that don't need full scope/binding information can use this field alone. For tools that support scopes, FindOriginalFunctionName from the scopes proposal supersedes this — but both should be supported for interop, since most source maps in the wild won't have scopes data.
- Parse
sourcesFunctionMappingsfield insrcmap-sourcemap - Decode per-source function name mappings (VLQ → function name index at position)
-
original_function_name_for(source, line, col)lookup API - Generate
sourcesFunctionMappingsinsrcmap-generator - Preserve through remapping/concatenation
- Use in symbolicate crate as fallback when scopes data is absent
- WASM bindings
- CLI: show function mappings in
srcmap info
ECMA-426 scopes proposal, actively evolving — srcmap already has srcmap-scopes, but the spec is still changing. The scopes proposal is the comprehensive approach to debugging metadata — it supersedes sourcesFunctionMappings for function name resolution and adds variable bindings, scope chains, and inlined frame reconstruction.
Boundary rules for generated ranges (tc39/ecma426#249): Defines where generated ranges must start and end relative to JavaScript syntax. Key rules:
- Generated ranges must be strictly well-nested with syntactic scopes (no partial overlap)
- For callable scopes (functions, arrows, methods), the range starts inside the opening boundary (before default parameter evaluation)
- Boundary table maps ECMAScript productions to their opening/closing boundaries and callable status:
FunctionDeclaration: opening =functionto(, closing =}, callableArrowFunction: opening =ArrowParametersto=>, closing = end ofConciseBody, callableBlockStatement: opening ={, closing =}, not callableClassDeclaration: opening =classto{, closing =}, not callable
Stack frame reconstruction algorithm (tc39/ecma426#219): Three operations being standardized:
FindOriginalFunctionName(position)— find innermost generated range, walk scope chain outward to findisStackFrame = truescopeSymbolizeStackTrace(rawFrames)— translate generated stack traces to original, expand inlined frames and collapse outlined frames across different bundlesBuildScopeChain(position)— returnOriginalScopeWithValues[]mapping original variables to concrete JS values
Hidden scopes (tc39/ecma426#113): A generated range without an originalScope reference signals compiler-generated code. Combined with isFunctionScope: if range.isFunctionScope && !range.hasDefinition, the function frame should be omitted from stack traces.
Null variable names (tc39/ecma426#244): When a names index is invalid, the variable entry becomes null (or empty string) instead of causing a parse error. Variables array type becomes (string | null)[].
- Validate generated range boundaries against JavaScript syntax rules (#249)
-
FindOriginalFunctionNamein the symbolicate crate -
SymbolizeStackTracewith inlining expansion and outlining collapse (#219) -
BuildScopeChainfor debugger integration - Handle hidden scopes (
isFunctionScope && !hasDefinition) in stack trace output - Tolerate null variable names (#244)
- Track Chrome DevTools scopes codec (@chrome-devtools/source-map-scopes-codec) for compatibility
ECMA-426 proposal, Stage 1 — sources-hash proposal
A new "sourcesHash" array parallel to "sources", containing content hashes for source file integrity verification.
{
"sources": ["src/foo.ts", "src/bar.ts"],
"sourcesHash": ["sha256-abc123...", "sha256-def456..."]
}Hash algorithm is implementation-defined (SHA-256 recommended). Format is a prefixed string like "sha256-<hex>".
- Deduplication of sources across code-split source maps
- Cache invalidation during HMR without comparing full source content
- Skipping redundant network fetches when
sourcesContentis omitted
- Parse
sourcesHashfield - Generate
sourcesHashfromsourcesContent(SHA-256 default) - Verify source content against hash
- Preserve through remapping/concatenation
- CLI: show hashes in
srcmap info, verify withsrcmap validate
ECMA-426 proposal, Stage 2 — debug-id proposal
srcmap already parses debugId from source map JSON. What's missing is extracting it from generated JavaScript/CSS files via the //# debugId=<UUID> comment.
- Scan the last 5 lines of the generated file
- Match pattern:
//# debugId=<UUID>(JS) or/*# debugId=<UUID> */(CSS) - Only
//#prefix (no legacy//@support, unlikesourceMappingURL) - Return the first match found scanning from end
- UUID format: canonical 128-bit hex with dashes (
85314830-023f-4cf1-a267-535f4e37bb17) - For reproducible builds, UUIDv3/v5 based on content hash is recommended
-
extract_debug_id(source: &str) -> Option<String>insrcmap-sourcemap - CSS comment format support (
/*# debugId=... */) - Match debug IDs between generated file and source map for validation
- CLI:
srcmap validatechecks debug ID consistency - Symbolicate crate: resolve source maps by debug ID
ECMA-426 discussion — range-mappings proposal
A proposed encoding that eliminates , and ; separators and packs metadata into VLQ bits. Claims ~35-50% raw size reduction.
- Prefix with line count, then mapping count per line, then emit all VLQs without separators
- Bit flags in genColumn (lowest 4 bits of unsigned VLQ):
- Bits 0,2: mapping length (
0b01= 4-field,0b11= 5-field, else 1-field) - Bit 1:
sourcesIndexPresent(if 0, delta = 0, reuse last) - Bit 3:
sourceLinePresent(if 0, delta = 0, reuse last) - Actual
genColumn = data >>> 4
- Bits 0,2: mapping length (
- 8-bit VLQ option: 7 data bits + 1 continuation bit per byte (binary) instead of 5+1 per base64 char
| Variant | Raw | gzip-6 | brotli-6 |
|---|---|---|---|
| Flags (6-bit VLQ) | -34.9% | -6.0% | -5.6% |
| Flags (8-bit VLQ) | -48.1% | -8.2% | -8.0% |
| Flags + remove 1-length (8-bit VLQ) | -48.8% | -9.6% | -9.5% |
This is early-stage and may never land. Worth implementing as an experimental encoder/decoder behind a feature flag if the proposal advances.
- Experimental v2 encoder behind
mappings-v2feature flag - v2 decoder
- Benchmark against v3 encoding on real-world source maps
- Track proposal status
ECMA-426 proposal, Stage 1 — env proposal
Environment metadata for source maps. The proposal is minimal at Stage 1 — track for details before implementing.
| Gap | Severity | Impact |
|---|---|---|
| Serialization overhead | Medium | Rspack |
| SIMD VLQ decoding | Medium | All consumers; biggest wins on 1MB+ maps |
VLQ encoding and composition have been optimized. Serialization may still have room for improvement — profile against rspack-sources to verify.
The scalar VLQ decoder is well-tuned (single-char fast path, fully inlined as of 2026-04 — see perf_findings.md) but VLQ decoding is still 35-75% of total parse time on real-world maps (preact 34%, chartjs 73%, pdfjs 61%). A SIMD base64+VLQ implementation could shave another 1.5-2x off that portion. Would need either base64-simd + custom VLQ logic, or a from-scratch SIMD VLQ pass. Anti-finding: both sonic-rs and simd-json were prototyped as JSON parser swaps and proved to be slower (sonic-rs) or significantly slower (simd-json) on srcmap's typical input sizes — JSON parse is not the bottleneck.
| Target | Approach | Stars |
|---|---|---|
| Sentry CLI | Contribute improvements upstream to rust-sourcemap, or position for symbolicator |
2k |
| Node.js runtime | Publish benchmarks → contribute algorithm improvements → WASM vendoring proposal | 110k |
| Webpack | Add streaming API matching StreamChunksOfCombinedSourceMap pattern |
65k |
| Metro | Low priority — needs multiple wrappers for custom source map extensions | 5.2k |
- Source map v1/v2 format support
- Tight coupling to any specific bundler or compiler