Skip to content

Conversation

@schiller-manuel
Copy link
Contributor

@schiller-manuel schiller-manuel commented Dec 21, 2025

fixes #6169

Summary by CodeRabbit

  • New Features

    • Added a Middleware page ("Server imports in middleware are stripped from client build") with UI to invoke a server-backed action and display a returned header.
    • Exposed helper APIs for isomorphic, server-only, and client-only function patterns (re-exports).
  • Bug Fixes

    • Ensure server-only imports used in middleware are not bundled into client builds.
  • Tests

    • Added an end-to-end test validating server-only imports inside middleware are excluded from the client build.
  • Chores

    • Added a small package providing function stubs and updated package exports.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 21, 2025

Walkthrough

Adds a middleware test route and E2E test; introduces a new start-fn-stubs package and re-wires client imports; significantly refactors the create-server-fn plugin/compiler to support Isomorphic/ServerOnly/ClientOnly kinds with new AST handlers and utilities; and removes the previous start-compiler-plugin pipeline.

Changes

Cohort / File(s) Summary
Generated route tree
e2e/react-start/server-functions/src/routeTree.gen.ts
Registers new route /middleware/server-import-middleware (import, Route constant, entries in FileRoutesByFullPath/ByTo/ById, FileRouteTypes, RootRouteChildren and @tanstack/react-router augmentation).
Route UI & page
e2e/react-start/server-functions/src/routes/middleware/index.tsx, e2e/react-start/server-functions/src/routes/middleware/server-import-middleware.tsx
Adds navigation link and a new TSX route that defines middleware.server() using a server-only header helper, wires a server function, and exposes UI (button/result/error) with test IDs.
E2E test
e2e/react-start/server-functions/tests/server-functions.spec.ts
New end-to-end test that navigates to the middleware page, invokes the server function via UI, and asserts the returned header to verify server-only imports are not bundled to the client.
create-server-fn plugin — core changes
packages/start-plugin-core/src/create-server-fn-plugin/* (notably plugin.ts, compiler.ts, handleCreateIsomorphicFn.ts, handleEnvOnly.ts, utils.ts, types.ts)
Expands LookupKind to include IsomorphicFn / ServerOnlyFn / ClientOnlyFn; switches identifier→Set; adds handleCreateIsomorphicFn and handleEnvOnlyFn handlers; adds codeFrameError util and shared lookup/config/transform filter logic; updates init/resolve signatures.
Start-compiler-plugin removals
packages/start-plugin-core/src/start-compiler-plugin/* (deleted: compilers.ts, plugin.ts, constants.ts, envOnly.ts, isomorphicFn.ts, utils.ts)
Removes the previous start-compiler-plugin compilation pipeline, related handlers, constants and helpers; deletes several public types and utilities previously provided by that module.
Plugin types relocation & imports
packages/start-plugin-core/src/plugin.ts, packages/start-plugin-core/src/types.ts, packages/start-plugin-core/src/schema.ts, packages/start-plugin-core/src/start-manifest-plugin/plugin.ts, packages/start-plugin-core/src/start-router-plugin/plugin.ts
Moves exported type declarations from plugin.ts into a new types.ts; updates type import sites to use ./types.
New package: start-fn-stubs
packages/start-fn-stubs/*, scripts/publish.js, labeler-config.yml, packages/start-client-core/package.json
Adds @tanstack/start-fn-stubs package (src, tsconfig, vite, package.json, eslint), updates workspace & publish scripts, and adds labeler package entry.
Client imports & public exports updated
packages/start-client-core/src/*, packages/start-client-core/src/index.tsx
Replaces several local imports (createIsomorphicFn, createServerOnlyFn, createClientOnlyFn) with re-exports from @tanstack/start-fn-stubs; updates index exports to expose server/client-only functions from stubs.
Tests & snapshots
packages/start-plugin-core/tests/**, packages/start-plugin-core/tests/**/snapshots/**
Tests refactored to use ServerFnCompiler/async compile helper; many snapshots and tests updated (removed helper functions/imports); lookupConfigurations in tests now include kind: 'Root' where appropriate.
Misc tooling & configs
labeler-config.yml, scripts/publish.js, packages/start-fn-stubs/vite.config.ts, packages/start-fn-stubs/package.json, packages/start-fn-stubs/tsconfig.json, packages/start-fn-stubs/src/*
Adds per-package tooling and config for start-fn-stubs, test config, package exports, and publish entries; minor type-import path adjustments across plugins.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay extra attention to:
    • create-server-fn compiler refactor (LookupKind expansion, Identifier→Kinds map and resolve/init signature changes).
    • Large deletions under start-compiler-plugin — ensure no dangling imports and verify replacement flows.
    • AST transform helpers: handleCreateIsomorphicFn and handleEnvOnlyFn — validate AST replacements, diagnostics, and edge cases.
    • start-fn-stubs package exports and client re-exports — confirm types and module resolution across workspaces.
    • Test changes that add kind: 'Root' — ensure expected init/resolve behavior matches runtime.

Possibly related PRs

Suggested reviewers

  • schiller-manuel
  • nlynzaad

Poem

"🐇 I hopped through code and AST,
Carrots scattered tests at last.
A stub, a route, a middleware cheer,
Compiler hops and errors clear —
I nibble bugs and bound past."

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR introduces substantial refactoring beyond the middleware filter fix, including removal of start-compiler-plugin, creation of start-fn-stubs package, and restructuring of compiler logic—changes not explicitly required by issue #6169. Clarify whether the architectural refactoring (start-compiler-plugin removal, start-fn-stubs creation, compiler restructuring) is necessary to fix the middleware filter issue or should be separated into distinct PRs.
Docstring Coverage ⚠️ Warning Docstring coverage is 4.55% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'fix: compiler plugin' is vague and generic—it does not clearly convey what was actually fixed or the primary nature of the substantial changes throughout this PR. Improve the title to be more specific about the actual fix, e.g., 'fix: prevent server-only imports from leaking into client builds' or 'fix: refactor compiler plugin to use unified ServerFnCompiler' to better describe the main change.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The PR addresses issue #6169 by fixing middleware expression matching in the compiler plugin filter to prevent node:* modules from leaking into client builds.
✨ 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 fix-6169

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

@nx-cloud
Copy link

nx-cloud bot commented Dec 21, 2025

View your CI Pipeline Execution ↗ for commit 5ff68a1

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

☁️ Nx Cloud last updated this comment at 2025-12-22 13:57:27 UTC

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 (1)
e2e/react-start/server-functions/src/routes/middleware/server-import-middleware.tsx (1)

1-82: LGTM! Test route effectively demonstrates the fix.

The route implementation correctly uses the createMiddleware().server() pattern with server-only imports, which validates that the compiler plugin fix works as intended.

The explicit React import on line 4 may be unnecessary in React 19, as the new JSX transform doesn't require it. However, since React.useState is used, the import is needed. Consider using named import instead:

🔎 Optional refactor for consistency with React 19
-import React from 'react'
+import { useState } from 'react'

And update the component:

 function RouteComponent() {
-  const [result, setResult] = React.useState<{ testHeader: string } | null>(
+  const [result, setResult] = useState<{ testHeader: string } | null>(
     null,
   )
-  const [error, setError] = React.useState<string | null>(null)
+  const [error, setError] = useState<string | null>(null)
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6fd9c95 and 2cd6c7a.

📒 Files selected for processing (5)
  • e2e/react-start/server-functions/src/routeTree.gen.ts (11 hunks)
  • e2e/react-start/server-functions/src/routes/middleware/index.tsx (1 hunks)
  • e2e/react-start/server-functions/src/routes/middleware/server-import-middleware.tsx (1 hunks)
  • e2e/react-start/server-functions/tests/server-functions.spec.ts (1 hunks)
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript strict mode with extensive type safety for all code

Files:

  • e2e/react-start/server-functions/tests/server-functions.spec.ts
  • e2e/react-start/server-functions/src/routes/middleware/index.tsx
  • e2e/react-start/server-functions/src/routes/middleware/server-import-middleware.tsx
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
  • e2e/react-start/server-functions/src/routeTree.gen.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Implement ESLint rules for router best practices using the ESLint plugin router

Files:

  • e2e/react-start/server-functions/tests/server-functions.spec.ts
  • e2e/react-start/server-functions/src/routes/middleware/index.tsx
  • e2e/react-start/server-functions/src/routes/middleware/server-import-middleware.tsx
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
  • e2e/react-start/server-functions/src/routeTree.gen.ts
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{js,ts,tsx} : Implement ESLint rules for router best practices using the ESLint plugin router
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.

Applied to files:

  • e2e/react-start/server-functions/tests/server-functions.spec.ts
  • e2e/react-start/server-functions/src/routes/middleware/index.tsx
  • e2e/react-start/server-functions/src/routes/middleware/server-import-middleware.tsx
  • e2e/react-start/server-functions/src/routeTree.gen.ts
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.

Applied to files:

  • e2e/react-start/server-functions/src/routes/middleware/server-import-middleware.tsx
  • e2e/react-start/server-functions/src/routeTree.gen.ts
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{js,ts,tsx} : Implement ESLint rules for router best practices using the ESLint plugin router

Applied to files:

  • e2e/react-start/server-functions/src/routes/middleware/server-import-middleware.tsx
  • e2e/react-start/server-functions/src/routeTree.gen.ts
📚 Learning: 2025-12-17T02:17:55.086Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 6120
File: packages/router-generator/src/generator.ts:654-657
Timestamp: 2025-12-17T02:17:55.086Z
Learning: In `packages/router-generator/src/generator.ts`, pathless_layout routes must receive a `path` property when they have a `cleanedPath`, even though they are non-path routes. This is necessary because child routes inherit the path from their parent, and without this property, child routes would not have the correct full path at runtime.

Applied to files:

  • e2e/react-start/server-functions/src/routeTree.gen.ts
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
Repo: TanStack/router PR: 5330
File: e2e/react-start/custom-basepath/src/routeTree.gen.ts:58-61
Timestamp: 2025-10-01T18:31:35.420Z
Learning: Do not review files named `routeTree.gen.ts` in TanStack Router repositories, as these are autogenerated files that should not be manually modified.

Applied to files:

  • e2e/react-start/server-functions/src/routeTree.gen.ts
📚 Learning: 2025-09-28T21:41:45.233Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5284
File: e2e/react-start/basic/server.js:50-0
Timestamp: 2025-09-28T21:41:45.233Z
Learning: In Express v5, catch-all routes must use named wildcards. Use `/*splat` to match everything except root path, or `/{*splat}` (with braces) to match including root path. The old `*` syntax is not allowed and will cause "Missing parameter name" errors. This breaking change requires explicit naming of wildcard parameters.

Applied to files:

  • e2e/react-start/server-functions/src/routeTree.gen.ts
📚 Learning: 2025-12-21T12:52:27.078Z
Learnt from: Sheraff
Repo: TanStack/router PR: 6171
File: packages/router-core/src/new-process-route-tree.ts:898-898
Timestamp: 2025-12-21T12:52:27.078Z
Learning: In `packages/router-core/src/new-process-route-tree.ts`, the matching logic intentionally allows paths without trailing slashes to match index routes with trailing slashes (e.g., `/a` can match `/a/` route), but not vice-versa (e.g., `/a/` cannot match `/a` layout route). This is implemented via the condition `!pathIsIndex || node.kind === SEGMENT_TYPE_INDEX` and is a deliberate design decision to provide better UX by being permissive with missing trailing slashes.

Applied to files:

  • e2e/react-start/server-functions/src/routeTree.gen.ts
🧬 Code graph analysis (2)
e2e/react-start/server-functions/src/routes/middleware/index.tsx (2)
e2e/react-start/server-functions/src/routes/middleware/server-import-middleware.tsx (1)
  • Route (36-38)
e2e/react-start/server-functions/src/routes/middleware/request-middleware.tsx (1)
  • Route (23-26)
e2e/react-start/server-functions/src/routes/middleware/server-import-middleware.tsx (2)
packages/start-server-core/src/request-response.ts (1)
  • getRequestHeaders (149-152)
e2e/react-start/server-functions/src/routes/middleware/index.tsx (1)
  • Route (3-5)
⏰ 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: Test
  • GitHub Check: Preview
🔇 Additional comments (4)
e2e/react-start/server-functions/src/routes/middleware/index.tsx (1)

36-43: LGTM! Navigation link follows established patterns.

The new navigation link is correctly structured and follows the same pattern as other middleware test links in the list.

e2e/react-start/server-functions/tests/server-functions.spec.ts (1)

619-637: LGTM! Comprehensive test validates the fix.

The test effectively validates that server-only imports inside createMiddleware().server() are properly stripped from the client build. The fact that the page loads without build errors proves the fix works, and the assertion verifies the middleware functionality.

e2e/react-start/server-functions/src/routeTree.gen.ts (1)

37-37: Autogenerated file - no review needed.

This is an autogenerated route tree file that should not be manually modified or reviewed.

Based on learnings, routeTree.gen.ts files in TanStack Router repositories are autogenerated and should not be manually modified.

Also applies to: 170-175, 231-231, 265-265, 300-300, 336-336, 370-370, 404-404, 439-439, 630-636, 703-703

packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (1)

60-66: LGTM! Core fix correctly matches the actual middleware usage pattern.

The change from matching .createMiddleware( to .server( correctly addresses the issue. All middleware usage in the codebase follows the pattern createMiddleware().server(...) or createMiddleware({...}).server(...), so matching .server( ensures the server-only code block is properly identified and stripped from client builds. The .handler( pattern for createServerFn is also correct.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 21, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/directive-functions-plugin

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-fn-stubs@6177

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/vue-start@6177

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: 5ff68a1

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (1)

79-106: Update regex patterns to use word boundaries for more precise filtering.

The transform code filters use regex patterns without word boundaries:

  • /createIsomorphicFn/ would match myCreateIsomorphicFn
  • /createServerOnlyFn/ would match notCreateServerOnlyFn
  • /createClientOnlyFn/ would match prefixCreateClientOnlyFn

While the compiler performs AST-based validation later, these false positives would cause unnecessary transform hook invocations. Use word boundaries for precise matching:

const commonTransformCodeFilter = [
  /\.\s*handler\(/,
  /\bcreateIsomorphicFn\b/,
  /\bcreateServerOnlyFn\b/,
  /\bcreateClientOnlyFn\b/,
]

(Note: The original suggestion had a typo—creatIsomorphicFn was missing the 'e')

🧹 Nitpick comments (2)
packages/start-plugin-core/src/create-server-fn-plugin/handleCreateIsomorphicFn.ts (1)

20-25: Consider structured logging for compile-time warnings.

Using console.warn with multiple arguments works but produces fragmented output. A single formatted string would be more readable in build logs.

🔎 Suggested improvement
-    console.warn(
-      'createIsomorphicFn called without a client or server implementation!',
-      'This will result in a no-op function.',
-      'Variable name:',
-      t.isIdentifier(variableId) ? variableId.name : 'unknown',
-    )
+    const varName = t.isIdentifier(variableId) ? variableId.name : 'unknown'
+    console.warn(
+      `createIsomorphicFn called without a client or server implementation! This will result in a no-op function. Variable name: ${varName}`,
+    )
packages/start-plugin-core/src/start-router-plugin/plugin.ts (1)

20-20: Use import type for type-only imports.

Since TanStackStartVitePluginCoreOptions and GetConfigFn are only used as type annotations in this file, using import type improves clarity and enables better tree-shaking.

🔎 Suggested fix
-import { TanStackStartVitePluginCoreOptions, GetConfigFn } from '../types'
+import type { TanStackStartVitePluginCoreOptions, GetConfigFn } from '../types'
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2cd6c7a and 597397c.

📒 Files selected for processing (45)
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/handleCreateIsomorphicFn.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/handleCreateServerFn.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/handleEnvOnly.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/utils.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-plugin-core/src/start-compiler-plugin/compilers.ts
  • packages/start-plugin-core/src/start-compiler-plugin/constants.ts
  • packages/start-plugin-core/src/start-compiler-plugin/envOnly.ts
  • packages/start-plugin-core/src/start-compiler-plugin/isomorphicFn.ts
  • packages/start-plugin-core/src/start-compiler-plugin/plugin.ts
  • packages/start-plugin-core/src/start-compiler-plugin/utils.ts
  • packages/start-plugin-core/src/start-manifest-plugin/plugin.ts
  • packages/start-plugin-core/src/start-router-plugin/plugin.ts
  • packages/start-plugin-core/src/types.ts
  • packages/start-plugin-core/tests/createIsomorphicFn/createIsomorphicFn.test.ts
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/client/createIsomorphicFnDestructured.tsx
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/client/createIsomorphicFnDestructuredRename.tsx
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/client/createIsomorphicFnStarImport.tsx
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/server/createIsomorphicFnDestructured.tsx
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/server/createIsomorphicFnDestructuredRename.tsx
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/server/createIsomorphicFnStarImport.tsx
  • packages/start-plugin-core/tests/createMiddleware/createMiddleware.test.ts
  • packages/start-plugin-core/tests/createMiddleware/snapshots/client/create-function-middleware.ts
  • packages/start-plugin-core/tests/createMiddleware/snapshots/client/createMiddlewareDestructured.tsx
  • packages/start-plugin-core/tests/createMiddleware/snapshots/client/createMiddlewareDestructuredRename.tsx
  • packages/start-plugin-core/tests/createMiddleware/snapshots/client/createMiddlewareStarImport.tsx
  • packages/start-plugin-core/tests/createMiddleware/snapshots/client/createMiddlewareValidator.tsx
  • packages/start-plugin-core/tests/createMiddleware/snapshots/client/createStart.tsx
  • packages/start-plugin-core/tests/createMiddleware/test-files/create-function-middleware.ts
  • packages/start-plugin-core/tests/createMiddleware/test-files/createMiddlewareDestructured.tsx
  • packages/start-plugin-core/tests/createMiddleware/test-files/createMiddlewareDestructuredRename.tsx
  • packages/start-plugin-core/tests/createMiddleware/test-files/createMiddlewareStarImport.tsx
  • packages/start-plugin-core/tests/createMiddleware/test-files/createMiddlewareValidator.tsx
  • packages/start-plugin-core/tests/createMiddleware/test-files/createStart.tsx
  • packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts
  • packages/start-plugin-core/tests/envOnly/envOnly.test.ts
  • packages/start-plugin-core/tests/envOnly/snapshots/client/envOnlyDestructured.tsx
  • packages/start-plugin-core/tests/envOnly/snapshots/client/envOnlyDestructuredRename.tsx
  • packages/start-plugin-core/tests/envOnly/snapshots/client/envOnlyStarImport.tsx
  • packages/start-plugin-core/tests/envOnly/snapshots/server/envOnlyDestructured.tsx
  • packages/start-plugin-core/tests/envOnly/snapshots/server/envOnlyDestructuredRename.tsx
  • packages/start-plugin-core/tests/envOnly/snapshots/server/envOnlyStarImport.tsx
💤 Files with no reviewable changes (18)
  • packages/start-plugin-core/tests/envOnly/snapshots/server/envOnlyStarImport.tsx
  • packages/start-plugin-core/src/start-compiler-plugin/utils.ts
  • packages/start-plugin-core/src/start-compiler-plugin/plugin.ts
  • packages/start-plugin-core/src/start-compiler-plugin/isomorphicFn.ts
  • packages/start-plugin-core/src/start-compiler-plugin/constants.ts
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/server/createIsomorphicFnStarImport.tsx
  • packages/start-plugin-core/tests/envOnly/snapshots/client/envOnlyDestructuredRename.tsx
  • packages/start-plugin-core/tests/envOnly/snapshots/client/envOnlyDestructured.tsx
  • packages/start-plugin-core/tests/envOnly/snapshots/server/envOnlyDestructured.tsx
  • packages/start-plugin-core/src/start-compiler-plugin/compilers.ts
  • packages/start-plugin-core/src/start-compiler-plugin/envOnly.ts
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/server/createIsomorphicFnDestructured.tsx
  • packages/start-plugin-core/tests/envOnly/snapshots/client/envOnlyStarImport.tsx
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/client/createIsomorphicFnStarImport.tsx
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/client/createIsomorphicFnDestructuredRename.tsx
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/client/createIsomorphicFnDestructured.tsx
  • packages/start-plugin-core/tests/createIsomorphicFn/snapshots/server/createIsomorphicFnDestructuredRename.tsx
  • packages/start-plugin-core/tests/envOnly/snapshots/server/envOnlyDestructuredRename.tsx
✅ Files skipped from review due to trivial changes (1)
  • packages/start-plugin-core/src/start-manifest-plugin/plugin.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript strict mode with extensive type safety for all code

Files:

  • packages/start-plugin-core/src/create-server-fn-plugin/handleCreateServerFn.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/handleCreateIsomorphicFn.ts
  • packages/start-plugin-core/src/start-router-plugin/plugin.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/handleEnvOnly.ts
  • packages/start-plugin-core/tests/envOnly/envOnly.test.ts
  • packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/utils.ts
  • packages/start-plugin-core/tests/createMiddleware/createMiddleware.test.ts
  • packages/start-plugin-core/tests/createIsomorphicFn/createIsomorphicFn.test.ts
  • packages/start-plugin-core/src/types.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Implement ESLint rules for router best practices using the ESLint plugin router

Files:

  • packages/start-plugin-core/src/create-server-fn-plugin/handleCreateServerFn.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/handleCreateIsomorphicFn.ts
  • packages/start-plugin-core/src/start-router-plugin/plugin.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/handleEnvOnly.ts
  • packages/start-plugin-core/tests/envOnly/envOnly.test.ts
  • packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/utils.ts
  • packages/start-plugin-core/tests/createMiddleware/createMiddleware.test.ts
  • packages/start-plugin-core/tests/createIsomorphicFn/createIsomorphicFn.test.ts
  • packages/start-plugin-core/src/types.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
🧠 Learnings (3)
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.

Applied to files:

  • packages/start-plugin-core/src/start-router-plugin/plugin.ts
  • packages/start-plugin-core/tests/envOnly/envOnly.test.ts
  • packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-plugin-core/tests/createMiddleware/createMiddleware.test.ts
  • packages/start-plugin-core/tests/createIsomorphicFn/createIsomorphicFn.test.ts
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{js,ts,tsx} : Implement ESLint rules for router best practices using the ESLint plugin router

Applied to files:

  • packages/start-plugin-core/src/start-router-plugin/plugin.ts
  • packages/start-plugin-core/src/schema.ts
📚 Learning: 2025-10-08T08:11:47.088Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.

Applied to files:

  • packages/start-plugin-core/tests/envOnly/envOnly.test.ts
🧬 Code graph analysis (6)
packages/start-plugin-core/src/create-server-fn-plugin/handleCreateIsomorphicFn.ts (2)
packages/start-plugin-core/src/create-server-fn-plugin/types.ts (1)
  • RewriteCandidate (39-42)
packages/router-core/src/route.ts (1)
  • path (1553-1555)
packages/start-plugin-core/src/create-server-fn-plugin/handleEnvOnly.ts (3)
packages/start-plugin-core/src/create-server-fn-plugin/types.ts (1)
  • RewriteCandidate (39-42)
packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (1)
  • LookupKind (35-40)
packages/router-core/src/route.ts (1)
  • path (1553-1555)
packages/start-plugin-core/tests/createIsomorphicFn/createIsomorphicFn.test.ts (1)
packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (2)
  • compile (308-461)
  • ServerFnCompiler (125-817)
packages/start-plugin-core/src/types.ts (1)
packages/start-plugin-core/src/schema.ts (1)
  • TanStackStartOutputConfig (207-207)
packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (3)
packages/start-plugin-core/src/create-server-fn-plugin/handleCreateMiddleware.ts (1)
  • handleCreateMiddleware (10-45)
packages/start-plugin-core/src/create-server-fn-plugin/handleCreateIsomorphicFn.ts (1)
  • handleCreateIsomorphicFn (4-46)
packages/start-plugin-core/src/create-server-fn-plugin/handleEnvOnly.ts (1)
  • handleEnvOnlyFn (10-45)
packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (2)
packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (2)
  • LookupKind (35-40)
  • LookupConfig (110-114)
packages/start-plugin-core/src/types.ts (1)
  • CompileStartFrameworkOptions (3-3)
⏰ 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: Test
  • GitHub Check: Preview
🔇 Additional comments (24)
packages/start-plugin-core/src/create-server-fn-plugin/handleCreateServerFn.ts (1)

2-2: LGTM - Import path update aligns with module reorganization.

The import relocation from ../start-compiler-plugin/utils to ./utils is consistent with the new utils.ts file added in this plugin directory.

packages/start-plugin-core/src/create-server-fn-plugin/utils.ts (1)

1-24: LGTM - Clean utility for formatted error messages.

The codeFrameError function provides a useful abstraction for creating developer-friendly error messages with code context. The implementation correctly uses @babel/code-frame for syntax highlighting.

packages/start-plugin-core/src/create-server-fn-plugin/handleCreateIsomorphicFn.ts (1)

4-46: LGTM - Solid implementation of environment-specific isomorphic function handling.

The function correctly:

  • Selects the appropriate implementation based on the build environment
  • Provides a warning when no implementations are provided
  • Falls back to no-op functions when the current environment has no implementation
  • Validates that the extracted argument is an expression
packages/start-plugin-core/src/create-server-fn-plugin/handleEnvOnly.ts (2)

28-44: Good defensive runtime error for environment mismatch.

The throwing arrow function provides clear runtime feedback when a server-only or client-only function is called in the wrong environment. This is the correct approach for environment-specific code that shouldn't leak across boundaries - directly addressing the issue #6169.


10-15: The logic at line 15 is correct. The calling code in compiler.ts (lines 430-451) uses an if/else chain that ensures handleEnvOnlyFn is only invoked when kind is either ServerOnlyFn or ClientOnlyFn. Other LookupKind values are routed to different handlers (ServerFn, Middleware, IsomorphicFn), so the assumption is valid and no unexpected behavior can occur.

Likely an incorrect or invalid review comment.

packages/start-plugin-core/src/schema.ts (1)

4-4: LGTM - Import path updated to reference types from centralized module.

This aligns with the broader refactoring that moves type declarations to ./types.ts.

packages/start-plugin-core/tests/createServerFn/createServerFn.test.ts (3)

22-27: LGTM - Test configuration updated for new kind: 'Root' property.

The addition of kind: 'Root' correctly reflects the updated LookupConfig type that now supports root-based resolution for library imports.


249-253: LGTM - Test assertions updated to reflect fast-path resolution behavior.

The updated expectation correctly validates that root imports use the fast path with knownRootImports, requiring only the library name (single argument) rather than both library and file name.


298-306: LGTM - Factory pattern test correctly validates slow-path behavior.

The test properly verifies that:

  1. Root imports (@tanstack/react-start) use the fast path (single arg)
  2. Local factory imports (./factory) use the slow path (two args with file context)
packages/start-plugin-core/tests/envOnly/envOnly.test.ts (1)

5-41: LGTM! Well-structured test helper.

The new compile helper properly configures the ServerFnCompiler with all required options and provides a clean async interface for tests.

packages/start-plugin-core/src/plugin.ts (2)

28-32: LGTM! Type consolidation improves maintainability.

Moving these types to a dedicated types.ts file reduces duplication and provides a single source of truth for shared type definitions.


406-407: Helpful architectural context.

The comment clearly explains that startCompilerPlugin functionality has been merged into createServerFnPlugin, which aids understanding of the refactor.

packages/start-plugin-core/tests/createIsomorphicFn/createIsomorphicFn.test.ts (1)

5-36: LGTM! Consistent test refactoring.

The test helper follows the same pattern as other test files, providing consistent async handling across the test suite.

packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (1)

12-25: LGTM! Expanded environment support.

The addition of IsomorphicFn, ServerOnlyFn, and ClientOnlyFn to both client and server lookup kinds properly supports the new function types across environments.

packages/start-plugin-core/tests/createMiddleware/createMiddleware.test.ts (2)

17-17: LGTM! Simplified test configuration.

The loadModule callback no longer needs the id parameter since it's a no-op in tests, making the test setup cleaner.


98-100: Clear documentation of behavioral change.

The comment effectively explains that init() now resolves from the project root rather than from a specific file, which helps future maintainers understand the resolution behavior.

packages/start-plugin-core/src/types.ts (1)

1-34: LGTM! Well-organized type definitions.

The new types module consolidates shared types across the plugin core, reducing duplication and improving maintainability. All type definitions are clear and properly documented through their structure.

packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (7)

35-69: LGTM! Well-designed detection strategy.

The new LookupKind variants and detection strategies properly handle:

  • Method chain patterns (ServerFn, Middleware, IsomorphicFn)
  • Direct call patterns (ServerOnlyFn, ClientOnlyFn)
  • Root-as-candidate for IsomorphicFn (which can be called without .client() or .server())

The design is flexible and extensible.


71-87: Excellent optimization for shared identifiers.

The IdentifierToKinds map correctly handles the fact that identifiers like 'server' and 'client' are used by multiple kinds (both Middleware and IsomorphicFn). Using Set<LookupKind> allows O(1) lookup while supporting multiple kinds per identifier.


437-451: LGTM! Comprehensive kind handling.

The compiler now properly delegates to the appropriate handler for each kind:

  • ServerFnhandleCreateServerFn
  • MiddlewarehandleCreateMiddleware
  • IsomorphicFnhandleCreateIsomorphicFn
  • ServerOnlyFn/ClientOnlyFnhandleEnvOnlyFn

The else branch correctly catches the environment-specific function kinds.


464-501: Well-structured candidate collection with clear patterns.

The updated logic correctly handles:

  1. Method chain patterns (existing behavior)
  2. Direct call patterns for environment-specific functions
  3. Root-as-candidate patterns for IsomorphicFn

The two-phase approach (method chain check first, then direct/root call check) ensures proper candidate identification.


679-693: Correct handling of root-as-candidate kinds.

The logic properly identifies when a Root kind call should be treated as a specific kind (e.g., createIsomorphicFn() without method chains returns IsomorphicFn kind). This enables the no-op function generation for IsomorphicFn without implementations.


819-843: Efficient candidate detection using pre-computed map.

The use of IdentifierToKinds.get() provides O(1) lookup and correctly handles identifiers shared across multiple kinds. The validation that at least one possible kind exists in validLookupKinds ensures proper filtering.


146-194: Line 149 calls resolveId(config.libName) without an importer parameter. While importer values may affect resolution in Vite's dev server, this change is safe because:

  1. Root-level package resolution: The libName values are always root package names (e.g., @tanstack/react-start), not subpaths. Conditional exports and subpath patterns can provide different implementations based on conditions, but root package imports typically don't vary by importer context.

  2. No importer-dependent exports in monorepo: The packages have conditional exports (found in packages/start-client-core, start-server-core, etc.), but they use simple "import" conditions without importer-specific logic.

  3. Tests verify intended behavior: The test suite explicitly expects resolveId to be called with just the package name during init() (line 253: expect(resolveIdMock).toHaveBeenCalledWith('@tanstack/react-start')), confirming this is the intended design.

This is a deliberate optimization to cache root-level resolutions and avoid redundant async calls.

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 (4)
packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (2)

107-139: Consider validating the single root-as-candidate assumption.

The rootAsCandidateKind field assumes only one LookupKind can have allowRootAsCandidate: true. If multiple kinds set this flag in the future, the loop at lines 131-139 will silently override earlier values, storing only the last one.

Currently this works fine since only IsomorphicFn uses this feature, but for future maintainability, consider adding validation:

🔎 Proposed validation
    // Precompute flags for candidate detection
    this.hasDirectCallKinds = false
    this.hasRootAsCandidateKinds = false
    for (const kind of options.lookupKinds) {
      const setup = LookupSetup[kind]
      if (setup.type === 'directCall') {
        this.hasDirectCallKinds = true
      } else if (setup.allowRootAsCandidate) {
+       if (this.rootAsCandidateKind !== null) {
+         throw new Error(
+           `Multiple kinds with allowRootAsCandidate detected: ${this.rootAsCandidateKind} and ${kind}. Only one kind can have this flag.`
+         )
+       }
        this.hasRootAsCandidateKinds = true
        this.rootAsCandidateKind = kind
      }
    }

715-747: Pattern matching logic is correct, minor duplication acceptable.

The updated pattern matching correctly uses the new IdentifierToKinds map to handle shared identifiers across multiple kinds. The base expression is resolved once (line 719) and reused for all checks, which is efficient.

There's some structural duplication across the ServerFn, Middleware, and IsomorphicFn blocks (lines 725-744), but each has different base-kind requirements:

  • ServerFn: accepts Root or Builder
  • Middleware: accepts Root, Builder, or Middleware
  • IsomorphicFn: accepts Root, Builder, or IsomorphicFn

Given these differences and the "Chill" review mode, the current implementation is acceptable. A helper function could reduce duplication but might not improve clarity.

packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (1)

80-85: Consider adding word boundaries to function name patterns.

The patterns for createIsomorphicFn, createServerOnlyFn, and createClientOnlyFn (lines 82-84) don't include word boundaries or parenthesis checks. While this works as a filter (false positives are acceptable), adding boundaries like /\bcreateIsomorphicFn\b/ would make the patterns more precise and reduce unnecessary file processing.

Similarly, the .handler( pattern (line 81) matches ANY .handler( call, not just those on server functions. Consider whether this could be more specific.

🔎 Proposed improvement for pattern precision
 const commonTransformCodeFilter = [
-  /\.\s*handler\(/,
-  /createIsomorphicFn/,
-  /createServerOnlyFn/,
-  /createClientOnlyFn/,
+  /\.\s*handler\s*\(/,
+  /\bcreateIsomorphicFn\b/,
+  /\bcreateServerOnlyFn\b/,
+  /\bcreateClientOnlyFn\b/,
 ]
packages/start-fn-stubs/eslint.config.js (1)

10-13: Consider removing the empty config object.

The empty plugins and rules object appears to be placeholder scaffolding. Unless this is intentionally reserved for future package-specific rules, it can be removed for cleaner configuration.

🔎 Proposed cleanup
 export default [
   ...rootConfig,
   {
     files: ['**/*.{ts,tsx}'],
   },
