Skip to content

Conversation

@schiller-manuel
Copy link
Contributor

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

Summary by CodeRabbit

  • New Features

    • Added a split‑exports optimization plugin and a module‑loader integration to reduce client bundle size and improve server/client separation; includes Vite integration and dev HMR support.
    • Added a sample e2e React app showcasing split‑exports scenarios and import patterns.
  • Tests

    • Added extensive unit and end‑to‑end test suites covering split‑exports behavior, query utilities, HMR, and many import/export scenarios.
  • Chores

    • Added project config files and ignore rules for the new e2e setup.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Walkthrough

Adds a Split-Exports Vite plugin and integrates it into the start-plugin-core pipeline; provides AST-based import/export rewriting, module-loader support, per-environment server-fn compilation changes, extensive unit tests, and an e2e React project (with Playwright HMR tests) that validates import-splitting, SSR/client boundaries, and HMR behavior.

Changes

Cohort / File(s) Summary
E2E project config & tooling
e2e/react-start/split-exports/{.gitignore,.prettierignore,package.json,tsconfig.json,postcss.config.mjs,vite.config.ts,playwright.config.ts,playwright-hmr.config.ts}
Adds a full E2E test app and tooling (Vite, TS, Tailwind/PostCSS, Playwright configs, ignore rules, scripts).
E2E routes & app code
e2e/react-start/split-exports/src/routes/*, e2e/react-start/split-exports/src/{router.tsx,routeTree.gen.ts,styles/app.css}
Adds route components and router bootstrap exercising direct/re-export/alias/nested/server-request import patterns, plus generated route tree and styles.
E2E utils & public API
e2e/react-start/split-exports/src/utils/{shared.ts,nested.ts,server-request.ts,public-api.ts}
Implements shared utilities demonstrating server-only/isomorphic APIs, nested modules, server-request helpers, and a public-api aggregator.
E2E tests
e2e/react-start/split-exports/tests/{split-exports.spec.ts,split-exports-hmr.spec.ts}
Adds comprehensive Playwright tests, including HMR scenarios and client-bundle scanning to ensure no server-only leaks.
Split-Exports plugin implementation
packages/start-plugin-core/src/split-exports-plugin/{plugin.ts,compiler.ts,plugin-utils.ts,query-utils.ts,README.md}
New plugin with four sub-plugins (import rewriter, resolver, export transformer, HMR), AST import/export extraction & transform utilities, query helpers, exclusion logic, and docs.
Split-Exports plugin tests & fixtures
packages/start-plugin-core/tests/split-exports-plugin/{split-exports.test.ts,test-files/*,snapshots/*}
Extensive unit tests, fixtures and snapshots covering import/export extraction, transform, edge cases, and DCE scenarios.
Module loader plugin
packages/start-plugin-core/src/module-loader-plugin/{plugin.ts,index.ts}
Adds moduleLoaderPlugin with public ModuleLoaderApi, capture plugin for dev mode, HMR-aware caching, and public exports.
Core plugin integration & schema
packages/start-plugin-core/src/{plugin.ts,schema.ts}
Registers moduleLoaderPlugin and splitExportsPlugin in pipeline; adds importOptimization option (enabled by default) to schema.
Server-fn plugin changes
packages/start-plugin-core/src/create-server-fn-plugin/{plugin.ts,compiler.ts}
Refactors server-fn compilation to per-environment compilers, loaderApi usage, adjusted resolveId signature typing, and threading of visited Sets for cycle detection.
Misc E2E/test tweaks
e2e/*/server-functions/src/routes/factory/*
Adjusts fakeFn tests to guard against missing global window (server-safe fallback).

Sequence Diagram(s)

sequenceDiagram
    participant Importing as Importing Module
    participant ImportRewriter as ImportRewriter (pre)
    participant Resolver as Resolver (resolve plugin)
    participant ExportTransformer as ExportTransformer (transform)
    participant ModuleLoader as ModuleLoaderApi
    participant Compiler as ServerFnCompiler / DCE

    Importing->>ImportRewriter: analyze imports, extract used symbols
    ImportRewriter->>Resolver: rewrite imports adding split-exports query
    Resolver->>ExportTransformer: request module ID with split-exports query
    ExportTransformer->>Compiler: parse module, keep requested exports, transform others to locals
    ExportTransformer->>ModuleLoader: (dev) capture transformed code / (build) produce transformed file
    ModuleLoader->>Importing: supply transformed module code
    Importing->>Compiler: (server-fn paths) request per-env compiled module via ModuleLoaderApi
    Compiler->>Compiler: perform per-env compilation, DCE, return final code
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus review on:
    • packages/start-plugin-core/src/split-exports-plugin/compiler.ts (AST transforms, export pruning, DCE correctness)
    • packages/start-plugin-core/src/split-exports-plugin/plugin.ts (import rewriter / resolver / HMR interactions and source-map behavior)
    • packages/start-plugin-core/src/module-loader-plugin/plugin.ts and plugin-utils.ts (dev capture, caching, ModuleLoaderApi)
    • packages/start-plugin-core/src/create-server-fn-plugin/{plugin.ts,compiler.ts} (per-environment changes, resolveId typing)
    • E2E tests (e2e/react-start/split-exports/tests/*) for flakiness and timing assumptions

Possibly related PRs

Suggested labels

documentation

Suggested reviewers

  • brenelz

Poem

🐰 I hopped through imports, split them neat,
Tiny queries guide what code to keep,
Server secrets tucked out of sight,
HMR dances, changes take flight,
Cheers — the bundle's lighter, the tests repeat!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 52.86% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add split-exports plugin' directly describes the primary change - introducing a new split-exports plugin feature to the codebase.
✨ 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 split-exports

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

@nx-cloud
Copy link

nx-cloud bot commented Dec 15, 2025

🤖 Nx Cloud AI Fix Eligible

An automatically generated fix could have helped fix failing tasks for this run, but Self-healing CI is disabled for this workspace. Visit workspace settings to enable it and get automatic fixes in future runs.

To disable these notifications, a workspace admin can disable them in workspace settings.


View your CI Pipeline Execution ↗ for commit 39eb869

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

☁️ Nx Cloud last updated this comment at 2025-12-16 02:53:11 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 15, 2025

More templates

@tanstack/arktype-adapter

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

@tanstack/directive-functions-plugin

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/server-functions-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: 39eb869

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

🧹 Nitpick comments (10)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts (1)

2-3: Verify quote style consistency in plugin output.

The snapshot shows inconsistent quote styles: single quotes for the external import (line 2) and double quotes for the transformed local import (line 3). This inconsistency may indicate that the plugin changes quote styles during transformation.

Please verify whether this quote style change is intentional. If not, consider updating the plugin to preserve the original quote style when adding the query parameter:

 import { useState } from 'react';
-import { foo } from "./utils?tss-split-exports=foo";
+import { foo } from './utils?tss-split-exports=foo';
e2e/react-start/split-exports/.gitignore (1)

1-5: Gitignore entries are solid; optionally ignore Playwright artifacts

Patterns for build/cache dirs look good. Since this project runs Playwright, you might optionally add playwright-report and test-results to keep test artifacts out of git.

e2e/react-start/split-exports/src/router.tsx (1)

1-12: Router factory is correct; consider adding an explicit return type

The getRouter implementation is standard and uses routeTree, scrollRestoration, and defaultPreload appropriately. To better align with strict typing, you could explicitly annotate the return type (e.g., ReturnType<typeof createRouter>) so the router type is stable even if the body changes.

packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/functionExports.ts (1)

1-4: Comment wording slightly misleading vs snapshot content

This snapshot only contains myFunction, but the header comment still says “Multiple function exports”. Not a blocker, but consider clarifying the comment to reflect that this snapshot represents the pruned output (single remaining export) from the multi-export source module.

e2e/react-start/split-exports/src/routes/__root.tsx (1)

38-55: Consider adding navigation link for server-request-import route.

The navigation includes links to most test routes, but the server-request-import route (mentioned in the context and present in the route files) is not included in the navigation menu.

If this route should be accessible via navigation, add it to the menu:

         <Link to="/alias-import" activeProps={{ className: 'font-bold' }}>
           Alias Import
         </Link>
+        <Link to="/server-request-import" activeProps={{ className: 'font-bold' }}>
+          Server Request Import
+        </Link>
       </div>
e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts (1)

59-78: Consider making HMR wait more robust.

The waitForHmr helper relies on networkidle plus a fixed 500ms delay. While this works in most cases, HMR completion timing can vary. Consider using a more deterministic approach if flakiness occurs.

 async function waitForHmr(page: Page, timeout = 5000): Promise<void> {
   // Wait for any pending HMR connections
   await page.waitForLoadState('networkidle', { timeout })
   // Additional small delay for HMR processing
-  await page.waitForTimeout(500)
+  await page.waitForTimeout(1000)
 }
e2e/react-start/split-exports/src/utils/shared.ts (1)

49-61: Consider adding runtime guards to other server-only exports for consistency.

getServerOnlyUserData and serverOnlyConfig rely on getServerOnlyDatabase() being called to trigger the guard, but direct access to serverOnlyConfig properties wouldn't throw. Since this is a test fixture, this may be intentional to verify the plugin properly eliminates these exports.

packages/start-plugin-core/src/split-exports-plugin/query-utils.ts (1)

33-42: Consider edge case with special characters in export names.

Export names containing commas or other URL-sensitive characters could cause parsing issues. While uncommon in practice, JavaScript allows identifiers like export { foo as "bar,baz" } in some contexts.

If special characters in export names need to be supported, consider URL-encoding individual names:

-  const sortedNames = Array.from(exportNames).sort()
-  const newParam = `${SPLIT_EXPORTS_QUERY_KEY}=${sortedNames.join(',')}`
+  const sortedNames = Array.from(exportNames).sort().map(encodeURIComponent)
+  const newParam = `${SPLIT_EXPORTS_QUERY_KEY}=${sortedNames.join(',')}`

And decode when parsing:

-  return new Set(value.split(',').filter(Boolean))
+  return new Set(value.split(',').filter(Boolean).map(decodeURIComponent))
packages/start-plugin-core/tests/split-exports-plugin/split-exports.test.ts (1)

3-3: Unused import detected.

The vi import from vitest appears to be unused in this test file.

-import { describe, expect, test, vi } from 'vitest'
+import { describe, expect, test } from 'vitest'
packages/start-plugin-core/src/split-exports-plugin/plugin.ts (1)

219-221: Static analysis false positive - ReDoS warning is not applicable.

The static analysis tool flags new RegExp(\[?&]${SPLIT_EXPORTS_QUERY_KEY}=`)as a potential ReDoS risk. However,SPLIT_EXPORTS_QUERY_KEY is a constant string ('tss-split-exports'), making the resulting regex effectively static: /[?&]tss-split-exports=/`. This pattern has no backtracking and is safe.

Consider extracting this to a module-level constant to silence the warning and avoid regex re-creation:

+const SPLIT_EXPORTS_FILTER_REGEX = new RegExp(`[?&]${SPLIT_EXPORTS_QUERY_KEY}=`)
+
 function resolverPlugin(): Plugin {
   return {
     name: 'tanstack-split-exports:resolve',

     resolveId: {
       filter: {
-        id: new RegExp(`[?&]${SPLIT_EXPORTS_QUERY_KEY}=`),
+        id: SPLIT_EXPORTS_FILTER_REGEX,
       },

Also applies to: 268-270

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4256268 and f29c613.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (60)
  • e2e/react-start/split-exports/.gitignore (1 hunks)
  • e2e/react-start/split-exports/.prettierignore (1 hunks)
  • e2e/react-start/split-exports/package.json (1 hunks)
  • e2e/react-start/split-exports/playwright-hmr.config.ts (1 hunks)
  • e2e/react-start/split-exports/playwright.config.ts (1 hunks)
  • e2e/react-start/split-exports/postcss.config.mjs (1 hunks)
  • e2e/react-start/split-exports/src/routeTree.gen.ts (1 hunks)
  • e2e/react-start/split-exports/src/router.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/__root.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/alias-import.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/direct-import.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/index.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/reexport-import.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/server-request-import.tsx (1 hunks)
  • e2e/react-start/split-exports/src/styles/app.css (1 hunks)
  • e2e/react-start/split-exports/src/utils/nested.ts (1 hunks)
  • e2e/react-start/split-exports/src/utils/public-api.ts (1 hunks)
  • e2e/react-start/split-exports/src/utils/server-request.ts (1 hunks)
  • e2e/react-start/split-exports/src/utils/shared.ts (1 hunks)
  • e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts (1 hunks)
  • e2e/react-start/split-exports/tests/split-exports.spec.ts (1 hunks)
  • e2e/react-start/split-exports/tsconfig.json (1 hunks)
  • e2e/react-start/split-exports/vite.config.ts (1 hunks)
  • packages/start-plugin-core/src/plugin.ts (2 hunks)
  • packages/start-plugin-core/src/schema.ts (1 hunks)
  • packages/start-plugin-core/src/split-exports-plugin/README.md (1 hunks)
  • packages/start-plugin-core/src/split-exports-plugin/compiler.ts (1 hunks)
  • packages/start-plugin-core/src/split-exports-plugin/plugin-utils.ts (1 hunks)
  • packages/start-plugin-core/src/split-exports-plugin/plugin.ts (1 hunks)
  • packages/start-plugin-core/src/split-exports-plugin/query-utils.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/functionExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/multipleDeclarators.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/namedExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/reExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/serverOnlyCode.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/starReExport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/defaultImport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/existingQuery.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedTypeValueImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/namedImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/split-exports.test.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultExport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultImport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/existingQuery.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/externalImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/functionExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/mixedImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/mixedTypeValueImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/multipleDeclarators.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namedExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namedImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namespaceImport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/reExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/starReExport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/typeOnlyImports.ts (1 hunks)
🧰 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:

  • e2e/react-start/split-exports/vite.config.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/externalImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/defaultImport.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namedImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/existingQuery.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultImport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/reExports.ts
  • e2e/react-start/split-exports/src/router.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/mixedTypeValueImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/reExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/serverOnlyCode.ts
  • e2e/react-start/split-exports/src/routes/__root.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/existingQuery.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/functionExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultExport.ts
  • e2e/react-start/split-exports/playwright-hmr.config.ts
  • e2e/react-start/split-exports/src/routes/direct-import.tsx
  • e2e/react-start/split-exports/tests/split-exports.spec.ts
  • e2e/react-start/split-exports/src/utils/server-request.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/mixedImports.ts
  • packages/start-plugin-core/src/split-exports-plugin/query-utils.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts
  • e2e/react-start/split-exports/playwright.config.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedTypeValueImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/multipleDeclarators.ts
  • e2e/react-start/split-exports/src/utils/public-api.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/namedImports.ts
  • packages/start-plugin-core/src/split-exports-plugin/plugin-utils.ts
  • e2e/react-start/split-exports/src/utils/shared.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namespaceImport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/functionExports.ts
  • e2e/react-start/split-exports/src/routes/server-request-import.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/typeOnlyImports.ts
  • e2e/react-start/split-exports/src/utils/nested.ts
  • packages/start-plugin-core/src/split-exports-plugin/plugin.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/multipleDeclarators.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/starReExport.ts
  • e2e/react-start/split-exports/src/routes/alias-import.tsx
  • e2e/react-start/split-exports/src/routeTree.gen.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/namedExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/starReExport.ts
  • e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts
  • e2e/react-start/split-exports/src/routes/index.tsx
  • packages/start-plugin-core/src/split-exports-plugin/compiler.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/split-exports.test.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namedExports.ts
  • e2e/react-start/split-exports/src/routes/reexport-import.tsx
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • e2e/react-start/split-exports/vite.config.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/externalImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/defaultImport.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namedImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/existingQuery.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultImport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/reExports.ts
  • e2e/react-start/split-exports/src/router.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/mixedTypeValueImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/reExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/serverOnlyCode.ts
  • e2e/react-start/split-exports/src/routes/__root.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/existingQuery.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/functionExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultExport.ts
  • e2e/react-start/split-exports/playwright-hmr.config.ts
  • e2e/react-start/split-exports/src/routes/direct-import.tsx
  • e2e/react-start/split-exports/tests/split-exports.spec.ts
  • e2e/react-start/split-exports/src/utils/server-request.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/mixedImports.ts
  • packages/start-plugin-core/src/split-exports-plugin/query-utils.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts
  • e2e/react-start/split-exports/playwright.config.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedTypeValueImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/multipleDeclarators.ts
  • e2e/react-start/split-exports/src/utils/public-api.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/namedImports.ts
  • packages/start-plugin-core/src/split-exports-plugin/plugin-utils.ts
  • e2e/react-start/split-exports/src/utils/shared.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namespaceImport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/functionExports.ts
  • e2e/react-start/split-exports/src/routes/server-request-import.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/typeOnlyImports.ts
  • e2e/react-start/split-exports/src/utils/nested.ts
  • packages/start-plugin-core/src/split-exports-plugin/plugin.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/multipleDeclarators.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/starReExport.ts
  • e2e/react-start/split-exports/src/routes/alias-import.tsx
  • e2e/react-start/split-exports/src/routeTree.gen.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/namedExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/starReExport.ts
  • e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts
  • e2e/react-start/split-exports/src/routes/index.tsx
  • packages/start-plugin-core/src/split-exports-plugin/compiler.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/split-exports.test.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namedExports.ts
  • e2e/react-start/split-exports/src/routes/reexport-import.tsx
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • e2e/react-start/split-exports/package.json
🧠 Learnings (9)
📚 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/split-exports/.prettierignore
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/existingQuery.ts
  • e2e/react-start/split-exports/src/routes/direct-import.tsx
  • e2e/react-start/split-exports/tests/split-exports.spec.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts
  • e2e/react-start/split-exports/src/routes/server-request-import.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedImports.ts
  • e2e/react-start/split-exports/src/routes/alias-import.tsx
  • e2e/react-start/split-exports/src/routeTree.gen.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/starReExport.ts
  • e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts
  • e2e/react-start/split-exports/src/routes/index.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/split-exports.test.ts
  • e2e/react-start/split-exports/src/routes/reexport-import.tsx
📚 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/split-exports/.prettierignore
  • e2e/react-start/split-exports/src/routes/__root.tsx
  • e2e/react-start/split-exports/src/routeTree.gen.ts
📚 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/split-exports/.prettierignore
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/externalImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/defaultImport.ts
  • packages/start-plugin-core/src/plugin.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namedImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultImport.ts
  • packages/start-plugin-core/src/split-exports-plugin/README.md
  • e2e/react-start/split-exports/src/routes/__root.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/existingQuery.ts
  • e2e/react-start/split-exports/package.json
  • e2e/react-start/split-exports/playwright-hmr.config.ts
  • e2e/react-start/split-exports/src/routes/direct-import.tsx
  • e2e/react-start/split-exports/tests/split-exports.spec.ts
  • e2e/react-start/split-exports/src/utils/server-request.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedTypeValueImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/namedImports.ts
  • e2e/react-start/split-exports/src/utils/shared.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts
  • e2e/react-start/split-exports/tsconfig.json
  • e2e/react-start/split-exports/src/routes/server-request-import.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/typeOnlyImports.ts
  • e2e/react-start/split-exports/src/utils/nested.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedImports.ts
  • e2e/react-start/split-exports/src/routes/alias-import.tsx
  • e2e/react-start/split-exports/src/routeTree.gen.ts
  • e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts
  • e2e/react-start/split-exports/src/routes/index.tsx
  • e2e/react-start/split-exports/src/routes/reexport-import.tsx
📚 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: Always run `pnpm test:eslint`, `pnpm test:types`, and `pnpm test:unit` before committing code

Applied to files:

  • e2e/react-start/split-exports/.prettierignore
📚 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/split-exports/src/routes/__root.tsx
  • e2e/react-start/split-exports/src/routes/direct-import.tsx
  • e2e/react-start/split-exports/tsconfig.json
  • e2e/react-start/split-exports/src/routes/alias-import.tsx
  • e2e/react-start/split-exports/src/routeTree.gen.ts
  • e2e/react-start/split-exports/src/routes/index.tsx
  • e2e/react-start/split-exports/src/routes/reexport-import.tsx
📚 Learning: 2025-10-09T12:59:14.842Z
Learnt from: hokkyss
Repo: TanStack/router PR: 5418
File: e2e/react-start/custom-identifier-prefix/public/site.webmanifest:2-3
Timestamp: 2025-10-09T12:59:14.842Z
Learning: In e2e test fixtures (files under e2e directories), empty or placeholder values in configuration files like site.webmanifest are acceptable and should not be flagged unless the test specifically validates those fields.

Applied to files:

  • e2e/react-start/split-exports/playwright.config.ts
  • e2e/react-start/split-exports/tests/split-exports-hmr.spec.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 **/*.{ts,tsx} : Use TypeScript strict mode with extensive type safety for all code

