Skip to content

Commit f717964

Browse files
committed
Improve type of NodePredicate
1 parent 45d4b44 commit f717964

2 files changed

Lines changed: 61 additions & 55 deletions

File tree

packages/utils/README.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -178,8 +178,8 @@ The package provides a comprehensive set of utilities for traversing, transformi
178178

179179
| Function | Description |
180180
| ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------- |
181-
| [`forEachNode`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L109) | Visit every node in the tree synchronously using pre-order traversal |
182-
| [`forEachNodeAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L142) | Visit every node in the tree asynchronously using pre-order traversal |
181+
| [`forEachNode`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L111) | Visit every node in the tree synchronously using pre-order traversal |
182+
| [`forEachNodeAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L145) | Visit every node in the tree asynchronously using pre-order traversal |
183183

184184
Visit all nodes in the tree using pre-order traversal:
185185

@@ -201,8 +201,8 @@ await forEachNodeAsync(structuredText, async (node, parent, path) => {
201201

202202
| Function | Description |
203203
| --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
204-
| [`mapNodes`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L176) | Transform nodes in the tree synchronously while preserving structure |
205-
| [`mapNodesAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L257) | Transform nodes in the tree asynchronously while preserving structure |
204+
| [`mapNodes`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L180) | Transform nodes in the tree synchronously while preserving structure |
205+
| [`mapNodesAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L261) | Transform nodes in the tree asynchronously while preserving structure |
206206

207207
Transform nodes while preserving the tree structure:
208208

@@ -237,10 +237,10 @@ const processed = await mapNodesAsync(structuredText, async (node) => {
237237

238238
| Function | Description |
239239
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ |
240-
| [`collectNodes`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L340) | Collect all nodes that match a predicate function |
241-
| [`collectNodesAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L398) | Collect all nodes that match an async predicate function |
242-
| [`findFirstNode`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L437) | Find the first node that matches a predicate function |
243-
| [`findFirstNodeAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L515) | Find the first node that matches an async predicate function |
240+
| [`collectNodes`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L344) | Collect all nodes that match a predicate function |
241+
| [`collectNodesAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L402) | Collect all nodes that match an async predicate function |
242+
| [`findFirstNode`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L441) | Find the first node that matches a predicate function |
243+
| [`findFirstNodeAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L519) | Find the first node that matches an async predicate function |
244244

245245
Find specific nodes using predicates or type guards:
246246

@@ -275,8 +275,8 @@ const strongText = collectNodes(
275275

276276
| Function | Description |
277277
| ------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------- |
278-
| [`filterNodes`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L564) | Remove nodes that don't match a predicate synchronously |
279-
| [`filterNodesAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L647) | Remove nodes that don't match an async predicate |
278+
| [`filterNodes`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L568) | Remove nodes that don't match a predicate synchronously |
279+
| [`filterNodesAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L651) | Remove nodes that don't match an async predicate |
280280

281281
Remove nodes that don't match a predicate:
282282

@@ -304,8 +304,8 @@ const validated = await filterNodesAsync(structuredText, async (node) => {
304304

305305
| Function | Description |
306306
| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |
307-
| [`reduceNodes`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L734) | Reduce the tree to a single value using a synchronous reducer function |
308-
| [`reduceNodesAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L779) | Reduce the tree to a single value using an async reducer function |
307+
| [`reduceNodes`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L738) | Reduce the tree to a single value using a synchronous reducer function |
308+
| [`reduceNodesAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L783) | Reduce the tree to a single value using an async reducer function |
309309

310310
Reduce the entire tree to a single value:
311311

@@ -339,10 +339,10 @@ const nodeCounts = reduceNodes(
339339

340340
| Function | Description |
341341
| ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
342-
| [`someNode`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L821) | Check if any node in the tree matches a predicate (short-circuit evaluation) |
343-
| [`someNodeAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L862) | Check if any node in the tree matches an async predicate (short-circuit evaluation) |
344-
| [`everyNode`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L903) | Check if every node in the tree matches a predicate (short-circuit evaluation) |
345-
| [`everyNodeAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L934) | Check if every node in the tree matches an async predicate (short-circuit evaluation) |
342+
| [`someNode`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L825) | Check if any node in the tree matches a predicate (short-circuit evaluation) |
343+
| [`someNodeAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L867) | Check if any node in the tree matches an async predicate (short-circuit evaluation) |
344+
| [`everyNode`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L909) | Check if every node in the tree matches a predicate (short-circuit evaluation) |
345+
| [`everyNodeAsync`](https://github.com/datocms/structured-text/blob/main/packages/utils/src/manipulation.ts#L940) | Check if every node in the tree matches an async predicate (short-circuit evaluation) |
346346

347347
Test if any or all nodes match a condition:
348348

packages/utils/src/manipulation.ts

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ type AllNodesInTree<T, Depth extends number = 10> = Depth extends 0
1818
? T | AllNodesInTree<Children, Prev<Depth>>
1919
: T;
2020

21+
type WithChildren<T> = Extract<T, { children: readonly unknown[] }>;
22+
2123
type Prev<T extends number> = T extends 0
2224
? 0
2325
: T extends 1
@@ -57,7 +59,7 @@ type StructuredTextDocumentOrNode<T> = T | Document<T>;
5759
*/
5860
export type NodePredicate<T, R> = (
5961
node: AllNodesInTree<T>,
60-
parent: AllNodesInTree<T> | null,
62+
parent: WithChildren<AllNodesInTree<T>> | null,
6163
path: TreePath,
6264
) => R;
6365

@@ -109,7 +111,7 @@ function hasChildren(node: unknown): node is { children: readonly unknown[] } {
109111
export function forEachNode<T>(
110112
input: StructuredTextDocumentOrNode<T>,
111113
visitor: NodePredicate<T, void>,
112-
parent: AllNodesInTree<T> | null = null,
114+
parent: WithChildren<AllNodesInTree<T>> | null = null,
113115
path: TreePath = [],
114116
): void {
115117
const node = extractNode(input);
@@ -121,11 +123,12 @@ export function forEachNode<T>(
121123
if (hasChildren(node)) {
122124
for (let index = 0; index < node.children.length; index++) {
123125
const child = node.children[index];
124-
forEachNode(child as T, visitor, node as AllNodesInTree<T>, [
125-
...path,
126-
'children',
127-
index,
128-
]);
126+
forEachNode(
127+
child as T,
128+
visitor,
129+
node as WithChildren<AllNodesInTree<T>>,
130+
[...path, 'children', index],
131+
);
129132
}
130133
}
131134
}
@@ -142,7 +145,7 @@ export function forEachNode<T>(
142145
export async function forEachNodeAsync<T>(
143146
input: StructuredTextDocumentOrNode<T>,
144147
visitor: NodePredicate<T, Promise<void>>,
145-
parent: AllNodesInTree<T> | null = null,
148+
parent: WithChildren<AllNodesInTree<T>> | null = null,
146149
path: TreePath = [],
147150
): Promise<void> {
148151
const node = extractNode(input);
@@ -154,11 +157,12 @@ export async function forEachNodeAsync<T>(
154157
if (hasChildren(node)) {
155158
for (let index = 0; index < node.children.length; index++) {
156159
const child = node.children[index];
157-
await forEachNodeAsync(child as T, visitor, node as AllNodesInTree<T>, [
158-
...path,
159-
'children',
160-
index,
161-
]);
160+
await forEachNodeAsync(
161+
child as T,
162+
visitor,
163+
node as WithChildren<AllNodesInTree<T>>,
164+
[...path, 'children', index],
165+
);
162166
}
163167
}
164168
}
@@ -200,7 +204,7 @@ export function mapNodes<T, R>(
200204
export function mapNodes<T, R>(
201205
input: StructuredTextDocumentOrNode<T>,
202206
mapper: NodePredicate<T, R>,
203-
parent: AllNodesInTree<T> | null = null,
207+
parent: WithChildren<AllNodesInTree<T>> | null = null,
204208
path: TreePath = [],
205209
): StructuredTextDocumentOrNode<R> {
206210
const node = extractNode(input);
@@ -281,7 +285,7 @@ export async function mapNodesAsync<T, R>(
281285
export async function mapNodesAsync<T, R>(
282286
input: StructuredTextDocumentOrNode<T>,
283287
mapper: NodePredicate<T, Promise<R>>,
284-
parent: AllNodesInTree<T> | null = null,
288+
parent: WithChildren<AllNodesInTree<T>> | null = null,
285289
path: TreePath = [],
286290
): Promise<StructuredTextDocumentOrNode<R>> {
287291
const node = extractNode(input);
@@ -367,7 +371,7 @@ export function collectNodes<T>(
367371
export function collectNodes<T>(
368372
input: StructuredTextDocumentOrNode<T>,
369373
predicate: NodePredicate<T, boolean>,
370-
parent: AllNodesInTree<T> | null = null,
374+
parent: WithChildren<AllNodesInTree<T>> | null = null,
371375
path: TreePath = [],
372376
): Array<{ node: AllNodesInTree<T>; path: TreePath }> {
373377
const results: Array<{ node: AllNodesInTree<T>; path: TreePath }> = [];
@@ -405,7 +409,7 @@ export async function collectNodesAsync<T>(
405409
export async function collectNodesAsync<T>(
406410
input: StructuredTextDocumentOrNode<T>,
407411
predicate: NodePredicate<T, Promise<boolean>>,
408-
parent: AllNodesInTree<T> | null = null,
412+
parent: WithChildren<AllNodesInTree<T>> | null = null,
409413
path: TreePath = [],
410414
): Promise<Array<{ node: AllNodesInTree<T>; path: TreePath }>> {
411415
const results: Array<{ node: AllNodesInTree<T>; path: TreePath }> = [];
@@ -464,7 +468,7 @@ export function findFirstNode<T>(
464468
export function findFirstNode<T>(
465469
input: StructuredTextDocumentOrNode<T>,
466470
predicate: NodePredicate<T, boolean>,
467-
parent: AllNodesInTree<T> | null = null,
471+
parent: WithChildren<AllNodesInTree<T>> | null = null,
468472
path: TreePath = [],
469473
): { node: AllNodesInTree<T>; path: TreePath } | null {
470474
const node = extractNode(input);
@@ -522,7 +526,7 @@ export async function findFirstNodeAsync<T>(
522526
export async function findFirstNodeAsync<T>(
523527
input: StructuredTextDocumentOrNode<T>,
524528
predicate: NodePredicate<T, Promise<boolean>>,
525-
parent: AllNodesInTree<T> | null = null,
529+
parent: WithChildren<AllNodesInTree<T>> | null = null,
526530
path: TreePath = [],
527531
): Promise<{ node: AllNodesInTree<T>; path: TreePath } | null> {
528532
const node = extractNode(input);
@@ -588,7 +592,7 @@ export function filterNodes<T>(
588592
export function filterNodes<T>(
589593
input: StructuredTextDocumentOrNode<T>,
590594
predicate: NodePredicate<T, boolean>,
591-
parent: AllNodesInTree<T> | null = null,
595+
parent: WithChildren<AllNodesInTree<T>> | null = null,
592596
path: TreePath = [],
593597
): StructuredTextDocumentOrNode<T> | null {
594598
const node = extractNode(input);
@@ -671,7 +675,7 @@ export async function filterNodesAsync<T>(
671675
export async function filterNodesAsync<T>(
672676
input: StructuredTextDocumentOrNode<T>,
673677
predicate: NodePredicate<T, Promise<boolean>>,
674-
parent: AllNodesInTree<T> | null = null,
678+
parent: WithChildren<AllNodesInTree<T>> | null = null,
675679
path: TreePath = [],
676680
): Promise<StructuredTextDocumentOrNode<T> | null> {
677681
const node = extractNode(input);
@@ -740,7 +744,7 @@ export function reduceNodes<T, R>(
740744
path: TreePath,
741745
) => R,
742746
initialValue: R,
743-
parent: AllNodesInTree<T> | null = null,
747+
parent: WithChildren<AllNodesInTree<T>> | null = null,
744748
path: TreePath = [],
745749
): R {
746750
const node = extractNode(input);
@@ -785,7 +789,7 @@ export async function reduceNodesAsync<T, R>(
785789
path: TreePath,
786790
) => Promise<R>,
787791
initialValue: R,
788-
parent: AllNodesInTree<T> | null = null,
792+
parent: WithChildren<AllNodesInTree<T>> | null = null,
789793
path: TreePath = [],
790794
): Promise<R> {
791795
const node = extractNode(input);
@@ -821,7 +825,7 @@ export async function reduceNodesAsync<T, R>(
821825
export function someNode<T>(
822826
input: StructuredTextDocumentOrNode<T>,
823827
predicate: NodePredicate<T, boolean>,
824-
parent: AllNodesInTree<T> | null = null,
828+
parent: WithChildren<AllNodesInTree<T>> | null = null,
825829
path: TreePath = [],
826830
): boolean {
827831
const node = extractNode(input);
@@ -836,11 +840,12 @@ export function someNode<T>(
836840
for (let index = 0; index < node.children.length; index++) {
837841
const child = node.children[index];
838842
if (
839-
someNode(child as T, predicate, node as AllNodesInTree<T>, [
840-
...path,
841-
'children',
842-
index,
843-
])
843+
someNode(
844+
child as T,
845+
predicate,
846+
node as WithChildren<AllNodesInTree<T>>,
847+
[...path, 'children', index],
848+
)
844849
) {
845850
return true;
846851
}
@@ -862,7 +867,7 @@ export function someNode<T>(
862867
export async function someNodeAsync<T>(
863868
input: StructuredTextDocumentOrNode<T>,
864869
predicate: NodePredicate<T, Promise<boolean>>,
865-
parent: AllNodesInTree<T> | null = null,
870+
parent: WithChildren<AllNodesInTree<T>> | null = null,
866871
path: TreePath = [],
867872
): Promise<boolean> {
868873
const node = extractNode(input);
@@ -877,11 +882,12 @@ export async function someNodeAsync<T>(
877882
for (let index = 0; index < node.children.length; index++) {
878883
const child = node.children[index];
879884
if (
880-
await someNodeAsync(child as T, predicate, node as AllNodesInTree<T>, [
881-
...path,
882-
'children',
883-
index,
884-
])
885+
await someNodeAsync(
886+
child as T,
887+
predicate,
888+
node as WithChildren<AllNodesInTree<T>>,
889+
[...path, 'children', index],
890+
)
885891
) {
886892
return true;
887893
}
@@ -903,7 +909,7 @@ export async function someNodeAsync<T>(
903909
export function everyNode<T>(
904910
input: StructuredTextDocumentOrNode<T>,
905911
predicate: NodePredicate<T, boolean>,
906-
parent: AllNodesInTree<T> | null = null,
912+
parent: WithChildren<AllNodesInTree<T>> | null = null,
907913
path: TreePath = [],
908914
): boolean {
909915
const node = extractNode(input);
@@ -912,7 +918,7 @@ export function everyNode<T>(
912918
node,
913919
(
914920
currentNode: AllNodesInTree<T>,
915-
currentParent: AllNodesInTree<T> | null,
921+
currentParent: WithChildren<AllNodesInTree<T>> | null,
916922
currentPath: TreePath,
917923
) => {
918924
return !predicate(currentNode, currentParent, currentPath);
@@ -934,7 +940,7 @@ export function everyNode<T>(
934940
export async function everyNodeAsync<T>(
935941
input: StructuredTextDocumentOrNode<T>,
936942
predicate: NodePredicate<T, Promise<boolean>>,
937-
parent: AllNodesInTree<T> | null = null,
943+
parent: WithChildren<AllNodesInTree<T>> | null = null,
938944
path: TreePath = [],
939945
): Promise<boolean> {
940946
const node = extractNode(input);
@@ -943,7 +949,7 @@ export async function everyNodeAsync<T>(
943949
node,
944950
async (
945951
currentNode: AllNodesInTree<T>,
946-
currentParent: AllNodesInTree<T> | null,
952+
currentParent: WithChildren<AllNodesInTree<T>> | null,
947953
currentPath: TreePath,
948954
) => {
949955
return !(await predicate(currentNode, currentParent, currentPath));

0 commit comments

Comments
 (0)