-  {
-    plugins: {},
-    rules: {},
-  },
 ]
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 597397c and 8081472.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • labeler-config.yml
  • packages/start-client-core/package.json
  • packages/start-client-core/src/getGlobalStartContext.ts
  • packages/start-client-core/src/getRouterInstance.ts
  • packages/start-client-core/src/getStartContextServerOnly.ts
  • packages/start-client-core/src/getStartOptions.ts
  • packages/start-client-core/src/index.tsx
  • packages/start-fn-stubs/eslint.config.js
  • packages/start-fn-stubs/package.json
  • packages/start-fn-stubs/src/index.ts
  • packages/start-fn-stubs/tsconfig.json
  • packages/start-fn-stubs/vite.config.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/types.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-plugin-core/src/types.ts
  • scripts/publish.js
💤 Files with no reviewable changes (1)
  • packages/start-plugin-core/src/create-server-fn-plugin/types.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-plugin-core/src/types.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript strict mode with extensive type safety for all code

Files:

  • packages/start-fn-stubs/vite.config.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
  • packages/start-client-core/src/index.tsx
  • packages/start-client-core/src/getGlobalStartContext.ts
  • packages/start-fn-stubs/src/index.ts
  • packages/start-client-core/src/getStartOptions.ts
  • packages/start-client-core/src/getStartContextServerOnly.ts
  • packages/start-client-core/src/getRouterInstance.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Implement ESLint rules for router best practices using the ESLint plugin router

