fast-tree-builder 2.0.0-beta.3 → 2.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
  ## Features
20
20
 
21
21
  - **Supports `parentId` and `childIds` Models** – Choose your relation style via options.
22
- - **Fully Typed** – TypeScript support with correct types for the built tree.
22
+ - **Fully Typed** – Carefully written TypeScript types for the built tree.
23
23
  - **Highly Customizable** – Design the node structure as you like.
24
24
  - **Any Iterable Accepted** – Works on arrays, sets, or any iterable type.
25
25
  - **Flexible ID Types** – Anything can be an identifier; relations matched with `childId === parentId`.
@@ -59,18 +59,18 @@ Builds a tree structure from an iterable list of items.
59
59
 
60
60
  - `id`: A key or function used to extract the unique identifier from each item.
61
61
 
62
- ##### One of:
62
+ ##### One of
63
63
 
64
64
  - `parentId`: A key or function that access the parent ID of the item.
65
65
  - `childIds`: A key or function that access an iterable of child IDs for the item.
66
66
 
67
67
  ##### Optional
68
68
 
69
- - `nodeValueMapper`: Function to map an item to a custom value stored in the node.
70
- - `nodeValueKey`: Key where the item is stored in the output node. Set to `false` to inline the item directly into the node. Defaults to `'value'`.
71
- - `nodeParentKey`: Key where the node's parent reference is stored. Set to `false` to omit parent links. Defaults to `'parent'`.
72
- - `nodeChildrenKey`: Key where the node's children are stored. Defaults to `'children'`.
73
- - `nodeDepthKey`: Object key used to store the node's depth in the tree (root = 0). Set to `false` to omit depth values. Turns on `validateTree` when a string value is set here. Defaults to `false`.
69
+ - `valueResolver`: Function to transform an item to a custom value stored in the node. Defaults to use the input item as is.
70
+ - `valueKey`: Key where the item is stored in the output node. Set to `false` to inline the item directly into the node. Defaults to `'value'`.
71
+ - `parentKey`: Key where the node's parent reference is stored in the output node. Set to `false` to omit parent links. Defaults to `'parent'`.
72
+ - `childrenKey`: Key where the node's children are stored in the output node. Defaults to `'children'`.
73
+ - `depthKey`: Key where the node's depth (with root = 0) is stored in the output node. Set to `false` to omit depth values. Automatically enables `validateTree` when a string value is set here. Defaults to `false`.
74
74
  - `validateReferences`: When `true`, verifies all `parentId` or `childIds` resolve to real items. Errors are thrown on invalid references. Defaults to `false`.
75
75
  - `validateTree`: When `true`, verifies that the final structure is a valid tree (no cycles or nodes reachable via multipla paths). Errors are thrown if the check fails. Defaults to `false`.
76
76
 
@@ -88,7 +88,7 @@ Builds a tree structure from an iterable list of items.
88
88
  - Missing required `id`, `parentId`/`childIds`, or `options` parameter
89
89
  - Duplicate item identifiers in input
90
90
  - Invalid reference (if `validateReferences` is enabled)
91
- - Cycle or structural error (if `validateTree` is enabled or `nodeDepthKey` is string)
91
+ - Cycle or structural error (if `validateTree` is enabled or `depthKey` is string)
92
92
 
93
93
 
94
94
  ## Usage
@@ -115,9 +115,9 @@ const { roots, nodes } = buildTree(items, {
115
115
  id: 'id',
116
116
  parentId: 'parent',
117
117
  // the built node:
118
- nodeValueKey: 'value',
119
- nodeParentKey: 'parent',
120
- nodeChildrenKey: 'children',
118
+ valueKey: 'value',
119
+ parentKey: 'parent',
120
+ childrenKey: 'children',
121
121
  });
122
122
 
123
123
  console.log(roots[0].value.name);
