Track sourcemap location on a stack - fixes T7255

This commit is contained in:
Logan Smyth
2016-04-10 21:16:11 -07:00
parent 7d6d4c204b
commit 76bb1dffaa
15 changed files with 118 additions and 77 deletions

View File

@@ -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;

View File

@@ -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>) {

View File

@@ -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;
}