Skip to content

perf(start-plugin-core): skip resolving member calls on non-namespace receivers - #8758

Merged
Sheraff merged 3 commits into
TanStack:mainfrom
shkumbinhasani:perf/start-compiler-member-call-receivers
Oct 18, 2026
Merged

Sheraff merged 3 commits into
TanStack:mainfrom
shkumbinhasani:perf/start-compiler-member-call-receivers

Conversation

@shkumbinhasani

@shkumbinhasani shkumbinhasani commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

🎯 Changes

Fixes #8757.

A top-level call in a compiled file is a direct-call candidate, so that a renamed createServerOnlyFn/createClientOnlyFn is still found. isTopLevelDirectCallCandidate accepts any a.b(), so const Input = z.object({...}) resolves z, and in dev that loads zod through ?server-fn-module-lookup and parses it with Babel, while every compiled file waits.

For a.b() where a is not a namespace import, resolveCalleeKind returns the kind of a itself. That passes the candidate filter only when a is an env-only factory and the code calls one of its properties (createServerOnlyFn.bind(...)): a Root/Builder receiver resolves to Builder, and a method-chain kind fails isDirectCallCandidateForKind. This PR skips those calls. Namespace calls (Start.createServerOnlyFn(), or factory.serverOnly() through a renamed re-export), identifier calls and method chains resolve as before. In the full traversal, a skipped call still goes through the nested factory-name check.

Reproducer from the issue (start-basic-cloudflare, 60 server function files with import { z } from 'zod' schemas and a top-level Sentry.getClient()), baseline and patched builds alternated, medians:

before after
?server-fn-module-lookup transforms, client 4 (976 KiB) 2 (544 KiB)
?server-fn-module-lookup transforms, ssr 5 (977 KiB) 3 (545 KiB)
compiler this.resolve calls, client / ssr 363 / 249 123 / 125
average wall time per client compiler transform 50 ms 33 ms
compiler hook time, files one at a time, client / ssr 92 / 159 ms 74 / 96 ms
first load of /perf 1355 ms 1320 ms

The gain only applies when the receiver is not a namespace import: import * as z from 'zod' still loads zod.

Tests: two new tests in tests/compiler.test.ts. One checks that a named-import receiver (z.object(), api.users()) triggers no resolve and no load; it fails on main. The other checks that a renamed env-only factory called through a namespace import still compiles on client and server. Ran test:unit, test:types and test:eslint for @tanstack/start-plugin-core, and the hmr (vite, vite bundled dev, rsbuild) and server-functions (vite, rsbuild) e2e suites; all pass.

Written with an AI assistant (Claude Code).

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested code changes locally with the relevant test commands, or tests do not apply to this pull request.
  • I fully understand the code in this pull request, including any code generated with AI assistance.

🚀 Release Impact

  • This change affects published code, and I have generated a changeset.
  • This change is docs/CI/dev-only (no release).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • The development compiler skips top-level member calls made through named imports, preventing unrelated calls from interfering with compilation.
    • Environment-specific functions called through namespace imports compile correctly in both client and server environments. Client builds retain a throwing stub, while server builds retain the function implementation.

… receivers

A top-level `a.b()` initializer was a direct-call candidate for any `a`, so
`const Input = z.object({...})` resolved `z` and, in dev, loaded and parsed all
of zod through `?server-fn-module-lookup` while every compiled file waited.

For a receiver that is not a namespace import, resolution gives the call the
kind of the receiver itself, which only yields a candidate when the receiver
is an env-only factory and the code calls one of its properties. Skip those
calls; namespace calls, identifier calls and method chains resolve as before.

Refs TanStack#8757

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 655264c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
@tanstack/start-plugin-core Patch
@tanstack/react-start-rsc Patch
@tanstack/react-start Patch
@tanstack/solid-start Patch
@tanstack/vue-start Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Important

Review skipped

Review was skipped as selected files did not have any reviewable changes.

⚙️ Run configuration
  • Configuration used: Repository: TanStack/router/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 95efa864-a0e9-448e-83b4-374efb97b8cb
📥 Commits

Reviewing files that changed from the base of the PR and between 0d32d9c and 655264c.

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: TanStack/router/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 623202ab-3418-43d4-bf50-db8e85fc527d
📥 Commits

Reviewing files that changed from the base of the PR and between e6a80f1 and 0d32d9c.

📒 Files selected for processing: 1
  • packages/start-plugin-core/tests/compiler.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

The dev compiler now skips direct-call resolution for top-level member calls on identifier receivers that are not namespace imports. Tests cover named-import receivers and namespace-import factories in client and server compilation.

Changes: Compiler member-call filtering

Layer / File(s) Summary
Candidate filtering and validation
packages/start-plugin-core/src/start-compiler/compiler.ts, packages/start-plugin-core/tests/compiler.test.ts, .changeset/start-compiler-member-call-receivers.md
A shared receiver check excludes non-namespace identifier member calls in both traversal paths. Tests verify that named-import calls do not resolve or load modules, and that a renamed factory accessed through a namespace import compiles for client and server. A changeset records the compiler change.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: schiller-manuel

Merge Risk: ⚪ Minimal · up to 0d32d