Applied to files:

  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts
  • e2e/react-start/split-exports/tsconfig.json
📚 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: Use file-based routing in `src/routes/` directories or code-based routing with route definitions

Applied to files:

  • e2e/react-start/split-exports/src/routeTree.gen.ts
  • e2e/react-start/split-exports/src/routes/index.tsx
📚 Learning: 2025-10-09T12:59:02.129Z
Learnt from: hokkyss
Repo: TanStack/router PR: 5418
File: e2e/react-start/custom-identifier-prefix/src/styles/app.css:19-21
Timestamp: 2025-10-09T12:59:02.129Z
Learning: In e2e test directories (paths containing `e2e/`), accessibility concerns like outline suppression patterns are less critical since the code is for testing purposes, not production use.

Applied to files:

  • packages/start-plugin-core/tests/split-exports-plugin/split-exports.test.ts
🧬 Code graph analysis (28)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/defaultImport.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultImport.ts (1)
  • main (5-7)
packages/start-plugin-core/src/plugin.ts (1)
packages/start-plugin-core/src/split-exports-plugin/plugin.ts (1)
  • splitExportsPlugin (399-414)
packages/start-plugin-core/tests/split-exports-plugin/test-files/namedImports.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/namedImports.ts (1)
  • main (4-6)
