Skip to content

Conversation

@schiller-manuel
Copy link
Contributor

@schiller-manuel schiller-manuel commented Oct 17, 2025

Summary by CodeRabbit

  • Documentation

    • Updated production-build guidance for server function ID generation; recommends deterministic inputs for stable IDs and marks customization as experimental.
  • Refactor

    • Unified and simplified server-function plugin and runtime flows with a smaller public API surface and a single client/SSR path.
    • Switched to an ESM-first distribution and moved manifest resolution to an async ID-based resolver.
  • Breaking Changes

    • Removed several legacy public exports and CommonJS entry points; plugin API and manifest access patterns have changed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 17, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Refactors server-functions and directive tooling to use function-ID-centric async resolution, propagates extractedFilename into function ID generation and replacer options, replaces a static manifest with an async getServerFnById virtual import, consolidates plugin APIs, and updates docs to focus on production-build ID behavior.

Changes

Cohort / File(s) Summary
Documentation
docs/start/framework/react/guide/server-functions.md
Focus docs on production build Function ID generation; rename subsection; remove dev-specific defaults; mark customization experimental; example uses SHA-1; recommend deterministic inputs (filename + functionName).
Directive functions plugin
packages/directive-functions-plugin/src/compilers.ts, packages/directive-functions-plugin/src/index.ts
Add extractedFilename to GenerateFunctionIdFn and ReplacerFn opts and propagate it through compilation; remove directiveRx parameter from transformCode; remove exported TanStackDirectiveFunctionsPlugin and change directive filtering flow.
Server functions plugin
packages/server-functions-plugin/src/index.ts, packages/server-functions-plugin/vite.config.ts, packages/server-functions-plugin/README.md, packages/server-functions-plugin/package.json
Consolidate public API into TanStackServerFnPlugin returning Plugin[]; remove legacy ssr env API; change generateFunctionId behavior (dev vs prod deterministic hashing); introduce resolved manifest import ID and runtime getServerFnById; add cjs: false to vite config; drop CJS/require export.
Start plugin integration
packages/start-plugin-core/src/plugin.ts
Replace use of TanStackServerFnPluginEnv with TanStackServerFnPlugin and pass through generateFunctionId from start options.
Start server core — manifest & runtime
packages/start-server-core/package.json, packages/start-server-core/src/fake-start-server-fn-manifest.ts, packages/start-server-core/src/getServerFnById.ts, packages/start-server-core/src/loadVirtualModule.ts, packages/start-server-core/src/tanstack-start.d.ts, packages/start-server-core/src/virtual-modules.ts, packages/start-server-core/vite.config.ts, packages/start-server-core/src/index.tsx
Replace static virtual manifest tanstack-start-server-fn-manifest:v with async #tanstack-start-server-fn-manifest; add package imports mapping; add fake manifest file and re-export getServerFnById from the manifest; remove local virtual-module case for server manifest; remove handleServerAction export.

Sequence Diagram(s)

sequenceDiagram
    participant Compiler
    participant DirectivePlugin
    participant ServerPlugin
    participant Manifest as "#tanstack-start-server-fn-manifest"

    Note over Compiler,DirectivePlugin: Build-time compilation
    Compiler->>DirectivePlugin: provide extractedFilename + function metadata
    DirectivePlugin->>ServerPlugin: generateFunctionId(opts including extractedFilename)
    ServerPlugin-->>DirectivePlugin: return functionId

    rect rgb(225,245,235)
    Note over ServerPlugin,Manifest: Runtime resolution (async)
    ServerPlugin->>Manifest: getServerFnById(functionId)
    Manifest-->>ServerPlugin: async ServerFn (or throw)
    ServerPlugin->>ServerPlugin: invoke ServerFn(...)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • birkskyum

Poem

🐰 I nibble hashes late at night,

filenames snug, IDs take flight,
extracted bits rolled up just right,
manifests now async and light,
hop—server functions gleam so bright. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title Check ⚠️ Warning The pull request title "fix: no invalidation for server functions in dev" is partially related to the changeset but significantly undersells the scope and primary intent of the changes. While the changeset does include modifications to development behavior and documentation updates about dev vs production builds, the core changes represent a major architectural refactoring: restructuring the server functions plugin API (renaming functions, removing public exports, consolidating plugins), overhauling the virtual module system, refactoring the manifest approach, removing CommonJS support, and propagating new parameters throughout the compilation flow. The title focuses narrowly on a dev-time invalidation issue, which does not capture the primary refactoring work. A developer scanning the git history based on this title would expect a targeted bug fix rather than a substantial architectural overhaul, making the title misleading about the PR's true scope and impact. Consider revising the title to reflect the primary architectural changes, such as "refactor: restructure server functions plugin API and manifest system" or "refactor: consolidate server plugin architecture and update manifest handling". This would better convey the main intent of the changeset and align with the principle that a title should help teammates understand the primary change by scanning history.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 618d858 and dbcde6c.

📒 Files selected for processing (1)
  • packages/start-server-core/src/tanstack-start.d.ts (1 hunks)

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

@nx-cloud
Copy link

nx-cloud bot commented Oct 17, 2025

View your CI Pipeline Execution ↗ for commit dbcde6c

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

☁️ Nx Cloud last updated this comment at 2025-10-17 18:13:01 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 17, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/directive-functions-plugin

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/zod-adapter

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

commit: 618d858

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: 7

Caution

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

⚠️ Outside diff range comments (3)
packages/directive-functions-plugin/src/compilers.ts (2)

482-491: Sanitize extractedFilename to avoid dev invalidation (strip volatile query params).

Keeping existing QueryString entries propagates Vite/Dev HMR params (e.g., t=, v=), defeating “no invalidation in dev.” Build extractedFilename with only the directive param.

Apply:

-    const [baseFilename, ..._searchParams] = opts.filename.split('?') as [
-      string,
-      ...Array<string>,
-    ]
-    const searchParams = new URLSearchParams(_searchParams.join('&'))
-    searchParams.set(opts.directiveSplitParam, '')
-    const extractedFilename = `${baseFilename}?${searchParams.toString()}`
+    const [baseFilename] = opts.filename.split('?') as [string, ...Array<string>]
+    const searchParams = new URLSearchParams()
+    // Only include our stable directive split param to keep IDs/chunks stable in dev
+    searchParams.set(opts.directiveSplitParam, '')
+    const extractedFilename = `${baseFilename}?${searchParams.toString()}`

501-507: Bug: isSourceFn is always true.

!!opts.directiveSplitParam is a constant truthy string; replacers can’t distinguish source vs split files. Pass the actual context.

Apply:

