-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
refactor(router-core): defaultParseSearch,defaultStringifySearch better performance w/ early json exit #5071
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
refactor(router-core): defaultParseSearch,defaultStringifySearch better performance w/ early json exit #5071
Conversation
…er performance w/ early json exit
WalkthroughUpdates search param parsing/stringifying to avoid unconditional JSON.parse. Introduces an internal looksLikeJson helper. parseSearchWith and stringifySearchWith now apply JSON.parse only to values that appear JSON-like; otherwise, values are passed through or to provided parsers. Public API remains unchanged. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Caller
participant SP as searchParams.ts
participant LLJ as looksLikeJson
participant JP as JSON.parse
Note over SP: parseSearchWith flow (per value)
Caller->>SP: parseSearchWith(value, parser)
SP->>SP: isJsonParser = (parser === JSON.parse)
alt value is string
SP->>LLJ: looksLikeJson(value)
alt isJsonParser AND looksLikeJson
SP->>JP: JSON.parse(value)
JP-->>SP: parsed
SP-->>Caller: parsed
else isJsonParser AND NOT looksLikeJson
SP-->>Caller: value (string as-is)
else not JSON parser
SP->>SP: parser(value)
SP-->>Caller: result
end
else non-string
SP-->>Caller: value (unchanged)
end
sequenceDiagram
autonumber
actor Caller
participant SP as searchParams.ts
participant LLJ as looksLikeJson
participant JP as JSON.parse
Note over SP: stringifySearchWith flow (per value)
Caller->>SP: stringifySearchWith(value, parser)
alt parser exists
alt value is string
SP->>LLJ: looksLikeJson(value)
alt looksLikeJson
SP->>JP: JSON.parse(value)
JP-->>SP: obj
SP->>SP: parser(obj)
SP-->>Caller: string
else not JSON-like
SP-->>Caller: value (string as-is)
end
else non-string
SP->>SP: parser(value)
SP-->>Caller: string
end
else no parser
SP-->>Caller: default handling (unchanged)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
View your CI Pipeline Execution ↗ for commit 5bbf768
☁️ Nx Cloud last updated this comment at |
More templates
@tanstack/arktype-adapter
@tanstack/directive-functions-plugin
@tanstack/eslint-plugin-router
@tanstack/history
@tanstack/react-router
@tanstack/react-router-devtools
@tanstack/react-router-ssr-query
@tanstack/react-start
@tanstack/react-start-client
@tanstack/react-start-plugin
@tanstack/react-start-server
@tanstack/router-cli
@tanstack/router-core
@tanstack/router-devtools
@tanstack/router-devtools-core
@tanstack/router-generator
@tanstack/router-plugin
@tanstack/router-ssr-query-core
@tanstack/router-utils
@tanstack/router-vite-plugin
@tanstack/server-functions-plugin
@tanstack/solid-router
@tanstack/solid-router-devtools
@tanstack/solid-start
@tanstack/solid-start-client
@tanstack/solid-start-plugin
@tanstack/solid-start-server
@tanstack/start-client-core
@tanstack/start-plugin-core
@tanstack/start-server-core
@tanstack/start-server-functions-client
@tanstack/start-server-functions-fetcher
@tanstack/start-server-functions-server
@tanstack/start-storage-context
@tanstack/valibot-adapter
@tanstack/virtual-file-routes
@tanstack/zod-adapter
commit: |
There was a problem hiding this 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
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/router-core/src/searchParams.ts (1)
51-63
: Bug: stringify(val) double-quotes JSON-like strings and breaks round-trip typing.When parser succeeds, you’re stringifying the original string instead of the parsed value. Example: "123" becomes ""123"" in the URL, and parses back to the string "123" instead of number 123. Same for "true"/"false". Use stringify(parser(val)).
Apply this diff:
} else if ( hasParser && typeof val === 'string' && (!isJsonParser || looksLikeJson(val)) ) { try { - // Check if it's a valid parseable string. - // If it is, then stringify it again. - parser(val) - return stringify(val) + // If parseable, canonicalize by stringifying the parsed value. + const parsed = parser!(val) + return stringify(parsed) } catch (_err) { // silent } }
🧹 Nitpick comments (3)
packages/router-core/src/searchParams.ts (3)
22-25
: Behavior change: strings with leading whitespace won’t be parsed.looksLikeJson checks only the first char. Values like " 123", "\n[1]" are valid JSON for JSON.parse but will now be skipped and remain strings. Confirm this is acceptable, or trim leading whitespace in looksLikeJson (see suggestion below).
77-81
: Doc fix: claim about “no false negatives” is inaccurate.Leading whitespace produces false negatives with the current heuristic.
Apply this diff:
/** - * Fast check to see if the string is a likely to be a JSON value. - * It could return false positives (returned true but wasn't actually a json), - * but not false negatives (returned false but was actually a json). + * Fast, conservative check to see if the string is likely a JSON value. + * May return false positives (true but not actually JSON) and rare false negatives + * (e.g. when the string starts with whitespace). */
82-95
: Optional: skip leading whitespace in looksLikeJson to avoid false negatives.Small loop; negligible cost, removes a class of surprises.
Apply this diff:
function looksLikeJson(str: string): boolean { - if (!str) return false - const c = str.charCodeAt(0) + if (!str) return false + // Skip leading whitespace: space(32), tab(9), lf(10), cr(13) + let i = 0 + while (i < str.length) { + const code = str.charCodeAt(i) + if (code !== 32 && code !== 9 && code !== 10 && code !== 13) break + i++ + } + if (i >= str.length) return false + const c = str.charCodeAt(i) return ( c === 34 || // " c === 123 || // { c === 91 || // [ c === 45 || // - (c >= 48 && c <= 57) || // 0-9 c === 116 || // t (true) c === 102 || // f (false) c === 110 // n (null) ) }
📜 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.
📒 Files selected for processing (1)
packages/router-core/src/searchParams.ts
(4 hunks)
⏰ 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 (2)
packages/router-core/src/searchParams.ts (2)
11-11
: LGTM: caching the JSON.parse identity.Keeps the hot-path branch simple without re-checking inside the loop.
43-43
: LGTM: parity with parse path.Mirrors the parse side’s identity check and keeps the conditionals cheap.
Improve performance of
defaultParseSearch
anddefaultStringifySearch
by skipping callingJSON.parse
if we know from the 1 char that it cannot be a valid JSON string.Summary by CodeRabbit