Files:

  • packages/start-fn-stubs/vite.config.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
  • packages/start-client-core/src/index.tsx
  • packages/start-client-core/src/getGlobalStartContext.ts
  • packages/start-fn-stubs/src/index.ts
  • packages/start-client-core/src/getStartOptions.ts
  • packages/start-client-core/src/getStartContextServerOnly.ts
  • packages/start-client-core/src/getRouterInstance.ts
  • packages/start-fn-stubs/eslint.config.js
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • scripts/publish.js
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

Use workspace protocol workspace:* for internal dependencies in package.json files

Files:

  • packages/start-fn-stubs/package.json
  • packages/start-client-core/package.json
🧠 Learnings (6)
📓 Common learnings
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.
Learnt from: nlynzaad
Repo: TanStack/router PR: 5402
File: packages/router-generator/tests/generator/no-formatted-route-tree/routeTree.nonnested.snapshot.ts:19-21
Timestamp: 2025-10-08T08:11:47.088Z
Learning: Test snapshot files in the router-generator tests directory (e.g., files matching the pattern `packages/router-generator/tests/generator/**/routeTree*.snapshot.ts` or `routeTree*.snapshot.js`) should not be modified or have issues flagged, as they are fixtures used to verify the generator's output and are intentionally preserved as-is.
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript strict mode with extensive type safety for all code

