Comparing version 0.11.23 to 0.12.0
@@ -9,4 +9,4 @@ var assert = require("assert"); | ||
function FastPath(value) { | ||
assert.ok(this instanceof FastPath); | ||
this.stack = [value]; | ||
assert.ok(this instanceof FastPath); | ||
this.stack = [value]; | ||
} | ||
@@ -19,26 +19,26 @@ | ||
FastPath.from = function(obj) { | ||
if (obj instanceof FastPath) { | ||
// Return a defensive copy of any existing FastPath instances. | ||
return obj.copy(); | ||
} | ||
if (obj instanceof FastPath) { | ||
// Return a defensive copy of any existing FastPath instances. | ||
return obj.copy(); | ||
} | ||
if (obj instanceof types.NodePath) { | ||
// For backwards compatibility, unroll NodePath instances into | ||
// lightweight FastPath [..., name, value] stacks. | ||
var copy = Object.create(FastPath.prototype); | ||
var stack = [obj.value]; | ||
for (var pp; (pp = obj.parentPath); obj = pp) | ||
stack.push(obj.name, pp.value); | ||
copy.stack = stack.reverse(); | ||
return copy; | ||
} | ||
if (obj instanceof types.NodePath) { | ||
// For backwards compatibility, unroll NodePath instances into | ||
// lightweight FastPath [..., name, value] stacks. | ||
var copy = Object.create(FastPath.prototype); | ||
var stack = [obj.value]; | ||
for (var pp; (pp = obj.parentPath); obj = pp) | ||
stack.push(obj.name, pp.value); | ||
copy.stack = stack.reverse(); | ||
return copy; | ||
} | ||
// Otherwise use obj as the value of the new FastPath instance. | ||
return new FastPath(obj); | ||
// 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; | ||
var copy = Object.create(FastPath.prototype); | ||
copy.stack = this.stack.slice(0); | ||
return copy; | ||
}; | ||
@@ -49,10 +49,10 @@ | ||
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; | ||
}; | ||
@@ -63,25 +63,31 @@ | ||
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; | ||
}; | ||
function getNodeHelper(path, count) { | ||
var s = path.stack; | ||
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; | ||
} | ||
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); | ||
return getNodeHelper(this, ~~count); | ||
}; | ||
FPp.getParentNode = function getParentNode(count) { | ||
return getNodeHelper(this, ~~count + 1); | ||
return getNodeHelper(this, ~~count + 1); | ||
}; | ||
@@ -95,7 +101,7 @@ | ||
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]; | ||
}; | ||
@@ -109,14 +115,14 @@ | ||
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; | ||
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; | ||
}; | ||
@@ -129,24 +135,24 @@ | ||
FPp.each = function each(callback/*, name1, name2, ... */) { | ||
var s = this.stack; | ||
var origLen = s.length; | ||
var value = s[origLen - 1]; | ||
var argc = arguments.length; | ||
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 = 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; | ||
} | ||
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; | ||
s.length = origLen; | ||
}; | ||
@@ -158,26 +164,26 @@ | ||
FPp.map = function map(callback/*, name1, name2, ... */) { | ||
var s = this.stack; | ||
var origLen = s.length; | ||
var value = s[origLen - 1]; | ||
var argc = arguments.length; | ||
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 = 1; i < argc; ++i) { | ||
var name = arguments[i]; | ||
value = value[name]; | ||
s.push(name, value); | ||
} | ||
var result = new Array(value.length); | ||
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; | ||
} | ||
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; | ||
s.length = origLen; | ||
return result; | ||
return result; | ||
}; | ||
@@ -188,193 +194,192 @@ | ||
FPp.needsParens = function(assumeExpressionContext) { | ||
var parent = this.getParentNode(); | ||
if (!parent) { | ||
return false; | ||
} | ||
var parent = this.getParentNode(); | ||
if (!parent) { | ||
return false; | ||
} | ||
var name = this.getName(); | ||
var node = this.getNode(); | ||
var name = this.getName(); | ||
var node = this.getNode(); | ||
// If the value of this path is some child of a Node and not a Node | ||
// itself, then it doesn't need parentheses. Only Node objects (in | ||
// fact, only Expression nodes) need parentheses. | ||
if (this.getValue() !== node) { | ||
return false; | ||
} | ||
// 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; | ||
} | ||
// Only statements don't need parentheses. | ||
if (n.Statement.check(node)) { | ||
return false; | ||
} | ||
// Identifiers never need parentheses. | ||
if (node.type === "Identifier") { | ||
return false; | ||
} | ||
// Identifiers never need parentheses. | ||
if (node.type === "Identifier") { | ||
return false; | ||
} | ||
if (parent.type === "ParenthesizedExpression") { | ||
return false; | ||
} | ||
if (parent.type === "ParenthesizedExpression") { | ||
return false; | ||
} | ||
switch (node.type) { | ||
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 parent.type === "MemberExpression" | ||
&& name === "object" | ||
&& parent.object === node; | ||
return true; | ||
case "MemberExpression": | ||
return name === "object" | ||
&& parent.object === node; | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
switch (parent.type) { | ||
case "CallExpression": | ||
return name === "callee" | ||
&& parent.callee === node; | ||
var po = parent.operator; | ||
var pp = PRECEDENCE[po]; | ||
var no = node.operator; | ||
var np = PRECEDENCE[no]; | ||
case "UnaryExpression": | ||
case "SpreadElement": | ||
case "SpreadProperty": | ||
return true; | ||
if (pp > np) { | ||
return true; | ||
} | ||
case "MemberExpression": | ||
return name === "object" | ||
&& parent.object === node; | ||
if (pp === np && name === "right") { | ||
assert.strictEqual(parent.right, node); | ||
return true; | ||
} | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
var po = parent.operator; | ||
var pp = PRECEDENCE[po]; | ||
var no = node.operator; | ||
var np = PRECEDENCE[no]; | ||
default: | ||
return false; | ||
} | ||
if (pp > np) { | ||
return true; | ||
} | ||
case "SequenceExpression": | ||
switch (parent.type) { | ||
case "ReturnStatement": | ||
return false; | ||
if (pp === np && name === "right") { | ||
assert.strictEqual(parent.right, node); | ||
return true; | ||
} | ||
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; | ||
default: | ||
return false; | ||
} | ||
case "ExpressionStatement": | ||
return name !== "expression"; | ||
case "SequenceExpression": | ||
switch (parent.type) { | ||
case "ReturnStatement": | ||
return false; | ||
default: | ||
// Otherwise err on the side of overparenthesization, adding | ||
// explicit exceptions above if this proves overzealous. | ||
return true; | ||
} | ||
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 "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; | ||
case "ExpressionStatement": | ||
return name !== "expression"; | ||
default: | ||
return false; | ||
} | ||
default: | ||
// Otherwise err on the side of overparenthesization, adding | ||
// explicit exceptions above if this proves overzealous. | ||
return true; | ||
} | ||
case "IntersectionTypeAnnotation": | ||
case "UnionTypeAnnotation": | ||
return parent.type === "NullableTypeAnnotation"; | ||
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; | ||
case "Literal": | ||
return parent.type === "MemberExpression" | ||
&& isNumber.check(node.value) | ||
&& name === "object" | ||
&& parent.object === node; | ||
default: | ||
return false; | ||
} | ||
case "AssignmentExpression": | ||
case "ConditionalExpression": | ||
switch (parent.type) { | ||
case "UnaryExpression": | ||
case "SpreadElement": | ||
case "SpreadProperty": | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
return true; | ||
case "IntersectionTypeAnnotation": | ||
case "UnionTypeAnnotation": | ||
return parent.type === "NullableTypeAnnotation"; | ||
case "CallExpression": | ||
return name === "callee" | ||
&& parent.callee === node; | ||
case "Literal": | ||
return parent.type === "MemberExpression" | ||
&& isNumber.check(node.value) | ||
&& name === "object" | ||
&& parent.object === node; | ||
case "AssignmentExpression": | ||
case "ConditionalExpression": | ||
switch (parent.type) { | ||
case "UnaryExpression": | ||
case "SpreadElement": | ||
case "SpreadProperty": | ||
case "BinaryExpression": | ||
case "LogicalExpression": | ||
return true; | ||
return name === "test" | ||
&& parent.test === node; | ||
case "CallExpression": | ||
return name === "callee" | ||
&& parent.callee === node; | ||
case "MemberExpression": | ||
return name === "object" | ||
&& parent.object === node; | ||
case "ConditionalExpression": | ||
return name === "test" | ||
&& parent.test === node; | ||
default: | ||
return false; | ||
} | ||
case "MemberExpression": | ||
return name === "object" | ||
&& parent.object === node; | ||
case "ArrowFunctionExpression": | ||
if(n.CallExpression.check(parent) && name === 'callee') { | ||
return true; | ||
} | ||
if(n.MemberExpression.check(parent) && name === 'object') { | ||
return true; | ||
} | ||
default: | ||
return false; | ||
} | ||
return isBinary(parent); | ||
case "ArrowFunctionExpression": | ||
if(n.CallExpression.check(parent) && name === 'callee') { | ||
return true; | ||
} | ||
if(n.MemberExpression.check(parent) && name === 'object') { | ||
return true; | ||
} | ||
case "ObjectExpression": | ||
if (parent.type === "ArrowFunctionExpression" && | ||
name === "body") { | ||
return true; | ||
} | ||
return isBinary(parent); | ||
case "ObjectExpression": | ||
if (parent.type === "ArrowFunctionExpression" && | ||
name === "body") { | ||
return true; | ||
} | ||
default: | ||
if (parent.type === "NewExpression" && | ||
name === "callee" && | ||
parent.callee === node) { | ||
return containsCallExpression(node); | ||
} | ||
default: | ||
if (parent.type === "NewExpression" && | ||
name === "callee" && | ||
parent.callee === node) { | ||
return containsCallExpression(node); | ||
} | ||
} | ||
if (assumeExpressionContext !== true && | ||
!this.canBeFirstInStatement() && | ||
this.firstInStatement()) | ||
return true; | ||
if (assumeExpressionContext !== true && | ||
!this.canBeFirstInStatement() && | ||
this.firstInStatement()) | ||
return true; | ||
return false; | ||
return false; | ||
}; | ||
function isBinary(node) { | ||
return n.BinaryExpression.check(node) | ||
|| n.LogicalExpression.check(node); | ||
return n.BinaryExpression.check(node) | ||
|| n.LogicalExpression.check(node); | ||
} | ||
function isUnaryLike(node) { | ||
return n.UnaryExpression.check(node) | ||
// I considered making SpreadElement and SpreadProperty subtypes | ||
// of UnaryExpression, but they're not really Expression nodes. | ||
|| (n.SpreadElement && n.SpreadElement.check(node)) | ||
|| (n.SpreadProperty && n.SpreadProperty.check(node)); | ||
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)); | ||
} | ||
@@ -394,103 +399,103 @@ | ||
].forEach(function(tier, i) { | ||
tier.forEach(function(op) { | ||
PRECEDENCE[op] = i; | ||
}); | ||
tier.forEach(function(op) { | ||
PRECEDENCE[op] = i; | ||
}); | ||
}); | ||
function containsCallExpression(node) { | ||
if (n.CallExpression.check(node)) { | ||
return true; | ||
} | ||
if (n.CallExpression.check(node)) { | ||
return true; | ||
} | ||
if (isArray.check(node)) { | ||
return node.some(containsCallExpression); | ||
} | ||
if (isArray.check(node)) { | ||
return node.some(containsCallExpression); | ||
} | ||
if (n.Node.check(node)) { | ||
return types.someField(node, function(name, child) { | ||
return containsCallExpression(child); | ||
}); | ||
} | ||
if (n.Node.check(node)) { | ||
return types.someField(node, function(name, child) { | ||
return containsCallExpression(child); | ||
}); | ||
} | ||
return false; | ||
return false; | ||
} | ||
FPp.canBeFirstInStatement = function() { | ||
var node = this.getNode(); | ||
return !n.FunctionExpression.check(node) | ||
&& !n.ObjectExpression.check(node); | ||
var node = this.getNode(); | ||
return !n.FunctionExpression.check(node) | ||
&& !n.ObjectExpression.check(node); | ||
}; | ||
FPp.firstInStatement = function() { | ||
var s = this.stack; | ||
var parentName, parent; | ||
var childName, child; | ||
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]; | ||
} | ||
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 (!parent || !child) { | ||
continue; | ||
} | ||
if (n.BlockStatement.check(parent) && | ||
parentName === "body" && | ||
childName === 0) { | ||
assert.strictEqual(parent.body[0], child); | ||
return true; | ||
} | ||
if (n.BlockStatement.check(parent) && | ||
parentName === "body" && | ||
childName === 0) { | ||
assert.strictEqual(parent.body[0], child); | ||
return true; | ||
} | ||
if (n.ExpressionStatement.check(parent) && | ||
childName === "expression") { | ||
assert.strictEqual(parent.expression, child); | ||
return true; | ||
} | ||
if (n.ExpressionStatement.check(parent) && | ||
childName === "expression") { | ||
assert.strictEqual(parent.expression, child); | ||
return true; | ||
} | ||
if (n.SequenceExpression.check(parent) && | ||
parentName === "expressions" && | ||
childName === 0) { | ||
assert.strictEqual(parent.expressions[0], child); | ||
continue; | ||
} | ||
if (n.SequenceExpression.check(parent) && | ||
parentName === "expressions" && | ||
childName === 0) { | ||
assert.strictEqual(parent.expressions[0], child); | ||
continue; | ||
} | ||
if (n.CallExpression.check(parent) && | ||
childName === "callee") { | ||
assert.strictEqual(parent.callee, child); | ||
continue; | ||
} | ||
if (n.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.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 (n.ConditionalExpression.check(parent) && | ||
childName === "test") { | ||
assert.strictEqual(parent.test, child); | ||
continue; | ||
} | ||
if (isBinary(parent) && | ||
childName === "left") { | ||
assert.strictEqual(parent.left, 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; | ||
if (n.UnaryExpression.check(parent) && | ||
!parent.prefix && | ||
childName === "argument") { | ||
assert.strictEqual(parent.argument, child); | ||
continue; | ||
} | ||
return true; | ||
return false; | ||
} | ||
return true; | ||
}; |
@@ -13,2 +13,3 @@ var assert = require("assert"); | ||
var util = require("./util"); | ||
var Map = global.Map || require("core-js/es6/map"); | ||
@@ -96,2 +97,3 @@ exports.parse = function parse(source, options) { | ||
this.indent = 0; | ||
this.seen = new Map; | ||
} | ||
@@ -102,4 +104,13 @@ | ||
TCp.copy = function(node) { | ||
if (this.seen.has(node)) { | ||
return this.seen.get(node); | ||
} | ||
if (isArray.check(node)) { | ||
return node.map(this.copy, this); | ||
var copy = new Array(node.length); | ||
this.seen.set(node, copy); | ||
node.forEach(function (item, i) { | ||
copy[i] = this.copy(item); | ||
}, this); | ||
return copy; | ||
} | ||
@@ -122,2 +133,4 @@ | ||
this.seen.set(node, copy); | ||
var loc = node.loc; | ||
@@ -124,0 +137,0 @@ var oldIndent = this.indent; |
@@ -18,51 +18,51 @@ var assert = require("assert"); | ||
function Patcher(lines) { | ||
assert.ok(this instanceof Patcher); | ||
assert.ok(lines instanceof linesModule.Lines); | ||
assert.ok(this instanceof Patcher); | ||
assert.ok(lines instanceof linesModule.Lines); | ||
var self = this, | ||
replacements = []; | ||
var self = this, | ||
replacements = []; | ||
self.replace = function(loc, lines) { | ||
if (isString.check(lines)) | ||
lines = linesModule.fromString(lines); | ||
self.replace = function(loc, lines) { | ||
if (isString.check(lines)) | ||
lines = linesModule.fromString(lines); | ||
replacements.push({ | ||
lines: lines, | ||
start: loc.start, | ||
end: loc.end | ||
}); | ||
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) } | ||
}; | ||
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 = []; | ||
var sliceFrom = loc.start, | ||
toConcat = []; | ||
function pushSlice(from, to) { | ||
assert.ok(comparePos(from, to) <= 0); | ||
toConcat.push(lines.slice(from, to)); | ||
} | ||
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; | ||
} | ||
}); | ||
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); | ||
pushSlice(sliceFrom, loc.end); | ||
return linesModule.concat(toConcat); | ||
}; | ||
return linesModule.concat(toConcat); | ||
}; | ||
} | ||
@@ -74,37 +74,37 @@ exports.Patcher = Patcher; | ||
Pp.tryToReprintComments = function(newNode, oldNode, print) { | ||
var patcher = this; | ||
var patcher = this; | ||
if (!newNode.comments && | ||
!oldNode.comments) { | ||
// We were (vacuously) able to reprint all the comments! | ||
return true; | ||
} | ||
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); | ||
var newPath = FastPath.from(newNode); | ||
var oldPath = FastPath.from(oldNode); | ||
newPath.stack.push("comments", getSurroundingComments(newNode)); | ||
oldPath.stack.push("comments", getSurroundingComments(oldNode)); | ||
newPath.stack.push("comments", getSurroundingComments(newNode)); | ||
oldPath.stack.push("comments", getSurroundingComments(oldNode)); | ||
var reprints = []; | ||
var ableToReprintComments = | ||
findArrayReprints(newPath, oldPath, reprints); | ||
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. | ||
// 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) | ||
); | ||
}); | ||
} | ||
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; | ||
return ableToReprintComments; | ||
}; | ||
@@ -116,108 +116,108 @@ | ||
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; | ||
} | ||
if (!node.comments) { | ||
return; | ||
} | ||
var patcher = this; | ||
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) | ||
}, ""); | ||
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 | ||
}, ""); | ||
} | ||
}); | ||
} else if (comment.trailing) { | ||
// Delete trailing comments along with any leading whitespace they | ||
// might have. | ||
patcher.replace({ | ||
start: node.loc.lines.skipSpaces( | ||
comment.loc.start, true, false), | ||
end: comment.loc.end | ||
}, ""); | ||
} | ||
}); | ||
}; | ||
exports.getReprinter = function(path) { | ||
assert.ok(path instanceof FastPath); | ||
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; | ||
// 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 = []; | ||
var orig = node.original; | ||
var origLoc = orig && orig.loc; | ||
var lines = origLoc && origLoc.lines; | ||
var reprints = []; | ||
if (!lines || !findReprints(path, reprints)) | ||
return; | ||
if (!lines || !findReprints(path, reprints)) | ||
return; | ||
return function(print) { | ||
var patcher = new Patcher(lines); | ||
return function(print) { | ||
var patcher = new Patcher(lines); | ||
reprints.forEach(function(reprint) { | ||
var newNode = reprint.newPath.getValue(); | ||
var oldNode = reprint.oldPath.getValue(); | ||
reprints.forEach(function(reprint) { | ||
var newNode = reprint.newPath.getValue(); | ||
var oldNode = reprint.oldPath.getValue(); | ||
SourceLocation.assert(oldNode.loc, true); | ||
SourceLocation.assert(oldNode.loc, true); | ||
var needToPrintNewPathWithComments = | ||
!patcher.tryToReprintComments(newNode, oldNode, print) | ||
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); | ||
} | ||
if (needToPrintNewPathWithComments) { | ||
// Since we were not able to preserve all leading/trailing | ||
// comments, we delete oldNode's comments, print newPath with | ||
// comments, and then patch the resulting lines where oldNode used | ||
// to be. | ||
patcher.deleteComments(oldNode); | ||
} | ||
var newLines = print( | ||
reprint.newPath, | ||
needToPrintNewPathWithComments | ||
).indentTail(oldNode.loc.indent); | ||
var newLines = print( | ||
reprint.newPath, | ||
needToPrintNewPathWithComments | ||
).indentTail(oldNode.loc.indent); | ||
var nls = needsLeadingSpace(lines, oldNode.loc, newLines); | ||
var nts = needsTrailingSpace(lines, oldNode.loc, newLines); | ||
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); | ||
} | ||
// 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); | ||
}); | ||
patcher.replace(oldNode.loc, newLines); | ||
}); | ||
// Recall that origLoc is the .loc of an ancestor node that is | ||
// guaranteed to contain all the reprinted nodes and comments. | ||
return patcher.get(origLoc).indentTail(-orig.loc.indent); | ||
}; | ||
// Recall that origLoc is the .loc of an ancestor node that is | ||
// guaranteed to contain all the reprinted nodes and comments. | ||
return patcher.get(origLoc).indentTail(-orig.loc.indent); | ||
}; | ||
}; | ||
@@ -229,16 +229,16 @@ | ||
function needsLeadingSpace(oldLines, oldLoc, newLines) { | ||
var posBeforeOldLoc = util.copyPos(oldLoc.start); | ||
var posBeforeOldLoc = util.copyPos(oldLoc.start); | ||
// The character just before the location occupied by oldNode. | ||
var charBeforeOldLoc = | ||
oldLines.prevPos(posBeforeOldLoc) && | ||
oldLines.charAt(posBeforeOldLoc); | ||
// 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()); | ||
// First character of the reprinted node. | ||
var newFirstChar = newLines.charAt(newLines.firstPos()); | ||
return charBeforeOldLoc && | ||
riskyAdjoiningCharExp.test(charBeforeOldLoc) && | ||
newFirstChar && | ||
riskyAdjoiningCharExp.test(newFirstChar); | ||
return charBeforeOldLoc && | ||
riskyAdjoiningCharExp.test(charBeforeOldLoc) && | ||
newFirstChar && | ||
riskyAdjoiningCharExp.test(newFirstChar); | ||
} | ||
@@ -250,146 +250,159 @@ | ||
function needsTrailingSpace(oldLines, oldLoc, newLines) { | ||
// The character just after the location occupied by oldNode. | ||
var charAfterOldLoc = oldLines.charAt(oldLoc.end); | ||
// The character just after the location occupied by oldNode. | ||
var charAfterOldLoc = oldLines.charAt(oldLoc.end); | ||
var newLastPos = newLines.lastPos(); | ||
var newLastPos = newLines.lastPos(); | ||
// Last character of the reprinted node. | ||
var newLastChar = newLines.prevPos(newLastPos) && | ||
newLines.charAt(newLastPos); | ||
// Last character of the reprinted node. | ||
var newLastChar = newLines.prevPos(newLastPos) && | ||
newLines.charAt(newLastPos); | ||
return newLastChar && | ||
riskyAdjoiningCharExp.test(newLastChar) && | ||
charAfterOldLoc && | ||
riskyAdjoiningCharExp.test(charAfterOldLoc); | ||
return newLastChar && | ||
riskyAdjoiningCharExp.test(newLastChar) && | ||
charAfterOldLoc && | ||
riskyAdjoiningCharExp.test(charAfterOldLoc); | ||
} | ||
function findReprints(newPath, reprints) { | ||
var newNode = newPath.getValue(); | ||
Printable.assert(newNode); | ||
var newNode = newPath.getValue(); | ||
Printable.assert(newNode); | ||
var oldNode = newNode.original; | ||
Printable.assert(oldNode); | ||
var oldNode = newNode.original; | ||
Printable.assert(oldNode); | ||
assert.deepEqual(reprints, []); | ||
assert.deepEqual(reprints, []); | ||
if (newNode.type !== oldNode.type) { | ||
return false; | ||
} | ||
if (newNode.type !== oldNode.type) { | ||
return false; | ||
} | ||
var oldPath = new FastPath(oldNode); | ||
var canReprint = findChildReprints(newPath, oldPath, reprints); | ||
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; | ||
} | ||
if (!canReprint) { | ||
// Make absolutely sure the calling code does not attempt to reprint | ||
// any nodes. | ||
reprints.length = 0; | ||
} | ||
return canReprint; | ||
return canReprint; | ||
} | ||
function findAnyReprints(newPath, oldPath, reprints) { | ||
var newNode = newPath.getValue(); | ||
var oldNode = oldPath.getValue(); | ||
var newNode = newPath.getValue(); | ||
var oldNode = oldPath.getValue(); | ||
if (newNode === oldNode) | ||
return true; | ||
if (newNode === oldNode) | ||
return true; | ||
if (isArray.check(newNode)) | ||
return findArrayReprints(newPath, oldPath, reprints); | ||
if (isArray.check(newNode)) | ||
return findArrayReprints(newPath, oldPath, reprints); | ||
if (isObject.check(newNode)) | ||
return findObjectReprints(newPath, oldPath, reprints); | ||
if (isObject.check(newNode)) | ||
return findObjectReprints(newPath, oldPath, reprints); | ||
return false; | ||
return false; | ||
} | ||
function findArrayReprints(newPath, oldPath, reprints) { | ||
var newNode = newPath.getValue(); | ||
var oldNode = oldPath.getValue(); | ||
isArray.assert(newNode); | ||
var len = newNode.length; | ||
var newNode = newPath.getValue(); | ||
var oldNode = oldPath.getValue(); | ||
if (!(isArray.check(oldNode) && | ||
oldNode.length === len)) | ||
return false; | ||
if (newNode === oldNode || | ||
newPath.valueIsDuplicate() || | ||
oldPath.valueIsDuplicate()) { | ||
return true; | ||
} | ||
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; | ||
} | ||
isArray.assert(newNode); | ||
var len = newNode.length; | ||
if (!(isArray.check(oldNode) && | ||
oldNode.length === len)) | ||
return false; | ||
for (var i = 0; i < len; ++i) { | ||
newPath.stack.push(i, newNode[i]); | ||
oldPath.stack.push(i, oldNode[i]); | ||
var canReprint = findAnyReprints(newPath, oldPath, reprints); | ||
newPath.stack.length -= 2; | ||
oldPath.stack.length -= 2; | ||
if (!canReprint) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
return true; | ||
} | ||
function findObjectReprints(newPath, oldPath, reprints) { | ||
var newNode = newPath.getValue(); | ||
isObject.assert(newNode); | ||
var newNode = newPath.getValue(); | ||
isObject.assert(newNode); | ||
if (newNode.original === null) { | ||
// If newNode.original node was set to null, reprint the node. | ||
return false; | ||
} | ||
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; | ||
var oldNode = oldPath.getValue(); | ||
if (!isObject.check(oldNode)) | ||
return false; | ||
if (Printable.check(newNode)) { | ||
if (!Printable.check(oldNode)) { | ||
return false; | ||
} | ||
if (newNode === oldNode || | ||
newPath.valueIsDuplicate() || | ||
oldPath.valueIsDuplicate()) { | ||
return true; | ||
} | ||
// Here we need to decide whether the reprinted code for newNode | ||
// is appropriate for patching into the location of oldNode. | ||
if (Printable.check(newNode)) { | ||
if (!Printable.check(oldNode)) { | ||
return false; | ||
} | ||
if (newNode.type === oldNode.type) { | ||
var childReprints = []; | ||
// Here we need to decide whether the reprinted code for newNode is | ||
// appropriate for patching into the location of oldNode. | ||
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; | ||
} | ||
if (newNode.type === oldNode.type) { | ||
var childReprints = []; | ||
return true; | ||
} | ||
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; | ||
} | ||
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) { | ||
return true; | ||
} | ||
// 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() | ||
}); | ||
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) { | ||
return true; | ||
} | ||
// 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() | ||
}); | ||
// 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 true; | ||
} | ||
return findChildReprints(newPath, oldPath, reprints); | ||
// 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); | ||
} | ||
@@ -403,139 +416,145 @@ | ||
function hasOpeningParen(oldPath) { | ||
var oldNode = oldPath.getValue(); | ||
var loc = oldNode.loc; | ||
var lines = loc && loc.lines; | ||
var oldNode = oldPath.getValue(); | ||
var loc = oldNode.loc; | ||
var lines = loc && loc.lines; | ||
if (lines) { | ||
var pos = reusablePos; | ||
pos.line = loc.start.line; | ||
pos.column = loc.start.column; | ||
if (lines) { | ||
var pos = reusablePos; | ||
pos.line = loc.start.line; | ||
pos.column = loc.start.column; | ||
while (lines.prevPos(pos)) { | ||
var ch = lines.charAt(pos); | ||
while (lines.prevPos(pos)) { | ||
var ch = lines.charAt(pos); | ||
if (ch === "(") { | ||
// If we found an opening parenthesis but it occurred before | ||
// the start of the original subtree for this reprinting, then | ||
// we must not return true for hasOpeningParen(oldPath). | ||
return comparePos(oldPath.getRootValue().loc.start, pos) <= 0; | ||
} | ||
if (ch === "(") { | ||
// If we found an opening parenthesis but it occurred before the | ||
// start of the original subtree for this reprinting, then we must | ||
// not return true for hasOpeningParen(oldPath). | ||
return comparePos(oldPath.getRootValue().loc.start, pos) <= 0; | ||
} | ||
if (nonSpaceExp.test(ch)) { | ||
return false; | ||
} | ||
} | ||
if (nonSpaceExp.test(ch)) { | ||
return false; | ||
} | ||
} | ||
} | ||
return false; | ||
return false; | ||
} | ||
function hasClosingParen(oldPath) { | ||
var oldNode = oldPath.getValue(); | ||
var loc = oldNode.loc; | ||
var lines = loc && loc.lines; | ||
var oldNode = oldPath.getValue(); | ||
var loc = oldNode.loc; | ||
var lines = loc && loc.lines; | ||
if (lines) { | ||
var pos = reusablePos; | ||
pos.line = loc.end.line; | ||
pos.column = loc.end.column; | ||
if (lines) { | ||
var pos = reusablePos; | ||
pos.line = loc.end.line; | ||
pos.column = loc.end.column; | ||
do { | ||
var ch = lines.charAt(pos); | ||
do { | ||
var ch = lines.charAt(pos); | ||
if (ch === ")") { | ||
// If we found a closing parenthesis but it occurred after the | ||
// end of the original subtree for this reprinting, then we | ||
// must not return true for hasClosingParen(oldPath). | ||
return comparePos(pos, oldPath.getRootValue().loc.end) <= 0; | ||
} | ||
if (ch === ")") { | ||
// If we found a closing parenthesis but it occurred after the end | ||
// of the original subtree for this reprinting, then we must not | ||
// return true for hasClosingParen(oldPath). | ||
return comparePos(pos, oldPath.getRootValue().loc.end) <= 0; | ||
} | ||
if (nonSpaceExp.test(ch)) { | ||
return false; | ||
} | ||
if (nonSpaceExp.test(ch)) { | ||
return false; | ||
} | ||
} while (lines.nextPos(pos)); | ||
} | ||
} while (lines.nextPos(pos)); | ||
} | ||
return false; | ||
return false; | ||
} | ||
function hasParens(oldPath) { | ||
// This logic can technically be fooled if the node has parentheses | ||
// but there are comments intervening between the parentheses and the | ||
// node. In such cases the node will be harmlessly wrapped in an | ||
// additional layer of parentheses. | ||
return hasOpeningParen(oldPath) && hasClosingParen(oldPath); | ||
// This logic can technically be fooled if the node has parentheses but | ||
// there are comments intervening between the parentheses and the | ||
// node. In such cases the node will be harmlessly wrapped in an | ||
// additional layer of parentheses. | ||
return hasOpeningParen(oldPath) && hasClosingParen(oldPath); | ||
} | ||
function findChildReprints(newPath, oldPath, reprints) { | ||
var newNode = newPath.getValue(); | ||
var oldNode = oldPath.getValue(); | ||
var newNode = newPath.getValue(); | ||
var oldNode = oldPath.getValue(); | ||
isObject.assert(newNode); | ||
isObject.assert(oldNode); | ||
isObject.assert(newNode); | ||
isObject.assert(oldNode); | ||
if (newNode.original === null) { | ||
// If newNode.original node was set to null, reprint the node. | ||
return false; | ||
} | ||
if (newNode.original === null) { | ||
// If newNode.original node was set to null, reprint the node. | ||
return false; | ||
} | ||
// If this type of node cannot come lexically first in its enclosing | ||
// statement (e.g. a function expression or object literal), and it | ||
// seems to be doing so, then the only way we can ignore this problem | ||
// and save ourselves from falling back to the pretty printer is if an | ||
// opening parenthesis happens to precede the node. For example, | ||
// (function(){ ... }()); does not need to be reprinted, even though | ||
// the FunctionExpression comes lexically first in the enclosing | ||
// ExpressionStatement and fails the hasParens test, because the | ||
// parent CallExpression passes the hasParens test. If we relied on | ||
// the path.needsParens() && !hasParens(oldNode) check below, the | ||
// absence of a closing parenthesis after the FunctionExpression would | ||
// trigger pretty-printing unnecessarily. | ||
if (!newPath.canBeFirstInStatement() && | ||
newPath.firstInStatement() && | ||
!hasOpeningParen(oldPath)) | ||
return false; | ||
// If this type of node cannot come lexically first in its enclosing | ||
// statement (e.g. a function expression or object literal), and it | ||
// seems to be doing so, then the only way we can ignore this problem | ||
// and save ourselves from falling back to the pretty printer is if an | ||
// opening parenthesis happens to precede the node. For example, | ||
// (function(){ ... }()); does not need to be reprinted, even though the | ||
// FunctionExpression comes lexically first in the enclosing | ||
// ExpressionStatement and fails the hasParens test, because the parent | ||
// CallExpression passes the hasParens test. If we relied on the | ||
// path.needsParens() && !hasParens(oldNode) check below, the absence of | ||
// a closing parenthesis after the FunctionExpression would trigger | ||
// pretty-printing unnecessarily. | ||
if (!newPath.canBeFirstInStatement() && | ||
newPath.firstInStatement() && | ||
!hasOpeningParen(oldPath)) | ||
return false; | ||
// If this node needs parentheses and will not be wrapped with | ||
// parentheses when reprinted, then return false to skip reprinting | ||
// and let it be printed generically. | ||
if (newPath.needsParens(true) && !hasParens(oldPath)) { | ||
return false; | ||
} | ||
// If this node needs parentheses and will not be wrapped with | ||
// parentheses when reprinted, then return false to skip reprinting and | ||
// let it be printed generically. | ||
if (newPath.needsParens(true) && !hasParens(oldPath)) { | ||
return false; | ||
} | ||
var keys = util.getUnionOfKeys(oldNode, newNode); | ||
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; | ||
} | ||
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; | ||
// Don't bother traversing .loc objects looking for reprintable nodes. | ||
delete keys.loc; | ||
var originalReprintCount = reprints.length; | ||
var originalReprintCount = reprints.length; | ||
for (var k in keys) { | ||
newPath.stack.push(k, types.getFieldValue(newNode, k)); | ||
oldPath.stack.push(k, types.getFieldValue(oldNode, k)); | ||
var canReprint = findAnyReprints(newPath, oldPath, reprints); | ||
newPath.stack.length -= 2; | ||
oldPath.stack.length -= 2; | ||
if (!canReprint) { | ||
return false; | ||
} | ||
for (var k in keys) { | ||
if (k.charAt(0) === "_") { | ||
// Ignore "private" AST properties added by e.g. Babel plugins and | ||
// parsers like Babylon. | ||
continue; | ||
} | ||
// 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; | ||
newPath.stack.push(k, types.getFieldValue(newNode, k)); | ||
oldPath.stack.push(k, types.getFieldValue(oldNode, k)); | ||
var canReprint = findAnyReprints(newPath, oldPath, reprints); | ||
newPath.stack.length -= 2; | ||
oldPath.stack.length -= 2; | ||
if (!canReprint) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
// 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; | ||
} |
@@ -166,6 +166,6 @@ var assert = require("assert"); | ||
if (node.type === "TemplateLiteral") { | ||
fixTemplateLiteral(node, lines); | ||
fixForLoopHead(node, lines); | ||
fixTemplateLiteral(node, lines); | ||
} else if (loc && node.decorators) { | ||
if (loc && node.decorators) { | ||
// Expand the .loc of the node responsible for printing the decorators | ||
@@ -224,4 +224,33 @@ // (here, the decorated node) so that it includes node.decorators. | ||
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; | ||
} | ||
} | ||
} | ||
fix(node.init); | ||
fix(node.test); | ||
fix(node.update); | ||
} | ||
function fixTemplateLiteral(node, lines) { | ||
assert.strictEqual(node.type, "TemplateLiteral"); | ||
if (node.type !== "TemplateLiteral") { | ||
return; | ||
} | ||
@@ -228,0 +257,0 @@ if (node.quasis.length === 0) { |
@@ -15,3 +15,3 @@ { | ||
], | ||
"version": "0.11.23", | ||
"version": "0.12.0", | ||
"homepage": "http://github.com/benjamn/recast", | ||
@@ -33,2 +33,3 @@ "repository": { | ||
"ast-types": "0.9.6", | ||
"core-js": "^2.4.1", | ||
"esprima": "~3.1.0", | ||
@@ -39,5 +40,8 @@ "private": "~0.1.5", | ||
"devDependencies": { | ||
"babel-core": "^6.23.1", | ||
"babel-preset-es2015": "^6.22.0", | ||
"babylon": "~6.15.0", | ||
"esprima-fb": "^15001.1001.0-dev-harmony-fb", | ||
"mocha": "~3.1.2" | ||
"mocha": "~3.1.2", | ||
"reify": "^0.4.16" | ||
}, | ||
@@ -44,0 +48,0 @@ "engines": { |
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
4358
164971
5
6
+ Addedcore-js@^2.4.1
+ Addedcore-js@2.6.12(transitive)