initial commit
This commit is contained in:
351
node_modules/recast/lib/comments.js
generated
vendored
Normal file
351
node_modules/recast/lib/comments.js
generated
vendored
Normal file
@@ -0,0 +1,351 @@
|
||||
var assert = require("assert");
|
||||
var types = require("./types");
|
||||
var n = types.namedTypes;
|
||||
var isArray = types.builtInTypes.array;
|
||||
var isObject = types.builtInTypes.object;
|
||||
var linesModule = require("./lines");
|
||||
var fromString = linesModule.fromString;
|
||||
var Lines = linesModule.Lines;
|
||||
var concat = linesModule.concat;
|
||||
var util = require("./util");
|
||||
var comparePos = util.comparePos;
|
||||
var childNodesCacheKey = require("private").makeUniqueKey();
|
||||
|
||||
// TODO Move a non-caching implementation of this function into ast-types,
|
||||
// and implement a caching wrapper function here.
|
||||
function getSortedChildNodes(node, lines, resultArray) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The .loc checks below are sensitive to some of the problems that
|
||||
// are fixed by this utility function. Specifically, if it decides to
|
||||
// set node.loc to null, indicating that the node's .loc information
|
||||
// is unreliable, then we don't want to add node to the resultArray.
|
||||
util.fixFaultyLocations(node, lines);
|
||||
|
||||
if (resultArray) {
|
||||
if (n.Node.check(node) &&
|
||||
n.SourceLocation.check(node.loc)) {
|
||||
// This reverse insertion sort almost always takes constant
|
||||
// time because we almost always (maybe always?) append the
|
||||
// nodes in order anyway.
|
||||
for (var i = resultArray.length - 1; i >= 0; --i) {
|
||||
if (comparePos(resultArray[i].loc.end,
|
||||
node.loc.start) <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
resultArray.splice(i + 1, 0, node);
|
||||
return;
|
||||
}
|
||||
} else if (node[childNodesCacheKey]) {
|
||||
return node[childNodesCacheKey];
|
||||
}
|
||||
|
||||
var names;
|
||||
if (isArray.check(node)) {
|
||||
names = Object.keys(node);
|
||||
} else if (isObject.check(node)) {
|
||||
names = types.getFieldNames(node);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!resultArray) {
|
||||
Object.defineProperty(node, childNodesCacheKey, {
|
||||
value: resultArray = [],
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
|
||||
for (var i = 0, nameCount = names.length; i < nameCount; ++i) {
|
||||
getSortedChildNodes(node[names[i]], lines, resultArray);
|
||||
}
|
||||
|
||||
return resultArray;
|
||||
}
|
||||
|
||||
// As efficiently as possible, decorate the comment object with
|
||||
// .precedingNode, .enclosingNode, and/or .followingNode properties, at
|
||||
// least one of which is guaranteed to be defined.
|
||||
function decorateComment(node, comment, lines) {
|
||||
var childNodes = getSortedChildNodes(node, lines);
|
||||
|
||||
// Time to dust off the old binary search robes and wizard hat.
|
||||
var left = 0, right = childNodes.length;
|
||||
while (left < right) {
|
||||
var middle = (left + right) >> 1;
|
||||
var child = childNodes[middle];
|
||||
|
||||
if (comparePos(child.loc.start, comment.loc.start) <= 0 &&
|
||||
comparePos(comment.loc.end, child.loc.end) <= 0) {
|
||||
// The comment is completely contained by this child node.
|
||||
decorateComment(comment.enclosingNode = child, comment, lines);
|
||||
return; // Abandon the binary search at this level.
|
||||
}
|
||||
|
||||
if (comparePos(child.loc.end, comment.loc.start) <= 0) {
|
||||
// This child node falls completely before the comment.
|
||||
// Because we will never consider this node or any nodes
|
||||
// before it again, this node must be the closest preceding
|
||||
// node we have encountered so far.
|
||||
var precedingNode = child;
|
||||
left = middle + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (comparePos(comment.loc.end, child.loc.start) <= 0) {
|
||||
// This child node falls completely after the comment.
|
||||
// Because we will never consider this node or any nodes after
|
||||
// it again, this node must be the closest following node we
|
||||
// have encountered so far.
|
||||
var followingNode = child;
|
||||
right = middle;
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new Error("Comment location overlaps with node location");
|
||||
}
|
||||
|
||||
if (precedingNode) {
|
||||
comment.precedingNode = precedingNode;
|
||||
}
|
||||
|
||||
if (followingNode) {
|
||||
comment.followingNode = followingNode;
|
||||
}
|
||||
}
|
||||
|
||||
exports.attach = function(comments, ast, lines) {
|
||||
if (!isArray.check(comments)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tiesToBreak = [];
|
||||
|
||||
comments.forEach(function(comment) {
|
||||
comment.loc.lines = lines;
|
||||
decorateComment(ast, comment, lines);
|
||||
|
||||
var pn = comment.precedingNode;
|
||||
var en = comment.enclosingNode;
|
||||
var fn = comment.followingNode;
|
||||
|
||||
if (pn && fn) {
|
||||
var tieCount = tiesToBreak.length;
|
||||
if (tieCount > 0) {
|
||||
var lastTie = tiesToBreak[tieCount - 1];
|
||||
|
||||
assert.strictEqual(
|
||||
lastTie.precedingNode === comment.precedingNode,
|
||||
lastTie.followingNode === comment.followingNode
|
||||
);
|
||||
|
||||
if (lastTie.followingNode !== comment.followingNode) {
|
||||
breakTies(tiesToBreak, lines);
|
||||
}
|
||||
}
|
||||
|
||||
tiesToBreak.push(comment);
|
||||
|
||||
} else if (pn) {
|
||||
// No contest: we have a trailing comment.
|
||||
breakTies(tiesToBreak, lines);
|
||||
addTrailingComment(pn, comment);
|
||||
|
||||
} else if (fn) {
|
||||
// No contest: we have a leading comment.
|
||||
breakTies(tiesToBreak, lines);
|
||||
addLeadingComment(fn, comment);
|
||||
|
||||
} else if (en) {
|
||||
// The enclosing node has no child nodes at all, so what we
|
||||
// have here is a dangling comment, e.g. [/* crickets */].
|
||||
breakTies(tiesToBreak, lines);
|
||||
addDanglingComment(en, comment);
|
||||
|
||||
} else {
|
||||
throw new Error("AST contains no nodes at all?");
|
||||
}
|
||||
});
|
||||
|
||||
breakTies(tiesToBreak, lines);
|
||||
|
||||
comments.forEach(function(comment) {
|
||||
// These node references were useful for breaking ties, but we
|
||||
// don't need them anymore, and they create cycles in the AST that
|
||||
// may lead to infinite recursion if we don't delete them here.
|
||||
delete comment.precedingNode;
|
||||
delete comment.enclosingNode;
|
||||
delete comment.followingNode;
|
||||
});
|
||||
};
|
||||
|
||||
function breakTies(tiesToBreak, lines) {
|
||||
var tieCount = tiesToBreak.length;
|
||||
if (tieCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var pn = tiesToBreak[0].precedingNode;
|
||||
var fn = tiesToBreak[0].followingNode;
|
||||
var gapEndPos = fn.loc.start;
|
||||
|
||||
// Iterate backwards through tiesToBreak, examining the gaps
|
||||
// between the tied comments. In order to qualify as leading, a
|
||||
// comment must be separated from fn by an unbroken series of
|
||||
// whitespace-only gaps (or other comments).
|
||||
for (var indexOfFirstLeadingComment = tieCount;
|
||||
indexOfFirstLeadingComment > 0;
|
||||
--indexOfFirstLeadingComment) {
|
||||
var comment = tiesToBreak[indexOfFirstLeadingComment - 1];
|
||||
assert.strictEqual(comment.precedingNode, pn);
|
||||
assert.strictEqual(comment.followingNode, fn);
|
||||
|
||||
var gap = lines.sliceString(comment.loc.end, gapEndPos);
|
||||
if (/\S/.test(gap)) {
|
||||
// The gap string contained something other than whitespace.
|
||||
break;
|
||||
}
|
||||
|
||||
gapEndPos = comment.loc.start;
|
||||
}
|
||||
|
||||
while (indexOfFirstLeadingComment <= tieCount &&
|
||||
(comment = tiesToBreak[indexOfFirstLeadingComment]) &&
|
||||
// If the comment is a //-style comment and indented more
|
||||
// deeply than the node itself, reconsider it as trailing.
|
||||
(comment.type === "Line" || comment.type === "CommentLine") &&
|
||||
comment.loc.start.column > fn.loc.start.column) {
|
||||
++indexOfFirstLeadingComment;
|
||||
}
|
||||
|
||||
tiesToBreak.forEach(function(comment, i) {
|
||||
if (i < indexOfFirstLeadingComment) {
|
||||
addTrailingComment(pn, comment);
|
||||
} else {
|
||||
addLeadingComment(fn, comment);
|
||||
}
|
||||
});
|
||||
|
||||
tiesToBreak.length = 0;
|
||||
}
|
||||
|
||||
function addCommentHelper(node, comment) {
|
||||
var comments = node.comments || (node.comments = []);
|
||||
comments.push(comment);
|
||||
}
|
||||
|
||||
function addLeadingComment(node, comment) {
|
||||
comment.leading = true;
|
||||
comment.trailing = false;
|
||||
addCommentHelper(node, comment);
|
||||
}
|
||||
|
||||
function addDanglingComment(node, comment) {
|
||||
comment.leading = false;
|
||||
comment.trailing = false;
|
||||
addCommentHelper(node, comment);
|
||||
}
|
||||
|
||||
function addTrailingComment(node, comment) {
|
||||
comment.leading = false;
|
||||
comment.trailing = true;
|
||||
addCommentHelper(node, comment);
|
||||
}
|
||||
|
||||
function printLeadingComment(commentPath, print) {
|
||||
var comment = commentPath.getValue();
|
||||
n.Comment.assert(comment);
|
||||
|
||||
var loc = comment.loc;
|
||||
var lines = loc && loc.lines;
|
||||
var parts = [print(commentPath)];
|
||||
|
||||
if (comment.trailing) {
|
||||
// When we print trailing comments as leading comments, we don't
|
||||
// want to bring any trailing spaces along.
|
||||
parts.push("\n");
|
||||
|
||||
} else if (lines instanceof Lines) {
|
||||
var trailingSpace = lines.slice(
|
||||
loc.end,
|
||||
lines.skipSpaces(loc.end)
|
||||
);
|
||||
|
||||
if (trailingSpace.length === 1) {
|
||||
// If the trailing space contains no newlines, then we want to
|
||||
// preserve it exactly as we found it.
|
||||
parts.push(trailingSpace);
|
||||
} else {
|
||||
// If the trailing space contains newlines, then replace it
|
||||
// with just that many newlines, with all other spaces removed.
|
||||
parts.push(new Array(trailingSpace.length).join("\n"));
|
||||
}
|
||||
|
||||
} else {
|
||||
parts.push("\n");
|
||||
}
|
||||
|
||||
return concat(parts);
|
||||
}
|
||||
|
||||
function printTrailingComment(commentPath, print) {
|
||||
var comment = commentPath.getValue(commentPath);
|
||||
n.Comment.assert(comment);
|
||||
|
||||
var loc = comment.loc;
|
||||
var lines = loc && loc.lines;
|
||||
var parts = [];
|
||||
|
||||
if (lines instanceof Lines) {
|
||||
var fromPos = lines.skipSpaces(loc.start, true) || lines.firstPos();
|
||||
var leadingSpace = lines.slice(fromPos, loc.start);
|
||||
|
||||
if (leadingSpace.length === 1) {
|
||||
// If the leading space contains no newlines, then we want to
|
||||
// preserve it exactly as we found it.
|
||||
parts.push(leadingSpace);
|
||||
} else {
|
||||
// If the leading space contains newlines, then replace it
|
||||
// with just that many newlines, sans all other spaces.
|
||||
parts.push(new Array(leadingSpace.length).join("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
parts.push(print(commentPath));
|
||||
|
||||
return concat(parts);
|
||||
}
|
||||
|
||||
exports.printComments = function(path, print) {
|
||||
var value = path.getValue();
|
||||
var innerLines = print(path);
|
||||
var comments = n.Node.check(value) &&
|
||||
types.getFieldValue(value, "comments");
|
||||
|
||||
if (!comments || comments.length === 0) {
|
||||
return innerLines;
|
||||
}
|
||||
|
||||
var leadingParts = [];
|
||||
var trailingParts = [innerLines];
|
||||
|
||||
path.each(function(commentPath) {
|
||||
var comment = commentPath.getValue();
|
||||
var leading = types.getFieldValue(comment, "leading");
|
||||
var trailing = types.getFieldValue(comment, "trailing");
|
||||
|
||||
if (leading || (trailing && !(n.Statement.check(value) ||
|
||||
comment.type === "Block" ||
|
||||
comment.type === "CommentBlock"))) {
|
||||
leadingParts.push(printLeadingComment(commentPath, print));
|
||||
} else if (trailing) {
|
||||
trailingParts.push(printTrailingComment(commentPath, print));
|
||||
}
|
||||
}, "comments");
|
||||
|
||||
leadingParts.push.apply(leadingParts, trailingParts);
|
||||
return concat(leadingParts);
|
||||
};
|
||||
486
node_modules/recast/lib/fast-path.js
generated
vendored
Normal file
486
node_modules/recast/lib/fast-path.js
generated
vendored
Normal file
@@ -0,0 +1,486 @@
|
||||
var assert = require("assert");
|
||||
var types = require("./types");
|
||||
var n = types.namedTypes;
|
||||
var Node = n.Node;
|
||||
var isArray = types.builtInTypes.array;
|
||||
var isNumber = types.builtInTypes.number;
|
||||
|
||||
function FastPath(value) {
|
||||
assert.ok(this instanceof FastPath);
|
||||
this.stack = [value];
|
||||
}
|
||||
|
||||
var FPp = FastPath.prototype;
|
||||
module.exports = FastPath;
|
||||
|
||||
// Static convenience function for coercing a value to a FastPath.
|
||||
FastPath.from = function(obj) {
|
||||
if (obj instanceof FastPath) {
|
||||
// Return a defensive copy of any existing FastPath instances.
|
||||
return obj.copy();
|
||||
}
|
||||
|
||||
if (obj instanceof types.NodePath) {
|
||||
// For backwards compatibility, unroll NodePath instances into
|
||||
// lightweight FastPath [..., name, value] stacks.
|
||||
var copy = Object.create(FastPath.prototype);
|
||||
var stack = [obj.value];
|
||||
for (var pp; (pp = obj.parentPath); obj = pp)
|
||||
stack.push(obj.name, pp.value);
|
||||
copy.stack = stack.reverse();
|
||||
return copy;
|
||||
}
|
||||
|
||||
// Otherwise use obj as the value of the new FastPath instance.
|
||||
return new FastPath(obj);
|
||||
};
|
||||
|
||||
FPp.copy = function copy() {
|
||||
var copy = Object.create(FastPath.prototype);
|
||||
copy.stack = this.stack.slice(0);
|
||||
return copy;
|
||||
};
|
||||
|
||||
// The name of the current property is always the penultimate element of
|
||||
// this.stack, and always a String.
|
||||
FPp.getName = function getName() {
|
||||
var s = this.stack;
|
||||
var len = s.length;
|
||||
if (len > 1) {
|
||||
return s[len - 2];
|
||||
}
|
||||
// Since the name is always a string, null is a safe sentinel value to
|
||||
// return if we do not know the name of the (root) value.
|
||||
return null;
|
||||
};
|
||||
|
||||
// The value of the current property is always the final element of
|
||||
// this.stack.
|
||||
FPp.getValue = function getValue() {
|
||||
var s = this.stack;
|
||||
return s[s.length - 1];
|
||||
};
|
||||
|
||||
function getNodeHelper(path, count) {
|
||||
var s = path.stack;
|
||||
|
||||
for (var i = s.length - 1; i >= 0; i -= 2) {
|
||||
var value = s[i];
|
||||
if (n.Node.check(value) && --count < 0) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
FPp.getNode = function getNode(count) {
|
||||
return getNodeHelper(this, ~~count);
|
||||
};
|
||||
|
||||
FPp.getParentNode = function getParentNode(count) {
|
||||
return getNodeHelper(this, ~~count + 1);
|
||||
};
|
||||
|
||||
// The length of the stack can be either even or odd, depending on whether
|
||||
// or not we have a name for the root value. The difference between the
|
||||
// index of the root value and the index of the final value is always
|
||||
// even, though, which allows us to return the root value in constant time
|
||||
// (i.e. without iterating backwards through the stack).
|
||||
FPp.getRootValue = function getRootValue() {
|
||||
var s = this.stack;
|
||||
if (s.length % 2 === 0) {
|
||||
return s[1];
|
||||
}
|
||||
return s[0];
|
||||
};
|
||||
|
||||
// Temporarily push properties named by string arguments given after the
|
||||
// callback function onto this.stack, then call the callback with a
|
||||
// reference to this (modified) FastPath object. Note that the stack will
|
||||
// be restored to its original state after the callback is finished, so it
|
||||
// is probably a mistake to retain a reference to the path.
|
||||
FPp.call = function call(callback/*, name1, name2, ... */) {
|
||||
var s = this.stack;
|
||||
var origLen = s.length;
|
||||
var value = s[origLen - 1];
|
||||
var argc = arguments.length;
|
||||
for (var i = 1; i < argc; ++i) {
|
||||
var name = arguments[i];
|
||||
value = value[name];
|
||||
s.push(name, value);
|
||||
}
|
||||
var result = callback(this);
|
||||
s.length = origLen;
|
||||
return result;
|
||||
};
|
||||
|
||||
// Similar to FastPath.prototype.call, except that the value obtained by
|
||||
// accessing this.getValue()[name1][name2]... should be array-like. The
|
||||
// callback will be called with a reference to this path object for each
|
||||
// element of the array.
|
||||
FPp.each = function each(callback/*, name1, name2, ... */) {
|
||||
var s = this.stack;
|
||||
var origLen = s.length;
|
||||
var value = s[origLen - 1];
|
||||
var argc = arguments.length;
|
||||
|
||||
for (var i = 1; i < argc; ++i) {
|
||||
var name = arguments[i];
|
||||
value = value[name];
|
||||
s.push(name, value);
|
||||
}
|
||||
|
||||
for (var i = 0; i < value.length; ++i) {
|
||||
if (i in value) {
|
||||
s.push(i, value[i]);
|
||||
// If the callback needs to know the value of i, call
|
||||
// path.getName(), assuming path is the parameter name.
|
||||
callback(this);
|
||||
s.length -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
s.length = origLen;
|
||||
};
|
||||
|
||||
// Similar to FastPath.prototype.each, except that the results of the
|
||||
// callback function invocations are stored in an array and returned at
|
||||
// the end of the iteration.
|
||||
FPp.map = function map(callback/*, name1, name2, ... */) {
|
||||
var s = this.stack;
|
||||
var origLen = s.length;
|
||||
var value = s[origLen - 1];
|
||||
var argc = arguments.length;
|
||||
|
||||
for (var i = 1; i < argc; ++i) {
|
||||
var name = arguments[i];
|
||||
value = value[name];
|
||||
s.push(name, value);
|
||||
}
|
||||
|
||||
var result = new Array(value.length);
|
||||
|
||||
for (var i = 0; i < value.length; ++i) {
|
||||
if (i in value) {
|
||||
s.push(i, value[i]);
|
||||
result[i] = callback(this, i);
|
||||
s.length -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
s.length = origLen;
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// Inspired by require("ast-types").NodePath.prototype.needsParens, but
|
||||
// more efficient because we're iterating backwards through a stack.
|
||||
FPp.needsParens = function(assumeExpressionContext) {
|
||||
var parent = this.getParentNode();
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var name = this.getName();
|
||||
var node = this.getNode();
|
||||
|
||||
// If the value of this path is some child of a Node and not a Node
|
||||
// itself, then it doesn't need parentheses. Only Node objects (in
|
||||
// fact, only Expression nodes) need parentheses.
|
||||
if (this.getValue() !== node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only statements don't need parentheses.
|
||||
if (n.Statement.check(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Identifiers never need parentheses.
|
||||
if (node.type === "Identifier") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parent.type === "ParenthesizedExpression") {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case "UnaryExpression":
|
||||
case "SpreadElement":
|
||||
case "SpreadProperty":
|
||||
return parent.type === "MemberExpression"
|
||||
&& name === "object"
|
||||
&& parent.object === node;
|
||||
|
||||
case "BinaryExpression":
|
||||
case "LogicalExpression":
|
||||
switch (parent.type) {
|
||||
case "CallExpression":
|
||||
return name === "callee"
|
||||
&& parent.callee === node;
|
||||
|
||||
case "UnaryExpression":
|
||||
case "SpreadElement":
|
||||
case "SpreadProperty":
|
||||
return true;
|
||||
|
||||
case "MemberExpression":
|
||||
return name === "object"
|
||||
&& parent.object === node;
|
||||
|
||||
case "BinaryExpression":
|
||||
case "LogicalExpression":
|
||||
var po = parent.operator;
|
||||
var pp = PRECEDENCE[po];
|
||||
var no = node.operator;
|
||||
var np = PRECEDENCE[no];
|
||||
|
||||
if (pp > np) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (pp === np && name === "right") {
|
||||
assert.strictEqual(parent.right, node);
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
case "SequenceExpression":
|
||||
switch (parent.type) {
|
||||
case "ReturnStatement":
|
||||
return false;
|
||||
|
||||
case "ForStatement":
|
||||
// Although parentheses wouldn't hurt around sequence
|
||||
// expressions in the head of for loops, traditional style
|
||||
// dictates that e.g. i++, j++ should not be wrapped with
|
||||
// parentheses.
|
||||
return false;
|
||||
|
||||
case "ExpressionStatement":
|
||||
return name !== "expression";
|
||||
|
||||
default:
|
||||
// Otherwise err on the side of overparenthesization, adding
|
||||
// explicit exceptions above if this proves overzealous.
|
||||
return true;
|
||||
}
|
||||
|
||||
case "YieldExpression":
|
||||
switch (parent.type) {
|
||||
case "BinaryExpression":
|
||||
case "LogicalExpression":
|
||||
case "UnaryExpression":
|
||||
case "SpreadElement":
|
||||
case "SpreadProperty":
|
||||
case "CallExpression":
|
||||
case "MemberExpression":
|
||||
case "NewExpression":
|
||||
case "ConditionalExpression":
|
||||
case "YieldExpression":
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
case "IntersectionTypeAnnotation":
|
||||
case "UnionTypeAnnotation":
|
||||
return parent.type === "NullableTypeAnnotation";
|
||||
|
||||
case "Literal":
|
||||
return parent.type === "MemberExpression"
|
||||
&& isNumber.check(node.value)
|
||||
&& name === "object"
|
||||
&& parent.object === node;
|
||||
|
||||
case "AssignmentExpression":
|
||||
case "ConditionalExpression":
|
||||
switch (parent.type) {
|
||||
case "UnaryExpression":
|
||||
case "SpreadElement":
|
||||
case "SpreadProperty":
|
||||
case "BinaryExpression":
|
||||
case "LogicalExpression":
|
||||
return true;
|
||||
|
||||
case "CallExpression":
|
||||
return name === "callee"
|
||||
&& parent.callee === node;
|
||||
|
||||
case "ConditionalExpression":
|
||||
return name === "test"
|
||||
&& parent.test === node;
|
||||
|
||||
case "MemberExpression":
|
||||
return name === "object"
|
||||
&& parent.object === node;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
case "ArrowFunctionExpression":
|
||||
if(n.CallExpression.check(parent) && name === 'callee') {
|
||||
return true;
|
||||
}
|
||||
if(n.MemberExpression.check(parent) && name === 'object') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isBinary(parent);
|
||||
|
||||
case "ObjectExpression":
|
||||
if (parent.type === "ArrowFunctionExpression" &&
|
||||
name === "body") {
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
if (parent.type === "NewExpression" &&
|
||||
name === "callee" &&
|
||||
parent.callee === node) {
|
||||
return containsCallExpression(node);
|
||||
}
|
||||
}
|
||||
|
||||
if (assumeExpressionContext !== true &&
|
||||
!this.canBeFirstInStatement() &&
|
||||
this.firstInStatement())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
function isBinary(node) {
|
||||
return n.BinaryExpression.check(node)
|
||||
|| n.LogicalExpression.check(node);
|
||||
}
|
||||
|
||||
function isUnaryLike(node) {
|
||||
return n.UnaryExpression.check(node)
|
||||
// I considered making SpreadElement and SpreadProperty subtypes
|
||||
// of UnaryExpression, but they're not really Expression nodes.
|
||||
|| (n.SpreadElement && n.SpreadElement.check(node))
|
||||
|| (n.SpreadProperty && n.SpreadProperty.check(node));
|
||||
}
|
||||
|
||||
var PRECEDENCE = {};
|
||||
[["||"],
|
||||
["&&"],
|
||||
["|"],
|
||||
["^"],
|
||||
["&"],
|
||||
["==", "===", "!=", "!=="],
|
||||
["<", ">", "<=", ">=", "in", "instanceof"],
|
||||
[">>", "<<", ">>>"],
|
||||
["+", "-"],
|
||||
["*", "/", "%", "**"]
|
||||
].forEach(function(tier, i) {
|
||||
tier.forEach(function(op) {
|
||||
PRECEDENCE[op] = i;
|
||||
});
|
||||
});
|
||||
|
||||
function containsCallExpression(node) {
|
||||
if (n.CallExpression.check(node)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isArray.check(node)) {
|
||||
return node.some(containsCallExpression);
|
||||
}
|
||||
|
||||
if (n.Node.check(node)) {
|
||||
return types.someField(node, function(name, child) {
|
||||
return containsCallExpression(child);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
FPp.canBeFirstInStatement = function() {
|
||||
var node = this.getNode();
|
||||
return !n.FunctionExpression.check(node)
|
||||
&& !n.ObjectExpression.check(node);
|
||||
};
|
||||
|
||||
FPp.firstInStatement = function() {
|
||||
var s = this.stack;
|
||||
var parentName, parent;
|
||||
var childName, child;
|
||||
|
||||
for (var i = s.length - 1; i >= 0; i -= 2) {
|
||||
if (n.Node.check(s[i])) {
|
||||
childName = parentName;
|
||||
child = parent;
|
||||
parentName = s[i - 1];
|
||||
parent = s[i];
|
||||
}
|
||||
|
||||
if (!parent || !child) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n.BlockStatement.check(parent) &&
|
||||
parentName === "body" &&
|
||||
childName === 0) {
|
||||
assert.strictEqual(parent.body[0], child);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (n.ExpressionStatement.check(parent) &&
|
||||
childName === "expression") {
|
||||
assert.strictEqual(parent.expression, child);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (n.SequenceExpression.check(parent) &&
|
||||
parentName === "expressions" &&
|
||||
childName === 0) {
|
||||
assert.strictEqual(parent.expressions[0], child);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n.CallExpression.check(parent) &&
|
||||
childName === "callee") {
|
||||
assert.strictEqual(parent.callee, child);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n.MemberExpression.check(parent) &&
|
||||
childName === "object") {
|
||||
assert.strictEqual(parent.object, child);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n.ConditionalExpression.check(parent) &&
|
||||
childName === "test") {
|
||||
assert.strictEqual(parent.test, child);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isBinary(parent) &&
|
||||
childName === "left") {
|
||||
assert.strictEqual(parent.left, child);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (n.UnaryExpression.check(parent) &&
|
||||
!parent.prefix &&
|
||||
childName === "argument") {
|
||||
assert.strictEqual(parent.argument, child);
|
||||
continue;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
895
node_modules/recast/lib/lines.js
generated
vendored
Normal file
895
node_modules/recast/lib/lines.js
generated
vendored
Normal file
@@ -0,0 +1,895 @@
|
||||
var assert = require("assert");
|
||||
var sourceMap = require("source-map");
|
||||
var normalizeOptions = require("./options").normalize;
|
||||
var secretKey = require("private").makeUniqueKey();
|
||||
var types = require("./types");
|
||||
var isString = types.builtInTypes.string;
|
||||
var comparePos = require("./util").comparePos;
|
||||
var Mapping = require("./mapping");
|
||||
|
||||
// Goals:
|
||||
// 1. Minimize new string creation.
|
||||
// 2. Keep (de)identation O(lines) time.
|
||||
// 3. Permit negative indentations.
|
||||
// 4. Enforce immutability.
|
||||
// 5. No newline characters.
|
||||
|
||||
function getSecret(lines) {
|
||||
return lines[secretKey];
|
||||
}
|
||||
|
||||
function Lines(infos, sourceFileName) {
|
||||
assert.ok(this instanceof Lines);
|
||||
assert.ok(infos.length > 0);
|
||||
|
||||
if (sourceFileName) {
|
||||
isString.assert(sourceFileName);
|
||||
} else {
|
||||
sourceFileName = null;
|
||||
}
|
||||
|
||||
Object.defineProperty(this, secretKey, {
|
||||
value: {
|
||||
infos: infos,
|
||||
mappings: [],
|
||||
name: sourceFileName,
|
||||
cachedSourceMap: null
|
||||
}
|
||||
});
|
||||
|
||||
if (sourceFileName) {
|
||||
getSecret(this).mappings.push(new Mapping(this, {
|
||||
start: this.firstPos(),
|
||||
end: this.lastPos()
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Exposed for instanceof checks. The fromString function should be used
|
||||
// to create new Lines objects.
|
||||
exports.Lines = Lines;
|
||||
var Lp = Lines.prototype;
|
||||
|
||||
// These properties used to be assigned to each new object in the Lines
|
||||
// constructor, but we can more efficiently stuff them into the secret and
|
||||
// let these lazy accessors compute their values on-the-fly.
|
||||
Object.defineProperties(Lp, {
|
||||
length: {
|
||||
get: function() {
|
||||
return getSecret(this).infos.length;
|
||||
}
|
||||
},
|
||||
|
||||
name: {
|
||||
get: function() {
|
||||
return getSecret(this).name;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function copyLineInfo(info) {
|
||||
return {
|
||||
line: info.line,
|
||||
indent: info.indent,
|
||||
locked: info.locked,
|
||||
sliceStart: info.sliceStart,
|
||||
sliceEnd: info.sliceEnd
|
||||
};
|
||||
}
|
||||
|
||||
var fromStringCache = {};
|
||||
var hasOwn = fromStringCache.hasOwnProperty;
|
||||
var maxCacheKeyLen = 10;
|
||||
|
||||
function countSpaces(spaces, tabWidth) {
|
||||
var count = 0;
|
||||
var len = spaces.length;
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
switch (spaces.charCodeAt(i)) {
|
||||
case 9: // '\t'
|
||||
assert.strictEqual(typeof tabWidth, "number");
|
||||
assert.ok(tabWidth > 0);
|
||||
|
||||
var next = Math.ceil(count / tabWidth) * tabWidth;
|
||||
if (next === count) {
|
||||
count += tabWidth;
|
||||
} else {
|
||||
count = next;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 11: // '\v'
|
||||
case 12: // '\f'
|
||||
case 13: // '\r'
|
||||
case 0xfeff: // zero-width non-breaking space
|
||||
// These characters contribute nothing to indentation.
|
||||
break;
|
||||
|
||||
case 32: // ' '
|
||||
default: // Treat all other whitespace like ' '.
|
||||
count += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
exports.countSpaces = countSpaces;
|
||||
|
||||
var leadingSpaceExp = /^\s*/;
|
||||
|
||||
// As specified here: http://www.ecma-international.org/ecma-262/6.0/#sec-line-terminators
|
||||
var lineTerminatorSeqExp =
|
||||
/\u000D\u000A|\u000D(?!\u000A)|\u000A|\u2028|\u2029/;
|
||||
|
||||
/**
|
||||
* @param {Object} options - Options object that configures printing.
|
||||
*/
|
||||
function fromString(string, options) {
|
||||
if (string instanceof Lines)
|
||||
return string;
|
||||
|
||||
string += "";
|
||||
|
||||
var tabWidth = options && options.tabWidth;
|
||||
var tabless = string.indexOf("\t") < 0;
|
||||
var locked = !! (options && options.locked);
|
||||
var cacheable = !options && tabless && (string.length <= maxCacheKeyLen);
|
||||
|
||||
assert.ok(tabWidth || tabless, "No tab width specified but encountered tabs in string\n" + string);
|
||||
|
||||
if (cacheable && hasOwn.call(fromStringCache, string))
|
||||
return fromStringCache[string];
|
||||
|
||||
var lines = new Lines(string.split(lineTerminatorSeqExp).map(function(line) {
|
||||
var spaces = leadingSpaceExp.exec(line)[0];
|
||||
return {
|
||||
line: line,
|
||||
indent: countSpaces(spaces, tabWidth),
|
||||
// Boolean indicating whether this line can be reindented.
|
||||
locked: locked,
|
||||
sliceStart: spaces.length,
|
||||
sliceEnd: line.length
|
||||
};
|
||||
}), normalizeOptions(options).sourceFileName);
|
||||
|
||||
if (cacheable)
|
||||
fromStringCache[string] = lines;
|
||||
|
||||
return lines;
|
||||
}
|
||||
exports.fromString = fromString;
|
||||
|
||||
function isOnlyWhitespace(string) {
|
||||
return !/\S/.test(string);
|
||||
}
|
||||
|
||||
Lp.toString = function(options) {
|
||||
return this.sliceString(this.firstPos(), this.lastPos(), options);
|
||||
};
|
||||
|
||||
Lp.getSourceMap = function(sourceMapName, sourceRoot) {
|
||||
if (!sourceMapName) {
|
||||
// Although we could make up a name or generate an anonymous
|
||||
// source map, instead we assume that any consumer who does not
|
||||
// provide a name does not actually want a source map.
|
||||
return null;
|
||||
}
|
||||
|
||||
var targetLines = this;
|
||||
|
||||
function updateJSON(json) {
|
||||
json = json || {};
|
||||
|
||||
isString.assert(sourceMapName);
|
||||
json.file = sourceMapName;
|
||||
|
||||
if (sourceRoot) {
|
||||
isString.assert(sourceRoot);
|
||||
json.sourceRoot = sourceRoot;
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
var secret = getSecret(targetLines);
|
||||
if (secret.cachedSourceMap) {
|
||||
// Since Lines objects are immutable, we can reuse any source map
|
||||
// that was previously generated. Nevertheless, we return a new
|
||||
// JSON object here to protect the cached source map from outside
|
||||
// modification.
|
||||
return updateJSON(secret.cachedSourceMap.toJSON());
|
||||
}
|
||||
|
||||
var smg = new sourceMap.SourceMapGenerator(updateJSON());
|
||||
var sourcesToContents = {};
|
||||
|
||||
secret.mappings.forEach(function(mapping) {
|
||||
var sourceCursor = mapping.sourceLines.skipSpaces(
|
||||
mapping.sourceLoc.start
|
||||
) || mapping.sourceLines.lastPos();
|
||||
|
||||
var targetCursor = targetLines.skipSpaces(
|
||||
mapping.targetLoc.start
|
||||
) || targetLines.lastPos();
|
||||
|
||||
while (comparePos(sourceCursor, mapping.sourceLoc.end) < 0 &&
|
||||
comparePos(targetCursor, mapping.targetLoc.end) < 0) {
|
||||
|
||||
var sourceChar = mapping.sourceLines.charAt(sourceCursor);
|
||||
var targetChar = targetLines.charAt(targetCursor);
|
||||
assert.strictEqual(sourceChar, targetChar);
|
||||
|
||||
var sourceName = mapping.sourceLines.name;
|
||||
|
||||
// Add mappings one character at a time for maximum resolution.
|
||||
smg.addMapping({
|
||||
source: sourceName,
|
||||
original: { line: sourceCursor.line,
|
||||
column: sourceCursor.column },
|
||||
generated: { line: targetCursor.line,
|
||||
column: targetCursor.column }
|
||||
});
|
||||
|
||||
if (!hasOwn.call(sourcesToContents, sourceName)) {
|
||||
var sourceContent = mapping.sourceLines.toString();
|
||||
smg.setSourceContent(sourceName, sourceContent);
|
||||
sourcesToContents[sourceName] = sourceContent;
|
||||
}
|
||||
|
||||
targetLines.nextPos(targetCursor, true);
|
||||
mapping.sourceLines.nextPos(sourceCursor, true);
|
||||
}
|
||||
});
|
||||
|
||||
secret.cachedSourceMap = smg;
|
||||
|
||||
return smg.toJSON();
|
||||
};
|
||||
|
||||
Lp.bootstrapCharAt = function(pos) {
|
||||
assert.strictEqual(typeof pos, "object");
|
||||
assert.strictEqual(typeof pos.line, "number");
|
||||
assert.strictEqual(typeof pos.column, "number");
|
||||
|
||||
var line = pos.line,
|
||||
column = pos.column,
|
||||
strings = this.toString().split(lineTerminatorSeqExp),
|
||||
string = strings[line - 1];
|
||||
|
||||
if (typeof string === "undefined")
|
||||
return "";
|
||||
|
||||
if (column === string.length &&
|
||||
line < strings.length)
|
||||
return "\n";
|
||||
|
||||
if (column >= string.length)
|
||||
return "";
|
||||
|
||||
return string.charAt(column);
|
||||
};
|
||||
|
||||
Lp.charAt = function(pos) {
|
||||
assert.strictEqual(typeof pos, "object");
|
||||
assert.strictEqual(typeof pos.line, "number");
|
||||
assert.strictEqual(typeof pos.column, "number");
|
||||
|
||||
var line = pos.line,
|
||||
column = pos.column,
|
||||
secret = getSecret(this),
|
||||
infos = secret.infos,
|
||||
info = infos[line - 1],
|
||||
c = column;
|
||||
|
||||
if (typeof info === "undefined" || c < 0)
|
||||
return "";
|
||||
|
||||
var indent = this.getIndentAt(line);
|
||||
if (c < indent)
|
||||
return " ";
|
||||
|
||||
c += info.sliceStart - indent;
|
||||
|
||||
if (c === info.sliceEnd &&
|
||||
line < this.length)
|
||||
return "\n";
|
||||
|
||||
if (c >= info.sliceEnd)
|
||||
return "";
|
||||
|
||||
return info.line.charAt(c);
|
||||
};
|
||||
|
||||
Lp.stripMargin = function(width, skipFirstLine) {
|
||||
if (width === 0)
|
||||
return this;
|
||||
|
||||
assert.ok(width > 0, "negative margin: " + width);
|
||||
|
||||
if (skipFirstLine && this.length === 1)
|
||||
return this;
|
||||
|
||||
var secret = getSecret(this);
|
||||
|
||||
var lines = new Lines(secret.infos.map(function(info, i) {
|
||||
if (info.line && (i > 0 || !skipFirstLine)) {
|
||||
info = copyLineInfo(info);
|
||||
info.indent = Math.max(0, info.indent - width);
|
||||
}
|
||||
return info;
|
||||
}));
|
||||
|
||||
if (secret.mappings.length > 0) {
|
||||
var newMappings = getSecret(lines).mappings;
|
||||
assert.strictEqual(newMappings.length, 0);
|
||||
secret.mappings.forEach(function(mapping) {
|
||||
newMappings.push(mapping.indent(width, skipFirstLine, true));
|
||||
});
|
||||
}
|
||||
|
||||
return lines;
|
||||
};
|
||||
|
||||
Lp.indent = function(by) {
|
||||
if (by === 0)
|
||||
return this;
|
||||
|
||||
var secret = getSecret(this);
|
||||
|
||||
var lines = new Lines(secret.infos.map(function(info) {
|
||||
if (info.line && ! info.locked) {
|
||||
info = copyLineInfo(info);
|
||||
info.indent += by;
|
||||
}
|
||||
return info
|
||||
}));
|
||||
|
||||
if (secret.mappings.length > 0) {
|
||||
var newMappings = getSecret(lines).mappings;
|
||||
assert.strictEqual(newMappings.length, 0);
|
||||
secret.mappings.forEach(function(mapping) {
|
||||
newMappings.push(mapping.indent(by));
|
||||
});
|
||||
}
|
||||
|
||||
return lines;
|
||||
};
|
||||
|
||||
Lp.indentTail = function(by) {
|
||||
if (by === 0)
|
||||
return this;
|
||||
|
||||
if (this.length < 2)
|
||||
return this;
|
||||
|
||||
var secret = getSecret(this);
|
||||
|
||||
var lines = new Lines(secret.infos.map(function(info, i) {
|
||||
if (i > 0 && info.line && ! info.locked) {
|
||||
info = copyLineInfo(info);
|
||||
info.indent += by;
|
||||
}
|
||||
|
||||
return info;
|
||||
}));
|
||||
|
||||
if (secret.mappings.length > 0) {
|
||||
var newMappings = getSecret(lines).mappings;
|
||||
assert.strictEqual(newMappings.length, 0);
|
||||
secret.mappings.forEach(function(mapping) {
|
||||
newMappings.push(mapping.indent(by, true));
|
||||
});
|
||||
}
|
||||
|
||||
return lines;
|
||||
};
|
||||
|
||||
Lp.lockIndentTail = function () {
|
||||
if (this.length < 2) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var infos = getSecret(this).infos;
|
||||
|
||||
return new Lines(infos.map(function (info, i) {
|
||||
info = copyLineInfo(info);
|
||||
info.locked = i > 0;
|
||||
return info;
|
||||
}));
|
||||
};
|
||||
|
||||
Lp.getIndentAt = function(line) {
|
||||
assert.ok(line >= 1, "no line " + line + " (line numbers start from 1)");
|
||||
var secret = getSecret(this),
|
||||
info = secret.infos[line - 1];
|
||||
return Math.max(info.indent, 0);
|
||||
};
|
||||
|
||||
Lp.guessTabWidth = function() {
|
||||
var secret = getSecret(this);
|
||||
if (hasOwn.call(secret, "cachedTabWidth")) {
|
||||
return secret.cachedTabWidth;
|
||||
}
|
||||
|
||||
var counts = []; // Sparse array.
|
||||
var lastIndent = 0;
|
||||
|
||||
for (var line = 1, last = this.length; line <= last; ++line) {
|
||||
var info = secret.infos[line - 1];
|
||||
var sliced = info.line.slice(info.sliceStart, info.sliceEnd);
|
||||
|
||||
// Whitespace-only lines don't tell us much about the likely tab
|
||||
// width of this code.
|
||||
if (isOnlyWhitespace(sliced)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var diff = Math.abs(info.indent - lastIndent);
|
||||
counts[diff] = ~~counts[diff] + 1;
|
||||
lastIndent = info.indent;
|
||||
}
|
||||
|
||||
var maxCount = -1;
|
||||
var result = 2;
|
||||
|
||||
for (var tabWidth = 1;
|
||||
tabWidth < counts.length;
|
||||
tabWidth += 1) {
|
||||
if (hasOwn.call(counts, tabWidth) &&
|
||||
counts[tabWidth] > maxCount) {
|
||||
maxCount = counts[tabWidth];
|
||||
result = tabWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return secret.cachedTabWidth = result;
|
||||
};
|
||||
|
||||
// Determine if the list of lines has a first line that starts with a //
|
||||
// or /* comment. If this is the case, the code may need to be wrapped in
|
||||
// parens to avoid ASI issues.
|
||||
Lp.startsWithComment = function () {
|
||||
var secret = getSecret(this);
|
||||
if (secret.infos.length === 0) {
|
||||
return false;
|
||||
}
|
||||
var firstLineInfo = secret.infos[0],
|
||||
sliceStart = firstLineInfo.sliceStart,
|
||||
sliceEnd = firstLineInfo.sliceEnd,
|
||||
firstLine = firstLineInfo.line.slice(sliceStart, sliceEnd).trim();
|
||||
return firstLine.length === 0 ||
|
||||
firstLine.slice(0, 2) === "//" ||
|
||||
firstLine.slice(0, 2) === "/*";
|
||||
};
|
||||
|
||||
Lp.isOnlyWhitespace = function() {
|
||||
return isOnlyWhitespace(this.toString());
|
||||
};
|
||||
|
||||
Lp.isPrecededOnlyByWhitespace = function(pos) {
|
||||
var secret = getSecret(this);
|
||||
var info = secret.infos[pos.line - 1];
|
||||
var indent = Math.max(info.indent, 0);
|
||||
|
||||
var diff = pos.column - indent;
|
||||
if (diff <= 0) {
|
||||
// If pos.column does not exceed the indentation amount, then
|
||||
// there must be only whitespace before it.
|
||||
return true;
|
||||
}
|
||||
|
||||
var start = info.sliceStart;
|
||||
var end = Math.min(start + diff, info.sliceEnd);
|
||||
var prefix = info.line.slice(start, end);
|
||||
|
||||
return isOnlyWhitespace(prefix);
|
||||
};
|
||||
|
||||
Lp.getLineLength = function(line) {
|
||||
var secret = getSecret(this),
|
||||
info = secret.infos[line - 1];
|
||||
return this.getIndentAt(line) + info.sliceEnd - info.sliceStart;
|
||||
};
|
||||
|
||||
Lp.nextPos = function(pos, skipSpaces) {
|
||||
var l = Math.max(pos.line, 0),
|
||||
c = Math.max(pos.column, 0);
|
||||
|
||||
if (c < this.getLineLength(l)) {
|
||||
pos.column += 1;
|
||||
|
||||
return skipSpaces
|
||||
? !!this.skipSpaces(pos, false, true)
|
||||
: true;
|
||||
}
|
||||
|
||||
if (l < this.length) {
|
||||
pos.line += 1;
|
||||
pos.column = 0;
|
||||
|
||||
return skipSpaces
|
||||
? !!this.skipSpaces(pos, false, true)
|
||||
: true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Lp.prevPos = function(pos, skipSpaces) {
|
||||
var l = pos.line,
|
||||
c = pos.column;
|
||||
|
||||
if (c < 1) {
|
||||
l -= 1;
|
||||
|
||||
if (l < 1)
|
||||
return false;
|
||||
|
||||
c = this.getLineLength(l);
|
||||
|
||||
} else {
|
||||
c = Math.min(c - 1, this.getLineLength(l));
|
||||
}
|
||||
|
||||
pos.line = l;
|
||||
pos.column = c;
|
||||
|
||||
return skipSpaces
|
||||
? !!this.skipSpaces(pos, true, true)
|
||||
: true;
|
||||
};
|
||||
|
||||
Lp.firstPos = function() {
|
||||
// Trivial, but provided for completeness.
|
||||
return { line: 1, column: 0 };
|
||||
};
|
||||
|
||||
Lp.lastPos = function() {
|
||||
return {
|
||||
line: this.length,
|
||||
column: this.getLineLength(this.length)
|
||||
};
|
||||
};
|
||||
|
||||
Lp.skipSpaces = function(pos, backward, modifyInPlace) {
|
||||
if (pos) {
|
||||
pos = modifyInPlace ? pos : {
|
||||
line: pos.line,
|
||||
column: pos.column
|
||||
};
|
||||
} else if (backward) {
|
||||
pos = this.lastPos();
|
||||
} else {
|
||||
pos = this.firstPos();
|
||||
}
|
||||
|
||||
if (backward) {
|
||||
while (this.prevPos(pos)) {
|
||||
if (!isOnlyWhitespace(this.charAt(pos)) &&
|
||||
this.nextPos(pos)) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
} else {
|
||||
while (isOnlyWhitespace(this.charAt(pos))) {
|
||||
if (!this.nextPos(pos)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
};
|
||||
|
||||
Lp.trimLeft = function() {
|
||||
var pos = this.skipSpaces(this.firstPos(), false, true);
|
||||
return pos ? this.slice(pos) : emptyLines;
|
||||
};
|
||||
|
||||
Lp.trimRight = function() {
|
||||
var pos = this.skipSpaces(this.lastPos(), true, true);
|
||||
return pos ? this.slice(this.firstPos(), pos) : emptyLines;
|
||||
};
|
||||
|
||||
Lp.trim = function() {
|
||||
var start = this.skipSpaces(this.firstPos(), false, true);
|
||||
if (start === null)
|
||||
return emptyLines;
|
||||
|
||||
var end = this.skipSpaces(this.lastPos(), true, true);
|
||||
assert.notStrictEqual(end, null);
|
||||
|
||||
return this.slice(start, end);
|
||||
};
|
||||
|
||||
Lp.eachPos = function(callback, startPos, skipSpaces) {
|
||||
var pos = this.firstPos();
|
||||
|
||||
if (startPos) {
|
||||
pos.line = startPos.line,
|
||||
pos.column = startPos.column
|
||||
}
|
||||
|
||||
if (skipSpaces && !this.skipSpaces(pos, false, true)) {
|
||||
return; // Encountered nothing but spaces.
|
||||
}
|
||||
|
||||
do callback.call(this, pos);
|
||||
while (this.nextPos(pos, skipSpaces));
|
||||
};
|
||||
|
||||
Lp.bootstrapSlice = function(start, end) {
|
||||
var strings = this.toString().split(
|
||||
lineTerminatorSeqExp
|
||||
).slice(
|
||||
start.line - 1,
|
||||
end.line
|
||||
);
|
||||
|
||||
strings.push(strings.pop().slice(0, end.column));
|
||||
strings[0] = strings[0].slice(start.column);
|
||||
|
||||
return fromString(strings.join("\n"));
|
||||
};
|
||||
|
||||
Lp.slice = function(start, end) {
|
||||
if (!end) {
|
||||
if (!start) {
|
||||
// The client seems to want a copy of this Lines object, but
|
||||
// Lines objects are immutable, so it's perfectly adequate to
|
||||
// return the same object.
|
||||
return this;
|
||||
}
|
||||
|
||||
// Slice to the end if no end position was provided.
|
||||
end = this.lastPos();
|
||||
}
|
||||
|
||||
var secret = getSecret(this);
|
||||
var sliced = secret.infos.slice(start.line - 1, end.line);
|
||||
|
||||
if (start.line === end.line) {
|
||||
sliced[0] = sliceInfo(sliced[0], start.column, end.column);
|
||||
} else {
|
||||
assert.ok(start.line < end.line);
|
||||
sliced[0] = sliceInfo(sliced[0], start.column);
|
||||
sliced.push(sliceInfo(sliced.pop(), 0, end.column));
|
||||
}
|
||||
|
||||
var lines = new Lines(sliced);
|
||||
|
||||
if (secret.mappings.length > 0) {
|
||||
var newMappings = getSecret(lines).mappings;
|
||||
assert.strictEqual(newMappings.length, 0);
|
||||
secret.mappings.forEach(function(mapping) {
|
||||
var sliced = mapping.slice(this, start, end);
|
||||
if (sliced) {
|
||||
newMappings.push(sliced);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
|
||||
return lines;
|
||||
};
|
||||
|
||||
function sliceInfo(info, startCol, endCol) {
|
||||
var sliceStart = info.sliceStart;
|
||||
var sliceEnd = info.sliceEnd;
|
||||
var indent = Math.max(info.indent, 0);
|
||||
var lineLength = indent + sliceEnd - sliceStart;
|
||||
|
||||
if (typeof endCol === "undefined") {
|
||||
endCol = lineLength;
|
||||
}
|
||||
|
||||
startCol = Math.max(startCol, 0);
|
||||
endCol = Math.min(endCol, lineLength);
|
||||
endCol = Math.max(endCol, startCol);
|
||||
|
||||
if (endCol < indent) {
|
||||
indent = endCol;
|
||||
sliceEnd = sliceStart;
|
||||
} else {
|
||||
sliceEnd -= lineLength - endCol;
|
||||
}
|
||||
|
||||
lineLength = endCol;
|
||||
lineLength -= startCol;
|
||||
|
||||
if (startCol < indent) {
|
||||
indent -= startCol;
|
||||
} else {
|
||||
startCol -= indent;
|
||||
indent = 0;
|
||||
sliceStart += startCol;
|
||||
}
|
||||
|
||||
assert.ok(indent >= 0);
|
||||
assert.ok(sliceStart <= sliceEnd);
|
||||
assert.strictEqual(lineLength, indent + sliceEnd - sliceStart);
|
||||
|
||||
if (info.indent === indent &&
|
||||
info.sliceStart === sliceStart &&
|
||||
info.sliceEnd === sliceEnd) {
|
||||
return info;
|
||||
}
|
||||
|
||||
return {
|
||||
line: info.line,
|
||||
indent: indent,
|
||||
// A destructive slice always unlocks indentation.
|
||||
locked: false,
|
||||
sliceStart: sliceStart,
|
||||
sliceEnd: sliceEnd
|
||||
};
|
||||
}
|
||||
|
||||
Lp.bootstrapSliceString = function(start, end, options) {
|
||||
return this.slice(start, end).toString(options);
|
||||
};
|
||||
|
||||
Lp.sliceString = function(start, end, options) {
|
||||
if (!end) {
|
||||
if (!start) {
|
||||
// The client seems to want a copy of this Lines object, but
|
||||
// Lines objects are immutable, so it's perfectly adequate to
|
||||
// return the same object.
|
||||
return this;
|
||||
}
|
||||
|
||||
// Slice to the end if no end position was provided.
|
||||
end = this.lastPos();
|
||||
}
|
||||
|
||||
options = normalizeOptions(options);
|
||||
|
||||
var infos = getSecret(this).infos;
|
||||
var parts = [];
|
||||
var tabWidth = options.tabWidth;
|
||||
|
||||
for (var line = start.line; line <= end.line; ++line) {
|
||||
var info = infos[line - 1];
|
||||
|
||||
if (line === start.line) {
|
||||
if (line === end.line) {
|
||||
info = sliceInfo(info, start.column, end.column);
|
||||
} else {
|
||||
info = sliceInfo(info, start.column);
|
||||
}
|
||||
} else if (line === end.line) {
|
||||
info = sliceInfo(info, 0, end.column);
|
||||
}
|
||||
|
||||
var indent = Math.max(info.indent, 0);
|
||||
|
||||
var before = info.line.slice(0, info.sliceStart);
|
||||
if (options.reuseWhitespace &&
|
||||
isOnlyWhitespace(before) &&
|
||||
countSpaces(before, options.tabWidth) === indent) {
|
||||
// Reuse original spaces if the indentation is correct.
|
||||
parts.push(info.line.slice(0, info.sliceEnd));
|
||||
continue;
|
||||
}
|
||||
|
||||
var tabs = 0;
|
||||
var spaces = indent;
|
||||
|
||||
if (options.useTabs) {
|
||||
tabs = Math.floor(indent / tabWidth);
|
||||
spaces -= tabs * tabWidth;
|
||||
}
|
||||
|
||||
var result = "";
|
||||
|
||||
if (tabs > 0) {
|
||||
result += new Array(tabs + 1).join("\t");
|
||||
}
|
||||
|
||||
if (spaces > 0) {
|
||||
result += new Array(spaces + 1).join(" ");
|
||||
}
|
||||
|
||||
result += info.line.slice(info.sliceStart, info.sliceEnd);
|
||||
|
||||
parts.push(result);
|
||||
}
|
||||
|
||||
return parts.join(options.lineTerminator);
|
||||
};
|
||||
|
||||
Lp.isEmpty = function() {
|
||||
return this.length < 2 && this.getLineLength(1) < 1;
|
||||
};
|
||||
|
||||
Lp.join = function(elements) {
|
||||
var separator = this;
|
||||
var separatorSecret = getSecret(separator);
|
||||
var infos = [];
|
||||
var mappings = [];
|
||||
var prevInfo;
|
||||
|
||||
function appendSecret(secret) {
|
||||
if (secret === null)
|
||||
return;
|
||||
|
||||
if (prevInfo) {
|
||||
var info = secret.infos[0];
|
||||
var indent = new Array(info.indent + 1).join(" ");
|
||||
var prevLine = infos.length;
|
||||
var prevColumn = Math.max(prevInfo.indent, 0) +
|
||||
prevInfo.sliceEnd - prevInfo.sliceStart;
|
||||
|
||||
prevInfo.line = prevInfo.line.slice(
|
||||
0, prevInfo.sliceEnd) + indent + info.line.slice(
|
||||
info.sliceStart, info.sliceEnd);
|
||||
|
||||
// If any part of a line is indentation-locked, the whole line
|
||||
// will be indentation-locked.
|
||||
prevInfo.locked = prevInfo.locked || info.locked;
|
||||
|
||||
prevInfo.sliceEnd = prevInfo.line.length;
|
||||
|
||||
if (secret.mappings.length > 0) {
|
||||
secret.mappings.forEach(function(mapping) {
|
||||
mappings.push(mapping.add(prevLine, prevColumn));
|
||||
});
|
||||
}
|
||||
|
||||
} else if (secret.mappings.length > 0) {
|
||||
mappings.push.apply(mappings, secret.mappings);
|
||||
}
|
||||
|
||||
secret.infos.forEach(function(info, i) {
|
||||
if (!prevInfo || i > 0) {
|
||||
prevInfo = copyLineInfo(info);
|
||||
infos.push(prevInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function appendWithSeparator(secret, i) {
|
||||
if (i > 0)
|
||||
appendSecret(separatorSecret);
|
||||
appendSecret(secret);
|
||||
}
|
||||
|
||||
elements.map(function(elem) {
|
||||
var lines = fromString(elem);
|
||||
if (lines.isEmpty())
|
||||
return null;
|
||||
return getSecret(lines);
|
||||
}).forEach(separator.isEmpty()
|
||||
? appendSecret
|
||||
: appendWithSeparator);
|
||||
|
||||
if (infos.length < 1)
|
||||
return emptyLines;
|
||||
|
||||
var lines = new Lines(infos);
|
||||
|
||||
getSecret(lines).mappings = mappings;
|
||||
|
||||
return lines;
|
||||
};
|
||||
|
||||
exports.concat = function(elements) {
|
||||
return emptyLines.join(elements);
|
||||
};
|
||||
|
||||
Lp.concat = function(other) {
|
||||
var args = arguments,
|
||||
list = [this];
|
||||
list.push.apply(list, args);
|
||||
assert.strictEqual(list.length, args.length + 1);
|
||||
return emptyLines.join(list);
|
||||
};
|
||||
|
||||
// The emptyLines object needs to be created all the way down here so that
|
||||
// Lines.prototype will be fully populated.
|
||||
var emptyLines = fromString("");
|
||||
277
node_modules/recast/lib/mapping.js
generated
vendored
Normal file
277
node_modules/recast/lib/mapping.js
generated
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
var assert = require("assert");
|
||||
var types = require("./types");
|
||||
var isString = types.builtInTypes.string;
|
||||
var isNumber = types.builtInTypes.number;
|
||||
var SourceLocation = types.namedTypes.SourceLocation;
|
||||
var Position = types.namedTypes.Position;
|
||||
var linesModule = require("./lines");
|
||||
var comparePos = require("./util").comparePos;
|
||||
|
||||
function Mapping(sourceLines, sourceLoc, targetLoc) {
|
||||
assert.ok(this instanceof Mapping);
|
||||
assert.ok(sourceLines instanceof linesModule.Lines);
|
||||
SourceLocation.assert(sourceLoc);
|
||||
|
||||
if (targetLoc) {
|
||||
// In certain cases it's possible for targetLoc.{start,end}.column
|
||||
// values to be negative, which technically makes them no longer
|
||||
// valid SourceLocation nodes, so we need to be more forgiving.
|
||||
assert.ok(
|
||||
isNumber.check(targetLoc.start.line) &&
|
||||
isNumber.check(targetLoc.start.column) &&
|
||||
isNumber.check(targetLoc.end.line) &&
|
||||
isNumber.check(targetLoc.end.column)
|
||||
);
|
||||
} else {
|
||||
// Assume identity mapping if no targetLoc specified.
|
||||
targetLoc = sourceLoc;
|
||||
}
|
||||
|
||||
Object.defineProperties(this, {
|
||||
sourceLines: { value: sourceLines },
|
||||
sourceLoc: { value: sourceLoc },
|
||||
targetLoc: { value: targetLoc }
|
||||
});
|
||||
}
|
||||
|
||||
var Mp = Mapping.prototype;
|
||||
module.exports = Mapping;
|
||||
|
||||
Mp.slice = function(lines, start, end) {
|
||||
assert.ok(lines instanceof linesModule.Lines);
|
||||
Position.assert(start);
|
||||
|
||||
if (end) {
|
||||
Position.assert(end);
|
||||
} else {
|
||||
end = lines.lastPos();
|
||||
}
|
||||
|
||||
var sourceLines = this.sourceLines;
|
||||
var sourceLoc = this.sourceLoc;
|
||||
var targetLoc = this.targetLoc;
|
||||
|
||||
function skip(name) {
|
||||
var sourceFromPos = sourceLoc[name];
|
||||
var targetFromPos = targetLoc[name];
|
||||
var targetToPos = start;
|
||||
|
||||
if (name === "end") {
|
||||
targetToPos = end;
|
||||
} else {
|
||||
assert.strictEqual(name, "start");
|
||||
}
|
||||
|
||||
return skipChars(
|
||||
sourceLines, sourceFromPos,
|
||||
lines, targetFromPos, targetToPos
|
||||
);
|
||||
}
|
||||
|
||||
if (comparePos(start, targetLoc.start) <= 0) {
|
||||
if (comparePos(targetLoc.end, end) <= 0) {
|
||||
targetLoc = {
|
||||
start: subtractPos(targetLoc.start, start.line, start.column),
|
||||
end: subtractPos(targetLoc.end, start.line, start.column)
|
||||
};
|
||||
|
||||
// The sourceLoc can stay the same because the contents of the
|
||||
// targetLoc have not changed.
|
||||
|
||||
} else if (comparePos(end, targetLoc.start) <= 0) {
|
||||
return null;
|
||||
|
||||
} else {
|
||||
sourceLoc = {
|
||||
start: sourceLoc.start,
|
||||
end: skip("end")
|
||||
};
|
||||
|
||||
targetLoc = {
|
||||
start: subtractPos(targetLoc.start, start.line, start.column),
|
||||
end: subtractPos(end, start.line, start.column)
|
||||
};
|
||||
}
|
||||
|
||||
} else {
|
||||
if (comparePos(targetLoc.end, start) <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (comparePos(targetLoc.end, end) <= 0) {
|
||||
sourceLoc = {
|
||||
start: skip("start"),
|
||||
end: sourceLoc.end
|
||||
};
|
||||
|
||||
targetLoc = {
|
||||
// Same as subtractPos(start, start.line, start.column):
|
||||
start: { line: 1, column: 0 },
|
||||
end: subtractPos(targetLoc.end, start.line, start.column)
|
||||
};
|
||||
|
||||
} else {
|
||||
sourceLoc = {
|
||||
start: skip("start"),
|
||||
end: skip("end")
|
||||
};
|
||||
|
||||
targetLoc = {
|
||||
// Same as subtractPos(start, start.line, start.column):
|
||||
start: { line: 1, column: 0 },
|
||||
end: subtractPos(end, start.line, start.column)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return new Mapping(this.sourceLines, sourceLoc, targetLoc);
|
||||
};
|
||||
|
||||
Mp.add = function(line, column) {
|
||||
return new Mapping(this.sourceLines, this.sourceLoc, {
|
||||
start: addPos(this.targetLoc.start, line, column),
|
||||
end: addPos(this.targetLoc.end, line, column)
|
||||
});
|
||||
};
|
||||
|
||||
function addPos(toPos, line, column) {
|
||||
return {
|
||||
line: toPos.line + line - 1,
|
||||
column: (toPos.line === 1)
|
||||
? toPos.column + column
|
||||
: toPos.column
|
||||
};
|
||||
}
|
||||
|
||||
Mp.subtract = function(line, column) {
|
||||
return new Mapping(this.sourceLines, this.sourceLoc, {
|
||||
start: subtractPos(this.targetLoc.start, line, column),
|
||||
end: subtractPos(this.targetLoc.end, line, column)
|
||||
});
|
||||
};
|
||||
|
||||
function subtractPos(fromPos, line, column) {
|
||||
return {
|
||||
line: fromPos.line - line + 1,
|
||||
column: (fromPos.line === line)
|
||||
? fromPos.column - column
|
||||
: fromPos.column
|
||||
};
|
||||
}
|
||||
|
||||
Mp.indent = function(by, skipFirstLine, noNegativeColumns) {
|
||||
if (by === 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
var targetLoc = this.targetLoc;
|
||||
var startLine = targetLoc.start.line;
|
||||
var endLine = targetLoc.end.line;
|
||||
|
||||
if (skipFirstLine && startLine === 1 && endLine === 1) {
|
||||
return this;
|
||||
}
|
||||
|
||||
targetLoc = {
|
||||
start: targetLoc.start,
|
||||
end: targetLoc.end
|
||||
};
|
||||
|
||||
if (!skipFirstLine || startLine > 1) {
|
||||
var startColumn = targetLoc.start.column + by;
|
||||
targetLoc.start = {
|
||||
line: startLine,
|
||||
column: noNegativeColumns
|
||||
? Math.max(0, startColumn)
|
||||
: startColumn
|
||||
};
|
||||
}
|
||||
|
||||
if (!skipFirstLine || endLine > 1) {
|
||||
var endColumn = targetLoc.end.column + by;
|
||||
targetLoc.end = {
|
||||
line: endLine,
|
||||
column: noNegativeColumns
|
||||
? Math.max(0, endColumn)
|
||||
: endColumn
|
||||
};
|
||||
}
|
||||
|
||||
return new Mapping(this.sourceLines, this.sourceLoc, targetLoc);
|
||||
};
|
||||
|
||||
function skipChars(
|
||||
sourceLines, sourceFromPos,
|
||||
targetLines, targetFromPos, targetToPos
|
||||
) {
|
||||
assert.ok(sourceLines instanceof linesModule.Lines);
|
||||
assert.ok(targetLines instanceof linesModule.Lines);
|
||||
Position.assert(sourceFromPos);
|
||||
Position.assert(targetFromPos);
|
||||
Position.assert(targetToPos);
|
||||
|
||||
var targetComparison = comparePos(targetFromPos, targetToPos);
|
||||
if (targetComparison === 0) {
|
||||
// Trivial case: no characters to skip.
|
||||
return sourceFromPos;
|
||||
}
|
||||
|
||||
if (targetComparison < 0) {
|
||||
// Skipping forward.
|
||||
|
||||
var sourceCursor = sourceLines.skipSpaces(sourceFromPos);
|
||||
var targetCursor = targetLines.skipSpaces(targetFromPos);
|
||||
|
||||
var lineDiff = targetToPos.line - targetCursor.line;
|
||||
sourceCursor.line += lineDiff;
|
||||
targetCursor.line += lineDiff;
|
||||
|
||||
if (lineDiff > 0) {
|
||||
// If jumping to later lines, reset columns to the beginnings
|
||||
// of those lines.
|
||||
sourceCursor.column = 0;
|
||||
targetCursor.column = 0;
|
||||
} else {
|
||||
assert.strictEqual(lineDiff, 0);
|
||||
}
|
||||
|
||||
while (comparePos(targetCursor, targetToPos) < 0 &&
|
||||
targetLines.nextPos(targetCursor, true)) {
|
||||
assert.ok(sourceLines.nextPos(sourceCursor, true));
|
||||
assert.strictEqual(
|
||||
sourceLines.charAt(sourceCursor),
|
||||
targetLines.charAt(targetCursor)
|
||||
);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Skipping backward.
|
||||
|
||||
var sourceCursor = sourceLines.skipSpaces(sourceFromPos, true);
|
||||
var targetCursor = targetLines.skipSpaces(targetFromPos, true);
|
||||
|
||||
var lineDiff = targetToPos.line - targetCursor.line;
|
||||
sourceCursor.line += lineDiff;
|
||||
targetCursor.line += lineDiff;
|
||||
|
||||
if (lineDiff < 0) {
|
||||
// If jumping to earlier lines, reset columns to the ends of
|
||||
// those lines.
|
||||
sourceCursor.column = sourceLines.getLineLength(sourceCursor.line);
|
||||
targetCursor.column = targetLines.getLineLength(targetCursor.line);
|
||||
} else {
|
||||
assert.strictEqual(lineDiff, 0);
|
||||
}
|
||||
|
||||
while (comparePos(targetToPos, targetCursor) < 0 &&
|
||||
targetLines.prevPos(targetCursor, true)) {
|
||||
assert.ok(sourceLines.prevPos(sourceCursor, true));
|
||||
assert.strictEqual(
|
||||
sourceLines.charAt(sourceCursor),
|
||||
targetLines.charAt(targetCursor)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return sourceCursor;
|
||||
}
|
||||
127
node_modules/recast/lib/options.js
generated
vendored
Normal file
127
node_modules/recast/lib/options.js
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
var defaults = {
|
||||
// If you want to use a different branch of esprima, or any other
|
||||
// module that supports a .parse function, pass that module object to
|
||||
// recast.parse as options.parser (legacy synonym: options.esprima).
|
||||
parser: require("esprima"),
|
||||
|
||||
// Number of spaces the pretty-printer should use per tab for
|
||||
// indentation. If you do not pass this option explicitly, it will be
|
||||
// (quite reliably!) inferred from the original code.
|
||||
tabWidth: 4,
|
||||
|
||||
// If you really want the pretty-printer to use tabs instead of
|
||||
// spaces, make this option true.
|
||||
useTabs: false,
|
||||
|
||||
// The reprinting code leaves leading whitespace untouched unless it
|
||||
// has to reindent a line, or you pass false for this option.
|
||||
reuseWhitespace: true,
|
||||
|
||||
// Override this option to use a different line terminator, e.g. \r\n.
|
||||
lineTerminator: require("os").EOL,
|
||||
|
||||
// Some of the pretty-printer code (such as that for printing function
|
||||
// parameter lists) makes a valiant attempt to prevent really long
|
||||
// lines. You can adjust the limit by changing this option; however,
|
||||
// there is no guarantee that line length will fit inside this limit.
|
||||
wrapColumn: 74, // Aspirational for now.
|
||||
|
||||
// Pass a string as options.sourceFileName to recast.parse to tell the
|
||||
// reprinter to keep track of reused code so that it can construct a
|
||||
// source map automatically.
|
||||
sourceFileName: null,
|
||||
|
||||
// Pass a string as options.sourceMapName to recast.print, and
|
||||
// (provided you passed options.sourceFileName earlier) the
|
||||
// PrintResult of recast.print will have a .map property for the
|
||||
// generated source map.
|
||||
sourceMapName: null,
|
||||
|
||||
// If provided, this option will be passed along to the source map
|
||||
// generator as a root directory for relative source file paths.
|
||||
sourceRoot: null,
|
||||
|
||||
// If you provide a source map that was generated from a previous call
|
||||
// to recast.print as options.inputSourceMap, the old source map will
|
||||
// be composed with the new source map.
|
||||
inputSourceMap: null,
|
||||
|
||||
// If you want esprima to generate .range information (recast only
|
||||
// uses .loc internally), pass true for this option.
|
||||
range: false,
|
||||
|
||||
// If you want esprima not to throw exceptions when it encounters
|
||||
// non-fatal errors, keep this option true.
|
||||
tolerant: true,
|
||||
|
||||
// If you want to override the quotes used in string literals, specify
|
||||
// either "single", "double", or "auto" here ("auto" will select the one
|
||||
// which results in the shorter literal)
|
||||
// Otherwise, double quotes are used.
|
||||
quote: null,
|
||||
|
||||
// Controls the printing of trailing commas in object literals,
|
||||
// array expressions and function parameters.
|
||||
//
|
||||
// This option could either be:
|
||||
// * Boolean - enable/disable in all contexts (objects, arrays and function params).
|
||||
// * Object - enable/disable per context.
|
||||
//
|
||||
// Example:
|
||||
// trailingComma: {
|
||||
// objects: true,
|
||||
// arrays: true,
|
||||
// parameters: false,
|
||||
// }
|
||||
trailingComma: false,
|
||||
|
||||
// Controls the printing of spaces inside array brackets.
|
||||
// See: http://eslint.org/docs/rules/array-bracket-spacing
|
||||
arrayBracketSpacing: false,
|
||||
|
||||
// Controls the printing of spaces inside object literals,
|
||||
// destructuring assignments, and import/export specifiers.
|
||||
// See: http://eslint.org/docs/rules/object-curly-spacing
|
||||
objectCurlySpacing: true,
|
||||
|
||||
// If you want parenthesis to wrap single-argument arrow function parameter
|
||||
// lists, pass true for this option.
|
||||
arrowParensAlways: false,
|
||||
|
||||
// There are 2 supported syntaxes (`,` and `;`) in Flow Object Types;
|
||||
// The use of commas is in line with the more popular style and matches
|
||||
// how objects are defined in JS, making it a bit more natural to write.
|
||||
flowObjectCommas: true,
|
||||
}, hasOwn = defaults.hasOwnProperty;
|
||||
|
||||
// Copy options and fill in default values.
|
||||
exports.normalize = function(options) {
|
||||
options = options || defaults;
|
||||
|
||||
function get(key) {
|
||||
return hasOwn.call(options, key)
|
||||
? options[key]
|
||||
: defaults[key];
|
||||
}
|
||||
|
||||
return {
|
||||
tabWidth: +get("tabWidth"),
|
||||
useTabs: !!get("useTabs"),
|
||||
reuseWhitespace: !!get("reuseWhitespace"),
|
||||
lineTerminator: get("lineTerminator"),
|
||||
wrapColumn: Math.max(get("wrapColumn"), 0),
|
||||
sourceFileName: get("sourceFileName"),
|
||||
sourceMapName: get("sourceMapName"),
|
||||
sourceRoot: get("sourceRoot"),
|
||||
inputSourceMap: get("inputSourceMap"),
|
||||
parser: get("esprima") || get("parser"),
|
||||
range: get("range"),
|
||||
tolerant: get("tolerant"),
|
||||
quote: get("quote"),
|
||||
trailingComma: get("trailingComma"),
|
||||
arrayBracketSpacing: get("arrayBracketSpacing"),
|
||||
objectCurlySpacing: get("objectCurlySpacing"),
|
||||
arrowParensAlways: get("arrowParensAlways"),
|
||||
flowObjectCommas: get("flowObjectCommas"),
|
||||
};
|
||||
};
|
||||
158
node_modules/recast/lib/parser.js
generated
vendored
Normal file
158
node_modules/recast/lib/parser.js
generated
vendored
Normal file
@@ -0,0 +1,158 @@
|
||||
var assert = require("assert");
|
||||
var types = require("./types");
|
||||
var n = types.namedTypes;
|
||||
var b = types.builders;
|
||||
var isObject = types.builtInTypes.object;
|
||||
var isArray = types.builtInTypes.array;
|
||||
var isFunction = types.builtInTypes.function;
|
||||
var Patcher = require("./patcher").Patcher;
|
||||
var normalizeOptions = require("./options").normalize;
|
||||
var fromString = require("./lines").fromString;
|
||||
var attachComments = require("./comments").attach;
|
||||
var util = require("./util");
|
||||
|
||||
exports.parse = function parse(source, options) {
|
||||
options = normalizeOptions(options);
|
||||
|
||||
var lines = fromString(source, options);
|
||||
|
||||
var sourceWithoutTabs = lines.toString({
|
||||
tabWidth: options.tabWidth,
|
||||
reuseWhitespace: false,
|
||||
useTabs: false
|
||||
});
|
||||
|
||||
var comments = [];
|
||||
var program = options.parser.parse(sourceWithoutTabs, {
|
||||
jsx: true,
|
||||
loc: true,
|
||||
locations: true,
|
||||
range: options.range,
|
||||
comment: true,
|
||||
onComment: comments,
|
||||
tolerant: options.tolerant,
|
||||
ecmaVersion: 6,
|
||||
sourceType: 'module'
|
||||
});
|
||||
|
||||
// If the source was empty, some parsers give loc.{start,end}.line
|
||||
// values of 0, instead of the minimum of 1.
|
||||
util.fixFaultyLocations(program, lines);
|
||||
|
||||
program.loc = program.loc || {
|
||||
start: lines.firstPos(),
|
||||
end: lines.lastPos()
|
||||
};
|
||||
|
||||
program.loc.lines = lines;
|
||||
program.loc.indent = 0;
|
||||
|
||||
// Expand the Program node's .loc to include all comments, since
|
||||
// typically its .loc.start and .loc.end will coincide with those of the
|
||||
// first and last statements, respectively, excluding any comments that
|
||||
// fall outside that region.
|
||||
var trueProgramLoc = util.getTrueLoc(program, lines);
|
||||
program.loc.start = trueProgramLoc.start;
|
||||
program.loc.end = trueProgramLoc.end;
|
||||
|
||||
if (program.comments) {
|
||||
comments = program.comments;
|
||||
delete program.comments;
|
||||
}
|
||||
|
||||
// In order to ensure we reprint leading and trailing program comments,
|
||||
// wrap the original Program node with a File node.
|
||||
var file = program;
|
||||
if (file.type === "Program") {
|
||||
var file = b.file(program, options.sourceFileName || null);
|
||||
file.loc = {
|
||||
lines: lines,
|
||||
indent: 0,
|
||||
start: lines.firstPos(),
|
||||
end: lines.lastPos()
|
||||
};
|
||||
} else if (file.type === "File") {
|
||||
program = file.program;
|
||||
}
|
||||
|
||||
// Passing file.program here instead of just file means that initial
|
||||
// comments will be attached to program.body[0] instead of program.
|
||||
attachComments(
|
||||
comments,
|
||||
program.body.length ? file.program : file,
|
||||
lines
|
||||
);
|
||||
|
||||
// Return a copy of the original AST so that any changes made may be
|
||||
// compared to the original.
|
||||
return new TreeCopier(lines).copy(file);
|
||||
};
|
||||
|
||||
function TreeCopier(lines) {
|
||||
assert.ok(this instanceof TreeCopier);
|
||||
this.lines = lines;
|
||||
this.indent = 0;
|
||||
}
|
||||
|
||||
var TCp = TreeCopier.prototype;
|
||||
|
||||
TCp.copy = function(node) {
|
||||
if (isArray.check(node)) {
|
||||
return node.map(this.copy, this);
|
||||
}
|
||||
|
||||
if (!isObject.check(node)) {
|
||||
return node;
|
||||
}
|
||||
|
||||
util.fixFaultyLocations(node, this.lines);
|
||||
|
||||
var copy = Object.create(Object.getPrototypeOf(node), {
|
||||
original: { // Provide a link from the copy to the original.
|
||||
value: node,
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
writable: true
|
||||
}
|
||||
});
|
||||
|
||||
var loc = node.loc;
|
||||
var oldIndent = this.indent;
|
||||
var newIndent = oldIndent;
|
||||
|
||||
if (loc) {
|
||||
// When node is a comment, we set node.loc.indent to
|
||||
// node.loc.start.column so that, when/if we print the comment by
|
||||
// itself, we can strip that much whitespace from the left margin of
|
||||
// the comment. This only really matters for multiline Block comments,
|
||||
// but it doesn't hurt for Line comments.
|
||||
if (node.type === "Block" || node.type === "Line" ||
|
||||
node.type === "CommentBlock" || node.type === "CommentLine" ||
|
||||
this.lines.isPrecededOnlyByWhitespace(loc.start)) {
|
||||
newIndent = this.indent = loc.start.column;
|
||||
}
|
||||
|
||||
loc.lines = this.lines;
|
||||
loc.indent = newIndent;
|
||||
}
|
||||
|
||||
var keys = Object.keys(node);
|
||||
var keyCount = keys.length;
|
||||
for (var i = 0; i < keyCount; ++i) {
|
||||
var key = keys[i];
|
||||
if (key === "loc") {
|
||||
copy[key] = node[key];
|
||||
} else if (key === "tokens" &&
|
||||
node.type === "File") {
|
||||
// Preserve file.tokens (uncopied) in case client code cares about
|
||||
// it, even though Recast ignores it when reprinting.
|
||||
copy[key] = node[key];
|
||||
} else {
|
||||
copy[key] = this.copy(node[key]);
|
||||
}
|
||||
}
|
||||
|
||||
this.indent = oldIndent;
|
||||
|
||||
return copy;
|
||||
};
|
||||
535
node_modules/recast/lib/patcher.js
generated
vendored
Normal file
535
node_modules/recast/lib/patcher.js
generated
vendored
Normal file
@@ -0,0 +1,535 @@
|
||||
var assert = require("assert");
|
||||
var linesModule = require("./lines");
|
||||
var types = require("./types");
|
||||
var getFieldValue = types.getFieldValue;
|
||||
var Printable = types.namedTypes.Printable;
|
||||
var Expression = types.namedTypes.Expression;
|
||||
var ReturnStatement = types.namedTypes.ReturnStatement;
|
||||
var SourceLocation = types.namedTypes.SourceLocation;
|
||||
var util = require("./util");
|
||||
var comparePos = util.comparePos;
|
||||
var FastPath = require("./fast-path");
|
||||
var isObject = types.builtInTypes.object;
|
||||
var isArray = types.builtInTypes.array;
|
||||
var isString = types.builtInTypes.string;
|
||||
var riskyAdjoiningCharExp = /[0-9a-z_$]/i;
|
||||
|
||||
function Patcher(lines) {
|
||||
assert.ok(this instanceof Patcher);
|
||||
assert.ok(lines instanceof linesModule.Lines);
|
||||
|
||||
var self = this,
|
||||
replacements = [];
|
||||
|
||||
self.replace = function(loc, lines) {
|
||||
if (isString.check(lines))
|
||||
lines = linesModule.fromString(lines);
|
||||
|
||||
replacements.push({
|
||||
lines: lines,
|
||||
start: loc.start,
|
||||
end: loc.end
|
||||
});
|
||||
};
|
||||
|
||||
self.get = function(loc) {
|
||||
// If no location is provided, return the complete Lines object.
|
||||
loc = loc || {
|
||||
start: { line: 1, column: 0 },
|
||||
end: { line: lines.length,
|
||||
column: lines.getLineLength(lines.length) }
|
||||
};
|
||||
|
||||
var sliceFrom = loc.start,
|
||||
toConcat = [];
|
||||
|
||||
function pushSlice(from, to) {
|
||||
assert.ok(comparePos(from, to) <= 0);
|
||||
toConcat.push(lines.slice(from, to));
|
||||
}
|
||||
|
||||
replacements.sort(function(a, b) {
|
||||
return comparePos(a.start, b.start);
|
||||
}).forEach(function(rep) {
|
||||
if (comparePos(sliceFrom, rep.start) > 0) {
|
||||
// Ignore nested replacement ranges.
|
||||
} else {
|
||||
pushSlice(sliceFrom, rep.start);
|
||||
toConcat.push(rep.lines);
|
||||
sliceFrom = rep.end;
|
||||
}
|
||||
});
|
||||
|
||||
pushSlice(sliceFrom, loc.end);
|
||||
|
||||
return linesModule.concat(toConcat);
|
||||
};
|
||||
}
|
||||
exports.Patcher = Patcher;
|
||||
|
||||
var Pp = Patcher.prototype;
|
||||
|
||||
Pp.tryToReprintComments = function(newNode, oldNode, print) {
|
||||
var patcher = this;
|
||||
|
||||
if (!newNode.comments &&
|
||||
!oldNode.comments) {
|
||||
// We were (vacuously) able to reprint all the comments!
|
||||
return true;
|
||||
}
|
||||
|
||||
var newPath = FastPath.from(newNode);
|
||||
var oldPath = FastPath.from(oldNode);
|
||||
|
||||
newPath.stack.push("comments", getSurroundingComments(newNode));
|
||||
oldPath.stack.push("comments", getSurroundingComments(oldNode));
|
||||
|
||||
var reprints = [];
|
||||
var ableToReprintComments =
|
||||
findArrayReprints(newPath, oldPath, reprints);
|
||||
|
||||
// No need to pop anything from newPath.stack or oldPath.stack, since
|
||||
// newPath and oldPath are fresh local variables.
|
||||
|
||||
if (ableToReprintComments && reprints.length > 0) {
|
||||
reprints.forEach(function(reprint) {
|
||||
var oldComment = reprint.oldPath.getValue();
|
||||
assert.ok(oldComment.leading || oldComment.trailing);
|
||||
patcher.replace(
|
||||
oldComment.loc,
|
||||
// Comments can't have .comments, so it doesn't matter
|
||||
// whether we print with comments or without.
|
||||
print(reprint.newPath).indentTail(oldComment.loc.indent)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return ableToReprintComments;
|
||||
};
|
||||
|
||||
// Get all comments that are either leading or trailing, ignoring any
|
||||
// comments that occur inside node.loc. Returns an empty array for nodes
|
||||
// with no leading or trailing comments.
|
||||
function getSurroundingComments(node) {
|
||||
var result = [];
|
||||
if (node.comments &&
|
||||
node.comments.length > 0) {
|
||||
node.comments.forEach(function(comment) {
|
||||
if (comment.leading || comment.trailing) {
|
||||
result.push(comment);
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Pp.deleteComments = function(node) {
|
||||
if (!node.comments) {
|
||||
return;
|
||||
}
|
||||
|
||||
var patcher = this;
|
||||
|
||||
node.comments.forEach(function(comment) {
|
||||
if (comment.leading) {
|
||||
// Delete leading comments along with any trailing whitespace
|
||||
// they might have.
|
||||
patcher.replace({
|
||||
start: comment.loc.start,
|
||||
end: node.loc.lines.skipSpaces(
|
||||
comment.loc.end, false, false)
|
||||
}, "");
|
||||
|
||||
} else if (comment.trailing) {
|
||||
// Delete trailing comments along with any leading whitespace
|
||||
// they might have.
|
||||
patcher.replace({
|
||||
start: node.loc.lines.skipSpaces(
|
||||
comment.loc.start, true, false),
|
||||
end: comment.loc.end
|
||||
}, "");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.getReprinter = function(path) {
|
||||
assert.ok(path instanceof FastPath);
|
||||
|
||||
// Make sure that this path refers specifically to a Node, rather than
|
||||
// some non-Node subproperty of a Node.
|
||||
var node = path.getValue();
|
||||
if (!Printable.check(node))
|
||||
return;
|
||||
|
||||
var orig = node.original;
|
||||
var origLoc = orig && orig.loc;
|
||||
var lines = origLoc && origLoc.lines;
|
||||
var reprints = [];
|
||||
|
||||
if (!lines || !findReprints(path, reprints))
|
||||
return;
|
||||
|
||||
return function(print) {
|
||||
var patcher = new Patcher(lines);
|
||||
|
||||
reprints.forEach(function(reprint) {
|
||||
var newNode = reprint.newPath.getValue();
|
||||
var oldNode = reprint.oldPath.getValue();
|
||||
|
||||
SourceLocation.assert(oldNode.loc, true);
|
||||
|
||||
var needToPrintNewPathWithComments =
|
||||
!patcher.tryToReprintComments(newNode, oldNode, print)
|
||||
|
||||
if (needToPrintNewPathWithComments) {
|
||||
// Since we were not able to preserve all leading/trailing
|
||||
// comments, we delete oldNode's comments, print newPath
|
||||
// with comments, and then patch the resulting lines where
|
||||
// oldNode used to be.
|
||||
patcher.deleteComments(oldNode);
|
||||
}
|
||||
|
||||
var newLines = print(
|
||||
reprint.newPath,
|
||||
needToPrintNewPathWithComments
|
||||
).indentTail(oldNode.loc.indent);
|
||||
|
||||
var nls = needsLeadingSpace(lines, oldNode.loc, newLines);
|
||||
var nts = needsTrailingSpace(lines, oldNode.loc, newLines);
|
||||
|
||||
// If we try to replace the argument of a ReturnStatement like
|
||||
// return"asdf" with e.g. a literal null expression, we run
|
||||
// the risk of ending up with returnnull, so we need to add an
|
||||
// extra leading space in situations where that might
|
||||
// happen. Likewise for "asdf"in obj. See #170.
|
||||
if (nls || nts) {
|
||||
var newParts = [];
|
||||
nls && newParts.push(" ");
|
||||
newParts.push(newLines);
|
||||
nts && newParts.push(" ");
|
||||
newLines = linesModule.concat(newParts);
|
||||
}
|
||||
|
||||
patcher.replace(oldNode.loc, newLines);
|
||||
});
|
||||
|
||||
// Recall that origLoc is the .loc of an ancestor node that is
|
||||
// guaranteed to contain all the reprinted nodes and comments.
|
||||
return patcher.get(origLoc).indentTail(-orig.loc.indent);
|
||||
};
|
||||
};
|
||||
|
||||
// If the last character before oldLoc and the first character of newLines
|
||||
// are both identifier characters, they must be separated by a space,
|
||||
// otherwise they will most likely get fused together into a single token.
|
||||
function needsLeadingSpace(oldLines, oldLoc, newLines) {
|
||||
var posBeforeOldLoc = util.copyPos(oldLoc.start);
|
||||
|
||||
// The character just before the location occupied by oldNode.
|
||||
var charBeforeOldLoc =
|
||||
oldLines.prevPos(posBeforeOldLoc) &&
|
||||
oldLines.charAt(posBeforeOldLoc);
|
||||
|
||||
// First character of the reprinted node.
|
||||
var newFirstChar = newLines.charAt(newLines.firstPos());
|
||||
|
||||
return charBeforeOldLoc &&
|
||||
riskyAdjoiningCharExp.test(charBeforeOldLoc) &&
|
||||
newFirstChar &&
|
||||
riskyAdjoiningCharExp.test(newFirstChar);
|
||||
}
|
||||
|
||||
// If the last character of newLines and the first character after oldLoc
|
||||
// are both identifier characters, they must be separated by a space,
|
||||
// otherwise they will most likely get fused together into a single token.
|
||||
function needsTrailingSpace(oldLines, oldLoc, newLines) {
|
||||
// The character just after the location occupied by oldNode.
|
||||
var charAfterOldLoc = oldLines.charAt(oldLoc.end);
|
||||
|
||||
var newLastPos = newLines.lastPos();
|
||||
|
||||
// Last character of the reprinted node.
|
||||
var newLastChar = newLines.prevPos(newLastPos) &&
|
||||
newLines.charAt(newLastPos);
|
||||
|
||||
return newLastChar &&
|
||||
riskyAdjoiningCharExp.test(newLastChar) &&
|
||||
charAfterOldLoc &&
|
||||
riskyAdjoiningCharExp.test(charAfterOldLoc);
|
||||
}
|
||||
|
||||
function findReprints(newPath, reprints) {
|
||||
var newNode = newPath.getValue();
|
||||
Printable.assert(newNode);
|
||||
|
||||
var oldNode = newNode.original;
|
||||
Printable.assert(oldNode);
|
||||
|
||||
assert.deepEqual(reprints, []);
|
||||
|
||||
if (newNode.type !== oldNode.type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var oldPath = new FastPath(oldNode);
|
||||
var canReprint = findChildReprints(newPath, oldPath, reprints);
|
||||
|
||||
if (!canReprint) {
|
||||
// Make absolutely sure the calling code does not attempt to reprint
|
||||
// any nodes.
|
||||
reprints.length = 0;
|
||||
}
|
||||
|
||||
return canReprint;
|
||||
}
|
||||
|
||||
function findAnyReprints(newPath, oldPath, reprints) {
|
||||
var newNode = newPath.getValue();
|
||||
var oldNode = oldPath.getValue();
|
||||
|
||||
if (newNode === oldNode)
|
||||
return true;
|
||||
|
||||
if (isArray.check(newNode))
|
||||
return findArrayReprints(newPath, oldPath, reprints);
|
||||
|
||||
if (isObject.check(newNode))
|
||||
return findObjectReprints(newPath, oldPath, reprints);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function findArrayReprints(newPath, oldPath, reprints) {
|
||||
var newNode = newPath.getValue();
|
||||
var oldNode = oldPath.getValue();
|
||||
isArray.assert(newNode);
|
||||
var len = newNode.length;
|
||||
|
||||
if (!(isArray.check(oldNode) &&
|
||||
oldNode.length === len))
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
newPath.stack.push(i, newNode[i]);
|
||||
oldPath.stack.push(i, oldNode[i]);
|
||||
var canReprint = findAnyReprints(newPath, oldPath, reprints);
|
||||
newPath.stack.length -= 2;
|
||||
oldPath.stack.length -= 2;
|
||||
if (!canReprint) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function findObjectReprints(newPath, oldPath, reprints) {
|
||||
var newNode = newPath.getValue();
|
||||
isObject.assert(newNode);
|
||||
|
||||
if (newNode.original === null) {
|
||||
// If newNode.original node was set to null, reprint the node.
|
||||
return false;
|
||||
}
|
||||
|
||||
var oldNode = oldPath.getValue();
|
||||
if (!isObject.check(oldNode))
|
||||
return false;
|
||||
|
||||
if (Printable.check(newNode)) {
|
||||
if (!Printable.check(oldNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Here we need to decide whether the reprinted code for newNode
|
||||
// is appropriate for patching into the location of oldNode.
|
||||
|
||||
if (newNode.type === oldNode.type) {
|
||||
var childReprints = [];
|
||||
|
||||
if (findChildReprints(newPath, oldPath, childReprints)) {
|
||||
reprints.push.apply(reprints, childReprints);
|
||||
} else if (oldNode.loc) {
|
||||
// If we have no .loc information for oldNode, then we
|
||||
// won't be able to reprint it.
|
||||
reprints.push({
|
||||
oldPath: oldPath.copy(),
|
||||
newPath: newPath.copy()
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Expression.check(newNode) &&
|
||||
Expression.check(oldNode) &&
|
||||
// If we have no .loc information for oldNode, then we won't
|
||||
// be able to reprint it.
|
||||
oldNode.loc) {
|
||||
|
||||
// If both nodes are subtypes of Expression, then we should be
|
||||
// able to fill the location occupied by the old node with
|
||||
// code printed for the new node with no ill consequences.
|
||||
reprints.push({
|
||||
oldPath: oldPath.copy(),
|
||||
newPath: newPath.copy()
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// The nodes have different types, and at least one of the types
|
||||
// is not a subtype of the Expression type, so we cannot safely
|
||||
// assume the nodes are syntactically interchangeable.
|
||||
return false;
|
||||
}
|
||||
|
||||
return findChildReprints(newPath, oldPath, reprints);
|
||||
}
|
||||
|
||||
// This object is reused in hasOpeningParen and hasClosingParen to avoid
|
||||
// having to allocate a temporary object.
|
||||
var reusablePos = { line: 1, column: 0 };
|
||||
var nonSpaceExp = /\S/;
|
||||
|
||||
function hasOpeningParen(oldPath) {
|
||||
var oldNode = oldPath.getValue();
|
||||
var loc = oldNode.loc;
|
||||
var lines = loc && loc.lines;
|
||||
|
||||
if (lines) {
|
||||
var pos = reusablePos;
|
||||
pos.line = loc.start.line;
|
||||
pos.column = loc.start.column;
|
||||
|
||||
while (lines.prevPos(pos)) {
|
||||
var ch = lines.charAt(pos);
|
||||
|
||||
if (ch === "(") {
|
||||
// If we found an opening parenthesis but it occurred before
|
||||
// the start of the original subtree for this reprinting, then
|
||||
// we must not return true for hasOpeningParen(oldPath).
|
||||
return comparePos(oldPath.getRootValue().loc.start, pos) <= 0;
|
||||
}
|
||||
|
||||
if (nonSpaceExp.test(ch)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasClosingParen(oldPath) {
|
||||
var oldNode = oldPath.getValue();
|
||||
var loc = oldNode.loc;
|
||||
var lines = loc && loc.lines;
|
||||
|
||||
if (lines) {
|
||||
var pos = reusablePos;
|
||||
pos.line = loc.end.line;
|
||||
pos.column = loc.end.column;
|
||||
|
||||
do {
|
||||
var ch = lines.charAt(pos);
|
||||
|
||||
if (ch === ")") {
|
||||
// If we found a closing parenthesis but it occurred after the
|
||||
// end of the original subtree for this reprinting, then we
|
||||
// must not return true for hasClosingParen(oldPath).
|
||||
return comparePos(pos, oldPath.getRootValue().loc.end) <= 0;
|
||||
}
|
||||
|
||||
if (nonSpaceExp.test(ch)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} while (lines.nextPos(pos));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasParens(oldPath) {
|
||||
// This logic can technically be fooled if the node has parentheses
|
||||
// but there are comments intervening between the parentheses and the
|
||||
// node. In such cases the node will be harmlessly wrapped in an
|
||||
// additional layer of parentheses.
|
||||
return hasOpeningParen(oldPath) && hasClosingParen(oldPath);
|
||||
}
|
||||
|
||||
function findChildReprints(newPath, oldPath, reprints) {
|
||||
var newNode = newPath.getValue();
|
||||
var oldNode = oldPath.getValue();
|
||||
|
||||
isObject.assert(newNode);
|
||||
isObject.assert(oldNode);
|
||||
|
||||
if (newNode.original === null) {
|
||||
// If newNode.original node was set to null, reprint the node.
|
||||
return false;
|
||||
}
|
||||
|
||||
// If this type of node cannot come lexically first in its enclosing
|
||||
// statement (e.g. a function expression or object literal), and it
|
||||
// seems to be doing so, then the only way we can ignore this problem
|
||||
// and save ourselves from falling back to the pretty printer is if an
|
||||
// opening parenthesis happens to precede the node. For example,
|
||||
// (function(){ ... }()); does not need to be reprinted, even though
|
||||
// the FunctionExpression comes lexically first in the enclosing
|
||||
// ExpressionStatement and fails the hasParens test, because the
|
||||
// parent CallExpression passes the hasParens test. If we relied on
|
||||
// the path.needsParens() && !hasParens(oldNode) check below, the
|
||||
// absence of a closing parenthesis after the FunctionExpression would
|
||||
// trigger pretty-printing unnecessarily.
|
||||
if (!newPath.canBeFirstInStatement() &&
|
||||
newPath.firstInStatement() &&
|
||||
!hasOpeningParen(oldPath))
|
||||
return false;
|
||||
|
||||
// If this node needs parentheses and will not be wrapped with
|
||||
// parentheses when reprinted, then return false to skip reprinting
|
||||
// and let it be printed generically.
|
||||
if (newPath.needsParens(true) && !hasParens(oldPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var keys = util.getUnionOfKeys(oldNode, newNode);
|
||||
|
||||
if (oldNode.type === "File" ||
|
||||
newNode.type === "File") {
|
||||
// Don't bother traversing file.tokens, an often very large array
|
||||
// returned by Babylon, and useless for our purposes.
|
||||
delete keys.tokens;
|
||||
}
|
||||
|
||||
// Don't bother traversing .loc objects looking for reprintable nodes.
|
||||
delete keys.loc;
|
||||
|
||||
var originalReprintCount = reprints.length;
|
||||
|
||||
for (var k in keys) {
|
||||
newPath.stack.push(k, types.getFieldValue(newNode, k));
|
||||
oldPath.stack.push(k, types.getFieldValue(oldNode, k));
|
||||
var canReprint = findAnyReprints(newPath, oldPath, reprints);
|
||||
newPath.stack.length -= 2;
|
||||
oldPath.stack.length -= 2;
|
||||
|
||||
if (!canReprint) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Return statements might end up running into ASI issues due to comments
|
||||
// inserted deep within the tree, so reprint them if anything changed
|
||||
// within them.
|
||||
if (ReturnStatement.check(newPath.getNode()) &&
|
||||
reprints.length > originalReprintCount) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
2010
node_modules/recast/lib/printer.js
generated
vendored
Normal file
2010
node_modules/recast/lib/printer.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
5
node_modules/recast/lib/types.js
generated
vendored
Normal file
5
node_modules/recast/lib/types.js
generated
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
// This module was originally created so that Recast could add its own
|
||||
// custom types to the AST type system (in particular, the File type), but
|
||||
// those types are now incorporated into ast-types, so this module doesn't
|
||||
// have much to do anymore. Still, it might prove useful in the future.
|
||||
module.exports = require("ast-types");
|
||||
313
node_modules/recast/lib/util.js
generated
vendored
Normal file
313
node_modules/recast/lib/util.js
generated
vendored
Normal file
@@ -0,0 +1,313 @@
|
||||
var assert = require("assert");
|
||||
var types = require("./types");
|
||||
var getFieldValue = types.getFieldValue;
|
||||
var n = types.namedTypes;
|
||||
var sourceMap = require("source-map");
|
||||
var SourceMapConsumer = sourceMap.SourceMapConsumer;
|
||||
var SourceMapGenerator = sourceMap.SourceMapGenerator;
|
||||
var hasOwn = Object.prototype.hasOwnProperty;
|
||||
var util = exports;
|
||||
|
||||
function getUnionOfKeys() {
|
||||
var result = {};
|
||||
var argc = arguments.length;
|
||||
for (var i = 0; i < argc; ++i) {
|
||||
var keys = Object.keys(arguments[i]);
|
||||
var keyCount = keys.length;
|
||||
for (var j = 0; j < keyCount; ++j) {
|
||||
result[keys[j]] = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
util.getUnionOfKeys = getUnionOfKeys;
|
||||
|
||||
function comparePos(pos1, pos2) {
|
||||
return (pos1.line - pos2.line) || (pos1.column - pos2.column);
|
||||
}
|
||||
util.comparePos = comparePos;
|
||||
|
||||
function copyPos(pos) {
|
||||
return {
|
||||
line: pos.line,
|
||||
column: pos.column
|
||||
};
|
||||
}
|
||||
util.copyPos = copyPos;
|
||||
|
||||
util.composeSourceMaps = function(formerMap, latterMap) {
|
||||
if (formerMap) {
|
||||
if (!latterMap) {
|
||||
return formerMap;
|
||||
}
|
||||
} else {
|
||||
return latterMap || null;
|
||||
}
|
||||
|
||||
var smcFormer = new SourceMapConsumer(formerMap);
|
||||
var smcLatter = new SourceMapConsumer(latterMap);
|
||||
var smg = new SourceMapGenerator({
|
||||
file: latterMap.file,
|
||||
sourceRoot: latterMap.sourceRoot
|
||||
});
|
||||
|
||||
var sourcesToContents = {};
|
||||
|
||||
smcLatter.eachMapping(function(mapping) {
|
||||
var origPos = smcFormer.originalPositionFor({
|
||||
line: mapping.originalLine,
|
||||
column: mapping.originalColumn
|
||||
});
|
||||
|
||||
var sourceName = origPos.source;
|
||||
if (sourceName === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
smg.addMapping({
|
||||
source: sourceName,
|
||||
original: copyPos(origPos),
|
||||
generated: {
|
||||
line: mapping.generatedLine,
|
||||
column: mapping.generatedColumn
|
||||
},
|
||||
name: mapping.name
|
||||
});
|
||||
|
||||
var sourceContent = smcFormer.sourceContentFor(sourceName);
|
||||
if (sourceContent && !hasOwn.call(sourcesToContents, sourceName)) {
|
||||
sourcesToContents[sourceName] = sourceContent;
|
||||
smg.setSourceContent(sourceName, sourceContent);
|
||||
}
|
||||
});
|
||||
|
||||
return smg.toJSON();
|
||||
};
|
||||
|
||||
util.getTrueLoc = function(node, lines) {
|
||||
// It's possible that node is newly-created (not parsed by Esprima),
|
||||
// in which case it probably won't have a .loc property (or an
|
||||
// .original property for that matter). That's fine; we'll just
|
||||
// pretty-print it as usual.
|
||||
if (!node.loc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = {
|
||||
start: node.loc.start,
|
||||
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) {
|
||||
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 === "File") {
|
||||
// Babylon returns File nodes whose .loc.{start,end} do not include
|
||||
// leading or trailing whitespace.
|
||||
loc.start = lines.firstPos();
|
||||
loc.end = lines.lastPos();
|
||||
}
|
||||
|
||||
if (node.type === "TemplateLiteral") {
|
||||
fixTemplateLiteral(node, lines);
|
||||
|
||||
} else if (loc && node.decorators) {
|
||||
// Expand the .loc of the node responsible for printing the decorators
|
||||
// (here, the decorated node) so that it includes node.decorators.
|
||||
node.decorators.forEach(function (decorator) {
|
||||
expandLoc(loc, decorator.loc);
|
||||
});
|
||||
|
||||
} else if (node.declaration && util.isExportDeclaration(node)) {
|
||||
// Nullify .loc information for the child declaration so that we never
|
||||
// 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.
|
||||
var decorators = node.declaration.decorators;
|
||||
if (decorators) {
|
||||
decorators.forEach(function (decorator) {
|
||||
expandLoc(loc, decorator.loc);
|
||||
});
|
||||
}
|
||||
|
||||
} else if ((n.MethodDefinition && n.MethodDefinition.check(node)) ||
|
||||
(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)) {
|
||||
// FunctionExpression method values should be anonymous,
|
||||
// because their .id fields are ignored anyway.
|
||||
node.value.id = null;
|
||||
}
|
||||
|
||||
} else if (node.type === "ObjectTypeProperty") {
|
||||
var loc = node.loc;
|
||||
var end = loc && loc.end;
|
||||
if (end) {
|
||||
end = copyPos(end);
|
||||
if (lines.prevPos(end) &&
|
||||
lines.charAt(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) {
|
||||
assert.strictEqual(node.type, "TemplateLiteral");
|
||||
|
||||
if (node.quasis.length === 0) {
|
||||
// If there are no quasi elements, then there is nothing to fix.
|
||||
return;
|
||||
}
|
||||
|
||||
// First we need to exclude the opening ` from the .loc of the first
|
||||
// quasi element, in case the parser accidentally decided to include it.
|
||||
var afterLeftBackTickPos = copyPos(node.loc.start);
|
||||
assert.strictEqual(lines.charAt(afterLeftBackTickPos), "`");
|
||||
assert.ok(lines.nextPos(afterLeftBackTickPos));
|
||||
var firstQuasi = node.quasis[0];
|
||||
if (comparePos(firstQuasi.loc.start, afterLeftBackTickPos) < 0) {
|
||||
firstQuasi.loc.start = afterLeftBackTickPos;
|
||||
}
|
||||
|
||||
// Next we need to exclude the closing ` from the .loc of the last quasi
|
||||
// element, in case the parser accidentally decided to include it.
|
||||
var rightBackTickPos = copyPos(node.loc.end);
|
||||
assert.ok(lines.prevPos(rightBackTickPos));
|
||||
assert.strictEqual(lines.charAt(rightBackTickPos), "`");
|
||||
var lastQuasi = node.quasis[node.quasis.length - 1];
|
||||
if (comparePos(rightBackTickPos, lastQuasi.loc.end) < 0) {
|
||||
lastQuasi.loc.end = rightBackTickPos;
|
||||
}
|
||||
|
||||
// Now we need to exclude ${ and } characters from the .loc's of all
|
||||
// quasi elements, since some parsers accidentally include them.
|
||||
node.expressions.forEach(function (expr, i) {
|
||||
// Rewind from expr.loc.start over any whitespace and the ${ that
|
||||
// precedes the expression. The position of the $ should be the same
|
||||
// as the .loc.end of the preceding quasi element, but some parsers
|
||||
// accidentally include the ${ in the .loc of the quasi element.
|
||||
var dollarCurlyPos = lines.skipSpaces(expr.loc.start, true, false);
|
||||
if (lines.prevPos(dollarCurlyPos) &&
|
||||
lines.charAt(dollarCurlyPos) === "{" &&
|
||||
lines.prevPos(dollarCurlyPos) &&
|
||||
lines.charAt(dollarCurlyPos) === "$") {
|
||||
var quasiBefore = node.quasis[i];
|
||||
if (comparePos(dollarCurlyPos, quasiBefore.loc.end) < 0) {
|
||||
quasiBefore.loc.end = dollarCurlyPos;
|
||||
}
|
||||
}
|
||||
|
||||
// Likewise, some parsers accidentally include the } that follows
|
||||
// the expression in the .loc of the following quasi element.
|
||||
var rightCurlyPos = lines.skipSpaces(expr.loc.end, false, false);
|
||||
if (lines.charAt(rightCurlyPos) === "}") {
|
||||
assert.ok(lines.nextPos(rightCurlyPos));
|
||||
// Now rightCurlyPos is technically the position just after the }.
|
||||
var quasiAfter = node.quasis[i + 1];
|
||||
if (comparePos(quasiAfter.loc.start, rightCurlyPos) < 0) {
|
||||
quasiAfter.loc.start = rightCurlyPos;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
util.isExportDeclaration = function (node) {
|
||||
if (node) switch (node.type) {
|
||||
case "ExportDeclaration":
|
||||
case "ExportDefaultDeclaration":
|
||||
case "ExportDefaultSpecifier":
|
||||
case "DeclareExportDeclaration":
|
||||
case "ExportNamedDeclaration":
|
||||
case "ExportAllDeclaration":
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
util.getParentExportDeclaration = function (path) {
|
||||
var parentNode = path.getParentNode();
|
||||
if (path.getName() === "declaration" &&
|
||||
util.isExportDeclaration(parentNode)) {
|
||||
return parentNode;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
util.isTrailingCommaEnabled = function(options, context) {
|
||||
var trailingComma = options.trailingComma;
|
||||
if (typeof trailingComma === "object") {
|
||||
return !!trailingComma[context];
|
||||
}
|
||||
return !!trailingComma;
|
||||
};
|
||||
Reference in New Issue
Block a user