Applied to files:

  • packages/start-fn-stubs/tsconfig.json
  • packages/start-fn-stubs/eslint.config.js
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.

Applied to files:

  • packages/start-fn-stubs/tsconfig.json
  • packages/start-fn-stubs/vite.config.ts
  • packages/start-fn-stubs/package.json
  • packages/start-client-core/src/index.tsx
  • packages/start-client-core/src/getGlobalStartContext.ts
  • packages/start-client-core/package.json
  • packages/start-client-core/src/getStartOptions.ts
  • packages/start-client-core/src/getStartContextServerOnly.ts
  • packages/start-client-core/src/getRouterInstance.ts
  • packages/start-fn-stubs/eslint.config.js
  • scripts/publish.js
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/package.json : Use workspace protocol `workspace:*` for internal dependencies in package.json files

Applied to files:

  • packages/start-client-core/package.json
📚 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/start-client-core/src/getRouterInstance.ts
📚 Learning: 2025-12-06T15:03:07.223Z
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{js,ts,tsx} : Implement ESLint rules for router best practices using the ESLint plugin router

Applied to files:

  • packages/start-fn-stubs/eslint.config.js
🧬 Code graph analysis (2)
packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (2)
packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (2)
  • LookupKind (35-40)
  • LookupConfig (89-93)
