Skip to content

Commit 9c47fe3

Browse files
committed
perf(shared): walk declaration in one pass
1 parent f3b6ff3 commit 9c47fe3

1 file changed

Lines changed: 29 additions & 22 deletions

File tree

packages/shared/src/graph/walk.ts

Lines changed: 29 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,41 +8,48 @@ import type { GetPointerPriorityFn, WalkFn } from './types/walk';
88
* added to the graph.
99
*/
1010
const walkDeclarations: WalkFn = (graph, callback, options) => {
11-
const pointers = Array.from(graph.nodes.keys());
12-
1311
if (options?.preferGroups && options.preferGroups.length) {
14-
// Precompute each pointer's group once so matchPointerToGroup is not called
15-
// O(groups × pointers) times.
16-
const pointerGroup = new Map<string, string | undefined>();
17-
if (options.matchPointerToGroup) {
18-
for (const pointer of pointers) {
19-
const result = options.matchPointerToGroup(pointer);
20-
pointerGroup.set(pointer, result.matched ? result.kind : undefined);
12+
// Single pass: bucket each pointer into its group (or unmatched).
13+
// This avoids re-scanning all pointers K times (once per preferred group).
14+
const buckets = new Map<string, string[]>();
15+
for (const kind of options.preferGroups) {
16+
if (!buckets.has(kind)) {
17+
buckets.set(kind, []);
2118
}
2219
}
20+
const unmatched: string[] = [];
2321

24-
// emit nodes that match each preferred group in order
25-
const emitted = new Set<string>();
26-
for (const kind of options.preferGroups) {
27-
for (const pointer of pointers) {
28-
if (pointerGroup.get(pointer) === kind) {
29-
emitted.add(pointer);
30-
callback(pointer, graph.nodes.get(pointer)!);
22+
for (const pointer of graph.nodes.keys()) {
23+
if (options.matchPointerToGroup) {
24+
const result = options.matchPointerToGroup(pointer);
25+
if (result.matched) {
26+
const bucket = buckets.get(result.kind);
27+
// kind not in preferGroups → treat as unmatched
28+
(bucket ?? unmatched).push(pointer);
29+
continue;
3130
}
3231
}
32+
unmatched.push(pointer);
3333
}
3434

35-
// emit anything not covered by the preferGroups (in declaration order)
36-
for (const pointer of pointers) {
37-
if (emitted.has(pointer)) continue;
35+
// emit in group order, then unmatched in declaration order
36+
const emittedGroups = new Set<string>();
37+
for (const kind of options.preferGroups) {
38+
if (emittedGroups.has(kind)) continue;
39+
emittedGroups.add(kind);
40+
for (const pointer of buckets.get(kind)!) {
41+
callback(pointer, graph.nodes.get(pointer)!);
42+
}
43+
}
44+
for (const pointer of unmatched) {
3845
callback(pointer, graph.nodes.get(pointer)!);
3946
}
4047
return;
4148
}
4249

43-
// fallback: simple declaration order
44-
for (const pointer of pointers) {
45-
callback(pointer, graph.nodes.get(pointer)!);
50+
// fallback: simple declaration order, no need to materialise an array
51+
for (const [pointer, node] of graph.nodes) {
52+
callback(pointer, node);
4653
}
4754
};
4855

0 commit comments

Comments
 (0)