packages/start-plugin-core/tests/split-exports-plugin/test-files/existingQuery.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/existingQuery.ts (1)
  • main (4-6)
packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultImport.ts (2)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/defaultImport.ts (1)
  • main (4-6)
packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultExport.ts (1)
  • main (4-6)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/serverOnlyCode.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts (1)
  • formatUser (5-7)
e2e/react-start/split-exports/src/routes/__root.tsx (5)
e2e/react-start/split-exports/src/routes/alias-import.tsx (1)
  • Route (32-42)
e2e/react-start/split-exports/src/routes/direct-import.tsx (1)
  • Route (22-31)
e2e/react-start/split-exports/src/routes/index.tsx (1)
  • Route (3-5)
e2e/react-start/split-exports/src/routes/reexport-import.tsx (1)
  • Route (19-26)
e2e/react-start/split-exports/src/routes/server-request-import.tsx (1)
  • Route (33-41)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/functionExports.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/functionExports.ts (1)
  • myFunction (2-4)
packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultExport.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.ts (1)
  • main (3-5)
e2e/react-start/split-exports/playwright-hmr.config.ts (1)
scripts/set-ts-version.js (1)
  • packageJson (33-33)
e2e/react-start/split-exports/src/utils/server-request.ts (2)
packages/start-server-core/src/request-response.ts (1)
  • getRequest (72-75)
examples/solid/kitchen-sink/src/useMutation.tsx (1)
  • data (47-49)
packages/start-plugin-core/src/split-exports-plugin/query-utils.ts (1)
packages/router-core/src/route.ts (2)
  • id (1549-1551)
  • path (1553-1555)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/externalImports.ts (1)
  • Component (5-8)
e2e/react-start/split-exports/playwright.config.ts (1)
scripts/set-ts-version.js (1)
  • packageJson (33-33)
packages/start-plugin-core/tests/split-exports-plugin/test-files/multipleDeclarators.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/multipleDeclarators.ts (2)
  • a (2-2)
  • processA (3-5)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/namedImports.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/namedImports.ts (1)
  • main (5-7)
e2e/react-start/split-exports/src/utils/shared.ts (1)
e2e/react-start/split-exports/src/utils/public-api.ts (8)
  • getServerEnvironment (15-15)
  • getUserById (15-15)
  • getAllUsers (15-15)
  • getEnvironment (8-8)
  • getEnvironment (18-18)
  • formatMessage (9-9)
  • formatUserName (10-10)
  • APP_NAME (11-11)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/typeOnlyImports.ts (1)
  • main (5-7)
e2e/react-start/split-exports/src/utils/nested.ts (2)
e2e/react-start/split-exports/src/utils/shared.ts (2)
  • getServerOnlyUserData (49-52)
  • APP_NAME (138-138)
e2e/react-start/split-exports/src/utils/public-api.ts (1)
  • APP_NAME (11-11)
packages/start-plugin-core/src/split-exports-plugin/plugin.ts (3)
packages/start-plugin-core/src/split-exports-plugin/plugin-utils.ts (5)
  • shouldExclude (69-110)
  • stripQueryString (61-63)
  • isParseableFile (43-56)
  • debug (3-5)
  • hasDirectiveQuery (35-37)
packages/start-plugin-core/src/split-exports-plugin/compiler.ts (3)
  • extractImportsFromModule (59-131)
  • transformImports (165-223)
  • transformExports (352-508)
packages/start-plugin-core/src/split-exports-plugin/query-utils.ts (5)
  • SPLIT_EXPORTS_QUERY_KEY (1-1)
  • extractSplitExportsQuery (87-94)
  • parseSplitExportsQuery (33-42)
  • removeSplitExportsQuery (48-58)
  • hasSplitExportsQuery (22-27)
packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/serverOnlyCode.ts (1)
  • formatUser (5-9)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/multipleDeclarators.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/multipleDeclarators.ts (2)
  • a (2-4)
  • processA (6-8)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedImports.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/mixedImports.ts (1)
  • main (4-6)
e2e/react-start/split-exports/src/routeTree.gen.ts (1)
e2e/react-start/split-exports/src/router.tsx (1)
  • getRouter (4-12)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/namedExports.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/namedExports.ts (2)
  • foo (2-2)
  • bar (3-3)
e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts (2)
packages/router-core/src/route.ts (1)
  • path (1553-1555)
packages/start-plugin-core/src/schema.ts (1)
  • Page (208-208)
e2e/react-start/split-exports/src/routes/index.tsx (5)
e2e/react-start/split-exports/src/routes/__root.tsx (1)
  • Route (12-29)
e2e/react-start/split-exports/src/routes/alias-import.tsx (1)
  • Route (32-42)
e2e/react-start/split-exports/src/routes/direct-import.tsx (1)
  • Route (22-31)
e2e/react-start/split-exports/src/routes/reexport-import.tsx (1)
  • Route (19-26)
e2e/react-start/split-exports/src/routes/server-request-import.tsx (1)
  • Route (33-41)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultExport.ts (1)
  • main (4-6)
🪛 ast-grep (0.40.0)
packages/start-plugin-core/src/split-exports-plugin/plugin.ts

[warning] 219-219: 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([?&]${SPLIT_EXPORTS_QUERY_KEY}=)
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)


[warning] 268-268: 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([?&]${SPLIT_EXPORTS_QUERY_KEY}=)
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)

⏰ 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

Comment on lines +15 to +37
"dependencies": {
"@tanstack/react-router": "workspace:^",
"@tanstack/react-router-devtools": "workspace:^",
"@tanstack/react-start": "workspace:^",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"tailwind-merge": "^2.6.0"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@tailwindcss/postcss": "^4.1.15",
"@tanstack/router-e2e-utils": "workspace:^",
"@types/node": "^22.10.2",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^4.3.4",
"postcss": "^8.5.1",
"srvx": "^0.8.6",
"tailwindcss": "^4.1.17",
"typescript": "^5.7.2",
"vite": "^7.1.7",
"vite-tsconfig-paths": "^5.1.4"
}
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

Use workspace:* for internal TanStack packages per repo guidelines

