Show More
Commit Description:
submission report
Commit Description:
submission report
References:
File last commit:
Show/Diff file:
Action:
node_modules/estraverse/estraverse.js
| 782 lines
| 25.6 KiB
| application/javascript
| JavascriptLexer
|
r789 | /* | |||
Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com> | ||||
Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com> | ||||
Redistribution and use in source and binary forms, with or without | ||||
modification, are permitted provided that the following conditions are met: | ||||
* Redistributions of source code must retain the above copyright | ||||
notice, this list of conditions and the following disclaimer. | ||||
* Redistributions in binary form must reproduce the above copyright | ||||
notice, this list of conditions and the following disclaimer in the | ||||
documentation and/or other materials provided with the distribution. | ||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||||
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY | ||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||
*/ | ||||
/*jslint vars:false, bitwise:true*/ | ||||
/*jshint indent:4*/ | ||||
/*global exports:true*/ | ||||
(function clone(exports) { | ||||
'use strict'; | ||||
var Syntax, | ||||
VisitorOption, | ||||
VisitorKeys, | ||||
BREAK, | ||||
SKIP, | ||||
REMOVE; | ||||
function deepCopy(obj) { | ||||
var ret = {}, key, val; | ||||
for (key in obj) { | ||||
if (obj.hasOwnProperty(key)) { | ||||
val = obj[key]; | ||||
if (typeof val === 'object' && val !== null) { | ||||
ret[key] = deepCopy(val); | ||||
} else { | ||||
ret[key] = val; | ||||
} | ||||
} | ||||
} | ||||
return ret; | ||||
} | ||||
// based on LLVM libc++ upper_bound / lower_bound | ||||
// MIT License | ||||
function upperBound(array, func) { | ||||
var diff, len, i, current; | ||||
len = array.length; | ||||
i = 0; | ||||
while (len) { | ||||
diff = len >>> 1; | ||||
current = i + diff; | ||||
if (func(array[current])) { | ||||
len = diff; | ||||
} else { | ||||
i = current + 1; | ||||
len -= diff + 1; | ||||
} | ||||
} | ||||
return i; | ||||
} | ||||
Syntax = { | ||||
AssignmentExpression: 'AssignmentExpression', | ||||
AssignmentPattern: 'AssignmentPattern', | ||||
ArrayExpression: 'ArrayExpression', | ||||
ArrayPattern: 'ArrayPattern', | ||||
ArrowFunctionExpression: 'ArrowFunctionExpression', | ||||
AwaitExpression: 'AwaitExpression', // CAUTION: It's deferred to ES7. | ||||
BlockStatement: 'BlockStatement', | ||||
BinaryExpression: 'BinaryExpression', | ||||
BreakStatement: 'BreakStatement', | ||||
CallExpression: 'CallExpression', | ||||
CatchClause: 'CatchClause', | ||||
ClassBody: 'ClassBody', | ||||
ClassDeclaration: 'ClassDeclaration', | ||||
ClassExpression: 'ClassExpression', | ||||
ComprehensionBlock: 'ComprehensionBlock', // CAUTION: It's deferred to ES7. | ||||
ComprehensionExpression: 'ComprehensionExpression', // CAUTION: It's deferred to ES7. | ||||
ConditionalExpression: 'ConditionalExpression', | ||||
ContinueStatement: 'ContinueStatement', | ||||
DebuggerStatement: 'DebuggerStatement', | ||||
DirectiveStatement: 'DirectiveStatement', | ||||
DoWhileStatement: 'DoWhileStatement', | ||||
EmptyStatement: 'EmptyStatement', | ||||
ExportAllDeclaration: 'ExportAllDeclaration', | ||||
ExportDefaultDeclaration: 'ExportDefaultDeclaration', | ||||
ExportNamedDeclaration: 'ExportNamedDeclaration', | ||||
ExportSpecifier: 'ExportSpecifier', | ||||
ExpressionStatement: 'ExpressionStatement', | ||||
ForStatement: 'ForStatement', | ||||
ForInStatement: 'ForInStatement', | ||||
ForOfStatement: 'ForOfStatement', | ||||
FunctionDeclaration: 'FunctionDeclaration', | ||||
FunctionExpression: 'FunctionExpression', | ||||
GeneratorExpression: 'GeneratorExpression', // CAUTION: It's deferred to ES7. | ||||
Identifier: 'Identifier', | ||||
IfStatement: 'IfStatement', | ||||
ImportExpression: 'ImportExpression', | ||||
ImportDeclaration: 'ImportDeclaration', | ||||
ImportDefaultSpecifier: 'ImportDefaultSpecifier', | ||||
ImportNamespaceSpecifier: 'ImportNamespaceSpecifier', | ||||
ImportSpecifier: 'ImportSpecifier', | ||||
Literal: 'Literal', | ||||
LabeledStatement: 'LabeledStatement', | ||||
LogicalExpression: 'LogicalExpression', | ||||
MemberExpression: 'MemberExpression', | ||||
MetaProperty: 'MetaProperty', | ||||
MethodDefinition: 'MethodDefinition', | ||||
ModuleSpecifier: 'ModuleSpecifier', | ||||
NewExpression: 'NewExpression', | ||||
ObjectExpression: 'ObjectExpression', | ||||
ObjectPattern: 'ObjectPattern', | ||||
Program: 'Program', | ||||
Property: 'Property', | ||||
RestElement: 'RestElement', | ||||
ReturnStatement: 'ReturnStatement', | ||||
SequenceExpression: 'SequenceExpression', | ||||
SpreadElement: 'SpreadElement', | ||||
Super: 'Super', | ||||
SwitchStatement: 'SwitchStatement', | ||||
SwitchCase: 'SwitchCase', | ||||
TaggedTemplateExpression: 'TaggedTemplateExpression', | ||||
TemplateElement: 'TemplateElement', | ||||
TemplateLiteral: 'TemplateLiteral', | ||||
ThisExpression: 'ThisExpression', | ||||
ThrowStatement: 'ThrowStatement', | ||||
TryStatement: 'TryStatement', | ||||
UnaryExpression: 'UnaryExpression', | ||||
UpdateExpression: 'UpdateExpression', | ||||
VariableDeclaration: 'VariableDeclaration', | ||||
VariableDeclarator: 'VariableDeclarator', | ||||
WhileStatement: 'WhileStatement', | ||||
WithStatement: 'WithStatement', | ||||
YieldExpression: 'YieldExpression' | ||||
}; | ||||
VisitorKeys = { | ||||
AssignmentExpression: ['left', 'right'], | ||||
AssignmentPattern: ['left', 'right'], | ||||
ArrayExpression: ['elements'], | ||||
ArrayPattern: ['elements'], | ||||
ArrowFunctionExpression: ['params', 'body'], | ||||
AwaitExpression: ['argument'], // CAUTION: It's deferred to ES7. | ||||
BlockStatement: ['body'], | ||||
BinaryExpression: ['left', 'right'], | ||||
BreakStatement: ['label'], | ||||
CallExpression: ['callee', 'arguments'], | ||||
CatchClause: ['param', 'body'], | ||||
ClassBody: ['body'], | ||||
ClassDeclaration: ['id', 'superClass', 'body'], | ||||
ClassExpression: ['id', 'superClass', 'body'], | ||||
ComprehensionBlock: ['left', 'right'], // CAUTION: It's deferred to ES7. | ||||
ComprehensionExpression: ['blocks', 'filter', 'body'], // CAUTION: It's deferred to ES7. | ||||
ConditionalExpression: ['test', 'consequent', 'alternate'], | ||||
ContinueStatement: ['label'], | ||||
DebuggerStatement: [], | ||||
DirectiveStatement: [], | ||||
DoWhileStatement: ['body', 'test'], | ||||
EmptyStatement: [], | ||||
ExportAllDeclaration: ['source'], | ||||
ExportDefaultDeclaration: ['declaration'], | ||||
ExportNamedDeclaration: ['declaration', 'specifiers', 'source'], | ||||
ExportSpecifier: ['exported', 'local'], | ||||
ExpressionStatement: ['expression'], | ||||
ForStatement: ['init', 'test', 'update', 'body'], | ||||
ForInStatement: ['left', 'right', 'body'], | ||||
ForOfStatement: ['left', 'right', 'body'], | ||||
FunctionDeclaration: ['id', 'params', 'body'], | ||||
FunctionExpression: ['id', 'params', 'body'], | ||||
GeneratorExpression: ['blocks', 'filter', 'body'], // CAUTION: It's deferred to ES7. | ||||
Identifier: [], | ||||
IfStatement: ['test', 'consequent', 'alternate'], | ||||
ImportExpression: ['source'], | ||||
ImportDeclaration: ['specifiers', 'source'], | ||||
ImportDefaultSpecifier: ['local'], | ||||
ImportNamespaceSpecifier: ['local'], | ||||
ImportSpecifier: ['imported', 'local'], | ||||
Literal: [], | ||||
LabeledStatement: ['label', 'body'], | ||||
LogicalExpression: ['left', 'right'], | ||||
MemberExpression: ['object', 'property'], | ||||
MetaProperty: ['meta', 'property'], | ||||
MethodDefinition: ['key', 'value'], | ||||
ModuleSpecifier: [], | ||||
NewExpression: ['callee', 'arguments'], | ||||
ObjectExpression: ['properties'], | ||||
ObjectPattern: ['properties'], | ||||
Program: ['body'], | ||||
Property: ['key', 'value'], | ||||
RestElement: [ 'argument' ], | ||||
ReturnStatement: ['argument'], | ||||
SequenceExpression: ['expressions'], | ||||
SpreadElement: ['argument'], | ||||
Super: [], | ||||
SwitchStatement: ['discriminant', 'cases'], | ||||
SwitchCase: ['test', 'consequent'], | ||||
TaggedTemplateExpression: ['tag', 'quasi'], | ||||
TemplateElement: [], | ||||
TemplateLiteral: ['quasis', 'expressions'], | ||||
ThisExpression: [], | ||||
ThrowStatement: ['argument'], | ||||
TryStatement: ['block', 'handler', 'finalizer'], | ||||
UnaryExpression: ['argument'], | ||||
UpdateExpression: ['argument'], | ||||
VariableDeclaration: ['declarations'], | ||||
VariableDeclarator: ['id', 'init'], | ||||
WhileStatement: ['test', 'body'], | ||||
WithStatement: ['object', 'body'], | ||||
YieldExpression: ['argument'] | ||||
}; | ||||
// unique id | ||||
BREAK = {}; | ||||
SKIP = {}; | ||||
REMOVE = {}; | ||||
VisitorOption = { | ||||
Break: BREAK, | ||||
Skip: SKIP, | ||||
Remove: REMOVE | ||||
}; | ||||
function Reference(parent, key) { | ||||
this.parent = parent; | ||||
this.key = key; | ||||
} | ||||
Reference.prototype.replace = function replace(node) { | ||||
this.parent[this.key] = node; | ||||
}; | ||||
Reference.prototype.remove = function remove() { | ||||
if (Array.isArray(this.parent)) { | ||||
this.parent.splice(this.key, 1); | ||||
return true; | ||||
} else { | ||||
this.replace(null); | ||||
return false; | ||||
} | ||||
}; | ||||
function Element(node, path, wrap, ref) { | ||||
this.node = node; | ||||
this.path = path; | ||||
this.wrap = wrap; | ||||
this.ref = ref; | ||||
} | ||||
function Controller() { } | ||||
// API: | ||||
// return property path array from root to current node | ||||
Controller.prototype.path = function path() { | ||||
var i, iz, j, jz, result, element; | ||||
function addToPath(result, path) { | ||||
if (Array.isArray(path)) { | ||||
for (j = 0, jz = path.length; j < jz; ++j) { | ||||
result.push(path[j]); | ||||
} | ||||
} else { | ||||
result.push(path); | ||||
} | ||||
} | ||||
// root node | ||||
if (!this.__current.path) { | ||||
return null; | ||||
} | ||||
// first node is sentinel, second node is root element | ||||
result = []; | ||||
for (i = 2, iz = this.__leavelist.length; i < iz; ++i) { | ||||
element = this.__leavelist[i]; | ||||
addToPath(result, element.path); | ||||
} | ||||
addToPath(result, this.__current.path); | ||||
return result; | ||||
}; | ||||
// API: | ||||
// return type of current node | ||||
Controller.prototype.type = function () { | ||||
var node = this.current(); | ||||
return node.type || this.__current.wrap; | ||||
}; | ||||
// API: | ||||
// return array of parent elements | ||||
Controller.prototype.parents = function parents() { | ||||
var i, iz, result; | ||||
// first node is sentinel | ||||
result = []; | ||||
for (i = 1, iz = this.__leavelist.length; i < iz; ++i) { | ||||
result.push(this.__leavelist[i].node); | ||||
} | ||||
return result; | ||||
}; | ||||
// API: | ||||
// return current node | ||||
Controller.prototype.current = function current() { | ||||
return this.__current.node; | ||||
}; | ||||
Controller.prototype.__execute = function __execute(callback, element) { | ||||
var previous, result; | ||||
result = undefined; | ||||
previous = this.__current; | ||||
this.__current = element; | ||||
this.__state = null; | ||||
if (callback) { | ||||
result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node); | ||||
} | ||||
this.__current = previous; | ||||
return result; | ||||
}; | ||||
// API: | ||||
// notify control skip / break | ||||
Controller.prototype.notify = function notify(flag) { | ||||
this.__state = flag; | ||||
}; | ||||
// API: | ||||
// skip child nodes of current node | ||||
Controller.prototype.skip = function () { | ||||
this.notify(SKIP); | ||||
}; | ||||
// API: | ||||
// break traversals | ||||
Controller.prototype['break'] = function () { | ||||
this.notify(BREAK); | ||||
}; | ||||
// API: | ||||
// remove node | ||||
Controller.prototype.remove = function () { | ||||
this.notify(REMOVE); | ||||
}; | ||||
Controller.prototype.__initialize = function(root, visitor) { | ||||
this.visitor = visitor; | ||||
this.root = root; | ||||
this.__worklist = []; | ||||
this.__leavelist = []; | ||||
this.__current = null; | ||||
this.__state = null; | ||||
this.__fallback = null; | ||||
if (visitor.fallback === 'iteration') { | ||||
this.__fallback = Object.keys; | ||||
} else if (typeof visitor.fallback === 'function') { | ||||
this.__fallback = visitor.fallback; | ||||
} | ||||
this.__keys = VisitorKeys; | ||||
if (visitor.keys) { | ||||
this.__keys = Object.assign(Object.create(this.__keys), visitor.keys); | ||||
} | ||||
}; | ||||
function isNode(node) { | ||||
if (node == null) { | ||||
return false; | ||||
} | ||||
return typeof node === 'object' && typeof node.type === 'string'; | ||||
} | ||||
function isProperty(nodeType, key) { | ||||
return (nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === key; | ||||
} | ||||
Controller.prototype.traverse = function traverse(root, visitor) { | ||||
var worklist, | ||||
leavelist, | ||||
element, | ||||
node, | ||||
nodeType, | ||||
ret, | ||||
key, | ||||
current, | ||||
current2, | ||||
candidates, | ||||
candidate, | ||||
sentinel; | ||||
this.__initialize(root, visitor); | ||||
sentinel = {}; | ||||
// reference | ||||
worklist = this.__worklist; | ||||
leavelist = this.__leavelist; | ||||
// initialize | ||||
worklist.push(new Element(root, null, null, null)); | ||||
leavelist.push(new Element(null, null, null, null)); | ||||
while (worklist.length) { | ||||
element = worklist.pop(); | ||||
if (element === sentinel) { | ||||
element = leavelist.pop(); | ||||
ret = this.__execute(visitor.leave, element); | ||||
if (this.__state === BREAK || ret === BREAK) { | ||||
return; | ||||
} | ||||
continue; | ||||
} | ||||
if (element.node) { | ||||
ret = this.__execute(visitor.enter, element); | ||||
if (this.__state === BREAK || ret === BREAK) { | ||||
return; | ||||
} | ||||
worklist.push(sentinel); | ||||
leavelist.push(element); | ||||
if (this.__state === SKIP || ret === SKIP) { | ||||
continue; | ||||
} | ||||
node = element.node; | ||||
nodeType = node.type || element.wrap; | ||||
candidates = this.__keys[nodeType]; | ||||
if (!candidates) { | ||||
if (this.__fallback) { | ||||
candidates = this.__fallback(node); | ||||
} else { | ||||
throw new Error('Unknown node type ' + nodeType + '.'); | ||||
} | ||||
} | ||||
current = candidates.length; | ||||
while ((current -= 1) >= 0) { | ||||
key = candidates[current]; | ||||
candidate = node[key]; | ||||
if (!candidate) { | ||||
continue; | ||||
} | ||||
if (Array.isArray(candidate)) { | ||||
current2 = candidate.length; | ||||
while ((current2 -= 1) >= 0) { | ||||
if (!candidate[current2]) { | ||||
continue; | ||||
} | ||||
if (isProperty(nodeType, candidates[current])) { | ||||
element = new Element(candidate[current2], [key, current2], 'Property', null); | ||||
} else if (isNode(candidate[current2])) { | ||||
element = new Element(candidate[current2], [key, current2], null, null); | ||||
} else { | ||||
continue; | ||||
} | ||||
worklist.push(element); | ||||
} | ||||
} else if (isNode(candidate)) { | ||||
worklist.push(new Element(candidate, key, null, null)); | ||||
} | ||||
} | ||||
} | ||||
} | ||||
}; | ||||
Controller.prototype.replace = function replace(root, visitor) { | ||||
var worklist, | ||||
leavelist, | ||||
node, | ||||
nodeType, | ||||
target, | ||||
element, | ||||
current, | ||||
current2, | ||||
candidates, | ||||
candidate, | ||||
sentinel, | ||||
outer, | ||||
key; | ||||
function removeElem(element) { | ||||
var i, | ||||
key, | ||||
nextElem, | ||||
parent; | ||||
if (element.ref.remove()) { | ||||
// When the reference is an element of an array. | ||||
key = element.ref.key; | ||||
parent = element.ref.parent; | ||||
// If removed from array, then decrease following items' keys. | ||||
i = worklist.length; | ||||
while (i--) { | ||||
nextElem = worklist[i]; | ||||
if (nextElem.ref && nextElem.ref.parent === parent) { | ||||
if (nextElem.ref.key < key) { | ||||
break; | ||||
} | ||||
--nextElem.ref.key; | ||||
} | ||||
} | ||||
} | ||||
} | ||||
this.__initialize(root, visitor); | ||||
sentinel = {}; | ||||
// reference | ||||
worklist = this.__worklist; | ||||
leavelist = this.__leavelist; | ||||
// initialize | ||||
outer = { | ||||
root: root | ||||
}; | ||||
element = new Element(root, null, null, new Reference(outer, 'root')); | ||||
worklist.push(element); | ||||
leavelist.push(element); | ||||
while (worklist.length) { | ||||
element = worklist.pop(); | ||||
if (element === sentinel) { | ||||
element = leavelist.pop(); | ||||
target = this.__execute(visitor.leave, element); | ||||
// node may be replaced with null, | ||||
// so distinguish between undefined and null in this place | ||||
if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) { | ||||
// replace | ||||
element.ref.replace(target); | ||||
} | ||||
if (this.__state === REMOVE || target === REMOVE) { | ||||
removeElem(element); | ||||
} | ||||
if (this.__state === BREAK || target === BREAK) { | ||||
return outer.root; | ||||
} | ||||
continue; | ||||
} | ||||
target = this.__execute(visitor.enter, element); | ||||
// node may be replaced with null, | ||||
// so distinguish between undefined and null in this place | ||||
if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) { | ||||
// replace | ||||
element.ref.replace(target); | ||||
element.node = target; | ||||
} | ||||
if (this.__state === REMOVE || target === REMOVE) { | ||||
removeElem(element); | ||||
element.node = null; | ||||
} | ||||
if (this.__state === BREAK || target === BREAK) { | ||||
return outer.root; | ||||
} | ||||
// node may be null | ||||
node = element.node; | ||||
if (!node) { | ||||
continue; | ||||
} | ||||
worklist.push(sentinel); | ||||
leavelist.push(element); | ||||
if (this.__state === SKIP || target === SKIP) { | ||||
continue; | ||||
} | ||||
nodeType = node.type || element.wrap; | ||||
candidates = this.__keys[nodeType]; | ||||
if (!candidates) { | ||||
if (this.__fallback) { | ||||
candidates = this.__fallback(node); | ||||
} else { | ||||
throw new Error('Unknown node type ' + nodeType + '.'); | ||||
} | ||||
} | ||||
current = candidates.length; | ||||
while ((current -= 1) >= 0) { | ||||
key = candidates[current]; | ||||
candidate = node[key]; | ||||
if (!candidate) { | ||||
continue; | ||||
} | ||||
if (Array.isArray(candidate)) { | ||||
current2 = candidate.length; | ||||
while ((current2 -= 1) >= 0) { | ||||
if (!candidate[current2]) { | ||||
continue; | ||||
} | ||||
if (isProperty(nodeType, candidates[current])) { | ||||
element = new Element(candidate[current2], [key, current2], 'Property', new Reference(candidate, current2)); | ||||
} else if (isNode(candidate[current2])) { | ||||
element = new Element(candidate[current2], [key, current2], null, new Reference(candidate, current2)); | ||||
} else { | ||||
continue; | ||||
} | ||||
worklist.push(element); | ||||
} | ||||
} else if (isNode(candidate)) { | ||||
worklist.push(new Element(candidate, key, null, new Reference(node, key))); | ||||
} | ||||
} | ||||
} | ||||
return outer.root; | ||||
}; | ||||
function traverse(root, visitor) { | ||||
var controller = new Controller(); | ||||
return controller.traverse(root, visitor); | ||||
} | ||||
function replace(root, visitor) { | ||||
var controller = new Controller(); | ||||
return controller.replace(root, visitor); | ||||
} | ||||
function extendCommentRange(comment, tokens) { | ||||
var target; | ||||
target = upperBound(tokens, function search(token) { | ||||
return token.range[0] > comment.range[0]; | ||||
}); | ||||
comment.extendedRange = [comment.range[0], comment.range[1]]; | ||||
if (target !== tokens.length) { | ||||
comment.extendedRange[1] = tokens[target].range[0]; | ||||
} | ||||
target -= 1; | ||||
if (target >= 0) { | ||||
comment.extendedRange[0] = tokens[target].range[1]; | ||||
} | ||||
return comment; | ||||
} | ||||
function attachComments(tree, providedComments, tokens) { | ||||
// At first, we should calculate extended comment ranges. | ||||
var comments = [], comment, len, i, cursor; | ||||
if (!tree.range) { | ||||
throw new Error('attachComments needs range information'); | ||||
} | ||||
// tokens array is empty, we attach comments to tree as 'leadingComments' | ||||
if (!tokens.length) { | ||||
if (providedComments.length) { | ||||
for (i = 0, len = providedComments.length; i < len; i += 1) { | ||||
comment = deepCopy(providedComments[i]); | ||||
comment.extendedRange = [0, tree.range[0]]; | ||||
comments.push(comment); | ||||
} | ||||
tree.leadingComments = comments; | ||||
} | ||||
return tree; | ||||
} | ||||
for (i = 0, len = providedComments.length; i < len; i += 1) { | ||||
comments.push(extendCommentRange(deepCopy(providedComments[i]), tokens)); | ||||
} | ||||
// This is based on John Freeman's implementation. | ||||
cursor = 0; | ||||
traverse(tree, { | ||||
enter: function (node) { | ||||
var comment; | ||||
while (cursor < comments.length) { | ||||
comment = comments[cursor]; | ||||
if (comment.extendedRange[1] > node.range[0]) { | ||||
break; | ||||
} | ||||
if (comment.extendedRange[1] === node.range[0]) { | ||||
if (!node.leadingComments) { | ||||
node.leadingComments = []; | ||||
} | ||||
node.leadingComments.push(comment); | ||||
comments.splice(cursor, 1); | ||||
} else { | ||||
cursor += 1; | ||||
} | ||||
} | ||||
// already out of owned node | ||||
if (cursor === comments.length) { | ||||
return VisitorOption.Break; | ||||
} | ||||
if (comments[cursor].extendedRange[0] > node.range[1]) { | ||||
return VisitorOption.Skip; | ||||
} | ||||
} | ||||
}); | ||||
cursor = 0; | ||||
traverse(tree, { | ||||
leave: function (node) { | ||||
var comment; | ||||
while (cursor < comments.length) { | ||||
comment = comments[cursor]; | ||||
if (node.range[1] < comment.extendedRange[0]) { | ||||
break; | ||||
} | ||||
if (node.range[1] === comment.extendedRange[0]) { | ||||
if (!node.trailingComments) { | ||||
node.trailingComments = []; | ||||
} | ||||
node.trailingComments.push(comment); | ||||
comments.splice(cursor, 1); | ||||
} else { | ||||
cursor += 1; | ||||
} | ||||
} | ||||
// already out of owned node | ||||
if (cursor === comments.length) { | ||||
return VisitorOption.Break; | ||||
} | ||||
if (comments[cursor].extendedRange[0] > node.range[1]) { | ||||
return VisitorOption.Skip; | ||||
} | ||||
} | ||||
}); | ||||
return tree; | ||||
} | ||||
exports.version = require('./package.json').version; | ||||
exports.Syntax = Syntax; | ||||
exports.traverse = traverse; | ||||
exports.replace = replace; | ||||
exports.attachComments = attachComments; | ||||
exports.VisitorKeys = VisitorKeys; | ||||
exports.VisitorOption = VisitorOption; | ||||
exports.Controller = Controller; | ||||
exports.cloneEnvironment = function () { return clone({}); }; | ||||
return exports; | ||||
}(exports)); | ||||
/* vim: set sw=4 ts=4 et tw=80 : */ | ||||