fast-tree-builder 2.0.0 → 2.0.2
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 +63 -16
- package/index.cjs +8 -1
- package/index.d.cts +22 -11
- package/index.d.mts +22 -11
- package/index.mjs +8 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,8 +61,8 @@ Builds a tree structure from an iterable list of items.
|
|
|
61
61
|
|
|
62
62
|
##### One of
|
|
63
63
|
|
|
64
|
-
- `parentId`: A key or function that
|
|
65
|
-
- `childIds`: A key or function that
|
|
64
|
+
- `parentId`: A key or a function that returns the parent ID of the item.
|
|
65
|
+
- `childIds`: A key or a function that returns an iterable of child IDs for the item.
|
|
66
66
|
|
|
67
67
|
##### Optional
|
|
68
68
|
|
|
@@ -71,20 +71,10 @@ Builds a tree structure from an iterable list of items.
|
|
|
71
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
72
|
- `childrenKey`: Key where the node's children are stored in the output node. Defaults to `'children'`.
|
|
73
73
|
- `depthKey`: Key where the node's depth (with root = 0) is stored in the output node. Set to `false` to omit depth values. Setting this enables validateTree implicitly, as depth calculation requires full tree validation. Defaults to `false`.
|
|
74
|
+
- `includeEmptyChildrenArray`: Leaf nodes will include an empty children array when this is set to `true`. Otherwise they are left as `undefined`. Defaults to `false`.
|
|
74
75
|
- `validateReferences`: When `true`, verifies all `parentId` or `childIds` resolve to real items. Only `null` and `undefined` are acceptable parent ids for root nodes when enabled. Errors are thrown on invalid references. Defaults to `false`.
|
|
75
76
|
- `validateTree`: When `true`, verifies that the final structure is a valid tree (no cycles or nodes reachable via multiple paths). Errors are thrown if the check fails. Defaults to `false`.
|
|
76
77
|
|
|
77
|
-
> **Input Accessors vs. Output Keys**
|
|
78
|
-
>
|
|
79
|
-
> * `id`, `parentId`, `childIds` works on the input item and can be property names or functions. The library does not make any assumption what an id should be so we purposely allow `null` and `undefined` as a valid id too!
|
|
80
|
-
> * `valueKey`, `parentKey`, `childrenKey`, `depthKey` are always strings or `false` and are used as keys in the output nodes.
|
|
81
|
-
|
|
82
|
-
> **'validateReferences' option**
|
|
83
|
-
>
|
|
84
|
-
> Validation operates differently when in `parentId` mode and in `childIds` mode!
|
|
85
|
-
> * in `parentId` mode: validates that the parent ids of root nodes was `null` or `undefined` and nothing else. If you expect these parent ids to be other than `null` or `undefined`, you can safely turn off this validation and loop trough on the roots manually to check the original parentId values are the ones you expect.
|
|
86
|
-
> * in `childIds` mode: validates that every referenced child is resolved. Even if the child list contains `undefined`, a node with an `undefined` as ID must exist in the input.
|
|
87
|
-
|
|
88
78
|
#### Returns
|
|
89
79
|
|
|
90
80
|
```ts
|
|
@@ -101,6 +91,36 @@ Builds a tree structure from an iterable list of items.
|
|
|
101
91
|
- Invalid reference (if `validateReferences` is enabled)
|
|
102
92
|
- Cycle or structural error (if `validateTree` is enabled or `depthKey` is string)
|
|
103
93
|
|
|
94
|
+
### Good to know
|
|
95
|
+
|
|
96
|
+
> **Input Accessors vs. Output Keys in Options**
|
|
97
|
+
>
|
|
98
|
+
> * `id`, `parentId`, `childIds` works on the input item and can be property names or functions.
|
|
99
|
+
> * `valueKey`, `parentKey`, `childrenKey`, `depthKey` are always strings or `false` and are used as keys in the output nodes.
|
|
100
|
+
>
|
|
101
|
+
> I considered prefixing these two groups with `input` and `output` to distinguish them, but in the end, this note in the README felt good enough.
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
> **Identifiers**
|
|
105
|
+
>
|
|
106
|
+
> The library makes no assumptions about ID values — any unique JavaScript value is accepted, including `null` and `undefined`.
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
> **Child Node Ordering**
|
|
110
|
+
>
|
|
111
|
+
> This library preserves the order of items when building tree structures, depending on how the tree is constructed:
|
|
112
|
+
>
|
|
113
|
+
> When using parent IDs to connect items, the order of child nodes will match the order in which the items appeared in the original input array.
|
|
114
|
+
>
|
|
115
|
+
> When using child IDs to connect items, the order of child nodes will match the order of the child IDs defined in the input item.
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
> **'validateReferences' option**
|
|
119
|
+
>
|
|
120
|
+
> Validation operates differently when in `parentId` mode and in `childIds` mode!
|
|
121
|
+
> * in `parentId` mode: validates that the parent IDs of root nodes was `null` or `undefined` and nothing else. If you expect these parent IDs to be other than `null` or `undefined`, you can safely turn off this validation and loop trough on the roots manually to check the original parentId values are the ones you expect.
|
|
122
|
+
> * in `childIds` mode: validates that every referenced child is resolved. Even if the child list contains `undefined`, a node with an `undefined` as ID must exist in the input.
|
|
123
|
+
|
|
104
124
|
|
|
105
125
|
## Usage
|
|
106
126
|
|
|
@@ -293,13 +313,40 @@ console.log(roots);
|
|
|
293
313
|
type TreeNode = typeof roots[number];
|
|
294
314
|
```
|
|
295
315
|
|
|
296
|
-
|
|
316
|
+
If the above doesn't work for your case, define your tree node type from scratch.
|
|
317
|
+
|
|
318
|
+
We intentionally don’t expose a generic `TreeNode` type in the package, as maintaining a complex set of generic parameters is often more cumbersome than writing a custom recursive type yourself.
|
|
319
|
+
|
|
320
|
+
2. How can I present the `children` list in a specific order?
|
|
321
|
+
|
|
322
|
+
Pre-sort your input items:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
const items = [
|
|
326
|
+
{ id: 0, name: 'X', order: 0 },
|
|
327
|
+
{ id: 1, name: 'A', order: 3, parent: 0 },
|
|
328
|
+
{ id: 2, name: 'B', order: 2, parent: 0 },
|
|
329
|
+
{ id: 3, name: 'C', order: 1, parent: 0 },
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
// sort input by your `order` value
|
|
333
|
+
items.sort((a, b) => a.order - b.order);
|
|
334
|
+
|
|
335
|
+
const { roots } = buildTree(items, {
|
|
336
|
+
id: 'id',
|
|
337
|
+
parentId: 'parent',
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
roots[0].children
|
|
341
|
+
// this will be sorted as 'C', 'B', 'A'
|
|
342
|
+
// according to their order values.
|
|
343
|
+
```
|
|
297
344
|
|
|
298
345
|
## Comparison with other tree building libraries
|
|
299
346
|
|
|
300
|
-
The package
|
|
347
|
+
The package is designed to be feature-complete and highly customizable, which often comes at the cost of performance. Some libraries may be more *performant*, but they lack features I regularly need. In typical use cases, this package performs well, and others are usually only faster when offering much less customization.
|
|
301
348
|
|
|
302
|
-
For scenarios where performance is critical and you start
|
|
349
|
+
For scenarios where performance is critical and you start benchmarking libraries, consider implementing your custom algorithm instead. It could be as simple as:
|
|
303
350
|
```js
|
|
304
351
|
const roots = [];
|
|
305
352
|
const nodes = new Map();
|
package/index.cjs
CHANGED
|
@@ -35,7 +35,8 @@ function buildTree(items, options) {
|
|
|
35
35
|
childrenKey = "children",
|
|
36
36
|
depthKey = false,
|
|
37
37
|
validateTree = false,
|
|
38
|
-
validateReferences = false
|
|
38
|
+
validateReferences = false,
|
|
39
|
+
includeEmptyChildrenArray = false
|
|
39
40
|
} = options;
|
|
40
41
|
if (!idAccessor) {
|
|
41
42
|
throw new Error(`Option 'id' is required.`);
|
|
@@ -62,6 +63,9 @@ function buildTree(items, options) {
|
|
|
62
63
|
}
|
|
63
64
|
delete node[childrenKey];
|
|
64
65
|
}
|
|
66
|
+
if (includeEmptyChildrenArray) {
|
|
67
|
+
node[childrenKey] = [];
|
|
68
|
+
}
|
|
65
69
|
nodes.set(id, node);
|
|
66
70
|
const parentId = typeof parentIdAccessor === "function" ? parentIdAccessor(item) : item[parentIdAccessor];
|
|
67
71
|
const parentNode = nodes.get(parentId);
|
|
@@ -113,6 +117,9 @@ function buildTree(items, options) {
|
|
|
113
117
|
}
|
|
114
118
|
delete node[childrenKey];
|
|
115
119
|
}
|
|
120
|
+
if (includeEmptyChildrenArray) {
|
|
121
|
+
node[childrenKey] = [];
|
|
122
|
+
}
|
|
116
123
|
nodes.set(id, node);
|
|
117
124
|
const childIds = typeof childIdsAccessor === "function" ? childIdsAccessor(item) : item[childIdsAccessor];
|
|
118
125
|
if (childIds != null) {
|
package/index.d.cts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
type TreeNode<TValue, TValueKey extends PropertyKey | false, TParentKey extends PropertyKey | false, TChildrenKey extends PropertyKey, TDepthKey extends PropertyKey | false> = (TValueKey extends false ? Omit<TValue, Exclude<TParentKey, false> | TChildrenKey | Exclude<TDepthKey, false>> : {
|
|
1
|
+
type TreeNode<TValue, TValueKey extends PropertyKey | false, TParentKey extends PropertyKey | false, TChildrenKey extends PropertyKey, TDepthKey extends PropertyKey | false, TIncludeEmptyChildrenArray extends boolean> = (TValueKey extends false ? Omit<TValue, Exclude<TParentKey, false> | TChildrenKey | Exclude<TDepthKey, false>> : {
|
|
2
2
|
[k in Exclude<TValueKey, false>]: TValue;
|
|
3
3
|
}) & (TParentKey extends false ? {} : {
|
|
4
|
-
[k in Exclude<TParentKey, false>]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>;
|
|
5
|
-
}) & {
|
|
6
|
-
[k in TChildrenKey]
|
|
7
|
-
}
|
|
4
|
+
[k in Exclude<TParentKey, false>]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>;
|
|
5
|
+
}) & (TIncludeEmptyChildrenArray extends true ? {
|
|
6
|
+
[k in TChildrenKey]: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>[];
|
|
7
|
+
} : {
|
|
8
|
+
[k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>[];
|
|
9
|
+
}) & (TDepthKey extends false ? {} : {
|
|
8
10
|
[k in Exclude<TDepthKey, false>]: number;
|
|
9
11
|
});
|
|
10
12
|
type AccessorReturnType<O, P extends (keyof O) | ((item: O) => any)> = P extends ((item: O) => infer R) ? R : P extends (keyof O) ? O[P] : never;
|
|
11
|
-
type ObjectKeysOfIterableProperties<T> = {
|
|
13
|
+
type ObjectKeysOfIterableProperties<T> = 0 extends (1 & T) ? PropertyKey : {
|
|
12
14
|
[K in keyof T]: T[K] extends (Iterable<unknown> & object) | null | undefined ? K : never;
|
|
13
15
|
}[keyof T];
|
|
14
|
-
declare function buildTree<TIdAccessor extends
|
|
16
|
+
declare function buildTree<TIdAccessor extends NoInfer<keyof 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, TIncludeEmptyChildrenArray extends boolean = false>(items: Iterable<TInputValue>, options: {
|
|
15
17
|
/**
|
|
16
18
|
* A string key or function used to get the item's unique identifier.
|
|
17
19
|
*/
|
|
@@ -56,6 +58,15 @@ declare function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue>) | ((
|
|
|
56
58
|
* Defaults to `false`.
|
|
57
59
|
*/
|
|
58
60
|
depthKey?: TDepthKey;
|
|
61
|
+
/**
|
|
62
|
+
* Leaf nodes will include an empty children array when this is set to `true`.
|
|
63
|
+
* Otherwise they are left as `undefined`.
|
|
64
|
+
*
|
|
65
|
+
* This ensures you can loop over every node child list without checking its existence.
|
|
66
|
+
*
|
|
67
|
+
* Defaults to `false`.
|
|
68
|
+
*/
|
|
69
|
+
includeEmptyChildrenArray?: TIncludeEmptyChildrenArray;
|
|
59
70
|
/**
|
|
60
71
|
* Validates that the final structure forms a tree.
|
|
61
72
|
*
|
|
@@ -90,14 +101,14 @@ declare function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue>) | ((
|
|
|
90
101
|
*
|
|
91
102
|
* Either `parentId` or `childIds` must be provided.
|
|
92
103
|
*/
|
|
93
|
-
childIds: ObjectKeysOfIterableProperties<
|
|
104
|
+
childIds: NoInfer<ObjectKeysOfIterableProperties<TInputValue>> | ((item: NoInfer<TInputValue>) => (Iterable<unknown> & object) | null | undefined);
|
|
94
105
|
} | {
|
|
95
106
|
/**
|
|
96
107
|
* A string key or function used to get the item's parent identifier.
|
|
97
108
|
*
|
|
98
109
|
* Either `parentId` or `childIds` must be provided.
|
|
99
110
|
*/
|
|
100
|
-
parentId:
|
|
111
|
+
parentId: NoInfer<keyof TInputValue> | ((item: NoInfer<TInputValue>) => unknown);
|
|
101
112
|
/**
|
|
102
113
|
* A string key or function to retrieve a list of child identifiers from an item.
|
|
103
114
|
*
|
|
@@ -105,8 +116,8 @@ declare function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue>) | ((
|
|
|
105
116
|
*/
|
|
106
117
|
childIds?: never;
|
|
107
118
|
})): {
|
|
108
|
-
roots: TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>[];
|
|
109
|
-
nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>>;
|
|
119
|
+
roots: TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>[];
|
|
120
|
+
nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>>;
|
|
110
121
|
};
|
|
111
122
|
declare const _default: { default: typeof buildTree };
|
|
112
123
|
export = _default;
|
package/index.d.mts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
|
-
type TreeNode<TValue, TValueKey extends PropertyKey | false, TParentKey extends PropertyKey | false, TChildrenKey extends PropertyKey, TDepthKey extends PropertyKey | false> = (TValueKey extends false ? Omit<TValue, Exclude<TParentKey, false> | TChildrenKey | Exclude<TDepthKey, false>> : {
|
|
1
|
+
type TreeNode<TValue, TValueKey extends PropertyKey | false, TParentKey extends PropertyKey | false, TChildrenKey extends PropertyKey, TDepthKey extends PropertyKey | false, TIncludeEmptyChildrenArray extends boolean> = (TValueKey extends false ? Omit<TValue, Exclude<TParentKey, false> | TChildrenKey | Exclude<TDepthKey, false>> : {
|
|
2
2
|
[k in Exclude<TValueKey, false>]: TValue;
|
|
3
3
|
}) & (TParentKey extends false ? {} : {
|
|
4
|
-
[k in Exclude<TParentKey, false>]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>;
|
|
5
|
-
}) & {
|
|
6
|
-
[k in TChildrenKey]
|
|
7
|
-
}
|
|
4
|
+
[k in Exclude<TParentKey, false>]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>;
|
|
5
|
+
}) & (TIncludeEmptyChildrenArray extends true ? {
|
|
6
|
+
[k in TChildrenKey]: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>[];
|
|
7
|
+
} : {
|
|
8
|
+
[k in TChildrenKey]?: TreeNode<TValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>[];
|
|
9
|
+
}) & (TDepthKey extends false ? {} : {
|
|
8
10
|
[k in Exclude<TDepthKey, false>]: number;
|
|
9
11
|
});
|
|
10
12
|
type AccessorReturnType<O, P extends (keyof O) | ((item: O) => any)> = P extends ((item: O) => infer R) ? R : P extends (keyof O) ? O[P] : never;
|
|
11
|
-
type ObjectKeysOfIterableProperties<T> = {
|
|
13
|
+
type ObjectKeysOfIterableProperties<T> = 0 extends (1 & T) ? PropertyKey : {
|
|
12
14
|
[K in keyof T]: T[K] extends (Iterable<unknown> & object) | null | undefined ? K : never;
|
|
13
15
|
}[keyof T];
|
|
14
|
-
export default function buildTree<TIdAccessor extends
|
|
16
|
+
export default function buildTree<TIdAccessor extends NoInfer<keyof 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, TIncludeEmptyChildrenArray extends boolean = false>(items: Iterable<TInputValue>, options: {
|
|
15
17
|
/**
|
|
16
18
|
* A string key or function used to get the item's unique identifier.
|
|
17
19
|
*/
|
|
@@ -56,6 +58,15 @@ export default function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue
|
|
|
56
58
|
* Defaults to `false`.
|
|
57
59
|
*/
|
|
58
60
|
depthKey?: TDepthKey;
|
|
61
|
+
/**
|
|
62
|
+
* Leaf nodes will include an empty children array when this is set to `true`.
|
|
63
|
+
* Otherwise they are left as `undefined`.
|
|
64
|
+
*
|
|
65
|
+
* This ensures you can loop over every node child list without checking its existence.
|
|
66
|
+
*
|
|
67
|
+
* Defaults to `false`.
|
|
68
|
+
*/
|
|
69
|
+
includeEmptyChildrenArray?: TIncludeEmptyChildrenArray;
|
|
59
70
|
/**
|
|
60
71
|
* Validates that the final structure forms a tree.
|
|
61
72
|
*
|
|
@@ -90,14 +101,14 @@ export default function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue
|
|
|
90
101
|
*
|
|
91
102
|
* Either `parentId` or `childIds` must be provided.
|
|
92
103
|
*/
|
|
93
|
-
childIds: ObjectKeysOfIterableProperties<
|
|
104
|
+
childIds: NoInfer<ObjectKeysOfIterableProperties<TInputValue>> | ((item: NoInfer<TInputValue>) => (Iterable<unknown> & object) | null | undefined);
|
|
94
105
|
} | {
|
|
95
106
|
/**
|
|
96
107
|
* A string key or function used to get the item's parent identifier.
|
|
97
108
|
*
|
|
98
109
|
* Either `parentId` or `childIds` must be provided.
|
|
99
110
|
*/
|
|
100
|
-
parentId:
|
|
111
|
+
parentId: NoInfer<keyof TInputValue> | ((item: NoInfer<TInputValue>) => unknown);
|
|
101
112
|
/**
|
|
102
113
|
* A string key or function to retrieve a list of child identifiers from an item.
|
|
103
114
|
*
|
|
@@ -105,7 +116,7 @@ export default function buildTree<TIdAccessor extends (keyof NoInfer<TInputValue
|
|
|
105
116
|
*/
|
|
106
117
|
childIds?: never;
|
|
107
118
|
})): {
|
|
108
|
-
roots: TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>[];
|
|
109
|
-
nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey>>;
|
|
119
|
+
roots: TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>[];
|
|
120
|
+
nodes: Map<AccessorReturnType<TInputValue, TIdAccessor>, TreeNode<TResolvedValue extends undefined ? TInputValue : TResolvedValue, TValueKey, TParentKey, TChildrenKey, TDepthKey, TIncludeEmptyChildrenArray>>;
|
|
110
121
|
};
|
|
111
122
|
export {};
|
package/index.mjs
CHANGED
|
@@ -12,7 +12,8 @@ function buildTree(items, options) {
|
|
|
12
12
|
childrenKey = "children",
|
|
13
13
|
depthKey = false,
|
|
14
14
|
validateTree = false,
|
|
15
|
-
validateReferences = false
|
|
15
|
+
validateReferences = false,
|
|
16
|
+
includeEmptyChildrenArray = false
|
|
16
17
|
} = options;
|
|
17
18
|
if (!idAccessor) {
|
|
18
19
|
throw new Error(`Option 'id' is required.`);
|
|
@@ -39,6 +40,9 @@ function buildTree(items, options) {
|
|
|
39
40
|
}
|
|
40
41
|
delete node[childrenKey];
|
|
41
42
|
}
|
|
43
|
+
if (includeEmptyChildrenArray) {
|
|
44
|
+
node[childrenKey] = [];
|
|
45
|
+
}
|
|
42
46
|
nodes.set(id, node);
|
|
43
47
|
const parentId = typeof parentIdAccessor === "function" ? parentIdAccessor(item) : item[parentIdAccessor];
|
|
44
48
|
const parentNode = nodes.get(parentId);
|
|
@@ -90,6 +94,9 @@ function buildTree(items, options) {
|
|
|
90
94
|
}
|
|
91
95
|
delete node[childrenKey];
|
|
92
96
|
}
|
|
97
|
+
if (includeEmptyChildrenArray) {
|
|
98
|
+
node[childrenKey] = [];
|
|
99
|
+
}
|
|
93
100
|
nodes.set(id, node);
|
|
94
101
|
const childIds = typeof childIdsAccessor === "function" ? childIdsAccessor(item) : item[childIdsAccessor];
|
|
95
102
|
if (childIds != null) {
|
package/package.json
CHANGED