Per the workspace guideline, internal packages should use workspace:* instead of workspace:^. Updating these keeps version alignment explicit and consistent.

   "dependencies": {
-    "@tanstack/react-router": "workspace:^",
-    "@tanstack/react-router-devtools": "workspace:^",
-    "@tanstack/react-start": "workspace:^",
+    "@tanstack/react-router": "workspace:*",
+    "@tanstack/react-router-devtools": "workspace:*",
+    "@tanstack/react-start": "workspace:*",
@@
   "devDependencies": {
@@
-    "@tanstack/router-e2e-utils": "workspace:^",
+    "@tanstack/router-e2e-utils": "workspace:*",
🤖 Prompt for AI Agents
In e2e/react-start/split-exports/package.json around lines 15 to 37, the
internal TanStack workspace dependencies use "workspace:^" which violates the
repo guideline; update all internal workspace package versions
("@tanstack/react-router", "@tanstack/react-router-devtools",
"@tanstack/react-start", "@tanstack/router-e2e-utils") to use "workspace:*"
instead of "workspace:^" so they consistently reference the workspace range.

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

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/compiler.ts (1)

61-75: Type signature change to resolveId verified, but test implementations need updating.

The change from optional importer?: string to explicit importer: string | undefined is correctly implemented in the plugin (line 115-120) and all compiler call sites properly pass both parameters. However, the test implementations in createServerFn.test.ts and createMiddleware.test.ts define resolveId: async (id) => { return id } which only accepts one parameter, violating the new signature that requires (id: string, importer: string | undefined) => Promise<string | null>. Update test resolveId implementations to accept the importer parameter.

🧹 Nitpick comments (11)
e2e/react-start/split-exports/src/utils/server-request.ts (1)

89-98: Consider adding runtime validation in the inputValidator.

The current inputValidator relies solely on TypeScript's compile-time checking. For production code, consider adding runtime validation to ensure the message property exists and is a string. However, for this e2e test demonstrating the pattern, the pass-through approach is acceptable.

Optional enhancement for runtime safety:

 export const echoWithRequestInfo = createServerFn()
-  .inputValidator((data: { message: string }) => data)
+  .inputValidator((data: unknown) => {
+    if (typeof data !== 'object' || data === null || typeof (data as { message?: unknown }).message !== 'string') {
+      throw new Error('Invalid input: message must be a string')
+    }
+    return data as { message: string }
+  })
   .handler(({ data }) => {
packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts (1)

5-5: Optional: Consider lazy initialization for useState.

The current code calls foo() on every render. For test fixture purposes this works fine, but lazy initialization (useState(() => foo())) would be more idiomatic React and only call foo() once during initialization.

-  const [state] = useState(foo());
+  const [state] = useState(() => foo());
packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts (1)

10-17: Consider explicit return types for better type safety.

While the async functions correctly demonstrate server-only code for testing purposes, adding explicit return types would align with the coding guideline for "extensive type safety" in TypeScript files.

As per coding guidelines, consider adding explicit return types to avoid implicit any.

For example:

-export const getUser = async (id: string) => {
+export const getUser = async (id: string): Promise<any> => {
   return db.users.findOne({ id })
 }

-export const deleteUser = async (id: string) => {
+export const deleteUser = async (id: string): Promise<any> => {
   return db.users.delete({ id })
 }

Note: Since this is a test file, you could also define a more specific type instead of any if the db operations have known return types.

e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts (1)

59-64: HMR wait strategy may be fragile.

The networkidle state plus a fixed 500ms delay may not reliably detect HMR completion in all scenarios. If tests become flaky, consider polling for a specific DOM change or listening for Vite's HMR events via page.evaluate.

e2e/react-start/split-exports/tests/split-exports.spec.ts (1)

169-174: Consider extracting script-fetching logic into a helper.

The script source collection logic is duplicated in both leak tests. This could be extracted into a helper function for maintainability.

+async function getClientScriptSources(page: Page): Promise<Array<string>> {
+  const scripts = await page.evaluate(() => {
+    const scriptElements = Array.from(
+      document.querySelectorAll('script[src]'),
+    )
+    return scriptElements.map((s) => s.getAttribute('src')).filter(Boolean)
+  })
+  return scripts.filter(
+    (src): src is string => !!src && !src.includes('node_modules'),
+  )
+}

Then simplify both tests:

const scripts = await getClientScriptSources(page)
for (const scriptSrc of scripts) {
  const response = await page.request.get(scriptSrc)
  // ... assertions
}

Also applies to: 202-207

packages/start-plugin-core/src/split-exports-plugin/compiler.ts (3)

339-359: Consider handling destructured patterns in variable declarations.

getExportedNamesFromDeclaration only extracts names from simple identifier declarations. Destructured exports like export const { foo, bar } = obj or export const [a, b] = arr would return an empty array.

While this may be intentional (such exports are uncommon), it could silently skip these exports from transformation. Consider adding a comment documenting this limitation or extending support.

 function getExportedNamesFromDeclaration(
   declaration: t.Declaration,
 ): Array<string> {
   if (t.isVariableDeclaration(declaration)) {
-    return declaration.declarations
-      .filter((decl): decl is t.VariableDeclarator & { id: t.Identifier } =>
-        t.isIdentifier(decl.id),
-      )
-      .map((decl) => decl.id.name)
+    const names: Array<string> = []
+    for (const decl of declaration.declarations) {
+      if (t.isIdentifier(decl.id)) {
+        names.push(decl.id.name)
+      }
+      // Note: Destructured exports (ObjectPattern, ArrayPattern) are not
+      // currently supported and will be preserved as-is
+    }
+    return names
   }

374-397: Consider using getBindingIdentifiers from @babel/types for robustness.

The custom traversal to find binding identifiers works but could be simplified and made more robust using Babel's built-in utilities:

 function addBindingIdentifiersToRefIdents(
   path: babel.NodePath,
   refIdents: Set<babel.NodePath<t.Identifier>>,
 ): void {
-  path.traverse({
-    Identifier(identPath: babel.NodePath<t.Identifier>) {
-      // Add function/class name identifiers
-      if (
-        (identPath.parentPath.isFunctionDeclaration() ||
-          identPath.parentPath.isClassDeclaration()) &&
-        identPath.key === 'id'
-      ) {
-        refIdents.add(identPath)
-      }
-      // Add variable declarator identifiers
-      if (
-        identPath.parentPath.isVariableDeclarator() &&
-        identPath.key === 'id'
-      ) {
-        refIdents.add(identPath)
-      }
-    },
-  })
+  // Use Babel's built-in binding identifier extraction
+  const bindings = path.scope?.getAllBindings() ?? {}
+  for (const binding of Object.values(bindings)) {
+    if (t.isIdentifier(binding.identifier)) {
+      refIdents.add(binding.path as babel.NodePath<t.Identifier>)
+    }
+  }
 }

Alternatively, if the current approach is intentional to only catch specific patterns, document why.


519-526: Defensive fallback is unreachable but good to keep documented.

The comment correctly explains this is unreachable for function/class declarations. Consider adding an assertion or debug log to catch unexpected scenarios during development:

         } else {
           // Unreachable for function/class: they export exactly one name,
           // so keptNames is either length 0 (handled above) or 1 (all kept, returned early)
           // Keep as defensive fallback
+          if (process.env.NODE_ENV === 'development') {
+            console.warn('[split-exports] Unexpected: partial keep for non-variable declaration')
+          }
           path.replaceWith(node.declaration)
packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (1)

105-114: The non-null assertions are safe given the initialization check but could be cleaner.

The ! assertions on loaderApi, viteEnv, and ctxLoad are safe because they're set in the same block above. However, this pattern creates temporal coupling. Consider restructuring:

-              loadModule: async (moduleId: string) => {
-                const ctxLoadForEnv =
-                  viteEnv!.mode === 'build' ? ctxLoad : undefined
-                const moduleCode = await loaderApi!.loadModuleCode(
-                  viteEnv!,
-                  moduleId,
-                  ctxLoadForEnv,
-                )
-                compiler!.ingestModule({ code: moduleCode, id: moduleId })
+              loadModule: async (moduleId: string) => {
+                // Safe: loaderApi and viteEnv are guaranteed set before compiler creation
+                const ctxLoadForEnv =
+                  viteEnv!.mode === 'build' ? ctxLoad : undefined
+                const moduleCode = await loaderApi!.loadModuleCode(
+                  viteEnv!,
+                  moduleId,
+                  ctxLoadForEnv,
+                )
+                compiler!.ingestModule({ code: moduleCode, id: moduleId })
               },
packages/start-plugin-core/src/split-exports-plugin/plugin.ts (2)

201-205: Silent failure when loading module for class detection.

When loadModuleCode throws, the code assumes hasClasses = false and caches this. This is reasonable as a fallback, but consider logging a debug warning to help troubleshoot issues:

             } catch {
               // If we can't load the module, assume no classes (safe fallback)
+              if (debug) {
+                console.warn(
+                  '[split-exports] Failed to load module for class detection:',
+                  resolvedPath,
+                )
+              }
               hasClasses = false
               classExportCache.set(resolvedPath, hasClasses)
             }

297-300: Duplicate URL encoding logic with appendSplitExportsQuery.

The encoding and sorting logic here duplicates what's in query-utils.ts (line 82-83 of the relevant snippet). Consider extracting a shared helper:

+// In query-utils.ts, add:
+export function encodeExportNames(exportNames: Set<string>): string {
+  return Array.from(exportNames).sort().map(encodeURIComponent).join(',')
+}

 // Then in resolverPlugin:
-        const sortedNames = Array.from(exportNames)
-          .sort()
-          .map(encodeURIComponent)
-          .join(',')
+        const sortedNames = encodeExportNames(exportNames)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f29c613 and 39eb869.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (70)
  • e2e/react-start/server-functions/src/routes/factory/-functions/functions.ts (1 hunks)
  • e2e/react-start/server-functions/src/routes/factory/index.tsx (1 hunks)
  • e2e/react-start/split-exports/.gitignore (1 hunks)
  • e2e/react-start/split-exports/.prettierignore (1 hunks)
  • e2e/react-start/split-exports/package.json (1 hunks)
  • e2e/react-start/split-exports/playwright-hmr.config.ts (1 hunks)
  • e2e/react-start/split-exports/playwright.config.ts (1 hunks)
  • e2e/react-start/split-exports/postcss.config.mjs (1 hunks)
  • e2e/react-start/split-exports/src/routeTree.gen.ts (1 hunks)
  • e2e/react-start/split-exports/src/router.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/__root.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/alias-import.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/direct-import.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/index.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/reexport-import.tsx (1 hunks)
  • e2e/react-start/split-exports/src/routes/server-request-import.tsx (1 hunks)
  • e2e/react-start/split-exports/src/styles/app.css (1 hunks)
  • e2e/react-start/split-exports/src/utils/nested.ts (1 hunks)
  • e2e/react-start/split-exports/src/utils/public-api.ts (1 hunks)
  • e2e/react-start/split-exports/src/utils/server-request.ts (1 hunks)
  • e2e/react-start/split-exports/src/utils/shared.ts (1 hunks)
  • e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts (1 hunks)
  • e2e/react-start/split-exports/tests/split-exports.spec.ts (1 hunks)
  • e2e/react-start/split-exports/tsconfig.json (1 hunks)
  • e2e/react-start/split-exports/vite.config.ts (1 hunks)
  • e2e/solid-start/server-functions/src/routes/factory/-functions/functions.ts (1 hunks)
  • e2e/solid-start/server-functions/src/routes/factory/index.tsx (1 hunks)
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts (6 hunks)
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (6 hunks)
  • packages/start-plugin-core/src/module-loader-plugin/index.ts (1 hunks)
  • packages/start-plugin-core/src/module-loader-plugin/plugin.ts (1 hunks)
  • packages/start-plugin-core/src/plugin-utils.ts (1 hunks)
  • packages/start-plugin-core/src/plugin.ts (2 hunks)
  • packages/start-plugin-core/src/schema.ts (1 hunks)
  • packages/start-plugin-core/src/split-exports-plugin/README.md (1 hunks)
  • packages/start-plugin-core/src/split-exports-plugin/compiler.ts (1 hunks)
  • packages/start-plugin-core/src/split-exports-plugin/plugin-utils.ts (1 hunks)
  • packages/start-plugin-core/src/split-exports-plugin/plugin.ts (1 hunks)
  • packages/start-plugin-core/src/split-exports-plugin/query-utils.ts (1 hunks)
  • packages/start-plugin-core/tests/plugin-utils.test.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/functionExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/multipleDeclarators.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/namedExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/reExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/serverOnlyCode.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/starReExport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/defaultImport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/existingQuery.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedTypeValueImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/namedImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/split-exports.test.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultExport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultImport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/existingQuery.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/externalImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/functionExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/mixedImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/mixedTypeValueImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/multipleDeclarators.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namedExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namedImports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namespaceImport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/reExports.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/starReExport.ts (1 hunks)
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/typeOnlyImports.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/start-plugin-core/src/split-exports-plugin/README.md
🚧 Files skipped from review as they are similar to previous changes (37)
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/starReExport.ts
  • e2e/react-start/split-exports/.gitignore
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namespaceImport.ts
  • e2e/react-start/split-exports/src/routes/index.tsx
  • e2e/react-start/split-exports/vite.config.ts
  • e2e/react-start/split-exports/src/styles/app.css
  • e2e/react-start/split-exports/src/routes/alias-import.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/namedImports.ts
  • e2e/react-start/split-exports/tsconfig.json
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/mixedImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/existingQuery.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/serverOnlyCode.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namedExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultImport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/externalImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/existingQuery.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/reExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedImports.ts
  • e2e/react-start/split-exports/src/router.tsx
  • e2e/react-start/split-exports/playwright-hmr.config.ts
  • e2e/react-start/split-exports/postcss.config.mjs
  • e2e/react-start/split-exports/src/routes/__root.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/functionExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/reExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/defaultImport.ts
  • e2e/react-start/split-exports/package.json
  • e2e/react-start/split-exports/src/routes/server-request-import.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/split-exports.test.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/mixedTypeValueImports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/multipleDeclarators.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/namedImports.ts
  • e2e/react-start/split-exports/src/utils/nested.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/mixedTypeValueImports.ts
  • e2e/react-start/split-exports/src/routes/reexport-import.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultExport.ts
  • e2e/react-start/split-exports/.prettierignore
🧰 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/module-loader-plugin/index.ts
  • packages/start-plugin-core/src/plugin.ts
  • e2e/react-start/split-exports/playwright.config.ts
  • e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts
  • packages/start-plugin-core/src/plugin-utils.ts
  • e2e/react-start/server-functions/src/routes/factory/index.tsx
  • e2e/react-start/split-exports/src/routeTree.gen.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • e2e/solid-start/server-functions/src/routes/factory/index.tsx
  • e2e/react-start/split-exports/src/utils/public-api.ts
  • packages/start-plugin-core/src/module-loader-plugin/plugin.ts
  • e2e/react-start/server-functions/src/routes/factory/-functions/functions.ts
  • packages/start-plugin-core/src/split-exports-plugin/plugin-utils.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts
  • e2e/react-start/split-exports/src/routes/direct-import.tsx
  • e2e/react-start/split-exports/src/utils/server-request.ts
  • e2e/react-start/split-exports/src/utils/shared.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/multipleDeclarators.ts
  • packages/start-plugin-core/src/split-exports-plugin/compiler.ts
  • e2e/solid-start/server-functions/src/routes/factory/-functions/functions.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts
  • packages/start-plugin-core/src/split-exports-plugin/query-utils.ts
  • packages/start-plugin-core/tests/plugin-utils.test.ts
  • e2e/react-start/split-exports/tests/split-exports.spec.ts
  • packages/start-plugin-core/src/split-exports-plugin/plugin.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/starReExport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/functionExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/namedExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/typeOnlyImports.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/module-loader-plugin/index.ts
  • packages/start-plugin-core/src/plugin.ts
  • e2e/react-start/split-exports/playwright.config.ts
  • e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts
  • packages/start-plugin-core/src/plugin-utils.ts
  • e2e/react-start/server-functions/src/routes/factory/index.tsx
  • e2e/react-start/split-exports/src/routeTree.gen.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/compiler.ts
  • e2e/solid-start/server-functions/src/routes/factory/index.tsx
  • e2e/react-start/split-exports/src/utils/public-api.ts
  • packages/start-plugin-core/src/module-loader-plugin/plugin.ts
  • e2e/react-start/server-functions/src/routes/factory/-functions/functions.ts
  • packages/start-plugin-core/src/split-exports-plugin/plugin-utils.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts
  • e2e/react-start/split-exports/src/routes/direct-import.tsx
  • e2e/react-start/split-exports/src/utils/server-request.ts
  • e2e/react-start/split-exports/src/utils/shared.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/multipleDeclarators.ts
  • packages/start-plugin-core/src/split-exports-plugin/compiler.ts
  • e2e/solid-start/server-functions/src/routes/factory/-functions/functions.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts
  • packages/start-plugin-core/src/split-exports-plugin/query-utils.ts
  • packages/start-plugin-core/tests/plugin-utils.test.ts
  • e2e/react-start/split-exports/tests/split-exports.spec.ts
  • packages/start-plugin-core/src/split-exports-plugin/plugin.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/starReExport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/functionExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/namedExports.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts
  • packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/typeOnlyImports.ts
🧠 Learnings (7)
📚 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/plugin.ts
  • e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts
  • e2e/react-start/split-exports/src/routeTree.gen.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts
  • e2e/react-start/split-exports/src/routes/direct-import.tsx
  • e2e/react-start/split-exports/src/utils/server-request.ts
  • e2e/react-start/split-exports/src/utils/shared.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts
  • e2e/react-start/split-exports/tests/split-exports.spec.ts
  • packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts
📚 Learning: 2025-10-09T12:59:14.842Z
Learnt from: hokkyss
Repo: TanStack/router PR: 5418
File: e2e/react-start/custom-identifier-prefix/public/site.webmanifest:2-3
Timestamp: 2025-10-09T12:59:14.842Z
Learning: In e2e test fixtures (files under e2e directories), empty or placeholder values in configuration files like site.webmanifest are acceptable and should not be flagged unless the test specifically validates those fields.

Applied to files:

  • e2e/react-start/split-exports/playwright.config.ts
  • e2e/react-start/split-exports/tests/split-exports-hmr.spec.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/split-exports/tests/split-exports-hmr.spec.ts
  • e2e/react-start/split-exports/src/routeTree.gen.ts
  • e2e/react-start/split-exports/src/routes/direct-import.tsx
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/externalImports.ts
  • packages/start-plugin-core/tests/plugin-utils.test.ts
  • e2e/react-start/split-exports/tests/split-exports.spec.ts
  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.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/split-exports/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/split-exports/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: Use file-based routing in `src/routes/` directories or code-based routing with route definitions

Applied to files:

  • e2e/react-start/split-exports/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 **/*.{ts,tsx} : Use TypeScript strict mode with extensive type safety for all code

Applied to files:

  • packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts
🧬 Code graph analysis (14)
e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts (1)
packages/start-plugin-core/src/schema.ts (1)
  • Page (208-208)
packages/start-plugin-core/src/plugin-utils.ts (1)
packages/start-plugin-core/src/module-loader-plugin/plugin.ts (1)
  • ModuleLoaderApi (9-24)
e2e/react-start/split-exports/src/routeTree.gen.ts (1)
e2e/react-start/split-exports/src/router.tsx (1)
  • getRouter (4-12)
packages/start-plugin-core/src/split-exports-plugin/plugin-utils.ts (1)
packages/start-plugin-core/src/plugin-utils.ts (1)
  • stripQueryString (29-31)
e2e/react-start/split-exports/src/routes/direct-import.tsx (1)
e2e/react-start/split-exports/src/utils/shared.ts (6)
  • getEnvironment (111-113)
  • getUserById (82-92)
  • formatMessage (118-120)
  • getServerEnvironment (73-75)
  • APP_NAME (138-138)
  • formatUserName (131-133)
e2e/react-start/split-exports/src/utils/server-request.ts (1)
packages/start-server-core/src/request-response.ts (1)
  • getRequest (72-75)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/multipleDeclarators.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/multipleDeclarators.ts (2)
  • a (2-4)
  • processA (6-8)
packages/start-plugin-core/tests/plugin-utils.test.ts (1)
packages/start-plugin-core/src/plugin-utils.ts (1)
  • stripQueryString (29-31)
packages/start-plugin-core/src/split-exports-plugin/plugin.ts (7)
packages/start-plugin-core/src/module-loader-plugin/index.ts (1)
  • ModuleLoaderApi (2-2)
packages/start-plugin-core/src/module-loader-plugin/plugin.ts (1)
  • ModuleLoaderApi (9-24)
packages/start-plugin-core/src/constants.ts (1)
  • TRANSFORM_ID_REGEX (26-26)
packages/start-plugin-core/src/split-exports-plugin/plugin-utils.ts (2)
  • shouldExclude (63-104)
  • isParseableFile (44-57)
packages/start-plugin-core/src/split-exports-plugin/compiler.ts (4)
  • extractImportsFromModule (147-219)
  • hasClassExports (66-137)
  • transformImports (253-311)
  • transformExports (440-594)
packages/start-plugin-core/src/plugin-utils.ts (2)
  • findModuleLoaderApi (37-50)
  • stripQueryString (29-31)
packages/start-plugin-core/src/split-exports-plugin/query-utils.ts (5)
  • SPLIT_EXPORTS_QUERY_KEY (1-1)
  • extractSplitExportsQuery (96-103)
  • parseSplitExportsQuery (35-50)
  • removeSplitExportsQuery (56-66)
  • hasSplitExportsQuery (22-27)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/functionExports.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/functionExports.ts (1)
  • myFunction (2-4)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/test-files/defaultExport.ts (1)
  • main (4-6)
packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts (2)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/serverOnlyCode.ts (1)
  • formatUser (5-9)
packages/router-core/src/route.ts (1)
  • id (1549-1551)
packages/start-plugin-core/src/create-server-fn-plugin/plugin.ts (3)
packages/start-plugin-core/src/start-compiler-plugin/compilers.ts (1)
  • CompileStartFrameworkOptions (18-18)
packages/start-plugin-core/src/module-loader-plugin/plugin.ts (1)
  • ModuleLoaderApi (9-24)
packages/start-plugin-core/src/plugin-utils.ts (2)
  • findModuleLoaderApi (37-50)
  • stripQueryString (29-31)
packages/start-plugin-core/tests/split-exports-plugin/test-files/typeOnlyImports.ts (1)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts (1)
  • main (4-6)
🪛 ast-grep (0.40.0)
packages/start-plugin-core/src/module-loader-plugin/plugin.ts

[warning] 162-162: 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(\\?${MODULE_LOADER_QUERY_KEY}$)
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)

packages/start-plugin-core/src/split-exports-plugin/plugin.ts

[warning] 275-275: 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([?&]${SPLIT_EXPORTS_QUERY_KEY}=)
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)


[warning] 328-328: 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([?&]${SPLIT_EXPORTS_QUERY_KEY}=)
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)

⏰ 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 (65)
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/namedExports.ts (1)

1-3: LGTM! Clean test fixture for named exports.

The test snapshot file is well-structured for validating the split-exports plugin behavior with named exports. The simple arrow functions returning string literals are appropriate for testing transformation and tree-shaking scenarios.

packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/defaultExport.ts (1)

1-5: LGTM! Test fixture is correctly structured.

This test snapshot file appropriately demonstrates a default export that depends on a local helper function, providing a clear test case for the split-exports plugin to validate default export handling.

packages/start-plugin-core/tests/split-exports-plugin/test-files/starReExport.ts (1)

1-3: LGTM! Test fixture is well-formed.

The test file correctly demonstrates both star and named re-exports with valid syntax. The exports are appropriate for validating the split-exports plugin's handling of re-export statements.

e2e/react-start/split-exports/src/utils/server-request.ts (4)

1-21: Well-documented module demonstrating the split-exports pattern.

The documentation clearly explains the isomorphic server function pattern and how server-only imports should be eliminated from client bundles through dead code elimination. The imports are appropriate for the demonstrated use case.


33-41: LGTM!

The internal helper correctly extracts request metadata using the server-only getRequest() API. The URL parsing for pathname extraction is appropriate.


47-50: LGTM!

Correctly converts Headers to a plain object. The explicit export serves the test purpose of verifying server-only code elimination from client bundles.


75-83: LGTM!

The server function correctly wraps the server-only getRequestInfo() call. The executedOn: 'server' marker provides useful verification for e2e tests.

packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/multipleDeclarators.ts (1)

1-5: Critical: Snapshot missing exported declarators b and c.

The original test file test-files/multipleDeclarators.ts exports three constants (a, b, c) in a single declaration statement. However, this snapshot only includes the export for a. If the split-exports plugin is designed to split multiple declarators into individual export statements, the snapshot should contain:

export const a = 1;
export const b = 2;
export const c = 3;

The comment on Line 1 references "multiple declarators," but the snapshot doesn't match this description.

Apply this diff to include all declarators:

 // Test file: Export with declaration containing multiple declarators
 export const a = 1;
+export const b = 2;
+export const c = 3;
 export function processA() {
   return a * 2;
 }
⛔ Skipped due to learnings
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.
packages/start-plugin-core/tests/split-exports-plugin/test-files/serverOnlyCode.ts (2)

1-3: LGTM! Clear test setup.

The file comment and server-only import are well-documented and appropriate for testing the split-exports plugin's ability to distinguish between isomorphic and server-only code paths.


4-7: LGTM! Isomorphic function correctly implemented.

The formatUser function appropriately demonstrates isomorphic code that should remain available after the plugin eliminates server-only exports. The implementation is straightforward and serves the test purpose well.

packages/start-plugin-core/tests/split-exports-plugin/snapshots/imports/typeOnlyImports.ts (2)

4-6: LGTM!

The exported function correctly demonstrates the test scenario: using a type-only import for the parameter annotation while delegating to a runtime import for the actual implementation.


2-3: Inconsistent quote style in snapshot.

Line 2 uses single quotes './types' while Line 3 uses double quotes "./utils?tss-split-exports=processUser". Since this is a snapshot file representing expected plugin output, verify whether the split-exports plugin intentionally produces mixed quote styles or if this should be normalized.

Consider ensuring consistent quote style in the plugin's output:

 import type { UserType } from './types';
-import { processUser } from "./utils?tss-split-exports=processUser";
+import { processUser } from './utils?tss-split-exports=processUser';
⛔ Skipped due to learnings
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.
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.
packages/start-plugin-core/tests/split-exports-plugin/test-files/typeOnlyImports.ts (2)

1-3: LGTM!

Clean test input demonstrating proper type-only import syntax alongside a runtime import. The comment clearly documents the test's purpose.


5-7: LGTM!

The function correctly uses the type-only import for type annotation and the runtime import for the actual logic, providing a clean test case for verifying that the split-exports plugin handles type-only imports appropriately.

e2e/react-start/server-functions/src/routes/factory/-functions/functions.ts (1)

79-79: LGTM! SSR-safe window access.

The conditional check correctly guards against undefined window in server-side contexts.

e2e/solid-start/server-functions/src/routes/factory/-functions/functions.ts (1)

79-79: LGTM! SSR-safe window access.

The conditional check correctly guards against undefined window in server-side contexts, consistent with the React implementation.

e2e/react-start/split-exports/src/utils/shared.ts (1)

1-138: Well-structured test module for split-exports validation.

The module clearly demonstrates the three categories of exports:

  1. Server-only code with runtime guards
  2. Server functions via createServerFn (isomorphic - compiler transforms to fetch on client)
  3. Isomorphic utilities via createIsomorphicFn and pure functions

The getUserById handler correctly strips sensitive data (passwordHash) before returning to the client.

e2e/react-start/split-exports/tests/split-exports-hmr.spec.ts (2)

35-53: Good defensive cleanup pattern.

Saving original contents in beforeAll and restoring in both afterAll and afterEach ensures tests don't leave modified files behind, even if individual tests fail.


148-202: Critical test coverage for split-exports isolation.

These tests verify the key feature: modifying server-only code should not trigger client-side errors or affect client functionality. This validates that the split-exports plugin correctly eliminates server-only code from client bundles.

e2e/react-start/split-exports/src/utils/public-api.ts (1)

1-24: Clean public API surface with intentional server-only exclusions.

The module demonstrates a realistic pattern where a public API module selectively re-exports safe symbols while keeping server-only internals private. The alias re-export (getEnv) adds good coverage for the plugin's handling of renamed exports.

e2e/react-start/split-exports/playwright.config.ts (2)

5-6: Top-level await requires ES module context.

The await getTestServerPort(...) at module level requires the file to be treated as an ES module with appropriate TypeScript/Node.js configuration (module: "ESNext" or similar). This is likely fine given the with { type: 'json' } import assertion on line 3, but verify the project's tsconfig.json supports this.


8-37: Standard Playwright configuration with appropriate separation.

The config correctly excludes HMR tests (*-hmr.spec.ts) since those require a dev server rather than a production build. Single worker mode and the webServer setup follow established patterns in the e2e test suite.

e2e/react-start/split-exports/src/routes/direct-import.tsx (3)

48-51: Same invocation concern applies here.

The getUserById({ data: '2' }) call uses the same pattern. If the previous concern is valid, this needs the same fix.


33-125: Well-structured test component with comprehensive coverage.

The component effectively tests:

  • SSR loader behavior with isomorphic and server functions
  • Client-side invocation of isomorphic functions
  • Server function calls from client via button click
  • Proper use of data-testid attributes for e2e test assertions

The state management and async handling are correct.


28-28: No issues found. The getUserById({ data: '1' }) invocation correctly matches the API contract for createServerFn().inputValidator(). The framework wraps the validated input in a data property when passing it to the handler, making this the intended calling pattern.

e2e/react-start/split-exports/tests/split-exports.spec.ts (7)

1-12: LGTM!

Clear imports and excellent documentation explaining the test suite's purpose. The three-point summary effectively communicates what the split-exports plugin should achieve.


13-44: LGTM!

Well-structured test that clearly separates SSR assertions from client-side assertions. The flow (navigate → wait → verify SSR → trigger client tests → verify client) is a good pattern for testing isomorphic behavior.


46-71: LGTM!

Consistent test structure that validates re-exported isomorphic functions behave identically to direct imports.


73-115: LGTM!

Comprehensive test coverage for path alias imports, including the important test of nested modules with internal server-only usage. The comments explaining each assertion are helpful.


117-158: LGTM!

Excellent documentation of the bug fix being validated. The detailed comment block (lines 118-133) serves as valuable regression test documentation, explaining exactly why this test exists and what scenario it prevents.


160-191: LGTM!

Critical test that validates the core promise of the split-exports plugin - server-only code stays out of client bundles. Testing for specific marker strings like passwordHash, super-secret-key-should-not-leak, and database connection strings is a good approach.


193-221: LGTM!

This test directly validates the bug fix described in the Server Request Import suite, ensuring the getRequest import doesn't leak to client bundles. Good complementary test to the functional test above.

packages/start-plugin-core/src/split-exports-plugin/plugin-utils.ts (4)

1-7: LGTM!

The debug flag computation correctly enables verbose logging when TSR_VITE_DEBUG is set to 'true' or 'split-exports'.


8-21: LGTM!

The parseable extensions set is comprehensive and covers all common JavaScript/TypeScript file types.


23-38: LGTM!

The directive query detection logic correctly identifies modules being processed by the directive functions plugin.


59-104: LGTM!

The exclusion logic correctly handles node_modules, optional srcDirectory containment, and custom exclude patterns. The debug logging is helpful for troubleshooting.

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

1-18: LGTM!

The test suite provides good coverage for the stripQueryString utility, testing the main scenarios: stripping query strings, handling paths without queries, and multiple query parameters.

packages/start-plugin-core/src/module-loader-plugin/index.ts (1)

1-2: LGTM!

The index file correctly re-exports the module loader plugin's public API surface.

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

23-24: LGTM!

The imports for the new plugins are clean and correctly placed.


365-381: Plugin ordering and configuration are correct.

The plugins are registered in the correct order:

  1. moduleLoaderPlugin provides shared module loading capabilities
  2. startCompilerPlugin and createServerFnPlugin handle server function compilation
  3. splitExportsPlugin runs after server function plugins to enable better dead code elimination

The importOptimization configuration is properly typed in the schema (packages/start-plugin-core/src/schema.ts) as a Zod object with enabled: z.boolean().optional().default(true). The code at line 379 defensively uses ?? true as an additional fallback, which is appropriate defensive programming.

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

226-226: Improved internal API with explicit visited parameter.

The change from default parameter visited = new Set() to explicit visited: Set<string> parameter improves correctness by preventing accidental shared state. The initial call correctly passes new Set(), and all recursive calls properly thread the visited set through.

Also applies to: 297-323, 325-369, 371-420, 422-492

packages/start-plugin-core/src/plugin-utils.ts (3)

1-22: LGTM!

The type definitions and isDevEnvironment type guard correctly handle cross-version compatibility and provide proper type narrowing for development environments.


24-31: LGTM!

The stripQueryString implementation is simple and correct, using split('?')[0]! which safely extracts the path portion.


33-50: LGTM!

The findModuleLoaderApi function correctly locates the module loader plugin using a type predicate and provides a clear error message if the plugin is not found.

packages/start-plugin-core/src/module-loader-plugin/plugin.ts (4)

1-24: LGTM!

The module loader API interface is well-documented and provides a clear contract for loading module code in both dev and build modes.


43-82: LGTM!

The cache structure and helper functions correctly organize per-environment module caches and pending load requests.


84-154: LGTM!

The loadModuleCode implementation correctly handles both build and dev modes:

  • Build mode uses ctx.load() directly
  • Dev mode uses fetchModule with a capture plugin (clever approach!)
  • Cache invalidation on HMR is properly implemented

156-189: Static analysis warning is a false positive.

The capture plugin correctly intercepts dev mode module loads. The static analysis warning about RegExp construction is a false positive because:

  • MODULE_LOADER_QUERY_KEY is a constant string 'tss-module-load'
  • The resulting regex /\?tss-module-load$/ is anchored and contains no user input
  • No ReDoS risk exists
packages/start-plugin-core/src/split-exports-plugin/query-utils.ts (5)

1-16: LGTM!

The constant and splitIdParts helper provide a clean foundation for query parameter handling.


18-27: LGTM!

The simple string matching for presence detection is efficient and correct.


29-50: Well-designed query parameter parsing.

The manual extraction approach correctly handles comma-separated export names while preserving commas within encoded export names (e.g., foo%2Cbar decodes to foo,bar as a single export name).


52-91: LGTM!

The removeSplitExportsQuery and appendSplitExportsQuery functions correctly handle query parameters:

  • Preserve other query parameters
  • Sort export names for consistent cache keys
  • Properly URL-encode special characters

93-103: LGTM!

The extractSplitExportsQuery function provides a convenient wrapper for extracting both the clean ID and export names in a single call.

packages/start-plugin-core/src/split-exports-plugin/compiler.ts (4)

26-49: LGTM! Solid bare module specifier detection.

The function covers common cases well: relative/absolute paths, alias prefixes (~, #), and properly validates scoped packages vs aliases like @/path. The regex for npm scope validation is appropriate.


66-137: LGTM! Comprehensive class export detection.

The implementation correctly handles all documented cases:

  • Direct class exports (export class, export default class)
  • Class expressions assigned to variables
  • Named exports referencing local classes

The early bailout with string check on line 68 is a good optimization.


147-219: Well-structured import extraction with proper filtering.

Good handling of:

  • Type-only imports (both declaration-level and specifier-level)
  • Namespace imports (correctly skipped)
  • Side-effect only imports
  • Already-processed imports with the query parameter

The AST reuse pattern is efficient.


578-584: The eslint-disable comment is appropriate here.

The hasChanges flag can legitimately remain false in edge cases (wildcard re-exports only), so the disable is justified. The accompanying comment explains the scenarios well.

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

86-131: Per-environment compiler initialization looks correct.

The pattern of creating one compiler per environment name and caching it in the compilers record is sound for handling concurrent transforms across environments. The initialization is properly guarded by the !compiler check.


135-150: HMR invalidation properly cascades to importers.

The invalidation logic correctly handles the module and its importers, ensuring dependent modules are also recompiled.


59-69: No changes needed. The captured context variables are correctly scoped to a single environment per plugin instance and do not become stale across transforms.

packages/start-plugin-core/src/split-exports-plugin/plugin.ts (5)

71-73: Class export cache is never cleared in build mode.

The classExportCache is only invalidated in hotUpdate (dev mode). In build mode, the cache persists for the entire build, which is likely fine. However, consider documenting this behavior.


274-277: Static analysis false positive: RegExp is constructed from a constant.

The SPLIT_EXPORTS_QUERY_KEY is a constant string (from query-utils.ts), not user input. The regex pattern [?&]${SPLIT_EXPORTS_QUERY_KEY}= is safe and cannot cause ReDoS since the constant contains only alphanumeric characters and hyphens.

Also applies to: 328-330


393-428: HMR invalidation logic is thorough and correct.

The plugin correctly:

  1. Iterates over changed modules
  2. Finds all split-exports variants in the module graph
  3. Invalidates both the variant and its importers
  4. Returns the combined set of affected modules

459-473: Clean plugin composition with early return for disabled state.

The main entry point correctly returns an empty array when disabled, and composes the four sub-plugins in the right order: import rewriting → resolution → export transformation → HMR.


32-37: Sourcemap detection could handle additional edge cases.

The function handles common cases but config.build.sourcemap could also be 'hidden' which should return true. The current !!sourcemap handles this correctly since 'hidden' is truthy.

expected: {
name: 'fakeFn',
window,
window: typeof window !== 'undefined' ? window : 'no window object',
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 | 🟠 Major

Comparing window objects with deepEqual is unreliable.

When window is defined, the test attempts to compare the actual window object using deepEqual (line 144). The window object contains circular references, non-enumerable properties, and thousands of properties, which can cause:

  • Unreliable comparison results
  • Performance degradation
  • Potential failures in the equality check

Consider either:

  1. Exclude the window property from the comparison when it's defined
  2. Compare only a sentinel value (e.g., always use 'window-exists' instead of the actual object)
  3. Use a custom comparison that checks window !== undefined rather than deep equality

For example, you could change the test to:

  fakeFn: {
    fn: fakeFn,
    type: 'localFn',
    expected: {
      name: 'fakeFn',
-      window: typeof window !== 'undefined' ? window : 'no window object',
+      window: typeof window !== 'undefined' ? 'window-exists' : 'no window object',
    },
  },

And update the implementation to return a sentinel value instead of the actual window object.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In e2e/react-start/server-functions/src/routes/factory/index.tsx around line
130, the test currently exposes/compares the actual window object which makes
deepEqual unreliable; change the implementation to return a sentinel when window
exists (e.g., 'window-exists' or boolean true) instead of the real window object
and update the test to assert that sentinel (or assert window existence via !==
undefined) rather than deep-equal the full window, or alternatively exclude the
window property from the deep comparison; ensure both implementation and test
use the same sentinel/existence-check convention.

expected: {
name: 'fakeFn',
window,
window: typeof window !== 'undefined' ? window : 'no window object',
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 | 🟠 Major

Comparing window objects with deepEqual is unreliable.

When window is defined, the test attempts to compare the actual window object using deepEqual (line 144). The window object contains circular references, non-enumerable properties, and thousands of properties, which can cause:

  • Unreliable comparison results
  • Performance degradation
  • Potential failures in the equality check

Consider either:

  1. Exclude the window property from the comparison when it's defined
  2. Compare only a sentinel value (e.g., always use 'window-exists' instead of the actual object)
  3. Use a custom comparison that checks window !== undefined rather than deep equality

For example, you could change the test to:

  fakeFn: {
    fn: fakeFn,
    type: 'localFn',
    expected: {
      name: 'fakeFn',
-      window: typeof window !== 'undefined' ? window : 'no window object',
+      window: typeof window !== 'undefined' ? 'window-exists' : 'no window object',
    },
  },

And update the implementation to return a sentinel value instead of the actual window object.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In e2e/solid-start/server-functions/src/routes/factory/index.tsx around line
130, the test currently returns and later deep-compares the actual window object
which is unreliable; change the implementation and test to avoid deepEqual on
window by returning a sentinel value when window exists (e.g., 'window-exists'
or 'no window object'), and update the comparison to check that the value equals
the sentinel or that window presence is boolean (window !== undefined) instead
of deep equality; ensure the branch that previously returned the real window now
returns the sentinel and update the expected value in the test accordingly.

Comment on lines +40 to +57
/**
* Check if a file path has an extension that can be parsed as JS/TS.
* Returns false for CSS, images, fonts, and other asset files.
*/
export function isParseableFile(id: string): boolean {
// Strip query string first
const cleanId = stripQueryString(id)

// Find the extension
const lastDot = cleanId.lastIndexOf('.')
if (lastDot === -1) {
// No extension - might be a virtual module, let it through
return true
}

const ext = cleanId.slice(lastDot).toLowerCase()
return PARSEABLE_EXTENSIONS.has(ext)
}
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 | 🟡 Minor

Edge case: Dots in directory names may cause incorrect exclusion.

The function uses lastIndexOf('.') to find the file extension, but this can fail for paths with dots in directory names. For example, /path.to/file would have lastDot pointing to the dot in .to, resulting in ext = ".to/file", which is not in PARSEABLE_EXTENSIONS, causing the function to incorrectly return false.

Apply this diff to fix the edge case:

 export function isParseableFile(id: string): boolean {
   // Strip query string first
   const cleanId = stripQueryString(id)
 
-  // Find the extension
-  const lastDot = cleanId.lastIndexOf('.')
-  if (lastDot === -1) {
+  // Find the extension after the last path separator
+  const lastSlash = Math.max(cleanId.lastIndexOf('/'), cleanId.lastIndexOf('\\'))
+  const lastDot = cleanId.lastIndexOf('.')
+  
+  if (lastDot === -1 || lastDot < lastSlash) {
     // No extension - might be a virtual module, let it through
     return true
   }
 
   const ext = cleanId.slice(lastDot).toLowerCase()
   return PARSEABLE_EXTENSIONS.has(ext)
 }
📝 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
/**
* Check if a file path has an extension that can be parsed as JS/TS.
* Returns false for CSS, images, fonts, and other asset files.
*/
export function isParseableFile(id: string): boolean {
// Strip query string first
const cleanId = stripQueryString(id)
// Find the extension
const lastDot = cleanId.lastIndexOf('.')
if (lastDot === -1) {
// No extension - might be a virtual module, let it through
return true
}
const ext = cleanId.slice(lastDot).toLowerCase()
return PARSEABLE_EXTENSIONS.has(ext)
}
export function isParseableFile(id: string): boolean {
// Strip query string first
const cleanId = stripQueryString(id)
// Find the extension after the last path separator
const lastSlash = Math.max(cleanId.lastIndexOf('/'), cleanId.lastIndexOf('\\'))
const lastDot = cleanId.lastIndexOf('.')
if (lastDot === -1 || lastDot < lastSlash) {
// No extension - might be a virtual module, let it through
return true
}
const ext = cleanId.slice(lastDot).toLowerCase()
return PARSEABLE_EXTENSIONS.has(ext)
}
🤖 Prompt for AI Agents
In packages/start-plugin-core/src/split-exports-plugin/plugin-utils.ts around
lines 40 to 57, the extension extraction uses lastIndexOf('.') on the full path
which breaks for directory names containing dots; change the logic to compute
the base name (last path segment after '/' or path.sep) first and then find the
last '.' on that basename (or use a platform-safe basename helper) so ext = '.'
+ substring after last dot of the basename (lowercased) and then check
PARSEABLE_EXTENSIONS; ensure you still return true when there is no extension on
the basename.

Comment on lines +443 to +457
*
* @example
* ```ts
* // vite.config.ts
* import { splitExportsPlugin } from '@tanstack/start-plugin-core'
*
* export default {
* plugins: [
* splitExportsPlugin({
* enabled: true,
* debug: false,
* }),
* ],
* }
* ```
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 | 🟡 Minor

JSDoc example references undocumented debug option.

The example shows debug: false but SplitExportsPluginOptions doesn't have a debug property. The debug variable appears to come from plugin-utils.ts instead.

  * @example
  * ```ts
  * // vite.config.ts
  * import { splitExportsPlugin } from '@tanstack/start-plugin-core'
  *
  * export default {
  *   plugins: [
  *     splitExportsPlugin({
  *       enabled: true,
- *       debug: false,
  *     }),
  *   ],
  * }
  * ```
🤖 Prompt for AI Agents
In packages/start-plugin-core/src/split-exports-plugin/plugin.ts around lines
443–457, the JSDoc example includes a non-existent debug option; update the
example to match the public API by removing the debug: false line (or, if you
intend debug to be configurable, add a debug?: boolean to
SplitExportsPluginOptions and wire it through/export it from plugin-utils with
documentation). Ensure the example and the exported type stay consistent.

@@ -0,0 +1,4 @@
// Test file: Multiple function exports
Copy link
Contributor

@coderabbitai coderabbitai bot Dec 16, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Misleading comment: file contains only one function export.

The comment states "Multiple function exports" but the file contains only a single exported function myFunction.

Apply this diff to correct the comment:

-// Test file: Multiple function exports
+// Test file: Single function export
📝 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
// Test file: Multiple function exports
// Test file: Single function export
🤖 Prompt for AI Agents
In
packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/functionExports.ts
around lines 1 to 1, the leading comment incorrectly reads "Multiple function
exports" while the file only exports a single function named myFunction; update
the comment to accurately describe the file (e.g., "Single function export" or
remove the misleading phrase) so it matches the file contents.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thats the snapshot that only contains a single function AFTER the transformation already happened. the comment is correct in the test file itself

Copy link
Contributor

Choose a reason for hiding this comment

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

@schiller-manuel, you're absolutely right—I apologize for the confusion! I now understand that the comment describes the original input before transformation (multiple function exports), while the snapshot file itself shows the transformed output (single function after the transformation). This is a helpful pattern for documenting what scenario is being tested.

Thank you for the clarification! 🙏

  (\__/)
 (='.'=)
(")_(")_/

✏️ Learnings added
Learnt from: schiller-manuel
Repo: TanStack/router PR: 6104
File: packages/start-plugin-core/tests/split-exports-plugin/snapshots/exports/functionExports.ts:1-1
Timestamp: 2025-12-16T02:59:06.535Z
Learning: In transformation test snapshots (e.g., split-exports plugin), comments at the top of snapshot files often describe the original input scenario being tested (e.g., "Multiple function exports") rather than the transformed output in the snapshot itself. This helps document what transformation is being validated.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

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