Skip to content

perf(core): Optimize sortPaths with decorate-sort-undecorate pattern#1235

Merged
yamadashy merged 3 commits intomainfrom
claude/upbeat-haslett
Mar 19, 2026
Merged

perf(core): Optimize sortPaths with decorate-sort-undecorate pattern#1235
yamadashy merged 3 commits intomainfrom
claude/upbeat-haslett

Conversation

@yamadashy
Copy link
Owner

@yamadashy yamadashy commented Mar 17, 2026

Summary

Optimize three performance-critical functions in the file processing pipeline:

  1. sortPaths (filePathSort.ts): Pre-compute path.split() results before sorting, avoiding O(N log N) repeated string split allocations in sort comparisons
  2. generateFileTree (fileTreeGenerate.ts): Use Map for O(1) child node lookup instead of Array.find() O(N) scan; use array.push() + join() instead of string concatenation in tree stringification; eliminate redundant recursive sorting
  3. filterOutUntrustedFiles (filterOutUntrustedFiles.ts): Use Set.has() for O(1) lookup instead of Array.some() O(M) linear scan

Benchmark Results (median, 50 iterations)

sortPaths6-7x faster

Files Before After Speedup
1,000 5.27ms 0.83ms 6.4x
5,000 33.96ms 4.88ms 7.0x
10,000 65.51ms 10.83ms 6.0x

generateTreeString1.3x faster at scale

Files Before After Speedup
1,000 1.01ms 1.05ms ~1.0x
5,000 6.27ms 5.63ms 1.1x
10,000 16.42ms 12.53ms 1.3x

Tree construction only (Map vs Array.find):

Files Array.find Map Speedup
10,000 9.77ms 8.32ms 1.2x
20,000 33.74ms 18.23ms 1.9x

filterOutUntrustedFilesup to 30x faster

Files / Suspicious Before After Speedup
1,000 / 50 0.12ms 0.04ms 2.7x
5,000 / 200 2.78ms 0.20ms 13.9x
10,000 / 500 12.99ms 0.43ms 30.3x

Checklist

  • Run npm run test
  • Run npm run lint

🤖 Generated with Claude Code


Open with Devin

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request focuses on a series of targeted performance optimizations across key file processing functions within the core module. By strategically employing more efficient data structures and algorithms, the changes aim to significantly reduce execution times and resource consumption in path sorting, file tree generation, and security-related file filtering, particularly when dealing with larger datasets.

Highlights

  • Path Sorting Optimization: The sortPaths function now pre-computes path.split() results before sorting, significantly reducing repeated string split allocations during comparisons, leading to 6-7x faster performance.
  • File Tree Generation Optimization: The generateFileTree function was optimized by using a Map for O(1) child node lookups instead of Array.find(). Additionally, tree stringification now uses array.push() and join() for better performance than string concatenation, resulting in up to 1.9x faster tree construction and 1.3x faster string generation at scale.
  • File Filtering Optimization: The filterOutUntrustedFiles function was improved by using Set.has() for O(1) lookup of suspicious files, replacing Array.some()'s linear scan. This change provides up to 30x faster filtering.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • src/core/file/filePathSort.ts
    • Modified sortPaths to pre-compute split path parts into a separate array.
    • Implemented sorting using an index array based on pre-split parts, then reordered the original array in-place.
  • src/core/file/fileTreeGenerate.ts
    • Updated generateFileTree and addPathToTree to use a Map for O(1) child node lookups during tree construction.
    • Refactored treeToString and treeToStringWithLineCounts to use an array for accumulating string parts and then join() them, improving string concatenation performance.
  • src/core/security/filterOutUntrustedFiles.ts
    • Changed filterOutUntrustedFiles to create a Set of suspicious file paths.
    • Replaced Array.some() with Set.has() for O(1) lookup when filtering raw files.
Activity
  • The pull request was generated using Claude Code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: faa16c21-0223-4c80-9fe2-1757339f43bc

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Optimization and refactoring of file-handling utilities: sortPaths now precomputes split path components and uses an improved comparator with directory prioritization; fileTreeGenerate introduces an O(1) lookup map for child nodes and refactors string construction with helper functions; filterOutUntrustedFiles replaces linear search with Set-based membership check.

Changes

Cohort / File(s) Summary
File Path Sorting
src/core/file/filePathSort.ts
Enhanced sortPaths with pre-split path components, index-based comparator prioritizing directories over files, lexicographic ordering fallback, and path-length tiebreaker. In-place array reordering preserves mutation semantics.
File Tree Generation
src/core/file/fileTreeGenerate.ts
Introduced childLookup map for O(1) child node access during tree construction. Refactored string building from concatenation to accumulator-based approach with new buildTreeString and buildTreeStringWithLineCounts helpers. Public function signatures unchanged.
File Security Filtering
src/core/security/filterOutUntrustedFiles.ts
Optimized suspicious file filtering from linear search via some() to O(1) Set-based membership check. Internal implementation change only; public signature retained.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The description comprehensively covers changes to all three files, includes detailed benchmark results, and completes the provided template checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title accurately identifies the primary change—optimizing sortPaths with the decorate-sort-undecorate pattern—which is the most significant performance improvement in the changeset.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/upbeat-haslett
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Mar 17, 2026

Deploying repomix with  Cloudflare Pages  Cloudflare Pages

Latest commit: f38828a
Status: ✅  Deploy successful!
Preview URL: https://51445554.repomix.pages.dev
Branch Preview URL: https://claude-upbeat-haslett.repomix.pages.dev

View logs

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces significant performance optimizations across sortPaths, generateFileTree, and filterOutUntrustedFiles functions. The changes leverage efficient data structures and algorithms, such as pre-computing path splits for sorting, using Map for O(1) child lookups in tree generation, and Set for O(1) lookup of suspicious files. The refactoring to use array join() for string concatenation in tree stringification also contributes to better performance. The benchmark results provided clearly demonstrate the substantial speedups achieved by these optimizations. The code is clean, well-structured, and directly addresses the identified performance bottlenecks, making it a valuable improvement to the codebase.

@claude
Copy link
Contributor

claude bot commented Mar 17, 2026

Code Review

Overall: Clean, well-benchmarked performance PR. All three optimizations are algorithmically sound and the benchmark numbers are impressive. The changes are minimal in scope and maintain behavioral compatibility.

filePathSort.ts — Pre-split optimization

The decorate-sort-undecorate pattern (Schwartzian transform) is the right approach here. Avoiding O(N log N) redundant split() calls in comparators is a textbook optimization.

Minor: In-place reorder creates an intermediate array

Lines 31-34 allocate a sorted array to reorder filePaths in-place. This is fine for correctness, but note that the function returns the sorted array anyway, so callers likely use the return value. If no caller depends on the original array being mutated in-place, this could be simplified to just return indices.map((i) => filePaths[i]). That said, matching the original Array.sort() mutation semantics is a safe choice — no change needed.

fileTreeGenerate.ts — Map lookup + array join + sort dedup

Three wins in one:

  1. Map for O(1) child lookup — straightforward improvement over Array.find().
  2. push() + join() instead of string concatenation — avoids intermediate string allocations in the recursive stringifier.
  3. Elimination of redundant sorting — This is the most subtle and valuable change. The original treeToString called sortTreeNodes(node) (which recursively sorts the entire subtree), then for each child directory, recursively called treeToString(child) which called sortTreeNodes(child) again on an already-sorted subtree. The new design separates sorting from stringification, so sortTreeNodes runs exactly once. Same fix applies to treeToStringWithLineCounts. Well done.
Observation: optional chaining on nodeMap

Line 48: nodeMap?.set(part, child) — the ?. is defensive since childLookup.get(currentNode) should always return a valid Map (root is initialized at line 16, and every new node gets a Map at line 50). If it were ever undefined, the child would silently not be indexed, causing future lookups to miss it and create duplicates. This can't happen with the current code, but using ! (non-null assertion) or extracting the Map with a guard would make the invariant more explicit. Not a blocker.

filterOutUntrustedFiles.ts — Set lookup

Textbook O(N*M)O(N+M) optimization. Clean and correct.

Potential risks

  • None of the existing tests were modified, which is a good sign — the optimizations are purely internal and don't change observable behavior.
  • The childLookup Map uses TreeNode objects as keys (identity-based), which is correct here since nodes are never recreated.
  • Memory overhead from the Map/Set structures is negligible compared to the performance gains, especially at scale.

Verdict

Looks good to merge. No bugs or behavioral changes detected.


🤖 Generated with Claude Code

@codecov
Copy link

codecov bot commented Mar 17, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.18%. Comparing base (879d9c4) to head (f38828a).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1235   +/-   ##
=======================================
  Coverage   87.18%   87.18%           
=======================================
  Files         115      115           
  Lines        4322     4324    +2     
  Branches     1002     1002           
=======================================
+ Hits         3768     3770    +2     
  Misses        554      554           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 3 additional findings.

Open in Devin Review

Copy link
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 (1)
src/core/file/fileTreeGenerate.ts (1)

42-50: Optional chaining could mask invariant violations.

The nodeMap?. usage on lines 43 and 48 silently handles undefined, but if nodeMap is ever undefined, the child would still be added to currentNode.children (line 47) while the lookup map would not be updated—breaking the O(1) optimization without any visible error.

The invariant (every node has an entry in childLookup) should hold given the initialization at lines 16 and 50, but a non-null assertion or explicit check would surface bugs faster if the invariant is ever violated.

🔧 Optional: Add defensive assertion
     const nodeMap = childLookup.get(currentNode);
