Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"Bash(mkdir:*)",
"Bash(TEST_ONLY=dev yarn vitest --config ./vitest.config.rolldown.ts --run --reporter=dot --color=false api-rolldown.test.ts)",
"Bash(bun llink:*)",
"Bash(bun:*)"
"Bash(bun:*)",
"WebSearch"
],
"deny": []
}
Expand Down
149 changes: 149 additions & 0 deletions apps/onestack.dev/REWRITE_TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
# Testing URL Rewriting Features

This document describes how to test the URL rewriting and enhanced middleware features in One.

## Quick Start

1. **Start the dev server:**
```bash
cd apps/onestack.dev
yarn dev
```

2. **Visit the test page:**
Open http://localhost:6173/test-rewrites

3. **Test subdomain routing:**
Try these URLs in your browser (`.localhost` domains work on modern systems):
- http://app1.localhost:6173
- http://app2.localhost:6173
- http://docs.localhost:6173

## Features to Test

### 1. URL Rewriting

The following rewrite rules are configured in `vite.config.ts`:

```typescript
{
web: {
rewrites: {
'*.localhost': '/subdomain/*',
'docs.localhost': '/docs',
'/old-docs/*': '/docs/*'
}
}
}
```

### 2. Middleware Capabilities

The middleware in `app/_middleware.tsx` demonstrates:

- **Request rewriting**: Modifying the URL before it reaches route handlers
- **Response interception**: Returning responses directly from middleware
- **Subdomain handling**: Detecting and routing based on subdomains

### 3. Link Component Integration

Links with internal paths like `/subdomain/app1` should automatically render with external URLs like `http://app1.localhost` when rewrites are configured.

## Running Integration Tests

```bash
# Run all tests
yarn test

# Run rewrite tests specifically
yarn vitest run rewrite-integration.test.tsx

# Run with debugging (opens browser)
DEBUG=1 yarn vitest run rewrite-integration.test.tsx
```

## Test Files

- **Test page**: `/app/test-rewrites.tsx` - Interactive test page
- **Middleware**: `/app/_middleware.tsx` - Request/response handling
- **Subdomain pages**: `/app/subdomain/[name]/index.tsx` - Dynamic subdomain routing
- **Integration tests**: `/tests/rewrite-integration.test.tsx` - Automated tests

## Manual Testing Checklist

### Basic Functionality

- [ ] Visit http://localhost:6173/test-rewrites
- [ ] Check that current URL is displayed correctly
- [ ] Verify links show their rendered hrefs

### Subdomain Routing

- [ ] Visit http://app1.localhost:6173
- [ ] Verify it shows "Subdomain: app1"
- [ ] Navigate to http://app1.localhost:6173/about
- [ ] Verify navigation between subdomain pages works

### Link Transformation

- [ ] Hover over subdomain links on test page
- [ ] Verify browser shows subdomain URLs in status bar
- [ ] Click subdomain links
- [ ] Verify navigation works correctly

### Middleware Response

- [ ] Click "Test Middleware Response" button
- [ ] Verify JSON response appears
- [ ] Check response contains expected fields

### Path Rewrites

- [ ] Visit http://localhost:6173/old-docs/intro
- [ ] Verify it redirects or rewrites to /docs/intro

## Troubleshooting

### Subdomain Not Resolving

If `*.localhost` doesn't work on your system:

1. **Check your OS**: Modern macOS and Linux support `.localhost` by default
2. **Try 127.0.0.1**: Use `http://127.0.0.1:6173` instead
3. **Add to hosts file** (if needed):
```bash
sudo echo "127.0.0.1 app1.localhost" >> /etc/hosts
sudo echo "127.0.0.1 app2.localhost" >> /etc/hosts
```

### Links Not Transforming

1. Check that rewrites are configured in `vite.config.ts`
2. Verify environment variable is set: `process.env.ONE_URL_REWRITES`
3. Check browser console for errors
4. Ensure you've rebuilt after config changes

### Middleware Not Running

1. Verify `_middleware.tsx` is in the correct location
2. Check that it exports a default function
3. Look for errors in server console
4. Ensure middleware is created with `createMiddleware()`

## Implementation Details

### How It Works

1. **Configuration**: Rewrite rules are defined in `vite.config.ts`
2. **Environment**: Rules are passed to client via `process.env.ONE_URL_REWRITES`
3. **Middleware**: Requests are modified before routing
4. **Links**: The Link component applies reverse rewrites for display
5. **Navigation**: React Navigation handles routing with rewritten paths

### Key Files

- `/packages/one/src/utils/rewrite.ts` - Rewrite utilities
- `/packages/one/src/createMiddleware.ts` - Middleware types
- `/packages/one/src/createHandleRequest.ts` - Request handling
- `/packages/one/src/link/href.ts` - Link URL resolution
- `/packages/one/src/router/getLinkingConfig.ts` - React Navigation integration
51 changes: 51 additions & 0 deletions apps/onestack.dev/app/_middleware.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { createMiddleware } from 'one'

