Skip to content

perf: prevent duplicates in file tree sprite#2586

Open
ghostdevv wants to merge 2 commits intomainfrom
g/file-tree-sprite-optimisation
Open

perf: prevent duplicates in file tree sprite#2586
ghostdevv wants to merge 2 commits intomainfrom
g/file-tree-sprite-optimisation

Conversation

@ghostdevv
Copy link
Copy Markdown
Contributor

🔗 Linked issue

n/a

🧭 Context

#1347

📚 Description

I noticed that there is duplicate icons in the file tree sprite, which doesn't seem to be intended - but I may be wrong. If I'm correct then this would shrink it currently from 576K to 260K 🎉

@ghostdevv ghostdevv requested a review from alexdln April 19, 2026 21:59
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Apr 19, 2026 10:00pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Apr 19, 2026 10:00pm
npmx-lunaria Ignored Ignored Apr 19, 2026 10:00pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 19, 2026

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • Bug Fixes
    • Resolved an issue where duplicate icon symbols could be generated during icon sprite creation, ensuring cleaner and more efficient sprite output.

Walkthrough

The scripts/generate-file-tree-sprite.ts file is refactored to prevent duplicate icon symbols by iterating over a deduplicated Set, and async control flow is restructured from a main() wrapper to top-level await with module-level execution semantics.

Changes

Cohort / File(s) Summary
Sprite Generation Script
scripts/generate-file-tree-sprite.ts
Modified buildSprite to iterate over new Set(iconNames) to eliminate duplicate symbols. Removed main() async function wrapper and replaced explicit error handling with top-level await, moving all logic to module-level execution rather than promise rejection handling.
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the primary change: preventing duplicate icons in the file tree sprite generation, which is clearly the main objective of the changeset.
Description check ✅ Passed The description is directly related to the changeset, explaining the issue (duplicate icons in the sprite), the motivation (performance improvement reducing size from 576K to 260K), and acknowledging uncertainty about intentionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch g/file-tree-sprite-optimisation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 19, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@ghostdevv ghostdevv marked this pull request as ready for review April 19, 2026 22:05
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
scripts/generate-file-tree-sprite.ts (2)

57-57: Consider deduplicating iconNames once at source.

Deduplicating inside buildSprite works, but groupByCollection still iterates and pushes duplicates into each group, wasting work and memory. Dedup once at the call site keeps buildSprite/groupByCollection simpler and equally correct.

♻️ Proposed refactor
-    new Set(iconNames).forEach(name => {
+    iconNames.forEach(name => {
-const iconNames = [
+const iconNames = [...new Set([
   ...Object.values(EXTENSION_ICONS),
   ...Object.values(FILENAME_ICONS),
   ...Object.values(COMPOUND_EXTENSIONS),
   ...Object.values(ADDITIONAL_ICONS),
   DEFAULT_ICON,
-]
+])]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate-file-tree-sprite.ts` at line 57, Deduplicate iconNames
before it's passed into buildSprite/groupByCollection by converting it to a
unique list (e.g., create a Set from iconNames and use that array for subsequent
calls) so groupByCollection no longer receives duplicates; then remove the
in-buildSprite deduplication (the new Set(iconNames).forEach(...) usage) or any
redundant filtering inside groupByCollection to keep them simpler and avoid
double work—update all call sites that pass iconNames to use the deduplicated
array and adjust buildSprite and groupByCollection to assume input is already
unique.

72-91: Loss of explicit error handling on top-level await.

Dropping the main().catch(err => { console.error(err); process.exit(1) }) wrapper means error propagation now relies on Node's default unhandled-rejection behaviour to produce a non-zero exit code. Since this script is invoked via execSync from vite.config.ts, a missing/zero exit code on failure would silently pass a broken build. Node ≥15 defaults to --unhandled-rejections=throw (exit 1), so current behaviour is likely preserved, but:

  • Stack traces from top-level await rejections are less readable than the explicit console.error(error) output.
  • Other scripts in this repo (scripts/generate-fixtures.ts, scripts/release-notes.ts) still use the explicit main().catch pattern — this diverges from that convention.

Consider restoring an explicit handler, or wrapping in a try/catch, for consistency and clearer failure output:

🛡️ Suggested pattern
-const collections = await loadCollections()
-const iconNames = [
+try {
+  const collections = await loadCollections()
+  const iconNames = [
   ...
-await Promise.all([
-  fs.writeFile(outputDevPath, sprite, 'utf8'),
-  fs.writeFile(outputStagePath, sprite, 'utf8'),
-  fs.writeFile(outputProdPath, sprite, 'utf8'),
-])
+  await Promise.all([
+    fs.writeFile(outputDevPath, sprite, 'utf8'),
+    fs.writeFile(outputStagePath, sprite, 'utf8'),
+    fs.writeFile(outputProdPath, sprite, 'utf8'),
+  ])
+} catch (error) {
+  console.error(error)
+  process.exit(1)
+}

As per coding guidelines: "Use error handling patterns consistently".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/generate-file-tree-sprite.ts` around lines 72 - 91, Wrap the
top-level async flow in an explicit error handler for consistent, readable
failures: invoke main() (the async function that performs loadCollections,
groupByCollection and buildSprite and writes
outputDevPath/outputStagePath/outputProdPath) with .catch(err => {
console.error(err); process.exit(1); }) or, if you prefer a single-file pattern,
surround the body in try { ... } catch (err) { console.error(err);
process.exit(1); } so any rejection from loadCollections, buildSprite, fs.mkdir,
or fs.writeFile yields a clear log and non-zero exit code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@scripts/generate-file-tree-sprite.ts`:
- Line 57: Deduplicate iconNames before it's passed into
buildSprite/groupByCollection by converting it to a unique list (e.g., create a
Set from iconNames and use that array for subsequent calls) so groupByCollection
no longer receives duplicates; then remove the in-buildSprite deduplication (the
new Set(iconNames).forEach(...) usage) or any redundant filtering inside
groupByCollection to keep them simpler and avoid double work—update all call
sites that pass iconNames to use the deduplicated array and adjust buildSprite
and groupByCollection to assume input is already unique.
- Around line 72-91: Wrap the top-level async flow in an explicit error handler
for consistent, readable failures: invoke main() (the async function that
performs loadCollections, groupByCollection and buildSprite and writes
outputDevPath/outputStagePath/outputProdPath) with .catch(err => {
console.error(err); process.exit(1); }) or, if you prefer a single-file pattern,
surround the body in try { ... } catch (err) { console.error(err);
process.exit(1); } so any rejection from loadCollections, buildSprite, fs.mkdir,
or fs.writeFile yields a clear log and non-zero exit code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 14b74b12-5ba6-4193-8439-1f70d600775e

📥 Commits

Reviewing files that changed from the base of the PR and between aaaec6a and 62f5688.

📒 Files selected for processing (1)
  • scripts/generate-file-tree-sprite.ts

Copy link
Copy Markdown
Member

@serhalp serhalp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, good find

It would be nice to add some perf benchmarks to CI eventually!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants