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
12 changes: 12 additions & 0 deletions .changeset/plain-tools-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@tanstack/octane-router': patch
'@tanstack/octane-start': patch
'@tanstack/octane-start-client': patch
'@tanstack/octane-start-server': patch
'@tanstack/router-cli': patch
'@tanstack/router-generator': patch
'@tanstack/router-plugin': patch
'@tanstack/start-plugin-core': patch
---

Add first-class Octane Router and TanStack Start support, including `.tsrx` file routes, typed router APIs, code splitting, native Octane streaming injection, deferred hydration, `ClientOnly` server isolation, and Vite integration.
3 changes: 3 additions & 0 deletions e2e/octane-start/basic/netlify.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build]
command = "pnpm run build:netlify"
publish = "dist/client"
52 changes: 52 additions & 0 deletions e2e/octane-start/basic/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"name": "tanstack-octane-start-e2e-basic",
"private": true,
"sideEffects": false,
"type": "module",
"scripts": {
"dev": "vite dev --port 3000",
"dev:e2e": "vite dev",
"build": "vite build && tsc --noEmit",
"build:cloudflare": "vite build --config vite.config.cloudflare.ts",
"build:netlify": "vite build --config vite.config.netlify.ts",
"start": "node .output/server/index.mjs",
"test:platform-builds": "pnpm run build:cloudflare && pnpm run build:netlify",
"test:e2e:local": "pnpm build && playwright test --project=chromium",
"test:e2e:hmr:local": "E2E_DEV=true playwright test --project=chromium"
},
"dependencies": {
"@tanstack/octane-router": "workspace:^",
"@tanstack/octane-start": "workspace:^",
"octane": "catalog:"
},
"devDependencies": {
"@cloudflare/vite-plugin": "^1.29.0",
"@netlify/vite-plugin-tanstack-start": "^1.1.4",
"@playwright/test": "^1.61.0",
"@tanstack/router-e2e-utils": "workspace:^",
"@types/node": "^22.10.2",
"@typescript/native": "npm:typescript@^7.0.2",
"nitro": "^3.0.260311-beta",
"typescript": "npm:@typescript/typescript6@^6.0.2",
"vite": "8.1.5",
"wrangler": "^4.74.0"
},
"nx": {
"metadata": {
"playwrightModes": [
{
"toolchain": "vite",
"mode": "ssr"
},
{
"toolchain": "vite",
"mode": "ssr",
"name": "hmr",
"env": {
"E2E_DEV": "true"
}
}
]
}
}
}
42 changes: 42 additions & 0 deletions e2e/octane-start/basic/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import fs from 'node:fs'
import { defineConfig, devices } from '@playwright/test'
import { getTestServerPort } from '@tanstack/router-e2e-utils'
import packageJson from './package.json' with { type: 'json' }

const e2ePortKey = process.env.E2E_PORT_KEY ?? packageJson.name
const distDir = process.env.E2E_DIST_DIR ?? '.output'
const isDev = process.env.E2E_DEV === 'true'

if (process.env.TEST_WORKER_INDEX === undefined) {
fs.rmSync(`port-${e2ePortKey}.txt`, { force: true })
}

const port = await getTestServerPort(e2ePortKey)
const baseURL = `http://127.0.0.1:${port}`

export default defineConfig({
testDir: './tests',
testMatch: isDev ? 'hmr.spec.ts' : 'basic.spec.ts',
workers: 1,
reporter: [['line']],
use: { baseURL },
webServer: {
command: isDev
? `pnpm dev:e2e --host 127.0.0.1 --port ${port} --strictPort`
: `PORT=${port} node ${distDir}/server/index.mjs`,
url: baseURL,
reuseExistingServer: !process.env.CI,
stdout: 'pipe',
env: isDev
? { E2E_DEV: 'true' }
: {
NODE_ENV: 'production',
},
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
})
3 changes: 3 additions & 0 deletions e2e/octane-start/basic/src/client-only-proof.client.tsrx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function ClientOnlyProof() @{
<p data-testid="client-only-proof">{'client-only module rendered'}</p>
}
3 changes: 3 additions & 0 deletions e2e/octane-start/basic/src/deferred-hydration-proof.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.octane-start-deferred-hydration-proof {
color: rgb(12, 34, 56);
}
24 changes: 24 additions & 0 deletions e2e/octane-start/basic/src/deferred-hydration-proof.tsrx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useState } from 'octane';
import './deferred-hydration-proof.css';

const browserProof = globalThis as typeof globalThis & {
__octaneStartDeferredHydrationModuleLoaded?: boolean;
};

if (typeof document !== 'undefined') {
browserProof.__octaneStartDeferredHydrationModuleLoaded = true;
}

export function DeferredHydrationProof() @{
const [clicks, setClicks] = useState(0);

<button
class="octane-start-deferred-hydration-proof"
data-clicks={String(clicks)}
data-testid="deferred-hydration-proof"
type="button"
onClick={() => setClicks((count) => count + 1)}
>
{'deferred hydration clicks ' + clicks}
</button>
}
Loading
Loading