Use babylon directly and convert recast's comment algorith to use our own API
parent
7ad69b05ca
commit
5c53a2d59c
17
index.js
17
index.js
|
@ -1,7 +1,7 @@
|
||||||
const recast = require("recast");
|
|
||||||
const babylon = require("babylon");
|
const babylon = require("babylon");
|
||||||
const Printer = require("./src/printer").Printer;
|
const Printer = require("./src/printer").Printer;
|
||||||
const flowParser = require("flow-parser");
|
const flowParser = require("flow-parser");
|
||||||
|
const comments = require("./src/comments");
|
||||||
|
|
||||||
var babylonOptions = {
|
var babylonOptions = {
|
||||||
sourceType: 'module',
|
sourceType: 'module',
|
||||||
|
@ -40,17 +40,16 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ast = recast.parse(text, {
|
ast = babylon.parse(text, babylonOptions);
|
||||||
parser: {
|
|
||||||
parse: function(source) {
|
|
||||||
return babylon.parse(source, babylonOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
opts.originalText = text;
|
// Interleave comment nodes
|
||||||
|
if(ast.comments) {
|
||||||
|
comments.attach(ast.comments, ast, text);
|
||||||
|
ast.comments = [];
|
||||||
|
}
|
||||||
ast.tokens = [];
|
ast.tokens = [];
|
||||||
|
opts.originalText = text;
|
||||||
|
|
||||||
const printer = new Printer(opts);
|
const printer = new Printer(opts);
|
||||||
return printer.printGenerically(ast).code;
|
return printer.printGenerically(ast).code;
|
||||||
|
|
|
@ -13,26 +13,22 @@ var childNodesCacheKey = require("private").makeUniqueKey();
|
||||||
|
|
||||||
// TODO Move a non-caching implementation of this function into ast-types,
|
// TODO Move a non-caching implementation of this function into ast-types,
|
||||||
// and implement a caching wrapper function here.
|
// and implement a caching wrapper function here.
|
||||||
function getSortedChildNodes(node, lines, resultArray) {
|
function getSortedChildNodes(node, text, resultArray) {
|
||||||
if (!node) {
|
if (!node) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The .loc checks below are sensitive to some of the problems that
|
// The loc checks below are sensitive to some of the problems that
|
||||||
// are fixed by this utility function. Specifically, if it decides to
|
// are fixed by this utility function.
|
||||||
// set node.loc to null, indicating that the node's .loc information
|
util.fixFaultyLocations(node, text);
|
||||||
// is unreliable, then we don't want to add node to the resultArray.
|
|
||||||
util.fixFaultyLocations(node, lines);
|
|
||||||
|
|
||||||
if (resultArray) {
|
if (resultArray) {
|
||||||
if (n.Node.check(node) &&
|
if (n.Node.check(node)) {
|
||||||
n.SourceLocation.check(node.loc)) {
|
|
||||||
// This reverse insertion sort almost always takes constant
|
// This reverse insertion sort almost always takes constant
|
||||||
// time because we almost always (maybe always?) append the
|
// time because we almost always (maybe always?) append the
|
||||||
// nodes in order anyway.
|
// nodes in order anyway.
|
||||||
for (var i = resultArray.length - 1; i >= 0; --i) {
|
for (var i = resultArray.length - 1; i >= 0; --i) {
|
||||||
if (comparePos(resultArray[i].loc.end,
|
if (resultArray[i].end - node.start <= 0) {
|
||||||
node.loc.start) <= 0) {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +56,7 @@ function getSortedChildNodes(node, lines, resultArray) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0, nameCount = names.length; i < nameCount; ++i) {
|
for (var i = 0, nameCount = names.length; i < nameCount; ++i) {
|
||||||
getSortedChildNodes(node[names[i]], lines, resultArray);
|
getSortedChildNodes(node[names[i]], text, resultArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultArray;
|
return resultArray;
|
||||||
|
@ -69,8 +65,8 @@ function getSortedChildNodes(node, lines, resultArray) {
|
||||||
// As efficiently as possible, decorate the comment object with
|
// As efficiently as possible, decorate the comment object with
|
||||||
// .precedingNode, .enclosingNode, and/or .followingNode properties, at
|
// .precedingNode, .enclosingNode, and/or .followingNode properties, at
|
||||||
// least one of which is guaranteed to be defined.
|
// least one of which is guaranteed to be defined.
|
||||||
function decorateComment(node, comment, lines) {
|
function decorateComment(node, comment, text) {
|
||||||
var childNodes = getSortedChildNodes(node, lines);
|
var childNodes = getSortedChildNodes(node, text);
|
||||||
|
|
||||||
// Time to dust off the old binary search robes and wizard hat.
|
// Time to dust off the old binary search robes and wizard hat.
|
||||||
var left = 0, right = childNodes.length;
|
var left = 0, right = childNodes.length;
|
||||||
|
@ -78,14 +74,13 @@ function decorateComment(node, comment, lines) {
|
||||||
var middle = (left + right) >> 1;
|
var middle = (left + right) >> 1;
|
||||||
var child = childNodes[middle];
|
var child = childNodes[middle];
|
||||||
|
|
||||||
if (comparePos(child.loc.start, comment.loc.start) <= 0 &&
|
if (child.start - comment.start <= 0 && comment.end - child.end <= 0) {
|
||||||
comparePos(comment.loc.end, child.loc.end) <= 0) {
|
|
||||||
// The comment is completely contained by this child node.
|
// The comment is completely contained by this child node.
|
||||||
decorateComment(comment.enclosingNode = child, comment, lines);
|
decorateComment(comment.enclosingNode = child, comment, text);
|
||||||
return; // Abandon the binary search at this level.
|
return; // Abandon the binary search at this level.
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comparePos(child.loc.end, comment.loc.start) <= 0) {
|
if (child.end - comment.start <= 0) {
|
||||||
// This child node falls completely before the comment.
|
// This child node falls completely before the comment.
|
||||||
// Because we will never consider this node or any nodes
|
// Because we will never consider this node or any nodes
|
||||||
// before it again, this node must be the closest preceding
|
// before it again, this node must be the closest preceding
|
||||||
|
@ -95,7 +90,7 @@ function decorateComment(node, comment, lines) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (comparePos(comment.loc.end, child.loc.start) <= 0) {
|
if (comment.end - child.start <= 0) {
|
||||||
// This child node falls completely after the comment.
|
// This child node falls completely after the comment.
|
||||||
// Because we will never consider this node or any nodes after
|
// Because we will never consider this node or any nodes after
|
||||||
// it again, this node must be the closest following node we
|
// it again, this node must be the closest following node we
|
||||||
|
@ -117,7 +112,7 @@ function decorateComment(node, comment, lines) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.attach = function(comments, ast, lines) {
|
exports.attach = function(comments, ast, text) {
|
||||||
if (!isArray.check(comments)) {
|
if (!isArray.check(comments)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -125,8 +120,7 @@ exports.attach = function(comments, ast, lines) {
|
||||||
var tiesToBreak = [];
|
var tiesToBreak = [];
|
||||||
|
|
||||||
comments.forEach(function(comment) {
|
comments.forEach(function(comment) {
|
||||||
comment.loc.lines = lines;
|
decorateComment(ast, comment, text);
|
||||||
decorateComment(ast, comment, lines);
|
|
||||||
|
|
||||||
var pn = comment.precedingNode;
|
var pn = comment.precedingNode;
|
||||||
var en = comment.enclosingNode;
|
var en = comment.enclosingNode;
|
||||||
|
@ -143,7 +137,7 @@ exports.attach = function(comments, ast, lines) {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (lastTie.followingNode !== comment.followingNode) {
|
if (lastTie.followingNode !== comment.followingNode) {
|
||||||
breakTies(tiesToBreak, lines);
|
breakTies(tiesToBreak, text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,18 +145,18 @@ exports.attach = function(comments, ast, lines) {
|
||||||
|
|
||||||
} else if (pn) {
|
} else if (pn) {
|
||||||
// No contest: we have a trailing comment.
|
// No contest: we have a trailing comment.
|
||||||
breakTies(tiesToBreak, lines);
|
breakTies(tiesToBreak, text);
|
||||||
addTrailingComment(pn, comment);
|
addTrailingComment(pn, comment);
|
||||||
|
|
||||||
} else if (fn) {
|
} else if (fn) {
|
||||||
// No contest: we have a leading comment.
|
// No contest: we have a leading comment.
|
||||||
breakTies(tiesToBreak, lines);
|
breakTies(tiesToBreak, text);
|
||||||
addLeadingComment(fn, comment);
|
addLeadingComment(fn, comment);
|
||||||
|
|
||||||
} else if (en) {
|
} else if (en) {
|
||||||
// The enclosing node has no child nodes at all, so what we
|
// The enclosing node has no child nodes at all, so what we
|
||||||
// have here is a dangling comment, e.g. [/* crickets */].
|
// have here is a dangling comment, e.g. [/* crickets */].
|
||||||
breakTies(tiesToBreak, lines);
|
breakTies(tiesToBreak, text);
|
||||||
addDanglingComment(en, comment);
|
addDanglingComment(en, comment);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -170,7 +164,7 @@ exports.attach = function(comments, ast, lines) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
breakTies(tiesToBreak, lines);
|
breakTies(tiesToBreak, text);
|
||||||
|
|
||||||
comments.forEach(function(comment) {
|
comments.forEach(function(comment) {
|
||||||
// These node references were useful for breaking ties, but we
|
// These node references were useful for breaking ties, but we
|
||||||
|
@ -182,7 +176,7 @@ exports.attach = function(comments, ast, lines) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function breakTies(tiesToBreak, lines) {
|
function breakTies(tiesToBreak, text) {
|
||||||
var tieCount = tiesToBreak.length;
|
var tieCount = tiesToBreak.length;
|
||||||
if (tieCount === 0) {
|
if (tieCount === 0) {
|
||||||
return;
|
return;
|
||||||
|
@ -190,7 +184,7 @@ function breakTies(tiesToBreak, lines) {
|
||||||
|
|
||||||
var pn = tiesToBreak[0].precedingNode;
|
var pn = tiesToBreak[0].precedingNode;
|
||||||
var fn = tiesToBreak[0].followingNode;
|
var fn = tiesToBreak[0].followingNode;
|
||||||
var gapEndPos = fn.loc.start;
|
var gapEndPos = fn.start;
|
||||||
|
|
||||||
// Iterate backwards through tiesToBreak, examining the gaps
|
// Iterate backwards through tiesToBreak, examining the gaps
|
||||||
// between the tied comments. In order to qualify as leading, a
|
// between the tied comments. In order to qualify as leading, a
|
||||||
|
@ -203,23 +197,23 @@ function breakTies(tiesToBreak, lines) {
|
||||||
assert.strictEqual(comment.precedingNode, pn);
|
assert.strictEqual(comment.precedingNode, pn);
|
||||||
assert.strictEqual(comment.followingNode, fn);
|
assert.strictEqual(comment.followingNode, fn);
|
||||||
|
|
||||||
var gap = lines.sliceString(comment.loc.end, gapEndPos);
|
var gap = text.slice(comment.end, gapEndPos);
|
||||||
if (/\S/.test(gap)) {
|
if (/\S/.test(gap)) {
|
||||||
// The gap string contained something other than whitespace.
|
// The gap string contained something other than whitespace.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
gapEndPos = comment.loc.start;
|
gapEndPos = comment.start;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (indexOfFirstLeadingComment <= tieCount &&
|
// while (indexOfFirstLeadingComment <= tieCount &&
|
||||||
(comment = tiesToBreak[indexOfFirstLeadingComment]) &&
|
// (comment = tiesToBreak[indexOfFirstLeadingComment]) &&
|
||||||
// If the comment is a //-style comment and indented more
|
// // If the comment is a //-style comment and indented more
|
||||||
// deeply than the node itself, reconsider it as trailing.
|
// // deeply than the node itself, reconsider it as trailing.
|
||||||
(comment.type === "Line" || comment.type === "CommentLine") &&
|
// (comment.type === "Line" || comment.type === "CommentLine") &&
|
||||||
comment.loc.start.column > fn.loc.start.column) {
|
// comment.loc.start.column > fn.loc.start.column) {
|
||||||
++indexOfFirstLeadingComment;
|
// ++indexOfFirstLeadingComment;
|
||||||
}
|
// }
|
||||||
|
|
||||||
tiesToBreak.forEach(function(comment, i) {
|
tiesToBreak.forEach(function(comment, i) {
|
||||||
if (i < indexOfFirstLeadingComment) {
|
if (i < indexOfFirstLeadingComment) {
|
||||||
|
|
194
src/util.js
194
src/util.js
|
@ -84,136 +84,55 @@ util.composeSourceMaps = function(formerMap, latterMap) {
|
||||||
return smg.toJSON();
|
return smg.toJSON();
|
||||||
};
|
};
|
||||||
|
|
||||||
util.getTrueLoc = function(node, lines) {
|
function expandLoc(parentNode, childNode) {
|
||||||
// It's possible that node is newly-created (not parsed by Esprima),
|
if (childNode.start - parentNode.start < 0) {
|
||||||
// in which case it probably won't have a .loc property (or an
|
parentNode.start = childNode.start;
|
||||||
// .original property for that matter). That's fine; we'll just
|
|
||||||
// pretty-print it as usual.
|
|
||||||
if (!node.loc) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = {
|
if (parentNode.end - childNode.end < 0) {
|
||||||
start: node.loc.start,
|
parentNode.end = childNode.end;
|
||||||
end: node.loc.end
|
|
||||||
};
|
|
||||||
|
|
||||||
function include(node) {
|
|
||||||
expandLoc(result, node.loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the node has any comments, their locations might contribute to
|
|
||||||
// the true start/end positions of the node.
|
|
||||||
if (node.comments) {
|
|
||||||
node.comments.forEach(include);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the node is an export declaration and its .declaration has any
|
|
||||||
// decorators, their locations might contribute to the true start/end
|
|
||||||
// positions of the export declaration node.
|
|
||||||
if (node.declaration && util.isExportDeclaration(node) &&
|
|
||||||
node.declaration.decorators) {
|
|
||||||
node.declaration.decorators.forEach(include);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comparePos(result.start, result.end) < 0) {
|
|
||||||
// Trim leading whitespace.
|
|
||||||
result.start = copyPos(result.start);
|
|
||||||
lines.skipSpaces(result.start, false, true);
|
|
||||||
|
|
||||||
if (comparePos(result.start, result.end) < 0) {
|
|
||||||
// Trim trailing whitespace, if the end location is not already the
|
|
||||||
// same as the start location.
|
|
||||||
result.end = copyPos(result.end);
|
|
||||||
lines.skipSpaces(result.end, true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
function expandLoc(parentLoc, childLoc) {
|
|
||||||
if (parentLoc && childLoc) {
|
|
||||||
if (comparePos(childLoc.start, parentLoc.start) < 0) {
|
|
||||||
parentLoc.start = childLoc.start;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (comparePos(parentLoc.end, childLoc.end) < 0) {
|
|
||||||
parentLoc.end = childLoc.end;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
util.fixFaultyLocations = function(node, lines) {
|
util.fixFaultyLocations = function(node, text) {
|
||||||
var loc = node.loc;
|
|
||||||
if (loc) {
|
|
||||||
if (loc.start.line < 1) {
|
|
||||||
loc.start.line = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loc.end.line < 1) {
|
|
||||||
loc.end.line = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.type === "TemplateLiteral") {
|
if (node.type === "TemplateLiteral") {
|
||||||
fixTemplateLiteral(node, lines);
|
fixTemplateLiteral(node, text);
|
||||||
|
|
||||||
} else if (loc && node.decorators) {
|
} else if (node.decorators) {
|
||||||
// Expand the .loc of the node responsible for printing the decorators
|
// Expand the loc of the node responsible for printing the decorators
|
||||||
// (here, the decorated node) so that it includes node.decorators.
|
// (here, the decorated node) so that it includes node.decorators.
|
||||||
node.decorators.forEach(function (decorator) {
|
node.decorators.forEach(function (decorator) {
|
||||||
expandLoc(loc, decorator.loc);
|
expandLoc(node, decorator);
|
||||||
});
|
});
|
||||||
|
|
||||||
} else if (node.declaration && util.isExportDeclaration(node)) {
|
} else if (node.declaration && util.isExportDeclaration(node)) {
|
||||||
// Nullify .loc information for the child declaration so that we never
|
// Expand the loc of the node responsible for printing the decorators
|
||||||
// try to reprint it without also reprinting the export declaration.
|
|
||||||
node.declaration.loc = null;
|
|
||||||
|
|
||||||
// Expand the .loc of the node responsible for printing the decorators
|
|
||||||
// (here, the export declaration) so that it includes node.decorators.
|
// (here, the export declaration) so that it includes node.decorators.
|
||||||
var decorators = node.declaration.decorators;
|
var decorators = node.declaration.decorators;
|
||||||
if (decorators) {
|
if (decorators) {
|
||||||
decorators.forEach(function (decorator) {
|
decorators.forEach(function (decorator) {
|
||||||
expandLoc(loc, decorator.loc);
|
expandLoc(node, decorator);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if ((n.MethodDefinition && n.MethodDefinition.check(node)) ||
|
} else if ((n.MethodDefinition && n.MethodDefinition.check(node)) ||
|
||||||
(n.Property.check(node) && (node.method || node.shorthand))) {
|
(n.Property.check(node) && (node.method || node.shorthand))) {
|
||||||
// If the node is a MethodDefinition or a .method or .shorthand
|
|
||||||
// Property, then the location information stored in
|
|
||||||
// node.value.loc is very likely untrustworthy (just the {body}
|
|
||||||
// part of a method, or nothing in the case of shorthand
|
|
||||||
// properties), so we null out that information to prevent
|
|
||||||
// accidental reuse of bogus source code during reprinting.
|
|
||||||
node.value.loc = null;
|
|
||||||
|
|
||||||
if (n.FunctionExpression.check(node.value)) {
|
if (n.FunctionExpression.check(node.value)) {
|
||||||
// FunctionExpression method values should be anonymous,
|
// FunctionExpression method values should be anonymous,
|
||||||
// because their .id fields are ignored anyway.
|
// because their .id fields are ignored anyway.
|
||||||
node.value.id = null;
|
node.value.id = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (node.type === "ObjectTypeProperty") {
|
} else if (node.type === "ObjectTypeProperty") {
|
||||||
var loc = node.loc;
|
var end = skipSpaces(text, node.end, true);
|
||||||
var end = loc && loc.end;
|
if (end !== false && text.charAt(end) === ",") {
|
||||||
if (end) {
|
// Some parsers accidentally include trailing commas in the
|
||||||
end = copyPos(end);
|
// .end information for ObjectTypeProperty nodes.
|
||||||
if (lines.prevPos(end) &&
|
if ((end = skipSpaces(text, end - 1, true)) !== false) {
|
||||||
lines.charAt(end) === ",") {
|
loc.end = end;
|
||||||
// Some parsers accidentally include trailing commas in the
|
|
||||||
// .loc.end information for ObjectTypeProperty nodes.
|
|
||||||
if ((end = lines.skipSpaces(end, true, true))) {
|
|
||||||
loc.end = end;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function fixTemplateLiteral(node, lines) {
|
function fixTemplateLiteral(node, text) {
|
||||||
assert.strictEqual(node.type, "TemplateLiteral");
|
assert.strictEqual(node.type, "TemplateLiteral");
|
||||||
|
|
||||||
if (node.quasis.length === 0) {
|
if (node.quasis.length === 0) {
|
||||||
|
@ -221,53 +140,53 @@ function fixTemplateLiteral(node, lines) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First we need to exclude the opening ` from the .loc of the first
|
// First we need to exclude the opening ` from the loc of the first
|
||||||
// quasi element, in case the parser accidentally decided to include it.
|
// quasi element, in case the parser accidentally decided to include it.
|
||||||
var afterLeftBackTickPos = copyPos(node.loc.start);
|
var afterLeftBackTickPos = node.start;
|
||||||
assert.strictEqual(lines.charAt(afterLeftBackTickPos), "`");
|
assert.strictEqual(text.charAt(afterLeftBackTickPos), "`");
|
||||||
assert.ok(lines.nextPos(afterLeftBackTickPos));
|
assert.ok(afterLeftBackTickPos < text.length);
|
||||||
var firstQuasi = node.quasis[0];
|
var firstQuasi = node.quasis[0];
|
||||||
if (comparePos(firstQuasi.loc.start, afterLeftBackTickPos) < 0) {
|
if (firstQuasi.start - afterLeftBackTickPos < 0) {
|
||||||
firstQuasi.loc.start = afterLeftBackTickPos;
|
firstQuasi.start = afterLeftBackTickPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next we need to exclude the closing ` from the .loc of the last quasi
|
// Next we need to exclude the closing ` from the loc of the last quasi
|
||||||
// element, in case the parser accidentally decided to include it.
|
// element, in case the parser accidentally decided to include it.
|
||||||
var rightBackTickPos = copyPos(node.loc.end);
|
var rightBackTickPos = node.end;
|
||||||
assert.ok(lines.prevPos(rightBackTickPos));
|
assert.ok(rightBackTickPos >= 0);
|
||||||
assert.strictEqual(lines.charAt(rightBackTickPos), "`");
|
assert.strictEqual(text.charAt(rightBackTickPos), "`");
|
||||||
var lastQuasi = node.quasis[node.quasis.length - 1];
|
var lastQuasi = node.quasis[node.quasis.length - 1];
|
||||||
if (comparePos(rightBackTickPos, lastQuasi.loc.end) < 0) {
|
if (rightBackTickPos - lastQuasi.end < 0) {
|
||||||
lastQuasi.loc.end = rightBackTickPos;
|
lastQuasi.end = rightBackTickPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we need to exclude ${ and } characters from the .loc's of all
|
// Now we need to exclude ${ and } characters from the loc's of all
|
||||||
// quasi elements, since some parsers accidentally include them.
|
// quasi elements, since some parsers accidentally include them.
|
||||||
node.expressions.forEach(function (expr, i) {
|
node.expressions.forEach(function (expr, i) {
|
||||||
// Rewind from expr.loc.start over any whitespace and the ${ that
|
// Rewind from expr.start over any whitespace and the ${ that
|
||||||
// precedes the expression. The position of the $ should be the same
|
// precedes the expression. The position of the $ should be the same
|
||||||
// as the .loc.end of the preceding quasi element, but some parsers
|
// as the .end of the preceding quasi element, but some parsers
|
||||||
// accidentally include the ${ in the .loc of the quasi element.
|
// accidentally include the ${ in the loc of the quasi element.
|
||||||
var dollarCurlyPos = lines.skipSpaces(expr.loc.start, true, false);
|
var dollarCurlyPos = skipSpaces(text, expr.start - 1, true);
|
||||||
if (lines.prevPos(dollarCurlyPos) &&
|
if (dollarCurlyPos - 1 >= 0 &&
|
||||||
lines.charAt(dollarCurlyPos) === "{" &&
|
text.charAt(dollarCurlyPos - 1) === "{" &&
|
||||||
lines.prevPos(dollarCurlyPos) &&
|
dollarCurlyPos - 2 >= 0 &&
|
||||||
lines.charAt(dollarCurlyPos) === "$") {
|
text.charAt(dollarCurlyPos - 2) === "$") {
|
||||||
var quasiBefore = node.quasis[i];
|
var quasiBefore = node.quasis[i];
|
||||||
if (comparePos(dollarCurlyPos, quasiBefore.loc.end) < 0) {
|
if (dollarCurlyPos - quasiBefore.end < 0) {
|
||||||
quasiBefore.loc.end = dollarCurlyPos;
|
quasiBefore.end = dollarCurlyPos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Likewise, some parsers accidentally include the } that follows
|
// Likewise, some parsers accidentally include the } that follows
|
||||||
// the expression in the .loc of the following quasi element.
|
// the expression in the loc of the following quasi element.
|
||||||
var rightCurlyPos = lines.skipSpaces(expr.loc.end, false, false);
|
var rightCurlyPos = skipSpaces(text, expr.end);
|
||||||
if (lines.charAt(rightCurlyPos) === "}") {
|
if (text.charAt(rightCurlyPos) === "}") {
|
||||||
assert.ok(lines.nextPos(rightCurlyPos));
|
assert.ok(rightCurlyPos + 1 < text.length);
|
||||||
// Now rightCurlyPos is technically the position just after the }.
|
// Now rightCurlyPos is technically the position just after the }.
|
||||||
var quasiAfter = node.quasis[i + 1];
|
var quasiAfter = node.quasis[i + 1];
|
||||||
if (comparePos(quasiAfter.loc.start, rightCurlyPos) < 0) {
|
if (quasiAfter.start - rightCurlyPos < 0) {
|
||||||
quasiAfter.loc.start = rightCurlyPos;
|
quasiAfter.start = rightCurlyPos;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -337,3 +256,20 @@ util.newlineExistsBefore = function(text, index) {
|
||||||
util.newlineExistsAfter = function(text, index) {
|
util.newlineExistsAfter = function(text, index) {
|
||||||
return _findNewline(text, index);
|
return _findNewline(text, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function skipSpaces(text, index, backwards) {
|
||||||
|
const length = text.length;
|
||||||
|
let cursor = backwards ? (index - 1) : (index + 1);
|
||||||
|
// Look forward and see if there is a newline after/before this code
|
||||||
|
// by scanning up/back to the next non-indentation character.
|
||||||
|
while (cursor > 0 && cursor < length) {
|
||||||
|
const c = text.charAt(cursor);
|
||||||
|
// Skip any whitespace chars
|
||||||
|
if (!c.match(/\S/)) {
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
backwards ? cursor-- : cursor++;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
util.skipSpaces = skipSpaces;
|
||||||
|
|
Loading…
Reference in New Issue