index.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. // src/index.ts
  2. var to_string = (obj) => Object.prototype.toString.call(obj);
  3. var is_typed_array = (value) => ArrayBuffer.isView(value) && !(value instanceof DataView);
  4. var is_date = (obj) => to_string(obj) === "[object Date]";
  5. var is_regexp = (obj) => to_string(obj) === "[object RegExp]";
  6. var is_error = (obj) => to_string(obj) === "[object Error]";
  7. var is_boolean = (obj) => to_string(obj) === "[object Boolean]";
  8. var is_number = (obj) => to_string(obj) === "[object Number]";
  9. var is_string = (obj) => to_string(obj) === "[object String]";
  10. var is_array = Array.isArray;
  11. var gopd = Object.getOwnPropertyDescriptor;
  12. var is_property_enumerable = Object.prototype.propertyIsEnumerable;
  13. var get_own_property_symbols = Object.getOwnPropertySymbols;
  14. var has_own_property = Object.prototype.hasOwnProperty;
  15. function own_enumerable_keys(obj) {
  16. const res = Object.keys(obj);
  17. const symbols = get_own_property_symbols(obj);
  18. for (let i = 0; i < symbols.length; i++) {
  19. if (is_property_enumerable.call(obj, symbols[i])) {
  20. res.push(symbols[i]);
  21. }
  22. }
  23. return res;
  24. }
  25. function is_writable(object, key) {
  26. return !gopd(object, key)?.writable;
  27. }
  28. function copy(src, options) {
  29. if (typeof src === "object" && src !== null) {
  30. let dst;
  31. if (is_array(src)) {
  32. dst = [];
  33. } else if (is_date(src)) {
  34. dst = new Date(src.getTime ? src.getTime() : src);
  35. } else if (is_regexp(src)) {
  36. dst = new RegExp(src);
  37. } else if (is_error(src)) {
  38. dst = { message: src.message };
  39. } else if (is_boolean(src) || is_number(src) || is_string(src)) {
  40. dst = Object(src);
  41. } else if (is_typed_array(src)) {
  42. return src.slice();
  43. } else {
  44. dst = Object.create(Object.getPrototypeOf(src));
  45. }
  46. const iterator_function = options.includeSymbols ? own_enumerable_keys : Object.keys;
  47. for (const key of iterator_function(src)) {
  48. dst[key] = src[key];
  49. }
  50. return dst;
  51. }
  52. return src;
  53. }
  54. var empty_null = {
  55. includeSymbols: false,
  56. immutable: false
  57. };
  58. function walk(root, cb, options = empty_null) {
  59. const path = [];
  60. const parents = [];
  61. let alive = true;
  62. const iterator_function = options.includeSymbols ? own_enumerable_keys : Object.keys;
  63. const immutable = !!options.immutable;
  64. return function walker(node_) {
  65. const node = immutable ? copy(node_, options) : node_;
  66. const modifiers = {};
  67. let keep_going = true;
  68. const state = {
  69. node,
  70. node_,
  71. path: [].concat(path),
  72. parent: parents[parents.length - 1],
  73. parents,
  74. key: path[path.length - 1],
  75. isRoot: path.length === 0,
  76. level: path.length,
  77. circular: void 0,
  78. isLeaf: false,
  79. notLeaf: true,
  80. notRoot: true,
  81. isFirst: false,
  82. isLast: false,
  83. update: function(x, stopHere = false) {
  84. if (!state.isRoot) {
  85. state.parent.node[state.key] = x;
  86. }
  87. state.node = x;
  88. if (stopHere) {
  89. keep_going = false;
  90. }
  91. },
  92. delete: function(stopHere) {
  93. delete state.parent.node[state.key];
  94. if (stopHere) {
  95. keep_going = false;
  96. }
  97. },
  98. remove: function(stopHere) {
  99. if (is_array(state.parent.node)) {
  100. state.parent.node.splice(state.key, 1);
  101. } else {
  102. delete state.parent.node[state.key];
  103. }
  104. if (stopHere) {
  105. keep_going = false;
  106. }
  107. },
  108. keys: null,
  109. before: function(f) {
  110. modifiers.before = f;
  111. },
  112. after: function(f) {
  113. modifiers.after = f;
  114. },
  115. pre: function(f) {
  116. modifiers.pre = f;
  117. },
  118. post: function(f) {
  119. modifiers.post = f;
  120. },
  121. stop: function() {
  122. alive = false;
  123. },
  124. block: function() {
  125. keep_going = false;
  126. }
  127. };
  128. if (!alive) {
  129. return state;
  130. }
  131. function update_state() {
  132. if (typeof state.node === "object" && state.node !== null) {
  133. if (!state.keys || state.node_ !== state.node) {
  134. state.keys = iterator_function(state.node);
  135. }
  136. state.isLeaf = state.keys.length === 0;
  137. for (let i = 0; i < parents.length; i++) {
  138. if (parents[i].node_ === node_) {
  139. state.circular = parents[i];
  140. break;
  141. }
  142. }
  143. } else {
  144. state.isLeaf = true;
  145. state.keys = null;
  146. }
  147. state.notLeaf = !state.isLeaf;
  148. state.notRoot = !state.isRoot;
  149. }
  150. update_state();
  151. const ret = cb.call(state, state.node);
  152. if (ret !== void 0 && state.update) {
  153. state.update(ret);
  154. }
  155. if (modifiers.before) {
  156. modifiers.before.call(state, state.node);
  157. }
  158. if (!keep_going) {
  159. return state;
  160. }
  161. if (typeof state.node === "object" && state.node !== null && !state.circular) {
  162. parents.push(state);
  163. update_state();
  164. for (const [index, key] of Object.entries(state.keys ?? [])) {
  165. path.push(key);
  166. if (modifiers.pre) {
  167. modifiers.pre.call(state, state.node[key], key);
  168. }
  169. const child = walker(state.node[key]);
  170. if (immutable && has_own_property.call(state.node, key) && !is_writable(state.node, key)) {
  171. state.node[key] = child.node;
  172. }
  173. child.isLast = state.keys?.length ? +index === state.keys.length - 1 : false;
  174. child.isFirst = +index === 0;
  175. if (modifiers.post) {
  176. modifiers.post.call(state, child);
  177. }
  178. path.pop();
  179. }
  180. parents.pop();
  181. }
  182. if (modifiers.after) {
  183. modifiers.after.call(state, state.node);
  184. }
  185. return state;
  186. }(root).node;
  187. }
  188. var Traverse = class {
  189. // ! Have to keep these public as legacy mode requires them
  190. #value;
  191. #options;
  192. constructor(obj, options = empty_null) {
  193. this.#value = obj;
  194. this.#options = options;
  195. }
  196. /**
  197. * Get the element at the array `path`.
  198. */
  199. get(paths) {
  200. let node = this.#value;
  201. for (let i = 0; node && i < paths.length; i++) {
  202. const key = paths[i];
  203. if (!has_own_property.call(node, key) || !this.#options.includeSymbols && typeof key === "symbol") {
  204. return void 0;
  205. }
  206. node = node[key];
  207. }
  208. return node;
  209. }
  210. /**
  211. * Return whether the element at the array `path` exists.
  212. */
  213. has(paths) {
  214. let node = this.#value;
  215. for (let i = 0; node && i < paths.length; i++) {
  216. const key = paths[i];
  217. if (!has_own_property.call(node, key) || !this.#options.includeSymbols && typeof key === "symbol") {
  218. return false;
  219. }
  220. node = node[key];
  221. }
  222. return true;
  223. }
  224. /**
  225. * Set the element at the array `path` to `value`.
  226. */
  227. set(path, value) {
  228. let node = this.#value;
  229. let i = 0;
  230. for (i = 0; i < path.length - 1; i++) {
  231. const key = path[i];
  232. if (!has_own_property.call(node, key)) {
  233. node[key] = {};
  234. }
  235. node = node[key];
  236. }
  237. node[path[i]] = value;
  238. return value;
  239. }
  240. /**
  241. * Execute `fn` for each node in the object and return a new object with the results of the walk. To update nodes in the result use `this.update(value)`.
  242. */
  243. map(cb) {
  244. return walk(this.#value, cb, {
  245. immutable: true,
  246. includeSymbols: !!this.#options.includeSymbols
  247. });
  248. }
  249. /**
  250. * Execute `fn` for each node in the object but unlike `.map()`, when `this.update()` is called it updates the object in-place.
  251. */
  252. forEach(cb) {
  253. this.#value = walk(this.#value, cb, this.#options);
  254. return this.#value;
  255. }
  256. /**
  257. * For each node in the object, perform a [left-fold](http://en.wikipedia.org/wiki/Fold_(higher-order_function)) with the return value of `fn(acc, node)`.
  258. *
  259. * If `init` isn't specified, `init` is set to the root object for the first step and the root element is skipped.
  260. */
  261. reduce(cb, init) {
  262. const skip = arguments.length === 1;
  263. let acc = skip ? this.#value : init;
  264. this.forEach(function(x) {
  265. if (!this.isRoot || !skip) {
  266. acc = cb.call(this, acc, x);
  267. }
  268. });
  269. return acc;
  270. }
  271. /**
  272. * Return an `Array` of every possible non-cyclic path in the object.
  273. * Paths are `Array`s of string keys.
  274. */
  275. paths() {
  276. const acc = [];
  277. this.forEach(function() {
  278. acc.push(this.path);
  279. });
  280. return acc;
  281. }
  282. /**
  283. * Return an `Array` of every node in the object.
  284. */
  285. nodes() {
  286. const acc = [];
  287. this.forEach(function() {
  288. acc.push(this.node);
  289. });
  290. return acc;
  291. }
  292. /**
  293. * Create a deep clone of the object.
  294. */
  295. clone() {
  296. const parents = [];
  297. const nodes = [];
  298. const options = this.#options;
  299. if (is_typed_array(this.#value)) {
  300. return this.#value.slice();
  301. }
  302. return function clone(src) {
  303. for (let i = 0; i < parents.length; i++) {
  304. if (parents[i] === src) {
  305. return nodes[i];
  306. }
  307. }
  308. if (typeof src === "object" && src !== null) {
  309. const dst = copy(src, options);
  310. parents.push(src);
  311. nodes.push(dst);
  312. const iteratorFunction = options.includeSymbols ? own_enumerable_keys : Object.keys;
  313. for (const key of iteratorFunction(src)) {
  314. dst[key] = clone(src[key]);
  315. }
  316. parents.pop();
  317. nodes.pop();
  318. return dst;
  319. }
  320. return src;
  321. }(this.#value);
  322. }
  323. };
  324. var traverse = (obj, options) => {
  325. return new Traverse(obj, options);
  326. };
  327. traverse.get = (obj, paths, options) => {
  328. return new Traverse(obj, options).get(paths);
  329. };
  330. traverse.set = (obj, path, value, options) => {
  331. return new Traverse(obj, options).set(path, value);
  332. };
  333. traverse.has = (obj, paths, options) => {
  334. return new Traverse(obj, options).has(paths);
  335. };
  336. traverse.map = (obj, cb, options) => {
  337. return new Traverse(obj, options).map(cb);
  338. };
  339. traverse.forEach = (obj, cb, options) => {
  340. return new Traverse(obj, options).forEach(cb);
  341. };
  342. traverse.reduce = (obj, cb, init, options) => {
  343. return new Traverse(obj, options).reduce(cb, init);
  344. };
  345. traverse.paths = (obj, options) => {
  346. return new Traverse(obj, options).paths();
  347. };
  348. traverse.nodes = (obj, options) => {
  349. return new Traverse(obj, options).nodes();
  350. };
  351. traverse.clone = (obj, options) => {
  352. return new Traverse(obj, options).clone();
  353. };
  354. var src_default = traverse;
  355. export {
  356. Traverse,
  357. src_default as default
  358. };