Skip to content

fix: don't rely on syntheticNamedExports in import protection#6758

Merged
schiller-manuel merged 6 commits intomainfrom
fix-synthetic-exports
Feb 25, 2026
Merged

fix: don't rely on syntheticNamedExports in import protection#6758
schiller-manuel merged 6 commits intomainfrom
fix-synthetic-exports

Conversation

@schiller-manuel
Copy link
Contributor

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

Summary by CodeRabbit

  • Documentation

    • Clarified import-protection wording around resolved-target deny checks, excludeFiles behavior, and node_modules exclusions.
  • Tests

    • Strengthened tests to validate mock code contains side-effect and purity markers.
  • E2E / Stability

    • Improved route navigation waits with a fallback strategy and longer timeouts to reduce flakiness.
  • Chores

    • Added ignore patterns and removed a dev diagnostic artifact from the repository.

this is not supported by rolldown so we need to rewrite mock imports
@github-actions github-actions bot added documentation Everything documentation related package: start-plugin-core labels Feb 25, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

📝 Walkthrough

Walkthrough

Centralized virtual-module resolution APIs and refactored import-protection to emit per-violation mock-edge modules with explicit exports; documentation and tests updated; dev diagnostic artifact removed; small e2e navigation/wait adjustments and .gitignore additions.

Changes

