Skip to content

feat: add excludeFiles to import-protection#6757

Merged
schiller-manuel merged 2 commits intomainfrom
feat-import-protection-excludeFiles
Feb 24, 2026
Merged

feat: add excludeFiles to import-protection#6757
schiller-manuel merged 2 commits intomainfrom
feat-import-protection-excludeFiles

Conversation

@schiller-manuel
Copy link
Contributor

@schiller-manuel schiller-manuel commented Feb 24, 2026

Summary by CodeRabbit

  • New Features

    • Added excludeFiles option for import protection configuration, enabling you to specify file patterns to exclude from import restriction checks.
    • Configure exclusions per environment; excludeFiles fully replaces the default node_modules exclusion when specified.
  • Documentation

    • Expanded import protection guide with examples showing how to customize file exclusions for both client and server environments.
    • Added new section clarifying replacement versus additive behavior for exclusion patterns.

@github-actions github-actions bot added documentation Everything documentation related package: start-plugin-core labels Feb 24, 2026
@nx-cloud
Copy link

nx-cloud bot commented Feb 24, 2026

View your CI Pipeline Execution ↗ for commit dc06f32

Command Status Duration Result
nx run tanstack-router-e2e-bundle-size:build --... ✅ Succeeded 1m 25s View ↗

☁️ Nx Cloud last updated this comment at 2026-02-24 23:40:28 UTC

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

This PR adds an excludeFiles configuration option to the import protection feature for both client and server environments, enabling users to exclude specific file patterns from violation checks. The changes span schema definitions, plugin implementation, documentation, core tests, and e2e tests with a new test route.

Changes

