Skip to content

Conversation

@Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Aug 17, 2025

The pattern of copying an array just to .reverse() it and iterate it from the end ends up creating a transient object (a copy of the array) that needs to be garbage collected.

For example:

[...array].reverse().find(() => {/*...*/})

or

[...array].reverse().forEach(() => {/*...*/})

Instead we can use a regular for loop to iterate it from the end. This will be mostly beneficial in router-core > router > buildLocation that gets called pretty often.

Summary by CodeRabbit

  • Refactor

    • Improved head metadata processing in React and Solid routers to be more efficient and readable, reducing work for large route metadata sets.
    • Simplified route matching logic to consistently pick the last relevant match, improving clarity and maintainability.
  • Chores

    • Added a utility to locate the last list item meeting a condition, supporting cleaner routing internals.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 17, 2025

Walkthrough

Refactors meta tag processing in React and Solid HeadContent components to use reverse-indexed for-loops and local arrays; adds a new findLast utility and updates RouterCore.buildLocation to use it for last-match lookups. No exported/public API changes.

Changes

Cohort / File(s) Summary of Changes
HeadContent meta processing optimization
packages/react-router/src/HeadContent.tsx, packages/solid-router/src/HeadContent.tsx
Replaced nested forEach/spread logic with reverse-indexed for-loops, introduced local arrays, used continue to skip processed/duplicate meta items, preserved title handling and final output order.
RouterCore last-match refactor
packages/router-core/src/router.ts
Replaced manual reverse-plus-find patterns with findLast when computing matchedFrom and matchedCurrent inside buildLocation’s route-changing branch.
Utilities addition
packages/router-core/src/utils.ts
Added exported findLast<T>(array, predicate) performing a reverse-linear scan and returning the last element matching the predicate (or undefined).

Sequence Diagram(s)

sequenceDiagram
participant Router as RouterCore.buildLocation
participant Utility as findLast
participant Matches as allCurrentLocationMatches

Router->>Matches: gather matches array
Router->>Utility: findLast(allCurrentLocationMatches, comparer)
Utility-->>Router: last matching entry (or undefined)
Router->>Router: use matchedFrom / matchedCurrent in buildLocation logic
Loading
sequenceDiagram
participant Head as HeadContent.useTags
participant Routes as routeMeta()
participant Result as resultMeta

Head->>Routes: read routeMetasArray
Head->>Routes: iterate routes in reverse (for i = len-1 .. 0)
Head->>Routes: iterate each route's metas in reverse (for j = len-1 .. 0)
alt meta is title and no title set
Head->>Result: set title placeholder
else duplicate or falsy
Head->>Head: continue (skip)
else regular meta
Head->>Result: push meta tag
end
Head->>Result: push title (if found) and reverse resultMeta before return
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • schiller-manuel

Poem

I hop through tags in backward stride,
Reversing loops where secrets hide.
I hunt the last with careful hop—
No duplicate will make me stop.
Routes aligned, the head feels right. 🐰✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 49b770b and 86875e2.

📒 Files selected for processing (1)
  • packages/router-core/src/utils.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/router-core/src/utils.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-array-copy-reverse

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@nx-cloud
Copy link

nx-cloud bot commented Aug 17, 2025

View your CI Pipeline Execution ↗ for commit 86875e2

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 5m 6s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1m 34s View ↗

☁️ Nx Cloud last updated this comment at 2025-08-17 16:02:00 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Aug 17, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@4980

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@4980

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@4980

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@4980

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@4980

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@4980

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@4980

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@4980

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@4980

@tanstack/react-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-plugin@4980

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@4980

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@4980

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@4980

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@4980

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@4980

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@4980

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@4980

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@4980

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@4980

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@4980

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@4980

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@4980

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@4980

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@4980

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@4980

@tanstack/solid-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-plugin@4980

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@4980

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@4980

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@4980

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@4980

@tanstack/start-server-functions-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-client@4980

@tanstack/start-server-functions-fetcher

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-fetcher@4980

@tanstack/start-server-functions-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-server@4980

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@4980

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@4980

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@4980

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@4980

commit: 86875e2

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.

Actionable comments posted: 0

🧹 Nitpick comments (3)
packages/router-core/src/utils.ts (1)

486-497: Align findLast with ES2023 semantics and support ReadonlyArray (drop non-null assertions).

Good addition and matches the PR intent. To make this utility more generally usable and type-safe:

  • Accept ReadonlyArray<T> since we don't mutate the input.
  • Pass (item, index, array) to the predicate to mirror Array.prototype.findLast.
  • Avoid ! assertions to be robust with sparse arrays and unions that include undefined.