Cohort / File(s) Summary
Documentation
docs/start/framework/react/guide/import-protection.md, packages/start-plugin-core/src/import-protection-plugin/INTERNALS.md
Wording updates to describe "resolved-target deny checks"; INTERNALS.md explains per-violation mock-edge modules and dev/build semantics.
Virtual Module System
packages/start-plugin-core/src/import-protection-plugin/virtualModules.ts
Made many RESOLVED_* constants private; added public helpers: resolvedMarkerVirtualModuleId, getResolvedVirtualModuleMatchers, resolveInternalVirtualModuleId, loadResolvedVirtualModule; added /* @__NO_SIDE_EFFECTS__ / and / @__PURE__ */ annotations; simplified silent mock loader signature.
Plugin Core
packages/start-plugin-core/src/import-protection-plugin/plugin.ts
Replaced per-prefix resolution with new resolution helpers; generate per-violation base mock + mock-edge modules; DeferredBuildViolation adds optional checkModuleId; simplified HandleViolationResult to `string
Tests
packages/start-plugin-core/tests/importProtection/transform.test.ts, packages/start-plugin-core/tests/importProtection/virtualModules.test.ts
Removed import of resolved mock ID; updated test assertions to check purity/side-effect markers and renamed test.
E2E / Artifacts
e2e/react-start/import-protection/.gitignore, e2e/react-start/import-protection/error-dev-result.json, e2e/react-start/import-protection/tests/*.setup.ts
Added ignore patterns, removed verbose dev diagnostic JSON, adjusted Playwright navigation waits (fallback from networkidle → load) and added route-specific wait logic and small delays to allow deferred transforms/logging to flush.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Importer as Importing Module
participant Plugin as import-protection Plugin
participant Resolver as Internal Resolver Helpers
participant Loader as loadResolvedVirtualModule
participant Build as Bundler (Rollup/Rolldown) / Dev Runtime

Importer->>Plugin: encounter denied import
Plugin->>Plugin: record DeferredBuildViolation (mock base ID + mock-edge meta, optional checkModuleId)
Plugin->>Resolver: resolveInternalVirtualModuleId / resolvedMarkerVirtualModuleId
Resolver->>Loader: matched resolved virtual module id
Loader-->>Build: return mock-edge or base mock code (with /* `@__PURE__` */ / /* `@__NO_SIDE_EFFECTS__` */)
Build->>Plugin: substitute import with resolved mock-edge id (build) / serve edge module (dev)
Plugin->>Build: check checkModuleId (if present) to validate tree-shaking; log if survived

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • tannerlinsley
  • nlynzaad

Poem

"i'm a rabbit in the codewood, nibbling bits so neat,
mock-edge shells and pure marks make the bundle sweet.
resolved ids now hop in line, tidy, small, and clear,
dev logs trimmed, docs refined — a carrot for the year! 🥕🐇"

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% 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 'fix: don't rely on syntheticNamedExports in import protection' accurately summarizes the main technical change—removing dependency on syntheticNamedExports throughout the import-protection plugin by introducing mock-edge modules with explicit exports.

✏️ 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 fix-synthetic-exports

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

@nx-cloud
Copy link

nx-cloud bot commented Feb 25, 2026

View your CI Pipeline Execution ↗ for commit f0eda8f

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

☁️ Nx Cloud last updated this comment at 2026-02-25 01:05:12 UTC

@github-actions
Copy link

github-actions bot commented Feb 25, 2026

Bundle Size Benchmarks

  • Commit: fd804189b437
  • Measured at: 2026-02-25T00:56:41.196Z
  • Baseline source: history:95761bc4e3fa
  • 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 25, 2026

More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/history

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

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: f0eda8f

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

Caution

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

⚠️ Outside diff range comments (2)
docs/start/framework/react/guide/import-protection.md (1)

34-39: ⚠️ Potential issue | 🟡 Minor

Stale "file checks" wording in the Default Rules bullets (lines 34 and 39).

The PR updates line 41 and line 139 to say "excluded from resolved-target deny checks" (covering both file-pattern and marker checks), but the two bullet points directly above line 41 still read "Excluded from file checks". This leaves contradictory terminology within the same section.

📝 Suggested wording update for lines 34 and 39
 **Client environment denials:**

 - Files matching `**/*.server.*`
 - The specifier `@tanstack/react-start/server`
-- Excluded from file checks: `**/node_modules/**`
+- Excluded from resolved-target deny checks: `**/node_modules/**`

 **Server environment denials:**

 - Files matching `**/*.client.*`
-- Excluded from file checks: `**/node_modules/**`
+- Excluded from resolved-target deny checks: `**/node_modules/**`
🤖 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` around lines 34 - 39,
Update the two bullets that currently read "Excluded from file checks:
`**/node_modules/**`" to use the same terminology as the rest of the section by
changing them to "Excluded from resolved-target deny checks:
`**/node_modules/**`"; locate and edit the bullets near the "Default Rules" /
"Server environment denials" headings so the phrasing matches the updated lines
that reference "resolved-target deny checks" (ensure both occurrences that
previously said "file checks" are updated).
packages/start-plugin-core/src/import-protection-plugin/INTERNALS.md (1)

34-37: ⚠️ Potential issue | 🟡 Minor

Documentation says "dev only" but the plugin now runs in all environments.

Line 34 describes the mock-rewrite plugin as (enforce: 'pre', dev only), but the updated applyToEnvironmentinplugin.ts (lines 1621-1632) now enables it for all environments (environmentNames.has(env.name)), not just dev+mock. The comment in the code explicitly states it's needed in build too because Rolldown doesn't support syntheticNamedExports`.

Proposed fix
-### 3. `tanstack-start-core:import-protection-mock-rewrite` (enforce: `'pre'`, dev only)
+### 3. `tanstack-start-core:import-protection-mock-rewrite` (enforce: `'pre'`)
 
-Records expected named exports per importer so that dev mock-edge modules can
-provide explicit ESM named exports. Only active in dev + mock mode.
+Records expected named exports per importer so that mock-edge modules can
+provide explicit ESM named exports. Active whenever import protection is enabled.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/start-plugin-core/src/import-protection-plugin/INTERNALS.md` around
lines 34 - 37, The docs are out of sync: the plugin
`tanstack-start-core:import-protection-mock-rewrite` is described as "dev only"
but `applyToEnvironment` in `plugin.ts` now enables it for all environments;
update INTERNALS.md to reflect the current behavior (remove "dev only" and note
it runs in build too because Rollup lacks syntheticNamedExports), or
alternatively revert `applyToEnvironment` in `plugin.ts` to only enable the
plugin for dev+mock if you prefer the original behavior; reference
`tanstack-start-core:import-protection-mock-rewrite` and the
`applyToEnvironment` logic in `plugin.ts` when making the fix.
🤖 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`:
- Around line 448-453: Update the documentation so the merge-semantics labels
for client.specifiers and server.specifiers are consistent: either change the
server.specifiers parenthetical from "(replaces defaults)" to "(additive with
defaults)" to match client.specifiers, or if the implementation truly replaces
server defaults, add a short clarifying note next to server.specifiers stating
that the default is [] so "replaces" has no practical effect; reference the
symbols client.specifiers and server.specifiers when making the edit.

In `@packages/start-plugin-core/src/import-protection-plugin/virtualModules.ts`:
- Around line 40-54: Remove the duplicated JSDoc comment above
getResolvedVirtualModuleMatchers(): keep a single copy of the block that
explains resolved vs unresolved virtual ids and delete the repeated duplicate;
update the comment immediately preceding the export function
getResolvedVirtualModuleMatchers() and ensure the function still returns
RESOLVED_VIRTUAL_MODULE_MATCHERS unchanged.

---

Outside diff comments:
In `@docs/start/framework/react/guide/import-protection.md`:
- Around line 34-39: Update the two bullets that currently read "Excluded from
file checks: `**/node_modules/**`" to use the same terminology as the rest of
the section by changing them to "Excluded from resolved-target deny checks:
`**/node_modules/**`"; locate and edit the bullets near the "Default Rules" /
"Server environment denials" headings so the phrasing matches the updated lines
that reference "resolved-target deny checks" (ensure both occurrences that
previously said "file checks" are updated).

In `@packages/start-plugin-core/src/import-protection-plugin/INTERNALS.md`:
- Around line 34-37: The docs are out of sync: the plugin
`tanstack-start-core:import-protection-mock-rewrite` is described as "dev only"
but `applyToEnvironment` in `plugin.ts` now enables it for all environments;
update INTERNALS.md to reflect the current behavior (remove "dev only" and note
it runs in build too because Rollup lacks syntheticNamedExports), or
alternatively revert `applyToEnvironment` in `plugin.ts` to only enable the
plugin for dev+mock if you prefer the original behavior; reference
`tanstack-start-core:import-protection-mock-rewrite` and the
`applyToEnvironment` logic in `plugin.ts` when making the fix.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e5cb8cc and e2fbc52.

📒 Files selected for processing (8)
  • docs/start/framework/react/guide/import-protection.md
  • e2e/react-start/import-protection/.gitignore
  • e2e/react-start/import-protection/error-dev-result.json
  • packages/start-plugin-core/src/import-protection-plugin/INTERNALS.md
  • packages/start-plugin-core/src/import-protection-plugin/plugin.ts
  • packages/start-plugin-core/src/import-protection-plugin/virtualModules.ts
  • packages/start-plugin-core/tests/importProtection/transform.test.ts
  • packages/start-plugin-core/tests/importProtection/virtualModules.test.ts
💤 Files with no reviewable changes (2)
  • packages/start-plugin-core/tests/importProtection/transform.test.ts
  • e2e/react-start/import-protection/error-dev-result.json

Comment on lines 40 to 54
/**
* Convenience list for plugin `load` filters/handlers.
*
* Vite/Rollup call `load(id)` with the *resolved* virtual id (prefixed by `\0`).
* `resolveId(source)` sees the *unresolved* id/prefix (without `\0`).
*/
/**
* Convenience list for plugin `load` filters/handlers.
*
* Vite/Rollup call `load(id)` with the *resolved* virtual id (prefixed by `\0`).
* `resolveId(source)` sees the *unresolved* id/prefix (without `\0`).
*/
export function getResolvedVirtualModuleMatchers(): ReadonlyArray<string> {
return RESOLVED_VIRTUAL_MODULE_MATCHERS
}
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