Cohort / File(s) Summary
Documentation
docs/start/framework/react/guide/import-protection.md
Added excludeFiles option explanation for both client and server environments, including behavior details on replacement vs. default exclusion and examples for third-party packages.
Schema & Defaults
packages/start-plugin-core/src/schema.ts, packages/start-plugin-core/src/import-protection-plugin/defaults.ts
Extended schema to include optional excludeFiles field in environment rules; added default excludeFiles: ['**/node_modules/**'] to client and server rules.
Plugin Implementation
packages/start-plugin-core/src/import-protection-plugin/plugin.ts
Introduced excludeFiles as per-environment filter in PluginConfig; updated getRulesForEnvironment to return excludeFiles; added exclusion gating logic before file-pattern and marker-based denial checks.
Core Plugin Tests
packages/start-plugin-core/tests/importProtection/defaults.test.ts
Added two test cases verifying default excludeFiles behavior includes **/node_modules/** for both client and server.
E2E Configuration
e2e/react-start/import-protection/vite.config.ts, e2e/react-start/import-protection/package.json
Added ssr.noExternal: ['react-tweet'] to Vite config; added react-tweet@^3.3.0 dependency for testing noExternal client entry handling.
E2E Routes & Components
e2e/react-start/import-protection/src/routes/__root.tsx, e2e/react-start/import-protection/src/routes/noexternal-client-pkg.tsx
Added navigation link to "/noexternal-client-pkg" route; created new route component rendering a Tweet from react-tweet with test identifiers.
E2E Tests
e2e/react-start/import-protection/tests/import-protection.spec.ts
Added tests for noExternal client package handling: route load verification and violation filtering for react-tweet imports in build/dev modes.
E2E Test Output
e2e/react-start/import-protection/error-dev-result.json
Captured ANSI-escaped diagnostic output aggregating Vite and import-protection errors including re-optimization logs and violation details.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested labels

package: react-start

Suggested reviewers

  • tannerlinsley

Poem

🐰 Hop along with excludeFiles so true,
Node modules now dance away from our view!
Per-environment filters, a protective embrace,
Keeping react-tweet safe in its SSR space. ✨
Violations begone, false positives flee,
The import-protection now knows what should be!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 accurately describes the main change: adding an excludeFiles feature to the import-protection plugin, which is reflected across documentation, configuration schema, plugin implementation, and tests.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat-import-protection-excludeFiles

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

@github-actions
Copy link

Bundle Size Benchmarks

  • Commit: 361ff488a83d
  • Measured at: 2026-02-24T23:26:36.165Z
  • Baseline source: history:9a4161acff05
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Raw Brotli Trend
react-router.minimal 86.54 KiB 0 B (0.00%) 272.17 KiB 75.21 KiB █▆▆▆▁▁▁▁▁▁▁
react-router.full 89.55 KiB 0 B (0.00%) 282.51 KiB 77.88 KiB █▇▆▆▁▁▁▁▁▁▁
solid-router.minimal 35.85 KiB 0 B (0.00%) 107.29 KiB 32.22 KiB █▇▇▇▁▁▁▁▁▃▃
solid-router.full 40.18 KiB 0 B (0.00%) 120.34 KiB 36.05 KiB █▇▆▆▁▁▂▂▂▃▃
vue-router.minimal 51.71 KiB 0 B (0.00%) 147.26 KiB 46.49 KiB █▇▆▆▁▁▂▂▂▃▃
vue-router.full 56.51 KiB 0 B (0.00%) 162.85 KiB 50.75 KiB █▇▆▆▁▁▁▁▁▂▂
react-start.minimal 99.08 KiB 0 B (0.00%) 311.31 KiB 85.74 KiB ▇▆▆█▁▁▁▁▁▁▁
react-start.full 102.42 KiB 0 B (0.00%) 321.08 KiB 88.53 KiB █▆▅▇▁▁▁▁▁▁▁
solid-start.minimal 48.16 KiB 0 B (0.00%) 144.86 KiB 42.60 KiB ▇▆▅█▁▁▂▂▂▃▃
solid-start.full 53.62 KiB 0 B (0.00%) 160.77 KiB 47.26 KiB █▆▆█▁▁▁▁▁▃▃

Trend sparkline is historical gzip bytes ending with this PR measurement; lower is better.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 24, 2026

More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: dc06f32

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/start/framework/react/guide/import-protection.md`:
- Line 41: Update the docs text for the excludeFiles option to clearly state
that excluded resolved files are skipped for both file-pattern checks and
marker-based denials (i.e., runtime marker checks in the
import-protection-plugin), and apply this wording change to all occurrences of
the option in the guide so the docs match the actual logic in
import-protection-plugin where marker-based denial is bypassed for excluded
files.

In `@e2e/react-start/import-protection/error-dev-result.json`:
- Line 2: The committed fixture log contains a credential-like DSN string
"postgres://admin:s3cret@localhost:5432/myapp" under the "combined" fixture
output; redact or replace that DSN with a non-sensitive placeholder like
"postgres://REDACTED:REDACTED@localhost:5432/myapp" (or remove the credentials
entirely) so secret scanners stop flagging the artifact, update the "combined"
entry in error-dev-result.json (or the fixture that contains that string)
accordingly, and ensure the test/data fixture no longer contains any real
credentials before committing.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a4161a and dc06f32.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (11)
  • docs/start/framework/react/guide/import-protection.md
  • e2e/react-start/import-protection/error-dev-result.json
  • e2e/react-start/import-protection/package.json
  • e2e/react-start/import-protection/src/routes/__root.tsx
  • e2e/react-start/import-protection/src/routes/noexternal-client-pkg.tsx
  • e2e/react-start/import-protection/tests/import-protection.spec.ts
  • e2e/react-start/import-protection/vite.config.ts
  • packages/start-plugin-core/src/import-protection-plugin/defaults.ts
  • packages/start-plugin-core/src/import-protection-plugin/plugin.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-plugin-core/tests/importProtection/defaults.test.ts

- 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).
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

Clarify that excludeFiles currently suppresses marker checks too.

Line 41 and the option table describe excludeFiles as skipping only file-pattern checks, but runtime logic also bypasses marker-based denial for excluded resolved files (see packages/start-plugin-core/src/import-protection-plugin/plugin.ts, Lines 1358-1421). Please align wording to avoid user surprise.

📝 Suggested doc patch
-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`).
+By default, files inside `node_modules` are excluded from resolved-target deny 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`).

-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:
+By default, resolved files inside `node_modules` are excluded from resolved-target deny checks (file-pattern and marker 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:

-| `client.excludeFiles` | `Pattern[]`          | `['**/node_modules/**']`          | Resolved files matching these patterns skip file-pattern checks (replaces defaults) |
+| `client.excludeFiles` | `Pattern[]`          | `['**/node_modules/**']`          | Resolved files matching these patterns skip resolved-target checks (file-pattern + marker) (replaces defaults) |
-| `server.excludeFiles` | `Pattern[]`          | `['**/node_modules/**']`          | Resolved files matching these patterns skip file-pattern checks (replaces defaults) |
+| `server.excludeFiles` | `Pattern[]`          | `['**/node_modules/**']`          | Resolved files matching these patterns skip resolved-target checks (file-pattern + marker) (replaces defaults) |

Also applies to: 139-150, 450-454

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/start/framework/react/guide/import-protection.md` at line 41, Update the
docs text for the excludeFiles option to clearly state that excluded resolved
files are skipped for both file-pattern checks and marker-based denials (i.e.,
runtime marker checks in the import-protection-plugin), and apply this wording
change to all occurrences of the option in the guide so the docs match the
actual logic in import-protection-plugin where marker-based denial is bypassed
for excluded files.

@@ -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 | <div>\n 16 | <h1 data-testid=\"client-only-heading\">Client-Only Violations</h1>\n > 17 | <p data-testid=\"browser-title\">{getBrowserTitle()}</p>\n | ^\n 18 | <p data-testid=\"client-only-data\">{getClientOnlyDataViaEdge()}</p>\n 19 | </div>\n\n src/routes/client-only-violations.tsx:17:39\n\n Suggestions:\n - Wrap the JSX in <ClientOnly fallback={<Loading />}>...</ClientOnly> 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 | <strong>\n > 19 | <WindowSize />\n | ^\n 20 | </strong>\n 21 | </p>\n\n src/routes/client-only-jsx.tsx:19:12\n\n Suggestions:\n - Wrap the JSX in <ClientOnly fallback={<Loading />}>...</ClientOnly> 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"
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

Redact credential-like DSN from committed fixture logs.

Line 2 contains postgres://admin:s3cret@localhost:5432/myapp inside a committed artifact. Even in e2e fixtures, this is a secret-pattern leak risk and will keep triggering secret scanners.

🔒 Suggested fix
- [db] connecting to postgres://admin:s3cret@localhost:5432/myapp
+ [db] connecting to postgres://<redacted>@localhost:5432/myapp
🧰 Tools
🪛 Checkov (3.2.334)

[medium] 2-3: Basic Auth Credentials

(CKV_SECRET_4)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@e2e/react-start/import-protection/error-dev-result.json` at line 2, The
committed fixture log contains a credential-like DSN string
"postgres://admin:s3cret@localhost:5432/myapp" under the "combined" fixture
output; redact or replace that DSN with a non-sensitive placeholder like
"postgres://REDACTED:REDACTED@localhost:5432/myapp" (or remove the credentials
entirely) so secret scanners stop flagging the artifact, update the "combined"
entry in error-dev-result.json (or the fixture that contains that string)
accordingly, and ensure the test/data fixture no longer contains any real
credentials before committing.

@schiller-manuel schiller-manuel merged commit 95761bc into main Feb 24, 2026
8 checks passed
@schiller-manuel schiller-manuel deleted the feat-import-protection-excludeFiles branch February 24, 2026 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Everything documentation related package: start-plugin-core

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant