Skip to content

Commit a63d5f2

Browse files
authored
feat: hires boundary (#255)
1 parent 9213b45 commit a63d5f2

4 files changed

Lines changed: 61 additions & 3 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ Generates a [version 3 sourcemap](https://docs.google.com/document/d/1U1RGAehQwR
107107
* `file` - the filename where you plan to write the sourcemap
108108
* `source` - the filename of the file containing the original source
109109
* `includeContent` - whether to include the original content in the map's `sourcesContent` array
110-
* `hires` - whether the mapping should be high-resolution. Hi-res mappings map every single character, meaning (for example) your devtools will always be able to pinpoint the exact location of function calls and so on. With lo-res mappings, devtools may only be able to identify the correct line - but they're quicker to generate and less bulky. If sourcemap locations have been specified with `s.addSourcemapLocation()`, they will be used here.
110+
* `hires` - whether the mapping should be high-resolution. Hi-res mappings map every single character, meaning (for example) your devtools will always be able to pinpoint the exact location of function calls and so on. With lo-res mappings, devtools may only be able to identify the correct 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 sourcemap locations have been specified with `s.addSourcemapLocation()`, they will be used here.
111111

112112
The returned sourcemap has two (non-enumerable) methods attached for convenience:
113113

src/index.d.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ export interface SourceMapOptions {
1010
* be able to pinpoint the exact location of function calls and so on.
1111
* With lo-res mappings, devtools may only be able to identify the correct
1212
* line - but they're quicker to generate and less bulky.
13+
* You can also set `"boundary"` to generate a semi-hi-res mappings segmented per word boundary
14+
* instead of per character, suitable for string semantics that are separated by words.
1315
* If sourcemap locations have been specified with s.addSourceMapLocation(), they will be used here.
1416
*/
15-
hires?: boolean;
17+
hires?: boolean | 'boundary';
1618
/**
1719
* The filename where you plan to write the sourcemap.
1820
*/

src/utils/Mappings.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const wordRegex = /\w/;
2+
13
export default class Mappings {
24
constructor(hires) {
35
this.hires = hires;
@@ -26,10 +28,29 @@ export default class Mappings {
2628
addUneditedChunk(sourceIndex, chunk, original, loc, sourcemapLocations) {
2729
let originalCharIndex = chunk.start;
2830
let first = true;
31+
// when iterating each char, check if it's in a word boundary
32+
let charInHiresBoundary = false;
2933

3034
while (originalCharIndex < chunk.end) {
3135
if (this.hires || first || sourcemapLocations.has(originalCharIndex)) {
32-
this.rawSegments.push([this.generatedCodeColumn, sourceIndex, loc.line, loc.column]);
36+
const segment = [this.generatedCodeColumn, sourceIndex, loc.line, loc.column];
37+
38+
if (this.hires === 'boundary') {
39+
// in hires "boundary", group segments per word boundary than per char
40+
if (wordRegex.test(original[originalCharIndex])) {
41+
// for first char in the boundary found, start the boundary by pushing a segment
42+
if (!charInHiresBoundary) {
43+
this.rawSegments.push(segment);
44+
charInHiresBoundary = true;
45+
}
46+
} else {
47+
// for non-word char, end the boundary by pushing a segment
48+
this.rawSegments.push(segment);
49+
charInHiresBoundary = false;
50+
}
51+
} else {
52+
this.rawSegments.push(segment);
53+
}
3354
}
3455

3556
if (original[originalCharIndex] === '\n') {

test/MagicString.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,41 @@ describe('MagicString', () => {
434434
assert.deepEqual(map.sources, ['foo.js']);
435435
assert.deepEqual(map.x_google_ignoreList, [0]);
436436
});
437+
438+
it('generates segments per word boundary with hires "boundary"', () => {
439+
const s = new MagicString('function foo(){ console.log("bar") }');
440+
441+
// rename bar to hello
442+
s.overwrite(29, 32, 'hello');
443+
444+
const map = s.generateMap({
445+
file: 'output.js',
446+
source: 'input.js',
447+
includeContent: true,
448+
hires: 'boundary'
449+
});
450+
451+
assert.equal(map.mappings, 'AAAA,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAG,CAAC,CAAC,CAAC');
452+
453+
const smc = new SourceMapConsumer(map);
454+
let loc;
455+
456+
loc = smc.originalPositionFor({ line: 1, column: 3 });
457+
assert.equal(loc.line, 1);
458+
assert.equal(loc.column, 0);
459+
460+
loc = smc.originalPositionFor({ line: 1, column: 11 });
461+
assert.equal(loc.line, 1);
462+
assert.equal(loc.column, 9);
463+
464+
loc = smc.originalPositionFor({ line: 1, column: 29 });
465+
assert.equal(loc.line, 1);
466+
assert.equal(loc.column, 29);
467+
468+
loc = smc.originalPositionFor({ line: 1, column: 35 });
469+
assert.equal(loc.line, 1);
470+
assert.equal(loc.column, 33);
471+
});
437472
});
438473

439474
describe('getIndentString', () => {

0 commit comments

Comments
 (0)