packages/start-plugin-core/src/types.ts (1)
  • CompileStartFrameworkOptions (3-3)
packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (3)
packages/start-plugin-core/src/create-server-fn-plugin/handleCreateMiddleware.ts (1)
  • handleCreateMiddleware (10-45)
packages/start-plugin-core/src/create-server-fn-plugin/handleCreateIsomorphicFn.ts (1)
  • handleCreateIsomorphicFn (4-46)
packages/start-plugin-core/src/create-server-fn-plugin/handleEnvOnly.ts (1)
  • handleEnvOnlyFn (10-45)
⏰ 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: Test
  • GitHub Check: Preview
🔇 Additional comments (24)
labeler-config.yml (1)

85-87: LGTM!

The new labeler configuration for the start-fn-stubs package is correctly formatted, follows the established naming convention, and is properly placed in alphabetical order. This change appropriately supports the new package introduced in this PR.

packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (6)

35-87: LGTM! Key fix for middleware expression matching.

The expansion of LookupKind to include IsomorphicFn, ServerOnlyFn, and ClientOnlyFn, combined with the IdentifierToKinds map now supporting Set<LookupKind>, directly addresses the middleware matching issue. This allows multiple kinds to share the same identifier (e.g., 'server' and 'client' are used by both Middleware and IsomorphicFn) without conflicts, which was likely causing node:* modules to leak into client builds in issue #6169.

