1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125 | 1
1
1
102581
102580
65
111
65
102515
102515
102512
102512
102512
102933
102933
95667
99123
99123
99100
99097
99097
99097
99083
98886
98886
98875
89337
89337
95667
29588
33044
29577
33072
66079
66065
1
1
1
1
5239
1
472
1
1
7
7
7
7
6
5
42
2
2
5
| var VISITOR_KEYS = require("./visitor-keys");
var _ = require("lodash");
var traverse = module.exports = function (parent, callbacks, blacklistTypes) {
// falsy node
if (!parent) return;
// array of nodes
if (_.isArray(parent)) {
_.each(parent, function (node) {
traverse(node, callbacks, blacklistTypes);
});
return;
}
// unknown node type to traverse
var keys = VISITOR_KEYS[parent.type];
if (!keys) return;
// blacklist these node types from being traversed
blacklistTypes = blacklistTypes || [];
// normalise callbacks
if (_.isFunction(callbacks)) callbacks = { enter: callbacks };
_.each(keys, function (key) {
var nodes = parent[key];
if (!nodes) return;
var handle = function (obj, key) {
var node = obj[key];
if (!node) return;
// type is blacklisted
if (_.contains(blacklistTypes, node.type)) return;
var result;
// enter
Eif (callbacks.enter) {
result = callbacks.enter(node, parent, obj, key);
// stop iteration
if (result === false) return;
// replace node
if (result != null) node = obj[key] = result;
}
// traverse node
traverse(node, callbacks, blacklistTypes);
// exit
if (callbacks.exit) {
result = callbacks.exit(node, parent, obj, key);
if (result != null) obj[key] = result;
}
};
if (_.isArray(nodes)) {
_.each(nodes, function (node, i) {
handle(nodes, i);
});
// remove deleted nodes
parent[key] = _.flatten(parent[key]).filter(function (node) {
return node !== traverse.Delete;
});
} else {
handle(parent, key);
if (parent[key] === traverse.Delete) {
throw new Error("trying to delete property " + key + " from " +
parent.type + " but can't because it's required");
}
}
});
};
traverse.FUNCTION_TYPES = ["ArrowFunctionExpression", "FunctionDeclaration", "FunctionExpression"];
traverse.aliases = {
ArrowFunctionExpression: ["Function"],
FunctionDeclaration: ["Function"],
FunctionExpression: ["Function"]
};
traverse.isFunction = function (node) {
return _.contains(traverse.FUNCTION_TYPES, node.type);
};
traverse.isPattern = function (node) {
return node.type === "ArrayPattern" || node.type === "ObjectPattern";
};
traverse.Delete = {};
traverse.hasType = function (tree, type, blacklistTypes) {
blacklistTypes = [].concat(blacklistTypes || []);
var has = false;
Iif (_.isArray(tree)) {
// array of nodes, find the first
return !!_.find(tree, function (node) {
return traverse.hasType(node, type, blacklistTypes);
});
} else {
// the node we're searching in is blacklisted
if (_.contains(blacklistTypes, tree.type)) return false;
// the type we're looking for is the same as the passed node
if (tree.type === type) return true;
traverse(tree, function (node) {
if (node.type === type) {
has = true;
return false;
}
}, blacklistTypes);
}
return has;
};
|