Skip to content

fix(router-plugin): point split routes' source maps at the route file - #8860

Merged
Sheraff merged 3 commits into
mainfrom
router-plugin/split-source-maps
Nov 3, 2026
Merged

Sheraff merged 3 commits into
mainfrom
router-plugin/split-source-maps

Conversation

@Sheraff

@Sheraff Sheraff commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Source maps of split routes now point at the route file you wrote.

The fix

webpack, Rspack and Rsbuild: the router plugin's map was dropped. unplugin 3.3.0's webpack/Rspack transform loader returns map == null ? map : res.map || map. The router plugin's loader runs first (enforce: 'pre'), so no earlier loader passes a map in, and our map is thrown away. Later loaders (SWC in Rsbuild) then map against the splitter's output, so a split component maps to the wrong line. Upstream fixed this in unjs/unplugin#626, released in v3.5.0. unplugin now requires ^3.5.0; the lockfile resolves 3.7.0.

All bundlers: split and shared modules named their virtual id. The code splitter passed the module id, index.tsx?tsr-split=component / ?tsr-shared=1, as the map's sourceFileName. It now passes the route file. In Vite dev, devtools used to list index.tsx?tsr-split=component beside index.tsx. With only the unplugin bump, webpack and Rspack get the right line but list the split chunk's source as a separate ./routes/index.js?<hash> entry, so a breakpoint set in the route file doesn't bind there either. Both changes are needed.

After the fix, in the tested setups, each map names one source (the route file) and holds its text as written.

Issues

  • Fixes autoCodeSplitting on Rsbuild breaks source maps #3828. Verified on main with a real Rspack build: the split component's throw maps to line 3 instead of 8. With autoCodeSplitting off, the plugin doesn't transform the file, so no map is lost; that matches the report. Nothing about it is Windows-specific.
  • Part of Bug: Breakpoints fail to attach in VSCode when autoCodeSplitting is enabled #6667 (VS Code breakpoints with autoCodeSplitting on Vite). This fixes the split module's map naming about.tsx?tsr-split=component instead of the route file. VS Code itself wasn't run, so whether breakpoints now bind there, and the reporter's enableContentValidation workaround, aren't verified.

Tests

packages/router-plugin/tests/source-maps.test.ts uses public entry points only (@tanstack/router-plugin/{webpack,rspack,vite}):

  • a real webpack build (the plugin is the only loader) and a real Rspack build (with builtin:swc-loader after it, as in Rsbuild): the throw in the split component, and the throw in a function the loader and component share, each map through the emitted .map to their line in routes/index.*;
  • Vite's dev server: every module derived from the route file maps those throws to their line, and its inline map names /routes/index.tsx, not the ?tsr-* id.

All 3 fail on main, and each change alone leaves at least one failing (checked separately, including each of the 2 sourceFileName sites).

Validation

  • @tanstack/router-plugin: test:unit (21 files, 902 passed), test:types, test:eslint, test:build pass.
  • e2e: the 6 Rsbuild router apps ({react,solid,vue}-router/rspack-basic-file-based and …-virtual-named-export-config-file-based, all with autoCodeSplitting) pass. The webpack examples ({react,solid}/quickstart-webpack-file-based) build.
  • Bundle size (full run, all scenarios): every Vite scenario is byte-identical. The 3 react-start.rsbuild.* scenarios have the same raw bytes and +7 to +14 B gzip. Only Rspack's numeric module and chunk IDs differ (and, in the IIFE build, the minified names derived from them), because those IDs hash each module's loader request, which contains unplugin's versioned install path. No code changes.
  • Lockfile: unplugin 3.3.0 → 3.7.0, plus its new @rolldown/pluginutils dependency and optional @rsbuild/core peer. pnpm's re-resolution also moved one existing @typescript-eslint/eslint-plugin snapshot from ignore 7.0.5 to 7.0.12; both versions were already in the lockfile, and a no-op pnpm install --no-frozen-lockfile on main doesn't produce it.
  • start-plugin-core doesn't depend on unplugin; only router-plugin changes.

With #8817

This is one of the small PRs split out of #8817. Merging #8817's head on top conflicts in:

The new test passes on #8817's head unchanged (webpack, Rspack and Vite). #8817 uses its own webpack/Rspack loader for route modules, so the bump isn't needed for its maps but does no harm.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved source maps for split and shared route code, so errors map to the original route file and line rather than a generated module identifier.
    • Fixed source-map behavior in webpack and Rspack production builds and Vite’s development server, making it easier to trace errors to their source.
    • Verified that errors in both split components and code shared with a route module point to the expected locations.

Sheraff and others added 2 commits November 3, 2026 19:44
Real webpack and Rspack builds, and Vite's dev server: a throw in a split
component, and in code it shares, maps to its line in the route file, and
the map names the route file rather than its ?tsr-split / ?tsr-shared id.
Fails on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Require unplugin ^3.5.0: its webpack/Rspack transform loader dropped a
  plugin's map when no earlier loader passed one (unjs/unplugin#626).
- Name the route file, not the ?tsr-split / ?tsr-shared module id, as the
  source of split and shared modules' maps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 664878d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 7 packages
Name Type
@tanstack/router-plugin Patch
@tanstack/router-vite-plugin Patch
@tanstack/start-plugin-core Patch
@tanstack/react-start-rsc Patch
@tanstack/react-start Patch
@tanstack/solid-start Patch
@tanstack/vue-start Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@nx-cloud

nx-cloud Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit 664878d

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 9m 46s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1m 45s View ↗

☁️ Nx Cloud last updated this comment at 2026-11-03 19:19:54 UTC

@coderabbitai

coderabbitai Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Walkthrough

The router plugin now uses query-free route filenames in source maps for split and shared modules. The dependency range for unplugin is updated, and tests cover webpack, Rspack, and Vite.

Changes: Split source maps

Layer / File(s) Summary
Use route filenames for source maps
packages/router-plugin/src/core/code-splitter/compilers.ts, packages/router-plugin/package.json, .changeset/router-plugin-split-source-maps.md
The split and shared route compilers use query-free route filenames as source-map names. The unplugin range changes to ^3.5.0, and the changeset documents the source-map behavior.
Validate source maps across bundlers
packages/router-plugin/tests/source-maps.test.ts
Tests verify that throws in split components and shared code map to route-file lines in webpack and Rspack production builds and Vite development modules.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related PRs

  • TanStack/router#8804: Adds Bun code splitting and source maps for tsr-split and tsr-shared modules, mapping transformed modules to route files.
Merge Risk: Minimal · up to 66487

