Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: hires boundary #255

Merged
merged 1 commit into from
Jul 28, 2023
Merged
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Generates a [version 3 sourcemap](https://docs.google.com/document/d/1U1RGAehQwR
* `file` - the filename where you plan to write the sourcemap
* `source` - the filename of the file containing the original source
* `includeContent` - whether to include the original content in the map's `sourcesContent` array
* `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.
* `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.

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

Expand Down
4 changes: 3 additions & 1 deletion src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ export interface SourceMapOptions {
* 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.
*/
hires?: boolean;
hires?: boolean | 'boundary';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe word-boundary to be more specific? Not sure if we are going to have more different type of boundary tho

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking a shorter name would make it easier to type out when using it. But open to other names too.

/**
* The filename where you plan to write the sourcemap.
*/
Expand Down
23 changes: 22 additions & 1 deletion src/utils/Mappings.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
const wordRegex = /\w/;

export default class Mappings {
constructor(hires) {
this.hires = hires;
Expand Down Expand Up @@ -26,10 +28,29 @@ export default class Mappings {
addUneditedChunk(sourceIndex, chunk, original, loc, sourcemapLocations) {
let originalCharIndex = chunk.start;
let first = true;
// when iterating each char, check if it's in a word boundary
let charInHiresBoundary = false;

while (originalCharIndex < chunk.end) {
if (this.hires || first || sourcemapLocations.has(originalCharIndex)) {
this.rawSegments.push([this.generatedCodeColumn, sourceIndex, loc.line, loc.column]);
const segment = [this.generatedCodeColumn, sourceIndex, loc.line, loc.column];

if (this.hires === 'boundary') {
// in hires "boundary", group segments per word boundary than per char
if (wordRegex.test(original[originalCharIndex])) {
// for first char in the boundary found, start the boundary by pushing a segment
if (!charInHiresBoundary) {
this.rawSegments.push(segment);
charInHiresBoundary = true;
}
} else {
// for non-word char, end the boundary by pushing a segment
this.rawSegments.push(segment);
charInHiresBoundary = false;
}
} else {
this.rawSegments.push(segment);
}
}

if (original[originalCharIndex] === '\n') {
Expand Down
35 changes: 35 additions & 0 deletions test/MagicString.js
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,41 @@ describe('MagicString', () => {
assert.deepEqual(map.sources, ['foo.js']);
assert.deepEqual(map.x_google_ignoreList, [0]);
});

it('generates segments per word boundary with hires "boundary"', () => {
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: 'boundary'
});

assert.equal(map.mappings, 'AAAA,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAG,CAAC,CAAC,CAAC');

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

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

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

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

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

describe('getIndentString', () => {
Expand Down
Loading