No actionable issue is established from the supplied evidence; the change is mergeable after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 4 functions across 2 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly and concisely describes the main compiler optimization: skipping resolution of member calls on non-namespace receivers.
Description check Passed The description includes the required Changes, Checklist, and Release Impact sections. It explains the motivation, implementation scope, performance results, tests, changeset, and AI assistance disclo…
Linked Issues check Passed Issue #8757 requires the compiler to avoid resolving top-level member calls when the receiver is not a namespace import. isMemberCallOnNonNamespaceReceiver excludes those calls from both direct-call…
Out of Scope Changes check Passed The compiler change, compiler tests, and changeset directly support issue #8757. The namespace-import regression coverage preserves required behavior. No unrelated implementation or test changes are s…
Full details: Description check

Explanation

The description includes the required Changes, Checklist, and Release Impact sections. It explains the motivation, implementation scope, performance results, tests, changeset, and AI assistance disclosure. One checklist item remains unchecked, but the description is otherwise complete.

Full details: Linked Issues check

Explanation

Issue #8757 requires the compiler to avoid resolving top-level member calls when the receiver is not a namespace import. isMemberCallOnNonNamespaceReceiver excludes those calls from both direct-call candidate paths. The tests cover named-import receivers across client, SSR, and provider output. The tests also preserve renamed env-only factory calls through namespace imports for client and server builds.

Full details: Out of Scope Changes check

Explanation

The compiler change, compiler tests, and changeset directly support issue #8757. The namespace-import regression coverage preserves required behavior. No unrelated implementation or test changes are shown in the supplied pull request evidence.

✨ Finishing Touches: 💡 1
🧪 Generate unit tests (beta)
  • Create a new PR
🛠️ Fix failing CI checks: 💡
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@nx-cloud

nx-cloud Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit 655264c

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

☁️ Nx Cloud last updated this comment at 2026-10-18 21:01:32 UTC

@pkg-pr-new

pkg-pr-new Bot commented Oct 18, 2026 •

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/@tanstack/arktype-adapter@8758

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@8758

@tanstack/history

npm i https://pkg.pr.new/@tanstack/history@8758

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

npm i https://pkg.pr.new/@tanstack/react-start@8758

@tanstack/react-start-client

npm i https://pkg.pr.new/@tanstack/react-start-client@8758

@tanstack/react-start-rsc

npm i https://pkg.pr.new/@tanstack/react-start-rsc@8758

@tanstack/react-start-server

npm i https://pkg.pr.new/@tanstack/react-start-server@8758

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

npm i https://pkg.pr.new/@tanstack/solid-start@8758

@tanstack/solid-start-client

npm i https://pkg.pr.new/@tanstack/solid-start-client@8758

@tanstack/solid-start-server

npm i https://pkg.pr.new/@tanstack/solid-start-server@8758

@tanstack/start-client-core

npm i https://pkg.pr.new/@tanstack/start-client-core@8758

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/@tanstack/start-fn-stubs@8758

@tanstack/start-plugin-core

npm i https://pkg.pr.new/@tanstack/start-plugin-core@8758

@tanstack/start-server-core

npm i https://pkg.pr.new/@tanstack/start-server-core@8758

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

npm i https://pkg.pr.new/@tanstack/start-storage-context@8758

@tanstack/valibot-adapter

npm i https://pkg.pr.new/@tanstack/valibot-adapter@8758

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/@tanstack/virtual-file-routes@8758

@tanstack/vue-router

npm i https://pkg.pr.new/@tanstack/vue-router@8758

@tanstack/vue-router-devtools

npm i https://pkg.pr.new/@tanstack/vue-router-devtools@8758

@tanstack/vue-router-ssr-query

npm i https://pkg.pr.new/@tanstack/vue-router-ssr-query@8758

@tanstack/vue-start

npm i https://pkg.pr.new/@tanstack/vue-start@8758

@tanstack/vue-start-client

npm i https://pkg.pr.new/@tanstack/vue-start-client@8758

@tanstack/vue-start-server

npm i https://pkg.pr.new/@tanstack/vue-start-server@8758

@tanstack/zod-adapter

npm i https://pkg.pr.new/@tanstack/zod-adapter@8758

commit: 655264c

@codspeed

codspeed Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 1.9%

⚠️ 42 benchmarks spent significant time in system calls

System calls cannot be consistently instrumented, so they are not included in the measure, which understates the real cost. Please switch to the Walltime instrument to accurately measure system calls.

Measurement and system calls

ℹ️ 98 benchmarks have no flame graph

The flamegraph could not be generated because too much time was spent in runtime internals, making the visualization not useful for performance analysis.

⚠️ Different runtime environments detected

Some benchmarks with significant performance changes were compared across different runtime environments,
which may affect the accuracy of the results.

Open the report in CodSpeed to investigate

⚡ 1 improved benchmark
❌ 3 (👁 3) regressed benchmarks
✅ 190 untouched benchmarks
⏩ 3 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
⚡ Memory mem client interrupted-navigations (vue) 97.5 KB 92.5 KB +5.42%
👁 Memory mem client navigation-churn (react) 81.9 KB 84.5 KB -3.14%
👁 Memory mem server error-paths redirect (solid) 11.6 KB 12.3 KB -5.22%
👁 Memory mem client unique-location-churn (vue) 80.1 KB 83.7 KB -4.31%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing shkumbinhasani:perf/start-compiler-member-call-receivers (655264c) with main (336fdfb)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@Sheraff
Sheraff merged commit d9b07e8 into TanStack:main Oct 18, 2026
30 checks passed
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.

Start: dev compiler loads whole dependency modules to classify top-level calls such as z.object()

2 participants