Skip to content

feat(start): support rsbuild iife client output#7477

Merged
schiller-manuel merged 10 commits into
mainfrom
rsbuild-iife-cache
May 24, 2026
Merged

feat(start): support rsbuild iife client output#7477
schiller-manuel merged 10 commits into
mainfrom
rsbuild-iife-cache

Conversation

@schiller-manuel
Copy link
Copy Markdown
Collaborator

@schiller-manuel schiller-manuel commented May 23, 2026

also fix serverfns with rsbuild cache

Summary by CodeRabbit

  • New Features

    • Rsbuild client output: supports module and IIFE formats; SSR emits matching preload/link/script markers and manifest scriptFormat entries.
  • Bug Fixes

    • Server-function metadata preserved and replayed across warm restarts to prevent lost registrations.
    • Dev-style marker handling refined for more reliable SSR/CSR behavior.
  • Behavior Changes

    • Start manifests now expose separate css, scripts, and preloads (not generic assets); cross-origin config uses per-kind script/stylesheet.
    • Early Hints and preload semantics follow the selected client output (module vs IIFE).
  • Documentation

    • Clarified asset cross-origin, transformAssets kinds, and preload/early-hint guidance.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 23, 2026

📝 Walkthrough

Walkthrough

Refactors manifest to ServerManifest (preloads, scripts, css), adds ScriptFormat (module|iife), changes transformAsset kinds to 'script'/'stylesheet'/'css-url', implements rsbuild/Rspack per-environment server-function metadata persistence/replay, and updates routers, server SSR, tests, docs, and e2e suites.

Changes

ServerManifest model + rsbuild server-fn persistence

Layer / File(s) Summary
Manifest types & helpers
packages/router-core/src/manifest.ts, packages/router-core/src/index.ts
Adds ServerManifest shape, ScriptFormat, DEV_STYLES_ATTR, getManifestScriptFormat, getScriptPreloadAttrs, ManifestCssLink/ManifestScript, and related re-exports.
SSR server manifest & dehydration
packages/router-core/src/ssr/*, packages/start-server-core/src/finalManifest.ts, packages/start-server-core/src/router-manifest.ts
Switches SSR and final-manifest APIs to ServerManifest shape, prepares matched routes, inlines/strips styles, merges request-scoped assets into root, and dehydrates preloads/scripts/css.
transformAssets & client entry wiring
packages/start-server-core/src/transformAssetUrls.ts, e2e/*/transform-asset-urls/*
Transforms use `kind: 'script'
Start plugin: rsbuild planning, schema, virtual modules
packages/start-plugin-core/src/rsbuild/*
Adds rsbuild.client.output schema (module
Rsbuild start-compiler host & metadata loader
packages/start-plugin-core/src/rsbuild/start-compiler-host.ts, start-compiler-metadata-loader.ts
Persists per-environment server-fn build-info into module buildInfo, installs loader for replay, rebuilds per-environment snapshots on finishMake, and remerges serverFnsById.
Start manifest builder (css/scripts)
packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts
Emits css and scripts arrays per route, implements stylesheet/script identity and dedupe, chunk CSS collector, hydration merge logic, and sets scriptFormat for iife output.
Router consumers: React, Solid, Vue adapters
packages/*-router/src/*, packages/*-router/tests/*
Asset/Scripts/HeadContent/headContentUtils updated to consume ServerManifest, handle DEV_STYLES_ATTR, add preventScriptHoist, adjust script injection/deduping, and preserve SSR head elements.
Start-server, early hints, storage context
packages/start-server-core/src/*, packages/start-storage-context/*
Early hints and collectors now operate on ServerManifest; StartStorageContext.requestAssets typed to ManifestRouteAssets; createStartHandler wires getRequestAssets from start context.
Tests, e2e, configs, docs, changesets
e2e/**, packages/**/tests/*, docs/*, .changeset/*
Adds rsbuild-cache e2e suite, playwright iife mode entries, updates many unit/e2e tests to new manifest shape, updates docs to use script key and IIFE/module preload rules, and adds changesets.

Estimated code review effort
🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested reviewers

  • Sheraff
  • beaussan
  • SeanCassiere

I hopped through manifests, scripts, and style,
I nudged the cache where server-fns hide.
Module moons and IIFE suns gleam bright,
Preloads line up and CSS sits tight.
A rabbit cheers for manifests done right. 🐇✨

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rsbuild-iife-cache

@nx-cloud
Copy link
Copy Markdown
Contributor

nx-cloud Bot commented May 23, 2026

View your CI Pipeline Execution ↗ for commit 20dde7f

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

☁️ Nx Cloud last updated this comment at 2026-05-24 00:01:14 UTC

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 23, 2026

🚀 Changeset Version Preview

9 package(s) bumped directly, 14 bumped as dependents.

🟩 Patch bumps

Package Version Reason
@tanstack/react-router 1.170.7 → 1.170.8 Changeset
@tanstack/react-start 1.168.10 → 1.168.11 Changeset
@tanstack/router-core 1.171.5 → 1.171.6 Changeset
@tanstack/solid-router 1.170.7 → 1.170.8 Changeset
@tanstack/solid-start 1.168.10 → 1.168.11 Changeset
@tanstack/start-plugin-core 1.171.3 → 1.171.4 Changeset
@tanstack/start-server-core 1.169.2 → 1.169.3 Changeset
@tanstack/vue-router 1.170.7 → 1.170.8 Changeset
@tanstack/vue-start 1.168.10 → 1.168.11 Changeset
@tanstack/react-start-client 1.168.2 → 1.168.3 Dependent
@tanstack/react-start-rsc 0.1.10 → 0.1.11 Dependent
@tanstack/react-start-server 1.167.7 → 1.167.8 Dependent
@tanstack/router-cli 1.167.9 → 1.167.10 Dependent
@tanstack/router-generator 1.167.9 → 1.167.10 Dependent
@tanstack/router-plugin 1.168.10 → 1.168.11 Dependent
@tanstack/router-vite-plugin 1.167.10 → 1.167.11 Dependent
@tanstack/solid-start-client 1.168.2 → 1.168.3 Dependent
@tanstack/solid-start-server 1.167.7 → 1.167.8 Dependent
@tanstack/start-client-core 1.170.2 → 1.170.3 Dependent
@tanstack/start-static-server-functions 1.167.7 → 1.167.8 Dependent
@tanstack/start-storage-context 1.167.7 → 1.167.8 Dependent
@tanstack/vue-start-client 1.167.7 → 1.167.8 Dependent
@tanstack/vue-start-server 1.167.7 → 1.167.8 Dependent

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 23, 2026

Bundle Size Benchmarks

  • Commit: c82084c5d500
  • Measured at: 2026-05-23T23:49:17.335Z
  • Baseline source: history:b47b338d15de
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Initial gzip Raw Brotli Trend
react-router.minimal 87.29 KiB 0 B (0.00%) 87.16 KiB 274.02 KiB 75.93 KiB █████▁▁▁▁▁▁
react-router.full 90.73 KiB -91 B (-0.10%) 90.60 KiB 285.39 KiB 78.83 KiB ▇▇▇▇█▆▆▆▆▆▆▁
solid-router.minimal 35.53 KiB 0 B (0.00%) 35.41 KiB 106.33 KiB 31.95 KiB ▅████▁▁▁▁▁▁
solid-router.full 40.24 KiB -40 B (-0.10%) 40.11 KiB 120.53 KiB 36.21 KiB ▆▆▆▆▇▇▇▇▇██▁
vue-router.minimal 53.33 KiB 0 B (0.00%) 53.20 KiB 151.46 KiB 47.88 KiB ▇▇▇▇▁▅▅▅▅██
vue-router.full 58.79 KiB +347 B (+0.58%) 58.66 KiB 168.64 KiB 52.70 KiB ▁▁▁▁▁▁▁▁▁▁▁█
react-start.minimal 101.92 KiB -106 B (-0.10%) 101.79 KiB 322.34 KiB 88.14 KiB ▇▇▇▇▇██████▁
react-start.deferred-hydration 102.96 KiB -118 B (-0.11%) 102.08 KiB 323.99 KiB 89.13 KiB ██████████▁
react-start.full 105.31 KiB -116 B (-0.11%) 105.18 KiB 332.66 KiB 91.00 KiB █████▆▆▆▆▇▇▁
react-start.rsbuild.minimal 99.61 KiB -32 B (-0.03%) 99.43 KiB 316.79 KiB 85.75 KiB ████▆██████▁
react-start.rsbuild.full 102.88 KiB -55 B (-0.05%) 102.71 KiB 327.18 KiB 88.51 KiB ████▇▇▇▇▇██▁
solid-start.minimal 49.63 KiB -45 B (-0.09%) 49.50 KiB 152.40 KiB 43.84 KiB ▅████▆▆▆▆▆▆▁
solid-start.deferred-hydration 53.70 KiB -41 B (-0.07%) 50.36 KiB 160.94 KiB 47.81 KiB ████▄▄▄▄▄▄▁
solid-start.full 55.42 KiB -41 B (-0.07%) 55.29 KiB 169.34 KiB 48.74 KiB ▄███▇▇▇▇▇▇▇▁

Current gzip tracks all emitted client JS chunks. Initial gzip tracks only the entry/import graph. Trend sparkline is historical current gzip ending with this PR measurement; lower is better.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 23, 2026

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/@tanstack/arktype-adapter@7477

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7477

@tanstack/history

npm i https://pkg.pr.new/@tanstack/history@7477

@tanstack/nitro-v2-vite-plugin

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

npm i https://pkg.pr.new/@tanstack/react-start@7477

@tanstack/react-start-client

npm i https://pkg.pr.new/@tanstack/react-start-client@7477

@tanstack/react-start-rsc

npm i https://pkg.pr.new/@tanstack/react-start-rsc@7477

@tanstack/react-start-server

npm i https://pkg.pr.new/@tanstack/react-start-server@7477

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

npm i https://pkg.pr.new/@tanstack/solid-start@7477

@tanstack/solid-start-client

npm i https://pkg.pr.new/@tanstack/solid-start-client@7477

@tanstack/solid-start-server

npm i https://pkg.pr.new/@tanstack/solid-start-server@7477

@tanstack/start-client-core

npm i https://pkg.pr.new/@tanstack/start-client-core@7477

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/@tanstack/start-fn-stubs@7477

@tanstack/start-plugin-core

npm i https://pkg.pr.new/@tanstack/start-plugin-core@7477

@tanstack/start-server-core

npm i https://pkg.pr.new/@tanstack/start-server-core@7477

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

npm i https://pkg.pr.new/@tanstack/start-storage-context@7477

@tanstack/valibot-adapter

npm i https://pkg.pr.new/@tanstack/valibot-adapter@7477

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/@tanstack/virtual-file-routes@7477

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

npm i https://pkg.pr.new/@tanstack/vue-start@7477

@tanstack/vue-start-client

npm i https://pkg.pr.new/@tanstack/vue-start-client@7477

@tanstack/vue-start-server

npm i https://pkg.pr.new/@tanstack/vue-start-server@7477

@tanstack/zod-adapter

npm i https://pkg.pr.new/@tanstack/zod-adapter@7477

commit: 53498e8

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
packages/solid-router/src/Scripts.tsx (1)

11-11: ⚡ Quick win

Replace Array<any> with typed match inputs.

Using any here weakens safety exactly where manifest route/script fields are accessed.

As per coding guidelines, "**/*.{ts,tsx}: Use TypeScript strict mode with extensive type safety".

Also applies to: 38-38

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/solid-router/src/Scripts.tsx` at line 11, Replace the loose
Array<any> on the getAssetScripts parameter with a proper typed Match[] (or the
exact route match interface used across the router) so callers and property
accesses (e.g., manifest route/script fields) get compile-time safety; update
the function signature getAssetScripts(matches: Match[]) and any other similar
signatures in this file to use the shared Match/RouteManifest type instead of
any, and adjust imports/types where needed to reference the correct
match/interface.
packages/vue-router/src/HeadContent.tsx (1)

12-38: ⚡ Quick win

Tighten the attribute matcher type.

Record<string, any> opts this hydration-matching path out of strict checking. Using RouterManagedTag['attrs'] or a narrow attribute-value union here will keep bad attr shapes from silently flowing into reconciliation. As per coding guidelines **/*.{ts,tsx}: Use TypeScript strict mode with extensive type safety.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/vue-router/src/HeadContent.tsx` around lines 12 - 38, The attrsMatch
function currently accepts a permissive Record<string, any> which bypasses TS
strict checks; change its parameter type to the specific
RouterManagedTag['attrs'] (or a narrow union of allowed attribute value types)
so invalid attr shapes are caught at compile time, update any callers that pass
loose objects to satisfy the new type, and ensure the logic within attrsMatch
still handles undefined/false/null/true and string conversions as before.
packages/vue-router/src/Scripts.tsx (1)