/**
* Middleware for handling URL rewrites in onestack.dev
* This handles subdomain routing for testing purposes
*/
export default createMiddleware(async ({ request, next }) => {
const url = new URL(request.url)
const host = request.headers.get('host') || ''

// Handle subdomain rewrites for .localhost domains
if (host.includes('.localhost')) {
const parts = host.split('.')
const subdomain = parts[0]

// Special case for docs.localhost
if (subdomain === 'docs') {
const newUrl = new URL(request.url)
newUrl.pathname = `/docs${url.pathname === '/' ? '' : url.pathname}`
return next(new Request(newUrl, request))
}

// General subdomain handling
if (subdomain && subdomain !== 'www') {
const newUrl = new URL(request.url)
newUrl.pathname = `/subdomain/${subdomain}${url.pathname}`
return next(new Request(newUrl, request))
}
}

// Handle path rewrites
if (url.pathname.startsWith('/old-docs/')) {
const newUrl = new URL(request.url)
newUrl.pathname = url.pathname.replace('/old-docs/', '/docs/')
return next(new Request(newUrl, request))
}

// Test response interception
if (url.pathname === '/test-middleware-response') {
return new Response(JSON.stringify({
message: 'Direct response from middleware',
timestamp: Date.now(),
host,
}), {
status: 200,
headers: { 'Content-Type': 'application/json' }
})
}

return next()
})
7 changes: 7 additions & 0 deletions apps/onestack.dev/app/middleware-health+api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function GET() {
return Response.json({
status: 'healthy',
timestamp: Date.now(),
middleware: 'rewrite'
})
}
26 changes: 26 additions & 0 deletions apps/onestack.dev/app/subdomain/[name]/about.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Link, useParams } from 'one'

export default function SubdomainAboutPage() {
const params = useParams<{ name: string }>()
const name = params?.name || 'unknown'

return (
<div style={{ padding: 20 }}>
<h1>About - {name}</h1>
<p>This is the about page for subdomain: {name}</p>

<div style={{ marginTop: 20 }}>
<Link href={`/subdomain/${name}`}>
← Back to {name} home
</Link>
</div>

<div style={{ marginTop: 20 }}>
<h2>Navigation Test</h2>
<p>The link above should render with the subdomain URL.</p>
<p>Current path: /subdomain/{name}/about</p>
<p>Should display as: {name}.localhost/about</p>
</div>
</div>
)
}
48 changes: 48 additions & 0 deletions apps/onestack.dev/app/subdomain/[name]/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { Link, useParams, useLoader } from 'one'

export default function SubdomainPage() {
const params = useParams<{ name: string }>()
const data = useLoader(loader)
const name = params?.name || 'unknown'

return (
<div style={{ padding: 20 }}>
<h1>Subdomain: {name}</h1>
<p>This page is served from the rewritten path /subdomain/{name}</p>

<div style={{ marginTop: 20 }}>
<h2>Test Links (should show external URLs):</h2>
<ul>
<li>
<Link href={`/subdomain/${name}/about`}>
About Page (should render as {name}.localhost/about)
</Link>
</li>
<li>
<Link href="/subdomain/other/page">
Other Subdomain (should render as other.localhost/page)
</Link>
</li>
<li>
<Link href="/docs/intro">
Docs (regular link, no rewrite)
</Link>
</li>
</ul>
</div>

<div style={{ marginTop: 20 }}>
<h3>Debug Info:</h3>
<pre>{JSON.stringify({ params, loaderData: data }, null, 2)}</pre>
</div>
</div>
)
}

export async function loader({ params }: { params: { name: string } }) {
return {
subdomain: params.name,
timestamp: Date.now(),
message: `Loaded subdomain: ${params.name}`
}
}
8 changes: 8 additions & 0 deletions apps/onestack.dev/app/subdomain/myapp/about+spa.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function MyAppAboutPage() {
return (
<div>
<h1>About myapp</h1>
<p>This is the about page for myapp subdomain</p>
</div>
)
}
9 changes: 9 additions & 0 deletions apps/onestack.dev/app/subdomain/myapp/index+spa.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function MyAppSubdomainPage() {
return (
<div>
<h1>myapp Subdomain Home</h1>
<p>This is the home page for myapp subdomain</p>
<a href="/subdomain/myapp/about">About</a>
</div>
)
}
8 changes: 8 additions & 0 deletions apps/onestack.dev/app/subdomain/testapp/about+spa.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default function TestAppAboutPage() {
return (
<div>
<h1>About testapp</h1>
<p>This is the about page for testapp subdomain</p>
</div>
)
}
9 changes: 9 additions & 0 deletions apps/onestack.dev/app/subdomain/testapp/index+spa.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export default function TestAppSubdomainPage() {
return (
<div>
<h1>testapp Subdomain Home</h1>
<p>This is the home page for testapp subdomain</p>
<a href="/subdomain/testapp/about">About</a>
</div>
)
}
7 changes: 7 additions & 0 deletions apps/onestack.dev/app/test-middleware-response+api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export function GET() {
return Response.json({
message: 'Direct response from middleware',
timestamp: Date.now(),
source: 'test-middleware-response'
})
}
Loading
Loading