The detection strategy separation (MethodChainSetup vs DirectCallSetup) is a clean design that supports both chained method calls and direct function calls.


142-200: LGTM! Well-structured initialization with fast-path optimization.

The init() method correctly registers stub packages (@tanstack/start-fn-stubs) and populates both the fast-path lookup (knownRootImports) and module cache. The fast-path optimization avoids async resolveId calls for common imports, improving performance.

The comment on line 192 clearly explains why init is set to null when resolvedKind is present.


441-455: LGTM! Rewrite logic correctly handles all kinds.

The updated rewrite logic properly delegates to the appropriate handler for each kind:

  • ServerFnhandleCreateServerFn
  • MiddlewarehandleCreateMiddleware
  • IsomorphicFnhandleCreateIsomorphicFn
  • ServerOnlyFn/ClientOnlyFnhandleEnvOnlyFn with kind parameter

The structure is clear and extensible.


468-501: LGTM! Candidate collection handles both patterns efficiently.

The updated collectCandidates correctly handles:

  1. Method chain pattern: .handler(), .server(), .client() (checked first, takes precedence)
  2. Direct call pattern: createServerOnlyFn(), createClientOnlyFn(), createIsomorphicFn() (checked only when needed)

The continue statement on line 480 ensures method chain matches take precedence, and the conditional check on line 488 avoids unnecessary work when direct call kinds aren't enabled. The logic also correctly handles namespace calls (e.g., TanStackStart.createServerOnlyFn()).


678-686: LGTM! Root-as-candidate resolution is correct.

The logic correctly handles kinds that allow root as candidate (currently IsomorphicFn). When a call expression resolves to 'Root' and rootAsCandidateKind is set, it returns that kind, enabling direct calls like createIsomorphicFn() without chained methods.

This works in conjunction with the precomputed rootAsCandidateKind field from the constructor (lines 131-139).


806-830: LGTM! Candidate detection correctly handles shared identifiers.

The updated isCandidateCallExpression uses the new IdentifierToKinds map to support multiple kinds sharing the same identifier. The function:

  1. Performs O(1) map lookup to get possible kinds for an identifier
  2. Checks each possible kind against the valid lookup kinds (O(k) where k is small)
  3. Returns the node if any valid kind is found

This is part of the core fix for the middleware expression matching issue, enabling proper recognition of identifiers like 'server' and 'client' that are used by both Middleware and IsomorphicFn.

packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (5)

3-3: LGTM: Type import centralized.

The type import has been properly moved to the centralized types module, improving maintainability.


8-9: LGTM: Query parameter handling is correct.

The implementation correctly strips query parameters from module IDs.


28-73: LGTM: Configuration structure is well-organized.

The refactored getLookupConfigurationsForEnv function clearly separates common and environment-specific configurations. The distinction between 'Root' kind (for builder patterns) and direct kinds (for createServerOnlyFn and createClientOnlyFn) is logical.


98-107: LGTM: Middleware pattern correctly added to client filter (key fix).

