UNPKG

5.86 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.explode = explode;
7exports.merge = merge;
8exports.verify = verify;
9
10var virtualTypes = require("./path/lib/virtual-types");
11
12var _t = require("@babel/types");
13
14const {
15 DEPRECATED_KEYS,
16 FLIPPED_ALIAS_KEYS,
17 TYPES
18} = _t;
19
20function explode(visitor) {
21 if (visitor._exploded) return visitor;
22 visitor._exploded = true;
23
24 for (const nodeType of Object.keys(visitor)) {
25 if (shouldIgnoreKey(nodeType)) continue;
26 const parts = nodeType.split("|");
27 if (parts.length === 1) continue;
28 const fns = visitor[nodeType];
29 delete visitor[nodeType];
30
31 for (const part of parts) {
32 visitor[part] = fns;
33 }
34 }
35
36 verify(visitor);
37 delete visitor.__esModule;
38 ensureEntranceObjects(visitor);
39 ensureCallbackArrays(visitor);
40
41 for (const nodeType of Object.keys(visitor)) {
42 if (shouldIgnoreKey(nodeType)) continue;
43 const wrapper = virtualTypes[nodeType];
44 if (!wrapper) continue;
45 const fns = visitor[nodeType];
46
47 for (const type of Object.keys(fns)) {
48 fns[type] = wrapCheck(wrapper, fns[type]);
49 }
50
51 delete visitor[nodeType];
52
53 if (wrapper.types) {
54 for (const type of wrapper.types) {
55 if (visitor[type]) {
56 mergePair(visitor[type], fns);
57 } else {
58 visitor[type] = fns;
59 }
60 }
61 } else {
62 mergePair(visitor, fns);
63 }
64 }
65
66 for (const nodeType of Object.keys(visitor)) {
67 if (shouldIgnoreKey(nodeType)) continue;
68 const fns = visitor[nodeType];
69 let aliases = FLIPPED_ALIAS_KEYS[nodeType];
70 const deprecatedKey = DEPRECATED_KEYS[nodeType];
71
72 if (deprecatedKey) {
73 console.trace(`Visitor defined for ${nodeType} but it has been renamed to ${deprecatedKey}`);
74 aliases = [deprecatedKey];
75 }
76
77 if (!aliases) continue;
78 delete visitor[nodeType];
79
80 for (const alias of aliases) {
81 const existing = visitor[alias];
82
83 if (existing) {
84 mergePair(existing, fns);
85 } else {
86 visitor[alias] = Object.assign({}, fns);
87 }
88 }
89 }
90
91 for (const nodeType of Object.keys(visitor)) {
92 if (shouldIgnoreKey(nodeType)) continue;
93 ensureCallbackArrays(visitor[nodeType]);
94 }
95
96 return visitor;
97}
98
99function verify(visitor) {
100 if (visitor._verified) return;
101
102 if (typeof visitor === "function") {
103 throw new Error("You passed `traverse()` a function when it expected a visitor object, " + "are you sure you didn't mean `{ enter: Function }`?");
104 }
105
106 for (const nodeType of Object.keys(visitor)) {
107 if (nodeType === "enter" || nodeType === "exit") {
108 validateVisitorMethods(nodeType, visitor[nodeType]);
109 }
110
111 if (shouldIgnoreKey(nodeType)) continue;
112
113 if (TYPES.indexOf(nodeType) < 0) {
114 throw new Error(`You gave us a visitor for the node type ${nodeType} but it's not a valid type`);
115 }
116
117 const visitors = visitor[nodeType];
118
119 if (typeof visitors === "object") {
120 for (const visitorKey of Object.keys(visitors)) {
121 if (visitorKey === "enter" || visitorKey === "exit") {
122 validateVisitorMethods(`${nodeType}.${visitorKey}`, visitors[visitorKey]);
123 } else {
124 throw new Error("You passed `traverse()` a visitor object with the property " + `${nodeType} that has the invalid property ${visitorKey}`);
125 }
126 }
127 }
128 }
129
130 visitor._verified = true;
131}
132
133function validateVisitorMethods(path, val) {
134 const fns = [].concat(val);
135
136 for (const fn of fns) {
137 if (typeof fn !== "function") {
138 throw new TypeError(`Non-function found defined in ${path} with type ${typeof fn}`);
139 }
140 }
141}
142
143function merge(visitors, states = [], wrapper) {
144 const rootVisitor = {};
145
146 for (let i = 0; i < visitors.length; i++) {
147 const visitor = visitors[i];
148 const state = states[i];
149 explode(visitor);
150
151 for (const type of Object.keys(visitor)) {
152 let visitorType = visitor[type];
153
154 if (state || wrapper) {
155 visitorType = wrapWithStateOrWrapper(visitorType, state, wrapper);
156 }
157
158 const nodeVisitor = rootVisitor[type] = rootVisitor[type] || {};
159 mergePair(nodeVisitor, visitorType);
160 }
161 }
162
163 return rootVisitor;
164}
165
166function wrapWithStateOrWrapper(oldVisitor, state, wrapper) {
167 const newVisitor = {};
168
169 for (const key of Object.keys(oldVisitor)) {
170 let fns = oldVisitor[key];
171 if (!Array.isArray(fns)) continue;
172 fns = fns.map(function (fn) {
173 let newFn = fn;
174
175 if (state) {
176 newFn = function (path) {
177 return fn.call(state, path, state);
178 };
179 }
180
181 if (wrapper) {
182 newFn = wrapper(state.key, key, newFn);
183 }
184
185 if (newFn !== fn) {
186 newFn.toString = () => fn.toString();
187 }
188
189 return newFn;
190 });
191 newVisitor[key] = fns;
192 }
193
194 return newVisitor;
195}
196
197function ensureEntranceObjects(obj) {
198 for (const key of Object.keys(obj)) {
199 if (shouldIgnoreKey(key)) continue;
200 const fns = obj[key];
201
202 if (typeof fns === "function") {
203 obj[key] = {
204 enter: fns
205 };
206 }
207 }
208}
209
210function ensureCallbackArrays(obj) {
211 if (obj.enter && !Array.isArray(obj.enter)) obj.enter = [obj.enter];
212 if (obj.exit && !Array.isArray(obj.exit)) obj.exit = [obj.exit];
213}
214
215function wrapCheck(wrapper, fn) {
216 const newFn = function (path) {
217 if (wrapper.checkPath(path)) {
218 return fn.apply(this, arguments);
219 }
220 };
221
222 newFn.toString = () => fn.toString();
223
224 return newFn;
225}
226
227function shouldIgnoreKey(key) {
228 if (key[0] === "_") return true;
229 if (key === "enter" || key === "exit" || key === "shouldSkip") return true;
230
231 if (key === "denylist" || key === "noScope" || key === "skipKeys" || key === "blacklist") {
232 return true;
233 }
234
235 return false;
236}
237
238function mergePair(dest, src) {
239 for (const key of Object.keys(src)) {
240 dest[key] = [].concat(dest[key] || [], src[key]);
241 }
242}
\No newline at end of file