Comparing version 0.16.2 to 0.17.0
@@ -1,14 +0,15 @@ | ||
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(); | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var assert_1 = __importDefault(require("assert")); | ||
var types_1 = __importDefault(require("./types")); | ||
var n = types_1.default.namedTypes; | ||
var isArray = types_1.default.builtInTypes.array; | ||
var isObject = types_1.default.builtInTypes.object; | ||
var lines_1 = require("./lines"); | ||
var util_1 = require("./util"); | ||
var private_1 = require("private"); | ||
var childNodesCacheKey = private_1.makeUniqueKey(); | ||
// TODO Move a non-caching implementation of this function into ast-types, | ||
@@ -20,3 +21,2 @@ // and implement a caching wrapper function here. | ||
} | ||
// The .loc checks below are sensitive to some of the problems that | ||
@@ -26,4 +26,3 @@ // are fixed by this utility function. Specifically, if it decides to | ||
// is unreliable, then we don't want to add node to the resultArray. | ||
util.fixFaultyLocations(node, lines); | ||
util_1.fixFaultyLocations(node, lines); | ||
if (resultArray) { | ||
@@ -36,4 +35,3 @@ if (n.Node.check(node) && | ||
for (var i = resultArray.length - 1; i >= 0; --i) { | ||
if (comparePos(resultArray[i].loc.end, | ||
node.loc.start) <= 0) { | ||
if (util_1.comparePos(resultArray[i].loc.end, node.loc.start) <= 0) { | ||
break; | ||
@@ -45,15 +43,16 @@ } | ||
} | ||
} else if (node[childNodesCacheKey]) { | ||
} | ||
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 { | ||
} | ||
else if (isObject.check(node)) { | ||
names = types_1.default.getFieldNames(node); | ||
} | ||
else { | ||
return; | ||
} | ||
if (!resultArray) { | ||
@@ -65,10 +64,7 @@ Object.defineProperty(node, childNodesCacheKey, { | ||
} | ||
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 | ||
@@ -79,3 +75,2 @@ // .precedingNode, .enclosingNode, and/or .followingNode properties, at | ||
var childNodes = getSortedChildNodes(node, lines); | ||
// Time to dust off the old binary search robes and wizard hat. | ||
@@ -86,5 +81,4 @@ var left = 0, right = childNodes.length; | ||
var child = childNodes[middle]; | ||
if (comparePos(child.loc.start, comment.loc.start) <= 0 && | ||
comparePos(comment.loc.end, child.loc.end) <= 0) { | ||
if (util_1.comparePos(child.loc.start, comment.loc.start) <= 0 && | ||
util_1.comparePos(comment.loc.end, child.loc.end) <= 0) { | ||
// The comment is completely contained by this child node. | ||
@@ -94,4 +88,3 @@ decorateComment(comment.enclosingNode = child, comment, lines); | ||
} | ||
if (comparePos(child.loc.end, comment.loc.start) <= 0) { | ||
if (util_1.comparePos(child.loc.end, comment.loc.start) <= 0) { | ||
// This child node falls completely before the comment. | ||
@@ -105,4 +98,3 @@ // Because we will never consider this node or any nodes | ||
} | ||
if (comparePos(comment.loc.end, child.loc.start) <= 0) { | ||
if (util_1.comparePos(comment.loc.end, child.loc.start) <= 0) { | ||
// This child node falls completely after the comment. | ||
@@ -116,10 +108,7 @@ // Because we will never consider this node or any nodes after | ||
} | ||
throw new Error("Comment location overlaps with node location"); | ||
} | ||
if (precedingNode) { | ||
comment.precedingNode = precedingNode; | ||
} | ||
if (followingNode) { | ||
@@ -129,18 +118,13 @@ comment.followingNode = followingNode; | ||
} | ||
exports.attach = function(comments, ast, lines) { | ||
function attach(comments, ast, lines) { | ||
if (!isArray.check(comments)) { | ||
return; | ||
} | ||
var tiesToBreak = []; | ||
comments.forEach(function(comment) { | ||
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) { | ||
@@ -150,8 +134,3 @@ var tieCount = tiesToBreak.length; | ||
var lastTie = tiesToBreak[tieCount - 1]; | ||
assert.strictEqual( | ||
lastTie.precedingNode === comment.precedingNode, | ||
lastTie.followingNode === comment.followingNode | ||
); | ||
assert_1.default.strictEqual(lastTie.precedingNode === comment.precedingNode, lastTie.followingNode === comment.followingNode); | ||
if (lastTie.followingNode !== comment.followingNode) { | ||
@@ -161,16 +140,15 @@ breakTies(tiesToBreak, lines); | ||
} | ||
tiesToBreak.push(comment); | ||
} else if (pn) { | ||
} | ||
else if (pn) { | ||
// No contest: we have a trailing comment. | ||
breakTies(tiesToBreak, lines); | ||
addTrailingComment(pn, comment); | ||
} else if (fn) { | ||
} | ||
else if (fn) { | ||
// No contest: we have a leading comment. | ||
breakTies(tiesToBreak, lines); | ||
addLeadingComment(fn, comment); | ||
} else if (en) { | ||
} | ||
else if (en) { | ||
// The enclosing node has no child nodes at all, so what we | ||
@@ -180,11 +158,9 @@ // have here is a dangling comment, e.g. [/* crickets */]. | ||
addDanglingComment(en, comment); | ||
} else { | ||
} | ||
else { | ||
throw new Error("AST contains no nodes at all?"); | ||
} | ||
}); | ||
breakTies(tiesToBreak, lines); | ||
comments.forEach(function(comment) { | ||
comments.forEach(function (comment) { | ||
// These node references were useful for breaking ties, but we | ||
@@ -197,4 +173,5 @@ // don't need them anymore, and they create cycles in the AST that | ||
}); | ||
}; | ||
} | ||
exports.attach = attach; | ||
; | ||
function breakTies(tiesToBreak, lines) { | ||
@@ -205,7 +182,5 @@ var tieCount = tiesToBreak.length; | ||
} | ||
var pn = tiesToBreak[0].precedingNode; | ||
var fn = tiesToBreak[0].followingNode; | ||
var gapEndPos = fn.loc.start; | ||
// Iterate backwards through tiesToBreak, examining the gaps | ||
@@ -215,9 +190,6 @@ // between the tied comments. In order to qualify as leading, a | ||
// whitespace-only gaps (or other comments). | ||
for (var indexOfFirstLeadingComment = tieCount; | ||
indexOfFirstLeadingComment > 0; | ||
--indexOfFirstLeadingComment) { | ||
for (var indexOfFirstLeadingComment = tieCount; indexOfFirstLeadingComment > 0; --indexOfFirstLeadingComment) { | ||
var comment = tiesToBreak[indexOfFirstLeadingComment - 1]; | ||
assert.strictEqual(comment.precedingNode, pn); | ||
assert.strictEqual(comment.followingNode, fn); | ||
assert_1.default.strictEqual(comment.precedingNode, pn); | ||
assert_1.default.strictEqual(comment.followingNode, fn); | ||
var gap = lines.sliceString(comment.loc.end, gapEndPos); | ||
@@ -228,26 +200,22 @@ if (/\S/.test(gap)) { | ||
} | ||
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) { | ||
(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) { | ||
tiesToBreak.forEach(function (comment, i) { | ||
if (i < indexOfFirstLeadingComment) { | ||
addTrailingComment(pn, comment); | ||
} else { | ||
} | ||
else { | ||
addLeadingComment(fn, comment); | ||
} | ||
}); | ||
tiesToBreak.length = 0; | ||
} | ||
function addCommentHelper(node, comment) { | ||
@@ -257,3 +225,2 @@ var comments = node.comments || (node.comments = []); | ||
} | ||
function addLeadingComment(node, comment) { | ||
@@ -264,3 +231,2 @@ comment.leading = true; | ||
} | ||
function addDanglingComment(node, comment) { | ||
@@ -271,3 +237,2 @@ comment.leading = false; | ||
} | ||
function addTrailingComment(node, comment) { | ||
@@ -278,11 +243,8 @@ comment.leading = false; | ||
} | ||
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) { | ||
@@ -292,9 +254,5 @@ // When we print trailing comments as leading comments, we don't | ||
parts.push("\n"); | ||
} else if (lines instanceof Lines) { | ||
var trailingSpace = lines.slice( | ||
loc.end, | ||
lines.skipSpaces(loc.end) | ||
); | ||
} | ||
else if (lines instanceof lines_1.Lines) { | ||
var trailingSpace = lines.slice(loc.end, lines.skipSpaces(loc.end)); | ||
if (trailingSpace.length === 1) { | ||
@@ -304,3 +262,4 @@ // If the trailing space contains no newlines, then we want to | ||
parts.push(trailingSpace); | ||
} else { | ||
} | ||
else { | ||
// If the trailing space contains newlines, then replace it | ||
@@ -310,22 +269,17 @@ // with just that many newlines, with all other spaces removed. | ||
} | ||
} else { | ||
} | ||
else { | ||
parts.push("\n"); | ||
} | ||
return concat(parts); | ||
return lines_1.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) { | ||
if (lines instanceof lines_1.Lines) { | ||
var fromPos = lines.skipSpaces(loc.start, true) || lines.firstPos(); | ||
var leadingSpace = lines.slice(fromPos, loc.start); | ||
if (leadingSpace.length === 1) { | ||
@@ -335,3 +289,4 @@ // If the leading space contains no newlines, then we want to | ||
parts.push(leadingSpace); | ||
} else { | ||
} | ||
else { | ||
// If the leading space contains newlines, then replace it | ||
@@ -342,37 +297,32 @@ // with just that many newlines, sans all other spaces. | ||
} | ||
parts.push(print(commentPath)); | ||
return concat(parts); | ||
return lines_1.concat(parts); | ||
} | ||
exports.printComments = function(path, print) { | ||
function printComments(path, print) { | ||
var value = path.getValue(); | ||
var innerLines = print(path); | ||
var comments = n.Node.check(value) && | ||
types.getFieldValue(value, "comments"); | ||
types_1.default.getFieldValue(value, "comments"); | ||
if (!comments || comments.length === 0) { | ||
return innerLines; | ||
} | ||
var leadingParts = []; | ||
var trailingParts = [innerLines]; | ||
path.each(function(commentPath) { | ||
path.each(function (commentPath) { | ||
var comment = commentPath.getValue(); | ||
var leading = types.getFieldValue(comment, "leading"); | ||
var trailing = types.getFieldValue(comment, "trailing"); | ||
var leading = types_1.default.getFieldValue(comment, "leading"); | ||
var trailing = types_1.default.getFieldValue(comment, "trailing"); | ||
if (leading || (trailing && !(n.Statement.check(value) || | ||
comment.type === "Block" || | ||
comment.type === "CommentBlock"))) { | ||
comment.type === "Block" || | ||
comment.type === "CommentBlock"))) { | ||
leadingParts.push(printLeadingComment(commentPath, print)); | ||
} else if (trailing) { | ||
} | ||
else if (trailing) { | ||
trailingParts.push(printTrailingComment(commentPath, print)); | ||
} | ||
}, "comments"); | ||
leadingParts.push.apply(leadingParts, trailingParts); | ||
return concat(leadingParts); | ||
}; | ||
return lines_1.concat(leadingParts); | ||
} | ||
exports.printComments = printComments; | ||
; |
@@ -1,92 +0,89 @@ | ||
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; | ||
var util = require("./util.js"); | ||
function FastPath(value) { | ||
assert.ok(this instanceof FastPath); | ||
this.stack = [value]; | ||
} | ||
"use strict";; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var assert_1 = __importDefault(require("assert")); | ||
var types_1 = __importDefault(require("./types")); | ||
var n = types_1.default.namedTypes; | ||
var isArray = types_1.default.builtInTypes.array; | ||
var isNumber = types_1.default.builtInTypes.number; | ||
var util = __importStar(require("./util")); | ||
var FastPath = function FastPath(value) { | ||
assert_1.default.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. | ||
FastPath.from = function (obj) { | ||
if (obj instanceof FastPath) { | ||
// Return a defensive copy of any existing FastPath instances. | ||
return obj.copy(); | ||
} | ||
if (obj instanceof types_1.default.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); | ||
var stack = [obj.value]; | ||
for (var pp; (pp = obj.parentPath); obj = pp) | ||
stack.push(obj.name, pp.value); | ||
copy.stack = stack.reverse(); | ||
copy.stack = this.stack.slice(0); | ||
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; | ||
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]; | ||
var s = this.stack; | ||
return s[s.length - 1]; | ||
}; | ||
FPp.valueIsDuplicate = function () { | ||
var s = this.stack; | ||
var valueIndex = s.length - 1; | ||
return s.lastIndexOf(s[valueIndex], valueIndex - 1) >= 0; | ||
var s = this.stack; | ||
var valueIndex = s.length - 1; | ||
return s.lastIndexOf(s[valueIndex], valueIndex - 1) >= 0; | ||
}; | ||
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; | ||
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; | ||
return null; | ||
} | ||
FPp.getNode = function getNode(count) { | ||
return getNodeHelper(this, ~~count); | ||
if (count === void 0) { count = 0; } | ||
return getNodeHelper(this, ~~count); | ||
}; | ||
FPp.getParentNode = function getParentNode(count) { | ||
return getNodeHelper(this, ~~count + 1); | ||
if (count === void 0) { count = 0; } | ||
return getNodeHelper(this, ~~count + 1); | ||
}; | ||
// The length of the stack can be either even or odd, depending on whether | ||
@@ -98,9 +95,8 @@ // or not we have a name for the root value. The difference between the | ||
FPp.getRootValue = function getRootValue() { | ||
var s = this.stack; | ||
if (s.length % 2 === 0) { | ||
return s[1]; | ||
} | ||
return s[0]; | ||
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 | ||
@@ -111,17 +107,16 @@ // callback function onto this.stack, then call the callback with a | ||
// 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; | ||
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 | ||
@@ -131,57 +126,47 @@ // accessing this.getValue()[name1][name2]... should be array-like. The | ||
// 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; | ||
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); | ||
} | ||
} | ||
s.length = origLen; | ||
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; | ||
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); | ||
} | ||
} | ||
s.length = origLen; | ||
return result; | ||
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; | ||
}; | ||
// Returns true if the node at the tip of the path is wrapped with | ||
@@ -198,434 +183,362 @@ // parentheses, OR if the only reason the node needed parentheses was that | ||
FPp.hasParens = function () { | ||
const node = this.getNode(); | ||
const prevToken = this.getPrevToken(node); | ||
if (! prevToken) { | ||
return false; | ||
} | ||
const nextToken = this.getNextToken(node); | ||
if (! nextToken) { | ||
return false; | ||
} | ||
if (prevToken.value === "(") { | ||
if (nextToken.value === ")") { | ||
// If the node preceded by a `(` token and followed by a `)` token, | ||
// then of course it has parentheses. | ||
return true; | ||
var node = this.getNode(); | ||
var prevToken = this.getPrevToken(node); | ||
if (!prevToken) { | ||
return false; | ||
} | ||
// If this is one of the few Expression types that can't come first in | ||
// the enclosing statement because of parsing ambiguities (namely, | ||
// FunctionExpression, ObjectExpression, and ClassExpression) and | ||
// this.firstInStatement() returns true, and the node would not need | ||
// parentheses in an expression context because this.needsParens(true) | ||
// returns false, then it just needs an opening parenthesis to resolve | ||
// the parsing ambiguity that made it appear to need parentheses. | ||
const justNeedsOpeningParen = | ||
! this.canBeFirstInStatement() && | ||
this.firstInStatement() && | ||
! this.needsParens(true); | ||
if (justNeedsOpeningParen) { | ||
return true; | ||
var nextToken = this.getNextToken(node); | ||
if (!nextToken) { | ||
return false; | ||
} | ||
} | ||
return false; | ||
if (prevToken.value === "(") { | ||
if (nextToken.value === ")") { | ||
// If the node preceded by a `(` token and followed by a `)` token, | ||
// then of course it has parentheses. | ||
return true; | ||
} | ||
// If this is one of the few Expression types that can't come first in | ||
// the enclosing statement because of parsing ambiguities (namely, | ||
// FunctionExpression, ObjectExpression, and ClassExpression) and | ||
// this.firstInStatement() returns true, and the node would not need | ||
// parentheses in an expression context because this.needsParens(true) | ||
// returns false, then it just needs an opening parenthesis to resolve | ||
// the parsing ambiguity that made it appear to need parentheses. | ||
var justNeedsOpeningParen = !this.canBeFirstInStatement() && | ||
this.firstInStatement() && | ||
!this.needsParens(true); | ||
if (justNeedsOpeningParen) { | ||
return true; | ||
} | ||
} | ||
return false; | ||
}; | ||
FPp.getPrevToken = function (node) { | ||
node = node || this.getNode(); | ||
const loc = node && node.loc; | ||
const tokens = loc && loc.tokens; | ||
if (tokens && loc.start.token > 0) { | ||
const token = tokens[loc.start.token - 1]; | ||
if (token) { | ||
// Do not return tokens that fall outside the root subtree. | ||
const rootLoc = this.getRootValue().loc; | ||
if (util.comparePos(rootLoc.start, token.loc.start) <= 0) { | ||
return token; | ||
} | ||
node = node || this.getNode(); | ||
var loc = node && node.loc; | ||
var tokens = loc && loc.tokens; | ||
if (tokens && loc.start.token > 0) { | ||
var token = tokens[loc.start.token - 1]; | ||
if (token) { | ||
// Do not return tokens that fall outside the root subtree. | ||
var rootLoc = this.getRootValue().loc; | ||
if (util.comparePos(rootLoc.start, token.loc.start) <= 0) { | ||
return token; | ||
} | ||
} | ||
} | ||
} | ||
return null; | ||
return null; | ||
}; | ||
FPp.getNextToken = function (node) { | ||
node = node || this.getNode(); | ||
const loc = node && node.loc; | ||
const tokens = loc && loc.tokens; | ||
if (tokens && loc.end.token < tokens.length) { | ||
const token = tokens[loc.end.token]; | ||
if (token) { | ||
// Do not return tokens that fall outside the root subtree. | ||
const rootLoc = this.getRootValue().loc; | ||
if (util.comparePos(token.loc.end, rootLoc.end) <= 0) { | ||
return token; | ||
} | ||
node = node || this.getNode(); | ||
var loc = node && node.loc; | ||
var tokens = loc && loc.tokens; | ||
if (tokens && loc.end.token < tokens.length) { | ||
var token = tokens[loc.end.token]; | ||
if (token) { | ||
// Do not return tokens that fall outside the root subtree. | ||
var rootLoc = this.getRootValue().loc; | ||
if (util.comparePos(token.loc.end, rootLoc.end) <= 0) { | ||
return token; | ||
} | ||
} | ||
} | ||
} | ||
return null; | ||
return null; | ||
}; | ||
// Inspired by require("ast-types").NodePath.prototype.needsParens, but | ||
// more efficient because we're iterating backwards through a stack. | ||
FPp.needsParens = function(assumeExpressionContext) { | ||
var node = this.getNode(); | ||
// This needs to come before `if (!parent) { return false }` because | ||
// an object destructuring assignment requires parens for | ||
// correctness even when it's the topmost expression. | ||
if (node.type === "AssignmentExpression" && node.left.type === 'ObjectPattern') { | ||
return true; | ||
} | ||
var parent = this.getParentNode(); | ||
if (!parent) { | ||
return false; | ||
} | ||
var name = this.getName(); | ||
// 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) { | ||
FPp.needsParens = function (assumeExpressionContext) { | ||
var node = this.getNode(); | ||
// This needs to come before `if (!parent) { return false }` because | ||
// an object destructuring assignment requires parens for | ||
// correctness even when it's the topmost expression. | ||
if (node.type === "AssignmentExpression" && node.left.type === 'ObjectPattern') { | ||
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; | ||
var parent = this.getParentNode(); | ||
if (!parent) { | ||
return false; | ||
} | ||
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; | ||
var name = this.getName(); | ||
// 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; | ||
} | ||
case "IntersectionTypeAnnotation": | ||
case "UnionTypeAnnotation": | ||
return parent.type === "NullableTypeAnnotation"; | ||
case "Literal": | ||
return parent.type === "MemberExpression" | ||
&& isNumber.check(node.value) | ||
&& name === "object" | ||
&& parent.object === node; | ||
// Babel 6 Literal split | ||
case "NumericLiteral": | ||
return parent.type === "MemberExpression" | ||
&& 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": | ||
case "NewExpression": | ||
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; | ||
// Only statements don't need parentheses. | ||
if (n.Statement.check(node)) { | ||
return false; | ||
} | ||
case "ArrowFunctionExpression": | ||
if (n.CallExpression.check(parent) && | ||
name === 'callee') { | ||
return true; | ||
// Identifiers never need parentheses. | ||
if (node.type === "Identifier") { | ||
return false; | ||
} | ||
if (n.MemberExpression.check(parent) && | ||
name === 'object') { | ||
return true; | ||
if (parent.type === "ParenthesizedExpression") { | ||
return false; | ||
} | ||
return isBinary(parent); | ||
case "ObjectExpression": | ||
if (parent.type === "ArrowFunctionExpression" && | ||
name === "body") { | ||
return true; | ||
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_1.default.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; | ||
// Babel 6 Literal split | ||
case "NumericLiteral": | ||
return parent.type === "MemberExpression" | ||
&& 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": | ||
case "NewExpression": | ||
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; | ||
} | ||
break; | ||
case "CallExpression": | ||
if (name === "declaration" && | ||
n.ExportDefaultDeclaration.check(parent) && | ||
n.FunctionExpression.check(node.callee)) { | ||
return true; | ||
} | ||
} | ||
break; | ||
case "CallExpression": | ||
if (name === "declaration" && | ||
n.ExportDefaultDeclaration.check(parent) && | ||
n.FunctionExpression.check(node.callee)) { | ||
return true; | ||
if (parent.type === "NewExpression" && | ||
name === "callee" && | ||
parent.callee === node) { | ||
return containsCallExpression(node); | ||
} | ||
} | ||
if (parent.type === "NewExpression" && | ||
name === "callee" && | ||
parent.callee === node) { | ||
return containsCallExpression(node); | ||
} | ||
if (assumeExpressionContext !== true && | ||
!this.canBeFirstInStatement() && | ||
this.firstInStatement()) { | ||
return true; | ||
} | ||
return false; | ||
if (assumeExpressionContext !== true && | ||
!this.canBeFirstInStatement() && | ||
this.firstInStatement()) { | ||
return true; | ||
} | ||
return false; | ||
}; | ||
function isBinary(node) { | ||
return n.BinaryExpression.check(node) | ||
|| n.LogicalExpression.check(node); | ||
return n.BinaryExpression.check(node) | ||
|| n.LogicalExpression.check(node); | ||
} | ||
// @ts-ignore 'isUnaryLike' is declared but its value is never read. [6133] | ||
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)); | ||
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; | ||
}); | ||
["&&"], | ||
["|"], | ||
["^"], | ||
["&"], | ||
["==", "===", "!=", "!=="], | ||
["<", ">", "<=", ">=", "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(); | ||
if (n.FunctionExpression.check(node)) { | ||
return false; | ||
} | ||
if (n.ObjectExpression.check(node)) { | ||
return false; | ||
} | ||
if (n.ClassExpression.check(node)) { | ||
return false; | ||
} | ||
return true; | ||
}; | ||
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 (n.CallExpression.check(node)) { | ||
return true; | ||
} | ||
if (!parent || !child) { | ||
continue; | ||
if (isArray.check(node)) { | ||
return node.some(containsCallExpression); | ||
} | ||
if (n.BlockStatement.check(parent) && | ||
parentName === "body" && | ||
childName === 0) { | ||
assert.strictEqual(parent.body[0], child); | ||
return true; | ||
if (n.Node.check(node)) { | ||
return types_1.default.someField(node, function (_name, child) { | ||
return containsCallExpression(child); | ||
}); | ||
} | ||
if (n.ExpressionStatement.check(parent) && | ||
childName === "expression") { | ||
assert.strictEqual(parent.expression, child); | ||
return true; | ||
return false; | ||
} | ||
FPp.canBeFirstInStatement = function () { | ||
var node = this.getNode(); | ||
if (n.FunctionExpression.check(node)) { | ||
return false; | ||
} | ||
if (n.AssignmentExpression.check(parent) && | ||
childName === "left") { | ||
assert.strictEqual(parent.left, child); | ||
return true; | ||
if (n.ObjectExpression.check(node)) { | ||
return false; | ||
} | ||
if (n.ArrowFunctionExpression.check(parent) && | ||
childName === "body") { | ||
assert.strictEqual(parent.body, child); | ||
return true; | ||
if (n.ClassExpression.check(node)) { | ||
return false; | ||
} | ||
if (n.SequenceExpression.check(parent) && | ||
parentName === "expressions" && | ||
childName === 0) { | ||
assert.strictEqual(parent.expressions[0], child); | ||
continue; | ||
return true; | ||
}; | ||
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_1.default.strictEqual(parent.body[0], child); | ||
return true; | ||
} | ||
if (n.ExpressionStatement.check(parent) && | ||
childName === "expression") { | ||
assert_1.default.strictEqual(parent.expression, child); | ||
return true; | ||
} | ||
if (n.AssignmentExpression.check(parent) && | ||
childName === "left") { | ||
assert_1.default.strictEqual(parent.left, child); | ||
return true; | ||
} | ||
if (n.ArrowFunctionExpression.check(parent) && | ||
childName === "body") { | ||
assert_1.default.strictEqual(parent.body, child); | ||
return true; | ||
} | ||
if (n.SequenceExpression.check(parent) && | ||
parentName === "expressions" && | ||
childName === 0) { | ||
assert_1.default.strictEqual(parent.expressions[0], child); | ||
continue; | ||
} | ||
if (n.CallExpression.check(parent) && | ||
childName === "callee") { | ||
assert_1.default.strictEqual(parent.callee, child); | ||
continue; | ||
} | ||
if (n.MemberExpression.check(parent) && | ||
childName === "object") { | ||
assert_1.default.strictEqual(parent.object, child); | ||
continue; | ||
} | ||
if (n.ConditionalExpression.check(parent) && | ||
childName === "test") { | ||
assert_1.default.strictEqual(parent.test, child); | ||
continue; | ||
} | ||
if (isBinary(parent) && | ||
childName === "left") { | ||
assert_1.default.strictEqual(parent.left, child); | ||
continue; | ||
} | ||
if (n.UnaryExpression.check(parent) && | ||
!parent.prefix && | ||
childName === "argument") { | ||
assert_1.default.strictEqual(parent.argument, child); | ||
continue; | ||
} | ||
return false; | ||
} | ||
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; | ||
return true; | ||
}; | ||
exports.default = FastPath; | ||
module.exports = exports["default"]; |
1427
lib/lines.js
@@ -1,10 +0,15 @@ | ||
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"); | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var assert_1 = __importDefault(require("assert")); | ||
var source_map_1 = __importDefault(require("source-map")); | ||
var options_1 = require("./options"); | ||
var private_1 = require("private"); | ||
var secretKey = private_1.makeUniqueKey(); | ||
var types_1 = __importDefault(require("./types")); | ||
var isString = types_1.default.builtInTypes.string; | ||
var util_1 = require("./util"); | ||
var mapping_1 = __importDefault(require("./mapping")); | ||
// Goals: | ||
@@ -16,118 +21,95 @@ // 1. Minimize new string creation. | ||
// 5. No newline characters. | ||
var useSymbol = typeof Symbol === "function"; | ||
// @ts-ignore Subsequent variable declarations must have the same type. | ||
var secretKey = "recastLinesSecret"; | ||
if (useSymbol) { | ||
secretKey = Symbol.for(secretKey); | ||
secretKey = Symbol.for(secretKey); | ||
} | ||
function getSecret(lines) { | ||
return lines[secretKey]; | ||
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; | ||
} | ||
setSymbolOrKey(this, secretKey, { | ||
infos: infos, | ||
mappings: [], | ||
name: sourceFileName, | ||
cachedSourceMap: null | ||
}); | ||
this.length = infos.length; | ||
this.name = sourceFileName; | ||
if (sourceFileName) { | ||
getSecret(this).mappings.push(new Mapping(this, { | ||
start: this.firstPos(), | ||
end: this.lastPos() | ||
})); | ||
} | ||
} | ||
var Lines = function Lines(infos, sourceFileName) { | ||
assert_1.default.ok(this instanceof Lines); | ||
assert_1.default.ok(infos.length > 0); | ||
if (sourceFileName) { | ||
isString.assert(sourceFileName); | ||
} | ||
else { | ||
sourceFileName = null; | ||
} | ||
setSymbolOrKey(this, secretKey, { | ||
infos: infos, | ||
mappings: [], | ||
name: sourceFileName, | ||
cachedSourceMap: null | ||
}); | ||
this.length = infos.length; | ||
this.name = sourceFileName; | ||
if (sourceFileName) { | ||
getSecret(this).mappings.push(new mapping_1.default(this, { | ||
start: this.firstPos(), | ||
end: this.lastPos() | ||
})); | ||
} | ||
}; | ||
exports.Lines = Lines; | ||
function setSymbolOrKey(obj, key, value) { | ||
if (useSymbol) { | ||
return obj[key] = value; | ||
} | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: false, | ||
writable: false, | ||
configurable: true | ||
}); | ||
return value; | ||
if (useSymbol) { | ||
return obj[key] = value; | ||
} | ||
Object.defineProperty(obj, key, { | ||
value: value, | ||
enumerable: false, | ||
writable: false, | ||
configurable: true | ||
}); | ||
return value; | ||
} | ||
// Exposed for instanceof checks. The fromString function should be used | ||
// to create new Lines objects. | ||
exports.Lines = Lines; | ||
var Lp = Lines.prototype; | ||
function copyLineInfo(info) { | ||
return { | ||
line: info.line, | ||
indent: info.indent, | ||
locked: info.locked, | ||
sliceStart: info.sliceStart, | ||
sliceEnd: info.sliceEnd | ||
}; | ||
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; | ||
var count = 0; | ||
var len = spaces.length; | ||
for (var i = 0; i < len; ++i) { | ||
switch (spaces.charCodeAt(i)) { | ||
case 9: // '\t' | ||
assert_1.default.strictEqual(typeof tabWidth, "number"); | ||
assert_1.default.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; | ||
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/; | ||
var lineTerminatorSeqExp = /\u000D\u000A|\u000D(?!\u000A)|\u000A|\u2028|\u2029/; | ||
/** | ||
@@ -137,322 +119,235 @@ * @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; | ||
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_1.default.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) { | ||
// TODO: handle null exec result | ||
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 | ||
}; | ||
}), options_1.normalize(options).sourceFileName); | ||
if (cacheable) | ||
fromStringCache[string] = lines; | ||
return lines; | ||
} | ||
exports.fromString = fromString; | ||
function isOnlyWhitespace(string) { | ||
return !/\S/.test(string); | ||
return !/\S/.test(string); | ||
} | ||
Lp.toString = function(options) { | ||
return this.sliceString(this.firstPos(), this.lastPos(), options); | ||
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; | ||
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; | ||
} | ||
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, | ||
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 source_map_1.default.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 (util_1.comparePos(sourceCursor, mapping.sourceLoc.end) < 0 && | ||
util_1.comparePos(targetCursor, mapping.targetLoc.end) < 0) { | ||
var sourceChar = mapping.sourceLines.charAt(sourceCursor); | ||
var targetChar = targetLines.charAt(targetCursor); | ||
assert_1.default.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(); | ||
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.bootstrapCharAt = function (pos) { | ||
assert_1.default.strictEqual(typeof pos, "object"); | ||
assert_1.default.strictEqual(typeof pos.line, "number"); | ||
assert_1.default.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.charAt = function (pos) { | ||
assert_1.default.strictEqual(typeof pos, "object"); | ||
assert_1.default.strictEqual(typeof pos.line, "number"); | ||
assert_1.default.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); | ||
Lp.stripMargin = function (width, skipFirstLine) { | ||
if (width === 0) | ||
return this; | ||
assert_1.default.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_1.default.strictEqual(newMappings.length, 0); | ||
secret.mappings.forEach(function (mapping) { | ||
newMappings.push(mapping.indent(width, skipFirstLine, true)); | ||
}); | ||
} | ||
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; | ||
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; | ||
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_1.default.strictEqual(newMappings.length, 0); | ||
secret.mappings.forEach(function (mapping) { | ||
newMappings.push(mapping.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; | ||
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; | ||
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_1.default.strictEqual(newMappings.length, 0); | ||
secret.mappings.forEach(function (mapping) { | ||
newMappings.push(mapping.indent(by, true)); | ||
}); | ||
} | ||
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; | ||
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; | ||
})); | ||
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.getIndentAt = function (line) { | ||
assert_1.default.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; | ||
Lp.guessTabWidth = function () { | ||
var secret = getSecret(this); | ||
if (hasOwn.call(secret, "cachedTabWidth")) { | ||
return secret.cachedTabWidth; | ||
} | ||
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; | ||
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; | ||
} | ||
} | ||
return secret.cachedTabWidth = result; | ||
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 // | ||
@@ -462,442 +357,358 @@ // or /* comment. If this is the case, the code may need to be wrapped in | ||
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) === "/*"; | ||
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.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.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.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; | ||
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, false, true) | ||
: true; | ||
} | ||
if (l < this.length) { | ||
pos.line += 1; | ||
pos.column = 0; | ||
return skipSpaces | ||
? !!this.skipSpaces(pos, false, true) | ||
: true; | ||
} | ||
return false; | ||
? !!this.skipSpaces(pos, true, true) | ||
: true; | ||
}; | ||
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.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.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; | ||
} | ||
Lp.skipSpaces = function (pos, backward, modifyInPlace) { | ||
if (pos) { | ||
pos = modifyInPlace ? pos : { | ||
line: pos.line, | ||
column: pos.column | ||
}; | ||
} | ||
return null; | ||
} else { | ||
while (isOnlyWhitespace(this.charAt(pos))) { | ||
if (!this.nextPos(pos)) { | ||
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; | ||
} | ||
} | ||
return pos; | ||
} | ||
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.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.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.trim = function () { | ||
var start = this.skipSpaces(this.firstPos(), false, true); | ||
if (start === null) | ||
return emptyLines; | ||
var end = this.skipSpaces(this.lastPos(), true, true); | ||
assert_1.default.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.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.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) { | ||
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(); | ||
} | ||
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; | ||
throw new Error("cannot slice with end but not start"); | ||
} | ||
// 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; | ||
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_1.default.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_1.default.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; | ||
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; | ||
} | ||
// 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); | ||
startCol = Math.max(startCol, 0); | ||
endCol = Math.min(endCol, lineLength); | ||
endCol = Math.max(endCol, startCol); | ||
if (endCol < indent) { | ||
indent = endCol; | ||
sliceEnd = sliceStart; | ||
} | ||
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; | ||
else { | ||
sliceEnd -= lineLength - endCol; | ||
} | ||
var tabs = 0; | ||
var spaces = indent; | ||
if (options.useTabs) { | ||
tabs = Math.floor(indent / tabWidth); | ||
spaces -= tabs * tabWidth; | ||
lineLength = endCol; | ||
lineLength -= startCol; | ||
if (startCol < indent) { | ||
indent -= startCol; | ||
} | ||
var result = ""; | ||
if (tabs > 0) { | ||
result += new Array(tabs + 1).join("\t"); | ||
else { | ||
startCol -= indent; | ||
indent = 0; | ||
sliceStart += startCol; | ||
} | ||
if (spaces > 0) { | ||
result += new Array(spaces + 1).join(" "); | ||
assert_1.default.ok(indent >= 0); | ||
assert_1.default.ok(sliceStart <= sliceEnd); | ||
assert_1.default.strictEqual(lineLength, indent + sliceEnd - sliceStart); | ||
if (info.indent === indent && | ||
info.sliceStart === sliceStart && | ||
info.sliceEnd === sliceEnd) { | ||
return info; | ||
} | ||
result += info.line.slice(info.sliceStart, info.sliceEnd); | ||
parts.push(result); | ||
} | ||
return parts.join(options.lineTerminator); | ||
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.isEmpty = function() { | ||
return this.length < 2 && this.getLineLength(1) < 1; | ||
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 = options_1.normalize(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.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)); | ||
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); | ||
} | ||
}); | ||
} | ||
} 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; | ||
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); | ||
function concat(elements) { | ||
return emptyLines.join(elements); | ||
} | ||
exports.concat = concat; | ||
; | ||
Lp.concat = function () { | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
var list = [this]; | ||
list.push.apply(list, args); | ||
assert_1.default.strictEqual(list.length, args.length + 1); | ||
return emptyLines.join(list); | ||
}; | ||
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(""); |
@@ -1,15 +0,24 @@ | ||
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); | ||
"use strict";; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var assert_1 = __importDefault(require("assert")); | ||
var types_1 = __importDefault(require("./types")); | ||
var isNumber = types_1.default.builtInTypes.number; | ||
var SourceLocation = types_1.default.namedTypes.SourceLocation; | ||
var Position = types_1.default.namedTypes.Position; | ||
var linesModule = __importStar(require("./lines")); | ||
var util_1 = require("./util"); | ||
var Mapping = function Mapping(sourceLines, sourceLoc, targetLoc) { | ||
assert_1.default.ok(this instanceof Mapping); | ||
assert_1.default.ok(sourceLines instanceof linesModule.Lines); | ||
SourceLocation.assert(sourceLoc); | ||
if (targetLoc) { | ||
@@ -19,13 +28,11 @@ // In certain cases it's possible for targetLoc.{start,end}.column | ||
// valid SourceLocation nodes, so we need to be more forgiving. | ||
assert.ok( | ||
isNumber.check(targetLoc.start.line) && | ||
assert_1.default.ok(isNumber.check(targetLoc.start.line) && | ||
isNumber.check(targetLoc.start.column) && | ||
isNumber.check(targetLoc.end.line) && | ||
isNumber.check(targetLoc.end.column) | ||
); | ||
} else { | ||
isNumber.check(targetLoc.end.column)); | ||
} | ||
else { | ||
// Assume identity mapping if no targetLoc specified. | ||
targetLoc = sourceLoc; | ||
} | ||
Object.defineProperties(this, { | ||
@@ -36,21 +43,17 @@ sourceLines: { value: sourceLines }, | ||
}); | ||
} | ||
}; | ||
var Mp = Mapping.prototype; | ||
module.exports = Mapping; | ||
Mp.slice = function(lines, start, end) { | ||
assert.ok(lines instanceof linesModule.Lines); | ||
exports.default = Mapping; | ||
Mp.slice = function (lines, start, end) { | ||
assert_1.default.ok(lines instanceof linesModule.Lines); | ||
Position.assert(start); | ||
if (end) { | ||
Position.assert(end); | ||
} else { | ||
} | ||
else { | ||
end = lines.lastPos(); | ||
} | ||
var sourceLines = this.sourceLines; | ||
var sourceLoc = this.sourceLoc; | ||
var targetLoc = this.targetLoc; | ||
function skip(name) { | ||
@@ -60,17 +63,12 @@ var sourceFromPos = sourceLoc[name]; | ||
var targetToPos = start; | ||
if (name === "end") { | ||
targetToPos = end; | ||
} else { | ||
assert.strictEqual(name, "start"); | ||
} | ||
return skipChars( | ||
sourceLines, sourceFromPos, | ||
lines, targetFromPos, targetToPos | ||
); | ||
else { | ||
assert_1.default.strictEqual(name, "start"); | ||
} | ||
return skipChars(sourceLines, sourceFromPos, lines, targetFromPos, targetToPos); | ||
} | ||
if (comparePos(start, targetLoc.start) <= 0) { | ||
if (comparePos(targetLoc.end, end) <= 0) { | ||
if (util_1.comparePos(start, targetLoc.start) <= 0) { | ||
if (util_1.comparePos(targetLoc.end, end) <= 0) { | ||
targetLoc = { | ||
@@ -80,10 +78,9 @@ start: subtractPos(targetLoc.start, 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) { | ||
} | ||
else if (util_1.comparePos(end, targetLoc.start) <= 0) { | ||
return null; | ||
} else { | ||
} | ||
else { | ||
sourceLoc = { | ||
@@ -93,3 +90,2 @@ start: sourceLoc.start, | ||
}; | ||
targetLoc = { | ||
@@ -100,9 +96,8 @@ start: subtractPos(targetLoc.start, start.line, start.column), | ||
} | ||
} else { | ||
if (comparePos(targetLoc.end, start) <= 0) { | ||
} | ||
else { | ||
if (util_1.comparePos(targetLoc.end, start) <= 0) { | ||
return null; | ||
} | ||
if (comparePos(targetLoc.end, end) <= 0) { | ||
if (util_1.comparePos(targetLoc.end, end) <= 0) { | ||
sourceLoc = { | ||
@@ -112,3 +107,2 @@ start: skip("start"), | ||
}; | ||
targetLoc = { | ||
@@ -119,4 +113,4 @@ // Same as subtractPos(start, start.line, start.column): | ||
}; | ||
} else { | ||
} | ||
else { | ||
sourceLoc = { | ||
@@ -126,3 +120,2 @@ start: skip("start"), | ||
}; | ||
targetLoc = { | ||
@@ -135,7 +128,5 @@ // Same as subtractPos(start, start.line, start.column): | ||
} | ||
return new Mapping(this.sourceLines, sourceLoc, targetLoc); | ||
}; | ||
Mp.add = function(line, column) { | ||
Mp.add = function (line, column) { | ||
return new Mapping(this.sourceLines, this.sourceLoc, { | ||
@@ -146,3 +137,2 @@ start: addPos(this.targetLoc.start, line, column), | ||
}; | ||
function addPos(toPos, line, column) { | ||
@@ -156,4 +146,3 @@ return { | ||
} | ||
Mp.subtract = function(line, column) { | ||
Mp.subtract = function (line, column) { | ||
return new Mapping(this.sourceLines, this.sourceLoc, { | ||
@@ -164,3 +153,2 @@ start: subtractPos(this.targetLoc.start, line, column), | ||
}; | ||
function subtractPos(fromPos, line, column) { | ||
@@ -174,16 +162,12 @@ return { | ||
} | ||
Mp.indent = function(by, skipFirstLine, noNegativeColumns) { | ||
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 = { | ||
@@ -193,3 +177,2 @@ start: targetLoc.start, | ||
}; | ||
if (!skipFirstLine || startLine > 1) { | ||
@@ -204,3 +187,2 @@ var startColumn = targetLoc.start.column + by; | ||
} | ||
if (!skipFirstLine || endLine > 1) { | ||
@@ -215,17 +197,11 @@ var endColumn = targetLoc.end.column + by; | ||
} | ||
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); | ||
function skipChars(sourceLines, sourceFromPos, targetLines, targetFromPos, targetToPos) { | ||
assert_1.default.ok(sourceLines instanceof linesModule.Lines); | ||
assert_1.default.ok(targetLines instanceof linesModule.Lines); | ||
Position.assert(sourceFromPos); | ||
Position.assert(targetFromPos); | ||
Position.assert(targetToPos); | ||
var targetComparison = comparePos(targetFromPos, targetToPos); | ||
var targetComparison = util_1.comparePos(targetFromPos, targetToPos); | ||
if (targetComparison === 0) { | ||
@@ -235,13 +211,9 @@ // Trivial case: no characters to skip. | ||
} | ||
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) { | ||
@@ -252,25 +224,19 @@ // If jumping to later lines, reset columns to the beginnings | ||
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 { | ||
assert_1.default.strictEqual(lineDiff, 0); | ||
} | ||
} else { | ||
while (util_1.comparePos(targetCursor, targetToPos) < 0 && | ||
targetLines.nextPos(targetCursor, true)) { | ||
assert_1.default.ok(sourceLines.nextPos(sourceCursor, true)); | ||
assert_1.default.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) { | ||
@@ -281,17 +247,14 @@ // If jumping to earlier lines, reset columns to the ends of | ||
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) | ||
); | ||
else { | ||
assert_1.default.strictEqual(lineDiff, 0); | ||
} | ||
while (util_1.comparePos(targetToPos, targetCursor) < 0 && | ||
targetLines.prevPos(targetCursor, true)) { | ||
assert_1.default.ok(sourceLines.prevPos(sourceCursor, true)); | ||
assert_1.default.strictEqual(sourceLines.charAt(sourceCursor), targetLines.charAt(targetCursor)); | ||
} | ||
} | ||
return sourceCursor; | ||
} | ||
module.exports = exports["default"]; |
@@ -0,130 +1,55 @@ | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
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("../parsers/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 || "\n", | ||
// 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, use double quotes. | ||
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, | ||
// Whether to return an array of .tokens on the root AST node. | ||
tokens: true | ||
parser: require("../parsers/esprima"), | ||
tabWidth: 4, | ||
useTabs: false, | ||
reuseWhitespace: true, | ||
lineTerminator: require("os").EOL || "\n", | ||
wrapColumn: 74, | ||
sourceFileName: null, | ||
sourceMapName: null, | ||
sourceRoot: null, | ||
inputSourceMap: null, | ||
range: false, | ||
tolerant: true, | ||
quote: null, | ||
trailingComma: false, | ||
arrayBracketSpacing: false, | ||
objectCurlySpacing: true, | ||
arrowParensAlways: false, | ||
flowObjectCommas: true, | ||
tokens: 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"), | ||
tokens: !!get("tokens") | ||
}; | ||
}; | ||
function normalize(opts) { | ||
var options = opts || 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"), | ||
tokens: !!get("tokens") | ||
}; | ||
} | ||
exports.normalize = normalize; | ||
; |
"use strict"; | ||
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); | ||
const lines = fromString(source, options); | ||
const sourceWithoutTabs = lines.toString({ | ||
tabWidth: options.tabWidth, | ||
reuseWhitespace: false, | ||
useTabs: false | ||
}); | ||
let comments = []; | ||
const ast = options.parser.parse(sourceWithoutTabs, { | ||
jsx: true, | ||
loc: true, | ||
locations: true, | ||
range: options.range, | ||
comment: true, | ||
onComment: comments, | ||
tolerant: util.getOption(options, "tolerant", true), | ||
ecmaVersion: 6, | ||
sourceType: util.getOption(options, "sourceType", "module") | ||
}); | ||
// Use ast.tokens if possible, and otherwise fall back to the Esprima | ||
// tokenizer. All the preconfigured ../parsers/* expose ast.tokens | ||
// automatically, but custom parsers might need additional configuration | ||
// to avoid this fallback. | ||
const tokens = Array.isArray(ast.tokens) | ||
? ast.tokens | ||
: require("esprima").tokenize(sourceWithoutTabs, { | ||
loc: true | ||
}); | ||
// We will reattach the tokens array to the file object below. | ||
delete ast.tokens; | ||
// Make sure every token has a token.value string. | ||
tokens.forEach(function (token) { | ||
if (typeof token.value !== "string") { | ||
token.value = lines.sliceString(token.loc.start, token.loc.end); | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var assert_1 = __importDefault(require("assert")); | ||
var types_1 = __importDefault(require("./types")); | ||
var b = types_1.default.builders; | ||
var isObject = types_1.default.builtInTypes.object; | ||
var isArray = types_1.default.builtInTypes.array; | ||
var options_1 = require("./options"); | ||
var lines_1 = require("./lines"); | ||
var comments_1 = require("./comments"); | ||
var util = __importStar(require("./util")); | ||
function parse(source, options) { | ||
options = options_1.normalize(options); | ||
var lines = lines_1.fromString(source, options); | ||
var sourceWithoutTabs = lines.toString({ | ||
tabWidth: options.tabWidth, | ||
reuseWhitespace: false, | ||
useTabs: false | ||
}); | ||
var comments = []; | ||
var ast = options.parser.parse(sourceWithoutTabs, { | ||
jsx: true, | ||
loc: true, | ||
locations: true, | ||
range: options.range, | ||
comment: true, | ||
onComment: comments, | ||
tolerant: util.getOption(options, "tolerant", true), | ||
ecmaVersion: 6, | ||
sourceType: util.getOption(options, "sourceType", "module") | ||
}); | ||
// Use ast.tokens if possible, and otherwise fall back to the Esprima | ||
// tokenizer. All the preconfigured ../parsers/* expose ast.tokens | ||
// automatically, but custom parsers might need additional configuration | ||
// to avoid this fallback. | ||
var tokens = Array.isArray(ast.tokens) | ||
? ast.tokens | ||
: require("esprima").tokenize(sourceWithoutTabs, { | ||
loc: true | ||
}); | ||
// We will reattach the tokens array to the file object below. | ||
delete ast.tokens; | ||
// Make sure every token has a token.value string. | ||
tokens.forEach(function (token) { | ||
if (typeof token.value !== "string") { | ||
token.value = lines.sliceString(token.loc.start, token.loc.end); | ||
} | ||
}); | ||
if (Array.isArray(ast.comments)) { | ||
comments = ast.comments; | ||
delete ast.comments; | ||
} | ||
}); | ||
if (Array.isArray(ast.comments)) { | ||
comments = ast.comments; | ||
delete ast.comments; | ||
} | ||
if (ast.loc) { | ||
// If the source was empty, some parsers give loc.{start,end}.line | ||
// values of 0, instead of the minimum of 1. | ||
util.fixFaultyLocations(ast, lines); | ||
} else { | ||
ast.loc = { | ||
start: lines.firstPos(), | ||
end: lines.lastPos() | ||
}; | ||
} | ||
ast.loc.lines = lines; | ||
ast.loc.indent = 0; | ||
let file; | ||
let program; | ||
if (ast.type === "Program") { | ||
program = ast; | ||
// In order to ensure we reprint leading and trailing program | ||
// comments, wrap the original Program node with a File node. Only | ||
// ESTree parsers (Acorn and Esprima) return a Program as the root AST | ||
// node. Most other (Babylon-like) parsers return a File. | ||
file = b.file(ast, options.sourceFileName || null); | ||
file.loc = { | ||
start: lines.firstPos(), | ||
end: lines.lastPos(), | ||
lines: lines, | ||
indent: 0 | ||
}; | ||
} else if (ast.type === "File") { | ||
file = ast; | ||
program = file.program; | ||
} | ||
// Expose file.tokens unless the caller passed false for options.tokens. | ||
if (options.tokens) { | ||
file.tokens = tokens; | ||
} | ||
// Expand the Program's .loc to include all comments (not just those | ||
// attached to the Program node, as its children may have comments as | ||
// well), since sometimes program.loc.{start,end} will coincide with the | ||
// .loc.{start,end} of the first and last *statements*, mistakenly | ||
// excluding comments that fall outside that region. | ||
var trueProgramLoc = util.getTrueLoc({ | ||
type: program.type, | ||
loc: program.loc, | ||
body: [], | ||
comments | ||
}, lines); | ||
program.loc.start = trueProgramLoc.start; | ||
program.loc.end = trueProgramLoc.end; | ||
// 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, tokens).copy(file); | ||
if (ast.loc) { | ||
// If the source was empty, some parsers give loc.{start,end}.line | ||
// values of 0, instead of the minimum of 1. | ||
util.fixFaultyLocations(ast, lines); | ||
} | ||
else { | ||
ast.loc = { | ||
start: lines.firstPos(), | ||
end: lines.lastPos() | ||
}; | ||
} | ||
ast.loc.lines = lines; | ||
ast.loc.indent = 0; | ||
var file; | ||
var program; | ||
if (ast.type === "Program") { | ||
program = ast; | ||
// In order to ensure we reprint leading and trailing program | ||
// comments, wrap the original Program node with a File node. Only | ||
// ESTree parsers (Acorn and Esprima) return a Program as the root AST | ||
// node. Most other (Babylon-like) parsers return a File. | ||
file = b.file(ast, options.sourceFileName || null); | ||
file.loc = { | ||
start: lines.firstPos(), | ||
end: lines.lastPos(), | ||
lines: lines, | ||
indent: 0 | ||
}; | ||
} | ||
else if (ast.type === "File") { | ||
file = ast; | ||
program = file.program; | ||
} | ||
// Expose file.tokens unless the caller passed false for options.tokens. | ||
if (options.tokens) { | ||
file.tokens = tokens; | ||
} | ||
// Expand the Program's .loc to include all comments (not just those | ||
// attached to the Program node, as its children may have comments as | ||
// well), since sometimes program.loc.{start,end} will coincide with the | ||
// .loc.{start,end} of the first and last *statements*, mistakenly | ||
// excluding comments that fall outside that region. | ||
var trueProgramLoc = util.getTrueLoc({ | ||
type: program.type, | ||
loc: program.loc, | ||
body: [], | ||
comments: comments | ||
}, lines); | ||
program.loc.start = trueProgramLoc.start; | ||
program.loc.end = trueProgramLoc.end; | ||
// Passing file.program here instead of just file means that initial | ||
// comments will be attached to program.body[0] instead of program. | ||
comments_1.attach(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, tokens).copy(file); | ||
} | ||
exports.parse = parse; | ||
; | ||
var TreeCopier = function TreeCopier(lines, tokens) { | ||
assert_1.default.ok(this instanceof TreeCopier); | ||
this.lines = lines; | ||
this.tokens = tokens; | ||
this.startTokenIndex = 0; | ||
this.endTokenIndex = tokens.length; | ||
this.indent = 0; | ||
this.seen = new Map; | ||
}; | ||
function TreeCopier(lines, tokens) { | ||
assert.ok(this instanceof TreeCopier); | ||
this.lines = lines; | ||
this.tokens = tokens; | ||
this.startTokenIndex = 0; | ||
this.endTokenIndex = tokens.length; | ||
this.indent = 0; | ||
this.seen = new Map; | ||
} | ||
var TCp = TreeCopier.prototype; | ||
TCp.copy = function(node) { | ||
if (this.seen.has(node)) { | ||
return this.seen.get(node); | ||
} | ||
if (isArray.check(node)) { | ||
var copy = new Array(node.length); | ||
TCp.copy = function (node) { | ||
if (this.seen.has(node)) { | ||
return this.seen.get(node); | ||
} | ||
if (isArray.check(node)) { | ||
var copy = new Array(node.length); | ||
this.seen.set(node, copy); | ||
node.forEach(function (item, i) { | ||
copy[i] = this.copy(item); | ||
}, this); | ||
return copy; | ||
} | ||
if (!isObject.check(node)) { | ||
return node; | ||
} | ||
util.fixFaultyLocations(node, this.lines); | ||
var copy = Object.create(Object.getPrototypeOf(node), { | ||
original: { | ||
value: node, | ||
configurable: false, | ||
enumerable: false, | ||
writable: true | ||
} | ||
}); | ||
this.seen.set(node, copy); | ||
node.forEach(function (item, i) { | ||
copy[i] = this.copy(item); | ||
}, this); | ||
return copy; | ||
} | ||
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; | ||
var oldStartTokenIndex = this.startTokenIndex; | ||
var oldEndTokenIndex = this.endTokenIndex; | ||
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; | ||
} | ||
// Every node.loc has a reference to the original source lines as well | ||
// as a complete list of source tokens. | ||
loc.lines = this.lines; | ||
loc.tokens = this.tokens; | ||
loc.indent = newIndent; | ||
// Set loc.start.token and loc.end.token such that | ||
// loc.tokens.slice(loc.start.token, loc.end.token) returns a list of | ||
// all the tokens that make up this node. | ||
this.findTokenRange(loc); | ||
} | ||
}); | ||
this.seen.set(node, copy); | ||
var loc = node.loc; | ||
var oldIndent = this.indent; | ||
var newIndent = oldIndent; | ||
const oldStartTokenIndex = this.startTokenIndex; | ||
const oldEndTokenIndex = this.endTokenIndex; | ||
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; | ||
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]); | ||
} | ||
} | ||
// Every node.loc has a reference to the original source lines as well | ||
// as a complete list of source tokens. | ||
loc.lines = this.lines; | ||
loc.tokens = this.tokens; | ||
loc.indent = newIndent; | ||
// Set loc.start.token and loc.end.token such that | ||
// loc.tokens.slice(loc.start.token, loc.end.token) returns a list of | ||
// all the tokens that make up this node. | ||
this.findTokenRange(loc); | ||
} | ||
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; | ||
this.startTokenIndex = oldStartTokenIndex; | ||
this.endTokenIndex = oldEndTokenIndex; | ||
return copy; | ||
this.indent = oldIndent; | ||
this.startTokenIndex = oldStartTokenIndex; | ||
this.endTokenIndex = oldEndTokenIndex; | ||
return copy; | ||
}; | ||
// If we didn't have any idea where in loc.tokens to look for tokens | ||
@@ -234,45 +211,48 @@ // contained by this loc, a binary search would be appropriate, but | ||
TCp.findTokenRange = function (loc) { | ||
// In the unlikely event that loc.tokens[this.startTokenIndex] starts | ||
// *after* loc.start, we need to rewind this.startTokenIndex first. | ||
while (this.startTokenIndex > 0) { | ||
const token = loc.tokens[this.startTokenIndex]; | ||
if (util.comparePos(loc.start, token.loc.start) < 0) { | ||
--this.startTokenIndex; | ||
} else break; | ||
} | ||
// In the unlikely event that loc.tokens[this.endTokenIndex - 1] ends | ||
// *before* loc.end, we need to fast-forward this.endTokenIndex first. | ||
while (this.endTokenIndex < loc.tokens.length) { | ||
const token = loc.tokens[this.endTokenIndex]; | ||
if (util.comparePos(token.loc.end, loc.end) < 0) { | ||
++this.endTokenIndex; | ||
} else break; | ||
} | ||
// Increment this.startTokenIndex until we've found the first token | ||
// contained by this node. | ||
while (this.startTokenIndex < this.endTokenIndex) { | ||
const token = loc.tokens[this.startTokenIndex]; | ||
if (util.comparePos(token.loc.start, loc.start) < 0) { | ||
++this.startTokenIndex; | ||
} else break; | ||
} | ||
// Index into loc.tokens of the first token within this node. | ||
loc.start.token = this.startTokenIndex; | ||
// Decrement this.endTokenIndex until we've found the first token after | ||
// this node (not contained by the node). | ||
while (this.endTokenIndex > this.startTokenIndex) { | ||
const token = loc.tokens[this.endTokenIndex - 1]; | ||
if (util.comparePos(loc.end, token.loc.end) < 0) { | ||
--this.endTokenIndex; | ||
} else break; | ||
} | ||
// Index into loc.tokens of the first token *after* this node. | ||
// If loc.start.token === loc.end.token, the node contains no tokens, | ||
// and the index is that of the next token following this node. | ||
loc.end.token = this.endTokenIndex; | ||
// In the unlikely event that loc.tokens[this.startTokenIndex] starts | ||
// *after* loc.start, we need to rewind this.startTokenIndex first. | ||
while (this.startTokenIndex > 0) { | ||
var token = loc.tokens[this.startTokenIndex]; | ||
if (util.comparePos(loc.start, token.loc.start) < 0) { | ||
--this.startTokenIndex; | ||
} | ||
else | ||
break; | ||
} | ||
// In the unlikely event that loc.tokens[this.endTokenIndex - 1] ends | ||
// *before* loc.end, we need to fast-forward this.endTokenIndex first. | ||
while (this.endTokenIndex < loc.tokens.length) { | ||
var token = loc.tokens[this.endTokenIndex]; | ||
if (util.comparePos(token.loc.end, loc.end) < 0) { | ||
++this.endTokenIndex; | ||
} | ||
else | ||
break; | ||
} | ||
// Increment this.startTokenIndex until we've found the first token | ||
// contained by this node. | ||
while (this.startTokenIndex < this.endTokenIndex) { | ||
var token = loc.tokens[this.startTokenIndex]; | ||
if (util.comparePos(token.loc.start, loc.start) < 0) { | ||
++this.startTokenIndex; | ||
} | ||
else | ||
break; | ||
} | ||
// Index into loc.tokens of the first token within this node. | ||
loc.start.token = this.startTokenIndex; | ||
// Decrement this.endTokenIndex until we've found the first token after | ||
// this node (not contained by the node). | ||
while (this.endTokenIndex > this.startTokenIndex) { | ||
var token = loc.tokens[this.endTokenIndex - 1]; | ||
if (util.comparePos(loc.end, token.loc.end) < 0) { | ||
--this.endTokenIndex; | ||
} | ||
else | ||
break; | ||
} | ||
// Index into loc.tokens of the first token *after* this node. | ||
// If loc.start.token === loc.end.token, the node contains no tokens, | ||
// and the index is that of the next token following this node. | ||
loc.end.token = this.endTokenIndex; | ||
}; |
@@ -1,111 +0,96 @@ | ||
var assert = require("assert"); | ||
var linesModule = require("./lines"); | ||
var types = require("./types"); | ||
var getFieldValue = types.getFieldValue; | ||
var Node = types.namedTypes.Node; | ||
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; | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var assert_1 = __importDefault(require("assert")); | ||
var linesModule = __importStar(require("./lines")); | ||
var types_1 = __importDefault(require("./types")); | ||
var Printable = types_1.default.namedTypes.Printable; | ||
var Expression = types_1.default.namedTypes.Expression; | ||
var ReturnStatement = types_1.default.namedTypes.ReturnStatement; | ||
var SourceLocation = types_1.default.namedTypes.SourceLocation; | ||
var util_1 = require("./util"); | ||
var fast_path_1 = __importDefault(require("./fast-path")); | ||
var isObject = types_1.default.builtInTypes.object; | ||
var isArray = types_1.default.builtInTypes.array; | ||
var isString = types_1.default.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 Patcher = function Patcher(lines) { | ||
assert_1.default.ok(this instanceof Patcher); | ||
assert_1.default.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 | ||
}); | ||
}; | ||
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); | ||
}; | ||
} | ||
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_1.default.ok(util_1.comparePos(from, to) <= 0); | ||
toConcat.push(lines.slice(from, to)); | ||
} | ||
replacements.sort(function (a, b) { | ||
return util_1.comparePos(a.start, b.start); | ||
}).forEach(function (rep) { | ||
if (util_1.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; | ||
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 = fast_path_1.default.from(newNode); | ||
var oldPath = fast_path_1.default.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_1.default.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 | ||
@@ -115,122 +100,101 @@ // comments that occur inside node.loc. Returns an empty array for nodes | ||
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; | ||
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 | ||
}, ""); | ||
Pp.deleteComments = function (node) { | ||
if (!node.comments) { | ||
return; | ||
} | ||
}); | ||
}; | ||
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, { | ||
includeComments: needToPrintNewPathWithComments, | ||
// If the oldNode we're replacing already had parentheses, we may | ||
// not need to print the new node with any extra parentheses, | ||
// because the existing parentheses will suffice. However, if the | ||
// newNode has a different type than the oldNode, let the printer | ||
// decide if reprint.newPath needs parentheses, as usual. | ||
avoidRootParens: (oldNode.type === newNode.type && | ||
reprint.oldPath.hasParens()) | ||
}).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); | ||
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 | ||
}, ""); | ||
} | ||
}); | ||
// Recall that origLoc is the .loc of an ancestor node that is | ||
// guaranteed to contain all the reprinted nodes and comments. | ||
const patchedLines = patcher.get(origLoc).indentTail(-orig.loc.indent); | ||
if (path.needsParens()) { | ||
return linesModule.concat(["(", patchedLines, ")"]); | ||
} | ||
return patchedLines; | ||
}; | ||
}; | ||
function getReprinter(path) { | ||
assert_1.default.ok(path instanceof fast_path_1.default); | ||
// 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, { | ||
includeComments: needToPrintNewPathWithComments, | ||
// If the oldNode we're replacing already had parentheses, we may | ||
// not need to print the new node with any extra parentheses, | ||
// because the existing parentheses will suffice. However, if the | ||
// newNode has a different type than the oldNode, let the printer | ||
// decide if reprint.newPath needs parentheses, as usual. | ||
avoidRootParens: (oldNode.type === newNode.type && | ||
reprint.oldPath.hasParens()) | ||
}).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. | ||
var patchedLines = patcher.get(origLoc).indentTail(-orig.loc.indent); | ||
if (path.needsParens()) { | ||
return linesModule.concat(["(", patchedLines, ")"]); | ||
} | ||
return patchedLines; | ||
}; | ||
} | ||
exports.getReprinter = getReprinter; | ||
; | ||
// If the last character before oldLoc and the first character of newLines | ||
@@ -240,18 +204,13 @@ // are both identifier characters, they must be separated by a space, | ||
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); | ||
var posBeforeOldLoc = util_1.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 | ||
@@ -261,222 +220,176 @@ // are both identifier characters, they must be separated by a space, | ||
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); | ||
// 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; | ||
var newNode = newPath.getValue(); | ||
Printable.assert(newNode); | ||
var oldNode = newNode.original; | ||
Printable.assert(oldNode); | ||
assert_1.default.deepEqual(reprints, []); | ||
if (newNode.type !== oldNode.type) { | ||
return false; | ||
} | ||
var oldPath = new fast_path_1.default(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; | ||
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(); | ||
if (newNode === oldNode || | ||
newPath.valueIsDuplicate() || | ||
oldPath.valueIsDuplicate()) { | ||
return true; | ||
} | ||
isArray.assert(newNode); | ||
var len = newNode.length; | ||
if (!(isArray.check(oldNode) && | ||
var newNode = newPath.getValue(); | ||
var oldNode = oldPath.getValue(); | ||
if (newNode === oldNode || | ||
newPath.valueIsDuplicate() || | ||
oldPath.valueIsDuplicate()) { | ||
return true; | ||
} | ||
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 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; | ||
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 (newNode === oldNode || | ||
newPath.valueIsDuplicate() || | ||
oldPath.valueIsDuplicate()) { | ||
return true; | ||
} | ||
if (Printable.check(newNode)) { | ||
if (!Printable.check(oldNode)) { | ||
return false; | ||
var newNode = newPath.getValue(); | ||
isObject.assert(newNode); | ||
if (newNode.original === null) { | ||
// If newNode.original node was set to null, reprint the node. | ||
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 { | ||
var oldNode = oldPath.getValue(); | ||
if (!isObject.check(oldNode)) | ||
return false; | ||
} | ||
return true; | ||
if (newNode === oldNode || | ||
newPath.valueIsDuplicate() || | ||
oldPath.valueIsDuplicate()) { | ||
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; | ||
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; | ||
} | ||
// 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); | ||
return findChildReprints(newPath, oldPath, reprints); | ||
} | ||
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 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() && | ||
! oldPath.hasParens()) { | ||
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) { | ||
if (k.charAt(0) === "_") { | ||
// Ignore "private" AST properties added by e.g. Babel plugins and | ||
// parsers like Babylon. | ||
continue; | ||
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; | ||
} | ||
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; | ||
// 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() && | ||
!oldPath.hasParens()) { | ||
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; | ||
var keys = util_1.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) { | ||
if (k.charAt(0) === "_") { | ||
// Ignore "private" AST properties added by e.g. Babel plugins and | ||
// parsers like Babylon. | ||
continue; | ||
} | ||
newPath.stack.push(k, types_1.default.getFieldValue(newNode, k)); | ||
oldPath.stack.push(k, types_1.default.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; | ||
} |
@@ -0,1 +1,9 @@ | ||
"use strict";; | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
} | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// This module was originally created so that Recast could add its own | ||
@@ -5,2 +13,5 @@ // custom types to the AST type system (in particular, the File type), but | ||
// have much to do anymore. Still, it might prove useful in the future. | ||
module.exports = require("ast-types"); | ||
var ast_types_1 = __importDefault(require("ast-types")); | ||
exports.default = ast_types_1.default; | ||
__export(require("ast-types")); | ||
module.exports = exports["default"]; |
618
lib/util.js
@@ -1,354 +0,332 @@ | ||
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; | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var assert_1 = __importDefault(require("assert")); | ||
var types_1 = __importDefault(require("./types")); | ||
var n = types_1.default.namedTypes; | ||
var source_map_1 = __importDefault(require("source-map")); | ||
var SourceMapConsumer = source_map_1.default.SourceMapConsumer; | ||
var SourceMapGenerator = source_map_1.default.SourceMapGenerator; | ||
var hasOwn = Object.prototype.hasOwnProperty; | ||
var util = exports; | ||
function getOption(options, key, defaultValue) { | ||
if (options && hasOwn.call(options, key)) { | ||
return options[key]; | ||
} | ||
return defaultValue; | ||
if (options && hasOwn.call(options, key)) { | ||
return options[key]; | ||
} | ||
return defaultValue; | ||
} | ||
util.getOption = getOption; | ||
exports.getOption = getOption; | ||
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; | ||
var args = []; | ||
for (var _i = 0; _i < arguments.length; _i++) { | ||
args[_i] = arguments[_i]; | ||
} | ||
} | ||
return result; | ||
var result = {}; | ||
var argc = args.length; | ||
for (var i = 0; i < argc; ++i) { | ||
var keys = Object.keys(args[i]); | ||
var keyCount = keys.length; | ||
for (var j = 0; j < keyCount; ++j) { | ||
result[keys[j]] = true; | ||
} | ||
} | ||
return result; | ||
} | ||
util.getUnionOfKeys = getUnionOfKeys; | ||
exports.getUnionOfKeys = getUnionOfKeys; | ||
function comparePos(pos1, pos2) { | ||
return (pos1.line - pos2.line) || (pos1.column - pos2.column); | ||
return (pos1.line - pos2.line) || (pos1.column - pos2.column); | ||
} | ||
util.comparePos = comparePos; | ||
exports.comparePos = comparePos; | ||
function copyPos(pos) { | ||
return { | ||
line: pos.line, | ||
column: pos.column | ||
}; | ||
return { | ||
line: pos.line, | ||
column: pos.column | ||
}; | ||
} | ||
util.copyPos = copyPos; | ||
util.composeSourceMaps = function(formerMap, latterMap) { | ||
if (formerMap) { | ||
if (!latterMap) { | ||
return formerMap; | ||
exports.copyPos = copyPos; | ||
function composeSourceMaps(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; | ||
else { | ||
return latterMap || null; | ||
} | ||
smg.addMapping({ | ||
source: sourceName, | ||
original: copyPos(origPos), | ||
generated: { | ||
line: mapping.generatedLine, | ||
column: mapping.generatedColumn | ||
}, | ||
name: mapping.name | ||
var smcFormer = new SourceMapConsumer(formerMap); | ||
var smcLatter = new SourceMapConsumer(latterMap); | ||
var smg = new SourceMapGenerator({ | ||
file: latterMap.file, | ||
sourceRoot: latterMap.sourceRoot | ||
}); | ||
var sourceContent = smcFormer.sourceContentFor(sourceName); | ||
if (sourceContent && !hasOwn.call(sourcesToContents, sourceName)) { | ||
sourcesToContents[sourceName] = sourceContent; | ||
smg.setSourceContent(sourceName, sourceContent); | ||
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(); | ||
} | ||
exports.composeSourceMaps = composeSourceMaps; | ||
; | ||
function getTrueLoc(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; | ||
} | ||
}); | ||
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 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 && | ||
node.declaration.decorators && | ||
util.isExportDeclaration(node)) { | ||
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); | ||
var result = { | ||
start: node.loc.start, | ||
end: node.loc.end | ||
}; | ||
function include(node) { | ||
expandLoc(result, node.loc); | ||
} | ||
// 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 && | ||
node.declaration.decorators && | ||
isExportDeclaration(node)) { | ||
node.declaration.decorators.forEach(include); | ||
} | ||
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); | ||
// 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); | ||
} | ||
} | ||
} | ||
// 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); | ||
} | ||
return result; | ||
}; | ||
// 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); | ||
} | ||
return result; | ||
} | ||
exports.getTrueLoc = getTrueLoc; | ||
; | ||
function expandLoc(parentLoc, childLoc) { | ||
if (parentLoc && childLoc) { | ||
if (comparePos(childLoc.start, parentLoc.start) < 0) { | ||
parentLoc.start = childLoc.start; | ||
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; | ||
} | ||
} | ||
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; | ||
function fixFaultyLocations(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 (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 === "File") { | ||
// Babylon returns File nodes whose .loc.{start,end} do not include | ||
// leading or trailing whitespace. | ||
loc.start = lines.firstPos(); | ||
loc.end = lines.lastPos(); | ||
} | ||
fixForLoopHead(node, lines); | ||
fixTemplateLiteral(node, lines); | ||
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); | ||
}); | ||
fixForLoopHead(node, lines); | ||
fixTemplateLiteral(node, lines); | ||
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 ((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.declaration && 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 (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; | ||
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; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
exports.fixFaultyLocations = fixFaultyLocations; | ||
; | ||
function fixForLoopHead(node, lines) { | ||
if (node.type !== "ForStatement") { | ||
return; | ||
} | ||
function fix(child) { | ||
var loc = child && child.loc; | ||
var start = loc && loc.start; | ||
var end = loc && copyPos(loc.end); | ||
while (start && end && comparePos(start, end) < 0) { | ||
lines.prevPos(end); | ||
if (lines.charAt(end) === ";") { | ||
// Update child.loc.end to *exclude* the ';' character. | ||
loc.end.line = end.line; | ||
loc.end.column = end.column; | ||
} else { | ||
break; | ||
} | ||
if (node.type !== "ForStatement") { | ||
return; | ||
} | ||
} | ||
fix(node.init); | ||
fix(node.test); | ||
fix(node.update); | ||
function fix(child) { | ||
var loc = child && child.loc; | ||
var start = loc && loc.start; | ||
var end = loc && copyPos(loc.end); | ||
while (start && end && comparePos(start, end) < 0) { | ||
lines.prevPos(end); | ||
if (lines.charAt(end) === ";") { | ||
// Update child.loc.end to *exclude* the ';' character. | ||
loc.end.line = end.line; | ||
loc.end.column = end.column; | ||
} | ||
else { | ||
break; | ||
} | ||
} | ||
} | ||
fix(node.init); | ||
fix(node.test); | ||
fix(node.update); | ||
} | ||
function fixTemplateLiteral(node, lines) { | ||
if (node.type !== "TemplateLiteral") { | ||
return; | ||
} | ||
if (node.quasis.length === 0) { | ||
// If there are no quasi elements, then there is nothing to fix. | ||
return; | ||
} | ||
// node.loc is not present when using export default with a template literal | ||
if (node.loc) { | ||
// 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; | ||
if (node.type !== "TemplateLiteral") { | ||
return; | ||
} | ||
// 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; | ||
if (node.quasis.length === 0) { | ||
// If there are no quasi elements, then there is nothing to fix. | ||
return; | ||
} | ||
} | ||
// 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; | ||
} | ||
// node.loc is not present when using export default with a template literal | ||
if (node.loc) { | ||
// 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_1.default.strictEqual(lines.charAt(afterLeftBackTickPos), "`"); | ||
assert_1.default.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_1.default.ok(lines.prevPos(rightBackTickPos)); | ||
assert_1.default.strictEqual(lines.charAt(rightBackTickPos), "`"); | ||
var lastQuasi = node.quasis[node.quasis.length - 1]; | ||
if (comparePos(rightBackTickPos, lastQuasi.loc.end) < 0) { | ||
lastQuasi.loc.end = rightBackTickPos; | ||
} | ||
} | ||
// 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; | ||
} | ||
// 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_1.default.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; | ||
} | ||
} | ||
}); | ||
} | ||
function isExportDeclaration(node) { | ||
if (node) | ||
switch (node.type) { | ||
case "ExportDeclaration": | ||
case "ExportDefaultDeclaration": | ||
case "ExportDefaultSpecifier": | ||
case "DeclareExportDeclaration": | ||
case "ExportNamedDeclaration": | ||
case "ExportAllDeclaration": | ||
return true; | ||
} | ||
return false; | ||
} | ||
exports.isExportDeclaration = isExportDeclaration; | ||
; | ||
function getParentExportDeclaration(path) { | ||
var parentNode = path.getParentNode(); | ||
if (path.getName() === "declaration" && | ||
isExportDeclaration(parentNode)) { | ||
return parentNode; | ||
} | ||
}); | ||
return null; | ||
} | ||
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; | ||
}; | ||
exports.getParentExportDeclaration = getParentExportDeclaration; | ||
; | ||
function isTrailingCommaEnabled(options, context) { | ||
var trailingComma = options.trailingComma; | ||
if (typeof trailingComma === "object") { | ||
return !!trailingComma[context]; | ||
} | ||
return !!trailingComma; | ||
} | ||
exports.isTrailingCommaEnabled = isTrailingCommaEnabled; | ||
; |
44
main.js
@@ -1,19 +0,21 @@ | ||
var types = require("./lib/types"); | ||
var parse = require("./lib/parser").parse; | ||
var Printer = require("./lib/printer").Printer; | ||
"use strict";; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var fs_1 = __importDefault(require("fs")); | ||
var types_1 = __importDefault(require("./lib/types")); | ||
var parser_1 = require("./lib/parser"); | ||
var printer_1 = require("./lib/printer"); | ||
function print(node, options) { | ||
return new Printer(options).print(node); | ||
return new printer_1.Printer(options).print(node); | ||
} | ||
function prettyPrint(node, options) { | ||
return new Printer(options).printGenerically(node); | ||
return new printer_1.Printer(options).printGenerically(node); | ||
} | ||
function run(transformer, options) { | ||
return runFile(process.argv[2], transformer, options); | ||
} | ||
function runFile(path, transformer, options) { | ||
require("fs").readFile(path, "utf-8", function(err, code) { | ||
fs_1.default.readFile(path, "utf-8", function (err, code) { | ||
if (err) { | ||
@@ -23,19 +25,16 @@ console.error(err); | ||
} | ||
runString(code, transformer, options); | ||
}); | ||
} | ||
function defaultWriteback(output) { | ||
process.stdout.write(output); | ||
} | ||
function runString(code, transformer, options) { | ||
var writeback = options && options.writeback || defaultWriteback; | ||
transformer(parse(code, options), function(node) { | ||
transformer(parser_1.parse(code, options), function (node) { | ||
writeback(print(node, options).code); | ||
}); | ||
} | ||
Object.defineProperties(exports, { | ||
var main = {}; | ||
Object.defineProperties(main, { | ||
/** | ||
@@ -47,5 +46,4 @@ * Parse a string of code into an augmented syntax tree suitable for | ||
enumerable: true, | ||
value: parse | ||
value: parser_1.parse | ||
}, | ||
/** | ||
@@ -66,5 +64,4 @@ * Traverse and potentially modify an abstract syntax tree using a | ||
enumerable: true, | ||
value: types.visit | ||
value: types_1.default.visit | ||
}, | ||
/** | ||
@@ -78,3 +75,2 @@ * Reprint a modified syntax tree using as much of the original source | ||
}, | ||
/** | ||
@@ -87,3 +83,2 @@ * Print without attempting to reuse any original source code. | ||
}, | ||
/** | ||
@@ -94,5 +89,4 @@ * Customized version of require("ast-types"). | ||
enumerable: false, | ||
value: types | ||
value: types_1.default | ||
}, | ||
/** | ||
@@ -106,1 +100,3 @@ * Convenient command-line interface (see e.g. example/add-braces). | ||
}); | ||
exports.default = main; | ||
module.exports = exports["default"]; |
{ | ||
"author": "Ben Newman <bn@cs.stanford.edu>", | ||
"name": "recast", | ||
"version": "0.16.2", | ||
"version": "0.17.0", | ||
"description": "JavaScript syntax tree transformer, nondestructive pretty-printer, and automatic source map generator", | ||
@@ -23,5 +23,12 @@ "keywords": [ | ||
"main": "main.js", | ||
"types": "main.d.ts", | ||
"scripts": { | ||
"test": "test/run.sh", | ||
"debug": "test/run.sh --inspect-brk" | ||
"tsc": "tsc --noEmit", | ||
"mocha": "test/run.sh", | ||
"debug": "test/run.sh --inspect-brk", | ||
"test": "npm run tsc && npm run mocha", | ||
"build": "tsc && ts-add-module-exports", | ||
"clean": "ts-emit-clean", | ||
"prepack": "npm run clean && npm run build", | ||
"postpack": "npm run clean" | ||
}, | ||
@@ -32,3 +39,3 @@ "browser": { | ||
"dependencies": { | ||
"ast-types": "0.11.7", | ||
"ast-types": "0.12.0", | ||
"esprima": "~4.0.0", | ||
@@ -42,2 +49,6 @@ "private": "~0.1.5", | ||
"@babel/preset-env": "^7.0.0-beta.54", | ||
"@types/esprima": "^4.0.2", | ||
"@types/glob": "^7.1.1", | ||
"@types/mocha": "^5.2.5", | ||
"@types/node": "^10.12.9", | ||
"esprima-fb": "^15001.1001.0-dev-harmony-fb", | ||
@@ -47,3 +58,7 @@ "flow-parser": "^0.83.0", | ||
"mocha": "~5.2.0", | ||
"reify": "^0.18.0" | ||
"reify": "^0.18.0", | ||
"ts-add-module-exports": "^1.0.0", | ||
"ts-emit-clean": "^1.0.0", | ||
"ts-node": "^7.0.1", | ||
"typescript": "^3.1.6" | ||
}, | ||
@@ -50,0 +65,0 @@ "engines": { |
@@ -1,39 +0,42 @@ | ||
const getOption = require("../lib/util.js").getOption; | ||
module.exports = function (options) { | ||
// The goal here is to tolerate as much syntax as possible, since Recast | ||
// is not in the business of forbidding anything. If you want your | ||
// parser to be more restrictive for some reason, you can always pass | ||
// your own parser object to recast.parse. | ||
return { | ||
sourceType: getOption(options, "sourceType", "module"), | ||
strictMode: getOption(options, "strictMode", false), | ||
allowImportExportEverywhere: true, | ||
allowReturnOutsideFunction: true, | ||
startLine: 1, | ||
tokens: true, | ||
plugins: [ | ||
"asyncGenerators", | ||
"bigInt", | ||
"classPrivateMethods", | ||
"classPrivateProperties", | ||
"classProperties", | ||
"decorators-legacy", | ||
"doExpressions", | ||
"dynamicImport", | ||
"exportDefaultFrom", | ||
"exportExtensions", | ||
"exportNamespaceFrom", | ||
"functionBind", | ||
"functionSent", | ||
"importMeta", | ||
"nullishCoalescingOperator", | ||
"numericSeparator", | ||
"objectRestSpread", | ||
"optionalCatchBinding", | ||
"optionalChaining", | ||
["pipelineOperator", { proposal: "minimal" }], | ||
"throwExpressions", | ||
] | ||
}; | ||
}; | ||
"use strict";; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var util_1 = require("../lib/util"); | ||
function getBabelOptions(options) { | ||
// The goal here is to tolerate as much syntax as possible, since Recast | ||
// is not in the business of forbidding anything. If you want your | ||
// parser to be more restrictive for some reason, you can always pass | ||
// your own parser object to recast.parse. | ||
return { | ||
sourceType: util_1.getOption(options, "sourceType", "module"), | ||
strictMode: util_1.getOption(options, "strictMode", false), | ||
allowImportExportEverywhere: true, | ||
allowReturnOutsideFunction: true, | ||
startLine: 1, | ||
tokens: true, | ||
plugins: [ | ||
"asyncGenerators", | ||
"bigInt", | ||
"classPrivateMethods", | ||
"classPrivateProperties", | ||
"classProperties", | ||
"decorators-legacy", | ||
"doExpressions", | ||
"dynamicImport", | ||
"exportDefaultFrom", | ||
"exportExtensions", | ||
"exportNamespaceFrom", | ||
"functionBind", | ||
"functionSent", | ||
"importMeta", | ||
"nullishCoalescingOperator", | ||
"numericSeparator", | ||
"objectRestSpread", | ||
"optionalCatchBinding", | ||
"optionalChaining", | ||
["pipelineOperator", { proposal: "minimal" }], | ||
"throwExpressions", | ||
] | ||
}; | ||
} | ||
exports.default = getBabelOptions; | ||
module.exports = exports["default"]; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// This module is suitable for passing as options.parser when calling | ||
@@ -10,27 +10,25 @@ // recast.parse to process JavaScript code with Acorn: | ||
// | ||
const getOption = require("../lib/util.js").getOption; | ||
exports.parse = function parse(source, options) { | ||
const comments = []; | ||
const tokens = []; | ||
const ast = require("acorn").parse(source, { | ||
allowHashBang: true, | ||
allowImportExportEverywhere: true, | ||
allowReturnOutsideFunction: true, | ||
ecmaVersion: getOption(options, "ecmaVersion", 8), | ||
sourceType: getOption(options, "sourceType", "module"), | ||
locations: true, | ||
onComment: comments, | ||
onToken: tokens, | ||
}); | ||
if (! ast.comments) { | ||
ast.comments = comments; | ||
} | ||
if (! ast.tokens) { | ||
ast.tokens = tokens; | ||
} | ||
return ast; | ||
}; | ||
var util_1 = require("../lib/util"); | ||
function parse(source, options) { | ||
var comments = []; | ||
var tokens = []; | ||
var ast = require("acorn").parse(source, { | ||
allowHashBang: true, | ||
allowImportExportEverywhere: true, | ||
allowReturnOutsideFunction: true, | ||
ecmaVersion: util_1.getOption(options, "ecmaVersion", 8), | ||
sourceType: util_1.getOption(options, "sourceType", "module"), | ||
locations: true, | ||
onComment: comments, | ||
onToken: tokens, | ||
}); | ||
if (!ast.comments) { | ||
ast.comments = comments; | ||
} | ||
if (!ast.tokens) { | ||
ast.tokens = tokens; | ||
} | ||
return ast; | ||
} | ||
exports.parse = parse; | ||
; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var _babel_options_1 = __importDefault(require("./_babel_options")); | ||
// Prefer the new @babel/parser package, but fall back to babylon if | ||
// that's what's available. | ||
const parser = exports.parser = function () { | ||
try { | ||
return require("@babel/parser"); | ||
} catch (e) { | ||
return require("babylon"); | ||
} | ||
exports.parser = function () { | ||
try { | ||
return require("@babel/parser"); | ||
} | ||
catch (e) { | ||
return require("babylon"); | ||
} | ||
}(); | ||
// This module is suitable for passing as options.parser when calling | ||
@@ -20,6 +24,8 @@ // recast.parse to process JavaScript code with Babel: | ||
// | ||
exports.parse = function (source, options) { | ||
options = require("./_babel_options.js")(options); | ||
options.plugins.push("jsx", "flow"); | ||
return parser.parse(source, options); | ||
}; | ||
function parse(source, options) { | ||
var babelOptions = _babel_options_1.default(options); | ||
babelOptions.plugins.push("jsx", "flow"); | ||
return exports.parser.parse(source, babelOptions); | ||
} | ||
exports.parse = parse; | ||
; |
"use strict"; | ||
Object.assign(exports, require("./babel.js")); | ||
function __export(m) { | ||
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; | ||
} | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__export(require("./babel")); |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// This module is suitable for passing as options.parser when calling | ||
@@ -10,21 +10,20 @@ // recast.parse to process ECMAScript code with Esprima: | ||
// | ||
const getOption = require("../lib/util.js").getOption; | ||
exports.parse = function (source, options) { | ||
const comments = []; | ||
const ast = require("esprima").parse(source, { | ||
loc: true, | ||
locations: true, | ||
comment: true, | ||
onComment: comments, | ||
range: getOption(options, "range", false), | ||
tolerant: getOption(options, "tolerant", true), | ||
tokens: true | ||
}); | ||
if (! Array.isArray(ast.comments)) { | ||
ast.comments = comments; | ||
} | ||
return ast; | ||
}; | ||
var util_1 = require("../lib/util"); | ||
function parse(source, options) { | ||
var comments = []; | ||
var ast = require("esprima").parse(source, { | ||
loc: true, | ||
locations: true, | ||
comment: true, | ||
onComment: comments, | ||
range: util_1.getOption(options, "range", false), | ||
tolerant: util_1.getOption(options, "tolerant", true), | ||
tokens: true | ||
}); | ||
if (!Array.isArray(ast.comments)) { | ||
ast.comments = comments; | ||
} | ||
return ast; | ||
} | ||
exports.parse = parse; | ||
; |
"use strict"; | ||
const parser = require("./babel.js").parser; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var babel_1 = require("./babel"); | ||
var _babel_options_1 = __importDefault(require("./_babel_options")); | ||
// This module is suitable for passing as options.parser when calling | ||
@@ -12,6 +15,8 @@ // recast.parse to process Flow code: | ||
// | ||
exports.parse = function parse(source, options) { | ||
options = require("./_babel_options.js")(options); | ||
options.plugins.push("jsx", "flow"); | ||
return parser.parse(source, options); | ||
}; | ||
function parse(source, options) { | ||
var babelOptions = _babel_options_1.default(options); | ||
babelOptions.plugins.push("jsx", "flow"); | ||
return babel_1.parser.parse(source, babelOptions); | ||
} | ||
exports.parse = parse; | ||
; |
"use strict"; | ||
const parser = require("./babel.js").parser; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var babel_1 = require("./babel"); | ||
var _babel_options_1 = __importDefault(require("./_babel_options")); | ||
// This module is suitable for passing as options.parser when calling | ||
@@ -12,6 +15,8 @@ // recast.parse to process TypeScript code: | ||
// | ||
exports.parse = function parse(source, options) { | ||
options = require("./_babel_options.js")(options); | ||
options.plugins.push("typescript"); | ||
return parser.parse(source, options); | ||
}; | ||
function parse(source, options) { | ||
var babelOptions = _babel_options_1.default(options); | ||
babelOptions.plugins.push("typescript"); | ||
return babel_1.parser.parse(source, babelOptions); | ||
} | ||
exports.parse = parse; | ||
; |
Sorry, the diff of this file is too big to display
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
227791
46
5680
16
+ Addedast-types@0.12.0(transitive)
- Removedast-types@0.11.7(transitive)
Updatedast-types@0.12.0