Skip to content

refactor(router-plugin): Build-time route matching #4714

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 61 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
63200f6
feat(router-plugin) Build-time route matching
Sheraff Jul 19, 2025
a72186e
ci: apply automated fixes
autofix-ci[bot] Jul 19, 2025
02ccf64
fix depth iteration issue
Sheraff Jul 19, 2025
90ae082
better leaf conditions
Sheraff Jul 19, 2025
a66b79f
ci: apply automated fixes
autofix-ci[bot] Jul 19, 2025
5f0b327
2 more cases
Sheraff Jul 19, 2025
1eb8ce5
test setup now shows actual results
Sheraff Jul 19, 2025
0d19c8b
can condense multiple depths in a single if
Sheraff Jul 19, 2025
3251741
prettier
Sheraff Jul 19, 2025
7890c3f
rename stuff
Sheraff Jul 19, 2025
af589e5
dynamic params
Sheraff Jul 19, 2025
6e9c361
prettier
Sheraff Jul 19, 2025
6799fcb
support for wildcard
Sheraff Jul 19, 2025
c74a36a
prettier
Sheraff Jul 19, 2025
e40ccbd
root route
Sheraff Jul 19, 2025
adba433
ci: apply automated fixes
autofix-ci[bot] Jul 19, 2025
e5501fb
fix route rebuilding
Sheraff Jul 19, 2025
a5eb6c6
recursion preserves order
Sheraff Jul 20, 2025
7d2b299
alternate implementation
Sheraff Jul 20, 2025
31700cb
better condition grouping
Sheraff Jul 20, 2025
80e3343
skip length check if constraint applies to all children
Sheraff Jul 20, 2025
0adb157
reduce array access
Sheraff Jul 20, 2025
d3712b4
add rank conditions
Sheraff Jul 20, 2025
c058759
fix some rank and length checks
Sheraff Jul 20, 2025
815c3d0
use rank for early return opportunities
Sheraff Jul 20, 2025
225ebaa
global 'caseSensitive'
Sheraff Jul 20, 2025
c076904
Merge branch 'main' into router-core-build-time-route-matcher
Sheraff Jul 20, 2025
7808d97
fix index routes
Sheraff Jul 20, 2025
b22924e
handle empty case
Sheraff Jul 20, 2025
a25fc83
rewrite 1st implem so I actually understand it
Sheraff Jul 21, 2025
5e394dc
Merge branch 'main' into router-core-build-time-route-matcher
Sheraff Jul 21, 2025
9797c77
better wildstar route test
Sheraff Jul 21, 2025
171353f
no need to limit length grouping
Sheraff Jul 21, 2025
d836b81
param retrieval code gen
Sheraff Jul 21, 2025
e07330f
add direct param extraction
Sheraff Jul 22, 2025
a716491
Merge branch 'main' into router-core-build-time-route-matcher
Sheraff Jul 23, 2025
844df5e
support runtime fuzzy options
Sheraff Jul 23, 2025
b4f7d74
fix level 0 grouping
Sheraff Jul 23, 2025
7391bad
cleanup
Sheraff Jul 23, 2025
173ad09
support case insensitive maatching
Sheraff Jul 24, 2025
50635c5
code comments
Sheraff Jul 24, 2025
99678b5
productize
Sheraff Jul 24, 2025
f282ab2
cleanup
Sheraff Jul 24, 2025
ec6e4dc
missing export
Sheraff Jul 24, 2025
ead4795
rename file
Sheraff Jul 24, 2025
4e64eb1
rename
Sheraff Jul 24, 2025
f9e0740
better comments and naming
Sheraff Jul 24, 2025
c6617f7
case sensitivity applies to all parts, not just static pathnames
Sheraff Jul 24, 2025
9460511
reduce accessor complexity
Sheraff Jul 24, 2025
c89f87d
add a tree pruning step
Sheraff Jul 24, 2025
0f5402a
typo
Sheraff Jul 24, 2025
7ca2c40
add bench
Sheraff Jul 25, 2025
d4bc4a5
add compiled-matcher E2E project
Sheraff Jul 25, 2025
0356bc7
prettier
Sheraff Jul 27, 2025
7ae9b8a
add unit test for optional param followed by regular param
Sheraff Jul 30, 2025
c6d5ca2
Merge branch 'main' into router-core-build-time-route-matcher
Sheraff Jul 30, 2025
b8f9dd1
improve tree pruning
Sheraff Jul 30, 2025
c86a84f
simplify tree pruning
Sheraff Jul 31, 2025
2858cfa
add optional+regular param test case to benchmark
Sheraff Jul 31, 2025
99a98f9
add code comment for possible pruning improvement
Sheraff Jul 31, 2025
a2f0f17
add another benchmark for single lookup
Sheraff Jul 31, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions e2e/react-router/compiled-matcher/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
node_modules
.DS_Store
dist
dist-hash
dist-ssr
*.local

/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
11 changes: 11 additions & 0 deletions e2e/react-router/compiled-matcher/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
36 changes: 36 additions & 0 deletions e2e/react-router/compiled-matcher/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "tanstack-router-e2e-react-compiled-matcher",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port 3000",
"dev:e2e": "vite",
"build": "vite build && tsc --noEmit",
"build:fresh": "rm -rf .tanstack && rm ./src/routeTree.gen.ts && pnpm i && vite build",
"serve": "vite preview",
"start": "vite",
"test:e2e": "rm -rf port*.txt; playwright test --project=chromium"
},
"dependencies": {
"@tanstack/react-router": "workspace:^",
"@tanstack/react-router-devtools": "workspace:^",
"@tanstack/router-plugin": "workspace:^",
"@tanstack/zod-adapter": "workspace:^",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"redaxios": "^0.5.1",
"postcss": "^8.5.1",
"autoprefixer": "^10.4.20",
"tailwindcss": "^3.4.17",
"zod": "^3.24.2"
},
"devDependencies": {
"@playwright/test": "^1.50.1",
"@tanstack/router-e2e-utils": "workspace:^",
"@types/react": "^19.0.8",
"@types/react-dom": "^19.0.3",
"@vitejs/plugin-react": "^4.3.4",
"combinate": "^1.1.11",
"vite": "^6.3.5"
}
}
41 changes: 41 additions & 0 deletions e2e/react-router/compiled-matcher/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { defineConfig, devices } from '@playwright/test'
import {
getDummyServerPort,
getTestServerPort,
} from '@tanstack/router-e2e-utils'
import packageJson from './package.json' with { type: 'json' }

const PORT = await getTestServerPort(packageJson.name)
const EXTERNAL_PORT = await getDummyServerPort(packageJson.name)
const baseURL = `http://localhost:${PORT}`
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
workers: 1,

reporter: [['line']],

globalSetup: './tests/setup/global.setup.ts',
globalTeardown: './tests/setup/global.teardown.ts',

use: {
/* Base URL to use in actions like `await page.goto('/')`. */
baseURL,
},

webServer: {
command: `VITE_NODE_ENV="test" VITE_SERVER_PORT=${PORT} VITE_EXTERNAL_PORT=${EXTERNAL_PORT} pnpm build && VITE_SERVER_PORT=${PORT} pnpm serve --port ${PORT}`,
url: baseURL,
reuseExistingServer: !process.env.CI,
stdout: 'pipe',
},

projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
})
6 changes: 6 additions & 0 deletions e2e/react-router/compiled-matcher/postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
27 changes: 27 additions & 0 deletions e2e/react-router/compiled-matcher/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import { RouterProvider, createRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
import './styles.css'

// Set up a Router instance
const router = createRouter({
routeTree,
defaultPreload: 'intent',
defaultStaleTime: 5000,
scrollRestoration: true,
})

// Register things for typesafety
declare module '@tanstack/react-router' {
interface Register {
router: typeof router
}
}

const rootElement = document.getElementById('app')!

if (!rootElement.innerHTML) {
const root = ReactDOM.createRoot(rootElement)
root.render(<RouterProvider router={router} />)
}
Loading
Loading