@@ -193,10 +193,10 @@ const items = [
193
193
  const { roots, nodes } = buildTree(items, {
194
194
  id: item => item.key?.n,
195
195
  parentId: item => item.parentKey?.n,
196
- nodeValueMapper: item => ({ title: item.name }),
197
- nodeValueKey: false, // merge item data into node
198
- nodeParentKey: 'up',
199
- nodeChildrenKey: 'down',
196
+ valueResolver: item => ({ title: item.name }),
197
+ valueKey: false, // merge item data into node
198
+ parentKey: 'up',
199
+ childrenKey: 'down',
200
200
  });
201
201
 
202
202
  console.log(roots[0].title);
@@ -251,8 +251,8 @@ const items = [
251
251
  const { roots, nodes } = buildTree(items, {
252
252
  id: item => item.substring(2, 4),
253
253
  parentKey: item => item.substring(0, 2),
254
- nodeValueMapper: item => ({ name: item.substring(4) }),
255
- nodeValueKey: false, // merge item data into node
254
+ valueResolver: item => ({ name: item.substring(4) }),
255
+ valueKey: false, // merge item data into node
256
256
  });
257
257
 
258
258
  console.log(roots[0].name);
package/index.cjs CHANGED
@@ -29,11 +29,11 @@ function buildTree(items, options) {
29
29
  id: idAccessor,
30
30
  parentId: parentIdAccessor,
31
31
  childIds: childIdsAccessor,
32
- nodeValueMapper,
33
- nodeValueKey = "value",
34
- nodeParentKey = "parent",
35
- nodeChildrenKey = "children",
36
- nodeDepthKey = false,
32
+ valueResolver,
33
+ valueKey = "value",
34
+ parentKey = "parent",
35
+ childrenKey = "children",
36
+ depthKey = false,
37
37
  validateTree = false,
38
38
  validateReferences = false
39
39
  } = options;
@@ -55,21 +55,21 @@ function buildTree(items, options) {
55
55
  if (nodes.has(id)) {
56
56
  throw new Error(`Duplicate identifier '${id}'.`);
57
57
  }
58
- const node = nodeValueKey !== false ? { [nodeValueKey]: nodeValueMapper ? nodeValueMapper(item) : item } : { ...nodeValueMapper ? nodeValueMapper(item) : item };
59
- if (nodeValueKey === false) {
60
- if (nodeParentKey !== false) {
61
- delete node[nodeParentKey];
58
+ const node = valueKey !== false ? { [valueKey]: valueResolver ? valueResolver(item) : item } : { ...valueResolver ? valueResolver(item) : item };
59
+ if (valueKey === false) {
60
+ if (parentKey !== false) {
61
+ delete node[parentKey];
62
62
  }
63
- delete node[nodeChildrenKey];
63
+ delete node[childrenKey];
64
64
  }
65
65
  nodes.set(id, node);
66
66
  const parentId = typeof parentIdAccessor === "function" ? parentIdAccessor(item) : item[parentIdAccessor];
67
67
  const parentNode = nodes.get(parentId);
68
68
  if (parentNode) {
69
- parentNode[nodeChildrenKey] ||= [];
70
- parentNode[nodeChildrenKey].push(node);
71
- if (nodeParentKey !== false) {
72
- node[nodeParentKey] = parentNode;
69
+ parentNode[childrenKey] ||= [];
70
+ parentNode[childrenKey].push(node);
71
+ if (parentKey !== false) {
72
+ node[parentKey] = parentNode;
73
73
  }
74
74
  } else {
75
75
  const siblings = waitingForParent.get(parentId);
@@ -81,10 +81,10 @@ function buildTree(items, options) {
81
81
  }
82
82
  const children = waitingForParent.get(id);
83
83
  if (children) {
84
- node[nodeChildrenKey] = children;
85
- if (nodeParentKey !== false) {
84
+ node[childrenKey] = children;
85
+ if (parentKey !== false) {
86
86
  for (const child of children) {
87
- child[nodeParentKey] = node;
87
+ child[parentKey] = node;
88
88
  }
89
89
  }
90
90
  waitingForParent.delete(id);
@@ -106,12 +106,12 @@ function buildTree(items, options) {
106
106
  if (nodes.has(id)) {
107
107
  throw new Error(`Duplicate identifier '${id}'.`);
108
108
  }
109
- const node = nodeValueKey !== false ? { [nodeValueKey]: nodeValueMapper ? nodeValueMapper(item) : item } : { ...nodeValueMapper ? nodeValueMapper(item) : item };
110
- if (nodeValueKey === false) {
111
- if (nodeParentKey !== false) {
112
- delete node[nodeParentKey];
109
+ const node = valueKey !== false ? { [valueKey]: valueResolver ? valueResolver(item) : item } : { ...valueResolver ? valueResolver(item) : item };
110
+ if (valueKey === false) {
111
+ if (parentKey !== false) {
112
+ delete node[parentKey];
113
113
  }
114
- delete node[nodeChildrenKey];
114
+ delete node[childrenKey];
115
115
  }
116
116
  nodes.set(id, node);
117
117
  const childIds = typeof childIdsAccessor === "function" ? childIdsAccessor(item) : item[childIdsAccessor];
@@ -119,16 +119,16 @@ function buildTree(items, options) {
119
119
  if (typeof childIds[Symbol.iterator] !== "function") {
120
120
  throw new Error(`Item '${id}' has invalid children: expected an iterable value.`);
121
121
  }
122
- node[nodeChildrenKey] = [];
122
+ node[childrenKey] = [];
123
123
  for (const childId of childIds) {
124
124
  const childNode = nodes.get(childId);
125
125
  if (childNode) {
126
- node[nodeChildrenKey].push(childNode);
127
- if (nodeParentKey !== false) {
128
- if (childNode[nodeParentKey] && childNode[nodeParentKey] !== node) {
126
+ node[childrenKey].push(childNode);
127
+ if (parentKey !== false) {
128
+ if (childNode[parentKey] && childNode[parentKey] !== node) {
129
129
  throw new Error(`Node '${childId}' already has a different parent, refusing to overwrite.`);
130
130
  }
131
- childNode[nodeParentKey] = node;
131
+ childNode[parentKey] = node;
132
132
  }
133
133
  rootCandidates.delete(childId);
134
134
  } else {
@@ -137,24 +137,24 @@ function buildTree(items, options) {
137
137
  }
138
138
  waitingChildren.set(childId, {
139
139
  parentNode: node,
140
- childIndex: node[nodeChildrenKey].length
140
+ childIndex: node[childrenKey].length
141
141
  });
142
- node[nodeChildrenKey].push(null);
142
+ node[childrenKey].push(null);
143
143
  }
144
144
  }
145
- if (node[nodeChildrenKey].length === 0) {
146
- delete node[nodeChildrenKey];
145
+ if (node[childrenKey].length === 0) {
146
+ delete node[childrenKey];
147
147
  }
148
148
  }
149
149
  const parentDescriptor = waitingChildren.get(id);
150
150
  if (parentDescriptor) {
151
151
  const { parentNode, childIndex } = parentDescriptor;
152
- parentNode[nodeChildrenKey][childIndex] = node;
153
- if (nodeParentKey !== false) {
154
- if (node[nodeParentKey] && node[nodeParentKey] !== parentNode) {
152
+ parentNode[childrenKey][childIndex] = node;
153
+ if (parentKey !== false) {
154
+ if (node[parentKey] && node[parentKey] !== parentNode) {
155
155
  throw new Error(`Node '${id}' already has a different parent, refusing to overwrite.`);
156
156
  }
157
- node[nodeParentKey] = parentNode;
157
+ node[parentKey] = parentNode;
158
158
  }
159
159
  waitingChildren.delete(id);
160
160
  } else {
@@ -169,9 +169,9 @@ function buildTree(items, options) {
169
169
  const pending = Array.from(waitingChildren.values());
170
170
  for (let i = pending.length - 1; i >= 0; i--) {
171
171
  const { parentNode, childIndex } = pending[i];
172
- parentNode[nodeChildrenKey].splice(childIndex, 1);
173
- if (parentNode[nodeChildrenKey].length === 0) {
174
- delete parentNode[nodeChildrenKey];
172
+ parentNode[childrenKey].splice(childIndex, 1);
173
+ if (parentNode[childrenKey].length === 0) {
174
+ delete parentNode[childrenKey];
175
175
  }
176
176
  }
177
177
  }
@@ -179,7 +179,7 @@ function buildTree(items, options) {
179
179
  roots.push(node);
180
180
  }
181
181
  }
182
- const withDepth = typeof nodeDepthKey === "string" || typeof nodeDepthKey === "symbol" || typeof nodeDepthKey === "number";
182
+ const withDepth = typeof depthKey === "string" || typeof depthKey === "symbol" || typeof depthKey === "number";
183
183
  if (validateTree || withDepth) {
184
184
  if (roots.length === 0 && nodes.size > 0) {
185
185
  throw new Error("Tree validation failed: detected a cycle.");
@@ -195,10 +195,10 @@ function buildTree(items, options) {
195
195
  }
196
196
  visited.add(node);
197
197
  if (withDepth) {
198
- node[nodeDepthKey] = depth;
198
+ node[depthKey] = depth;
199
199
  }
200
- if (node[nodeChildrenKey]) {
201
- for (const child of node[nodeChildrenKey]) {
200
+ if (node[childrenKey]) {
201
+ for (const child of node[childrenKey]) {
202
202
  stack.push({ node: child, depth: depth + 1 });
203
203
  }
204
204
  }
package/index.d.cts CHANGED
@@ -11,51 +11,51 @@ type AccessorReturnType<O, P extends (keyof O) | ((item: O) => any)> = P extends
11
11
  type ObjectKeysOfIterableProperties<T> = {
12
12
  [K in keyof T]: T[K] extends (Iterable<unknown> & object) | null | undefined ? K : never;
13
13
  }[keyof T];
14
- declare function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue>) | ((item: NoInfer<TInputValue>) => unknown), TNodeValueKey extends PropertyKey | false = 'value', TNodeParentKey extends PropertyKey | false = 'parent', TNodeChildrenKey extends PropertyKey = 'children', TNodeDepthKey extends PropertyKey | false = false, TInputValue extends (TNodeValueKey extends false ? object : TIdAccessor extends PropertyKey ? object : unknown) = any, TMappedValue extends (TNodeValueKey extends false ? object : unknown) = TInputValue>(items: Iterable<TInputValue>, options: {
14
+ declare function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue>) | ((item: NoInfer<TInputValue>) => unknown), TValueKey extends PropertyKey | false = 'value', TParentKey extends PropertyKey | false = 'parent', TChildrenKey extends PropertyKey = 'children', TDepthKey extends PropertyKey | false = false, TInputValue extends (TValueKey extends false ? object : TIdAccessor extends PropertyKey ? object : unknown) = any, TResolvedValue extends (TValueKey extends false ? object : unknown) = TInputValue>(items: Iterable<TInputValue>, options: {
15
15
  /**
16
16
  * A string key or function used to get the item's unique identifier.
17
17
  */
18
18
  id: TIdAccessor;
19
19
  /**
20
- * Maps the input item to a different value stored in the resulting tree node.
20
+ * Function to transform an item to a custom value stored in the node.
21
21
  */
22
- nodeValueMapper?: {
23
- (item: NoInfer<TInputValue>): TMappedValue;
22
+ valueResolver?: {
23
+ (item: NoInfer<TInputValue>): TResolvedValue;
24
24
  };
25
25
  /**
26
- * Object key used to store the input item in the output tree node.
26
+ * Key where the item is stored in the output node.
27
27
  *
28
- * Set to `false` to merge the item into the node itself.
28
+ * Set to `false` to inline the item directly into the node.
29
29
  *
30
30
  * Defaults to `'value'`.
31
31
  */
32
- nodeValueKey?: TNodeValueKey;
32
+ valueKey?: TValueKey;
33
33
  /**
34
- * Object key used to store the reference to the parent node in the output tree node.
34
+ * Key where the node's parent reference is stored in the output node.
35
35
  *
36
36
  * Set to `false` to omit parent links.
37
37
  *
38
38
  * Defaults to `'parent'`.
39
39
  */
40
- nodeParentKey?: TNodeParentKey;
40
+ parentKey?: TParentKey;
41
41
  /**
42
- * Object key used to store the list of child nodes in the output tree node.
42
+ * Key where the node's children are stored in the output node.
43
43
  *
44
44
  * Defaults to `'children'`.
45
45
  */
46
- nodeChildrenKey?: TNodeChildrenKey;
46
+ childrenKey?: TChildrenKey;
47
47
  /**
48
- * Object key used to store a value indicating the node depth in the tree on each output node.
48
+ * Key where the node's depth is stored in the output node.
49
49
  * Root nodes have a depth of 0.
50
50
  *
51
51
  * Set to `false` to omit depth values.
52
52
  *
53
- * Enables `validateTree`, as depth assignment requires a valid tree structure,
54
- * and both operations also share the same traversal logic.
53
+ * Automatically enables `validateTree` when a string value is set here:
54
+ * Depth assignment requires a valid tree structure, and both operations also share the same traversal logic.
55
55
  *
56
56
  * Defaults to `false`.
57
57
  */
58
- nodeDepthKey?: TNodeDepthKey;
58
+ depthKey?: TDepthKey;
59
59
  /**
60
60
  * Validates that the final structure forms a tree.
61
61
  *
@@ -107,8 +107,8 @@ declare function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue>) | ((
107
107
  */
108
108
  childIds?: never;
109
109
  })): {
110
- roots: TreeNode<TMappedValue extends undefined ? TInputValue : TMappedValue, TNodeValueKey, TNodeParentKey, TNodeChildrenKey, TNodeDepthKey>[];
111
- nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TreeNode<TMappedValue extends undefined ? TInputValue : TMappedValue, TNodeValueKey, TNodeParentKey, TNodeChildrenKey, TNodeDepthKey>>;
110
+ roots: TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>[];
111
+ nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>>;
112
112
  };
113
113
  declare const _default: { default: typeof buildTree };
114
114
  export = _default;
package/index.d.mts CHANGED
@@ -11,51 +11,51 @@ type AccessorReturnType<O, P extends (keyof O) | ((item: O) => any)> = P extends
11
11
  type ObjectKeysOfIterableProperties<T> = {
12
12
  [K in keyof T]: T[K] extends (Iterable<unknown> & object) | null | undefined ? K : never;
13
13
  }[keyof T];
14
- export default function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue>) | ((item: NoInfer<TInputValue>) => unknown), TNodeValueKey extends PropertyKey | false = 'value', TNodeParentKey extends PropertyKey | false = 'parent', TNodeChildrenKey extends PropertyKey = 'children', TNodeDepthKey extends PropertyKey | false = false, TInputValue extends (TNodeValueKey extends false ? object : TIdAccessor extends PropertyKey ? object : unknown) = any, TMappedValue extends (TNodeValueKey extends false ? object : unknown) = TInputValue>(items: Iterable<TInputValue>, options: {
14
+ export default function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue>) | ((item: NoInfer<TInputValue>) => unknown), TValueKey extends PropertyKey | false = 'value', TParentKey extends PropertyKey | false = 'parent', TChildrenKey extends PropertyKey = 'children', TDepthKey extends PropertyKey | false = false, TInputValue extends (TValueKey extends false ? object : TIdAccessor extends PropertyKey ? object : unknown) = any, TResolvedValue extends (TValueKey extends false ? object : unknown) = TInputValue>(items: Iterable<TInputValue>, options: {
15
15
  /**
16
16
  * A string key or function used to get the item's unique identifier.
17
17
  */
18
18
  id: TIdAccessor;
19
19
  /**
20
- * Maps the input item to a different value stored in the resulting tree node.
20
+ * Function to transform an item to a custom value stored in the node.
21
21
  */
22
- nodeValueMapper?: {
23
- (item: NoInfer<TInputValue>): TMappedValue;
22
+ valueResolver?: {
23
+ (item: NoInfer<TInputValue>): TResolvedValue;
24
24
  };
25
25
  /**
26
- * Object key used to store the input item in the output tree node.
26
+ * Key where the item is stored in the output node.
27
27
  *
28
- * Set to `false` to merge the item into the node itself.
28
+ * Set to `false` to inline the item directly into the node.
29
29
  *
30
30
  * Defaults to `'value'`.
31
31
  */
32
- nodeValueKey?: TNodeValueKey;
32
+ valueKey?: TValueKey;
33
33
  /**
34
- * Object key used to store the reference to the parent node in the output tree node.
34
+ * Key where the node's parent reference is stored in the output node.
35
35
  *
36
36
  * Set to `false` to omit parent links.
37
37
  *
38
38
  * Defaults to `'parent'`.
39
39
  */
40
- nodeParentKey?: TNodeParentKey;
40
+ parentKey?: TParentKey;
41
41
  /**
42
- * Object key used to store the list of child nodes in the output tree node.
42
+ * Key where the node's children are stored in the output node.
43
43
  *
44
44
  * Defaults to `'children'`.
45
45
  */
46
- nodeChildrenKey?: TNodeChildrenKey;
46
+ childrenKey?: TChildrenKey;
47
47
  /**
48
- * Object key used to store a value indicating the node depth in the tree on each output node.
48
+ * Key where the node's depth is stored in the output node.
49
49
  * Root nodes have a depth of 0.
50
50
  *
51
51
  * Set to `false` to omit depth values.
52
52
  *
53
- * Enables `validateTree`, as depth assignment requires a valid tree structure,
54
- * and both operations also share the same traversal logic.
53
+ * Automatically enables `validateTree` when a string value is set here:
54
+ * Depth assignment requires a valid tree structure, and both operations also share the same traversal logic.
55
55
  *
56
56
  * Defaults to `false`.
57
57
  */
58
- nodeDepthKey?: TNodeDepthKey;
58
+ depthKey?: TDepthKey;
59
59
  /**
60
60
  * Validates that the final structure forms a tree.
61
61
  *
@@ -107,7 +107,7 @@ export default function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue
107
107
  */
108
108
  childIds?: never;
109
109
  })): {
110
- roots: TreeNode<TMappedValue extends undefined ? TInputValue : TMappedValue, TNodeValueKey, TNodeParentKey, TNodeChildrenKey, TNodeDepthKey>[];
111
- nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TreeNode<TMappedValue extends undefined ? TInputValue : TMappedValue, TNodeValueKey, TNodeParentKey, TNodeChildrenKey, TNodeDepthKey>>;
110
+ roots: TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>[];
111
+ nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>>;
112
112
  };
113
113
  export {};
package/index.mjs CHANGED
@@ -6,11 +6,11 @@ function buildTree(items, options) {
6
6
  id: idAccessor,
7
7
  parentId: parentIdAccessor,
8
8
  childIds: childIdsAccessor,
9
- nodeValueMapper,
10
- nodeValueKey = "value",
11
- nodeParentKey = "parent",
12
- nodeChildrenKey = "children",
13
- nodeDepthKey = false,
9
+ valueResolver,
10
+ valueKey = "value",
11
+ parentKey = "parent",
12
+ childrenKey = "children",
13
+ depthKey = false,
14
14
  validateTree = false,
15
15
  validateReferences = false
16
16
  } = options;
@@ -32,21 +32,21 @@ function buildTree(items, options) {
32
32
  if (nodes.has(id)) {
33
33
  throw new Error(`Duplicate identifier '${id}'.`);
34
34
  }
35
- const node = nodeValueKey !== false ? { [nodeValueKey]: nodeValueMapper ? nodeValueMapper(item) : item } : { ...nodeValueMapper ? nodeValueMapper(item) : item };
36
- if (nodeValueKey === false) {
37
- if (nodeParentKey !== false) {
38
- delete node[nodeParentKey];
35
+ const node = valueKey !== false ? { [valueKey]: valueResolver ? valueResolver(item) : item } : { ...valueResolver ? valueResolver(item) : item };
36
+ if (valueKey === false) {
37
+ if (parentKey !== false) {
38
+ delete node[parentKey];
39
39
  }
40
- delete node[nodeChildrenKey];
40
+ delete node[childrenKey];
41
41
  }
42
42
  nodes.set(id, node);
43
43
  const parentId = typeof parentIdAccessor === "function" ? parentIdAccessor(item) : item[parentIdAccessor];
44
44
  const parentNode = nodes.get(parentId);
45
45
  if (parentNode) {
46
- parentNode[nodeChildrenKey] ||= [];
47
- parentNode[nodeChildrenKey].push(node);
48
- if (nodeParentKey !== false) {
49
- node[nodeParentKey] = parentNode;
46
+ parentNode[childrenKey] ||= [];
47
+ parentNode[childrenKey].push(node);
48
+ if (parentKey !== false) {
49
+ node[parentKey] = parentNode;
50
50
  }
51
51
  } else {
52
52
  const siblings = waitingForParent.get(parentId);
@@ -58,10 +58,10 @@ function buildTree(items, options) {
58
58
  }
59
59
  const children = waitingForParent.get(id);
60
60
  if (children) {
61
- node[nodeChildrenKey] = children;
62
- if (nodeParentKey !== false) {
61
+ node[childrenKey] = children;
62
+ if (parentKey !== false) {
63
63
  for (const child of children) {
64
- child[nodeParentKey] = node;
64
+ child[parentKey] = node;
65
65
  }
66
66
  }
67
67
  waitingForParent.delete(id);
@@ -83,12 +83,12 @@ function buildTree(items, options) {
83
83
  if (nodes.has(id)) {
84
84
  throw new Error(`Duplicate identifier '${id}'.`);
85
85
  }
86
- const node = nodeValueKey !== false ? { [nodeValueKey]: nodeValueMapper ? nodeValueMapper(item) : item } : { ...nodeValueMapper ? nodeValueMapper(item) : item };
87
- if (nodeValueKey === false) {
88
- if (nodeParentKey !== false) {
89
- delete node[nodeParentKey];
86
+ const node = valueKey !== false ? { [valueKey]: valueResolver ? valueResolver(item) : item } : { ...valueResolver ? valueResolver(item) : item };
87
+ if (valueKey === false) {
88
+ if (parentKey !== false) {
89
+ delete node[parentKey];
90
90
  }
91
- delete node[nodeChildrenKey];
91
+ delete node[childrenKey];
92
92
  }
93
93
  nodes.set(id, node);
94
94
  const childIds = typeof childIdsAccessor === "function" ? childIdsAccessor(item) : item[childIdsAccessor];
@@ -96,16 +96,16 @@ function buildTree(items, options) {
96
96
  if (typeof childIds[Symbol.iterator] !== "function") {
97
97
  throw new Error(`Item '${id}' has invalid children: expected an iterable value.`);
98
98
  }
99
- node[nodeChildrenKey] = [];
99
+ node[childrenKey] = [];
100
100
  for (const childId of childIds) {
101
101
  const childNode = nodes.get(childId);
102
102
  if (childNode) {
103
- node[nodeChildrenKey].push(childNode);
104
- if (nodeParentKey !== false) {
105
- if (childNode[nodeParentKey] && childNode[nodeParentKey] !== node) {
103
+ node[childrenKey].push(childNode);
104
+ if (parentKey !== false) {
105
+ if (childNode[parentKey] && childNode[parentKey] !== node) {
106
106
  throw new Error(`Node '${childId}' already has a different parent, refusing to overwrite.`);
107
107
  }
108
- childNode[nodeParentKey] = node;
108
+ childNode[parentKey] = node;
109
109
  }
110
110
  rootCandidates.delete(childId);
111
111
  } else {
@@ -114,24 +114,24 @@ function buildTree(items, options) {
114
114
  }
115
115
  waitingChildren.set(childId, {
116
116
  parentNode: node,
117
- childIndex: node[nodeChildrenKey].length
117
+ childIndex: node[childrenKey].length
118
118
  });
119
- node[nodeChildrenKey].push(null);
119
+ node[childrenKey].push(null);
120
120
  }
121
121
  }
122
- if (node[nodeChildrenKey].length === 0) {
123
- delete node[nodeChildrenKey];
122
+ if (node[childrenKey].length === 0) {
123
+ delete node[childrenKey];
124
124
  }
125
125
  }
126
126
  const parentDescriptor = waitingChildren.get(id);
127
127
  if (parentDescriptor) {
128
128
  const { parentNode, childIndex } = parentDescriptor;
129
- parentNode[nodeChildrenKey][childIndex] = node;
130
- if (nodeParentKey !== false) {
131
- if (node[nodeParentKey] && node[nodeParentKey] !== parentNode) {
129
+ parentNode[childrenKey][childIndex] = node;
130
+ if (parentKey !== false) {
131
+ if (node[parentKey] && node[parentKey] !== parentNode) {
132
132
  throw new Error(`Node '${id}' already has a different parent, refusing to overwrite.`);
133
133
  }
134
- node[nodeParentKey] = parentNode;
134
+ node[parentKey] = parentNode;
135
135
  }
136
136
  waitingChildren.delete(id);
137
137
  } else {
@@ -146,9 +146,9 @@ function buildTree(items, options) {
146
146
  const pending = Array.from(waitingChildren.values());
147
147
  for (let i = pending.length - 1; i >= 0; i--) {
148
148
  const { parentNode, childIndex } = pending[i];
149
- parentNode[nodeChildrenKey].splice(childIndex, 1);
150
- if (parentNode[nodeChildrenKey].length === 0) {
151
- delete parentNode[nodeChildrenKey];
149
+ parentNode[childrenKey].splice(childIndex, 1);
150
+ if (parentNode[childrenKey].length === 0) {
151
+ delete parentNode[childrenKey];
152
152
  }
153
153
  }
154
154
  }
@@ -156,7 +156,7 @@ function buildTree(items, options) {
156
156
  roots.push(node);
157
157
  }
158
158
  }
159
- const withDepth = typeof nodeDepthKey === "string" || typeof nodeDepthKey === "symbol" || typeof nodeDepthKey === "number";
159
+ const withDepth = typeof depthKey === "string" || typeof depthKey === "symbol" || typeof depthKey === "number";
160
160
  if (validateTree || withDepth) {
161
161
  if (roots.length === 0 && nodes.size > 0) {
162
162
  throw new Error("Tree validation failed: detected a cycle.");
@@ -172,10 +172,10 @@ function buildTree(items, options) {
172
172
  }
173
173
  visited.add(node);
174
174
  if (withDepth) {
175
- node[nodeDepthKey] = depth;
175
+ node[depthKey] = depth;
176
176
  }
177
- if (node[nodeChildrenKey]) {
178
- for (const child of node[nodeChildrenKey]) {
177
+ if (node[childrenKey]) {
178
+ for (const child of node[childrenKey]) {
179
179
  stack.push({ node: child, depth: depth + 1 });
180
180
  }
181
181
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fast-tree-builder",
3
- "version": "2.0.0-beta.3",
3
+ "version": "2.0.0-beta.4",
4
4
  "description": "Easily construct highly customizable bi-directional tree structures from iterable data.",
5
5
  "types": "./index.d.mts",
6
6
  "module": "./index.mjs",