Skip to content

Conversation

@Sheraff
Copy link
Contributor

@Sheraff Sheraff commented Nov 14, 2025

Minor performance improvements of decodePath in router-core/utils:

  • splitAndDecode is pure and should be declared outside of decodePath so it has a better chance of getting JIT optimized
  • DECODE_IGNORE_LIST is a static array, and should be initialized as an array (instead of Array.from(new Map([...]).values()))
  • length of partsToJoin is known in advance, so should be initialized w/ a length from the start to avoid memory layout changes when we .push() on every iteration

Summary by CodeRabbit

  • Refactor
    • Optimized internal path decoding logic in the router utility to improve code structure and maintainability. Public API remains unchanged and fully compatible.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 14, 2025

Walkthrough

Refactored the path decoding logic in the router utilities by simplifying DECODE_IGNORE_LIST from a Map to an array, extracting a new internal helper function splitAndDecode for recursive decoding, and normalizing percent-encoded sequences through uppercasing during processing.

Changes

Cohort / File(s) Change Summary
Router utilities refactoring
packages/router-core/src/utils.ts
Changed DECODE_IGNORE_LIST from Map-based to simple array; introduced splitAndDecode helper for recursive decoding of path segments; updated decodePath to normalize percent-encoded sequences and delegate decoding to the new helper while maintaining existing public API

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Focus areas:
    • Logic correctness of the new splitAndDecode recursive implementation vs. the previous inlined approach
    • Verification that uppercase normalization of percent-encoded sequences doesn't break existing behavior
    • Ensure the fallback to decodeURI for remaining parts handles edge cases correctly
    • Confirm public API compatibility and no behavioral changes in decodePath

Suggested reviewers

  • schiller-manuel
  • Sheraff

Poem

🐰✨ A hop, skip, and a decode refactor—
Where paths split cleanly, no longer a factor.
From maps to arrays, we've simplified the way,
Helper functions dance through the decoding ballet. 🎩

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: a refactor of the decodePath utility function in router-core with performance improvements.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-router-core-decode-path-utils-perf

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

@nx-cloud
Copy link

nx-cloud bot commented Nov 14, 2025

View your CI Pipeline Execution ↗ for commit 1590a0a

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

☁️ Nx Cloud last updated this comment at 2025-11-14 11:45:38 UTC

@Sheraff Sheraff requested a review from nlynzaad November 14, 2025 11:34
@pkg-pr-new
Copy link

pkg-pr-new bot commented Nov 14, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/directive-functions-plugin

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/zod-adapter

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

commit: 1590a0a

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 (2)
packages/router-core/src/utils.ts (2)

550-550: Remove unnecessary global flag in regex.

The g (global) flag is unnecessary when using .test(), which only checks for existence of a match. The global flag is for methods that match multiple times like .exec() or when used with .replace().

Apply this diff:

-  if (part === '' || !/%[0-9A-Fa-f]{2}/g.test(part)) return part
+  if (part === '' || !/%[0-9A-Fa-f]{2}/.test(part)) return part

553-555: Consider checking if normalization is needed first.

The replaceAll call creates a new string even when no replacements are made. For inputs that are already uppercase, this is unnecessary overhead.

Consider this optimization:

-  const normalizedPart = part.replaceAll(/%[0-9a-f]{2}/g, (match) =>
-    match.toUpperCase(),
-  )
+  const needsNormalization = /%[0-9a-f]{2}/.test(part)
+  const normalizedPart = needsNormalization
+    ? part.replaceAll(/%[0-9a-f]{2}/g, (match) => match.toUpperCase())
+    : part

However, this adds an extra regex test, so benchmark to confirm it's actually faster for typical inputs.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c05679e and 1590a0a.

📒 Files selected for processing (1)
  • packages/router-core/src/utils.ts (1 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/tests/non-nested-paths.spec.ts:167-172
Timestamp: 2025-09-22T00:56:53.426Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments during path parsing, but preserved in base path segments. This is the expected behavior implemented in PR #5182.
Learnt from: nlynzaad
Repo: TanStack/router PR: 5182
File: e2e/react-router/basic-file-based/src/routes/non-nested/named/$baz_.bar.tsx:3-5
Timestamp: 2025-09-22T00:56:49.237Z
Learning: In TanStack Router, underscores are intentionally stripped from route segments (e.g., `$baz_` becomes `baz` in generated types) but should be preserved in base path segments. This is the correct behavior as of the fix in PR #5182.
📚 Learning: 2025-10-01T18:30:26.591Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: packages/router-core/src/router.ts:2231-2245
Timestamp: 2025-10-01T18:30:26.591Z
Learning: In `packages/router-core/src/router.ts`, the `resolveRedirect` method intentionally strips the router's origin from redirect URLs when they match (e.g., `https://foo.com/bar` → `/bar` for same-origin redirects) while preserving the full URL for cross-origin redirects. This logic should not be removed or simplified to use `location.publicHref` directly.

Applied to files:

  • packages/router-core/src/utils.ts
🧬 Code graph analysis (1)
packages/router-core/src/utils.ts (1)
packages/router-core/src/index.ts (1)
  • decodePath (281-281)
⏰ 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 (3)
packages/router-core/src/utils.ts (3)

492-495: Simplification looks good.

The change from a Map to a simple array is a sensible optimization for a small, fixed list of two items. Array iteration is straightforward and efficient for this use case.


497-543: Well-structured recursive algorithm.

The splitAndDecode helper implements a clean recursive approach that:

  • Efficiently traverses the ignore list using index-based iteration (avoiding redundant checks)
  • Preserves ignore characters while decoding everything else
  • Handles edge cases gracefully (empty strings, consecutive ignore chars, boundary positions)
  • Includes robust fallback for malformed encodings

The .toUpperCase() call on line 507 ensures compatibility with custom ignore lists that may not be pre-normalized, which is good defensive programming.


535-541: Update build target to match runtime requirements or add transpilation support for replaceAll.

The replaceAll() method was added in ES2021, but the project's root tsconfig.json specifies "target": "ES2020". This creates a type/runtime mismatch: while TypeScript compiles successfully due to "lib": ["ES2022"] in the compiler options, the actual runtime code will fail in ES2020 environments where replaceAll is not available.

replaceAll is used in multiple files across the codebase:

  • packages/router-core/src/utils.ts (lines 535, 553)
  • packages/router-generator/src/ (multiple files)
  • packages/router-core/src/path.ts (line 500)

Either update the target in tsconfig.json to "ES2021" or higher to accurately reflect runtime requirements, or implement transpilation/polyfill support to backport replaceAll for ES2020 environments if that compatibility is required.

Copy link
Contributor

@nlynzaad nlynzaad left a comment

Choose a reason for hiding this comment

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

🔥

@Sheraff Sheraff merged commit 965e653 into main Nov 14, 2025
6 checks passed
@Sheraff Sheraff deleted the refactor-router-core-decode-path-utils-perf branch November 14, 2025 17:40
roduyemi pushed a commit to roduyemi/oss-router that referenced this pull request Nov 19, 2025
TanStack#5865)

* refactor(router-core): utils/decodePath minor performance improvements

* ci: apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
@coderabbitai coderabbitai bot mentioned this pull request Jan 5, 2026
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