Track sourcemap location on a stack - fixes T7255
This commit is contained in:
@@ -19,6 +19,13 @@ export default class Buffer {
|
||||
// to make sure that v8 doesn't "flatten" the string more often than needed
|
||||
// see https://github.com/babel/babel/pull/3283 for details.
|
||||
this.last = "";
|
||||
|
||||
this.map = null;
|
||||
this._sourcePosition = {
|
||||
line: null,
|
||||
column: null,
|
||||
filename: null,
|
||||
};
|
||||
}
|
||||
|
||||
printedCommentStarts: Object;
|
||||
@@ -230,6 +237,40 @@ export default class Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a given position as the current source location so generated code after this call
|
||||
* will be given this position in the sourcemap.
|
||||
*/
|
||||
|
||||
source(prop: string, loc: Location) {
|
||||
if (prop && !loc) return;
|
||||
|
||||
let pos = loc ? loc[prop] : null;
|
||||
|
||||
this._sourcePosition.line = pos ? pos.line : null;
|
||||
this._sourcePosition.column = pos ? pos.column : null;
|
||||
this._sourcePosition.filename = loc && loc.filename || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call a callback with a specific source location and restore on completion.
|
||||
*/
|
||||
|
||||
withSource(prop: string, loc: Location, cb: () => void) {
|
||||
// Use the call stack to manage a stack of "source location" data.
|
||||
let originalLine = this._sourcePosition.line;
|
||||
let originalColumn = this._sourcePosition.column;
|
||||
let originalFilename = this._sourcePosition.filename;
|
||||
|
||||
this.source(prop, loc);
|
||||
|
||||
cb();
|
||||
|
||||
this._sourcePosition.line = originalLine;
|
||||
this._sourcePosition.column = originalColumn;
|
||||
this._sourcePosition.filename = originalFilename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push a string to the buffer, maintaining indentation and newlines.
|
||||
*/
|
||||
@@ -276,6 +317,9 @@ export default class Buffer {
|
||||
}
|
||||
}
|
||||
|
||||
// If there the line is ending, adding a new mapping marker is redundant
|
||||
if (str[0] !== "\n") this.map.mark(this._sourcePosition);
|
||||
|
||||
//
|
||||
this.position.push(str);
|
||||
this.buf += str;
|
||||
|
||||
@@ -51,9 +51,10 @@ export default class Printer extends Buffer {
|
||||
|
||||
if (opts.before) opts.before();
|
||||
|
||||
this.map.mark(node);
|
||||
|
||||
this._print(node, parent);
|
||||
let loc = (t.isProgram(node) || t.isFile(node)) ? null : node.loc;
|
||||
this.withSource("start", loc, () => {
|
||||
this._print(node, parent);
|
||||
});
|
||||
|
||||
// Check again if any of our children may have left an aux comment on the stack
|
||||
if (node.loc) this.printAuxAfterComment();
|
||||
@@ -64,7 +65,6 @@ export default class Printer extends Buffer {
|
||||
|
||||
// end
|
||||
this._printStack.pop();
|
||||
if (parent) this.map.mark(parent);
|
||||
if (opts.after) opts.after();
|
||||
|
||||
this.format.concise = oldConcise;
|
||||
@@ -268,46 +268,50 @@ export default class Printer extends Buffer {
|
||||
this.printedCommentStarts[comment.start] = true;
|
||||
}
|
||||
|
||||
this.catchUp(comment);
|
||||
// Exclude comments from source mappings since they will only clutter things.
|
||||
this.withSource(null, null, () => {
|
||||
this.catchUp(comment);
|
||||
|
||||
// whitespace before
|
||||
this.newline(this.whitespace.getNewlinesBefore(comment));
|
||||
// whitespace before
|
||||
this.newline(this.whitespace.getNewlinesBefore(comment));
|
||||
|
||||
let column = this.position.column;
|
||||
let val = this.generateComment(comment);
|
||||
let column = this.position.column;
|
||||
let val = this.generateComment(comment);
|
||||
|
||||
if (column && !this.isLast(["\n", " ", "[", "{"])) {
|
||||
this._push(" ");
|
||||
column++;
|
||||
}
|
||||
|
||||
//
|
||||
if (comment.type === "CommentBlock" && this.format.indent.adjustMultilineComment) {
|
||||
let offset = comment.loc && comment.loc.start.column;
|
||||
if (offset) {
|
||||
let newlineRegex = new RegExp("\\n\\s{1," + offset + "}", "g");
|
||||
val = val.replace(newlineRegex, "\n");
|
||||
if (column && !this.isLast(["\n", " ", "[", "{"])) {
|
||||
this._push(" ");
|
||||
column++;
|
||||
}
|
||||
|
||||
let indent = Math.max(this.indentSize(), column);
|
||||
val = val.replace(/\n/g, `\n${repeating(" ", indent)}`);
|
||||
}
|
||||
//
|
||||
if (comment.type === "CommentBlock" && this.format.indent.adjustMultilineComment) {
|
||||
let offset = comment.loc && comment.loc.start.column;
|
||||
if (offset) {
|
||||
let newlineRegex = new RegExp("\\n\\s{1," + offset + "}", "g");
|
||||
val = val.replace(newlineRegex, "\n");
|
||||
}
|
||||
|
||||
if (column === 0) {
|
||||
val = this.getIndent() + val;
|
||||
}
|
||||
let indent = Math.max(this.indentSize(), column);
|
||||
val = val.replace(/\n/g, `\n${repeating(" ", indent)}`);
|
||||
}
|
||||
|
||||
// force a newline for line comments when retainLines is set in case the next printed node
|
||||
// doesn't catch up
|
||||
if ((this.format.compact || this.format.concise || this.format.retainLines) && comment.type === "CommentLine") {
|
||||
val += "\n";
|
||||
}
|
||||
if (column === 0) {
|
||||
val = this.getIndent() + val;
|
||||
}
|
||||
|
||||
//
|
||||
this._push(val);
|
||||
// force a newline for line comments when retainLines is set in case the next printed node
|
||||
// doesn't catch up
|
||||
if ((this.format.compact || this.format.concise || this.format.retainLines) &&
|
||||
comment.type === "CommentLine") {
|
||||
val += "\n";
|
||||
}
|
||||
|
||||
// whitespace after
|
||||
this.newline(this.whitespace.getNewlinesAfter(comment));
|
||||
//
|
||||
this._push(val);
|
||||
|
||||
// whitespace after
|
||||
this.newline(this.whitespace.getNewlinesAfter(comment));
|
||||
});
|
||||
}
|
||||
|
||||
printComments(comments?: Array<Object>) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import sourceMap from "source-map";
|
||||
import * as t from "babel-types";
|
||||
|
||||
/**
|
||||
* Build a sourcemap.
|
||||
@@ -43,46 +42,40 @@ export default class SourceMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a node's generated position, and add it to the sourcemap.
|
||||
* Mark the current generated position with a source position. May also be passed null line/column
|
||||
* values to insert a mapping to nothing.
|
||||
*/
|
||||
|
||||
mark(node) {
|
||||
let loc = node.loc;
|
||||
if (!loc) return; // no location info
|
||||
|
||||
mark(sourcePos: Object) {
|
||||
let map = this.map;
|
||||
if (!map) return; // no source map
|
||||
|
||||
if (t.isProgram(node) || t.isFile(node)) return; // illegal mapping nodes
|
||||
|
||||
let position = this.position;
|
||||
|
||||
let generated = {
|
||||
line: position.line,
|
||||
column: position.column
|
||||
};
|
||||
// Adding an empty mapping at the start of a generated line just clutters the map.
|
||||
if (this._lastGenLine !== position.line && sourcePos.line === null) return;
|
||||
|
||||
let original = loc.start;
|
||||
|
||||
// Avoid emitting duplicates on either side. Duplicated
|
||||
// original values creates unnecesssarily large source maps
|
||||
// and increases compile time. Duplicates on the generated
|
||||
// side can lead to incorrect mappings.
|
||||
if (comparePosition(original, this.last.original)
|
||||
|| comparePosition(generated, this.last.generated)) {
|
||||
// If this mapping points to the same source location as the last one, we can ignore it since
|
||||
// the previous one covers it.
|
||||
if (this._lastGenLine === position.line && this._lastSourceLine === sourcePos.line &&
|
||||
this._lastSourceColumn === sourcePos.column) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.last = {
|
||||
source: loc.filename || this.opts.sourceFileName,
|
||||
generated: generated,
|
||||
original: original
|
||||
};
|
||||
this._lastGenLine = position.line;
|
||||
this._lastSourceLine = sourcePos.line;
|
||||
this._lastSourceColumn = sourcePos.column;
|
||||
|
||||
map.addMapping(this.last);
|
||||
map.addMapping({
|
||||
generated: {
|
||||
line: position.line,
|
||||
column: position.column
|
||||
},
|
||||
source: sourcePos.line == null ? null : sourcePos.filename || this.opts.sourceFileName,
|
||||
original: sourcePos.line == null ? null : {
|
||||
line: sourcePos.line,
|
||||
column: sourcePos.column,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function comparePosition(a, b) {
|
||||
return a.line === b.line && a.column === b.column;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user