Skip to content

Commit 0761ff6

Browse files
fix: alias and namespace import support for import protection
fixes #6770
1 parent de66f0e commit 0761ff6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+3857
-541
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
node_modules
2+
package-lock.json
3+
yarn.lock
4+
.DS_Store
5+
.cache
6+
.env
7+
.vercel
8+
.output
9+
/build/
10+
/api/
11+
/server/build
12+
/public/build
13+
.env.sentry-build-plugin
14+
/test-results/
15+
/playwright-report/
16+
/dist/
17+
*.log
18+
violations.*.json
19+
error-build-result.json
20+
error-dev-result.json
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "tanstack-react-start-e2e-import-protection-custom-config",
3+
"private": true,
4+
"sideEffects": false,
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite dev --port 3000",
8+
"dev:e2e": "vite dev",
9+
"build": "vite build && tsc --noEmit",
10+
"start": "pnpx srvx --prod -s ../client dist/server/server.js",
11+
"test:e2e:mockMode": "rm -rf port*.txt; playwright test --project=chromium",
12+
"test:e2e:errorMode": "rm -rf port*.txt; BEHAVIOR=error playwright test --project=chromium",
13+
"test:e2e": "pnpm run test:e2e:mockMode && pnpm run test:e2e:errorMode"
14+
},
15+
"dependencies": {
16+
"@tanstack/react-router": "workspace:^",
17+
"@tanstack/react-start": "workspace:^",
18+
"react": "^19.0.0",
19+
"react-dom": "^19.0.0",
20+
"vite": "^7.3.1"
21+
},
22+
"devDependencies": {
23+
"@playwright/test": "^1.50.1",
24+
"@tanstack/router-e2e-utils": "workspace:^",
25+
"@types/node": "^22.10.2",
26+
"@types/react": "^19.0.8",
27+
"@types/react-dom": "^19.0.3",
28+
"srvx": "^0.11.7",
29+
"typescript": "^5.7.2"
30+
}
31+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { defineConfig, devices } from '@playwright/test'
2+
import { getTestServerPort } from '@tanstack/router-e2e-utils'
3+
import { isErrorMode } from './tests/utils/isErrorMode'
4+
import packageJson from './package.json' with { type: 'json' }
5+
6+
const PORT = await getTestServerPort(packageJson.name)
7+
const baseURL = `http://localhost:${PORT}`
8+
9+
console.log('running in error mode:', isErrorMode.toString())
10+
11+
export default defineConfig({
12+
testDir: './tests',
13+
workers: 1,
14+
15+
globalSetup: isErrorMode
16+
? './tests/error-mode.setup.ts'
17+
: './tests/violations.setup.ts',
18+
19+
reporter: [['line']],
20+
21+
use: {
22+
baseURL,
23+
},
24+
25+
...(isErrorMode
26+
? {}
27+
: {
28+
webServer: {
29+
command: `rm -f webserver-build.log violations.build.json violations.dev.json && VITE_SERVER_PORT=${PORT} pnpm build > webserver-build.log 2>&1 && PORT=${PORT} VITE_SERVER_PORT=${PORT} pnpm start`,
30+
url: baseURL,
31+
reuseExistingServer: !process.env.CI,
32+
stdout: 'pipe',
33+
cwd: import.meta.dirname,
34+
},
35+
}),
36+
37+
projects: [
38+
{
39+
name: 'chromium',
40+
use: { ...devices['Desktop Chrome'] },
41+
testMatch: isErrorMode ? 'error-mode.spec.ts' : 'custom-config.spec.ts',
42+
},
43+
],
44+
})
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/* eslint-disable */
2+
3+
// @ts-nocheck
4+
5+
// noinspection JSUnusedGlobalSymbols
6+
7+
// This file was automatically generated by TanStack Router.
8+
// You should NOT make any changes in this file as it will be overwritten.
9+
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
10+
11+
import { Route as rootRouteImport } from './routes/__root'
12+
import { Route as FrontendLeakRouteImport } from './routes/frontend-leak'
13+
import { Route as BackendLeakRouteImport } from './routes/backend-leak'
14+
import { Route as IndexRouteImport } from './routes/index'
15+
16+
const FrontendLeakRoute = FrontendLeakRouteImport.update({
17+
id: '/frontend-leak',
18+
path: '/frontend-leak',
19+
getParentRoute: () => rootRouteImport,
20+
} as any)
21+
const BackendLeakRoute = BackendLeakRouteImport.update({
22+
id: '/backend-leak',
23+
path: '/backend-leak',
24+
getParentRoute: () => rootRouteImport,
25+
} as any)
26+
const IndexRoute = IndexRouteImport.update({
27+
id: '/',
28+
path: '/',
29+
getParentRoute: () => rootRouteImport,
30+
} as any)
31+
32+
export interface FileRoutesByFullPath {
33+
'/': typeof IndexRoute
34+
'/backend-leak': typeof BackendLeakRoute
35+
'/frontend-leak': typeof FrontendLeakRoute
36+
}
37+
export interface FileRoutesByTo {
38+
'/': typeof IndexRoute
39+
'/backend-leak': typeof BackendLeakRoute
40+
'/frontend-leak': typeof FrontendLeakRoute
41+
}
42+
export interface FileRoutesById {
43+
__root__: typeof rootRouteImport
44+
'/': typeof IndexRoute
45+
'/backend-leak': typeof BackendLeakRoute
46+
'/frontend-leak': typeof FrontendLeakRoute
47+
}
48+
export interface FileRouteTypes {
49+
fileRoutesByFullPath: FileRoutesByFullPath
50+
fullPaths: '/' | '/backend-leak' | '/frontend-leak'
51+
fileRoutesByTo: FileRoutesByTo
52+
to: '/' | '/backend-leak' | '/frontend-leak'
53+
id: '__root__' | '/' | '/backend-leak' | '/frontend-leak'
54+
fileRoutesById: FileRoutesById
55+
}
56+
export interface RootRouteChildren {
57+
IndexRoute: typeof IndexRoute
58+
BackendLeakRoute: typeof BackendLeakRoute
59+
FrontendLeakRoute: typeof FrontendLeakRoute
60+
}
61+
62+
declare module '@tanstack/react-router' {
63+
interface FileRoutesByPath {
64+
'/frontend-leak': {
65+
id: '/frontend-leak'
66+
path: '/frontend-leak'
67+
fullPath: '/frontend-leak'
68+
preLoaderRoute: typeof FrontendLeakRouteImport
69+
parentRoute: typeof rootRouteImport
70+
}
71+
'/backend-leak': {
72+
id: '/backend-leak'
73+
path: '/backend-leak'
74+
fullPath: '/backend-leak'
75+
preLoaderRoute: typeof BackendLeakRouteImport
76+
parentRoute: typeof rootRouteImport
77+
}
78+
'/': {
79+
id: '/'
80+
path: '/'
81+
fullPath: '/'
82+
preLoaderRoute: typeof IndexRouteImport
83+
parentRoute: typeof rootRouteImport
84+
}
85+
}
86+
}
87+
88+
const rootRouteChildren: RootRouteChildren = {
89+
IndexRoute: IndexRoute,
90+
BackendLeakRoute: BackendLeakRoute,
91+
FrontendLeakRoute: FrontendLeakRoute,
92+
}
93+
export const routeTree = rootRouteImport
94+
._addFileChildren(rootRouteChildren)
95+
._addFileTypes<FileRouteTypes>()
96+
97+
import type { getRouter } from './router.tsx'
98+
import type { createStart } from '@tanstack/react-start'
99+
declare module '@tanstack/react-start' {
100+
interface Register {
101+
ssr: true
102+
router: Awaited<ReturnType<typeof getRouter>>
103+
}
104+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { createRouter } from '@tanstack/react-router'
2+
import { routeTree } from './routeTree.gen'
3+
4+
export function getRouter() {
5+
return createRouter({
6+
routeTree,
7+
scrollRestoration: true,
8+
})
9+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import {
2+
createRootRoute,
3+
HeadContent,
4+
Link,
5+
linkOptions,
6+
Outlet,
7+
Scripts,
8+
} from '@tanstack/react-router'
9+
10+
export const Route = createRootRoute({
11+
head: () => ({
12+
meta: [
13+
{ charSet: 'utf-8' },
14+
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
15+
{ title: 'Import Protection Custom Config E2E' },
16+
],
17+
}),
18+
component: RootComponent,
19+
})
20+
21+
const navLinks = linkOptions([
22+
{ to: '/', label: 'Home' },
23+
{ to: '/backend-leak', label: 'Backend Leak' },
24+
{ to: '/frontend-leak', label: 'Frontend Leak' },
25+
])
26+
27+
function RootComponent() {
28+
return (
29+
<html>
30+
<head>
31+
<HeadContent />
32+
</head>
33+
<body>
34+
<nav>
35+
{navLinks.map((link, index) => (
36+
<span key={link.to}>
37+
{index > 0 ? ' | ' : null}
38+
<Link to={link.to}>{link.label}</Link>
39+
</span>
40+
))}
41+
</nav>
42+
<Outlet />
43+
<Scripts />
44+
</body>
45+
</html>
46+
)
47+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { createFileRoute } from '@tanstack/react-router'
2+
// This import triggers a file-based violation in the CLIENT env:
3+
// backend-leak.tsx -> lib/credentials.backend.ts
4+
// The custom deny pattern `**/*.backend.*` should catch this.
5+
import { getBackendSecret } from '../lib/credentials.backend'
6+
7+
export const Route = createFileRoute('/backend-leak')({
8+
component: BackendLeakRoute,
9+
})
10+
11+
function BackendLeakRoute() {
12+
return (
13+
<div>
14+
<h1 data-testid="backend-leak-heading">Backend Leak</h1>
15+
<p data-testid="backend-leak-result">{String(getBackendSecret())}</p>
16+
</div>
17+
)
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { createFileRoute } from '@tanstack/react-router'
2+
// This import triggers a file-based violation in the SERVER (SSR) env:
3+
// frontend-leak.tsx -> lib/browser-api.frontend.ts
4+
// The custom deny pattern `**/*.frontend.*` should catch this.
5+
import { getBrowserInfo } from '../lib/browser-api.frontend'
6+
7+
export const Route = createFileRoute('/frontend-leak')({
8+
component: FrontendLeakRoute,
9+
})
10+
11+
function FrontendLeakRoute() {
12+
return (
13+
<div>
14+
<h1 data-testid="frontend-leak-heading">Frontend Leak</h1>
15+
<p data-testid="frontend-leak-result">{String(getBrowserInfo())}</p>
16+
</div>
17+
)
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { createFileRoute } from '@tanstack/react-router'
2+
3+
export const Route = createFileRoute('/')({
4+
component: Home,
5+
})
6+
7+
function Home() {
8+
return (
9+
<div>
10+
<h1 data-testid="heading">Import Protection Custom Config E2E</h1>
11+
<p data-testid="status">
12+
App loaded successfully with custom file patterns
13+
</p>
14+
</div>
15+
)
16+
}

0 commit comments

Comments
 (0)