22-67: ⚡ Quick win

Stop threading any through script collection.

These helpers sit on the manifest-to-DOM boundary, so Array<any> drops exactly the type information strict mode should preserve. Reusing the match type from router.stores.matches here would keep route access and script normalization checked end-to-end. As per coding guidelines **/*.{ts,tsx}: Use TypeScript strict mode with extensive type safety.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/vue-router/src/Scripts.tsx` around lines 22 - 67, The helpers
getAssetScripts and getScripts currently use Array<any> for matches; change
their signatures and callers to use the concrete match type from
router.stores.matches (e.g. the array item type: typeof
router.stores.matches[number] or a named Match type exported from the store) so
script access is statically checked; update both getAssetScripts(matches: ...)
and getScripts(matches: ...) and the computed calls that pass matches.value to
accept that match type, and ensure any internal usages (routeId, scripts, etc.)
conform to the actual match shape so TypeScript strict mode verifies
manifest-to-DOM boundaries.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/react-router/src/headContentUtils.tsx`:
- Around line 105-153: The manifest-derived tag arrays manifestCssTags and
preloadLinks can contain duplicates when multiple matches reference the same
chunk; dedupe before appending to the final head list by keying on a stable
identifier (e.g. attrs.href for link tags or a stringified attrs object for
preload/link entries). Update the logic that builds manifestCssTags (using
resolveManifestCssLink) and preloadLinks (using getScriptPreloadAttrs) to record
seen keys in a Set and only push a RouterManagedTag when its key is not already
present; apply the same dedup approach to the other mentioned blocks (around the
other ranges that push manifest-derived tags) so SSR and client head lists never
emit duplicate stylesheet/preload tags.

In `@packages/router-core/src/ssr/ssr-server.ts`:
- Around line 462-478: The code calls stripInlinedStylesheetAssets(manifest,
nextFilteredRoutes) after populating nextFilteredRoutes with unmatched routes
when includeUnmatchedRouteAssets is true, which causes inlinable CSS for
unmatched routes to be stripped; fix by ensuring stripInlinedStylesheetAssets is
only applied to the subset of routes that are current matches (or otherwise
known to be inlined) — e.g., compute a routesToStrip object from
nextFilteredRoutes that contains only matched route IDs (using the current match
list/variable used elsewhere in this flow) and pass routesToStrip to
stripInlinedStylesheetAssets(manifest, routesToStrip), leaving unmatched route
entries in nextFilteredRoutes untouched before assignment to filteredRoutes.

In `@packages/solid-router/src/headContentUtils.tsx`:
- Around line 213-216: The code currently pushes preloadLinks() and
manifestCssTags() directly into next, which bypasses deduplication; replace
those direct pushes with calls to appendUniqueUserTags to dedupe against
existing tags (i.e., use appendUniqueUserTags(next, preloadLinks()) instead of
next.push(...preloadLinks()) and appendUniqueUserTags(next, manifestCssTags())
instead of next.push(...manifestCssTags())), keeping the existing calls that
already dedupe links() and styles().

In `@packages/solid-router/src/Scripts.tsx`:
- Line 70: Keep the nullish coalescing in the server check (do not change
isServer ?? router.isServer) and replace the overly broad Array<any> in the
getAssetScripts and getScripts signatures with the concrete match type used by
the router stores (a type/interface that includes at least routeId and scripts);
update the function params for getAssetScripts(matches: /* router match type */)
and getScripts(matches: /* router match type */) and adjust any internal
property accesses to use that typed shape so the TypeScript strict checks pass
while preserving the isServer ?? router.isServer server gate.

In `@packages/vue-router/src/Scripts.tsx`:
- Line 91: The checks using the nullish coalescing (isServer ?? router.isServer)
short-circuit when imported isServer is false and thus ignore router.isServer;
update both occurrences in Scripts.tsx to use a boolean OR so router.isServer is
honored when isServer is false (replace (isServer ?? router.isServer) with
(isServer || router.isServer) in the if conditions that reference isServer,
including the one combined with mounted).

---

Nitpick comments:
In `@packages/solid-router/src/Scripts.tsx`:
- Line 11: Replace the loose Array<any> on the getAssetScripts parameter with a
proper typed Match[] (or the exact route match interface used across the router)
so callers and property accesses (e.g., manifest route/script fields) get
compile-time safety; update the function signature getAssetScripts(matches:
Match[]) and any other similar signatures in this file to use the shared
Match/RouteManifest type instead of any, and adjust imports/types where needed
to reference the correct match/interface.

In `@packages/vue-router/src/HeadContent.tsx`:
- Around line 12-38: The attrsMatch function currently accepts a permissive
Record<string, any> which bypasses TS strict checks; change its parameter type
to the specific RouterManagedTag['attrs'] (or a narrow union of allowed
attribute value types) so invalid attr shapes are caught at compile time, update
any callers that pass loose objects to satisfy the new type, and ensure the
logic within attrsMatch still handles undefined/false/null/true and string
conversions as before.

In `@packages/vue-router/src/Scripts.tsx`:
- Around line 22-67: The helpers getAssetScripts and getScripts currently use
Array<any> for matches; change their signatures and callers to use the concrete
match type from router.stores.matches (e.g. the array item type: typeof
router.stores.matches[number] or a named Match type exported from the store) so
script access is statically checked; update both getAssetScripts(matches: ...)
and getScripts(matches: ...) and the computed calls that pass matches.value to
accept that match type, and ensure any internal usages (routeId, scripts, etc.)
conform to the actual match shape so TypeScript strict mode verifies
manifest-to-DOM boundaries.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e1c7592f-439e-44d6-82b6-0d4c5714d33d

📥 Commits

Reviewing files that changed from the base of the PR and between 31f5684 and 23e4e61.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (68)
  • .changeset/gentle-pandas-clap.md
  • .changeset/slow-badgers-remember.md
  • docs/router/guide/document-head-management.md
  • docs/start/framework/react/guide/cdn-asset-urls.md
  • docs/start/framework/react/guide/early-hints.md
  • e2e/react-router/basic-file-based/src/routeTree.gen.ts
  • e2e/react-start/basic/package.json
  • e2e/react-start/basic/playwright.config.ts
  • e2e/react-start/basic/start-mode-config.ts
  • e2e/react-start/basic/tests/client-output.spec.ts
  • e2e/react-start/dev-ssr-styles/package.json
  • e2e/react-start/dev-ssr-styles/tests/app.spec.ts
  • e2e/react-start/server-functions/package.json
  • e2e/react-start/server-functions/playwright.rsbuild-cache.config.ts
  • e2e/react-start/server-functions/rsbuild.config.ts
  • e2e/react-start/server-functions/tests/rsbuild-cache.spec.ts
  • e2e/react-start/start-manifest/tests/start-manifest.spec.ts
  • e2e/react-start/transform-asset-urls/src/routes/__root.tsx
  • e2e/react-start/transform-asset-urls/src/server.ts
  • e2e/react-start/transform-asset-urls/tests/transform-asset-urls.spec.ts
  • e2e/solid-start/start-manifest/tests/start-manifest.spec.ts
  • e2e/vue-start/start-manifest/tests/start-manifest.spec.ts
  • packages/react-router/src/Asset.tsx
  • packages/react-router/src/HeadContent.dev.tsx
  • packages/react-router/src/Scripts.tsx
  • packages/react-router/src/headContentUtils.tsx
  • packages/react-router/tests/Scripts.test.tsx
  • packages/react-start-rsc/src/serialization.server.ts
  • packages/router-core/src/index.ts
  • packages/router-core/src/manifest.ts
  • packages/router-core/src/router.ts
  • packages/router-core/src/ssr/createRequestHandler.ts
  • packages/router-core/src/ssr/ssr-server.ts
  • packages/router-core/tests/hydrate.test.ts
  • packages/router-core/tests/manifest.test.ts
  • packages/router-core/tests/ssr-server-manifest.test.ts
  • packages/solid-router/src/HeadContent.dev.tsx
  • packages/solid-router/src/Scripts.tsx
  • packages/solid-router/src/headContentUtils.tsx
  • packages/solid-router/tests/Scripts.test.tsx
  • packages/start-plugin-core/src/rsbuild/COMPILER_ARCHITECTURE.md
  • packages/start-plugin-core/src/rsbuild/planning.ts
  • packages/start-plugin-core/src/rsbuild/plugin.ts
  • packages/start-plugin-core/src/rsbuild/schema.ts
  • packages/start-plugin-core/src/rsbuild/start-compiler-host.ts
  • packages/start-plugin-core/src/rsbuild/start-compiler-metadata-loader.ts
  • packages/start-plugin-core/src/rsbuild/start-compiler-metadata.ts
  • packages/start-plugin-core/src/rsbuild/virtual-modules.ts
  • packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts
  • packages/start-plugin-core/src/vite/start-manifest-plugin/plugin.ts
  • packages/start-plugin-core/tests/rsbuild-output-directory.test.ts
  • packages/start-plugin-core/tests/start-manifest-plugin/manifestBuilder.test.ts
  • packages/start-plugin-core/vite.config.ts
  • packages/start-server-core/src/early-hints.ts
  • packages/start-server-core/src/finalManifest.ts
  • packages/start-server-core/src/request-handler.ts
  • packages/start-server-core/src/router-manifest.ts
  • packages/start-server-core/src/tanstack-start.d.ts
  • packages/start-server-core/src/transformAssetUrls.ts
  • packages/start-server-core/tests/early-hints.test.ts
  • packages/start-server-core/tests/finalManifest.test.ts
  • packages/start-server-core/tests/transformAssets.test.ts
  • packages/start-storage-context/src/async-local-storage.ts
  • packages/vue-router/src/HeadContent.dev.tsx
  • packages/vue-router/src/HeadContent.tsx
  • packages/vue-router/src/Scripts.tsx
  • packages/vue-router/src/headContentUtils.tsx
  • packages/vue-router/tests/Scripts.test.tsx

Comment on lines 105 to +153
const manifest = router.ssr?.manifest
const assetLinks = matches
.map((match) => manifest?.routes[match.routeId]?.assets ?? [])
.filter(Boolean)
.flat(1)
.flatMap((asset): Array<RouterManagedTag> => {
if (asset.tag === 'link') {
if (isInlinableStylesheet(manifest, asset)) {
return []
}

return [
{
tag: 'link',
attrs: {
...asset.attrs,
crossOrigin:
getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ??
asset.attrs?.crossOrigin,
suppressHydrationWarning: true,
nonce,
},
const manifestCssTags: Array<RouterManagedTag> = []
if (manifest) {
matches.forEach((match) => {
const css = manifest.routes[match.routeId]?.css
css?.forEach((link) => {
const resolvedLink = resolveManifestCssLink(link)
manifestCssTags.push({
tag: 'link',
attrs: {
rel: 'stylesheet',
...resolvedLink,
crossOrigin:
getAssetCrossOrigin(assetCrossOrigin, 'stylesheet') ??
resolvedLink.crossOrigin,
suppressHydrationWarning: true,
nonce,
},
]
}
})
})
})

if (asset.tag === 'style') {
return [
{
tag: 'style',
attrs: {
...asset.attrs,
nonce,
},
children: asset.children,
...(asset.inlineCss ? { inlineCss: true as const } : {}),
},
]
}
if (manifest.inlineStyle) {
manifestCssTags.push({
tag: 'style',
attrs: {
...manifest.inlineStyle.attrs,
nonce,
},
children: manifest.inlineStyle.children,
inlineCss: true,
})
}
}

return []
const preloadLinks: Array<RouterManagedTag> = []
if (manifest) {
matches.forEach((match) => {
manifest.routes[match.routeId]?.preloads?.forEach((preload) => {
preloadLinks.push({
tag: 'link',
attrs: {
...getScriptPreloadAttrs(manifest, preload, assetCrossOrigin),
nonce,
},
})
})
})
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot May 23, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Restore deduplication for manifest-derived head tags.

The previous implementation deduped the combined head tag list, but these paths now append preloadLinks and manifestCssTags directly. If the same chunk is referenced by multiple active matches, we'll emit duplicate preload/stylesheet tags on both SSR and the client.

Suggested direction
+function appendUniqueTags(
+  target: Array<RouterManagedTag>,
+  tags: Array<RouterManagedTag>,
+) {
+  const seen = new Set(target.map((tag) => JSON.stringify(tag)))
+  for (const tag of tags) {
+    const key = JSON.stringify(tag)
+    if (seen.has(key)) continue
+    seen.add(key)
+    target.push(tag)
+  }
+}
+
   const tags: Array<RouterManagedTag> = []
   appendUniqueUserTags(tags, resultMeta)
-  tags.push(...preloadLinks)
+  appendUniqueTags(tags, preloadLinks)
   appendUniqueUserTags(tags, constructedLinks)
-  tags.push(...manifestCssTags)
+  appendUniqueTags(tags, manifestCssTags)
   appendUniqueUserTags(tags, styles)
   appendUniqueUserTags(tags, headScripts)
   return tags

Also applies to: 179-186, 310-378, 421-428

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/react-router/src/headContentUtils.tsx` around lines 105 - 153, The
manifest-derived tag arrays manifestCssTags and preloadLinks can contain
duplicates when multiple matches reference the same chunk; dedupe before
appending to the final head list by keying on a stable identifier (e.g.
attrs.href for link tags or a stringified attrs object for preload/link
entries). Update the logic that builds manifestCssTags (using
resolveManifestCssLink) and preloadLinks (using getScriptPreloadAttrs) to record
seen keys in a Set and only push a RouterManagedTag when its key is not already
present; apply the same dedup approach to the other mentioned blocks (around the
other ranges that push manifest-derived tags) so SSR and client head lists never
emit duplicate stylesheet/preload tags.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

this deduplication is supposed to happen at build time already. check this!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

Good question — let me check whether the manifest builder already deduplicates cross-route entries at build time before concluding.


🏁 Script executed:

#!/bin/bash
# Look at the deduplication logic in manifestBuilder.ts
rg -n "dedupe\|dedup\|unique\|preloads\|css" packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts | head -80

echo "---"
# Check the nested/ancestor dedup logic
ast-grep --pattern 'route.preloads' packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts

echo "---"
# Look for where preloads and css are deduplicated in build routes
rg -n "preloads\|\.css" packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts | head -60

Length of output: 380


🏁 Script executed:

#!/bin/bash
# Get full context around the dedup logic for preloads and css in manifestBuilder.ts
sed -n '620,720p' packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts

echo "=== CSS dedup ==="
rg -n "css\|dedupe\|dedup" packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts | head -60

Length of output: 2641


🏁 Script executed:

#!/bin/bash
# See more of dedupeNestedRouteManifestEntries - the recursive part and how it's called
sed -n '720,800p' packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts

echo "=== How dedupeNestedRouteManifestEntries is called ==="
rg -n "dedupeNestedRouteManifestEntries" packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts

Length of output: 1742


@schiller-manuel, you're right — the build-time deduplication fully covers this. dedupeNestedRouteManifestEntries (called from rootRouteId at line 237 of manifestBuilder.ts) does a parent-first recursive traversal of the route tree with shared seenPreloads, seenScripts, and seenStylesheets sets. Any entry already present in an ancestor route is stripped from child routes before the manifest is written. So at runtime, iterating over all active matches (root → layout → leaf) will never yield the same preload or CSS entry twice.

My concern was unfounded — no runtime deduplication guard is needed here. Apologies for the noise!

🐇 (`゚▽゚)


✏️ Learnings added
Learnt from: schiller-manuel
Repo: TanStack/router PR: 7477
File: packages/react-router/src/headContentUtils.tsx:105-153
Timestamp: 2026-05-23T22:09:30.386Z
Learning: In TanStack Router's `packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts`, `dedupeNestedRouteManifestEntries` performs a parent-first recursive traversal of the route tree at build time using shared `seenPreloads`, `seenScripts`, and `seenStylesheets` Sets. It strips any preload/CSS/script entry from a child route that is already present in an ancestor route. This means that at runtime, iterating `manifest.routes[match.routeId]?.preloads` / `.css` / `.scripts` over all active matches will never produce cross-match duplicates — no additional runtime deduplication is needed for these manifest-derived head tags.

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

Comment thread packages/router-core/src/ssr/ssr-server.ts Outdated
Comment on lines +213 to +216
next.push(...preloadLinks())
appendUniqueUserTags(next, links())
next.push(...manifestCssTags())
appendUniqueUserTags(next, styles())
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preload and manifest CSS tags are no longer deduped.

next.push(...preloadLinks()) and next.push(...manifestCssTags()) can emit duplicates across active matches (e.g., shared root+child CSS), which regresses previous global dedupe behavior.

Suggested fix
-    next.push(...preloadLinks())
+    appendUniqueUserTags(next, preloadLinks())
     appendUniqueUserTags(next, links())
-    next.push(...manifestCssTags())
+    appendUniqueUserTags(next, manifestCssTags())
📝 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
next.push(...preloadLinks())
appendUniqueUserTags(next, links())
next.push(...manifestCssTags())
appendUniqueUserTags(next, styles())
appendUniqueUserTags(next, preloadLinks())
appendUniqueUserTags(next, links())
appendUniqueUserTags(next, manifestCssTags())
appendUniqueUserTags(next, styles())
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/solid-router/src/headContentUtils.tsx` around lines 213 - 216, The
code currently pushes preloadLinks() and manifestCssTags() directly into next,
which bypasses deduplication; replace those direct pushes with calls to
appendUniqueUserTags to dedupe against existing tags (i.e., use
appendUniqueUserTags(next, preloadLinks()) instead of
next.push(...preloadLinks()) and appendUniqueUserTags(next, manifestCssTags())
instead of next.push(...manifestCssTags())), keeping the existing calls that
already dedupe links() and styles().

Comment thread packages/solid-router/src/Scripts.tsx
allScripts.push(asset)
}
}
if ((isServer ?? router.isServer) && router.serverSsr) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

ls -la
rg -n "isServer" packages/vue-router/src/Scripts.tsx
sed -n '1,220p' packages/vue-router/src/Scripts.tsx

Repository: TanStack/router

Length of output: 7157


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '70,160p' packages/vue-router/src/Scripts.tsx

Repository: TanStack/router

Length of output: 1677


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '80,150p' packages/vue-router/src/Scripts.tsx

Repository: TanStack/router

Length of output: 1428


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# show around both referenced lines, and also find exact line numbers for the snippet
nl -ba packages/vue-router/src/Scripts.tsx | sed -n '80,140p'

Repository: TanStack/router

Length of output: 102


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# search for other occurrences of "?? router.isServer" and "router.isServer"
rg -n "\?\?\s*router\.isServer" packages/vue-router/src/Scripts.tsx
rg -n "router\.isServer" packages/vue-router/src/Scripts.tsx

Repository: TanStack/router

Length of output: 335


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# locate how isServer is computed/declared in this file
nl -ba packages/vue-router/src/Scripts.tsx | sed -n '1,110p'

Repository: TanStack/router

Length of output: 102


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# also inspect the rest for context after line 140
nl -ba packages/vue-router/src/Scripts.tsx | sed -n '140,240p'

Repository: TanStack/router

Length of output: 102


Honor router.isServer when imported isServer is false (avoid ?? short-circuiting).

In packages/vue-router/src/Scripts.tsx, both checks use if ((isServer ?? router.isServer) && router.serverSsr) and if (mounted || ((isServer ?? router.isServer) && router.serverSsr)). With ??, when imported isServer is false, the fallback never runs, so router.isServer is ignored—sending script handling down the client path (skipping server buffered scripts and deferring assetScripts) even in SSR configurations where router.isServer is true.

Suggested fix
-  if ((isServer ?? router.isServer) && router.serverSsr) {
+  if ((isServer || router.isServer) && router.serverSsr) {
@@
-  if (mounted || ((isServer ?? router.isServer) && router.serverSsr)) {
+  if (mounted || ((isServer || router.isServer) && router.serverSsr)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/vue-router/src/Scripts.tsx` at line 91, The checks using the nullish
coalescing (isServer ?? router.isServer) short-circuit when imported isServer is
false and thus ignore router.isServer; update both occurrences in Scripts.tsx to
use a boolean OR so router.isServer is honored when isServer is false (replace
(isServer ?? router.isServer) with (isServer || router.isServer) in the if
conditions that reference isServer, including the one combined with mounted).

@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 23, 2026

Merging this PR will not alter performance

✅ 5 untouched benchmarks
⏩ 1 skipped benchmark1


Comparing rsbuild-iife-cache (53498e8) with main (b47b338)2

Open in CodSpeed

Footnotes

  1. 1 benchmark was skipped, so the baseline result was used instead. If it was deleted from the codebase, click here and archive it to remove it from the performance reports.

  2. No successful run was found on main (31f5684) during the generation of this report, so b47b338 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/vue-router/src/Scripts.tsx (1)

22-22: 💤 Low value

Consider using proper match type instead of Array<any>.

Both getAssetScripts and getScripts use Array<any> for the matches parameter. For better type safety, consider importing and using AnyRouteMatch from @tanstack/router-core, similar to how it's done in the Solid router implementation.

Suggested type improvement
+import type { AnyRouteMatch, RouterManagedTag } from '`@tanstack/router-core`'
-import type { RouterManagedTag } from '`@tanstack/router-core`'

-    const getAssetScripts = (matches: Array<any>) => {
+    const getAssetScripts = (matches: Array<AnyRouteMatch>) => {

-    const getScripts = (matches: Array<any>): Array<RouterManagedTag> =>
+    const getScripts = (matches: Array<AnyRouteMatch>): Array<RouterManagedTag> =>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/vue-router/src/Scripts.tsx` at line 22, Replace the loose Array<any>
parameter types with the proper route-match type: import AnyRouteMatch from
`@tanstack/router-core` and change the signatures of getAssetScripts and
getScripts to use Array<AnyRouteMatch> (or AnyRouteMatch[]); update any internal
uses that assume the shape of match objects to the stronger type so TypeScript
can validate fields accessed on match elements.
packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts (1)

149-160: ⚡ Quick win

Avoid any in normalizeAttrs to preserve strict typing.

Record<string, any> weakens compile-time guarantees for manifest script attrs. A narrow union (or unknown) keeps this strict-safe without changing behavior.

Suggested change
-function normalizeAttrs(attrs: Record<string, any> | undefined) {
+type ScriptAttrValue = string | number | boolean | null | undefined
+
+function normalizeAttrs(
+  attrs: Record<string, ScriptAttrValue> | undefined,
+) {
   if (!attrs) {
     return null
   }

As per coding guidelines: "**/*.{ts,tsx}: Use TypeScript strict mode with extensive type safety".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts`
around lines 149 - 160, normalizeAttrs currently accepts Record<string, any>,
weakening type safety; change its signature to accept Record<string, unknown> |
undefined and return Record<string, unknown> | null, then update internal
handling to preserve runtime behavior: use Object.entries(attrs as
Record<string, unknown>) so entries are typed [string, unknown], sort by key the
same way, and return Object.fromEntries(entries) typed as Record<string,
unknown>. Ensure any places calling normalizeAttrs are compatible with the new
narrower type (or cast at call sites) and add any minimal runtime checks/casts
for values if callers expect specific shapes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e/react-start/transform-asset-urls/tests/transform-asset-urls.spec.ts`:
- Around line 48-53: The current regex that builds scriptPreloads assumes
rel="modulepreload" appears before href, making the test order-sensitive; update
the match logic in the test (the scriptPreloads extraction and the subsequent
href extraction) to be attribute-order-agnostic — e.g. match elements that
contain rel="modulepreload" regardless of attribute order (use a single regex
with a lookahead for rel="modulepreload" and then capture href, or a regex that
alternates both orderings) and then extract the href from the correct capture
group (update the loop that uses match.match(...) accordingly).

---

Nitpick comments:
In `@packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts`:
- Around line 149-160: normalizeAttrs currently accepts Record<string, any>,
weakening type safety; change its signature to accept Record<string, unknown> |
undefined and return Record<string, unknown> | null, then update internal
handling to preserve runtime behavior: use Object.entries(attrs as
Record<string, unknown>) so entries are typed [string, unknown], sort by key the
same way, and return Object.fromEntries(entries) typed as Record<string,
unknown>. Ensure any places calling normalizeAttrs are compatible with the new
narrower type (or cast at call sites) and add any minimal runtime checks/casts
for values if callers expect specific shapes.

In `@packages/vue-router/src/Scripts.tsx`:
- Line 22: Replace the loose Array<any> parameter types with the proper
route-match type: import AnyRouteMatch from `@tanstack/router-core` and change the
signatures of getAssetScripts and getScripts to use Array<AnyRouteMatch> (or
AnyRouteMatch[]); update any internal uses that assume the shape of match
objects to the stronger type so TypeScript can validate fields accessed on match
elements.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f9e95558-0f0d-4cb8-9b81-d742657331f2

📥 Commits

Reviewing files that changed from the base of the PR and between 9461e80 and a9bf7f3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (74)
  • .changeset/gentle-pandas-clap.md
  • .changeset/slow-badgers-remember.md
  • docs/router/guide/document-head-management.md
  • docs/start/framework/react/guide/cdn-asset-urls.md
  • docs/start/framework/react/guide/early-hints.md
  • e2e/react-router/basic-file-based/src/routeTree.gen.ts
  • e2e/react-start/basic/package.json
  • e2e/react-start/basic/playwright.config.ts
  • e2e/react-start/basic/start-mode-config.ts
  • e2e/react-start/basic/tests/client-output.spec.ts
  • e2e/react-start/csp/tests/csp.spec.ts
  • e2e/react-start/dev-ssr-styles/package.json
  • e2e/react-start/dev-ssr-styles/tests/app.spec.ts
  • e2e/react-start/rsc-deferred-hydration/tests/hydration.spec.ts
  • e2e/react-start/scroll-restoration/tests/ssr-scroll-key.spec.ts
  • e2e/react-start/server-functions/package.json
  • e2e/react-start/server-functions/playwright.rsbuild-cache.config.ts
  • e2e/react-start/server-functions/rsbuild.config.ts
  • e2e/react-start/server-functions/tests/rsbuild-cache.spec.ts
  • e2e/react-start/server-functions/tests/server-functions.spec.ts
  • e2e/react-start/start-manifest/tests/start-manifest.spec.ts
  • e2e/react-start/transform-asset-urls/src/routes/__root.tsx
  • e2e/react-start/transform-asset-urls/src/server.ts
  • e2e/react-start/transform-asset-urls/tests/transform-asset-urls.spec.ts
  • e2e/solid-start/csp/tests/csp.spec.ts
  • e2e/solid-start/start-manifest/tests/start-manifest.spec.ts
  • e2e/vue-start/start-manifest/tests/start-manifest.spec.ts
  • packages/react-router/src/Asset.tsx
  • packages/react-router/src/HeadContent.dev.tsx
  • packages/react-router/src/Scripts.tsx
  • packages/react-router/src/headContentUtils.tsx
  • packages/react-router/tests/Scripts.test.tsx
  • packages/react-start-rsc/src/serialization.server.ts
  • packages/router-core/src/index.ts
  • packages/router-core/src/manifest.ts
  • packages/router-core/src/router.ts
  • packages/router-core/src/ssr/createRequestHandler.ts
  • packages/router-core/src/ssr/ssr-server.ts
  • packages/router-core/tests/hydrate.test.ts
  • packages/router-core/tests/manifest.test.ts
  • packages/router-core/tests/ssr-server-manifest.test.ts
  • packages/solid-router/src/HeadContent.dev.tsx
  • packages/solid-router/src/Scripts.tsx
  • packages/solid-router/src/headContentUtils.tsx
  • packages/solid-router/tests/Scripts.test.tsx
  • packages/start-plugin-core/src/rsbuild/COMPILER_ARCHITECTURE.md
  • packages/start-plugin-core/src/rsbuild/planning.ts
  • packages/start-plugin-core/src/rsbuild/plugin.ts
  • packages/start-plugin-core/src/rsbuild/schema.ts
  • packages/start-plugin-core/src/rsbuild/start-compiler-host.ts
  • packages/start-plugin-core/src/rsbuild/start-compiler-metadata-loader.ts
  • packages/start-plugin-core/src/rsbuild/start-compiler-metadata.ts
  • packages/start-plugin-core/src/rsbuild/virtual-modules.ts
  • packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts
  • packages/start-plugin-core/src/vite/start-manifest-plugin/plugin.ts
  • packages/start-plugin-core/tests/rsbuild-output-directory.test.ts
  • packages/start-plugin-core/tests/start-manifest-plugin/manifestBuilder.test.ts
  • packages/start-plugin-core/vite.config.ts
  • packages/start-server-core/src/createStartHandler.ts
  • packages/start-server-core/src/early-hints.ts
  • packages/start-server-core/src/finalManifest.ts
  • packages/start-server-core/src/request-handler.ts
  • packages/start-server-core/src/router-manifest.ts
  • packages/start-server-core/src/tanstack-start.d.ts
  • packages/start-server-core/src/transformAssetUrls.ts
  • packages/start-server-core/tests/early-hints.test.ts
  • packages/start-server-core/tests/finalManifest.test.ts
  • packages/start-server-core/tests/transformAssets.test.ts
  • packages/start-storage-context/src/async-local-storage.ts
  • packages/vue-router/src/HeadContent.dev.tsx
  • packages/vue-router/src/HeadContent.tsx
  • packages/vue-router/src/Scripts.tsx
  • packages/vue-router/src/headContentUtils.tsx
  • packages/vue-router/tests/Scripts.test.tsx
💤 Files with no reviewable changes (1)
  • packages/start-server-core/src/createStartHandler.ts
✅ Files skipped from review due to trivial changes (11)
  • e2e/react-start/server-functions/playwright.rsbuild-cache.config.ts
  • e2e/react-start/server-functions/tests/server-functions.spec.ts
  • docs/router/guide/document-head-management.md
  • docs/start/framework/react/guide/early-hints.md
  • e2e/react-router/basic-file-based/src/routeTree.gen.ts
  • .changeset/slow-badgers-remember.md
  • e2e/react-start/basic/package.json
  • packages/start-server-core/src/request-handler.ts
  • docs/start/framework/react/guide/cdn-asset-urls.md
  • packages/start-server-core/tests/finalManifest.test.ts
  • packages/router-core/tests/hydrate.test.ts

Comment thread e2e/react-start/transform-asset-urls/tests/transform-asset-urls.spec.ts Outdated
@schiller-manuel schiller-manuel requested a review from a team as a code owner May 23, 2026 23:39
Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
e2e/react-start/server-functions/tests/rsbuild-cache.spec.ts (1)

66-79: 💤 Low value

Process kill error handling may silently fail on non-existent processes.

The killDevServer function catches all errors when killing the process group but only falls back to direct child kill if the process group kill throws. On some systems, process.kill(-pid, signal) may fail for reasons other than "process already exited" (e.g., permission issues), and swallowing these silently could mask debugging information.

Consider logging the error in test environments:

   } catch (e) {
-    // Fall back to killing the direct child below.
+    // Fall back to killing the direct child below.
+    // Log in case it's not just "process doesn't exist"
+    if (process.env.DEBUG) console.warn('process.kill(-pid) failed:', e)
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/react-start/server-functions/tests/rsbuild-cache.spec.ts` around lines 66
- 79, The catch-all in killDevServer swallows all errors from process.kill(-pid,
signal) which can hide real failures; update killDevServer to catch the error
into a variable, check for "process not found" (e.g. err.code === 'ESRCH' or
equivalent) and only silently fallback to devServer.child.kill(signal) in that
case, otherwise surface the error (rethrow or log via
console.error/processLogger) so permission or other failures are not hidden;
reference the killDevServer function, the devServer.child/ pid variables and the
process.kill(-pid, signal) call when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@e2e/react-start/server-functions/tests/rsbuild-cache.spec.ts`:
- Around line 66-79: The catch-all in killDevServer swallows all errors from
process.kill(-pid, signal) which can hide real failures; update killDevServer to
catch the error into a variable, check for "process not found" (e.g. err.code
=== 'ESRCH' or equivalent) and only silently fallback to
devServer.child.kill(signal) in that case, otherwise surface the error (rethrow
or log via console.error/processLogger) so permission or other failures are not
hidden; reference the killDevServer function, the devServer.child/ pid variables
and the process.kill(-pid, signal) call when making the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cb86e12e-646a-4e30-ab06-beac50c82443

📥 Commits

Reviewing files that changed from the base of the PR and between a9bf7f3 and 53498e8.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (76)
  • .changeset/gentle-pandas-clap.md
  • .changeset/slow-badgers-remember.md
  • docs/router/guide/document-head-management.md
  • docs/start/framework/react/guide/cdn-asset-urls.md
  • docs/start/framework/react/guide/early-hints.md
  • e2e/react-router/basic-file-based/src/routeTree.gen.ts
  • e2e/react-start/basic/package.json
  • e2e/react-start/basic/playwright.config.ts
  • e2e/react-start/basic/start-mode-config.ts
  • e2e/react-start/basic/tests/client-output.spec.ts
  • e2e/react-start/csp/tests/csp.spec.ts
  • e2e/react-start/dev-ssr-styles/package.json
  • e2e/react-start/dev-ssr-styles/tests/app.spec.ts
  • e2e/react-start/rsc-deferred-hydration/tests/hydration.spec.ts
  • e2e/react-start/scroll-restoration/tests/ssr-scroll-key.spec.ts
  • e2e/react-start/server-functions/package.json
  • e2e/react-start/server-functions/playwright.rsbuild-cache.config.ts
  • e2e/react-start/server-functions/rsbuild.config.ts
  • e2e/react-start/server-functions/tests/rsbuild-cache.spec.ts
  • e2e/react-start/server-functions/tests/server-functions.spec.ts
  • e2e/react-start/start-manifest/tests/start-manifest.spec.ts
  • e2e/react-start/transform-asset-urls/src/routes/__root.tsx
  • e2e/react-start/transform-asset-urls/src/server.ts
  • e2e/react-start/transform-asset-urls/tests/transform-asset-urls.spec.ts
  • e2e/solid-start/csp/tests/csp.spec.ts
  • e2e/solid-start/start-manifest/tests/start-manifest.spec.ts
  • e2e/vue-start/start-manifest/tests/start-manifest.spec.ts
  • packages/react-router/src/Asset.tsx
  • packages/react-router/src/HeadContent.dev.tsx
  • packages/react-router/src/Scripts.tsx
  • packages/react-router/src/headContentUtils.tsx
  • packages/react-router/tests/Scripts.test.tsx
  • packages/react-start-rsc/src/serialization.server.ts
  • packages/router-core/src/index.ts
  • packages/router-core/src/manifest.ts
  • packages/router-core/src/router.ts
  • packages/router-core/src/ssr/createRequestHandler.ts
  • packages/router-core/src/ssr/ssr-server.ts
  • packages/router-core/tests/hydrate.test.ts
  • packages/router-core/tests/manifest.test.ts
  • packages/router-core/tests/ssr-server-manifest.test.ts
  • packages/solid-router/src/HeadContent.dev.tsx
  • packages/solid-router/src/Scripts.tsx
  • packages/solid-router/src/headContentUtils.tsx
  • packages/solid-router/tests/Scripts.test.tsx
  • packages/start-plugin-core/src/import-protection/virtualModules.ts
  • packages/start-plugin-core/src/rsbuild/COMPILER_ARCHITECTURE.md
  • packages/start-plugin-core/src/rsbuild/planning.ts
  • packages/start-plugin-core/src/rsbuild/plugin.ts
  • packages/start-plugin-core/src/rsbuild/schema.ts
  • packages/start-plugin-core/src/rsbuild/start-compiler-host.ts
  • packages/start-plugin-core/src/rsbuild/start-compiler-metadata-loader.ts
  • packages/start-plugin-core/src/rsbuild/start-compiler-metadata.ts
  • packages/start-plugin-core/src/rsbuild/virtual-modules.ts
  • packages/start-plugin-core/src/start-manifest-plugin/manifestBuilder.ts
  • packages/start-plugin-core/src/vite/start-manifest-plugin/plugin.ts
  • packages/start-plugin-core/tests/importProtection/virtualModules.test.ts
  • packages/start-plugin-core/tests/rsbuild-output-directory.test.ts
  • packages/start-plugin-core/tests/start-manifest-plugin/manifestBuilder.test.ts
  • packages/start-plugin-core/vite.config.ts
  • packages/start-server-core/src/createStartHandler.ts
  • packages/start-server-core/src/early-hints.ts
  • packages/start-server-core/src/finalManifest.ts
  • packages/start-server-core/src/request-handler.ts
  • packages/start-server-core/src/router-manifest.ts
  • packages/start-server-core/src/tanstack-start.d.ts
  • packages/start-server-core/src/transformAssetUrls.ts
  • packages/start-server-core/tests/early-hints.test.ts
  • packages/start-server-core/tests/finalManifest.test.ts
  • packages/start-server-core/tests/transformAssets.test.ts
  • packages/start-storage-context/src/async-local-storage.ts
  • packages/vue-router/src/HeadContent.dev.tsx
  • packages/vue-router/src/HeadContent.tsx
  • packages/vue-router/src/Scripts.tsx
  • packages/vue-router/src/headContentUtils.tsx
  • packages/vue-router/tests/Scripts.test.tsx
💤 Files with no reviewable changes (1)
  • packages/start-server-core/src/createStartHandler.ts
✅ Files skipped from review due to trivial changes (8)
  • packages/start-server-core/src/request-handler.ts
  • e2e/react-start/server-functions/rsbuild.config.ts
  • .changeset/gentle-pandas-clap.md
  • docs/start/framework/react/guide/early-hints.md
  • .changeset/slow-badgers-remember.md
  • e2e/react-start/basic/package.json
  • docs/router/guide/document-head-management.md
  • docs/start/framework/react/guide/cdn-asset-urls.md

@schiller-manuel schiller-manuel merged commit 51a97a1 into main May 24, 2026
19 checks passed
@schiller-manuel schiller-manuel deleted the rsbuild-iife-cache branch May 24, 2026 00:02
@github-actions github-actions Bot mentioned this pull request May 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant