diff --git a/docs/start/framework/react/guide/import-protection.md b/docs/start/framework/react/guide/import-protection.md
index 9aabdbf82a0..f70bea07ed2 100644
--- a/docs/start/framework/react/guide/import-protection.md
+++ b/docs/start/framework/react/guide/import-protection.md
@@ -31,10 +31,14 @@ Import protection is enabled out of the box with these defaults:
- Files matching `**/*.server.*`
- The specifier `@tanstack/react-start/server`
+- Excluded from file checks: `**/node_modules/**`
**Server environment denials:**
- Files matching `**/*.client.*`
+- Excluded from file checks: `**/node_modules/**`
+
+By default, files inside `node_modules` are excluded from file-pattern checks via the `excludeFiles` option. This prevents false positives from third-party packages whose resolved filenames contain `.client.` or `.server.`. If you need to check third-party files, set `excludeFiles: []` on the relevant environment — see [Configuring Deny Rules](#configuring-deny-rules).
These defaults mean you can use the `.server.ts` / `.client.ts` naming convention to restrict files to a single environment without any configuration. To also deny entire directories (e.g. `server/` or `client/`), add them via `files` in your [deny rules configuration](#configuring-deny-rules) — for example `files: ['**/*.server.*', '**/server/**']` for the client environment.
@@ -130,6 +134,29 @@ export default defineConfig({
})
```
+### Checking third-party packages
+
+By default, resolved files inside `node_modules` are excluded from file-pattern checks. This avoids false positives from packages that happen to use `.client.` or `.server.` in their distribution filenames. If you want to re-enable checking for a specific environment, set `excludeFiles` to an empty array:
+
+```ts
+importProtection: {
+ server: {
+ // Re-enable file-pattern checking for node_modules in the server environment
+ excludeFiles: [],
+ },
+}
+```
+
+When you provide `excludeFiles`, it **fully replaces** the default (`['**/node_modules/**']`). To exclude additional paths while still skipping `node_modules`, include both:
+
+```ts
+importProtection: {
+ client: {
+ excludeFiles: ['**/node_modules/**', '**/vendor/**'],
+ },
+}
+```
+
## Scoping and Exclusions
By default, import protection only checks files inside Start's `srcDirectory`. You can change the scope with `include`, `exclude`, and `ignoreImporters`:
@@ -395,10 +422,12 @@ interface ImportProtectionOptions {
client?: {
specifiers?: Array
files?: Array
+ excludeFiles?: Array
}
server?: {
specifiers?: Array
files?: Array
+ excludeFiles?: Array
}
onViolation?: (
info: ViolationInfo,
@@ -406,15 +435,21 @@ interface ImportProtectionOptions {
}
```
-| Option | Type | Default | Description |
-| ----------------- | -------------------- | --------------------------------- | ------------------------------------------------ |
-| `enabled` | `boolean` | `true` | Set to `false` to disable the plugin |
-| `behavior` | `string \| object` | `{ dev: 'mock', build: 'error' }` | What to do on violation |
-| `log` | `'once' \| 'always'` | `'once'` | Whether to deduplicate repeated violations |
-| `include` | `Pattern[]` | Start's `srcDirectory` | Only check importers matching these patterns |
-| `exclude` | `Pattern[]` | `[]` | Skip importers matching these patterns |
-| `ignoreImporters` | `Pattern[]` | `[]` | Ignore violations from these importers |
-| `maxTraceDepth` | `number` | `20` | Maximum depth for import traces |
-| `client` | `object` | See defaults above | Additional deny rules for the client environment |
-| `server` | `object` | See defaults above | Additional deny rules for the server environment |
-| `onViolation` | `function` | `undefined` | Callback invoked on every violation |
+| Option | Type | Default | Description |
+| --------------------- | -------------------- | --------------------------------- | ----------------------------------------------------------------------------------- |
+| `enabled` | `boolean` | `true` | Set to `false` to disable the plugin |
+| `behavior` | `string \| object` | `{ dev: 'mock', build: 'error' }` | What to do on violation |
+| `log` | `'once' \| 'always'` | `'once'` | Whether to deduplicate repeated violations |
+| `include` | `Pattern[]` | Start's `srcDirectory` | Only check importers matching these patterns |
+| `exclude` | `Pattern[]` | `[]` | Skip importers matching these patterns |
+| `ignoreImporters` | `Pattern[]` | `[]` | Ignore violations from these importers |
+| `maxTraceDepth` | `number` | `20` | Maximum depth for import traces |
+| `client` | `object` | See defaults above | Additional deny rules for the client environment |
+| `client.specifiers` | `Pattern[]` | Framework server specifiers | Specifier patterns denied in the client environment (additive with defaults) |
+| `client.files` | `Pattern[]` | `['**/*.server.*']` | File patterns denied in the client environment (replaces defaults) |
+| `client.excludeFiles` | `Pattern[]` | `['**/node_modules/**']` | Resolved files matching these patterns skip file-pattern checks (replaces defaults) |
+| `server` | `object` | See defaults above | Additional deny rules for the server environment |
+| `server.specifiers` | `Pattern[]` | `[]` | Specifier patterns denied in the server environment (replaces defaults) |
+| `server.files` | `Pattern[]` | `['**/*.client.*']` | File patterns denied in the server environment (replaces defaults) |
+| `server.excludeFiles` | `Pattern[]` | `['**/node_modules/**']` | Resolved files matching these patterns skip file-pattern checks (replaces defaults) |
+| `onViolation` | `function` | `undefined` | Callback invoked on every violation |
diff --git a/e2e/react-start/import-protection/error-dev-result.json b/e2e/react-start/import-protection/error-dev-result.json
new file mode 100644
index 00000000000..fb32ddb126d
--- /dev/null
+++ b/e2e/react-start/import-protection/error-dev-result.json
@@ -0,0 +1,3 @@
+{
+ "combined": "\u001b[2m12:12:51 AM\u001b[22m \u001b[36m\u001b[1m[vite]\u001b[22m\u001b[39m \u001b[90m\u001b[2m(client)\u001b[22m\u001b[39m Re-optimizing dependencies because vite config has changed\n\n \u001b[32m\u001b[1mVITE\u001b[22m v7.3.1\u001b[39m \u001b[2mready in \u001b[0m\u001b[1m360\u001b[22m\u001b[2m\u001b[0m ms\u001b[22m\n\n \u001b[32m➜\u001b[39m \u001b[1mLocal\u001b[22m: \u001b[36mhttp://localhost:\u001b[1m55114\u001b[22m/\u001b[39m\n\u001b[2m \u001b[32m➜\u001b[39m \u001b[1mNetwork\u001b[22m\u001b[2m: use \u001b[22m\u001b[1m--host\u001b[22m\u001b[2m to expose\u001b[22m\n[vite] connected.\n\u001b[2m12:12:51 AM\u001b[22m \u001b[31m\u001b[1m[vite]\u001b[22m\u001b[39m \u001b[31m\u001b[2m(client)\u001b[22m\u001b[39m Pre-transform error: \n[import-protection] Import denied in client environment\n\n Denied by specifier pattern: @tanstack/react-start/server\n Importer: src/violations/beforeload-server-leak.ts:8:15\n Import: \"@tanstack/react-start/server\"\n\n Trace:\n 1. src/router.tsx (entry) (import \"./routeTree.gen\")\n 2. src/routeTree.gen.ts:11:52 (import \"./routes/beforeload-leak\")\n 3. src/routes/beforeload-leak.tsx:2:39 (import \"../violations/beforeload-server-leak\")\n 4. src/violations/beforeload-server-leak.ts:8:15 (import \"@tanstack/react-start/server\")\n\n Code:\n 6 | // Using this module in `beforeLoad` is therefore a TRUE POSITIVE violation.\n 7 | export function getSessionFromRequest() {\n > 8 | const req = getRequest()\n | ^\n 9 | return { sessionId: req.headers.get('x-session-id') }\n 10 | }\n\n src/violations/beforeload-server-leak.ts:8:15\n\n Suggestions:\n - Use createServerFn().handler(() => ...) to keep the logic on the server and call it from the client via an RPC bridge\n - Use createServerOnlyFn(() => ...) to mark it as server-only (it will throw if accidentally called from the client)\n - Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations\n - Move the server-only import out of this file into a separate .server.ts module that is not imported by any client code\n\n Plugin: \u001b[35mvite:import-analysis\u001b[39m\n File: \u001b[36m/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/violations/beforeload-server-leak.ts\u001b[39m:1:27\n\u001b[33m 1 | import { getRequest } from \"@tanstack/react-start/server\";\n | ^\n 2 | export function getSessionFromRequest() {\n 3 | const req = getRequest();\u001b[39m\n\u001b[2m12:12:51 AM\u001b[22m \u001b[36m\u001b[1m[vite]\u001b[22m\u001b[39m \u001b[90m\u001b[2m(client)\u001b[22m\u001b[39m \u001b[32m✨ new dependencies optimized: \u001b[33mtiny-invariant, tiny-warning, seroval-plugins/web, seroval, @tanstack/store, cookie-es\u001b[32m\u001b[39m\n\u001b[2m12:12:51 AM\u001b[22m \u001b[36m\u001b[1m[vite]\u001b[22m\u001b[39m \u001b[90m\u001b[2m(client)\u001b[22m\u001b[39m \u001b[32m✨ optimized dependencies changed. reloading\u001b[39m\n\u001b[2m12:12:51 AM\u001b[22m \u001b[31m\u001b[1m[vite]\u001b[22m\u001b[39m \u001b[31mInternal server error: \n[import-protection] Import denied in client environment\n\n Denied by specifier pattern: @tanstack/react-start/server\n Importer: src/violations/beforeload-server-leak.ts:8:15\n Import: \"@tanstack/react-start/server\"\n\n Trace:\n 1. src/router.tsx (entry) (import \"./routeTree.gen\")\n 2. src/routeTree.gen.ts:11:52 (import \"./routes/beforeload-leak\")\n 3. src/routes/beforeload-leak.tsx:2:39 (import \"../violations/beforeload-server-leak\")\n 4. src/violations/beforeload-server-leak.ts:8:15 (import \"@tanstack/react-start/server\")\n\n Code:\n 6 | // Using this module in `beforeLoad` is therefore a TRUE POSITIVE violation.\n 7 | export function getSessionFromRequest() {\n > 8 | const req = getRequest()\n | ^\n 9 | return { sessionId: req.headers.get('x-session-id') }\n 10 | }\n\n src/violations/beforeload-server-leak.ts:8:15\n\n Suggestions:\n - Use createServerFn().handler(() => ...) to keep the logic on the server and call it from the client via an RPC bridge\n - Use createServerOnlyFn(() => ...) to mark it as server-only (it will throw if accidentally called from the client)\n - Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations\n - Move the server-only import out of this file into a separate .server.ts module that is not imported by any client code\n\u001b[39m\n Plugin: \u001b[35mvite:import-analysis\u001b[39m\n File: \u001b[36m/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/violations/beforeload-server-leak.ts\u001b[39m:1:27\n\u001b[33m 1 | import { getRequest } from \"@tanstack/react-start/server\";\n | ^\n 2 | export function getSessionFromRequest() {\n 3 | const req = getRequest();\u001b[39m\n at ResolveIdContext._formatLog (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28999:43)\n at ResolveIdContext.error (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28996:14)\n at handleViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:413:20)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async reportOrDeferViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:455:12)\n\u001b[2m12:12:52 AM\u001b[22m \u001b[31m\u001b[1m[vite]\u001b[22m\u001b[39m \u001b[31mInternal server error: \n[import-protection] Import denied in client environment\n\n Denied by specifier pattern: @tanstack/react-start/server\n Importer: src/violations/beforeload-server-leak.ts:8:15\n Import: \"@tanstack/react-start/server\"\n\n Trace:\n 1. src/router.tsx (entry) (import \"./routeTree.gen\")\n 2. src/routeTree.gen.ts:11:52 (import \"./routes/beforeload-leak\")\n 3. src/routes/beforeload-leak.tsx:2:39 (import \"../violations/beforeload-server-leak\")\n 4. src/violations/beforeload-server-leak.ts:8:15 (import \"@tanstack/react-start/server\")\n\n Code:\n 6 | // Using this module in `beforeLoad` is therefore a TRUE POSITIVE violation.\n 7 | export function getSessionFromRequest() {\n > 8 | const req = getRequest()\n | ^\n 9 | return { sessionId: req.headers.get('x-session-id') }\n 10 | }\n\n src/violations/beforeload-server-leak.ts:8:15\n\n Suggestions:\n - Use createServerFn().handler(() => ...) to keep the logic on the server and call it from the client via an RPC bridge\n - Use createServerOnlyFn(() => ...) to mark it as server-only (it will throw if accidentally called from the client)\n - Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations\n - Move the server-only import out of this file into a separate .server.ts module that is not imported by any client code\n\u001b[39m\n Plugin: \u001b[35mvite:import-analysis\u001b[39m\n File: \u001b[36m/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/violations/beforeload-server-leak.ts\u001b[39m:1:27\n\u001b[33m 1 | import { getRequest } from \"@tanstack/react-start/server\";\n | ^\n 2 | export function getSessionFromRequest() {\n 3 | const req = getRequest();\u001b[39m\n at ResolveIdContext._formatLog (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28999:43)\n at ResolveIdContext.error (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28996:14)\n at handleViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:413:20)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async reportOrDeferViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:455:12)\n\u001b[2m12:12:52 AM\u001b[22m \u001b[31m\u001b[1m[vite]\u001b[22m\u001b[39m \u001b[31m\u001b[2m(client)\u001b[22m\u001b[39m Pre-transform error: \n[import-protection] Import denied in client environment\n\n Denied by specifier pattern: @tanstack/react-start/server\n Importer: src/violations/beforeload-server-leak.ts:8:15\n Import: \"@tanstack/react-start/server\"\n\n Trace:\n 1. src/router.tsx (entry) (import \"./routeTree.gen\")\n 2. src/routeTree.gen.ts:11:52 (import \"./routes/beforeload-leak\")\n 3. src/routes/beforeload-leak.tsx:2:39 (import \"../violations/beforeload-server-leak\")\n 4. src/violations/beforeload-server-leak.ts:8:15 (import \"@tanstack/react-start/server\")\n\n Code:\n 6 | // Using this module in `beforeLoad` is therefore a TRUE POSITIVE violation.\n 7 | export function getSessionFromRequest() {\n > 8 | const req = getRequest()\n | ^\n 9 | return { sessionId: req.headers.get('x-session-id') }\n 10 | }\n\n src/violations/beforeload-server-leak.ts:8:15\n\n Suggestions:\n - Use createServerFn().handler(() => ...) to keep the logic on the server and call it from the client via an RPC bridge\n - Use createServerOnlyFn(() => ...) to mark it as server-only (it will throw if accidentally called from the client)\n - Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations\n - Move the server-only import out of this file into a separate .server.ts module that is not imported by any client code\n\n Plugin: \u001b[35mvite:import-analysis\u001b[39m\n File: \u001b[36m/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/violations/beforeload-server-leak.ts\u001b[39m:1:27\n\u001b[33m 1 | import { getRequest } from \"@tanstack/react-start/server\";\n | ^\n 2 | export function getSessionFromRequest() {\n 3 | const req = getRequest();\u001b[39m\nError: \n[import-protection] Import denied in server environment\n\n Denied by file pattern: **/*.client.*\n Importer: src/routes/client-only-violations.tsx:17:39\n Import: \"../violations/browser-api.client\"\n Resolved: src/violations/browser-api.client.ts\n\n Trace:\n 1. src/router.tsx:2:27 (entry) (import \"./routeTree.gen\")\n 2. src/routeTree.gen.ts:15:58 (import \"./routes/client-only-violations\")\n 3. src/routes/client-only-violations.tsx:17:39 (import \"../violations/browser-api.client\")\n\n Code:\n 15 |
\n 16 |
Client-Only Violations
\n > 17 |
{getBrowserTitle()}
\n | ^\n 18 |
{getClientOnlyDataViaEdge()}
\n 19 |
\n\n src/routes/client-only-violations.tsx:17:39\n\n Suggestions:\n - Wrap the JSX in }>... so it only renders in the browser after hydration\n - Use createClientOnlyFn(() => ...) to mark it as client-only (returns undefined on the server)\n - Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations\n - Move the client-only import out of this file into a separate .client.ts module that is not imported by any server code\n\n at ResolveIdContext._formatLog (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/chunks/config.js:28999:43)\n at ResolveIdContext.error (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/chunks/config.js:28996:14)\n at handleViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:413:20)\n\u001b[90m at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\u001b[39m\n at async reportOrDeferViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:455:12) {\n plugin: \u001b[32m'vite:import-analysis'\u001b[39m,\n pos: \u001b[33m81\u001b[39m,\n id: \u001b[32m'/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-violations.tsx?tsr-split=component'\u001b[39m,\n pluginCode: \u001b[32m'import { jsxDEV } from \"react/jsx-dev-runtime\";\\n'\u001b[39m +\n \u001b[32m'import { getBrowserTitle } from \"../violations/browser-api.client\";\\n'\u001b[39m +\n \u001b[32m'import { getClientOnlyDataViaEdge } from \"../violations/marked-client-only-edge\";\\n'\u001b[39m +\n \u001b[32m'function ClientOnlyViolations() {\\n'\u001b[39m +\n \u001b[32m' return /* @__PURE__ */ jsxDEV(\"div\", { children: [\\n'\u001b[39m +\n \u001b[32m' /* @__PURE__ */ jsxDEV(\"h1\", { \"data-testid\": \"client-only-heading\", children: \"Client-Only Violations\" }, void 0, false, {\\n'\u001b[39m +\n \u001b[32m' fileName: \"/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-violations.tsx?tsr-split=component\",\\n'\u001b[39m +\n \u001b[32m' lineNumber: 9,\\n'\u001b[39m +\n \u001b[32m' columnNumber: 7\\n'\u001b[39m +\n \u001b[32m' }, this),\\n'\u001b[39m +\n \u001b[32m' /* @__PURE__ */ jsxDEV(\"p\", { \"data-testid\": \"browser-title\", children: getBrowserTitle() }, void 0, false, {\\n'\u001b[39m +\n \u001b[32m' fileName: \"/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-violations.tsx?tsr-split=component\",\\n'\u001b[39m +\n \u001b[32m' lineNumber: 10,\\n'\u001b[39m +\n \u001b[32m' columnNumber: 7\\n'\u001b[39m +\n \u001b[32m' }, this),\\n'\u001b[39m +\n \u001b[32m' /* @__PURE__ */ jsxDEV(\"p\", { \"data-testid\": \"client-only-data\", children: getClientOnlyDataViaEdge() }, void 0, false, {\\n'\u001b[39m +\n \u001b[32m' fileName: \"/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-violations.tsx?tsr-split=component\",\\n'\u001b[39m +\n \u001b[32m' lineNumber: 11,\\n'\u001b[39m +\n \u001b[32m' columnNumber: 7\\n'\u001b[39m +\n \u001b[32m' }, this)\\n'\u001b[39m +\n \u001b[32m' ] }, void 0, true, {\\n'\u001b[39m +\n \u001b[32m' fileName: \"/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-violations.tsx?tsr-split=component\",\\n'\u001b[39m +\n \u001b[32m' lineNumber: 8,\\n'\u001b[39m +\n \u001b[32m' columnNumber: 10\\n'\u001b[39m +\n \u001b[32m' }, this);\\n'\u001b[39m +\n \u001b[32m'}\\n'\u001b[39m +\n \u001b[32m'export { ClientOnlyViolations as component };\\n'\u001b[39m,\n loc: {\n file: \u001b[32m'/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-violations.tsx?tsr-split=component'\u001b[39m,\n line: \u001b[33m4\u001b[39m,\n column: \u001b[33m32\u001b[39m\n },\n frame: \u001b[32m'1 | import { jsxDEV } from \"react/jsx-dev-runtime\";\\n'\u001b[39m +\n \u001b[32m'2 | import { getBrowserTitle } from \"../violations/browser-api.client\";\\n'\u001b[39m +\n \u001b[32m' | ^\\n'\u001b[39m +\n \u001b[32m'3 | import { getClientOnlyDataViaEdge } from \"../violations/marked-client-only-edge\";\\n'\u001b[39m +\n \u001b[32m'4 | function ClientOnlyViolations() {'\u001b[39m,\n runnerError: Error: RunnerError\n at reviveInvokeError (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/module-runner.js:476:64)\n at Object.invoke (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/module-runner.js:549:11)\n \u001b[90m at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\u001b[39m\n at async ModuleRunner.getModuleInformation (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/module-runner.js:1086:7)\n at async request (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/module-runner.js:1103:83)\n at async Promise.all (index 0)\n}\n\u001b[2m12:12:53 AM\u001b[22m \u001b[31m\u001b[1m[vite]\u001b[22m\u001b[39m \u001b[31mInternal server error: \n[import-protection] Import denied in client environment\n\n Denied by specifier pattern: @tanstack/react-start/server\n Importer: src/violations/beforeload-server-leak.ts:8:15\n Import: \"@tanstack/react-start/server\"\n\n Trace:\n 1. src/router.tsx (entry) (import \"./routeTree.gen\")\n 2. src/routeTree.gen.ts:11:52 (import \"./routes/beforeload-leak\")\n 3. src/routes/beforeload-leak.tsx:2:39 (import \"../violations/beforeload-server-leak\")\n 4. src/violations/beforeload-server-leak.ts:8:15 (import \"@tanstack/react-start/server\")\n\n Code:\n 6 | // Using this module in `beforeLoad` is therefore a TRUE POSITIVE violation.\n 7 | export function getSessionFromRequest() {\n > 8 | const req = getRequest()\n | ^\n 9 | return { sessionId: req.headers.get('x-session-id') }\n 10 | }\n\n src/violations/beforeload-server-leak.ts:8:15\n\n Suggestions:\n - Use createServerFn().handler(() => ...) to keep the logic on the server and call it from the client via an RPC bridge\n - Use createServerOnlyFn(() => ...) to mark it as server-only (it will throw if accidentally called from the client)\n - Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations\n - Move the server-only import out of this file into a separate .server.ts module that is not imported by any client code\n\u001b[39m\n Plugin: \u001b[35mvite:import-analysis\u001b[39m\n File: \u001b[36m/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/violations/beforeload-server-leak.ts\u001b[39m:1:27\n\u001b[33m 1 | import { getRequest } from \"@tanstack/react-start/server\";\n | ^\n 2 | export function getSessionFromRequest() {\n 3 | const req = getRequest();\u001b[39m\n at ResolveIdContext._formatLog (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28999:43)\n at ResolveIdContext.error (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28996:14)\n at handleViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:413:20)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async reportOrDeferViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:455:12)\nError: \n[import-protection] Import denied in server environment\n\n Denied by file pattern: **/*.client.*\n Importer: src/routes/client-only-jsx.tsx:19:12\n Import: \"../violations/window-size.client\"\n Resolved: src/violations/window-size.client.tsx\n\n Trace:\n 1. src/router.tsx:2:27 (entry) (import \"./routeTree.gen\")\n 2. src/routeTree.gen.ts:16:51 (import \"./routes/client-only-jsx\")\n 3. src/routes/client-only-jsx.tsx:19:12 (import \"../violations/window-size.client\")\n\n Code:\n 17 | Window:{' '}\n 18 | \n > 19 | \n | ^\n 20 | \n 21 |
\n\n src/routes/client-only-jsx.tsx:19:12\n\n Suggestions:\n - Wrap the JSX in }>... so it only renders in the browser after hydration\n - Use createClientOnlyFn(() => ...) to mark it as client-only (returns undefined on the server)\n - Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations\n - Move the client-only import out of this file into a separate .client.ts module that is not imported by any server code\n\n at ResolveIdContext._formatLog (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/chunks/config.js:28999:43)\n at ResolveIdContext.error (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/chunks/config.js:28996:14)\n at handleViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:413:20)\n\u001b[90m at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\u001b[39m\n at async reportOrDeferViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:455:12) {\n plugin: \u001b[32m'vite:import-analysis'\u001b[39m,\n pos: \u001b[33m76\u001b[39m,\n id: \u001b[32m'/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-jsx.tsx?tsr-split=component'\u001b[39m,\n pluginCode: \u001b[32m'import { jsxDEV } from \"react/jsx-dev-runtime\";\\n'\u001b[39m +\n \u001b[32m'import { WindowSize } from \"../violations/window-size.client\";\\n'\u001b[39m +\n \u001b[32m'function ClientOnlyJsx() {\\n'\u001b[39m +\n \u001b[32m' return /* @__PURE__ */ jsxDEV(\"div\", { children: [\\n'\u001b[39m +\n \u001b[32m' /* @__PURE__ */ jsxDEV(\"h1\", { \"data-testid\": \"client-only-jsx-heading\", children: \"Client-Only JSX\" }, void 0, false, {\\n'\u001b[39m +\n \u001b[32m' fileName: \"/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-jsx.tsx?tsr-split=component\",\\n'\u001b[39m +\n \u001b[32m' lineNumber: 8,\\n'\u001b[39m +\n \u001b[32m' columnNumber: 7\\n'\u001b[39m +\n \u001b[32m' }, this),\\n'\u001b[39m +\n \u001b[32m' /* @__PURE__ */ jsxDEV(\"p\", { \"data-testid\": \"window-size\", children: [\\n'\u001b[39m +\n \u001b[32m' \"Window:\",\\n'\u001b[39m +\n \u001b[32m' \" \",\\n'\u001b[39m +\n \u001b[32m' /* @__PURE__ */ jsxDEV(\"strong\", { children: /* @__PURE__ */ jsxDEV(WindowSize, {}, void 0, false, {\\n'\u001b[39m +\n \u001b[32m' fileName: \"/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-jsx.tsx?tsr-split=component\",\\n'\u001b[39m +\n \u001b[32m' lineNumber: 12,\\n'\u001b[39m +\n \u001b[32m' columnNumber: 11\\n'\u001b[39m +\n \u001b[32m' }, this) }, void 0, false, {\\n'\u001b[39m +\n \u001b[32m' fileName: \"/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-jsx.tsx?tsr-split=component\",\\n'\u001b[39m +\n \u001b[32m' lineNumber: 11,\\n'\u001b[39m +\n \u001b[32m' columnNumber: 9\\n'\u001b[39m +\n \u001b[32m' }, this)\\n'\u001b[39m +\n \u001b[32m' ] }, void 0, true, {\\n'\u001b[39m +\n \u001b[32m' fileName: \"/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-jsx.tsx?tsr-split=component\",\\n'\u001b[39m +\n \u001b[32m' lineNumber: 9,\\n'\u001b[39m +\n \u001b[32m' columnNumber: 7\\n'\u001b[39m +\n \u001b[32m' }, this)\\n'\u001b[39m +\n \u001b[32m' ] }, void 0, true, {\\n'\u001b[39m +\n \u001b[32m' fileName: \"/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-jsx.tsx?tsr-split=component\",\\n'\u001b[39m +\n \u001b[32m' lineNumber: 7,\\n'\u001b[39m +\n \u001b[32m' columnNumber: 10\\n'\u001b[39m +\n \u001b[32m' }, this);\\n'\u001b[39m +\n \u001b[32m'}\\n'\u001b[39m +\n \u001b[32m'export { ClientOnlyJsx as component };\\n'\u001b[39m,\n loc: {\n file: \u001b[32m'/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/routes/client-only-jsx.tsx?tsr-split=component'\u001b[39m,\n line: \u001b[33m6\u001b[39m,\n column: \u001b[33m27\u001b[39m\n },\n frame: \u001b[32m'1 | import { jsxDEV } from \"react/jsx-dev-runtime\";\\n'\u001b[39m +\n \u001b[32m'2 | import { WindowSize } from \"../violations/window-size.client\";\\n'\u001b[39m +\n \u001b[32m' | ^\\n'\u001b[39m +\n \u001b[32m'3 | function ClientOnlyJsx() {\\n'\u001b[39m +\n \u001b[32m'4 | return /* @__PURE__ */ jsxDEV(\"div\", { children: ['\u001b[39m,\n runnerError: Error: RunnerError\n at reviveInvokeError (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/module-runner.js:476:64)\n at Object.invoke (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/module-runner.js:549:11)\n \u001b[90m at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\u001b[39m\n at async ModuleRunner.getModuleInformation (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/module-runner.js:1086:7)\n at async request (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/\u001b[4m.pnpm\u001b[24m/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/\u001b[4mvite\u001b[24m/dist/node/module-runner.js:1103:83)\n at async Promise.all (index 0)\n}\n\u001b[2m12:12:53 AM\u001b[22m \u001b[31m\u001b[1m[vite]\u001b[22m\u001b[39m \u001b[31mInternal server error: \n[import-protection] Import denied in client environment\n\n Denied by specifier pattern: @tanstack/react-start/server\n Importer: src/violations/beforeload-server-leak.ts:8:15\n Import: \"@tanstack/react-start/server\"\n\n Trace:\n 1. src/router.tsx (entry) (import \"./routeTree.gen\")\n 2. src/routeTree.gen.ts:11:52 (import \"./routes/beforeload-leak\")\n 3. src/routes/beforeload-leak.tsx:2:39 (import \"../violations/beforeload-server-leak\")\n 4. src/violations/beforeload-server-leak.ts:8:15 (import \"@tanstack/react-start/server\")\n\n Code:\n 6 | // Using this module in `beforeLoad` is therefore a TRUE POSITIVE violation.\n 7 | export function getSessionFromRequest() {\n > 8 | const req = getRequest()\n | ^\n 9 | return { sessionId: req.headers.get('x-session-id') }\n 10 | }\n\n src/violations/beforeload-server-leak.ts:8:15\n\n Suggestions:\n - Use createServerFn().handler(() => ...) to keep the logic on the server and call it from the client via an RPC bridge\n - Use createServerOnlyFn(() => ...) to mark it as server-only (it will throw if accidentally called from the client)\n - Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations\n - Move the server-only import out of this file into a separate .server.ts module that is not imported by any client code\n\u001b[39m\n Plugin: \u001b[35mvite:import-analysis\u001b[39m\n File: \u001b[36m/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/violations/beforeload-server-leak.ts\u001b[39m:1:27\n\u001b[33m 1 | import { getRequest } from \"@tanstack/react-start/server\";\n | ^\n 2 | export function getSessionFromRequest() {\n 3 | const req = getRequest();\u001b[39m\n at ResolveIdContext._formatLog (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28999:43)\n at ResolveIdContext.error (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28996:14)\n at handleViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:413:20)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async reportOrDeferViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:455:12)\n\u001b[2m12:12:54 AM\u001b[22m \u001b[31m\u001b[1m[vite]\u001b[22m\u001b[39m \u001b[31mInternal server error: \n[import-protection] Import denied in client environment\n\n Denied by specifier pattern: @tanstack/react-start/server\n Importer: src/violations/beforeload-server-leak.ts:8:15\n Import: \"@tanstack/react-start/server\"\n\n Trace:\n 1. src/router.tsx (entry) (import \"./routeTree.gen\")\n 2. src/routeTree.gen.ts:11:52 (import \"./routes/beforeload-leak\")\n 3. src/routes/beforeload-leak.tsx:2:39 (import \"../violations/beforeload-server-leak\")\n 4. src/violations/beforeload-server-leak.ts:8:15 (import \"@tanstack/react-start/server\")\n\n Code:\n 6 | // Using this module in `beforeLoad` is therefore a TRUE POSITIVE violation.\n 7 | export function getSessionFromRequest() {\n > 8 | const req = getRequest()\n | ^\n 9 | return { sessionId: req.headers.get('x-session-id') }\n 10 | }\n\n src/violations/beforeload-server-leak.ts:8:15\n\n Suggestions:\n - Use createServerFn().handler(() => ...) to keep the logic on the server and call it from the client via an RPC bridge\n - Use createServerOnlyFn(() => ...) to mark it as server-only (it will throw if accidentally called from the client)\n - Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations\n - Move the server-only import out of this file into a separate .server.ts module that is not imported by any client code\n\u001b[39m\n Plugin: \u001b[35mvite:import-analysis\u001b[39m\n File: \u001b[36m/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/violations/beforeload-server-leak.ts\u001b[39m:1:27\n\u001b[33m 1 | import { getRequest } from \"@tanstack/react-start/server\";\n | ^\n 2 | export function getSessionFromRequest() {\n 3 | const req = getRequest();\u001b[39m\n at ResolveIdContext._formatLog (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28999:43)\n at ResolveIdContext.error (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28996:14)\n at handleViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:413:20)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async reportOrDeferViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:455:12)\n[db] connecting to postgres://admin:s3cret@localhost:5432/myapp\n\u001b[2m12:12:54 AM\u001b[22m \u001b[31m\u001b[1m[vite]\u001b[22m\u001b[39m \u001b[31mInternal server error: \n[import-protection] Import denied in client environment\n\n Denied by specifier pattern: @tanstack/react-start/server\n Importer: src/violations/beforeload-server-leak.ts:8:15\n Import: \"@tanstack/react-start/server\"\n\n Trace:\n 1. src/router.tsx (entry) (import \"./routeTree.gen\")\n 2. src/routeTree.gen.ts:11:52 (import \"./routes/beforeload-leak\")\n 3. src/routes/beforeload-leak.tsx:2:39 (import \"../violations/beforeload-server-leak\")\n 4. src/violations/beforeload-server-leak.ts:8:15 (import \"@tanstack/react-start/server\")\n\n Code:\n 6 | // Using this module in `beforeLoad` is therefore a TRUE POSITIVE violation.\n 7 | export function getSessionFromRequest() {\n > 8 | const req = getRequest()\n | ^\n 9 | return { sessionId: req.headers.get('x-session-id') }\n 10 | }\n\n src/violations/beforeload-server-leak.ts:8:15\n\n Suggestions:\n - Use createServerFn().handler(() => ...) to keep the logic on the server and call it from the client via an RPC bridge\n - Use createServerOnlyFn(() => ...) to mark it as server-only (it will throw if accidentally called from the client)\n - Use createIsomorphicFn().client(() => ...).server(() => ...) to provide separate client and server implementations\n - Move the server-only import out of this file into a separate .server.ts module that is not imported by any client code\n\u001b[39m\n Plugin: \u001b[35mvite:import-analysis\u001b[39m\n File: \u001b[36m/Users/caligano/source/import-protection/router-import-protection/e2e/react-start/import-protection/src/violations/beforeload-server-leak.ts\u001b[39m:1:27\n\u001b[33m 1 | import { getRequest } from \"@tanstack/react-start/server\";\n | ^\n 2 | export function getSessionFromRequest() {\n 3 | const req = getRequest();\u001b[39m\n at ResolveIdContext._formatLog (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28999:43)\n at ResolveIdContext.error (file:///Users/caligano/source/import-protection/router-import-protection/node_modules/.pnpm/vite@7.3.1_@types+node@25.0.9_jiti@2.6.1_lightningcss@1.30.2_sass-embedded@1.97.2_sass@_1d37aa1356b156747ad30a49305c26bc/node_modules/vite/dist/node/chunks/config.js:28996:14)\n at handleViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:413:20)\n at process.processTicksAndRejections (node:internal/process/task_queues:105:5)\n at async reportOrDeferViolation (file:///Users/caligano/source/import-protection/router-import-protection/packages/start-plugin-core/dist/esm/import-protection-plugin/plugin.js:455:12)\n"
+}
diff --git a/e2e/react-start/import-protection/package.json b/e2e/react-start/import-protection/package.json
index 7f9fc63ade6..4492974074c 100644
--- a/e2e/react-start/import-protection/package.json
+++ b/e2e/react-start/import-protection/package.json
@@ -17,6 +17,7 @@
"@tanstack/react-start": "workspace:^",
"react": "^19.0.0",
"react-dom": "^19.0.0",
+ "react-tweet": "^3.3.0",
"vite": "^7.3.1"
},
"devDependencies": {
diff --git a/e2e/react-start/import-protection/src/routes/__root.tsx b/e2e/react-start/import-protection/src/routes/__root.tsx
index 5d7fef8b067..1b67ae01cb1 100644
--- a/e2e/react-start/import-protection/src/routes/__root.tsx
+++ b/e2e/react-start/import-protection/src/routes/__root.tsx
@@ -38,6 +38,8 @@ function RootComponent() {
Component Server Leak
{' | '}
Barrel False Positive
+ {' | '}
+ noExternal Client Pkg
diff --git a/e2e/react-start/import-protection/src/routes/noexternal-client-pkg.tsx b/e2e/react-start/import-protection/src/routes/noexternal-client-pkg.tsx
new file mode 100644
index 00000000000..838fe025231
--- /dev/null
+++ b/e2e/react-start/import-protection/src/routes/noexternal-client-pkg.tsx
@@ -0,0 +1,17 @@
+import { createFileRoute } from '@tanstack/react-router'
+import { Tweet } from 'react-tweet'
+
+export const Route = createFileRoute('/noexternal-client-pkg')({
+ component: NoExternalClientPkg,
+})
+
+function NoExternalClientPkg() {
+ return (
+