No concrete current-head defect or merge-blocking risk was identified.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documenta… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title names the main change: split routes' source maps now point at the route file. It matches the changeset, the sourceFileName change in compilers.ts, and the regression tests. It uses the conve…
Description check Passed The description covers the required template sections. The Changes section explains the root causes, the unplugin bump, and the sourceFileName change, with motivation and issue links (#3828, #6667). T…
Linked Issues check Passed Active direct target: #3828. Requirement: with autoCodeSplitting enabled on Rspack, the generated source map must name the original route file and map to its lines, so a debugger stops at the source s…
Out of Scope Changes check Passed Changes at the reviewed head and their link to #3828:
- packages/router-plugin/package.json: unplugin ^3.3.0 changed to ^3.5.0. Required for the webpack/Rspack map fix. Confirmed at head.
- packages/r…
Full details: Title check

Explanation

The title names the main change: split routes' source maps now point at the route file. It matches the changeset, the sourceFileName change in compilers.ts, and the regression tests. It uses the conventional fix(router-plugin) prefix and is 10 words, which is concise. It does not mention the unplugin bump, but the title does not need to cover every detail.

Full details: Description check

Explanation

The description covers the required template sections. The Changes section explains the root causes, the unplugin bump, and the sourceFileName change, with motivation and issue links (#3828, #6667). The Checklist is not ticked, and the Release Impact section is not ticked either. The changeset file (.changeset/router-plugin-split-source-maps.md) exists per the summary, so the release box should be checked. The description also includes detailed validation and test notes. It is mostly complete. The template checkboxes for the contributing guide and the AI-understanding confirmation are not marked, and the AI-authorship disclosure is present only via the Claude Code footer.

Resolution

Tick the checklist items in the template: the contributing guide item, the local testing item, the understanding-of-code item, and the 'affects published code' changeset item. Add an explicit AI-authorship disclosure line near the top, as the template's AI-authored PR rules request. The current footer mentions Claude Code but does not state the disclosure as a template item.

Full details: Linked Issues check

Explanation

Active direct target: #3828. Requirement: with autoCodeSplitting enabled on Rspack, the generated source map must name the original route file and map to its lines, so a debugger stops at the source statement.

Code evidence at the reviewed head (merge base to head):

  • packages/router-plugin/package.json: unplugin range is ^3.5.0. This addresses the webpack/Rspack transform loader that dropped the router plugin's map in unplugin 3.3.0. The PR description states the lockfile resolves 3.7.0.
  • packages/router-plugin/src/core/code-splitter/compilers.ts: compileCodeSplitVirtualRoute and compileCodeSplitSharedRoute now pass removeSplitSearchParamFromFilename(opts.filename) as sourceFileName. Previously they passed the full ?tsr-* module id. This addresses the source naming that the issue depends on.

The PR reports source-map tests in packages/router-plugin/tests/source-maps.test.ts for a real webpack build, a real Rspack build with builtin:swc-loader after the router loader (matching Rsbuild), and Vite dev. The PR reports that these tests fail on main and that each change alone leaves a failure. The test file was not read in this assessment, so the test results rely on the PR report.

Limits: the helper removeSplitSearchParamFromFilename was not inspected in this assessment. The VS Code breakpoint binding from the issue was not run, as the PR states. The fix is verified at the source-map level, which is the mechanism the issue depends on. The PR reports the issue's Windows environment is not a limit on the fix. No unmet coding requirement was found.

Full details: Out of Scope Changes check

Explanation

Changes at the reviewed head and their link to #3828:

  • packages/router-plugin/package.json: unplugin ^3.3.0 changed to ^3.5.0. Required for the webpack/Rspack map fix. Confirmed at head.
  • packages/router-plugin/src/core/code-splitter/compilers.ts: two sourceFileName sites changed to the route filename without the ?tsr-* query, with comments. Confirmed in the whole-PR diff. Directly implements the naming fix.
  • packages/router-plugin/tests/source-maps.test.ts: regression tests for webpack, Rspack, and Vite source maps. Supports the objective. Not read in this assessment.
  • .changeset/router-plugin-split-source-maps.md: patch changeset for the two changes. Release documentation for this fix.
  • pnpm-lock.yaml: the unplugin move from 3.3.0 to 3.7.0 and its new transitive dependency follow from the package.json bump. The PR also reports one transitive resolution change, @typescript-eslint/eslint-plugin's ignore dependency from 7.0.5 to 7.0.12, attributed to pnpm re-resolution. The lockfile diff could not be read in this assessment, so this is not independently verified. The change does not show unrelated feature work, and the PR reports no source-level effect.

No change was found that lacks a connection to the #3828 source-map objective. The PR references #6667, which is not a linked issue in this review, so it was not assessed.

Full details: Docstring Coverage

Explanation

Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff with cyclomatic complexity of at least 2. Documentation-only edits and deletions within surviving functions are included; unchanged diff context and fully deleted functions are excluded. Analyzed 7 functions across 2 files.

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches
Generate docstrings
  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.
Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between 97640c1 and 664878d.

Files selected for processing: 1
  • packages/router-plugin/tests/source-maps.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 7 remain after this review.

@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

4 package(s) bumped directly, 6 bumped as dependents.

🟨 Minor bumps

Package Version Reason
@tanstack/router-cli 1.167.46 → 1.168.0 Changeset
@tanstack/router-generator 1.167.46 → 1.168.0 Changeset
@tanstack/router-plugin 1.168.48 → 1.169.0 Changeset
@tanstack/router-utils 1.162.4 → 1.163.0 Changeset

🟩 Patch bumps

Package Version Reason
@tanstack/react-start 1.169.4 → 1.169.5 Dependent
@tanstack/react-start-rsc 0.1.65 → 0.1.66 Dependent
@tanstack/router-vite-plugin 1.167.48 → 1.167.49 Dependent
@tanstack/solid-start 1.169.4 → 1.169.5 Dependent
@tanstack/start-plugin-core 1.171.55 → 1.171.56 Dependent
@tanstack/vue-start 1.169.4 → 1.169.5 Dependent

@pkg-pr-new

pkg-pr-new Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

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

@tanstack/eslint-plugin-router

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

@tanstack/eslint-plugin-start

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

@tanstack/history

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

@tanstack/react-router

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

@tanstack/react-router-devtools

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

@tanstack/react-router-ssr-query

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

@tanstack/react-start

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

@tanstack/react-start-client

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

@tanstack/react-start-rsc

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

@tanstack/react-start-server

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

@tanstack/router-cli

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

@tanstack/router-core

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

@tanstack/router-devtools

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

@tanstack/router-devtools-core

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

@tanstack/router-generator

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

@tanstack/router-plugin

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

@tanstack/router-ssr-query-core

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

@tanstack/router-utils

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

@tanstack/router-vite-plugin

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

@tanstack/solid-router

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

@tanstack/solid-router-devtools

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

@tanstack/solid-router-ssr-query

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

@tanstack/solid-start

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

@tanstack/solid-start-client

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

@tanstack/solid-start-server

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

@tanstack/start-client-core

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

@tanstack/start-fn-stubs

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

@tanstack/start-plugin-core

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

@tanstack/start-server-core

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

@tanstack/start-static-server-functions

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

@tanstack/start-storage-context

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

@tanstack/valibot-adapter

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

@tanstack/virtual-file-routes

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

@tanstack/vue-router

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

@tanstack/vue-router-devtools

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

@tanstack/vue-router-ssr-query

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

@tanstack/vue-start

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

@tanstack/vue-start-client

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

@tanstack/vue-start-server

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

@tanstack/zod-adapter

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

commit: 664878d

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Bundle Size Benchmarks

  • Commit: 9d144cdc20e3
  • Measured at: 2026-11-03T19:09:18.765Z
  • Baseline source: history:446ce1b30650
  • Dashboard: bundle-size history

The following scenarios have bundle-size changes compared with the baseline:

Scenario Current (gzip) Initial (gzip) Raw Brotli Trend
react-start.rsbuild.minimal 100.4 KiB
+9 B
100.2 KiB
+10 B
313.3 KiB
0 B
86.6 KiB
+21 B
▁▁▁▁▁▁▁▁▁▁▁█
react-start.rsbuild.minimal-iife 100.8 KiB
+14 B
100.6 KiB
+15 B
314.2 KiB
0 B
87.0 KiB
+3 B
▁▁▁▁▁▁▁▁▁▁▁█
react-start.rsbuild.full 103.5 KiB
+7 B
103.4 KiB
+8 B
322.9 KiB
0 B
89.3 KiB
-117 B
▁▁▁▁▁▁▁▁▁▁▁█

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.

@coderabbitai coderabbitai Bot left a comment •

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.

  • Fix comments: Fix CodeRabbit comments on this PR

…he app

runCompiler settled before compiler.close finished, so the test could remove
the app while webpack or Rspack was still shutting down, and a close error
went unreported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codspeed

codspeed Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Merging this PR will degrade performance by 4.42%

2 warnings, 1 note

⚠️ 47 benchmarks are dominated by system calls

System calls cannot be simulated consistently and are left out of the measure. The reported value understates the real cost. Switch to the Walltime instrument to measure them.

Learn more about system calls

See affected benchmarks

⚠️ 2 benchmarks with a significant change ran on different hardware

Their base and head results were measured on machines with a different CPU, core count or memory, so the changes might not come from the code. Run base and head on the same runner type.

Learn more about Macro Runners

See affected benchmarks

ℹ️ 20 benchmarks have no flamegraph

The flamegraph wasn't generated: most of the time was spent in runtime internals, which would leave nothing useful to analyze.

See affected benchmarks

❌ 2 (👁 2) regressed benchmarks
✅ 192 untouched benchmarks
⏩ 3 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
👁 Memory mem client navigation-churn (solid) 107.2 KB 113.4 KB -5.51%
👁 Memory mem client mount-unmount (react) 93.3 KB 96.5 KB -3.31%

Comparing router-plugin/split-source-maps (664878d) with main (ad5b3b7)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@Sheraff
Sheraff merged commit b5e2ac9 into main Nov 3, 2026
30 checks passed
@Sheraff
Sheraff deleted the router-plugin/split-source-maps branch November 3, 2026 20:37
@github-actions github-actions Bot mentioned this pull request Nov 3, 2026
Sheraff added a commit that referenced this pull request Nov 3, 2026
#8817's side for the split-out PRs:
- #8861 (Start router options): the PR's generator-config, Vite and
  Rsbuild wiring; main's start-router-wiring test and start-app helper
  dropped (start-build-options, start-dev-route-tree-footer and
  start-compose-project-path test the same four behaviours). Main's
  changeset kept; the duplicated bullets leave start-compiler-yuku.md.
- #8860 (split route source maps): the PR's own loader; compilers.ts stays
  deleted; main's source-maps test dropped (build-source-maps and
  dev-source-maps test webpack, Rspack and Vite dev). unplugin ^3.5.0 kept
  from main (no downgrade). Main's changeset kept; the PR's source map
  clause leaves router-plugin-yuku.md.
- #8653 (visible() hooks): merged to the PR's file, test and changeset
  unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sheraff added a commit that referenced this pull request Nov 8, 2026
… map-name checks

The slice has no test for two behaviours main guards: a webpack or Rspack
production build whose configuration has no `mode` ships no splitter
warning (#8269), and on webpack and Rspack the maps of a split component
and of shared code name the route file, not the `?tsr-split` id (#8860).
Keep those cases of main's tests, cut to what the slice does not cover:
the `mode: 'production'` rows, the line mapping and Vite's dev server are
asserted in the integration tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

autoCodeSplitting on Rsbuild breaks source maps

1 participant