-    let child = nodeMap?.get(part);
+    if (!nodeMap) {
+      throw new Error(`Missing childLookup entry for node: ${currentNode.name}`);
+    }
+    let child = nodeMap.get(part);
 
     if (!child) {
       child = createTreeNode(part, !isLastPart || isDirectory);
       currentNode.children.push(child);
-      nodeMap?.set(part, child);
+      nodeMap.set(part, child);
 
       childLookup.set(child, new Map<string, TreeNode>());
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/file/fileTreeGenerate.ts` around lines 42 - 50, The optional
chaining on nodeMap (nodeMap?.get and nodeMap?.set) can hide invariant
violations where childLookup lacks an entry for currentNode; change this to an
explicit check/assertion: retrieve nodeMap using childLookup.get(currentNode)
and if it's undefined throw or assert with a clear message referencing
childLookup and currentNode, so you fail fast instead of silently skipping the
map update; then proceed to create the child with createTreeNode and call
nodeMap.set(part, child) and childLookup.set(child, new Map<string, TreeNode>())
as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/core/file/fileTreeGenerate.ts`:
- Around line 42-50: The optional chaining on nodeMap (nodeMap?.get and
nodeMap?.set) can hide invariant violations where childLookup lacks an entry for
currentNode; change this to an explicit check/assertion: retrieve nodeMap using
childLookup.get(currentNode) and if it's undefined throw or assert with a clear
message referencing childLookup and currentNode, so you fail fast instead of
silently skipping the map update; then proceed to create the child with
createTreeNode and call nodeMap.set(part, child) and childLookup.set(child, new
Map<string, TreeNode>()) as before.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 44012b1e-23a0-4969-bafd-5e6a3805e03e

📥 Commits

Reviewing files that changed from the base of the PR and between 008e28c and 5353698.

📒 Files selected for processing (3)
  • src/core/file/filePathSort.ts
  • src/core/file/fileTreeGenerate.ts
  • src/core/security/filterOutUntrustedFiles.ts

@yamadashy
Copy link
Owner Author

Code Review

already left a thorough review covering the key points. Adding a few incremental observations below.

AI Bot Inline Comments

No inline review comments from bots — nothing to evaluate.

Incremental observations

fileTreeGenerate.ts — placement

The refactoring moves to the public API boundary (, ), which runs it once per call instead of recursively at every level. One thing to be aware of: if a caller ever reuses the same TreeNode for both treeToString and treeToStringWithLineCounts, the tree would be sorted twice. This is harmless (sorting an already-sorted tree is O(N log N) but correct), and in the current codebase every caller creates a fresh tree — so it's not an issue now. Just worth noting as the API evolves.

filePathSort.ts — presplit memory allocation

For very large repos (e.g., 100k files), presplit holds N arrays of path parts in memory simultaneously alongside the original filePaths array. Given the sort itself is O(N log N) and presplit is O(N * depth), the memory overhead is proportional to the input size — a reasonable tradeoff for the 6–7x speedup. No change needed, just confirming the tradeoff is intentional.

filterOutUntrustedFiles.ts

Clean and correct. The Set construction from suspiciousFilesResults.map() is O(M) and has() is O(1). For the typical case where suspicious files are a small fraction of total files, this is strictly better.


Overall: The optimizations are algorithmically sound, benchmarks are compelling, and the implementation matches the intent. The nodeMap?.set() optional chaining noted by claude[bot] is the only minor nit. Good to merge.

🤖 Generated with Claude Code

@yamadashy yamadashy force-pushed the claude/upbeat-haslett branch 2 times, most recently from 4aabb71 to 315494a Compare March 18, 2026 15:39
@claude
Copy link
Contributor

claude bot commented Mar 18, 2026

Follow-up Review

Previous Claude review was thorough and covered all key points. One incremental update:

nodeMap?. optional chaining — resolved: The childLookupCache WeakMap with the getOrCreateChildMap helper now guarantees a valid Map is always returned, eliminating the optional chaining concern noted by both the previous Claude review and CodeRabbit. Clean fix.

No new issues found. Good to merge.


🤖 Generated with Claude Code

@claude
Copy link
Contributor

claude bot commented Mar 19, 2026

Follow-up Review (3rd pass)

Previous Claude reviews covered the algorithmic improvements thoroughly. The nodeMap?. optional chaining concern has been cleanly resolved with the getOrCreateChildMap helper + WeakMap pattern. One new observation below.

New observation: module-level childLookupCache WeakMap

Details

The childLookupCache at fileTreeGenerate.ts:10 is a module-level WeakMap<TreeNode, Map<string, TreeNode>>. This is fine because:

  • WeakMap allows GC once TreeNode references are released — no memory leak risk
  • The cache is only populated during addPathToTree and only consumed there — treeToString/treeToStringWithLineCounts don't interact with it
  • sortTreeNodes reorders children arrays but doesn't invalidate the Map entries (Map keys are strings, unaffected by array reorder)

One edge to be aware of: if the same TreeNode tree were passed to addPathToTree again after sorting (e.g., adding files to an already-sorted tree), the childMap would still work correctly since it maps by name, not by position. So this is safe.

Test change in fileSearch.test.ts

The relaxed assertion (arrayContaining + toHaveLength instead of exact toEqual) is correct — searchFiles now returns a sorted array via the new sortPaths, so exact order matching against unsorted mock input would be brittle.

Verdict

No new issues. Good to merge.


🤖 Generated with Claude Code

@claude
Copy link
Contributor

claude bot commented Mar 19, 2026

Code Review

Overall: Looks good. Clean, well-motivated optimization with solid benchmarks.

What's Good

  • Decorate-sort-undecorate is the right pattern here. Pre-computing path.split() avoids O(N log N × K) string allocations during sort comparisons — the 6-7x speedup is expected and meaningful.
  • Good editorial judgment in the later commits: reverting the tree generation and filter optimizations that saved only ~16ms on 10k files was the right call. Keeping the PR focused on the impactful change.
  • The test fix correctly adapts to the new non-mutating behavior of sortPaths.

Minor Observations

Details
  1. PR title is stale: The title still says "Optimize path sorting, tree generation, and file filtering" but the final diff only touches sortPaths. Consider updating to match the actual scope (e.g., perf(core): Optimize path sorting with decorate-sort-undecorate).

  2. Behavioral change: sortPaths no longer mutates its input. The old implementation sorted in-place via Array.prototype.sort() and returned the same array. The new one returns a new array. This is actually a better API contract (no side effects), but worth noting for any callers that relied on the mutation. The test fix at fileSearch.test.ts:545-546 correctly handles this — the expect.arrayContaining + toHaveLength combo is equivalent to an order-independent equality check, which is appropriate since searchFiles sorts its own output.

  3. Object allocation trade-off: The decorated pattern creates N temporary objects. For the typical case this is negligible (and clearly net-positive per benchmarks), but it's worth knowing the memory profile shifts from CPU-bound (repeated splits) to a brief memory spike (N objects + N split arrays, all short-lived and GC-friendly).

Premortem Analysis

Details
Risk Likelihood Mitigation
Caller relies on sortPaths mutating input array Low — test fix suggests only one callsite was affected, and it was a test The test was already fixed. A quick grep for sortPaths confirms no other callers depend on mutation
Performance regression on very small arrays (N < 10) due to extra .map() overhead Negligible — two extra O(N) passes on tiny arrays are unmeasurable No action needed
path.sep inconsistency across platforms Pre-existing — not introduced by this PR N/A

Verdict

Approve. The optimization is well-targeted, the implementation is clean, and the benchmark data is convincing. The only suggested follow-up is updating the PR title to reflect the final scope.

🤖 Generated with Claude Code

@yamadashy yamadashy force-pushed the claude/upbeat-haslett branch from e14a9b2 to 482e0ab Compare March 19, 2026 15:51
Pre-compute path.split() once per path before sorting, avoiding
O(N log N) repeated string allocations during comparisons.
Benchmark: 10,000 files 65ms → 11ms (6x speedup).

Co-Authored-By: Claude Opus 4.6 (1M context) <koukun0120@gmail.com>
@yamadashy yamadashy force-pushed the claude/upbeat-haslett branch from 482e0ab to d9fa509 Compare March 19, 2026 15:54
@claude
Copy link
Contributor

claude bot commented Mar 19, 2026

Code Review

Overall, the sortPaths optimization using decorate-sort-undecorate is a solid, well-known pattern and the benchmark numbers are convincing. A few observations:

PR Scope vs. Content Mismatch

The PR title and description mention three optimizations (sortPaths, generateFileTree, filterOutUntrustedFiles), but the diff only contains changes to sortPaths. The generateFileTree Map optimization and filterOutUntrustedFiles Set optimization are not present in this PR. Either the title/body should be narrowed to match the actual changes, or the missing optimizations should be added.

Test Change Weakens Assertion

Details

The test change in fileSearch.test.ts:545-546 replaces:

expect(result.filePaths).toEqual(mockFileStructure);

with:

expect(result.filePaths).toEqual(expect.arrayContaining(mockFileStructure));
expect(result.filePaths).toHaveLength(mockFileStructure.length);

This no longer verifies ordering — it only checks that all elements are present. Since sortPaths is specifically a sorting function, the test should verify correct sort order. The original assertion was stricter and more valuable. If the mock data wasn't in sorted order before, the fix should be to update mockFileStructure to the expected sorted order, not to weaken the assertion.

Behavioral Change: No Longer Mutates Input

Details

The original implementation used filePaths.sort(...) which mutated the input array in-place. The new implementation creates a new array via map(), sorts the decorated copy, then maps back — so the original array is not mutated. This is actually a better API design (pure function), but it's worth noting as a behavioral change. All current callers use the return value (e.g., sortPaths(filePaths)) so this should be safe, but it's a subtle contract change worth documenting.

Minor: Object Allocation Overhead

Details

The decorated array creates an object { original, parts } per path. For the benchmarked sizes (1K-10K), this is clearly a net win since avoiding repeated split() in O(N log N) comparisons dominates. Just noting that for very small arrays (< ~50 paths), the allocation overhead might exceed the savings — but this is unlikely to matter in practice.

Summary

The core optimization is clean and well-motivated. Main action items:

  1. Fix PR title/body to match actual scope (only sortPaths optimization)
  2. Restore strict ordering assertion in the test, or update mock data to expected sorted order

🤖 Generated with Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

Replace weak arrayContaining assertion with exact toEqual using the
correct sorted order, so the test verifies both content and sort behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <koukun0120@gmail.com>
@yamadashy yamadashy changed the title perf(core): Optimize path sorting, tree generation, and file filtering perf(core): Optimize sortPaths with decorate-sort-undecorate pattern Mar 19, 2026
@claude
Copy link
Contributor

claude bot commented Mar 19, 2026

Code Review

Overall: Clean, well-motivated optimization. The decorate-sort-undecorate pattern is the right approach here and the benchmark numbers are convincing. A couple of observations:

PR Description vs. Actual Changes

The PR body describes three optimizations (sortPaths, generateFileTree, filterOutUntrustedFiles), but the diff only contains the sortPaths change. Consider trimming the description to match the actual scope of this PR to avoid confusion.

Code Quality

The implementation in filePathSort.ts is clean and correct. The decorate-sort-undecorate pattern is well-applied.

Behavioral note: in-place mutation removed

The original code used filePaths.sort(...) which mutated the input array in-place. The new code creates a new array via .map(), sorts that, then maps back. This means callers' original arrays are no longer mutated. I verified all call sites use the return value, so this is safe — and arguably an improvement (pure function).

Test Change

The test assertion change from toEqual(mockFileStructure) to an explicit sorted order is a good improvement — it now actually validates sort correctness rather than accidentally passing because the mock data happened to match.

No Issues Found

  • No bugs or edge cases identified
  • No security concerns
  • No performance regressions — the extra .map() allocations are negligible compared to the savings from avoiding repeated .split() in comparisons
  • Existing test coverage in filePathSort.test.ts is comprehensive (empty arrays, single files, nested dirs, mixed content)

LGTM 👍

🤖 Generated with Claude Code

devin-ai-integration[bot]

This comment was marked as resolved.

…bility

Mock data and expected sort order now use path.sep instead of hardcoded
'/' separators. On Windows, path.sep is '\' so sortPaths splits
differently, producing a different sort order.

Co-Authored-By: Claude Opus 4.6 (1M context) <koukun0120@gmail.com>
@claude
Copy link
Contributor

claude bot commented Mar 19, 2026

Code Review (Follow-up)

A previous review already covered the algorithmic analysis well. Two additional observations:

PR description vs actual changes

The PR description and benchmarks reference three optimizations (sortPaths, generateFileTree, filterOutUntrustedFiles), but only the sortPaths change is included in the diff (2 files changed). Consider updating the description to match the actual scope, or adding the other optimizations in follow-up commits.

Public API mutation semantics

sortPaths is exported via src/index.ts. The original implementation mutated the input array in-place (via Array.sort()), while the new implementation returns a new array without mutating the input. All internal callers use the return value, so this is safe — but external library consumers could theoretically depend on the mutation side-effect. Worth noting for semver purposes, though unlikely to cause issues in practice.

Verdict

Looks good. The core optimization is clean and correct, and the test improvements (exact sort-order assertion + path.sep usage) are welcome.


🤖 Generated with Claude Code

@yamadashy yamadashy merged commit a8cff7d into main Mar 19, 2026
56 checks passed
@yamadashy yamadashy deleted the claude/upbeat-haslett branch March 19, 2026 16:20
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.

1 participant