source: node_modules/traverse/index.js@ 65b6638

main
Last change on this file since 65b6638 was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 7.7 KB
Line 
1'use strict';
2
3// TODO: use call-bind, is-date, is-regex, is-string, is-boolean-object, is-number-object
4function toS(obj) { return Object.prototype.toString.call(obj); }
5function isDate(obj) { return toS(obj) === '[object Date]'; }
6function isRegExp(obj) { return toS(obj) === '[object RegExp]'; }
7function isError(obj) { return toS(obj) === '[object Error]'; }
8function isBoolean(obj) { return toS(obj) === '[object Boolean]'; }
9function isNumber(obj) { return toS(obj) === '[object Number]'; }
10function isString(obj) { return toS(obj) === '[object String]'; }
11
12// TODO: use isarray
13var isArray = Array.isArray || function isArray(xs) {
14 return Object.prototype.toString.call(xs) === '[object Array]';
15};
16
17// TODO: use for-each?
18function forEach(xs, fn) {
19 if (xs.forEach) { return xs.forEach(fn); }
20 for (var i = 0; i < xs.length; i++) {
21 fn(xs[i], i, xs);
22 }
23 return void undefined;
24}
25
26// TODO: use object-keys
27var objectKeys = Object.keys || function keys(obj) {
28 var res = [];
29 for (var key in obj) { res.push(key); } // eslint-disable-line no-restricted-syntax
30 return res;
31};
32
33var propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
34var getOwnPropertySymbols = Object.getOwnPropertySymbols; // eslint-disable-line id-length
35
36// TODO: use reflect.ownkeys and filter out non-enumerables
37function ownEnumerableKeys(obj) {
38 var res = objectKeys(obj);
39
40 // Include enumerable symbol properties.
41 if (getOwnPropertySymbols) {
42 var symbols = getOwnPropertySymbols(obj);
43 for (var i = 0; i < symbols.length; i++) {
44 if (propertyIsEnumerable.call(obj, symbols[i])) {
45 res.push(symbols[i]);
46 }
47 }
48 }
49 return res;
50}
51
52// TODO: use object.hasown
53var hasOwnProperty = Object.prototype.hasOwnProperty || function (obj, key) {
54 return key in obj;
55};
56
57function copy(src) {
58 if (typeof src === 'object' && src !== null) {
59 var dst;
60
61 if (isArray(src)) {
62 dst = [];
63 } else if (isDate(src)) {
64 dst = new Date(src.getTime ? src.getTime() : src);
65 } else if (isRegExp(src)) {
66 dst = new RegExp(src);
67 } else if (isError(src)) {
68 dst = { message: src.message };
69 } else if (isBoolean(src) || isNumber(src) || isString(src)) {
70 dst = Object(src);
71 } else if (Object.create && Object.getPrototypeOf) {
72 dst = Object.create(Object.getPrototypeOf(src));
73 } else if (src.constructor === Object) {
74 dst = {};
75 } else {
76 var proto = (src.constructor && src.constructor.prototype)
77 || src.__proto__
78 || {};
79 var T = function T() {}; // eslint-disable-line func-style, func-name-matching
80 T.prototype = proto;
81 dst = new T();
82 }
83
84 forEach(ownEnumerableKeys(src), function (key) {
85 dst[key] = src[key];
86 });
87 return dst;
88 }
89 return src;
90}
91
92function walk(root, cb, immutable) {
93 var path = [];
94 var parents = [];
95 var alive = true;
96
97 return (function walker(node_) {
98 var node = immutable ? copy(node_) : node_;
99 var modifiers = {};
100
101 var keepGoing = true;
102
103 var state = {
104 node: node,
105 node_: node_,
106 path: [].concat(path),
107 parent: parents[parents.length - 1],
108 parents: parents,
109 key: path[path.length - 1],
110 isRoot: path.length === 0,
111 level: path.length,
112 circular: null,
113 update: function (x, stopHere) {
114 if (!state.isRoot) {
115 state.parent.node[state.key] = x;
116 }
117 state.node = x;
118 if (stopHere) { keepGoing = false; }
119 },
120 delete: function (stopHere) {
121 delete state.parent.node[state.key];
122 if (stopHere) { keepGoing = false; }
123 },
124 remove: function (stopHere) {
125 if (isArray(state.parent.node)) {
126 state.parent.node.splice(state.key, 1);
127 } else {
128 delete state.parent.node[state.key];
129 }
130 if (stopHere) { keepGoing = false; }
131 },
132 keys: null,
133 before: function (f) { modifiers.before = f; },
134 after: function (f) { modifiers.after = f; },
135 pre: function (f) { modifiers.pre = f; },
136 post: function (f) { modifiers.post = f; },
137 stop: function () { alive = false; },
138 block: function () { keepGoing = false; },
139 };
140
141 if (!alive) { return state; }
142
143 function updateState() {
144 if (typeof state.node === 'object' && state.node !== null) {
145 if (!state.keys || state.node_ !== state.node) {
146 state.keys = ownEnumerableKeys(state.node);
147 }
148
149 state.isLeaf = state.keys.length === 0;
150
151 for (var i = 0; i < parents.length; i++) {
152 if (parents[i].node_ === node_) {
153 state.circular = parents[i];
154 break; // eslint-disable-line no-restricted-syntax
155 }
156 }
157 } else {
158 state.isLeaf = true;
159 state.keys = null;
160 }
161
162 state.notLeaf = !state.isLeaf;
163 state.notRoot = !state.isRoot;
164 }
165
166 updateState();
167
168 // use return values to update if defined
169 var ret = cb.call(state, state.node);
170 if (ret !== undefined && state.update) { state.update(ret); }
171
172 if (modifiers.before) { modifiers.before.call(state, state.node); }
173
174 if (!keepGoing) { return state; }
175
176 if (
177 typeof state.node === 'object'
178 && state.node !== null
179 && !state.circular
180 ) {
181 parents.push(state);
182
183 updateState();
184
185 forEach(state.keys, function (key, i) {
186 path.push(key);
187
188 if (modifiers.pre) { modifiers.pre.call(state, state.node[key], key); }
189
190 var child = walker(state.node[key]);
191 if (immutable && hasOwnProperty.call(state.node, key)) {
192 state.node[key] = child.node;
193 }
194
195 child.isLast = i === state.keys.length - 1;
196 child.isFirst = i === 0;
197
198 if (modifiers.post) { modifiers.post.call(state, child); }
199
200 path.pop();
201 });
202 parents.pop();
203 }
204
205 if (modifiers.after) { modifiers.after.call(state, state.node); }
206
207 return state;
208 }(root)).node;
209}
210
211function Traverse(obj) {
212 this.value = obj;
213}
214
215Traverse.prototype.get = function (ps) {
216 var node = this.value;
217 for (var i = 0; i < ps.length; i++) {
218 var key = ps[i];
219 if (!node || !hasOwnProperty.call(node, key)) {
220 return void undefined;
221 }
222 node = node[key];
223 }
224 return node;
225};
226
227Traverse.prototype.has = function (ps) {
228 var node = this.value;
229 for (var i = 0; i < ps.length; i++) {
230 var key = ps[i];
231 if (!node || !hasOwnProperty.call(node, key)) {
232 return false;
233 }
234 node = node[key];
235 }
236 return true;
237};
238
239Traverse.prototype.set = function (ps, value) {
240 var node = this.value;
241 for (var i = 0; i < ps.length - 1; i++) {
242 var key = ps[i];
243 if (!hasOwnProperty.call(node, key)) { node[key] = {}; }
244 node = node[key];
245 }
246 node[ps[i]] = value;
247 return value;
248};
249
250Traverse.prototype.map = function (cb) {
251 return walk(this.value, cb, true);
252};
253
254Traverse.prototype.forEach = function (cb) {
255 this.value = walk(this.value, cb, false);
256 return this.value;
257};
258
259Traverse.prototype.reduce = function (cb, init) {
260 var skip = arguments.length === 1;
261 var acc = skip ? this.value : init;
262 this.forEach(function (x) {
263 if (!this.isRoot || !skip) {
264 acc = cb.call(this, acc, x);
265 }
266 });
267 return acc;
268};
269
270Traverse.prototype.paths = function () {
271 var acc = [];
272 this.forEach(function () {
273 acc.push(this.path);
274 });
275 return acc;
276};
277
278Traverse.prototype.nodes = function () {
279 var acc = [];
280 this.forEach(function () {
281 acc.push(this.node);
282 });
283 return acc;
284};
285
286Traverse.prototype.clone = function () {
287 var parents = [];
288 var nodes = [];
289
290 return (function clone(src) {
291 for (var i = 0; i < parents.length; i++) {
292 if (parents[i] === src) {
293 return nodes[i];
294 }
295 }
296
297 if (typeof src === 'object' && src !== null) {
298 var dst = copy(src);
299
300 parents.push(src);
301 nodes.push(dst);
302
303 forEach(ownEnumerableKeys(src), function (key) {
304 dst[key] = clone(src[key]);
305 });
306
307 parents.pop();
308 nodes.pop();
309 return dst;
310 }
311
312 return src;
313
314 }(this.value));
315};
316
317function traverse(obj) {
318 return new Traverse(obj);
319}
320
321// TODO: replace with object.assign?
322forEach(ownEnumerableKeys(Traverse.prototype), function (key) {
323 traverse[key] = function (obj) {
324 var args = [].slice.call(arguments, 1);
325 var t = new Traverse(obj);
326 return t[key].apply(t, args);
327 };
328});
329
330module.exports = traverse;
Note: See TracBrowser for help on using the repository browser.