feat(form-core): 5-10x faster makePathArray#2152
feat(form-core): 5-10x faster makePathArray#2152GiacoCorsiglia wants to merge 8 commits intoTanStack:mainfrom
makePathArray#2152Conversation
📝 WalkthroughWalkthroughThis PR rewrites ChangesPath Parsing Optimization and Benchmarking
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
I would expect to delete this file before merging.
| * (No `--` separator: with pnpm 10's `--filter`, args after `--` get dropped | ||
| * before vitest sees them, so the filename filter is silently ignored and the | ||
| * full suite runs instead.) |
There was a problem hiding this comment.
You can tell Claude wrote this goofy comment 😅
But you can verify that the benchmark results match the real-world behavior visible here.
There was a problem hiding this comment.
This can be dropped if we drop the benchmarks
There was a problem hiding this comment.
This can be dropped if we drop the benchmarks
There was a problem hiding this comment.
I imagine this will be removed before this PR is merged (should that happen)
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
packages/form-core/src/utils.ts (1)
227-230: 💤 Low valueIncomplete comment — "for these because." is a sentence fragment.
✏️ Suggested fix
- // for these because. + // for these, because the old regex pipeline always produced at least one empty string + // from the split even when the input was only separator/meta characters.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/form-core/src/utils.ts` around lines 227 - 230, The comment above the conditional that checks "if (!result.length) result.push('')" is a fragment; update it to a complete sentence that explains why the old implementation returned [''] (i.e., when the input contained only phantom characters like ']', '[]', '[]]' producing no segments, the old behavior produced a single empty segment to represent an empty path). Edit the comment near the result variable and the conditional so it reads as a full explanatory sentence referencing the phantom-char inputs and the intended empty-segment fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/form-core/src/utils.ts`:
- Around line 188-198: The current logic in the segment parsing branch
(variables: treatAsNumber, allDigits, segLen, str.charCodeAt(segStart) ===
CC_ZERO, parseInt call and result.push) unconditionally pushes parseInt(...)
which loses precision for integers > Number.MAX_SAFE_INTEGER; add a round-trip
guard: when treatAsNumber is true, parse the segment into a number (e.g., parsed
= parseInt(str.slice(segStart, i), 10)) but only push parsed if String(parsed)
=== the original segment string; otherwise push the original slice
(str.slice(segStart, i)) so large integer strings are preserved as strings.
In `@packages/form-core/tests/utils.bench.ts`:
- Around line 4-6: The file contains a temporary snapshot and an inline legacy
function makePathArrayOld plus a "remove this" comment and paired benches that
must not land on main; remove the makePathArrayOld function and the accompanying
comment and any benchmark comparisons that reference it (e.g., paired benches)
and restructure the test to benchmark only the current live implementation (or
delete the whole bench file if benchmarking was never intended to be shipped);
ensure any helper regex like reLineOfOnlyDigits and tests only used by the old
implementation are also removed or repurposed so the file contains solely the
intended, production benchmark code.
In `@packages/react-form/tests/field-render-perf.test.tsx`:
- Line 29: This test file is marked for removal ("NOTE: This file is intended to
be removed before merge.") and should not land in main; remove the temporary
perf test from the PR (or move it out of the commit into a dedicated draft
branch) and instead open a follow-up issue or add a CI-safe permanent test in
the proper tests directory so the throwaway file does not get merged.
- Around line 178-184: The test uses import.meta.dirname when building mountPath
and unmountPath which fails on Node <21.2.0; add a fallback that computes a
dirname from fileURLToPath(import.meta.url) and use that variable instead of
import.meta.dirname. Specifically, near the top of the test module define a
const (e.g., testDir or dirname) that sets dirname = import.meta.dirname ??
path.dirname(fileURLToPath(import.meta.url)) and then update the join calls that
create mountPath and unmountPath (and any other uses of import.meta.dirname) to
use that dirname; reference functions/symbols: mountPath, unmountPath,
PROFILE_N, join, import.meta.dirname, import.meta.url, fileURLToPath.
---
Nitpick comments:
In `@packages/form-core/src/utils.ts`:
- Around line 227-230: The comment above the conditional that checks "if
(!result.length) result.push('')" is a fragment; update it to a complete
sentence that explains why the old implementation returned [''] (i.e., when the
input contained only phantom characters like ']', '[]', '[]]' producing no
segments, the old behavior produced a single empty segment to represent an empty
path). Edit the comment near the result variable and the conditional so it reads
as a full explanatory sentence referencing the phantom-char inputs and the
intended empty-segment fallback.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f2789ad5-a68f-4f50-a809-80d8c5d11698
📒 Files selected for processing (7)
.gitignorepackages/form-core/package.jsonpackages/form-core/src/utils.tspackages/form-core/tests/utils.bench.tspackages/form-core/tests/utils.spec.tspackages/form-core/vite.config.tspackages/react-form/tests/field-render-perf.test.tsx
| // Snapshot of the original implementation for side-by-side comparison. | ||
| // Remove this and the paired benches once the new implementation is merged. | ||
| const reLineOfOnlyDigits = /^(\d+)$/gm |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
makePathArrayOld and the "remove this" comment indicate this file (or at least lines 4–68) should not land on main as-is.
The inline makePathArrayOld exists purely for pre/post comparison and is explicitly marked for removal. If the benchmark file is kept post-merge, makePathArrayOld should be removed and the benchmarks restructured to measure only the live implementation (or dropped entirely if the intent was never to ship them).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/form-core/tests/utils.bench.ts` around lines 4 - 6, The file
contains a temporary snapshot and an inline legacy function makePathArrayOld
plus a "remove this" comment and paired benches that must not land on main;
remove the makePathArrayOld function and the accompanying comment and any
benchmark comparisons that reference it (e.g., paired benches) and restructure
the test to benchmark only the current live implementation (or delete the whole
bench file if benchmarking was never intended to be shipped); ensure any helper
regex like reLineOfOnlyDigits and tests only used by the old implementation are
also removed or repurposed so the file contains solely the intended, production
benchmark code.
There was a problem hiding this comment.
@GiacoCorsiglia, thanks for confirming! Would you like me to help clean up the benchmark file by removing makePathArrayOld, the "remove this" comment, and the paired comparison benches — leaving only the benchmarks for the current makePathArray implementation? I can plan those changes if that's helpful.
(ノ◕ヮ◕)ノ*:・゚✧
| * 2000 2709.0ms 8351.3ms 11060.6ms 10882.0..11462.1ms | ||
| * ============================================================== | ||
| * | ||
| * NOTE: This file is intended to be removed before merge. |
There was a problem hiding this comment.
File is self-marked for removal before merge.
Line 29 reads: "NOTE: This file is intended to be removed before merge." The PR description also calls these files "intended temporary." If this is not meant to land in main, it should be dropped before the PR is merged.
Happy to draft a follow-up issue to track a permanent, CI-safe perf regression test if that would help preserve the intent without the throwaway file.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/react-form/tests/field-render-perf.test.tsx` at line 29, This test
file is marked for removal ("NOTE: This file is intended to be removed before
merge.") and should not land in main; remove the temporary perf test from the PR
(or move it out of the commit into a dedicated draft branch) and instead open a
follow-up issue or add a CI-safe permanent test in the proper tests directory so
the throwaway file does not get merged.
There was a problem hiding this comment.
I leave it to the maintainers whether they want such a thing!
There was a problem hiding this comment.
@GiacoCorsiglia Fair enough! The original concern — that the file is self-marked for removal — still stands for maintainers to decide before merging. If they'd like, I'm happy to draft a follow-up GitHub issue to track adding a permanent, CI-safe perf regression test. Just let me know!
🎯 Changes
NB: I used Claude Code when doing this work (but not to write this description).
In #2150 I noted that mounting
<form.Field>is slow because it exhibits O(N^2) complexity, where N is the number of fields in the form. This PR does not fix the O(N^2) behavior, but when profiling it, I noticed thatmakePathArrayis a super hot path.On
main,makePathArrayincludes a bunch of regex string munging. I rewrote it as a single-pass for loop. This produces 5–10x speed up in microbenchmarks, and what appears to be ~2x faster<form.Field>mounting/unmounting.I also wrote additional tests for
makePathArrayto try to capture its edge case behavior (what I think are malformed inputs—things not allowed byDeepKeys<T>). I did my best to maintain backwards compatibility, but you will see there is one BC-break I identified:I may have missed some edge cases not covered by my new tests. It's possible to match the old behavior more closely. Claude's original implementation was much more complex and handled more edge cases, but I opted to simplify so that the code was easier to understand. Happy to adjust.
Benchmarks
This branch includes benchmark files I would not expect to be merged, but wanted to include temporarily so others can validate my results (run on my M4 Pro MackBook Pro).
Here's the output for the new
utils.bench.ts:In addition, I (well, Claude) wrote
field-render-perf.test.tsx, which is an automated version of the reproduction I put together for #2150.Results with the old
makePathArray():Results with the new
makePathArray():~2x faster!
✅ Checklist
pnpm test:pr.🚀 Release Impact
This change is docs/CI/dev-only (no release).Summary by CodeRabbit
Refactor
Tests
Chores