Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,7 @@ Simulating with N=5000 icons, M=20 slugs:
## 2024-10-10 - Avoid `new Map(array.map(...))` initialization pattern
**Learning:** Initializing Maps using `new Map(array.map(...))` creates an intermediate array of tuples, which increases memory allocation and triggers garbage collection. This is an explicit anti-pattern in this project.
**Action:** Always use an inline initialization function with a single-pass `for` loop to populate Maps and Sets, avoiding intermediate object allocations.

## 2024-10-12 - Prevent intermediate array allocations on Map entries
**Learning:** Using `[...map.entries()].map(([k, v]) => ({ k, v }))` allocates a full intermediate array of tuples before mapping them to objects. For large maps (like categories or icons), this creates a massive spike in memory usage and triggers aggressive garbage collection pauses on the main thread, leading to jitter in the UI during calculations.
**Action:** Always use `Array.from(map, ([k, v]) => ({ k, v }))` instead, which iterates over the Map entries and applies the mapping function in a single pass without allocating the intermediate tuple array.
5 changes: 3 additions & 2 deletions src/components/home-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,9 @@ export function HomeContent({ categoryCounts, count, recentIcons, collections, d
counts.set(c, (counts.get(c) || 0) + 1);
}
}
return [...counts.entries()]
.map(([name, count]) => ({ name, count }))
// ⚑ Bolt: Use `Array.from(map, mappingFunction)` instead of `[...map.entries()].map(...)`
// to avoid allocating a massive intermediate array of tuples and reduce GC pressure.
return Array.from(counts, ([name, count]) => ({ name, count }))
.sort((a, b) => a.name.localeCompare(b.name));
}, [collectionParam, collectionIcons, categoryCounts]);

Expand Down
5 changes: 3 additions & 2 deletions src/components/home-hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -394,8 +394,9 @@ export function HomeHero({
counts.set(c, (counts.get(c) || 0) + 1);
}
}
return [...counts.entries()]
.map(([name, count]) => ({ name, count }))
// ⚑ Bolt: Use `Array.from(map, mappingFunction)` instead of `[...map.entries()].map(...)`
// to avoid allocating a massive intermediate array of tuples and reduce GC pressure.
return Array.from(counts, ([name, count]) => ({ name, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 8);
}, [collectionIcons]);
Expand Down
5 changes: 3 additions & 2 deletions src/lib/command-palette.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ export function categoriesFromIcons(icons: readonly IconEntry[]): PaletteCategor
counts.set(name, (counts.get(name) ?? 0) + 1);
}
}
return [...counts.entries()]
.map(([name, count]) => ({ name, count }))
// ⚑ Bolt: Use `Array.from(map, mappingFunction)` instead of `[...map.entries()].map(...)`
// to avoid allocating a massive intermediate array of tuples and reduce GC pressure.
return Array.from(counts, ([name, count]) => ({ name, count }))
.sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
}

Expand Down
10 changes: 6 additions & 4 deletions src/lib/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,9 @@ export function getCategoryCounts(collection?: Collection): { name: string; coun
}
}
}
return [...counts.entries()]
.map(([name, count]) => ({ name, count }))
// ⚑ Bolt: Use `Array.from(map, mappingFunction)` instead of `[...map.entries()].map(...)`
// to avoid allocating a massive intermediate array of tuples and reduce GC pressure.
return Array.from(counts, ([name, count]) => ({ name, count }))
.sort((a, b) => a.name.localeCompare(b.name));
}

Expand Down Expand Up @@ -176,7 +177,8 @@ export function getCollections(): { name: Collection; count: number }[] {
for (const icon of icons) {
counts.set(icon.collection, (counts.get(icon.collection) || 0) + 1);
}
return [...counts.entries()]
.map(([name, count]) => ({ name, count }))
// ⚑ Bolt: Use `Array.from(map, mappingFunction)` instead of `[...map.entries()].map(...)`
// to avoid allocating a massive intermediate array of tuples and reduce GC pressure.
return Array.from(counts, ([name, count]) => ({ name, count }))
.sort((a, b) => b.count - a.count);
}
5 changes: 3 additions & 2 deletions src/scripts/generate-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ function main() {
counts.set(c, (counts.get(c) || 0) + 1);
}
}
const categories = [...counts.entries()]
.map(([name, count]) => ({ name, count }))
// ⚑ Bolt: Use `Array.from(map, mappingFunction)` instead of `[...map.entries()].map(...)`
// to avoid allocating a massive intermediate array of tuples and reduce GC pressure.
const categories = Array.from(counts, ([name, count]) => ({ name, count }))
.sort((a, b) => a.name.localeCompare(b.name));

writeFileSync(
Expand Down
Loading