diff --git a/package-lock.json b/package-lock.json index 9b501ff..2a10695 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@jridgewell/trace-mapping", - "version": "0.3.16", + "version": "0.3.17", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@jridgewell/trace-mapping", - "version": "0.3.16", + "version": "0.3.17", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "3.1.0", diff --git a/package.json b/package.json index 93ed138..db3f8ac 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@jridgewell/trace-mapping", - "version": "0.3.16", + "version": "0.3.17", "description": "Trace the original position through a source map", "keywords": [ "source", diff --git a/src/trace-mapping.ts b/src/trace-mapping.ts index 4698e35..dca6062 100644 --- a/src/trace-mapping.ts +++ b/src/trace-mapping.ts @@ -60,7 +60,6 @@ const COL_GTR_EQ_ZERO = '`column` must be greater than or equal to 0 (columns st export const LEAST_UPPER_BOUND = -1; export const GREATEST_LOWER_BOUND = 1; -const ALL_BOUND = 0; /** * Returns the encoded (VLQ string) form of the SourceMap's mappings field. @@ -248,12 +247,13 @@ export class TraceMap implements SourceMap { ); }; - allGeneratedPositionsFor = (map, { source, line, column }) => { - return generatedPosition(map, source, line, column, ALL_BOUND); + allGeneratedPositionsFor = (map, { source, line, column, bias }) => { + // SourceMapConsumer uses LEAST_UPPER_BOUND for some reason, so we follow suit. + return generatedPosition(map, source, line, column, bias || LEAST_UPPER_BOUND, true); }; generatedPositionFor = (map, { source, line, column, bias }) => { - return generatedPosition(map, source, line, column, bias || GREATEST_LOWER_BOUND); + return generatedPosition(map, source, line, column, bias || GREATEST_LOWER_BOUND, false); }; eachMapping = (map, cb) => { @@ -320,20 +320,23 @@ export class TraceMap implements SourceMap { line: number, column: number, bias: Bias, + all: false, ): GeneratedMapping | InvalidGeneratedMapping; function generatedPosition( map: TraceMap, source: string, line: number, column: number, - bias: 0, + bias: Bias, + all: true, ): GeneratedMapping[]; function generatedPosition( map: TraceMap, source: string, line: number, column: number, - bias: Bias | 0, + bias: Bias, + all: boolean, ): GeneratedMapping | InvalidGeneratedMapping | GeneratedMapping[] { line--; if (line < 0) throw new Error(LINE_GTR_ZERO); @@ -342,7 +345,7 @@ export class TraceMap implements SourceMap { const { sources, resolvedSources } = map; let sourceIndex = sources.indexOf(source); if (sourceIndex === -1) sourceIndex = resolvedSources.indexOf(source); - if (sourceIndex === -1) return bias === ALL_BOUND ? [] : GMapping(null, null); + if (sourceIndex === -1) return all ? [] : GMapping(null, null); const generated = (map._bySources ||= buildBySources( decodedMappings(map), @@ -350,11 +353,11 @@ export class TraceMap implements SourceMap { )); const segments = generated[sourceIndex][line]; - if (segments == null) return bias === ALL_BOUND ? [] : GMapping(null, null); + if (segments == null) return all ? [] : GMapping(null, null); const memo = map._bySourceMemos![sourceIndex]; - if (bias === ALL_BOUND) return sliceGeneratedPositions(segments, memo, line, column); + if (all) return sliceGeneratedPositions(segments, memo, line, column, bias); const index = traceSegmentInternal(segments, memo, line, column, bias); if (index === -1) return GMapping(null, null); @@ -440,9 +443,19 @@ function sliceGeneratedPositions( memo: MemoState, line: number, column: number, + bias: Bias, ): GeneratedMapping[] { let min = traceSegmentInternal(segments, memo, line, column, GREATEST_LOWER_BOUND); - if (min === -1) return []; + + // We ignored the bias when tracing the segment so that we're guarnateed to find the first (in + // insertion order) segment that matched. Even if we did respect the bias when tracing, we would + // still need to call `lowerBound()` to find the first segment, which is slower than just looking + // for the GREATEST_LOWER_BOUND to begin with. The only difference that matters for us is when the + // binary search didn't match, in which case GREATEST_LOWER_BOUND just needs to increment to + // match LEAST_UPPER_BOUND. + if (!bsFound && bias === LEAST_UPPER_BOUND) min++; + + if (min === -1 || min === segments.length) return []; // We may have found the segment that started at an earlier column. If this is the case, then we // need to slice all generated segments that match _that_ column, because all such segments span diff --git a/test/trace-mapping.test.ts b/test/trace-mapping.test.ts index b740908..5a0a33c 100644 --- a/test/trace-mapping.test.ts +++ b/test/trace-mapping.test.ts @@ -321,6 +321,34 @@ describe('TraceMap', () => { line: 2, column: 9, }), + [ + { line: 2, column: 8 }, + { line: 2, column: 17 }, + { line: 2, column: 32 }, + ], + ); + + t.deepEqual( + allGeneratedPositionsFor(tracer, { + source: 'input.js', + line: 2, + column: 9, + bias: LEAST_UPPER_BOUND, + }), + [ + { line: 2, column: 8 }, + { line: 2, column: 17 }, + { line: 2, column: 32 }, + ], + ); + + t.deepEqual( + allGeneratedPositionsFor(tracer, { + source: 'input.js', + line: 2, + column: 9, + bias: GREATEST_LOWER_BOUND, + }), [ { line: 2, column: 4 }, { line: 2, column: 33 }, @@ -340,6 +368,20 @@ describe('TraceMap', () => { ], ); + t.deepEqual( + allGeneratedPositionsFor(tracer, { + source: 'input.js', + line: 2, + column: 10, + bias: GREATEST_LOWER_BOUND, + }), + [ + { line: 2, column: 8 }, + { line: 2, column: 17 }, + { line: 2, column: 32 }, + ], + ); + t.deepEqual( allGeneratedPositionsFor(tracer, { source: 'input.js', line: 100, column: 13 }), [], @@ -347,11 +389,11 @@ describe('TraceMap', () => { t.deepEqual( allGeneratedPositionsFor(tracer, { source: 'input.js', line: 1, column: 100 }), - [{ line: 1, column: 18 }], + [], ); t.deepEqual(allGeneratedPositionsFor(tracer, { source: 'input.js', line: 1, column: 10 }), [ - { line: 1, column: 9 }, + { line: 1, column: 13 }, ]); }); };