diff --git a/lib/internal/source_map/source_map.js b/lib/internal/source_map/source_map.js index 42e7bca3c4c5..2bb23b00c167 100644 --- a/lib/internal/source_map/source_map.js +++ b/lib/internal/source_map/source_map.js @@ -286,7 +286,8 @@ class SourceMap { } columnNumber += decodeVLQ(stringCharIterator); - if (isSeparator(stringCharIterator.peek())) { + if (!stringCharIterator.hasNext() || + isSeparator(stringCharIterator.peek())) { ArrayPrototypePush(this.#mappings, [lineNumber, columnNumber]); continue; } @@ -300,7 +301,8 @@ class SourceMap { sourceColumnNumber += decodeVLQ(stringCharIterator); let name; - if (!isSeparator(stringCharIterator.peek())) { + if (stringCharIterator.hasNext() && + !isSeparator(stringCharIterator.peek())) { nameIndex += decodeVLQ(stringCharIterator); name = map.names?.[nameIndex]; } diff --git a/test/parallel/test-source-map-trailing-unmapped-segment.js b/test/parallel/test-source-map-trailing-unmapped-segment.js new file mode 100644 index 000000000000..d5c7243ad999 --- /dev/null +++ b/test/parallel/test-source-map-trailing-unmapped-segment.js @@ -0,0 +1,67 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { SourceMap } = require('node:module'); + +// A segment with only a generated column marks the code after it as unmapped. +// When it is the last segment of the mappings, it must not inherit the +// original position of the segment before it. +const unmapped = { + originalSource: undefined, + originalLine: undefined, + originalColumn: undefined, + name: undefined, +}; + +// Trailing unmapped segment on the same line as the last mapped segment. +{ + const sm = new SourceMap({ + version: 3, + sources: ['a.js'], + names: [], + mappings: 'AAAA,K', + }); + + assert.deepStrictEqual(sm.findEntry(0, 5), { + generatedLine: 0, + generatedColumn: 5, + ...unmapped, + }); + assert.deepStrictEqual(sm.findOrigin(1, 6), {}); +} + +// Trailing unmapped segment on a later line. +{ + const sm = new SourceMap({ + version: 3, + sources: ['a.js'], + names: [], + mappings: 'AAAA;A', + }); + + assert.deepStrictEqual(sm.findEntry(1, 3), { + generatedLine: 1, + generatedColumn: 0, + ...unmapped, + }); + assert.deepStrictEqual(sm.findOrigin(2, 4), {}); +} + +// A segment without a name field must not pick up a name from the names array. +{ + const sm = new SourceMap({ + version: 3, + sources: ['a.js'], + names: ['should-not-be-used'], + mappings: 'AAAA', + }); + + assert.deepStrictEqual(sm.findEntry(0, 0), { + generatedLine: 0, + generatedColumn: 0, + originalSource: 'a.js', + originalLine: 0, + originalColumn: 0, + name: undefined, + }); +}