-      const replacer = opts.replacer({
+      const replacer = opts.replacer({
         fn: '$$fn$$',
         extractedFilename,
         filename: opts.filename,
         functionId,
-        isSourceFn: !!opts.directiveSplitParam,
+        // Source file if we're NOT in the split-param file
+        isSourceFn: !compileDirectiveOpts.isDirectiveSplitParam,
       })
packages/server-functions-plugin/src/index.ts (1)

154-160: Fix stale error message.

Message references the old API name.

-            `TanStackServerFnPluginEnv: environment "${opts.server.envName}" not found`,
+            `TanStackServerFnPlugin: environment "${opts.server.envName}" not found`,
🧹 Nitpick comments (7)
packages/directive-functions-plugin/src/compilers.ts (2)

529-530: Chunk name derivation: consider using relative path for cross‑platform stability.

Optional: derive from relative path to avoid root substring quirks.

Apply:

-      chunkName: fileNameToChunkName(opts.root, extractedFilename),
+      chunkName: fileNameToChunkName(opts.root, path.relative(opts.root, extractedFilename)),

413-421: Redundant add to functionNameSet.

functionNameSet.add(functionName) is called twice; keep one.

Apply:

-    functionNameSet.add(functionName)
-
     while (programPath.scope.hasBinding(functionName)) {
       functionName = incrementFunctionNameVersion(functionName)
       programPath.scope.crawl()
     }
 
     functionNameSet.add(functionName)
packages/server-functions-plugin/src/index.ts (3)

55-66: Clarify dev behavior: no invalidation after server env is ready.

Early-returning when serverDevEnv exists freezes directiveFnsById post‑startup (dev uses on‑the‑fly import via encoded IDs). Add a brief comment so future edits don’t reintroduce dev invalidation.

Apply:

-  const onDirectiveFnsById = (d: Record<string, DirectiveFn>) => {
-    if (serverDevEnv) {
-      return
-    }
+  const onDirectiveFnsById = (d: Record<string, DirectiveFn>) => {
+    // In dev, once the server environment is ready we don't update this map anymore:
+    // dev resolution uses encoded file/export IDs and does not rely on this manifest.
+    if (serverDevEnv) return

161-166: Avoid regex from variable input; anchor/escape the pattern.

Even if the IDs are internal, use an anchored/escaped regex to prevent accidental metacharacter matches and silence tooling warnings.

-      resolveId: {
-        filter: { id: new RegExp(opts.manifestVirtualImportId) },
+      resolveId: {
+        filter: { id: new RegExp(`^${escapeRegExp(opts.manifestVirtualImportId)}$`) },
         handler() {
           return resolvedManifestVirtualImportId
         },
       },
-      load: {
-        filter: { id: new RegExp(resolvedManifestVirtualImportId) },
+      load: {
+        filter: { id: new RegExp(`^${escapeRegExp(resolvedManifestVirtualImportId)}$`) },
         handler() {

Add helper near resolveViteId:

 function resolveViteId(id: string) {
   return `\0${id}`
 }
+
+function escapeRegExp(s: string) {
+  return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+}

Also applies to: 168-169


174-185: Harden dev decode path with try/catch.

Guard against malformed IDs to surface actionable errors instead of uncaught exceptions.

           if (this.environment.mode !== 'build') {
             const mod = `
             export async function getServerFnById(id) {
-              const decoded = Buffer.from(id, 'base64url').toString('utf8')
-              const devServerFn = JSON.parse(decoded)
+              let devServerFn
+              try {
+                const decoded = Buffer.from(id, 'base64url').toString('utf8')
+                devServerFn = JSON.parse(decoded)
+              } catch (e) {
+                throw new Error('Invalid dev server function id: ' + id)
+              }
               const mod = await import(/* @vite-ignore */ devServerFn.file)
               return mod[devServerFn.export]
             }
             `
             return mod
           }
packages/start-server-core/vite.config.ts (1)

26-31: Remove duplicate external entry.

#tanstack-start-server-fn-manifest is already included via Object.values(VIRTUAL_MODULES). Drop the extra literal.

     externalDeps: [
       ...Object.values(VIRTUAL_MODULES),
       '#tanstack-start-entry',
       '#tanstack-router-entry',
-      '#tanstack-start-server-fn-manifest',
     ],
docs/start/framework/react/guide/server-functions.md (1)

235-235: Consider clarifying hash algorithm specifics.

The documentation mentions "SHA256 hashes" but should clarify whether this applies to dev and production or production only, especially given the PR's focus on dev mode behavior.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a0a321 and a69f375.

📒 Files selected for processing (14)
  • docs/start/framework/react/guide/server-functions.md (2 hunks)
  • packages/directive-functions-plugin/src/compilers.ts (2 hunks)
  • packages/directive-functions-plugin/src/index.ts (0 hunks)
  • packages/server-functions-plugin/README.md (2 hunks)
  • packages/server-functions-plugin/src/index.ts (4 hunks)
  • packages/server-functions-plugin/vite.config.ts (1 hunks)
  • packages/start-plugin-core/src/plugin.ts (2 hunks)
  • packages/start-server-core/package.json (1 hunks)
  • packages/start-server-core/src/fake-start-server-fn-manifest.ts (1 hunks)
  • packages/start-server-core/src/getServerFnById.ts (1 hunks)
  • packages/start-server-core/src/loadVirtualModule.ts (0 hunks)
  • packages/start-server-core/src/tanstack-start.d.ts (1 hunks)
  • packages/start-server-core/src/virtual-modules.ts (1 hunks)
  • packages/start-server-core/vite.config.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/start-server-core/src/loadVirtualModule.ts
  • packages/directive-functions-plugin/src/index.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/start-server-core/vite.config.ts
  • packages/start-server-core/src/fake-start-server-fn-manifest.ts
  • packages/start-server-core/src/tanstack-start.d.ts
  • packages/directive-functions-plugin/src/compilers.ts
  • packages/start-server-core/src/getServerFnById.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/server-functions-plugin/vite.config.ts
  • packages/start-server-core/src/virtual-modules.ts
  • packages/server-functions-plugin/src/index.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-server-core/vite.config.ts
  • packages/start-server-core/src/fake-start-server-fn-manifest.ts
  • packages/start-server-core/src/tanstack-start.d.ts
  • packages/start-server-core/src/getServerFnById.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-server-core/package.json
  • packages/start-server-core/src/virtual-modules.ts
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Use internal docs links relative to the docs/ folder (e.g., ./guide/data-loading)

Files:

  • docs/start/framework/react/guide/server-functions.md
docs/{router,start}/**

📄 CodeRabbit inference engine (AGENTS.md)

Place router docs under docs/router/ and start framework docs under docs/start/

Files:

  • docs/start/framework/react/guide/server-functions.md
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • packages/start-server-core/package.json
🧬 Code graph analysis (4)
packages/start-server-core/vite.config.ts (1)
packages/start-server-core/src/virtual-modules.ts (1)
  • VIRTUAL_MODULES (3-7)
packages/start-server-core/src/tanstack-start.d.ts (1)
packages/start-server-core/src/fake-start-server-fn-manifest.ts (1)
  • getServerFnById (1-1)
packages/start-plugin-core/src/plugin.ts (3)
packages/start-plugin-core/src/start-compiler-plugin/plugin.ts (1)
  • startCompilerPlugin (39-116)
packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (1)
  • createServerFnPlugin (42-175)
packages/server-functions-plugin/src/index.ts (1)
  • TanStackServerFnPlugin (37-227)
packages/server-functions-plugin/src/index.ts (1)
packages/directive-functions-plugin/src/compilers.ts (2)
  • GenerateFunctionIdFn (26-30)
  • DirectiveFn (12-19)
🪛 ast-grep (0.39.6)
packages/server-functions-plugin/src/index.ts

[warning] 167-167: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(resolvedManifestVirtualImportId)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

🪛 Biome (2.1.2)
packages/start-server-core/src/tanstack-start.d.ts

[error] 15-15: 'async' modifier cannot be used in an ambient context.

(parse)

⏰ 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 (10)
packages/directive-functions-plugin/src/compilers.ts (1)

494-499: FunctionId inputs: prefer sanitized extractedFilename for stability.

Now that extractedFilename is available, ensure your GenerateFunctionIdFn prefers it (sanitized as above) when differentiating source vs split files; keep filename for machine‑independent context. No action here if your implementation already does this.

If unsure, please confirm the generator logic.

packages/server-functions-plugin/src/index.ts (1)

71-116: Deterministic IDs in build, encoded file/export in dev — LGTM.

Good split: base64url(file/export) in dev, SHA‑256 (or user override) in build with de‑dup suffixing.

If you expect generateFunctionId to run outside Node (e.g. non‑Node build adapters), confirm Buffer/crypto availability. Otherwise, all good.

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

330-330: Plugin order note — LGTM.

Compiler before ServerFnPlugin ensures IDs/compilation are ready. Keep as‑is.


333-350: ServerFn plugin wiring confirmed — signature correctly excludes extractedFilename.

Verification confirms the code at lines 333-350 properly passes startPluginOpts?.serverFns?.generateFunctionId conforming to the new signature. The internal implementation (packages/server-functions-plugin/src/index.ts) explicitly types the user callback as GenerateFunctionIdFnOptional, which omits extractedFilename. User implementations only receive {filename, functionName} and the e2e test validates this usage pattern correctly.

packages/start-server-core/vite.config.ts (1)

21-25: Include fake manifest entry — LGTM.

Ensures the stub is built and published.

packages/start-server-core/src/virtual-modules.ts (1)

5-5: LGTM! Virtual module ID migration is consistent.

The migration from 'tanstack-start-server-fn-manifest:v' to '#tanstack-start-server-fn-manifest' is correctly applied to both the constant definition and the type mapping.

Also applies to: 11-11

packages/start-server-core/src/getServerFnById.ts (1)

1-1: LGTM! Delegation to virtual module aligns with the refactor.

The re-export delegates server function resolution to the virtual module, consistent with the PR's unified, function-ID-centric approach.

packages/server-functions-plugin/README.md (2)

32-56: LGTM! Clear client RPC implementation example.

The example demonstrates the expected pattern for implementing client-side runtime wrappers using fetch.


63-74: LGTM! Manifest usage updated to async function-based approach.

The example correctly demonstrates the new getServerFnById API, replacing the static manifest lookup pattern.

docs/start/framework/react/guide/server-functions.md (1)

231-231: Verify production-only focus aligns with PR scope.

The section title now specifies "for production build", but the PR title mentions dev mode ("no invalidation for server functions in dev"). Does function ID generation work differently in dev vs. production, or should this section cover both environments?

Comment on lines 258 to 264
generateFunctionId: ({ filename, functionName }) => {
// Return a custom ID string. If you return undefined, the default is used.
// For example, always hash (even in dev):
// return createHash('sha256').update(`${filename}--${functionName}`).digest('hex')
return crypto
.createHash('sha1')
.update(`${filename}--${functionName}`)
.digest('hex')
return undefined
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix unreachable code and hash algorithm inconsistency.

Two issues:

  1. Unreachable code: Line 264 has return undefined after line 263 already returns the hash, making it unreachable.
  2. Hash algorithm mismatch: Line 235 states defaults use "SHA256 hashes", but the example uses sha1 (line 261).

Apply this diff:

         generateFunctionId: ({ filename, functionName }) => {
           // Return a custom ID string. If you return undefined, the default is used.
-          return crypto
-            .createHash('sha1')
+          return crypto
+            .createHash('sha256')
             .update(`${filename}--${functionName}`)
             .digest('hex')
-          return undefined
         },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
generateFunctionId: ({ filename, functionName }) => {
// Return a custom ID string. If you return undefined, the default is used.
// For example, always hash (even in dev):
// return createHash('sha256').update(`${filename}--${functionName}`).digest('hex')
return crypto
.createHash('sha1')
.update(`${filename}--${functionName}`)
.digest('hex')
return undefined
generateFunctionId: ({ filename, functionName }) => {
// Return a custom ID string. If you return undefined, the default is used.
return crypto
.createHash('sha256')
.update(`${filename}--${functionName}`)
.digest('hex')
},
🤖 Prompt for AI Agents
In docs/start/framework/react/guide/server-functions.md around lines 258 to 264,
the example for generateFunctionId contains an unreachable `return undefined`
after returning a hash and uses `sha1` while the surrounding text says SHA256;
remove the trailing `return undefined` and replace `sha1` with `sha256` so the
function consistently returns a SHA-256 hex digest (or if you intended to allow
undefined, make the return conditional instead).

Comment on lines 26 to 31
export type GenerateFunctionIdFn = (opts: {
filename: string
functionName: string
extractedFilename: string
}) => string

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Threading extractedFilename into ID generation looks good; verify call sites updated.

The new GenerateFunctionIdFn signature adds extractedFilename. Ensure all implementations and usages across the repo are updated to avoid type drift.

Run to locate and validate implementors:


🏁 Script executed:

#!/bin/bash
# Find typedef and implementors/usages
rg -nP --type=ts 'type\s+GenerateFunctionIdFn\b|interface\s+GenerateFunctionIdFn\b'
rg -nP --type=ts '\bgenerateFunctionId\s*:\s*\(' -C3
rg -nP --type=ts '\bgenerateFunctionId\(\s*\{[^}]*\}\s*\)' -C2

Length of output: 712



🏁 Script executed:

#!/bin/bash
# Find all generateFunctionId call sites and implementations more broadly
echo "=== All generateFunctionId property definitions ==="
rg -nP --type=ts 'generateFunctionId\s*[:=]' -C5

echo ""
echo "=== All places where generateFunctionId is called ==="
rg -nP --type=ts 'generateFunctionId\s*\(' -C3

echo ""
echo "=== Files containing generateFunctionId ==="
fd --type f -e ts -e tsx | xargs rg -l 'generateFunctionId'

Length of output: 9970


🏁 Script executed:

#!/bin/bash
# Check the schema definition for generateFunctionId
echo "=== Schema definition for generateFunctionId ==="
sed -n '162,175p' packages/start-plugin-core/src/schema.ts

echo ""
echo "=== Full call site in server-functions-plugin ==="
sed -n '90,102p' packages/server-functions-plugin/src/index.ts

echo ""
echo "=== Full call site in directive-functions-plugin ==="
sed -n '490,500p' packages/directive-functions-plugin/src/compilers.ts

Length of output: 1387


Type signature mismatch: GenerateFunctionIdFn requires extractedFilename but not all call sites provide it.

The reviewed change adds extractedFilename as a required parameter to GenerateFunctionIdFn, but this creates a type safety violation:

  • directive-functions-plugin (compilers.ts:494) passes {filename, functionName, extractedFilename}
  • server-functions-plugin (index.ts:95) passes only {filename, functionName}
  • Schema (start-plugin-core/src/schema.ts:162) defines args as {filename, functionName} only—does not include extractedFilename

In strict TypeScript mode, the server-functions-plugin call will fail type checking. Either:

  1. Make extractedFilename optional in GenerateFunctionIdFn and update the schema accordingly, or
  2. Update server-functions-plugin to pass extractedFilename and update the schema to include it
🤖 Prompt for AI Agents
In packages/directive-functions-plugin/src/compilers.ts around lines 26–31, the
new GenerateFunctionIdFn requires extractedFilename but other call sites and the
schema only provide filename and functionName; make extractedFilename optional
on the GenerateFunctionIdFn type (e.g., extractedFilename?: string) and update
any related schema/type definitions (start-plugin-core/src/schema.ts) to declare
extractedFilename as optional too, then adjust callers (server-functions-plugin
index.ts and any others) to either pass extractedFilename when available or rely
on the optional property so TypeScript strict mode typechecks successfully.

Comment on lines 21 to 26
server: {
getRuntimeCode: () =>
`import { createServerRpc } from '@tanstack/react-start/server-runtime'`,
replacer: (opts) =>
`createServerRpc(${JSON.stringify(opts.functionId)}, ${opts.fn})`,
`import { createServerRpc } from '@tanstack/${corePluginOpts.framework}-start/server-rpc'`,
replacer: (d) => `createServerRpc('${d.functionId}', ${d.fn})`,
envName: 'ssr',
},
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify envName value for server configuration.

The server configuration has envName: 'ssr' (line 25), which seems inconsistent. Should this be 'server' instead? The client has envName: 'client', so having the server marked as 'ssr' appears counterintuitive.


🏁 Script executed:

#!/bin/bash
# Search for envName usage patterns in server-functions-plugin and related packages
rg -nP --type=ts "envName:\s*['\"]" -A2 -B2

Length of output: 665


I found that directive-functions-plugin uses envName: 'server' for the server configuration. Let me verify what the actual implementation in server-functions-plugin uses:



🏁 Script executed:

#!/bin/bash
# Search for envName in server-functions-plugin source code (not README)
rg -n "envName" packages/server-functions-plugin/src --type=ts

Length of output: 816


🏁 Script executed:

#!/bin/bash
# Also check the actual server-functions-plugin README to see full context
head -30 packages/server-functions-plugin/README.md

Length of output: 990


Update README.md to use correct envName value for server configuration.

The README documentation incorrectly shows envName: 'ssr' on line 25, but the actual implementation defaults to envName: 'server' (source: src/index.ts:48). This inconsistency should be fixed in the README to match the implementation and maintain consistency with the client configuration pattern (envName: 'client').

Update line 25 from:

    envName: 'ssr',

to:

    envName: 'server',
🤖 Prompt for AI Agents
In packages/server-functions-plugin/README.md around lines 21 to 26, the README
shows envName: 'ssr' which is inconsistent with the implementation
(src/index.ts:48) that defaults to 'server'; update the README to use envName:
'server' so it matches the code and the client pattern.

Comment on lines +58 to +62
"imports": {
"#tanstack-start-server-fn-manifest": {
"default": "./dist/esm/fake-start-server-fn-manifest.js"
}
},
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add a types condition for the import-mapped module.

TypeScript under NodeNext resolves "imports" with conditions. Provide a "types" target so consumers get declarations for #tanstack-start-server-fn-manifest.

Apply:

   "imports": {
     "#tanstack-start-server-fn-manifest": {
-      "default": "./dist/esm/fake-start-server-fn-manifest.js"
+      "types": "./dist/esm/fake-start-server-fn-manifest.d.ts",
+      "default": "./dist/esm/fake-start-server-fn-manifest.js"
     }
   },
🤖 Prompt for AI Agents
In packages/start-server-core/package.json around lines 58-62, the "imports"
entry for "#tanstack-start-server-fn-manifest" lacks a "types" condition so
TypeScript consumers won't get declaration files; add a "types" condition
alongside "default" pointing to the package's .d.ts declaration for that module
(for example "./dist/esm/fake-start-server-fn-manifest.d.ts"), ensuring the
"types" entry is a sibling of "default" under the import mapping so
NodeNext/TypeScript can resolve types correctly.

@@ -0,0 +1 @@
export function getServerFnById() {}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Make the stub explicit and typed.

Throw with a clear message so accidental runtime usage is obvious; provide a typed async signature.

-export function getServerFnById() {}
+/**
+ * Stub implementation used during package build and as a fallback.
+ * The real implementation is provided by the Start plugin via a virtual module.
+ */
+export async function getServerFnById(_id: string): Promise<unknown> {
+  throw new Error(
+    '#tanstack-start-server-fn-manifest is a virtual module. Ensure the Start Vite plugin is active.',
+  )
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function getServerFnById() {}
/**
* Stub implementation used during package build and as a fallback.
* The real implementation is provided by the Start plugin via a virtual module.
*/
export async function getServerFnById(_id: string): Promise<unknown> {
throw new Error(
'#tanstack-start-server-fn-manifest is a virtual module. Ensure the Start Vite plugin is active.',
)
}
🤖 Prompt for AI Agents
packages/start-server-core/src/fake-start-server-fn-manifest.ts lines 1-1:
current export is an untyped empty stub; replace it with an explicit async typed
signature (for example async function getServerFnById<T = unknown>(id: string):
Promise<T>) and make the body immediately throw a clear runtime error (e.g.
throw new Error("getServerFnById is a stub and must not be called at runtime;
use the real implementation")). Ensure the function is exported and its
signature reflects asynchronous usage so accidental runtime invocation fails
fast with a clear message.

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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a69f375 and 618d858.

📒 Files selected for processing (3)
  • packages/server-functions-plugin/package.json (0 hunks)
  • packages/start-server-core/src/index.tsx (0 hunks)
  • packages/start-server-core/src/virtual-modules.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • packages/start-server-core/src/index.tsx
  • packages/server-functions-plugin/package.json
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/start-server-core/src/virtual-modules.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-server-core/src/virtual-modules.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test
🔇 Additional comments (1)
packages/start-server-core/src/virtual-modules.ts (1)

5-5: No action required. The migration is complete and correct.

The change from the virtual module protocol 'tanstack-start-server-fn-manifest:v' to the Node.js subpath import '#tanstack-start-server-fn-manifest' has been fully implemented:

  • All references to the old identifier have been updated (none found)
  • The subpath import is properly configured in package.json
  • The removal of serverFnManifest from the VirtualModules type is intentional: it's kept in the VIRTUAL_MODULES constant as a configuration reference but no longer loaded through loadVirtualModule() since it's now resolved as a subpath import
  • External packages like start-plugin-core correctly use the constant value for build configuration

Comment on lines 9 to 12
export type VirtualModules = {
[VIRTUAL_MODULES.startManifest]: typeof import('tanstack-start-manifest:v')
[VIRTUAL_MODULES.serverFnManifest]: typeof import('tanstack-start-server-fn-manifest:v')
[VIRTUAL_MODULES.injectedHeadScripts]: typeof import('tanstack-start-injected-head-scripts:v')
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify intentional exclusion from VirtualModules type.

The serverFnManifest remains in the VIRTUAL_MODULES constant but was removed from the VirtualModules type. This creates an apparent inconsistency. Confirm this exclusion is intentional and that no code relies on VirtualModules[VIRTUAL_MODULES.serverFnManifest] type mapping.


🏁 Script executed:

#!/bin/bash
# Description: Check if any code expects the VirtualModules type to include serverFnManifest.

# Search for usage of VirtualModules type with serverFnManifest
echo "=== Checking for VirtualModules type usage with serverFnManifest ==="
rg -nP 'VirtualModules\[.*serverFnManifest.*\]' -A 2 -B 2

# Search for direct imports or type references that might expect this mapping
echo -e "\n=== Checking for type references to VirtualModules ==="
ast-grep --pattern 'type $_ = VirtualModules[$_]'

Length of output: 124


🏁 Script executed:

#!/bin/bash
# Display VIRTUAL_MODULES constant definition
echo "=== VIRTUAL_MODULES constant definition ==="
rg -nP -A 10 'export const VIRTUAL_MODULES|const VIRTUAL_MODULES|VIRTUAL_MODULES\s*=' --type ts --type tsx packages/start-server-core/src/

# Check for any serverFnManifest usage in codebase
echo -e "\n=== serverFnManifest usage across codebase ==="
rg -nP 'serverFnManifest' -C 3 --type ts --type tsx packages/

# Check git history if available for this file
echo -e "\n=== Recent changes to virtual-modules.ts ==="
git log --oneline -10 packages/start-server-core/src/virtual-modules.ts 2>/dev/null || echo "Git history not available"

Length of output: 345


🏁 Script executed:

#!/bin/bash
# Display the actual virtual-modules.ts file to see full context
echo "=== Full virtual-modules.ts file ==="
cat packages/start-server-core/src/virtual-modules.ts

# Check for serverFnManifest usage without type flags
echo -e "\n=== serverFnManifest usage across codebase ==="
rg -nP 'serverFnManifest' -C 2 -g 'packages/**/*.{ts,js}'

# Check git diff for the specific commit about server functions
echo -e "\n=== Git diff for 'fix: no invalidation for server functions in dev' ==="
git show a69f375c -- packages/start-server-core/src/virtual-modules.ts 2>/dev/null | head -50

Length of output: 3381



🏁 Script executed:

#!/bin/bash
# Check what the "fix build" commit changed in virtual-modules.ts
echo "=== Git diff for 'fix build' commit (618d8580) ==="
git show 618d8580 -- packages/start-server-core/src/virtual-modules.ts 2>/dev/null

# Also check the full diff to see all changes in that commit
echo -e "\n=== Commit message and stats ==="
git show 618d8580 --stat 2>/dev/null | head -30

Length of output: 1220


🏁 Script executed:

#!/bin/bash
# Check how VirtualModules type is actually used - is it indexed?
echo "=== VirtualModules type indexing usage ==="
rg -nP 'VirtualModules\s*\[' -C 3 -g 'packages/**/*.{ts,js}'

# Check if any code uses the type to access the manifest property
echo -e "\n=== Accessing VIRTUAL_MODULES.serverFnManifest at runtime ==="
rg -nP 'VIRTUAL_MODULES\.serverFnManifest' -C 2 -g 'packages/**/*.{ts,js}' | head -30

# Check if there are any type errors or comments about this
echo -e "\n=== Comments mentioning serverFnManifest or virtual modules type ==="
rg -nP '(TODO|FIXME|BUG|NOTE).*virtual|serverFn' -C 2 -g 'packages/**/*.{ts,js}' | head -30

Length of output: 4694


Restore missing serverFnManifest entry to VirtualModules type.

The serverFnManifest remains in the VIRTUAL_MODULES constant but was removed from the VirtualModules type. This creates a type safety issue: the loadVirtualModule function (in packages/start-server-core/src/loadVirtualModule.ts) expects id: TId extends keyof VirtualModules, so passing VIRTUAL_MODULES.serverFnManifest to it would cause a TypeScript error.

The removal appears unintentional (commit 618d8580 "fix build" lacks explanation). Restore the type mapping to match the constant and maintain type safety for the generic loadVirtualModule function.

🤖 Prompt for AI Agents
In packages/start-server-core/src/virtual-modules.ts around lines 9 to 12, the
VirtualModules type is missing the mapping for VIRTUAL_MODULES.serverFnManifest
which was removed accidentally; restore a line mapping that key to the correct
module import type (e.g. add an entry like [VIRTUAL_MODULES.serverFnManifest]:
typeof import('tanstack-start-server-fn-manifest:v') so the VirtualModules type
matches the VIRTUAL_MODULES constant and keeps loadVirtualModule's id generic
typing correct).

@schiller-manuel schiller-manuel merged commit 9b09616 into main Oct 17, 2025
4 of 5 checks passed
@schiller-manuel schiller-manuel deleted the no-invalidation-serverfn branch October 17, 2025 18:07
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.

2 participants