The addition of /createMiddleware\s*\(/ to the client-side transformCodeFilter (line 106) is the critical fix for issue #6169. This ensures middleware files are properly processed by the compiler for the client environment, preventing node:* imports from leaking into the client bundle.

The pattern correctly matches createMiddleware( with optional whitespace, including newlines. The inclusion of the parenthesis check (\s*\() makes the pattern more specific than the other function patterns, which is appropriate given that "middleware" is a more common word that could appear in comments or variable names.


12-26: Middleware in client set is correct and intentional.

All code in TanStack Start is isomorphic by default and runs in both server and client bundles unless explicitly constrained. During SSR, client Middleware will run on the server, which allows the client functionality to work while the client portion runs on the server. The plugin must identify Middleware declarations in client code to extract and transform them, removing server-specific code (.server() methods) from the client bundle. This prevents issues like those documented in GitHub issues where server-side code within middleware leaked into client bundles.

packages/start-client-core/package.json (1)

83-83: LGTM! Dependency correctly uses workspace protocol.

The new dependency on @tanstack/start-fn-stubs correctly uses the workspace:* protocol, consistent with other internal dependencies in this package.

packages/start-fn-stubs/tsconfig.json (1)

1-7: LGTM! TypeScript configuration is appropriate.

The configuration correctly extends the root tsconfig, sets module to esnext for ESM output, and includes the necessary source files.

packages/start-client-core/src/getGlobalStartContext.ts (1)

2-2: LGTM! Import correctly migrated to external stub package.

The import source change from a local module to @tanstack/start-fn-stubs aligns with the PR's objective to prevent server modules from leaking into client builds by extracting isomorphic function abstractions into a separate package.

packages/start-client-core/src/getStartContextServerOnly.ts (1)

2-2: LGTM! Import correctly migrated to external stub package.

The import source change from ./envOnly to @tanstack/start-fn-stubs is consistent with the refactoring to extract environment-specific function utilities into a shared package.

packages/start-client-core/src/getStartOptions.ts (1)

2-2: LGTM! Import correctly migrated to external stub package.

The import source change is consistent with the broader refactoring to use @tanstack/start-fn-stubs for isomorphic function abstractions.

packages/start-client-core/src/index.tsx (1)

5-13: LGTM! Public API correctly migrated to external stub package.

The export consolidation correctly moves all isomorphic, server-only, and client-only function utilities to @tanstack/start-fn-stubs. This centralizes the environment-specific function abstractions and aligns with the PR's objective to prevent server code from leaking into client builds.

packages/start-fn-stubs/package.json (1)

54-56: No action required. The Node.js engine requirement of >=22.12.0 aligns with all other start framework packages in the monorepo. This is a consistent, intentional design where start packages require >=22.12.0 while core router and adapter packages require >=12. The higher requirement does not inadvertently raise the project minimum since it is scoped to the start ecosystem.

scripts/publish.js (1)

147-150: LGTM! Package entry added correctly.

The new @tanstack/start-fn-stubs package entry follows the existing format and is properly positioned in the packages array.

packages/start-fn-stubs/vite.config.ts (1)

1-20: LGTM! Standard Vite/Vitest configuration.

The configuration appropriately:

  • Enables TypeScript type checking for build-time safety
  • Outputs ESM-only (suitable for a stub package transformed at build time)
  • Uses TanStack's standard Vite config
packages/start-client-core/src/getRouterInstance.ts (1)

2-2: Migration of createIsomorphicFn imports is complete and consistent.

All local imports of createIsomorphicFn across the codebase have been successfully migrated to the external @tanstack/start-fn-stubs package. Verified that production code in getRouterInstance.ts, getStartOptions.ts, and getGlobalStartContext.ts all import from @tanstack/start-fn-stubs, with no remaining relative imports outside of test files.

packages/start-fn-stubs/src/index.ts (2)

36-41: Verify transformer correctly replaces dummy implementation.

The dummy implementation relies on a build-time transformer to be replaced. The transformer is implemented in packages/start-plugin-core/src/create-server-fn-plugin/handleCreateIsomorphicFn.ts and explicitly registered for @tanstack/start-fn-stubs in the compiler's knownRootImports. It correctly extracts environment-specific implementations and throws errors if implementations are missing, with comprehensive test coverage ensuring correct transformations for both client and server environments. Given that this PR fixes issue #6169 (node:* modules leaking into client builds), ensure the transformer correctly processes these stubs in the build pipeline.


47-51: Stubs are intentionally simple; protection is provided by the compiler plugin.

Functions created using createClientOnlyFn() are replaced with a function that throws an Error on the server, and the reverse is true for createServerOnlyFn functions on the client. The stub functions in packages/start-fn-stubs/src/index.ts are correctly implemented as identity functions because they serve only for type definitions. The actual runtime protection is provided by the ServerFnCompiler in packages/start-plugin-core, which transforms calls during the build process:

  • Matching environment: Extracts and uses the inner function directly
  • Wrong environment: Replaces the call with a function that throws an error
  • Dead code elimination: Removes server-only code and unused imports (including node:* imports) from client bundles

The test snapshots confirm the compiler correctly strips server-only implementations from client builds, preventing node:* module imports from leaking to the client bundle.

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/start-plugin-core/src/create-server-fn-plugin/plugin.ts (2)

7-10: Consider edge cases in query string handling.

The cleanId function uses indexOf and substring to remove query parameters. While this handles the common case, it may not handle edge cases like:

  • Hash fragments (#)
  • Multiple ? characters in the path (though rare)
  • Encoded characters in the query string
More robust query string handling (if needed)
 function cleanId(id: string): string {
-  const queryIndex = id.indexOf('?')
-  return queryIndex === -1 ? id : id.substring(0, queryIndex)
+  // Remove query string and hash from the id
+  return id.split(/[?#]/)[0] || id
 }

80-85: The filter pattern inconsistency is intentional and partially documented, but the rationale could be clearer.

The commonTransformCodeFilter patterns are intentionally inconsistent:

  • /\.\s*handler\(/ matches only .handler( method calls
  • /createIsomorphicFn/, /createServerOnlyFn/, /createClientOnlyFn/ match these function names anywhere in the code

This design is documented in inline comments (lines 98-103) and serves a purpose: the specific .handler( pattern targets createServerFn().handler() calls, while the broader patterns ensure that any file mentioning these functions is included for transformation. However, the broader matching will include false positives (comments, strings, variable names containing these substrings), relying on the actual transform logic in the compiler to validate matches.

For maintainability, consider adding an explanation to the commonTransformCodeFilter definition clarifying why the .handler( pattern differs from the others, or standardizing the approach if possible.

packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (1)

469-498: Broad candidate collection may impact performance.

The direct call pattern detection (lines 486-493) adds ANY call expression with an identifier or member expression callee as a candidate when hasDirectCallKinds || hasRootAsCandidateKinds is true. While the comment notes "kind resolution will verify it's actually a known export," this could collect many false positive candidates in files with numerous function calls.

For example, in a file with 100 function calls, all 100 would become candidates if any direct-call kinds are enabled, even though most won't resolve to the target kinds.

Consider:

  1. Adding a fast-path check using knownRootImports before adding candidates
  2. Or profiling to verify this doesn't cause performance issues in large files
Potential optimization to reduce false positives
         // Pattern 2: Direct call pattern
         // Handles:
         // - createServerOnlyFn(), createClientOnlyFn() (direct call kinds)
         // - createIsomorphicFn() (root-as-candidate kinds)
         // - TanStackStart.createServerOnlyFn() (namespace calls)
         if (this.hasDirectCallKinds || this.hasRootAsCandidateKinds) {
-          if (
-            t.isIdentifier(binding.init.callee) ||
-            (t.isMemberExpression(binding.init.callee) &&
-              t.isIdentifier(binding.init.callee.property))
-          ) {
-            // Include as candidate - kind resolution will verify it's actually a known export
-            candidates.push(binding.init)
-          }
+          // Fast-path check: only add as candidate if the callee name could match a known export
+          let shouldAddCandidate = false
+          if (t.isIdentifier(binding.init.callee)) {
+            // Check if this identifier could be a known export from any library
+            shouldAddCandidate = true // Will be verified during resolution
+          } else if (
+            t.isMemberExpression(binding.init.callee) &&
+            t.isIdentifier(binding.init.callee.property)
+          ) {
+            shouldAddCandidate = true // Will be verified during resolution
+          }
+          
+          if (shouldAddCandidate) {
+            candidates.push(binding.init)
+          }
         }

Note: The current implementation may already be optimal since any filtering here would require similar checks to the resolution phase. Profile before optimizing.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2e368f0 and 5ff68a1.

📒 Files selected for processing (6)
  • labeler-config.yml
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-plugin-core/src/types.ts
  • packages/start-plugin-core/tests/createIsomorphicFn/createIsomorphicFn.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • labeler-config.yml
  • packages/start-plugin-core/src/plugin.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript strict mode with extensive type safety for all code

Files:

  • packages/start-plugin-core/tests/createIsomorphicFn/createIsomorphicFn.test.ts
  • packages/start-plugin-core/src/types.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Implement ESLint rules for router best practices using the ESLint plugin router

Files:

  • packages/start-plugin-core/tests/createIsomorphicFn/createIsomorphicFn.test.ts
  • packages/start-plugin-core/src/types.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
🧠 Learnings (2)
📓 Common learnings
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.
Learnt from: CR
Repo: TanStack/router PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-06T15:03:07.223Z
Learning: Applies to **/*.{js,ts,tsx} : Implement ESLint rules for router best practices using the ESLint plugin router
📚 Learning: 2025-11-02T16:16:24.898Z
Learnt from: nlynzaad
Repo: TanStack/router PR: 5732
File: packages/start-client-core/src/client/hydrateStart.ts:6-9
Timestamp: 2025-11-02T16:16:24.898Z
Learning: In packages/start-client-core/src/client/hydrateStart.ts, the `import/no-duplicates` ESLint disable is necessary for imports from `#tanstack-router-entry` and `#tanstack-start-entry` because both aliases resolve to the same placeholder file (`fake-start-entry.js`) in package.json during static analysis, even though they resolve to different files at runtime.

Applied to files:

  • packages/start-plugin-core/tests/createIsomorphicFn/createIsomorphicFn.test.ts
  • packages/start-plugin-core/src/types.ts
🧬 Code graph analysis (3)
packages/start-plugin-core/src/types.ts (1)
packages/start-plugin-core/src/schema.ts (1)
  • TanStackStartOutputConfig (207-207)
packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (3)
packages/start-plugin-core/src/create-server-fn-plugin/handleCreateMiddleware.ts (1)
  • handleCreateMiddleware (10-45)
packages/start-plugin-core/src/create-server-fn-plugin/handleCreateIsomorphicFn.ts (1)
  • handleCreateIsomorphicFn (4-46)
packages/start-plugin-core/src/create-server-fn-plugin/handleEnvOnly.ts (1)
  • handleEnvOnlyFn (10-45)
packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (2)
packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (2)
  • LookupKind (35-40)
  • LookupConfig (89-93)
packages/start-plugin-core/src/types.ts (1)
  • CompileStartFrameworkOptions (3-3)
⏰ 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). (1)
  • GitHub Check: Test
🔇 Additional comments (7)
packages/start-plugin-core/tests/createIsomorphicFn/createIsomorphicFn.test.ts (1)

1-120: LGTM! Test refactoring properly implements the new async compiler API.

The test file correctly migrates from the previous factory pattern to the new ServerFnCompiler class-based approach. The async patterns, error handling with rejects.toThrowError(), and console spy implementation are all properly structured.

packages/start-plugin-core/src/types.ts (1)

1-34: LGTM! Clean type definitions with proper separation of concerns.

The type definitions are well-structured and correctly centralize the public API types. The interfaces are clear and properly typed.

packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (5)

35-69: Well-designed multi-kind detection strategy.

The expanded LookupKind types and dual detection strategies (method chain vs direct call) are well-structured. The allowRootAsCandidate flag for IsomorphicFn (line 65) correctly handles the case where createIsomorphicFn() is called without .client() or .server(), resulting in a no-op function.

The design allowing multiple kinds to share identifiers (e.g., 'server' and 'client' used by both Middleware and IsomorphicFn) is correctly handled by using Set<LookupKind> in the IdentifierToKinds map.


71-87: Efficient pre-computation of identifier-to-kinds map.

The pre-computed IdentifierToKinds map enables O(1) lookup during candidate detection and correctly handles multiple kinds sharing the same identifier name. The iteration and Set building logic is correct.


705-737: Correct handling of shared identifiers across multiple kinds.

The logic for resolving method chain patterns that share identifiers (lines 706-736) correctly:

  1. Uses IdentifierToKinds.get(prop) to find all possible kinds for an identifier (O(1) lookup)
  2. Resolves the base expression once and reuses it for all pattern checks (avoiding redundant work)
  3. Checks each possible kind against the valid lookup kinds
  4. Returns the first matching kind

The specific kind checks (lines 715-735) properly validate the base kind before returning the target kind.


139-197: Verify async error handling in initialization.

The init() method performs async operations in Promise.all() (lines 151-194). If resolveId fails for any configuration, it will throw an error (line 164), which correctly propagates. However, ensure that partial initialization doesn't leave the compiler in an inconsistent state.

The initialized flag is set to true only after all configurations are processed (line 196), which is correct. But if an error occurs:

  • Line 320 will call init() again on the next compile() call
  • The moduleCache may have partial entries from the failed initialization

Consider testing error handling during initialization:

// Test case suggestion
test('should handle initialization errors gracefully', async () => {
  const compiler = new ServerFnCompiler({
    env: 'client',
    directive: 'use server',
    lookupKinds: new Set(['IsomorphicFn']),
    lookupConfigurations: [
      {
        libName: 'non-existent-package',
        rootExport: 'createIsomorphicFn',
        kind: 'IsomorphicFn',
      },
    ],
    loadModule: async () => {},
    resolveId: async (id) => {
      if (id === 'non-existent-package') {
        return null // Simulate package not found
      }
      return id
    },
  })

  await expect(
    compiler.compile({
      code: 'const x = 1',
      id: 'test.ts',
      isProviderFile: false,
    })
  ).rejects.toThrow('could not resolve "non-existent-package"')
  
  // Verify compiler can recover if configuration is fixed
  // (This may not be the intended behavior, just verifying)
})

139-149: The @tanstack/start-fn-stubs package exists in the monorepo at packages/start-fn-stubs/ with version 1.142.8 and is properly configured with exports. The three hardcoded functions—createIsomorphicFn, createServerOnlyFn, and createClientOnlyFn—are all correctly exported and match the Kind type union defined in the compiler. The registration in the init() method is accurate.

@schiller-manuel schiller-manuel changed the title fix: correctly match middleware expressions in compiler plugin filter fix: compiler plugin Dec 22, 2025
@schiller-manuel schiller-manuel merged commit 770a265 into main Dec 22, 2025
6 checks passed
@schiller-manuel schiller-manuel deleted the fix-6169 branch December 22, 2025 13:57
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.

Start 1.142.2 + Bun leaks node:* modules in build for client

2 participants