even more split up of path methods
This commit is contained in:
parent
7d88a1ca0b
commit
0bf95d6aea
10
src/babel/traversal/path/README.md
Normal file
10
src/babel/traversal/path/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
- `ancestry` - Methods that retrieve or validate anything related to the current paths ancestry.
|
||||
- `context` - Methods responsible for maintaing TraversalContexts.
|
||||
- `conversion` - Methods that convert the path node into another node or some other type of data.
|
||||
- `evaluation` - Methods responsible for statically evaluating code.
|
||||
- `modification` - Methods that modify the path/node in some ways.
|
||||
- `resolution` - Methods responsible for type inferrence etc.
|
||||
- `replacement` - Methods responsible for replacing a node with another.
|
||||
- `removal` - Methods responsible for removing a node.
|
||||
- `family` - Methods responsible for dealing with/retrieving children or siblings.
|
||||
- `verification` - Methodsresponsible for introspecting the current path for certain values.
|
||||
210
src/babel/traversal/path/context.js
Normal file
210
src/babel/traversal/path/context.js
Normal file
@ -0,0 +1,210 @@
|
||||
import * as messages from "../../messages";
|
||||
import NodePath from "./index";
|
||||
import traverse from "../index";
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function call(key) {
|
||||
var node = this.node;
|
||||
if (!node) return;
|
||||
|
||||
var opts = this.opts;
|
||||
if (!opts[key] && !opts[node.type]) return;
|
||||
|
||||
var fns = [].concat(opts[key]);
|
||||
if (opts[node.type]) fns = fns.concat(opts[node.type][key]);
|
||||
|
||||
for (var fn of (fns: Array)) {
|
||||
if (!fn) continue;
|
||||
|
||||
let node = this.node;
|
||||
if (!node) return;
|
||||
|
||||
var previousType = this.type;
|
||||
|
||||
// call the function with the params (node, parent, scope, state)
|
||||
var replacement = fn.call(this, node, this.parent, this.scope, this.state);
|
||||
|
||||
if (replacement) {
|
||||
this.replaceWith(replacement, true);
|
||||
}
|
||||
|
||||
if (this.shouldStop || this.shouldSkip || this.removed) return;
|
||||
|
||||
if (previousType !== this.type) {
|
||||
this.queueNode(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function isBlacklisted(): boolean {
|
||||
var blacklist = this.opts.blacklist;
|
||||
return blacklist && blacklist.indexOf(this.node.type) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function visit(): boolean {
|
||||
if (this.isBlacklisted()) return false;
|
||||
if (this.opts.shouldSkip && this.opts.shouldSkip(this)) return false;
|
||||
|
||||
this.call("enter");
|
||||
|
||||
if (this.shouldSkip) {
|
||||
return this.shouldStop;
|
||||
}
|
||||
|
||||
var node = this.node;
|
||||
var opts = this.opts;
|
||||
|
||||
if (node) {
|
||||
if (Array.isArray(node)) {
|
||||
// traverse over these replacement nodes we purposely don't call exitNode
|
||||
// as the original node has been destroyed
|
||||
for (var i = 0; i < node.length; i++) {
|
||||
traverse.node(node[i], opts, this.scope, this.state, this);
|
||||
}
|
||||
} else {
|
||||
traverse.node(node, opts, this.scope, this.state, this);
|
||||
this.call("exit");
|
||||
}
|
||||
}
|
||||
|
||||
return this.shouldStop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function skip() {
|
||||
this.shouldSkip = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function stop() {
|
||||
this.shouldStop = true;
|
||||
this.shouldSkip = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function setScope(file?) {
|
||||
if (this.opts && this.opts.noScope) return;
|
||||
|
||||
var target = this.context || this.parentPath;
|
||||
this.scope = NodePath.getScope(this, target && target.scope, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function setContext(context, file) {
|
||||
this.shouldSkip = false;
|
||||
this.shouldStop = false;
|
||||
this.removed = false;
|
||||
|
||||
if (context) {
|
||||
this.context = context;
|
||||
this.state = context.state;
|
||||
this.opts = context.opts;
|
||||
}
|
||||
|
||||
var log = file && this.type === "Program";
|
||||
if (log) file.log.debug("Start scope building");
|
||||
this.setScope(file);
|
||||
if (log) file.log.debug("End scope building");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function update() {
|
||||
if (this.node === this.container[this.key]) return;
|
||||
|
||||
// grrr, path key is out of sync. this is likely due to a modification to the AST
|
||||
// not through our path APIs
|
||||
|
||||
if (Array.isArray(this.container)) {
|
||||
for (var i = 0; i < this.container.length; i++) {
|
||||
if (this.container[i] === this.node) {
|
||||
return this.setKey(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var key in this.container) {
|
||||
if (this.container[key] === this.node) {
|
||||
return this.setKey(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(messages.get("lostTrackNodePath"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function shiftContext() {
|
||||
this.contexts.shift();
|
||||
this.setContext(this.contexts[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function unshiftContext(context) {
|
||||
this.contexts.unshift(context);
|
||||
this.setContext(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function setup(parentPath, key) {
|
||||
this.parentPath = parentPath || this.parentPath;
|
||||
this.setKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function setKey(key) {
|
||||
this.key = key;
|
||||
this.node = this.container[this.key];
|
||||
this.type = this.node && this.node.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function queueNode(path) {
|
||||
for (var context of (this.contexts: Array)) {
|
||||
if (context.queue) {
|
||||
context.queue.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
118
src/babel/traversal/path/family.js
Normal file
118
src/babel/traversal/path/family.js
Normal file
@ -0,0 +1,118 @@
|
||||
import NodePath from "./index";
|
||||
import * as t from "../../types";
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function getStatementParent(): ?NodePath {
|
||||
var path = this;
|
||||
|
||||
do {
|
||||
if (!path.parentPath || (Array.isArray(path.container) && path.isStatement())) {
|
||||
break;
|
||||
} else {
|
||||
path = path.parentPath;
|
||||
}
|
||||
} while (path);
|
||||
|
||||
if (path && (path.isProgram() || path.isFile())) {
|
||||
throw new Error("File/Program node, we can't possibly find a statement parent to this");
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function getCompletionRecords(): Array<NodePath> {
|
||||
var paths = [];
|
||||
|
||||
var add = function (path) {
|
||||
if (path) paths = paths.concat(path.getCompletionRecords());
|
||||
};
|
||||
|
||||
if (this.isIfStatement()) {
|
||||
add(this.get("consequent"));
|
||||
add(this.get("alternate"));
|
||||
} else if (this.isDoExpression() || this.isFor() || this.isWhile()) {
|
||||
add(this.get("body"));
|
||||
} else if (this.isProgram() || this.isBlockStatement()) {
|
||||
add(this.get("body").pop());
|
||||
} else if (this.isFunction()) {
|
||||
return this.get("body").getCompletionRecords();
|
||||
} else {
|
||||
paths.push(this);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function getSibling(key) {
|
||||
return NodePath.get(this.parentPath, this.parent, this.container, key, this.file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function get(key: string): NodePath {
|
||||
var parts = key.split(".");
|
||||
if (parts.length === 1) { // "foo"
|
||||
return this._getKey(key);
|
||||
} else { // "foo.bar"
|
||||
return this._getPattern(parts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function _getKey(key) {
|
||||
var node = this.node;
|
||||
var container = node[key];
|
||||
|
||||
if (Array.isArray(container)) {
|
||||
// requested a container so give them all the paths
|
||||
return container.map((_, i) => {
|
||||
return NodePath.get(this, node, container, i).setContext();
|
||||
});
|
||||
} else {
|
||||
return NodePath.get(this, node, node, key).setContext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function _getPattern(parts) {
|
||||
var path = this;
|
||||
for (var part of (parts: Array)) {
|
||||
if (part === ".") {
|
||||
path = path.parentPath;
|
||||
} else {
|
||||
if (Array.isArray(path)) {
|
||||
path = path[part];
|
||||
} else {
|
||||
path = path.get(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function getBindingIdentifiers() {
|
||||
return t.getBindingIdentifiers(this.node);
|
||||
}
|
||||
@ -1,7 +1,6 @@
|
||||
import PathHoister from "./lib/hoister";
|
||||
import * as virtualTypes from "./lib/virtual-types";
|
||||
import * as messages from "../../messages";
|
||||
import * as contextual from "./lib/contextual";
|
||||
import isBoolean from "lodash/lang/isBoolean";
|
||||
import isNumber from "lodash/lang/isNumber";
|
||||
import isRegExp from "lodash/lang/isRegExp";
|
||||
@ -66,177 +65,6 @@ export default class NodePath {
|
||||
return ourScope;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this node was a part of the original AST.
|
||||
*/
|
||||
|
||||
isUser() {
|
||||
return this.node && !!this.node.loc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this node was generated by us and not a part of the original AST.
|
||||
*/
|
||||
|
||||
isGenerated() {
|
||||
return !this.isUser();
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
queueNode(path) {
|
||||
for (var context of (this.contexts: Array)) {
|
||||
if (context.queue) {
|
||||
context.queue.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
insertBefore(nodes) {
|
||||
nodes = this._verifyNodeList(nodes);
|
||||
|
||||
if (this.parentPath.isExpressionStatement() || this.parentPath.isLabeledStatement()) {
|
||||
return this.parentPath.insertBefore(nodes);
|
||||
} else if (this.isPreviousType("Expression") || (this.parentPath.isForStatement() && this.key === "init")) {
|
||||
if (this.node) nodes.push(this.node);
|
||||
this.replaceExpressionWithStatements(nodes);
|
||||
} else if (this.isPreviousType("Statement") || !this.type) {
|
||||
this._maybePopFromStatements(nodes);
|
||||
if (Array.isArray(this.container)) {
|
||||
this._containerInsertBefore(nodes);
|
||||
} else if (this.isStatementOrBlock()) {
|
||||
if (this.node) nodes.push(this.node);
|
||||
this.node = this.container[this.key] = t.blockStatement(nodes);
|
||||
} else {
|
||||
throw new Error("We don't know what to do with this node type. We were previously a Statement but we can't fit in here?");
|
||||
}
|
||||
} else {
|
||||
throw new Error("No clue what to do with this node type.");
|
||||
}
|
||||
}
|
||||
|
||||
_containerInsert(from, nodes) {
|
||||
this.updateSiblingKeys(from, nodes.length);
|
||||
|
||||
var paths = [];
|
||||
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var to = from + i;
|
||||
var node = nodes[i];
|
||||
this.container.splice(to, 0, node);
|
||||
|
||||
if (this.context) {
|
||||
var path = this.context.create(this.parent, this.container, to);
|
||||
paths.push(path);
|
||||
this.queueNode(path);
|
||||
} else {
|
||||
paths.push(NodePath.get(this, node, this.container, to));
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
_containerInsertBefore(nodes) {
|
||||
return this._containerInsert(this.key, nodes);
|
||||
}
|
||||
|
||||
_containerInsertAfter(nodes) {
|
||||
return this._containerInsert(this.key + 1, nodes);
|
||||
}
|
||||
|
||||
_maybePopFromStatements(nodes) {
|
||||
var last = nodes[nodes.length - 1];
|
||||
if (t.isExpressionStatement(last) && t.isIdentifier(last.expression) && !this.isCompletionRecord()) {
|
||||
nodes.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
isCompletionRecord() {
|
||||
var path = this;
|
||||
|
||||
do {
|
||||
var container = path.container;
|
||||
|
||||
if (path.isFunction()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(container) && path.key !== container.length - 1) {
|
||||
return false;
|
||||
}
|
||||
} while ((path = path.parentPath) && !path.isProgram());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
isStatementOrBlock() {
|
||||
if (t.isLabeledStatement(this.parent) || t.isBlockStatement(this.container)) {
|
||||
return false;
|
||||
} else {
|
||||
return includes(t.STATEMENT_OR_BLOCK_KEYS, this.key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
insertAfter(nodes) {
|
||||
nodes = this._verifyNodeList(nodes);
|
||||
|
||||
if (this.parentPath.isExpressionStatement() || this.parentPath.isLabeledStatement()) {
|
||||
return this.parentPath.insertAfter(nodes);
|
||||
} else if (this.isPreviousType("Expression") || (this.parentPath.isForStatement() && this.key === "init")) {
|
||||
if (this.node) {
|
||||
var temp = this.scope.generateDeclaredUidIdentifier();
|
||||
nodes.unshift(t.expressionStatement(t.assignmentExpression("=", temp, this.node)));
|
||||
nodes.push(t.expressionStatement(temp));
|
||||
}
|
||||
this.replaceExpressionWithStatements(nodes);
|
||||
} else if (this.isPreviousType("Statement") || !this.type) {
|
||||
this._maybePopFromStatements(nodes);
|
||||
if (Array.isArray(this.container)) {
|
||||
this._containerInsertAfter(nodes);
|
||||
} else if (this.isStatementOrBlock()) {
|
||||
if (this.node) nodes.unshift(this.node);
|
||||
this.node = this.container[this.key] = t.blockStatement(nodes);
|
||||
} else {
|
||||
throw new Error("We don't know what to do with this node type. We were previously a Statement but we can't fit in here?");
|
||||
}
|
||||
} else {
|
||||
throw new Error("No clue what to do with this node type.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
updateSiblingKeys(fromIndex, incrementBy) {
|
||||
var paths = this.container._paths;
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
let path = paths[i];
|
||||
if (path.key >= fromIndex) {
|
||||
path.key += incrementBy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
@ -255,192 +83,6 @@ export default class NodePath {
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
setScope(file?) {
|
||||
if (this.opts && this.opts.noScope) return;
|
||||
|
||||
var target = this.context || this.parentPath;
|
||||
this.scope = NodePath.getScope(this, target && target.scope, file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
setContext(context, file) {
|
||||
this.shouldSkip = false;
|
||||
this.shouldStop = false;
|
||||
this.removed = false;
|
||||
|
||||
if (context) {
|
||||
this.context = context;
|
||||
this.state = context.state;
|
||||
this.opts = context.opts;
|
||||
}
|
||||
|
||||
var log = file && this.type === "Program";
|
||||
if (log) file.log.debug("Start scope building");
|
||||
this.setScope(file);
|
||||
if (log) file.log.debug("End scope building");
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
update() {
|
||||
if (this.node === this.container[this.key]) return;
|
||||
|
||||
// grrr, path key is out of sync. this is likely due to a modification to the AST
|
||||
// not through our path APIs
|
||||
|
||||
if (Array.isArray(this.container)) {
|
||||
for (var i = 0; i < this.container.length; i++) {
|
||||
if (this.container[i] === this.node) {
|
||||
return this.setKey(i);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var key in this.container) {
|
||||
if (this.container[key] === this.node) {
|
||||
return this.setKey(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(messages.get("lostTrackNodePath"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
shiftContext() {
|
||||
this.contexts.shift();
|
||||
this.setContext(this.contexts[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
unshiftContext(context) {
|
||||
this.contexts.unshift(context);
|
||||
this.setContext(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
setup(parentPath, key) {
|
||||
this.parentPath = parentPath || this.parentPath;
|
||||
this.setKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
setKey(key) {
|
||||
this.key = key;
|
||||
this.node = this.container[this.key];
|
||||
this.type = this.node && this.node.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Share comments amongst siblings.
|
||||
*/
|
||||
|
||||
shareCommentsWithSiblings() {
|
||||
var node = this.node;
|
||||
if (!node) return;
|
||||
|
||||
var trailing = node.trailingComments;
|
||||
var leading = node.leadingComments;
|
||||
if (!trailing && !leading) return;
|
||||
|
||||
var prev = this.getSibling(this.key - 1);
|
||||
var next = this.getSibling(this.key + 1);
|
||||
|
||||
if (!prev.node) prev = next;
|
||||
if (!next.node) next = prev;
|
||||
|
||||
prev.giveComments("trailing", leading);
|
||||
next.giveComments("leading", trailing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Give node `comments` of the specified `type`.
|
||||
*/
|
||||
|
||||
giveComments(type: string, comments: Array) {
|
||||
if (!comments) return;
|
||||
|
||||
var node = this.node;
|
||||
if (!node) return;
|
||||
|
||||
var key = `${type}Comments`;
|
||||
|
||||
if (node[key]) {
|
||||
node[key] = node[key].concat(comments);
|
||||
} else {
|
||||
node[key] = comments;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
remove() {
|
||||
if (this._contextualCall("removers", "pre")) return;
|
||||
|
||||
this.shareCommentsWithSiblings();
|
||||
this._remove();
|
||||
this.removed = true;
|
||||
|
||||
this._contextualCall("removers", "post");
|
||||
}
|
||||
|
||||
_contextualCall(type, position) {
|
||||
for (var fn of (contextual[type][position]: Array)) {
|
||||
if (fn(this, this.parentPath)) return;
|
||||
}
|
||||
}
|
||||
|
||||
_remove() {
|
||||
if (Array.isArray(this.container)) {
|
||||
this.container.splice(this.key, 1);
|
||||
this.updateSiblingKeys(this.key, -1);
|
||||
} else {
|
||||
this.container[this.key] = null;
|
||||
}
|
||||
this.node = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
skip() {
|
||||
this.shouldSkip = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
stop() {
|
||||
this.shouldStop = true;
|
||||
this.shouldSkip = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
@ -452,392 +94,13 @@ export default class NodePath {
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
_verifyNodeList(nodes) {
|
||||
if (nodes.constructor !== Array) {
|
||||
nodes = [nodes];
|
||||
}
|
||||
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = nodes[i];
|
||||
if (!node) {
|
||||
throw new Error(`Node list has falsy node with the index of ${i}`);
|
||||
} else if (typeof node !== "object") {
|
||||
throw new Error(`Node list contains a non-object node with the index of ${i}`);
|
||||
} else if (!node.type) {
|
||||
throw new Error(`Node list contains a node without a type with the index of ${i}`);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
unshiftContainer(containerKey, nodes) {
|
||||
nodes = this._verifyNodeList(nodes);
|
||||
|
||||
// get the first path and insert our nodes before it, if it doesn't exist then it
|
||||
// doesn't matter, our nodes will be inserted anyway
|
||||
|
||||
var container = this.node[containerKey];
|
||||
var path = NodePath.get(this, this.node, container, 0);
|
||||
|
||||
return path.insertBefore(nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
pushContainer(containerKey, nodes) {
|
||||
nodes = this._verifyNodeList(nodes);
|
||||
|
||||
// get an invisible path that represents the last node + 1 and replace it with our
|
||||
// nodes, effectively inlining it
|
||||
|
||||
var container = this.node[containerKey];
|
||||
var i = container.length;
|
||||
var path = NodePath.get(this, this.node, container, i);
|
||||
|
||||
return path.replaceWith(nodes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* This checks whether or now we're in one of the following positions:
|
||||
*
|
||||
* for (KEY in right);
|
||||
* for (KEY;;);
|
||||
*
|
||||
* This is because these spots allow VariableDeclarations AND normal expressions so we need to tell the
|
||||
* path replacement that it's ok to replace this with an expression.
|
||||
*/
|
||||
|
||||
canHaveVariableDeclarationOrExpression() {
|
||||
return (this.key === "init" || this.key === "left") && this.parentPath.isFor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
getStatementParent(): ?NodePath {
|
||||
var path = this;
|
||||
|
||||
do {
|
||||
if (!path.parentPath || (Array.isArray(path.container) && path.isStatement())) {
|
||||
break;
|
||||
} else {
|
||||
path = path.parentPath;
|
||||
}
|
||||
} while (path);
|
||||
|
||||
if (path && (path.isProgram() || path.isFile())) {
|
||||
throw new Error("File/Program node, we can't possibly find a statement parent to this");
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
getCompletionRecords(): Array<NodePath> {
|
||||
var paths = [];
|
||||
|
||||
var add = function (path) {
|
||||
if (path) paths = paths.concat(path.getCompletionRecords());
|
||||
};
|
||||
|
||||
if (this.isIfStatement()) {
|
||||
add(this.get("consequent"));
|
||||
add(this.get("alternate"));
|
||||
} else if (this.isDoExpression() || this.isFor() || this.isWhile()) {
|
||||
add(this.get("body"));
|
||||
} else if (this.isProgram() || this.isBlockStatement()) {
|
||||
add(this.get("body").pop());
|
||||
} else if (this.isFunction()) {
|
||||
return this.get("body").getCompletionRecords();
|
||||
} else {
|
||||
paths.push(this);
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
call(key) {
|
||||
var node = this.node;
|
||||
if (!node) return;
|
||||
|
||||
var opts = this.opts;
|
||||
if (!opts[key] && !opts[node.type]) return;
|
||||
|
||||
var fns = [].concat(opts[key]);
|
||||
if (opts[node.type]) fns = fns.concat(opts[node.type][key]);
|
||||
|
||||
for (var fn of (fns: Array)) {
|
||||
if (!fn) continue;
|
||||
|
||||
let node = this.node;
|
||||
if (!node) return;
|
||||
|
||||
var previousType = this.type;
|
||||
|
||||
// call the function with the params (node, parent, scope, state)
|
||||
var replacement = fn.call(this, node, this.parent, this.scope, this.state);
|
||||
|
||||
if (replacement) {
|
||||
this.replaceWith(replacement, true);
|
||||
}
|
||||
|
||||
if (this.shouldStop || this.shouldSkip || this.removed) return;
|
||||
|
||||
if (previousType !== this.type) {
|
||||
this.queueNode(this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
isBlacklisted(): boolean {
|
||||
var blacklist = this.opts.blacklist;
|
||||
return blacklist && blacklist.indexOf(this.node.type) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
visit(): boolean {
|
||||
if (this.isBlacklisted()) return false;
|
||||
if (this.opts.shouldSkip && this.opts.shouldSkip(this)) return false;
|
||||
|
||||
this.call("enter");
|
||||
|
||||
if (this.shouldSkip) {
|
||||
return this.shouldStop;
|
||||
}
|
||||
|
||||
var node = this.node;
|
||||
var opts = this.opts;
|
||||
|
||||
if (node) {
|
||||
if (Array.isArray(node)) {
|
||||
// traverse over these replacement nodes we purposely don't call exitNode
|
||||
// as the original node has been destroyed
|
||||
for (var i = 0; i < node.length; i++) {
|
||||
traverse.node(node[i], opts, this.scope, this.state, this);
|
||||
}
|
||||
} else {
|
||||
traverse.node(node, opts, this.scope, this.state, this);
|
||||
this.call("exit");
|
||||
}
|
||||
}
|
||||
|
||||
return this.shouldStop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
getSibling(key) {
|
||||
return NodePath.get(this.parentPath, this.parent, this.container, key, this.file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
get(key: string): NodePath {
|
||||
var parts = key.split(".");
|
||||
if (parts.length === 1) { // "foo"
|
||||
return this._getKey(key);
|
||||
} else { // "foo.bar"
|
||||
return this._getPattern(parts);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
_getKey(key) {
|
||||
var node = this.node;
|
||||
var container = node[key];
|
||||
|
||||
if (Array.isArray(container)) {
|
||||
// requested a container so give them all the paths
|
||||
return container.map((_, i) => {
|
||||
return NodePath.get(this, node, container, i).setContext();
|
||||
});
|
||||
} else {
|
||||
return NodePath.get(this, node, node, key).setContext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
_getPattern(parts) {
|
||||
var path = this;
|
||||
for (var part of (parts: Array)) {
|
||||
if (part === ".") {
|
||||
path = path.parentPath;
|
||||
} else {
|
||||
if (Array.isArray(path)) {
|
||||
path = path[part];
|
||||
} else {
|
||||
path = path.get(part);
|
||||
}
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
has(key): boolean {
|
||||
var val = this.node[key];
|
||||
if (val && Array.isArray(val)) {
|
||||
return !!val.length;
|
||||
} else {
|
||||
return !!val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
is(key): boolean {
|
||||
return this.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
isnt(key): boolean {
|
||||
return !this.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
equals(key, value): boolean {
|
||||
return this.node[key] === value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
isPreviousType(type: string): boolean {
|
||||
return t.isType(this.type, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
getBindingIdentifiers() {
|
||||
return t.getBindingIdentifiers(this.node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
traverse(visitor, state) {
|
||||
if (!this.scope) console.log(this.contexts);
|
||||
traverse(this.node, visitor, this.scope, state, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
hoist(scope = this.scope) {
|
||||
var hoister = new PathHoister(this, scope);
|
||||
return hoister.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Match the current node if it matches the provided `pattern`.
|
||||
*
|
||||
* For example, given the match `React.createClass` it would match the
|
||||
* parsed nodes of `React.createClass` and `React["createClass"]`.
|
||||
*/
|
||||
|
||||
matchesPattern(pattern: string, allowPartial?: boolean): boolean {
|
||||
var parts = pattern.split(".");
|
||||
|
||||
// not a member expression
|
||||
if (!this.isMemberExpression()) return false;
|
||||
|
||||
var search = [this.node];
|
||||
var i = 0;
|
||||
|
||||
function matches(name) {
|
||||
var part = parts[i];
|
||||
return part === "*" || name === part;
|
||||
}
|
||||
|
||||
while (search.length) {
|
||||
var node = search.shift();
|
||||
|
||||
if (allowPartial && i === parts.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (t.isIdentifier(node)) {
|
||||
// this part doesn't match
|
||||
if (!matches(node.name)) return false;
|
||||
} else if (t.isLiteral(node)) {
|
||||
// this part doesn't match
|
||||
if (!matches(node.value)) return false;
|
||||
} else if (t.isMemberExpression(node)) {
|
||||
if (node.computed && !t.isLiteral(node.property)) {
|
||||
// we can't deal with this
|
||||
return false;
|
||||
} else {
|
||||
search.push(node.object);
|
||||
search.push(node.property);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// we can't deal with this
|
||||
return false;
|
||||
}
|
||||
|
||||
// too many parts
|
||||
if (++i > parts.length) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
assign(NodePath.prototype, require("./ancestry"));
|
||||
@ -845,6 +108,11 @@ assign(NodePath.prototype, require("./resolution"));
|
||||
assign(NodePath.prototype, require("./replacement"));
|
||||
assign(NodePath.prototype, require("./evaluation"));
|
||||
assign(NodePath.prototype, require("./conversion"));
|
||||
assign(NodePath.prototype, require("./verification"));
|
||||
assign(NodePath.prototype, require("./context"));
|
||||
assign(NodePath.prototype, require("./removal"));
|
||||
assign(NodePath.prototype, require("./modification"));
|
||||
assign(NodePath.prototype, require("./family"));
|
||||
|
||||
for (let type in virtualTypes) {
|
||||
if (type[0] === "_") continue;
|
||||
|
||||
177
src/babel/traversal/path/modification.js
Normal file
177
src/babel/traversal/path/modification.js
Normal file
@ -0,0 +1,177 @@
|
||||
import PathHoister from "./lib/hoister";
|
||||
import NodePath from "./index";
|
||||
import * as t from "../../types";
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function insertBefore(nodes) {
|
||||
nodes = this._verifyNodeList(nodes);
|
||||
|
||||
if (this.parentPath.isExpressionStatement() || this.parentPath.isLabeledStatement()) {
|
||||
return this.parentPath.insertBefore(nodes);
|
||||
} else if (this.isPreviousType("Expression") || (this.parentPath.isForStatement() && this.key === "init")) {
|
||||
if (this.node) nodes.push(this.node);
|
||||
this.replaceExpressionWithStatements(nodes);
|
||||
} else if (this.isPreviousType("Statement") || !this.type) {
|
||||
this._maybePopFromStatements(nodes);
|
||||
if (Array.isArray(this.container)) {
|
||||
this._containerInsertBefore(nodes);
|
||||
} else if (this.isStatementOrBlock()) {
|
||||
if (this.node) nodes.push(this.node);
|
||||
this.node = this.container[this.key] = t.blockStatement(nodes);
|
||||
} else {
|
||||
throw new Error("We don't know what to do with this node type. We were previously a Statement but we can't fit in here?");
|
||||
}
|
||||
} else {
|
||||
throw new Error("No clue what to do with this node type.");
|
||||
}
|
||||
}
|
||||
|
||||
export function _containerInsert(from, nodes) {
|
||||
this.updateSiblingKeys(from, nodes.length);
|
||||
|
||||
var paths = [];
|
||||
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var to = from + i;
|
||||
var node = nodes[i];
|
||||
this.container.splice(to, 0, node);
|
||||
|
||||
if (this.context) {
|
||||
var path = this.context.create(this.parent, this.container, to);
|
||||
paths.push(path);
|
||||
this.queueNode(path);
|
||||
} else {
|
||||
paths.push(NodePath.get(this, node, this.container, to));
|
||||
}
|
||||
}
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
export function _containerInsertBefore(nodes) {
|
||||
return this._containerInsert(this.key, nodes);
|
||||
}
|
||||
|
||||
export function _containerInsertAfter(nodes) {
|
||||
return this._containerInsert(this.key + 1, nodes);
|
||||
}
|
||||
|
||||
export function _maybePopFromStatements(nodes) {
|
||||
var last = nodes[nodes.length - 1];
|
||||
if (t.isExpressionStatement(last) && t.isIdentifier(last.expression) && !this.isCompletionRecord()) {
|
||||
nodes.pop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function insertAfter(nodes) {
|
||||
nodes = this._verifyNodeList(nodes);
|
||||
|
||||
if (this.parentPath.isExpressionStatement() || this.parentPath.isLabeledStatement()) {
|
||||
return this.parentPath.insertAfter(nodes);
|
||||
} else if (this.isPreviousType("Expression") || (this.parentPath.isForStatement() && this.key === "init")) {
|
||||
if (this.node) {
|
||||
var temp = this.scope.generateDeclaredUidIdentifier();
|
||||
nodes.unshift(t.expressionStatement(t.assignmentExpression("=", temp, this.node)));
|
||||
nodes.push(t.expressionStatement(temp));
|
||||
}
|
||||
this.replaceExpressionWithStatements(nodes);
|
||||
} else if (this.isPreviousType("Statement") || !this.type) {
|
||||
this._maybePopFromStatements(nodes);
|
||||
if (Array.isArray(this.container)) {
|
||||
this._containerInsertAfter(nodes);
|
||||
} else if (this.isStatementOrBlock()) {
|
||||
if (this.node) nodes.unshift(this.node);
|
||||
this.node = this.container[this.key] = t.blockStatement(nodes);
|
||||
} else {
|
||||
throw new Error("We don't know what to do with this node type. We were previously a Statement but we can't fit in here?");
|
||||
}
|
||||
} else {
|
||||
throw new Error("No clue what to do with this node type.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function updateSiblingKeys(fromIndex, incrementBy) {
|
||||
var paths = this.container._paths;
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
let path = paths[i];
|
||||
if (path.key >= fromIndex) {
|
||||
path.key += incrementBy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function _verifyNodeList(nodes) {
|
||||
if (nodes.constructor !== Array) {
|
||||
nodes = [nodes];
|
||||
}
|
||||
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = nodes[i];
|
||||
if (!node) {
|
||||
throw new Error(`Node list has falsy node with the index of ${i}`);
|
||||
} else if (typeof node !== "object") {
|
||||
throw new Error(`Node list contains a non-object node with the index of ${i}`);
|
||||
} else if (!node.type) {
|
||||
throw new Error(`Node list contains a node without a type with the index of ${i}`);
|
||||
}
|
||||
}
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function unshiftContainer(containerKey, nodes) {
|
||||
nodes = this._verifyNodeList(nodes);
|
||||
|
||||
// get the first path and insert our nodes before it, if it doesn't exist then it
|
||||
// doesn't matter, our nodes will be inserted anyway
|
||||
|
||||
var container = this.node[containerKey];
|
||||
var path = NodePath.get(this, this.node, container, 0);
|
||||
|
||||
return path.insertBefore(nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function pushContainer(containerKey, nodes) {
|
||||
nodes = this._verifyNodeList(nodes);
|
||||
|
||||
// get an invisible path that represents the last node + 1 and replace it with our
|
||||
// nodes, effectively inlining it
|
||||
|
||||
var container = this.node[containerKey];
|
||||
var i = container.length;
|
||||
var path = NodePath.get(this, this.node, container, i);
|
||||
|
||||
return path.replaceWith(nodes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function hoist(scope = this.scope) {
|
||||
var hoister = new PathHoister(this, scope);
|
||||
return hoister.run();
|
||||
}
|
||||
73
src/babel/traversal/path/removal.js
Normal file
73
src/babel/traversal/path/removal.js
Normal file
@ -0,0 +1,73 @@
|
||||
import * as removalHooks from "./lib/removal-hooks";
|
||||
import * as t from "../../types";
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function remove() {
|
||||
if (this._callRemovalHooks("pre")) return;
|
||||
|
||||
this.shareCommentsWithSiblings();
|
||||
this._remove();
|
||||
this.removed = true;
|
||||
|
||||
this._callRemovalHooks("post");
|
||||
}
|
||||
|
||||
export function _callRemovalHooks(position) {
|
||||
for (var fn of (removalHooks[position]: Array)) {
|
||||
if (fn(this, this.parentPath)) return;
|
||||
}
|
||||
}
|
||||
|
||||
export function _remove() {
|
||||
if (Array.isArray(this.container)) {
|
||||
this.container.splice(this.key, 1);
|
||||
this.updateSiblingKeys(this.key, -1);
|
||||
} else {
|
||||
this.container[this.key] = null;
|
||||
}
|
||||
this.node = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Share comments amongst siblings.
|
||||
*/
|
||||
|
||||
export function shareCommentsWithSiblings() {
|
||||
var node = this.node;
|
||||
if (!node) return;
|
||||
|
||||
var trailing = node.trailingComments;
|
||||
var leading = node.leadingComments;
|
||||
if (!trailing && !leading) return;
|
||||
|
||||
var prev = this.getSibling(this.key - 1);
|
||||
var next = this.getSibling(this.key + 1);
|
||||
|
||||
if (!prev.node) prev = next;
|
||||
if (!next.node) next = prev;
|
||||
|
||||
prev.giveComments("trailing", leading);
|
||||
next.giveComments("leading", trailing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Give node `comments` of the specified `type`.
|
||||
*/
|
||||
|
||||
export function giveComments(type: string, comments: Array) {
|
||||
if (!comments) return;
|
||||
|
||||
var node = this.node;
|
||||
if (!node) return;
|
||||
|
||||
var key = `${type}Comments`;
|
||||
|
||||
if (node[key]) {
|
||||
node[key] = node[key].concat(comments);
|
||||
} else {
|
||||
node[key] = comments;
|
||||
}
|
||||
}
|
||||
168
src/babel/traversal/path/verification.js
Normal file
168
src/babel/traversal/path/verification.js
Normal file
@ -0,0 +1,168 @@
|
||||
import includes from "lodash/collection/includes";
|
||||
import * as t from "../../types";
|
||||
|
||||
/**
|
||||
* Match the current node if it matches the provided `pattern`.
|
||||
*
|
||||
* For example, given the match `React.createClass` it would match the
|
||||
* parsed nodes of `React.createClass` and `React["createClass"]`.
|
||||
*/
|
||||
|
||||
export function matchesPattern(pattern: string, allowPartial?: boolean): boolean {
|
||||
var parts = pattern.split(".");
|
||||
|
||||
// not a member expression
|
||||
if (!this.isMemberExpression()) return false;
|
||||
|
||||
var search = [this.node];
|
||||
var i = 0;
|
||||
|
||||
function matches(name) {
|
||||
var part = parts[i];
|
||||
return part === "*" || name === part;
|
||||
}
|
||||
|
||||
while (search.length) {
|
||||
var node = search.shift();
|
||||
|
||||
if (allowPartial && i === parts.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (t.isIdentifier(node)) {
|
||||
// this part doesn't match
|
||||
if (!matches(node.name)) return false;
|
||||
} else if (t.isLiteral(node)) {
|
||||
// this part doesn't match
|
||||
if (!matches(node.value)) return false;
|
||||
} else if (t.isMemberExpression(node)) {
|
||||
if (node.computed && !t.isLiteral(node.property)) {
|
||||
// we can't deal with this
|
||||
return false;
|
||||
} else {
|
||||
search.push(node.object);
|
||||
search.push(node.property);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// we can't deal with this
|
||||
return false;
|
||||
}
|
||||
|
||||
// too many parts
|
||||
if (++i > parts.length) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function has(key): boolean {
|
||||
var val = this.node[key];
|
||||
if (val && Array.isArray(val)) {
|
||||
return !!val.length;
|
||||
} else {
|
||||
return !!val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function is(key): boolean {
|
||||
return this.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function isnt(key): boolean {
|
||||
return !this.has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function equals(key, value): boolean {
|
||||
return this.node[key] === value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function isPreviousType(type: string): boolean {
|
||||
return t.isType(this.type, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* This checks whether or now we're in one of the following positions:
|
||||
*
|
||||
* for (KEY in right);
|
||||
* for (KEY;;);
|
||||
*
|
||||
* This is because these spots allow VariableDeclarations AND normal expressions so we need to tell the
|
||||
* path replacement that it's ok to replace this with an expression.
|
||||
*/
|
||||
|
||||
export function canHaveVariableDeclarationOrExpression() {
|
||||
return (this.key === "init" || this.key === "left") && this.parentPath.isFor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function isCompletionRecord() {
|
||||
var path = this;
|
||||
|
||||
do {
|
||||
var container = path.container;
|
||||
|
||||
if (path.isFunction()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(container) && path.key !== container.length - 1) {
|
||||
return false;
|
||||
}
|
||||
} while ((path = path.parentPath) && !path.isProgram());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Description
|
||||
*/
|
||||
|
||||
export function isStatementOrBlock() {
|
||||
if (t.isLabeledStatement(this.parent) || t.isBlockStatement(this.container)) {
|
||||
return false;
|
||||
} else {
|
||||
return includes(t.STATEMENT_OR_BLOCK_KEYS, this.key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this node was a part of the original AST.
|
||||
*/
|
||||
|
||||
export function isUser() {
|
||||
return this.node && !!this.node.loc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this node was generated by us and not a part of the original AST.
|
||||
*/
|
||||
|
||||
export function isGenerated() {
|
||||
return !this.isUser();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user