Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/MagicString.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export default class MagicString {
names,
mappings: mappings.raw,
x_google_ignoreList: this.ignoreList ? [sourceIndex] : undefined,
rangeMappings: mappings.rawRangeMappings,
};
}

Expand Down
14 changes: 13 additions & 1 deletion src/SourceMap.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { encode } from '@jridgewell/sourcemap-codec';
import { encode, encodeRangeMappings } from '@jridgewell/sourcemap-codec';

function getBtoa() {
if (typeof globalThis !== 'undefined' && typeof globalThis.btoa === 'function') {
Expand Down Expand Up @@ -28,6 +28,18 @@ export default class SourceMap {
if (typeof properties.debugId !== 'undefined') {
this.debugId = properties.debugId;
}
if (typeof properties.rangeMappings !== 'undefined') {
let shouldOutputRangeMapping = false;
for (const line of properties.rangeMappings) {
if (line.length !== 0) {
shouldOutputRangeMapping = true;
break;
}
}
if (shouldOutputRangeMapping) {
this.rangeMappings = encodeRangeMappings(properties.rangeMappings);
}
}
}

toString() {
Expand Down
4 changes: 3 additions & 1 deletion src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ export interface SourceMapOptions {
* line - but they're quicker to generate and less bulky.
* You can also set `"boundary"` to generate a semi-hi-res mappings segmented per word boundary
* instead of per character, suitable for string semantics that are separated by words.
* If you set `"experimental-range"` to generate hires mappings that use range mappings, a
* source map extension that can map all positions in a range. This feature is experimental.
* If sourcemap locations have been specified with s.addSourceMapLocation(), they will be used here.
*/
hires?: boolean | 'boundary';
hires?: boolean | 'boundary' | 'experimental-range';
/**
* The filename where you plan to write the sourcemap.
*/
Expand Down
13 changes: 13 additions & 0 deletions src/utils/Mappings.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export default class Mappings {
this.generatedCodeColumn = 0;
this.raw = [];
this.rawSegments = this.raw[this.generatedCodeLine] = [];
this.rawRangeMappings = [];
this.rawRangeMappingsIndices = this.rawRangeMappings[this.generatedCodeLine] = [];
this.pending = null;
}

Expand All @@ -26,6 +28,7 @@ export default class Mappings {

this.generatedCodeLine += 1;
this.raw[this.generatedCodeLine] = this.rawSegments = [];
this.rawRangeMappings[this.generatedCodeLine] = [];
this.generatedCodeColumn = 0;

previousContentLineEnd = contentLineEnd;
Expand Down Expand Up @@ -54,11 +57,15 @@ export default class Mappings {
let charInHiresBoundary = false;

while (originalCharIndex < chunk.end) {
if (this.hires === "experimental-range" && originalCharIndex + 1 >= chunk.end) {
this.rawSegments.push([this.generatedCodeColumn, sourceIndex, loc.line, loc.column]);
}
if (original[originalCharIndex] === '\n') {
loc.line += 1;
loc.column = 0;
this.generatedCodeLine += 1;
this.raw[this.generatedCodeLine] = this.rawSegments = [];
this.rawRangeMappings[this.generatedCodeLine] = this.rawRangeMappingsIndices = [];
this.generatedCodeColumn = 0;
first = true;
charInHiresBoundary = false;
Expand All @@ -79,6 +86,11 @@ export default class Mappings {
this.rawSegments.push(segment);
charInHiresBoundary = false;
}
} else if (this.hires === "experimental-range") {
if (originalCharIndex === chunk.start) {
this.rawRangeMappingsIndices.push(this.rawSegments.length);
this.rawSegments.push(segment);
}
} else {
this.rawSegments.push(segment);
}
Expand All @@ -104,6 +116,7 @@ export default class Mappings {
for (let i = 0; i < lines.length - 1; i++) {
this.generatedCodeLine++;
this.raw[this.generatedCodeLine] = this.rawSegments = [];
this.rawRangeMappings[this.generatedCodeLine] = this.rawRangeMappingsIndices = [];
}
this.generatedCodeColumn = 0;
}
Expand Down
138 changes: 138 additions & 0 deletions test/MagicString.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,144 @@ describe('MagicString', () => {
assert.equal(loc.column, 12);
});

it('generates segments per chunk with hires "experimental-range"', () => {
const s = new MagicString('function foo(){ console.log("bar") }');

// rename bar to hello
s.overwrite(29, 32, 'hello');

const map = s.generateMap({
file: 'output.js',
source: 'input.js',
includeContent: true,
hires: 'experimental-range',
});

assert.equal(
map.mappings,
'AAAA,4BAA4B,CAAC,KAAG,GAAG',
);
assert.equal(
map.rangeMappings,
'BD',
);

const smc = new SourceMapConsumer(map);
let loc;

// FIXME: the consumer library doesn't support range mappings yet
//loc = smc.originalPositionFor({ line: 1, column: 15 });
//assert.equal(loc.line, 1);
//assert.equal(loc.column, 15);

loc = smc.originalPositionFor({ line: 1, column: 28 });
assert.equal(loc.line, 1);
assert.equal(loc.column, 28);

loc = smc.originalPositionFor({ line: 1, column: 29 });
assert.equal(loc.line, 1);
assert.equal(loc.column, 29);

loc = smc.originalPositionFor({ line: 1, column: 34 });
assert.equal(loc.line, 1);
assert.equal(loc.column, 32);

// FIXME: see above
//loc = smc.originalPositionFor({ line: 1, column: 35 });
//assert.equal(loc.line, 1);
//assert.equal(loc.column, 33);
});

it('generates segments per chunk with hires "experimental-range" (multiple ranges on a line)', () => {
const s = new MagicString('function foo(){ console.log("bar") }');

// rename foo to baz, bar to hello
s.overwrite(9, 12, 'baz');
s.overwrite(29, 32, 'hello');

const map = s.generateMap({
file: 'output.js',
source: 'input.js',
includeContent: true,
hires: 'experimental-range',
});

assert.equal(
map.mappings,
'AAAA,QAAQ,CAAC,GAAG,gBAAgB,CAAC,KAAG,GAAG',
);
assert.equal(
map.rangeMappings,
'BDD',
);

const smc = new SourceMapConsumer(map);
let loc;

// FIXME: the consumer library doesn't support range mappings yet
//loc = smc.originalPositionFor({ line: 1, column: 15 });
//assert.equal(loc.line, 1);
//assert.equal(loc.column, 15);

loc = smc.originalPositionFor({ line: 1, column: 28 });
assert.equal(loc.line, 1);
assert.equal(loc.column, 28);

loc = smc.originalPositionFor({ line: 1, column: 29 });
assert.equal(loc.line, 1);
assert.equal(loc.column, 29);

loc = smc.originalPositionFor({ line: 1, column: 34 });
assert.equal(loc.line, 1);
assert.equal(loc.column, 32);

// FIXME: see above
//loc = smc.originalPositionFor({ line: 1, column: 35 });
//assert.equal(loc.line, 1);
//assert.equal(loc.column, 33);
});

it('generates segments per chunk with hires "experimental-range" in the next line', () => {
const s = new MagicString('// foo\nconsole.log("bar")');

// rename bar to hello
s.overwrite(20, 23, 'hello');

const map = s.generateMap({
file: 'output.js',
source: 'input.js',
includeContent: true,
hires: 'experimental-range',
});

assert.equal(map.mappings, 'AAAA;YACY,CAAC,KAAG,CAAC');
assert.equal(map.rangeMappings, 'B;D');

const smc = new SourceMapConsumer(map);
let loc;

// FIXME: the consumer library doesn't support range mappings yet
//loc = smc.originalPositionFor({ line: 1, column: 2 });
//assert.equal(loc.line, 1);
//assert.equal(loc.column, 2);

//loc = smc.originalPositionFor({ line: 2, column: 2 });
//assert.equal(loc.line, 2);
//assert.equal(loc.column, 2);

loc = smc.originalPositionFor({ line: 2, column: 12 });
assert.equal(loc.line, 2);
assert.equal(loc.column, 12);

loc = smc.originalPositionFor({ line: 2, column: 18 });
assert.equal(loc.line, 2);
assert.equal(loc.column, 16);

loc = smc.originalPositionFor({ line: 2, column: 19 });
assert.equal(loc.line, 2);
assert.equal(loc.column, 17);
});

it('generates a correct source map with update using a content containing a new line', () => {
const s = new MagicString('foobar');
s.update(3, 4, '\nbb');
Expand Down