Duplicate JSDoc comment block.

The JSDoc comment for getResolvedVirtualModuleMatchers() is duplicated — lines 40-44 and 46-51 contain the exact same text.

Proposed fix
-/**
- * Convenience list for plugin `load` filters/handlers.
- *
- * Vite/Rollup call `load(id)` with the *resolved* virtual id (prefixed by `\0`).
- * `resolveId(source)` sees the *unresolved* id/prefix (without `\0`).
- */
 /**
  * Convenience list for plugin `load` filters/handlers.
  *
  * Vite/Rollup call `load(id)` with the *resolved* virtual id (prefixed by `\0`).
  * `resolveId(source)` sees the *unresolved* id/prefix (without `\0`).
  */
 export function getResolvedVirtualModuleMatchers(): ReadonlyArray<string> {
📝 Committable suggestion

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

Suggested change
/**
* Convenience list for plugin `load` filters/handlers.
*
* Vite/Rollup call `load(id)` with the *resolved* virtual id (prefixed by `\0`).
* `resolveId(source)` sees the *unresolved* id/prefix (without `\0`).
*/
/**
* Convenience list for plugin `load` filters/handlers.
*
* Vite/Rollup call `load(id)` with the *resolved* virtual id (prefixed by `\0`).
* `resolveId(source)` sees the *unresolved* id/prefix (without `\0`).
*/
export function getResolvedVirtualModuleMatchers(): ReadonlyArray<string> {
return RESOLVED_VIRTUAL_MODULE_MATCHERS
}
/**
* Convenience list for plugin `load` filters/handlers.
*
* Vite/Rollup call `load(id)` with the *resolved* virtual id (prefixed by `\0`).
* `resolveId(source)` sees the *unresolved* id/prefix (without `\0`).
*/
export function getResolvedVirtualModuleMatchers(): ReadonlyArray<string> {
return RESOLVED_VIRTUAL_MODULE_MATCHERS
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/start-plugin-core/src/import-protection-plugin/virtualModules.ts`
around lines 40 - 54, Remove the duplicated JSDoc comment above
getResolvedVirtualModuleMatchers(): keep a single copy of the block that
explains resolved vs unresolved virtual ids and delete the repeated duplicate;
update the comment immediately preceding the export function
getResolvedVirtualModuleMatchers() and ensure the function still returns
RESOLVED_VIRTUAL_MODULE_MATCHERS unchanged.

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.

♻️ Duplicate comments (1)
packages/start-plugin-core/src/import-protection-plugin/virtualModules.ts (1)

40-48: Duplicate JSDoc resolved.

The previously flagged duplicate JSDoc block above getResolvedVirtualModuleMatchers has been removed. Only a single documentation block remains (lines 40–45).

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

In `@packages/start-plugin-core/src/import-protection-plugin/virtualModules.ts`
around lines 40 - 48, Remove the duplicate JSDoc so only one documentation block
documents the function: keep a single JSDoc for getResolvedVirtualModuleMatchers
and ensure it describes the returned RESOLVED_VIRTUAL_MODULE_MATCHERS; delete
any extra/duplicate comment blocks above or below
getResolvedVirtualModuleMatchers to avoid redundancy.
🧹 Nitpick comments (2)
packages/start-plugin-core/src/import-protection-plugin/virtualModules.ts (1)

40-56: getResolvedVirtualModuleMatchers references RESOLVED_VIRTUAL_MODULE_MATCHERS before it is declared.

getResolvedVirtualModuleMatchers (a hoisted function declaration) refers to RESOLVED_VIRTUAL_MODULE_MATCHERS, a const that is initialised two lines later (line 50). While this is safe at runtime (the const is fully initialised before any external caller can invoke the function), it creates a confusing inversion that could trip up future readers and tools. Reordering eliminates any doubt.

♻️ Suggested reorder
-/**
- * Convenience list for plugin `load` filters/handlers.
- *
- * Vite/Rollup call `load(id)` with the *resolved* virtual id (prefixed by `\0`).
- * `resolveId(source)` sees the *unresolved* id/prefix (without `\0`).
- */
-export function getResolvedVirtualModuleMatchers(): ReadonlyArray<string> {
-  return RESOLVED_VIRTUAL_MODULE_MATCHERS
-}
-
 const RESOLVED_VIRTUAL_MODULE_MATCHERS = [
   RESOLVED_MOCK_MODULE_ID,
   RESOLVED_MOCK_BUILD_PREFIX,
   RESOLVED_MOCK_EDGE_PREFIX,
   RESOLVED_MOCK_RUNTIME_PREFIX,
   RESOLVED_MARKER_PREFIX,
 ] as const
+
+/**
+ * Convenience list for plugin `load` filters/handlers.
+ *
+ * Vite/Rollup call `load(id)` with the *resolved* virtual id (prefixed by `\0`).
+ * `resolveId(source)` sees the *unresolved* id/prefix (without `\0`).
+ */
+export function getResolvedVirtualModuleMatchers(): ReadonlyArray<string> {
+  return RESOLVED_VIRTUAL_MODULE_MATCHERS
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/start-plugin-core/src/import-protection-plugin/virtualModules.ts`
around lines 40 - 56, Move the RESOLVED_VIRTUAL_MODULE_MATCHERS const so it is
declared before the getResolvedVirtualModuleMatchers function to avoid
referencing a const before its declaration; specifically, place the
RESOLVED_VIRTUAL_MODULE_MATCHERS array (which contains RESOLVED_MOCK_MODULE_ID,
RESOLVED_MOCK_BUILD_PREFIX, RESOLVED_MOCK_EDGE_PREFIX,
RESOLVED_MOCK_RUNTIME_PREFIX, RESOLVED_MARKER_PREFIX) above the
getResolvedVirtualModuleMatchers() function so the function returns an
already-declared constant.
e2e/react-start/import-protection/tests/violations.setup.ts (1)

86-94: routeReadyTestIds type could be narrowed for future-safety.

Record<string, string> means routeReadyTestIds[route] is typed as string (not string | undefined) without noUncheckedIndexedAccess. If a new route is added to routes but omitted from routeReadyTestIds, TypeScript won't flag it and the if (testId) guard on line 120 will be bypassed silently at runtime (the undefined value will be falsy but the type says otherwise).

A tighter type locks the allowed keys to those in routes:

♻️ Suggested narrower typing
-const routeReadyTestIds: Record<string, string> = {
+type AppRoute = (typeof routes)[number]
+const routeReadyTestIds: Partial<Record<AppRoute, string>> = {
   '/': 'heading',
   '/leaky-server-import': 'leaky-heading',
   '/client-only-violations': 'client-only-heading',
   '/client-only-jsx': 'client-only-jsx-heading',
   '/beforeload-leak': 'beforeload-leak-heading',
   '/component-server-leak': 'component-leak-heading',
   '/barrel-false-positive': 'barrel-heading',
 }

This keeps the if (testId) guard meaningful and ensures any new entry in routes that lacks a testId mapping will be caught by TypeScript. As per coding guidelines, **/*.ts files should use TypeScript strict mode with extensive type safety.

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

In `@e2e/react-start/import-protection/tests/violations.setup.ts` around lines 86
- 94, Narrow the type of routeReadyTestIds so its keys are the exact routes
defined in routes: make routes a readonly/as const array (so its elements form a
string literal union) and type routeReadyTestIds as Record<typeof
routes[number], string>; update the routeReadyTestIds declaration to use that
type (instead of Record<string,string>) so TypeScript will error if a route in
routes is missing from routeReadyTestIds and the runtime guard (if (testId))
remains meaningful.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@packages/start-plugin-core/src/import-protection-plugin/virtualModules.ts`:
- Around line 40-48: Remove the duplicate JSDoc so only one documentation block
documents the function: keep a single JSDoc for getResolvedVirtualModuleMatchers
and ensure it describes the returned RESOLVED_VIRTUAL_MODULE_MATCHERS; delete
any extra/duplicate comment blocks above or below
getResolvedVirtualModuleMatchers to avoid redundancy.

---

Nitpick comments:
In `@e2e/react-start/import-protection/tests/violations.setup.ts`:
- Around line 86-94: Narrow the type of routeReadyTestIds so its keys are the
exact routes defined in routes: make routes a readonly/as const array (so its
elements form a string literal union) and type routeReadyTestIds as
Record<typeof routes[number], string>; update the routeReadyTestIds declaration
to use that type (instead of Record<string,string>) so TypeScript will error if
a route in routes is missing from routeReadyTestIds and the runtime guard (if
(testId)) remains meaningful.

In `@packages/start-plugin-core/src/import-protection-plugin/virtualModules.ts`:
- Around line 40-56: Move the RESOLVED_VIRTUAL_MODULE_MATCHERS const so it is
declared before the getResolvedVirtualModuleMatchers function to avoid
referencing a const before its declaration; specifically, place the
RESOLVED_VIRTUAL_MODULE_MATCHERS array (which contains RESOLVED_MOCK_MODULE_ID,
RESOLVED_MOCK_BUILD_PREFIX, RESOLVED_MOCK_EDGE_PREFIX,
RESOLVED_MOCK_RUNTIME_PREFIX, RESOLVED_MARKER_PREFIX) above the
getResolvedVirtualModuleMatchers() function so the function returns an
already-declared constant.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e2fbc52 and f0eda8f.

📒 Files selected for processing (4)
  • docs/start/framework/react/guide/import-protection.md
  • e2e/react-start/import-protection/tests/error-mode.setup.ts
  • e2e/react-start/import-protection/tests/violations.setup.ts
  • packages/start-plugin-core/src/import-protection-plugin/virtualModules.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/start/framework/react/guide/import-protection.md

@schiller-manuel schiller-manuel merged commit c099ebf into main Feb 25, 2026
8 checks passed
@schiller-manuel schiller-manuel deleted the fix-synthetic-exports branch February 25, 2026 01:08
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