Suggested diff:

-export function findLast<T>(
-  array: Array<T>,
-  predicate: (item: T) => boolean,
-): T | undefined {
-  for (let i = array.length - 1; i >= 0; i--) {
-    if (predicate(array[i]!)) {
-      return array[i]!
-    }
-  }
-  return undefined
-}
+export function findLast<T>(
+  array: ReadonlyArray<T>,
+  predicate: (item: T, index: number, array: ReadonlyArray<T>) => boolean,
+): T | undefined {
+  for (let i = array.length - 1; i >= 0; i--) {
+    const item = array[i]
+    if (predicate(item, i, array)) {
+      return item
+    }
+  }
+  return undefined
+}
packages/react-router/src/HeadContent.tsx (1)

37-37: Using continue for duplicate meta attributes is correct and clearer.

Skips duplicates without extra closures and matches previous guard behavior.

packages/solid-router/src/HeadContent.tsx (1)

39-39: Use of continue to skip duplicates is correct and avoids nested returns.

Matches React implementation for consistency.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 72b65cf and 49b770b.

📒 Files selected for processing (4)
  • packages/react-router/src/HeadContent.tsx (3 hunks)
  • packages/router-core/src/router.ts (2 hunks)
  • packages/router-core/src/utils.ts (1 hunks)
  • packages/solid-router/src/HeadContent.tsx (3 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
packages/router-core/src/router.ts (1)
packages/router-core/src/utils.ts (1)
  • findLast (487-497)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test
🔇 Additional comments (4)
packages/router-core/src/router.ts (2)

11-11: Importing findLast is appropriate for de-duping reverse scans.

No concerns with adding this import alongside existing utils.


1445-1451: Eliminate transient array copies for last-match lookups (LGTM)

File: packages/router-core/src/router.ts
Lines: 1445–1451

const matchedFrom = findLast(allCurrentLocationMatches, (d) => {
  return comparePaths(d.fullPath, fromPath)
})

const matchedCurrent = findLast(allFromMatches, (d) => {
  return comparePaths(d.fullPath, currentLocation.pathname)
})

Replacing the […arr].reverse().find(…) pattern with findLast removes unnecessary allocations in this hot path while preserving behavior. The dev-only validation remains clear and semantically equivalent.

Verified: no remaining [...arr].reverse().<method>() patterns found in the repo.

packages/react-router/src/HeadContent.tsx (1)

20-25: Reverse-indexed loops avoid extra array allocations and preserve output order (LGTM).

Iterating from the end and reversing resultMeta at the end retains the prior behavior while removing spread+reverse overhead. Good call keeping title handling intact.

Also applies to: 50-52

packages/solid-router/src/HeadContent.tsx (1)

21-27: Solid: reverse loops + memoized routeMetasArray reduce allocations (LGTM).

  • Reverse iteration removes spread+reverse temporary arrays.
  • Caching routeMetasArray = routeMeta() avoids repeated accessor calls.
  • Final resultMeta.reverse() maintains prior output order.

Also applies to: 52-53

@Sheraff Sheraff merged commit 9437fee into main Aug 17, 2025
6 checks passed
@Sheraff Sheraff deleted the refactor-array-copy-reverse branch August 17, 2025 16:31
Comment on lines +487 to +496
export function findLast<T>(
array: ReadonlyArray<T>,
predicate: (item: T) => boolean,
): T | undefined {
for (let i = array.length - 1; i >= 0; i--) {
const item = array[i]!
if (predicate(item)) return item
}
return undefined
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

@Sheraff Sheraff Aug 25, 2025

Choose a reason for hiding this comment

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

.findLast is still a little bit recent (safari 15.4), and I didn't want this PR to be a breaking change. But I'd love to use the built-in array method instead.

tannerlinsley pushed a commit that referenced this pull request Aug 26, 2025
… just to .reverse() them (#4980)

The pattern of copying an array just to `.reverse()` it and iterate it
from the end ends up creating a transient object (a copy of the array)
that needs to be garbage collected.

For example:
```ts
[...array].reverse().find(() => {/*...*/})
```
or
```ts
[...array].reverse().forEach(() => {/*...*/})
```

Instead we can use a regular `for` loop to iterate it from the end. This
will be mostly beneficial in `router-core > router > buildLocation` that
gets called pretty often.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Refactor**
- Improved head metadata processing in React and Solid routers to be
more efficient and readable, reducing work for large route metadata
sets.
- Simplified route matching logic to consistently pick the last relevant
match, improving clarity and maintainability.

- **Chores**
- Added a utility to locate the last list item meeting a condition,
supporting cleaner routing internals.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants