From a205dca4c58f43ffea2f96f1b7fcc65155ba8e03 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Mon, 2 Mar 2026 17:55:28 -0800 Subject: [PATCH 1/6] trace-mapping: add operations needed for remapping * Adjust rangeSegments to map to range mapping info such as the end point, not just a boolean state of being a range or not. * Add isRange to allow querying the range mapping data for a segment * Add traceSegmentsInRange for finding segments in a range --- packages/trace-mapping/src/by-source.ts | 2 +- packages/trace-mapping/src/trace-mapping.ts | 127 ++++++++++++++++-- packages/trace-mapping/src/types.ts | 6 + .../trace-mapping/test/trace-mapping.test.ts | 20 +++ 4 files changed, 146 insertions(+), 9 deletions(-) diff --git a/packages/trace-mapping/src/by-source.ts b/packages/trace-mapping/src/by-source.ts index 5a69454..ed7bc13 100644 --- a/packages/trace-mapping/src/by-source.ts +++ b/packages/trace-mapping/src/by-source.ts @@ -15,7 +15,7 @@ export type Source = { export default function buildBySources( decoded: readonly SourceMapSegment[][], memos: unknown[], - rangeSegments: Set, + rangeSegments: Map, ): Source[] { const sources: Source[] = memos.map(() => ({ lines: [], rangeSegments: new Set() })); diff --git a/packages/trace-mapping/src/trace-mapping.ts b/packages/trace-mapping/src/trace-mapping.ts index 8be9362..6354617 100644 --- a/packages/trace-mapping/src/trace-mapping.ts +++ b/packages/trace-mapping/src/trace-mapping.ts @@ -1,4 +1,9 @@ -import { encode, decode, encodeRangeMappings, decodeRangeMappings } from '@jridgewell/sourcemap-codec'; +import { + encode, + decode, + encodeRangeMappings, + decodeRangeMappings, +} from '@jridgewell/sourcemap-codec'; import resolver from './resolve'; import maybeSort from './sort'; @@ -39,6 +44,7 @@ import type { XInput, SectionedSourceMap, Ro, + RangeInfo, } from './types'; import type { Source } from './by-source'; import type { MemoState } from './binary-search'; @@ -156,9 +162,9 @@ export class TraceMap implements SourceMap { declare private _decodedRangeMappings: number[][] | undefined; /** - * A set of segments that are range mappings. + * A map of segments that are range mappings to info about the range. */ - declare private _rangeSegments: Set | undefined; + declare private _rangeSegments: Map | undefined; constructor(map: Ro, mapUrl?: string | null) { const isString = typeof map === 'string'; @@ -272,6 +278,77 @@ export function traceSegment( ); } +/** + * Find all the segments that are in a given range. Used to find + * segments that are relevant for composing a range mapping with + * another range. + */ +export function traceSegmentsInRange( + map: TraceMap, + startLine: number, + startColumn: number, + endLine: number, + endColumn: number, +): Readonly[] { + const lines = decodedMappings(map); + const segments = startLine < lines.length ? lines[startLine] : []; + const memo = cast(map)._decodedMemo; + + if (startLine >= lines.length || endLine >= lines.length) return []; + + const segmentsInRange = []; + + let startIndex = memoizedBinarySearchSegments(segments, startColumn, memo, startLine); + if (bsFound) { + startIndex = lowerBound(segments, startColumn, startIndex); + } else { + startIndex++; + } + if (startIndex === segments.length) return []; + + const range = (start: number, end: number) => { + return Array.from({ length: end - start + 1 }, (_, i: number) => i + start); + }; + const gen = (line: number) => (index: number) => { + return lines[line][index]; + }; + + function previousPosition(line: number, column: number) { + if (column === 0) { + line--; + column = Infinity; + } else { + column--; + } + + return { line, column }; + } + + if (startLine == endLine) { + // We need to use the position decremented by one position because we + // want the index of a true lower bound, not an equal position. That is, + // the end point of the range should be exclusive and not inclusive. + const { line, column } = previousPosition(endLine, endColumn); + let endIndex = memoizedBinarySearchSegments(segments, column, memo, line); + if (bsFound) { + endIndex = upperBound(segments, column, endIndex); + } + segmentsInRange.push(...range(startIndex, endIndex).map(gen(startLine))); + } else { + segmentsInRange.push(...range(startIndex, segments.length - 1).map(gen(startLine))); + for (let i = startLine + 1; i < endLine; i++) { + segmentsInRange.push(...range(0, lines[i].length - 1).map(gen(i))); + } + let endIndex = memoizedBinarySearchSegments(lines[endLine], endColumn, memo, endLine); + if (bsFound) { + endIndex = upperBound(lines[endLine], endColumn, endIndex); + } + segmentsInRange.push(...range(0, endIndex).map(gen(endLine))); + } + + return segmentsInRange; +} + /** * A higher-level API to find the source/line/column associated with a generated line/column * (think, from a stack trace). Line is 1-based, but column is 0-based, due to legacy behavior in @@ -397,6 +474,16 @@ export function isIgnored(map: TraceMap, source: string): boolean { return index === -1 ? false : ignoreList.includes(index); } +/** + * Determines if a segment is for a range mapping, and if so returns + * some metadata about the range. + */ +export function isRange(map: TraceMap, segment: Readonly): RangeInfo | false { + const decoded = decodedMappings(map); + const rangeSegments = initRangeSegments(map, decoded); + return rangeSegments.get(segment as SourceMapSegment) || false; +} + /** * A helper that skips sorting of the input map's mappings array, which can be expensive for larger * maps. @@ -485,7 +572,7 @@ function GMapping( */ function traceSegmentInternal( lines: readonly T[][], - rangeSegments: Set, + rangeSegments: Set | Map, memo: MemoState, line: number, column: number, @@ -493,7 +580,7 @@ function traceSegmentInternal( ): T | null; function traceSegmentInternal( lines: readonly T[][], - rangeSegments: Set, + rangeSegments: Set | Map, memo: MemoState, line: number, column: number, @@ -501,7 +588,7 @@ function traceSegmentInternal( ): T | null; function traceSegmentInternal( lines: readonly T[][], - rangeSegments: Set, + rangeSegments: Set | Map, memo: MemoState, line: number, column: number, @@ -700,7 +787,7 @@ function initRangeSegments(map: TraceMap, decoded: readonly SourceMapSegment[][] const existing = cast(map)._rangeSegments; if (existing != null) return existing; - const set = new Set(); + const set = new Map(); cast(map)._rangeSegments = set; const rangeMappings = decodedRangeMappings(map); if (rangeMappings == null) return set; @@ -710,13 +797,37 @@ function initRangeSegments(map: TraceMap, decoded: readonly SourceMapSegment[][] const ranges = rangeMappings[i]; for (let j = 0; j < ranges.length; j++) { const seg = line[ranges[j]]; - set.add(seg); + const { line: endLine, index: endIndex } = findNextSegment(decoded, i, ranges[j]); + const endSegment = endLine && endIndex ? decoded[endLine][endIndex] : null; + set.set(seg, { line: i, endLine, endSegment }); } } return set; } +/** + * Find the index of the next segment in this line or in subsequent lines. + * Return null if there was no next segment. + */ +function findNextSegment( + lines: readonly T[][], + line: number, + index: number, +): { line: number | null; index: number | null } { + if (index + 1 < lines[line].length) { + return { line, index: index + 1 }; + } else { + for (let i = line + 1; i < lines.length; i++) { + for (let j = 0; j < lines[i].length; j++) { + return { line: i, index: j }; + } + } + } + + return { line: null, index: null }; +} + /** * If we didn't find a match on this line, back searches to find the previous * line that has a segment. diff --git a/packages/trace-mapping/src/types.ts b/packages/trace-mapping/src/types.ts index fbd481f..1cbb6e0 100644 --- a/packages/trace-mapping/src/types.ts +++ b/packages/trace-mapping/src/types.ts @@ -91,6 +91,12 @@ export type EachMapping = name: string | null; }; +export type RangeInfo = { + line: number; + endLine: number | null; + endSegment: Readonly | null; +}; + export abstract class SourceMap { declare version: SourceMapV3['version']; declare file: SourceMapV3['file']; diff --git a/packages/trace-mapping/test/trace-mapping.test.ts b/packages/trace-mapping/test/trace-mapping.test.ts index e0628da..4a66852 100644 --- a/packages/trace-mapping/test/trace-mapping.test.ts +++ b/packages/trace-mapping/test/trace-mapping.test.ts @@ -8,6 +8,7 @@ import { encodedMappings, decodedMappings, traceSegment, + traceSegmentsInRange, originalPositionFor, generatedPositionFor, presortedDecodedMap, @@ -446,6 +447,25 @@ describe('TraceMap', () => { [{ line: 1, column: 13 }], ); }); + describe('rangeMappings operations', () => { + // 2nd line, 5th mapping is a range mapping + const mapWithRangeMappings = replaceField(map, 'rangeMappings', ';F;'); + + it('traceSegmentsInRange', () => { + const tracer = new TraceMap(mapWithRangeMappings); + assert.deepEqual(traceSegmentsInRange(tracer, 0, 9, 0, 16), [ + [9, 0, 0, 9, 0], + [12, 0, 0, 0], + [13, 0, 0, 13, 1], + ]); + assert.deepEqual(traceSegmentsInRange(tracer, 0, 16, 1, 9), [ + [16, 0, 0, 0], + [18, 0, 0, 33], + [4, 0, 1, 4], + [8, 0, 1, 10], + ]); + }); + }); }; } From 62cdf4587fa514bdd0bd3d1514e01589cf127957 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Mon, 2 Mar 2026 15:59:42 -0800 Subject: [PATCH 2/6] remapping: support range mapping composition --- packages/remapping/src/source-map-tree.ts | 301 ++++++++++++++++-- .../test/unit/source-map-tree.test.ts | 169 +++++++++- 2 files changed, 451 insertions(+), 19 deletions(-) diff --git a/packages/remapping/src/source-map-tree.ts b/packages/remapping/src/source-map-tree.ts index 935240f..7d3faf8 100644 --- a/packages/remapping/src/source-map-tree.ts +++ b/packages/remapping/src/source-map-tree.ts @@ -1,5 +1,17 @@ -import { GenMapping, maybeAddSegment, setIgnore, setSourceContent } from '@jridgewell/gen-mapping'; -import { traceSegment, decodedMappings } from '@jridgewell/trace-mapping'; +import { + GenMapping, + maybeAddSegment, + setIgnore, + setSourceContent, + setRangeSegment, +} from '@jridgewell/gen-mapping'; +import { + traceSegment, + traceSegmentsInRange, + isRange, + decodedMappings, + decodedRangeMappings, +} from '@jridgewell/trace-mapping'; import type { TraceMap } from '@jridgewell/trace-mapping'; @@ -10,6 +22,8 @@ export type SourceMapSegmentObject = { source: string; content: string | null; ignore: boolean; + isRangeMapping: boolean; + rangeMappingOffset: { line: number; column: number }; }; export type OriginalSource = { @@ -40,8 +54,19 @@ function SegmentObject( name: string, content: string | null, ignore: boolean, + isRangeMapping?: boolean, + rangeMappingOffset?: { line: number; column: number }, ): SourceMapSegmentObject { - return { source, line, column, name, content, ignore }; + return { + source, + line, + column, + name, + content, + ignore, + isRangeMapping: isRangeMapping || false, + rangeMappingOffset: rangeMappingOffset || { line: 0, column: 0 }, + }; } function Source( @@ -105,36 +130,113 @@ export function traceMappings(tree: MapSource): GenMapping { const { sources: rootSources, map } = tree; const rootNames = map.names; const rootMappings = decodedMappings(map); + const rootRangeMappings = decodedRangeMappings(map) || []; + + // Find the next segment either in the current line or in + // the next line if we're at the end and there are further lines. + function nextSegment(line: number, index: number) { + let current = index + 1; + + while (line < rootMappings.length) { + if (current < rootMappings[line].length) { + return { line, segment: rootMappings[line][current] }; + } else { + line++; + current = 0; + } + } + + return null; + } for (let i = 0; i < rootMappings.length; i++) { const segments = rootMappings[i]; + const rangeMappings = rootRangeMappings[i] || []; for (let j = 0; j < segments.length; j++) { const segment = segments[j]; + const isRangeMapping = rangeMappings.includes(j); const genCol = segment[0]; - let traced: SourceMapSegmentObject | null = SOURCELESS_MAPPING; - // 1-length segments only move the current generated column, there's no source information - // to gather from it. - if (segment.length !== 1) { + if (segment.length === 1 || !isRangeMapping) { + let tracedSegment: SourceMapSegmentObject | null = SOURCELESS_MAPPING; + + // 1-length segments only move the current generated column, there's no source information + // to gather from it. + if (segment.length !== 1) { + const source = rootSources[segment[1]]; + + tracedSegment = originalPositionFor( + source, + segment[2], + segment[3], + segment.length === 5 ? rootNames[segment[4]] : '', + ); + + // If the trace is invalid, then the trace ran into a sourcemap that doesn't contain a + // respective segment into an original source. + if (tracedSegment === null) continue; + } + + const { column, line, name, content, source, ignore } = tracedSegment!; + + maybeAddSegment(gen, i, genCol, source, line, column, name); + if (source && content != null) setSourceContent(gen, source, content); + if (ignore) setIgnore(gen, source, true); + } else { + // isRangeMapping const source = rootSources[segment[1]]; - traced = originalPositionFor( + + // Find end segment, if none exists it's an invalid range mapping and + // we will skip it. + const next = nextSegment(i, j); + if (next === null) continue; + const { line: nextSegmentLine, segment: endSegment } = next; + const rangeLineOffset = nextSegmentLine - i; + const rangeColumnOffset = endSegment[0] - segment[0]; + const endLine = segment[2] + rangeLineOffset; + const endColumn = segment[2] === endLine ? segment[3] + rangeColumnOffset : endSegment[0]; + + const tracedSegments = originalPositionsForRange( source, segment[2], segment[3], segment.length === 5 ? rootNames[segment[4]] : '', + endLine, + endColumn, + false, ); - // If the trace is invalid, then the trace ran into a sourcemap that doesn't contain a - // respective segment into an original source. - if (traced == null) continue; - } + if (tracedSegments.length === 0) continue; - const { column, line, name, content, source, ignore } = traced; + for (const tracedSegment of tracedSegments) { + const { + column, + line, + name, + content, + source, + ignore, + isRangeMapping, + rangeMappingOffset, + } = tracedSegment; - maybeAddSegment(gen, i, genCol, source, line, column, name); - if (source && content != null) setSourceContent(gen, source, content); - if (ignore) setIgnore(gen, source, true); + // The range mapping offset is the amount that we need to offset the + // generated line/column from the root. We have to return this up + // because originalPositionsForRange can't increment it. + const genLine = i + rangeMappingOffset.line; + // If the traced segment isn't on the same line as the range start, + // genCol is irrelevant + const genColumn = + rangeMappingOffset.line === 0 + ? genCol + rangeMappingOffset.column + : rangeMappingOffset.column; + maybeAddSegment(gen, genLine, genColumn, source, line, column, name, null); + setRangeSegment(gen, genLine, genColumn, isRangeMapping); + if (source && content != null) setSourceContent(gen, source, content); + if (ignore) setIgnore(gen, source, true); + } + } } } @@ -159,14 +261,177 @@ export function originalPositionFor( // If we couldn't find a segment, then this doesn't exist in the sourcemap. if (segment == null) return null; + const maybeRange = isRange(source.map, segment); + let startLine = 0; // FIXME this should be the line of the segment + if (maybeRange) { + startLine = maybeRange.line; + } + // 1-length segments only move the current generated column, there's no source information // to gather from it. if (segment.length === 1) return SOURCELESS_MAPPING; + // If the child is a range mapping, we need to offset the next lookup point by the + // offset of the parent mapping into the range. + let rangeMappingOffset = { line: 0, column: 0 }; + if (maybeRange) { + if (startLine === line) rangeMappingOffset = { line: 0, column: column - segment[0] }; + else rangeMappingOffset = { line: line - startLine, column: 0 }; + } + return originalPositionFor( source.sources[segment[1]], - segment[2], - segment[3], + segment[2] + rangeMappingOffset.line, + segment[3] + rangeMappingOffset.column, segment.length === 5 ? source.map.names[segment[4]] : name, ); } + +function originalPositionsForRange( + source: Sources, + line: number, + column: number, + name: string, + endLine: number, + endColumn: number, + emitEndPoint: boolean, +): SourceMapSegmentObject[] { + // If this is the bottom node then we just return the current range. + if (source.map === null) { + return [ + SegmentObject(source.source, line, column, '', source.content, source.ignore, true), + // The end point isn't always emitted, because the end point may be + // a separate mapping that will be processed & translated too. We only emit this + // if we need to make up a mapping because we split or clamped a range. + ...(emitEndPoint + ? [ + SegmentObject( + source.source, + endLine, + endColumn, + '', + source.content, + source.ignore, + false, + { line: endLine - line, column: endColumn - column }, + ), + ] + : []), + ]; + } + + // We additional trace the start position of the range to because we may need to + // intersect with a range that starts before the given position, or map the start + // of the range to it if there aren't any exact hits. + const initialSegment = traceSegment(source.map, line, column); + const segments = traceSegmentsInRange(source.map, line, column, endLine, endColumn); + + // If tracing the start of the range hits a mapping that isn't in the segmenObjects list, + // add it to the list to process first. + if (initialSegment !== null && (segments.length === 0 || initialSegment !== segments[0])) { + segments.splice(0, 0, initialSegment); + } + + const originalPositions = []; + for (const segment of segments) { + if (segment == null) continue; + + let startLine = 0; // FIXME this should be the line of the segment + let childEndLine, endSegment; + const maybeRange = isRange(source.map, segment); + if (maybeRange) { + startLine = maybeRange.line; + childEndLine = maybeRange.endLine; + endSegment = maybeRange.endSegment; + } + + // At the very beginning of a range, the child position might be behind + // the start of the range. In that case we clamp the offset to 0. + const rangeOffsetLine = startLine - line; + const rangeOffsetColumn = rangeOffsetLine === 0 ? Math.max(0, segment[0] - column) : segment[0]; + const rangeOffset = { line: rangeOffsetLine, column: rangeOffsetColumn }; + + // Sourceless mappings just have the offset added and we skip the recursive + // step because there's no source to process. + if (segment.length === 1) { + const mapping = SOURCELESS_MAPPING; + mapping.rangeMappingOffset = rangeOffset; + originalPositions.push(mapping); + continue; + } + + if (!maybeRange) { + const position = originalPositionFor( + source.sources[segment[1]], + segment[2], + segment[3], + segment.length === 5 ? source.map.names[segment[4]] : name, + ); + if (position !== null) { + position.rangeMappingOffset.line += rangeOffset.line; + position.rangeMappingOffset.column += rangeOffset.column; + originalPositions.push(position); + } + } else { + // Compute the intersection of the child and parent ranges. + // + // line,column endLine,endColumn + // Parent range |-----------------------------| + // Child range |------------------------| + // startLine,segment[0] childEndLine,endSegment[0] + // Child mapped |------------------------| + // segment[2],segment[3] endSegment[2],endSegment[3] + // + // For example, if segment[0] < column as in this diagram, we + // need to clamp the start point to column, which maps to + // segment[3] + (column - segment[0]). The end point needs to + // be clamped if endSegment[3] > endColumn. + const clampedStartLine = Math.max(line, startLine); + const clampedStartColumn = Math.max(column, segment[0]); + const clampedEndLine = Math.min(endLine, childEndLine!); + const clampedEndColumn = Math.min(endColumn, endSegment![0]); + + const originalStartLine = segment[2] + (clampedStartLine - startLine); + let originalStartColumn; + if (startLine == line) { + originalStartColumn = segment[3] + (clampedStartColumn - segment[0]); + } else if (startLine > line) { + originalStartColumn = segment[3]; + } else { + originalStartColumn = column; + } + + const originalEndLine = originalStartLine + (clampedEndLine - clampedStartLine); + // When the range ends on the same line, the end column is calculated from the + // segment distance because the range is exclusive of the end segment. + // If the range ends on a different line, we end on the end segment generated + // column. + const originalEndColumn = + originalStartLine == originalEndLine + ? originalStartColumn + (clampedEndColumn - clampedStartColumn) + : clampedEndColumn; + + const positions = originalPositionsForRange( + source.sources[segment[1]], + originalStartLine, + originalStartColumn, + segment.length === 5 ? source.map.names[segment[4]] : name, + originalEndLine, + originalEndColumn, + // If the range had to be clamped then we need to emit a new mapping + // for the end, as no existing explicit mapping will exist at that point. + // Otherwise, we should be able to rely on the original end mapping being + // translated appropriately. + clampedEndLine !== childEndLine || clampedEndColumn !== endSegment![0], + ); + + for (const position of positions) { + position.rangeMappingOffset.line += rangeOffset.line; + position.rangeMappingOffset.column += rangeOffset.column; + } + originalPositions.push(...positions); + } + } + + return originalPositions; +} diff --git a/packages/remapping/test/unit/source-map-tree.test.ts b/packages/remapping/test/unit/source-map-tree.test.ts index 68730c4..e343677 100644 --- a/packages/remapping/test/unit/source-map-tree.test.ts +++ b/packages/remapping/test/unit/source-map-tree.test.ts @@ -1,4 +1,4 @@ -import { toDecodedMap } from '@jridgewell/gen-mapping'; +import { toDecodedMap, toEncodedMap } from '@jridgewell/gen-mapping'; import { TraceMap } from '@jridgewell/trace-mapping'; import assert from 'node:assert/strict'; @@ -31,7 +31,9 @@ describe('MapSource', () => { [4, 0, 1, 1], ], // line 0 [[1, 0, 0, 0, 0], [6]], // line 1 + [[3, 0, 3, 7], [9]], // line 2 ], + rangeMappings: [[], [], [0]], names: ['child'], sources: ['original.js'], version: 3, @@ -215,6 +217,141 @@ describe('MapSource', () => { assert.deepEqual(traced.mappings, [[[0, 0, 0, 0]], [[0, 0, 0, 0]]]); }); }); + + describe('range mappings', () => { + it('range mapping in base maps into normal mappings', () => { + const map: DecodedSourceMap = { + ...baseMap, + mappings: [[[5, 0, 0, 1], [9]]], + rangeMappings: [[0]], + }; + + const tree = MapSource(new TraceMap(map), [child]); + const traced = toDecodedMap(traceMappings(tree)); + // This doesn't have mappings at column 6 or 7 because they don't + // add more info (they'd map to the same original location). + assert.deepEqual(traced.mappings, [[[5, 0, 0, 0], [8, 0, 1, 1], [9]]]); + }); + + it('multi-line range mapping in base maps into normal mappings', () => { + const map: DecodedSourceMap = { + ...baseMap, + // range from [(1,5), (2,7)] maps to [(0,3), (1,7)] + mappings: [[], [[5, 0, 0, 3]], [[7, 0, 1, 7]]], + rangeMappings: [[], [0], []], + }; + + const tree = MapSource(new TraceMap(map), [child]); + const traced = toDecodedMap(traceMappings(tree)); + assert.deepEqual(traced.mappings, [ + [], + [ + [5, 0, 0, 0], + [6, 0, 1, 1], + ], + [[1, 0, 0, 0, 0], [6]], + ]); + }); + + it('multi-line range mapping in base maps into range mappings', () => { + const map: DecodedSourceMap = { + ...baseMap, + // range from [(0,2), (1,5)] maps to [(1,0), (2,5)] + mappings: [[[2, 0, 1, 0]], [[5, 0, 2, 5]]], + rangeMappings: [[0], []], + }; + + const tree = MapSource(new TraceMap(map), [child]); + const traced = toDecodedMap(traceMappings(tree)); + assert.deepEqual(traced.mappings, [ + [[3, 0, 0, 0, 0], [8]], + [ + [3, 0, 3, 7], + [5, 0, 3, 9], + ], + ]); + assert.deepEqual(traced.rangeMappings, [[], [0]]); + }); + + it('normal mapping in base maps into range mappings', () => { + const map: DecodedSourceMap = { + ...baseMap, + // child range mapping maps line 2, columns 3-9 + // so line 2, col 3 and line 2, col 5 are in the range + mappings: [ + [], + [], + [ + [5, 0, 2, 3], + [19, 0, 2, 5], + ], + ], + }; + + const tree = MapSource(new TraceMap(map), [child]); + const traced = toDecodedMap(traceMappings(tree)); + assert.deepEqual(traced.mappings, [ + [], + [], + [ + [5, 0, 3, 7], + [19, 0, 3, 9], + ], + ]); + }); + + it('range mapping in base maps into range mappings in child', () => { + const map: DecodedSourceMap = { + ...baseMap, + mappings: [ + [], + [], + [ + [5, 0, 2, 3], + [7, 0, 2, 5], + [15, 0, 2, 7], + [17, 0, 2, 9], + ], + ], + rangeMappings: [[], [], [0, 2]], + }; + + const tree = MapSource(new TraceMap(map), [child]); + const traced = traceMappings(tree); + const decoded = toDecodedMap(traced); + assert.deepEqual(decoded.mappings, [ + [], + [], + [[5, 0, 3, 7], [7, 0, 3, 9], [15, 0, 3, 11], [17]], + ]); + assert.deepEqual(decoded.rangeMappings, [[], [], [0, 2]]); + const encoded = toEncodedMap(traced); + assert.deepEqual(encoded.rangeMappings, ';;BC'); + }); + + it('range mapping in base maps into sub-ranges in child', () => { + const map: DecodedSourceMap = { + ...baseMap, + mappings: [ + [], + [], + [ + // base range has length 10, child range has length 6 + [15, 0, 2, 0], + [25, 0, 2, 10], + ], + ], + rangeMappings: [[], [], [0]], + }; + + const tree = MapSource(new TraceMap(map), [child]); + const traced = toDecodedMap(traceMappings(tree)); + assert.deepEqual(traced.mappings, [[], [], [[18, 0, 3, 7], [24]]]); + assert.deepEqual(traced.rangeMappings, [[], [], [0]]); + const encoded = toEncodedMap(traceMappings(tree)); + assert.deepEqual(encoded.rangeMappings, ';;B'); + }); + }); }); describe('originalPositionFor()', () => { @@ -340,4 +477,34 @@ describe('MapSource', () => { }); }); }); + + //describe('traceMappings() with range mappings', () => { + // const sourceRoot = 'foo'; + // const baseMap: DecodedSourceMap = { + // mappings: [], + // rangeMappings: [], + // names: ['name'], + // sourceRoot, + // sources: ['child.js'], + // version: 3, + // }; + // const child = MapSource( + // new TraceMap({ + // mappings: [ + // [ + // [0, 0, 0, 0], + // [1, 0, 0, 0], + // [2, 0, 0, 0], + // [4, 0, 1, 1], + // ], // line 0 + // [[1, 0, 0, 0, 0], [6]], // line 1 + // ], + // rangeMappings: [[]], + // names: ['child'], + // sources: ['original.js'], + // version: 3, + // }), + // [OriginalSource(`${sourceRoot}/original.js`, '', false)], + // ); + //}); }); From ae462f055010d0add854c4b97b178b32ef63fbae Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Thu, 5 Mar 2026 13:44:06 -0800 Subject: [PATCH 3/6] gen-mapping: encode range mappings in toEncodedMap --- packages/gen-mapping/src/gen-mapping.ts | 3 ++- packages/gen-mapping/src/types.ts | 17 +++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/packages/gen-mapping/src/gen-mapping.ts b/packages/gen-mapping/src/gen-mapping.ts index 84a8ec7..bbf7319 100644 --- a/packages/gen-mapping/src/gen-mapping.ts +++ b/packages/gen-mapping/src/gen-mapping.ts @@ -1,5 +1,5 @@ import { SetArray, put, remove } from './set-array'; -import { encode } from '@jridgewell/sourcemap-codec'; +import { encode, encodeRangeMappings } from '@jridgewell/sourcemap-codec'; import { TraceMap, decodedMappings } from '@jridgewell/trace-mapping'; import { @@ -290,6 +290,7 @@ export function toEncodedMap(map: GenMapping): EncodedSourceMap { const decoded = toDecodedMap(map); const encoded = decoded as unknown as EncodedSourceMap; encoded.mappings = encode(decoded.mappings as SourceMapSegment[][]); + encoded.rangeMappings = encodeRangeMappings((decoded.rangeMappings || []) as number[][]); return encoded; } diff --git a/packages/gen-mapping/src/types.ts b/packages/gen-mapping/src/types.ts index fa4edb5..1b8e4a6 100644 --- a/packages/gen-mapping/src/types.ts +++ b/packages/gen-mapping/src/types.ts @@ -41,12 +41,6 @@ export interface SourceMapV3 { * An optional array of indices of sources that should be ignored. */ ignoreList?: readonly number[]; - - /** - * An optional array of mapping indicies which cover a range of generated - * code and source code. - */ - rangeMappings?: MappingIndex[][]; } /** @@ -57,6 +51,11 @@ export interface EncodedSourceMap extends SourceMapV3 { * The mappings for the sourcemap, encoded as a VLQ string. */ mappings: string; + + /** + * An optional VLQ encoded string for the range mapping indices. + */ + rangeMappings?: string; } /** @@ -68,6 +67,12 @@ export interface DecodedSourceMap extends SourceMapV3 { * The mappings for the sourcemap, decoded into our internal format. */ mappings: readonly SourceMapSegment[][]; + + /** + * An optional array of mapping indicies which cover a range of generated + * code and source code. + */ + rangeMappings?: MappingIndex[][]; } /** From 996cbefc239e1144f649dfb9e9d7ec5e2d326c81 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Thu, 5 Mar 2026 13:45:42 -0800 Subject: [PATCH 4/6] remapping: get most test cases passing * add back line information for traceSegmentsInRange because it needs to be represented somehow in the return result (an alternative would be a start line + array of arrays) * fixes most range mapping remapping test failures that came up due to rebasing these patches --- packages/remapping/src/source-map-tree.ts | 12 +++++------- packages/trace-mapping/src/trace-mapping.ts | 10 ++++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/remapping/src/source-map-tree.ts b/packages/remapping/src/source-map-tree.ts index 7d3faf8..045e5b5 100644 --- a/packages/remapping/src/source-map-tree.ts +++ b/packages/remapping/src/source-map-tree.ts @@ -262,7 +262,7 @@ export function originalPositionFor( // If we couldn't find a segment, then this doesn't exist in the sourcemap. if (segment == null) return null; const maybeRange = isRange(source.map, segment); - let startLine = 0; // FIXME this should be the line of the segment + let startLine = line; if (maybeRange) { startLine = maybeRange.line; } @@ -320,7 +320,7 @@ function originalPositionsForRange( ]; } - // We additional trace the start position of the range to because we may need to + // We additionally trace the start position of the range to because we may need to // intersect with a range that starts before the given position, or map the start // of the range to it if there aren't any exact hits. const initialSegment = traceSegment(source.map, line, column); @@ -328,19 +328,17 @@ function originalPositionsForRange( // If tracing the start of the range hits a mapping that isn't in the segmenObjects list, // add it to the list to process first. - if (initialSegment !== null && (segments.length === 0 || initialSegment !== segments[0])) { - segments.splice(0, 0, initialSegment); + if (initialSegment !== null && (segments.length === 0 || initialSegment !== segments[0][1])) { + segments.splice(0, 0, [line, initialSegment]); } const originalPositions = []; - for (const segment of segments) { + for (const [startLine, segment] of segments) { if (segment == null) continue; - let startLine = 0; // FIXME this should be the line of the segment let childEndLine, endSegment; const maybeRange = isRange(source.map, segment); if (maybeRange) { - startLine = maybeRange.line; childEndLine = maybeRange.endLine; endSegment = maybeRange.endSegment; } diff --git a/packages/trace-mapping/src/trace-mapping.ts b/packages/trace-mapping/src/trace-mapping.ts index 6354617..81ab91d 100644 --- a/packages/trace-mapping/src/trace-mapping.ts +++ b/packages/trace-mapping/src/trace-mapping.ts @@ -289,7 +289,7 @@ export function traceSegmentsInRange( startColumn: number, endLine: number, endColumn: number, -): Readonly[] { +): [number, Readonly][] { const lines = decodedMappings(map); const segments = startLine < lines.length ? lines[startLine] : []; const memo = cast(map)._decodedMemo; @@ -309,9 +309,11 @@ export function traceSegmentsInRange( const range = (start: number, end: number) => { return Array.from({ length: end - start + 1 }, (_, i: number) => i + start); }; - const gen = (line: number) => (index: number) => { - return lines[line][index]; - }; + function gen(line: number) { + return (index: number) : [number, Readonly] => { + return [line, lines[line][index]]; + }; + } function previousPosition(line: number, column: number) { if (column === 0) { From 50a8648b4771d1c24ee64c095ab713a6c15f9382 Mon Sep 17 00:00:00 2001 From: Asumu Takikawa Date: Mon, 23 Mar 2026 17:05:44 -0700 Subject: [PATCH 5/6] Additional fixes * Expose range mappings from remapping SourceMap * Conditionalize calling setRangeSegment --- packages/remapping/src/source-map-tree.ts | 3 ++- packages/remapping/src/source-map.ts | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/remapping/src/source-map-tree.ts b/packages/remapping/src/source-map-tree.ts index 045e5b5..37f5634 100644 --- a/packages/remapping/src/source-map-tree.ts +++ b/packages/remapping/src/source-map-tree.ts @@ -232,7 +232,8 @@ export function traceMappings(tree: MapSource): GenMapping { ? genCol + rangeMappingOffset.column : rangeMappingOffset.column; maybeAddSegment(gen, genLine, genColumn, source, line, column, name, null); - setRangeSegment(gen, genLine, genColumn, isRangeMapping); + if (isRangeMapping) + setRangeSegment(gen, genLine, genColumn); if (source && content != null) setSourceContent(gen, source, content); if (ignore) setIgnore(gen, source, true); } diff --git a/packages/remapping/src/source-map.ts b/packages/remapping/src/source-map.ts index 5156086..607af4b 100644 --- a/packages/remapping/src/source-map.ts +++ b/packages/remapping/src/source-map.ts @@ -10,6 +10,7 @@ import type { DecodedSourceMap, EncodedSourceMap, Options } from './types'; export default class SourceMap { declare file?: string | null; declare mappings: EncodedSourceMap['mappings'] | DecodedSourceMap['mappings']; + declare rangeMappings?: EncodedSourceMap['rangeMappings'] | DecodedSourceMap['rangeMappings']; declare sourceRoot?: string; declare names: string[]; declare sources: (string | null)[]; @@ -22,6 +23,7 @@ export default class SourceMap { this.version = out.version; // SourceMap spec says this should be first. this.file = out.file; this.mappings = out.mappings as SourceMap['mappings']; + this.rangeMappings = out.rangeMappings as SourceMap['rangeMappings']; this.names = out.names as SourceMap['names']; this.ignoreList = out.ignoreList as SourceMap['ignoreList']; this.sourceRoot = out.sourceRoot; From 44961208dd3ab4d8946e3856dd44038078c8eec6 Mon Sep 17 00:00:00 2001 From: Justin Ridgewell Date: Sun, 29 Mar 2026 02:12:51 -0400 Subject: [PATCH 6/6] Fix decoding-tests --- decoding-tests/scopes/README.md | 18 +++ decoding-tests/scopes/hidden-ranges.map | 13 ++ .../scopes/hidden-ranges.map.golden | 75 +++++++++++ decoding-tests/scopes/nested-ranges.map | 14 +++ .../scopes/nested-ranges.map.golden | 75 +++++++++++ decoding-tests/scopes/range-call-site.map | 14 +++ .../scopes/range-call-site.map.golden | 79 ++++++++++++ decoding-tests/scopes/range-values.map | 20 +++ decoding-tests/scopes/range-values.map.golden | 119 ++++++++++++++++++ decoding-tests/scopes/sibling-ranges.map | 13 ++ .../scopes/sibling-ranges.map.golden | 93 ++++++++++++++ decoding-tests/scopes/sub-range-values.map | 17 +++ .../scopes/sub-range-values.map.golden | 79 ++++++++++++ package.json | 2 +- 14 files changed, 630 insertions(+), 1 deletion(-) create mode 100644 decoding-tests/scopes/README.md create mode 100644 decoding-tests/scopes/hidden-ranges.map create mode 100644 decoding-tests/scopes/hidden-ranges.map.golden create mode 100644 decoding-tests/scopes/nested-ranges.map create mode 100644 decoding-tests/scopes/nested-ranges.map.golden create mode 100644 decoding-tests/scopes/range-call-site.map create mode 100644 decoding-tests/scopes/range-call-site.map.golden create mode 100644 decoding-tests/scopes/range-values.map create mode 100644 decoding-tests/scopes/range-values.map.golden create mode 100644 decoding-tests/scopes/sibling-ranges.map create mode 100644 decoding-tests/scopes/sibling-ranges.map.golden create mode 100644 decoding-tests/scopes/sub-range-values.map create mode 100644 decoding-tests/scopes/sub-range-values.map.golden diff --git a/decoding-tests/scopes/README.md b/decoding-tests/scopes/README.md new file mode 100644 index 0000000..7b3271a --- /dev/null +++ b/decoding-tests/scopes/README.md @@ -0,0 +1,18 @@ +## Test cases + +- **empty-scopes-field**: Empty original scopes field +- **nil-scopes**: Multiple null original scopes +- **close-start-end-position-scopes**: Scopes with very close start and end + positions +- **single-root-original-scope**: A single global root original scope +- **nested-scopes**: Nested original scopes representing functions and blocks +- **sibling-scopes**: Multiple sibling top-level functions +- **scope-variables**: Scopes containing variable declarations +- **multiple-root-original-scopes-with-nil**: Multiple global root scopes with a + null scope in between +- **sibling-ranges**: Multiple sibling root ranges +- **nested-ranges**: A root range with a nested function range +- **range-values**: A root range with a non-function range with bindings +- **sub-range-values**: A root range with sub-range bindings +- **range-call-site**: A function inlined into the root scope +- **hidden-ranges**: A function range corresponding to an original block scope diff --git a/decoding-tests/scopes/hidden-ranges.map b/decoding-tests/scopes/hidden-ranges.map new file mode 100644 index 0000000..779a242 --- /dev/null +++ b/decoding-tests/scopes/hidden-ranges.map @@ -0,0 +1,13 @@ +{ + "version": 3, + "file": "hidden-ranges.js", + "sources": [ + "original_0.js" + ], + "mappings": "", + "names": [ + "global", + "block" + ], + "scopes": "BCAAA,BCBAC,CEA,CFA,ECAA,EPBAC,FEA,FFA" +} \ No newline at end of file diff --git a/decoding-tests/scopes/hidden-ranges.map.golden b/decoding-tests/scopes/hidden-ranges.map.golden new file mode 100644 index 0000000..6564cca --- /dev/null +++ b/decoding-tests/scopes/hidden-ranges.map.golden @@ -0,0 +1,75 @@ +{ + "file": "hidden-ranges.js", + "mappings": [], + "sources": [ + { + "url": "original_0.js", + "content": null, + "ignored": false, + "scope": { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 10, + "column": 0 + }, + "name": null, + "kind": "global", + "isStackFrame": false, + "variables": [], + "children": [ + { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 0 + }, + "name": null, + "kind": "block", + "isStackFrame": false, + "variables": [], + "children": [] + } + ] + } + } + ], + "ranges": [ + { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 10, + "column": 0 + }, + "definitionIndex": 0, + "stackFrameType": "none", + "callSite": null, + "bindings": [], + "children": [ + { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 0 + }, + "definitionIndex": 1, + "stackFrameType": "hidden", + "callSite": null, + "bindings": [], + "children": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/decoding-tests/scopes/nested-ranges.map b/decoding-tests/scopes/nested-ranges.map new file mode 100644 index 0000000..2ab6123 --- /dev/null +++ b/decoding-tests/scopes/nested-ranges.map @@ -0,0 +1,14 @@ +{ + "version": 3, + "file": "nested-ranges.js", + "sources": [ + "original_0.js" + ], + "mappings": "", + "names": [ + "global", + "foo", + "function" + ], + "scopes": "BCAAA,BHBACE,CEA,CFA,ECAA,EGCC,FC,FG" +} \ No newline at end of file diff --git a/decoding-tests/scopes/nested-ranges.map.golden b/decoding-tests/scopes/nested-ranges.map.golden new file mode 100644 index 0000000..4d4138f --- /dev/null +++ b/decoding-tests/scopes/nested-ranges.map.golden @@ -0,0 +1,75 @@ +{ + "file": "nested-ranges.js", + "mappings": [], + "sources": [ + { + "url": "original_0.js", + "content": null, + "ignored": false, + "scope": { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 10, + "column": 0 + }, + "name": null, + "kind": "global", + "isStackFrame": false, + "variables": [], + "children": [ + { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 0 + }, + "name": "foo", + "kind": "function", + "isStackFrame": true, + "variables": [], + "children": [] + } + ] + } + } + ], + "ranges": [ + { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 0, + "column": 10 + }, + "definitionIndex": 0, + "stackFrameType": "none", + "callSite": null, + "bindings": [], + "children": [ + { + "start": { + "line": 0, + "column": 2 + }, + "end": { + "line": 0, + "column": 4 + }, + "definitionIndex": 1, + "stackFrameType": "original", + "callSite": null, + "bindings": [], + "children": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/decoding-tests/scopes/range-call-site.map b/decoding-tests/scopes/range-call-site.map new file mode 100644 index 0000000..61e5a4c --- /dev/null +++ b/decoding-tests/scopes/range-call-site.map @@ -0,0 +1,14 @@ +{ + "version": 3, + "file": "range-call-site.js", + "sources": [ + "original_0.js" + ], + "mappings": "", + "names": [ + "global", + "inlineMe", + "function" + ], + "scopes": "BCAAA,BHBACE,CEA,CFA,ECAA,EDBAC,IAGC,FK,FJA" +} \ No newline at end of file diff --git a/decoding-tests/scopes/range-call-site.map.golden b/decoding-tests/scopes/range-call-site.map.golden new file mode 100644 index 0000000..b3ee230 --- /dev/null +++ b/decoding-tests/scopes/range-call-site.map.golden @@ -0,0 +1,79 @@ +{ + "file": "range-call-site.js", + "mappings": [], + "sources": [ + { + "url": "original_0.js", + "content": null, + "ignored": false, + "scope": { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 10, + "column": 0 + }, + "name": null, + "kind": "global", + "isStackFrame": false, + "variables": [], + "children": [ + { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 0 + }, + "name": "inlineMe", + "kind": "function", + "isStackFrame": true, + "variables": [], + "children": [] + } + ] + } + } + ], + "ranges": [ + { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 10, + "column": 0 + }, + "definitionIndex": 0, + "stackFrameType": "none", + "callSite": null, + "bindings": [], + "children": [ + { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 10 + }, + "definitionIndex": 1, + "stackFrameType": "none", + "callSite": { + "sourceIndex": 0, + "line": 6, + "column": 2 + }, + "bindings": [], + "children": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/decoding-tests/scopes/range-values.map b/decoding-tests/scopes/range-values.map new file mode 100644 index 0000000..c397423 --- /dev/null +++ b/decoding-tests/scopes/range-values.map @@ -0,0 +1,20 @@ +{ + "version": 3, + "file": "range-values.js", + "sources": [ + "original_0.js" + ], + "mappings": "", + "names": [ + "global", + "x", + "y", + "z", + "block", + "a", + "x_val", + "z_val", + "a_val" + ], + "scopes": "BCAAA,DCCC,BCBAI,DE,CEA,CFA,ECAA,GHAI,ECCC,GJ,FC,FG" +} \ No newline at end of file diff --git a/decoding-tests/scopes/range-values.map.golden b/decoding-tests/scopes/range-values.map.golden new file mode 100644 index 0000000..a7cdec5 --- /dev/null +++ b/decoding-tests/scopes/range-values.map.golden @@ -0,0 +1,119 @@ +{ + "file": "range-values.js", + "mappings": [], + "sources": [ + { + "url": "original_0.js", + "content": null, + "ignored": false, + "scope": { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 10, + "column": 0 + }, + "name": null, + "kind": "global", + "isStackFrame": false, + "variables": [ + "x", + "y", + "z" + ], + "children": [ + { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 5, + "column": 0 + }, + "name": null, + "kind": "block", + "isStackFrame": false, + "variables": [ + "a" + ], + "children": [] + } + ] + } + } + ], + "ranges": [ + { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 0, + "column": 10 + }, + "definitionIndex": 0, + "stackFrameType": "none", + "callSite": null, + "bindings": [ + [ + { + "binding": "x_val", + "from": { + "line": 0, + "column": 0 + } + } + ], + [ + { + "binding": null, + "from": { + "line": 0, + "column": 0 + } + } + ], + [ + { + "binding": "z_val", + "from": { + "line": 0, + "column": 0 + } + } + ] + ], + "children": [ + { + "start": { + "line": 0, + "column": 2 + }, + "end": { + "line": 0, + "column": 4 + }, + "definitionIndex": 1, + "stackFrameType": "none", + "callSite": null, + "bindings": [ + [ + { + "binding": "a_val", + "from": { + "line": 0, + "column": 2 + } + } + ] + ], + "children": [] + } + ] + } + ] +} \ No newline at end of file diff --git a/decoding-tests/scopes/sibling-ranges.map b/decoding-tests/scopes/sibling-ranges.map new file mode 100644 index 0000000..ea90f64 --- /dev/null +++ b/decoding-tests/scopes/sibling-ranges.map @@ -0,0 +1,13 @@ +{ + "version": 3, + "file": "sibling-ranges.js", + "sources": [ + "original_0.js", + "original_1.js" + ], + "mappings": "", + "names": [ + "global" + ], + "scopes": "BCAAA,CKA,BCKAA,CKA,ECAA,FK,EDKAC,FK,EBKA,FK" +} \ No newline at end of file diff --git a/decoding-tests/scopes/sibling-ranges.map.golden b/decoding-tests/scopes/sibling-ranges.map.golden new file mode 100644 index 0000000..dc1975d --- /dev/null +++ b/decoding-tests/scopes/sibling-ranges.map.golden @@ -0,0 +1,93 @@ +{ + "file": "sibling-ranges.js", + "mappings": [], + "sources": [ + { + "url": "original_0.js", + "content": null, + "ignored": false, + "scope": { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 10, + "column": 0 + }, + "name": null, + "kind": "global", + "isStackFrame": false, + "variables": [], + "children": [] + } + }, + { + "url": "original_1.js", + "content": null, + "ignored": false, + "scope": { + "start": { + "line": 10, + "column": 0 + }, + "end": { + "line": 20, + "column": 0 + }, + "name": null, + "kind": "global", + "isStackFrame": false, + "variables": [], + "children": [] + } + } + ], + "ranges": [ + { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 0, + "column": 10 + }, + "definitionIndex": 0, + "stackFrameType": "none", + "callSite": null, + "bindings": [], + "children": [] + }, + { + "start": { + "line": 10, + "column": 0 + }, + "end": { + "line": 10, + "column": 10 + }, + "definitionIndex": 1, + "stackFrameType": "none", + "callSite": null, + "bindings": [], + "children": [] + }, + { + "start": { + "line": 20, + "column": 0 + }, + "end": { + "line": 20, + "column": 10 + }, + "definitionIndex": null, + "stackFrameType": "none", + "callSite": null, + "bindings": [], + "children": [] + } + ] +} \ No newline at end of file diff --git a/decoding-tests/scopes/sub-range-values.map b/decoding-tests/scopes/sub-range-values.map new file mode 100644 index 0000000..40155a8 --- /dev/null +++ b/decoding-tests/scopes/sub-range-values.map @@ -0,0 +1,17 @@ +{ + "version": 3, + "file": "sub-range-values.js", + "sources": [ + "original_0.js" + ], + "mappings": "", + "names": [ + "global", + "x", + "y", + "x_sub_val1", + "y_val", + "x_sub_val2" + ], + "scopes": "BCAAA,DCC,CKA,ECAA,GEF,HAADAADG,FK" +} \ No newline at end of file diff --git a/decoding-tests/scopes/sub-range-values.map.golden b/decoding-tests/scopes/sub-range-values.map.golden new file mode 100644 index 0000000..c066210 --- /dev/null +++ b/decoding-tests/scopes/sub-range-values.map.golden @@ -0,0 +1,79 @@ +{ + "file": "sub-range-values.js", + "mappings": [], + "sources": [ + { + "url": "original_0.js", + "content": null, + "ignored": false, + "scope": { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 10, + "column": 0 + }, + "name": null, + "kind": "global", + "isStackFrame": false, + "variables": [ + "x", + "y" + ], + "children": [] + } + } + ], + "ranges": [ + { + "start": { + "line": 0, + "column": 0 + }, + "end": { + "line": 0, + "column": 10 + }, + "definitionIndex": 0, + "stackFrameType": "none", + "callSite": null, + "bindings": [ + [ + { + "binding": "x_sub_val1", + "from": { + "line": 0, + "column": 0 + } + }, + { + "binding": null, + "from": { + "line": 0, + "column": 3 + } + }, + { + "binding": "x_sub_val2", + "from": { + "line": 0, + "column": 6 + } + } + ], + [ + { + "binding": "y_val", + "from": { + "line": 0, + "column": 0 + } + } + ] + ], + "children": [] + } + ] +} \ No newline at end of file diff --git a/package.json b/package.json index bd13d97..9b60c8f 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "clean": "turbo run clean", "lint": "turbo run lint", "test": "turbo run test --parallel", - "postinstall": "degit tc39/source-map-tests/decoding decoding-tests" + "decoding-tests": "degit --force tc39/source-map-tests/decoding decoding-tests" }, "workspaces": [ "packages/*"