Skip to content

Commit

Permalink
Add sourcemap markings for each line of a string (#12086)
Browse files Browse the repository at this point in the history
* Add sourcemap markings for each line of a string

Fixes #12083

* Fix for multiple newlines

* Optimize with indexOf

* Comment explaining newline search
  • Loading branch information
jridgewell committed Sep 23, 2020
1 parent a4a14ca commit f49234a
Show file tree
Hide file tree
Showing 8 changed files with 164 additions and 19 deletions.
62 changes: 43 additions & 19 deletions packages/babel-generator/src/buffer.js
Expand Up @@ -116,30 +116,54 @@ export default class Buffer {
filename: ?string,
force?: boolean,
): void {
// If there the line is ending, adding a new mapping marker is redundant
if (this._map && str[0] !== "\n") {
this._map.mark(
this._position.line,
this._position.column,
line,
column,
identifierName,
filename,
force,
);
}

this._buf.push(str);
this._last = str[str.length - 1];

for (let i = 0; i < str.length; i++) {
if (str[i] === "\n") {
this._position.line++;
this._position.column = 0;
} else {
this._position.column++;
// Search for newline chars. We search only for `\n`, since both `\r` and
// `\r\n` are normalized to `\n` during parse. We exclude `\u2028` and
// `\u2029` for performance reasons, they're so uncommon that it's probably
// ok. It's also unclear how other sourcemap utilities handle them...
let i = str.indexOf("\n");
let last = 0;

// If the string starts with a newline char, then adding a mark is redundant.
// This catches both "no newlines" and "newline after several chars".
if (i !== 0) {
this._mark(line, column, identifierName, filename, force);
}

// Now, find each reamining newline char in the string.
while (i !== -1) {
this._position.line++;
this._position.column = 0;
last = i + 1;

// We mark the start of each line, which happens directly after this newline char
// unless this is the last char.
if (last < str.length) {
this._mark(++line, 0, identifierName, filename, force);
}
i = str.indexOf("\n", last);
}
this._position.column += str.length - last;
}

_mark(
line: number,
column: number,
identifierName: ?string,
filename: ?string,
force?: boolean,
): void {
this._map?.mark(
this._position.line,
this._position.column,
line,
column,
identifierName,
filename,
force,
);
}

removeTrailingNewline(): void {
Expand Down
@@ -0,0 +1,6 @@
"before\
after";

"before\
\
after";
@@ -0,0 +1,5 @@
"before\
after";
"before\
\
after";
@@ -0,0 +1,9 @@
{
"mappings": "AAAA;AACA,MADA;AAGA;AACA;AACA,MAFA",
"names": [],
"sources": ["fixtures/sourcemaps/string-literal-newline/input.js"],
"sourcesContent": [
"\"before\\\nafter\";\n\n\"before\\\n\\\nafter\";"
],
"version": 3
}
@@ -0,0 +1,27 @@
// Newline
`before
after`;

// Newline newline
`before
after`;

// Newline LineContinuation
`before
\
after`;

// LineContinuation
`before\
after`;

// LineContinuation newline
`before\
after`;

// LineContinuation LineContinuation
`before\
\
after`;
@@ -0,0 +1,22 @@
// Newline
`before
after`; // Newline newline

`before
after`; // Newline LineContinuation

`before
\
after`; // LineContinuation

`before\
after`; // LineContinuation newline

`before\
after`; // LineContinuation LineContinuation

`before\
\
after`;
@@ -0,0 +1,9 @@
{
"mappings": "AAAA;AACC;AACD,MADA,C,CAGA;;AACC;AACD;AACA,MAFA,C,CAIA;;AACC;AACD;AACA,MAFA,C,CAIA;;AACC;AACD,MADA,C,CAGA;;AACC;AACD;AACA,MAFA,C,CAIA;;AACC;AACD;AACA,MAFA",
"names": [],
"sources": ["fixtures/sourcemaps/template-literal-newline/input.js"],
"sourcesContent": [
"// Newline\n`before\nafter`;\n\n// Newline newline\n`before\n\nafter`;\n\n// Newline LineContinuation\n`before\n\\\nafter`;\n\n// LineContinuation\n`before\\\nafter`;\n\n// LineContinuation newline\n`before\\\n\nafter`;\n\n// LineContinuation LineContinuation\n`before\\\n\\\nafter`;"
],
"version": 3
}
43 changes: 43 additions & 0 deletions packages/babel-generator/test/index.js
Expand Up @@ -5,6 +5,7 @@ import * as t from "@babel/types";
import fs from "fs";
import path from "path";
import fixtures from "@babel/helper-fixtures";
import sourcemap from "source-map";

describe("generation", function () {
it("completeness", function () {
Expand Down Expand Up @@ -277,6 +278,48 @@ describe("generation", function () {
expect(generated.code).toBe("function foo2() {\n bar2;\n}");
});

it("newline in template literal", () => {
const code = "`before\n\nafter`;";
const ast = parse(code, { filename: "inline" }).program;
const generated = generate(
ast,
{
filename: "inline",
sourceFileName: "inline",
sourceMaps: true,
},
code,
);

const consumer = new sourcemap.SourceMapConsumer(generated.map);
const loc = consumer.originalPositionFor({ line: 2, column: 1 });
expect(loc).toMatchObject({
column: 0,
line: 2,
});
});

it("newline in string literal", () => {
const code = "'before\\\n\\\nafter';";
const ast = parse(code, { filename: "inline" }).program;
const generated = generate(
ast,
{
filename: "inline",
sourceFileName: "inline",
sourceMaps: true,
},
code,
);

const consumer = new sourcemap.SourceMapConsumer(generated.map);
const loc = consumer.originalPositionFor({ line: 2, column: 1 });
expect(loc).toMatchObject({
column: 0,
line: 2,
});
});

it("lazy source map generation", function () {
const code = "function hi (msg) { console.log(msg); }\n";

Expand Down

0 comments on commit f49234a

Please sign in to comment.