diff --git a/bun.lock b/bun.lock index e12b107..f230fa9 100644 --- a/bun.lock +++ b/bun.lock @@ -8,6 +8,7 @@ "fuzzysort": "^3.1.0", "hono": "^4.11.4", "preact": "^10.29.2", + "preact-render-to-string": "^6.6.7", "zod": "^4.4.3", }, "devDependencies": { @@ -20,7 +21,6 @@ "typescript": "~6.0.2", "vite": "npm:@voidzero-dev/vite-plus-core@latest", "vite-plus": "latest", - "vite-tsconfig-paths": "^6.1.1", "wrangler": "^4.87.0", }, }, @@ -469,8 +469,6 @@ "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - "globrex": ["globrex@0.1.2", "", {}, "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg=="], - "happy-dom": ["happy-dom@20.9.0", "", { "dependencies": { "@types/node": ">=20.0.0", "@types/whatwg-mimetype": "^3.0.2", "@types/ws": "^8.18.1", "entities": "^7.0.1", "whatwg-mimetype": "^3.0.0", "ws": "^8.18.3" } }, "sha512-GZZ9mKe8r646NUAf/zemnGbjYh4Bt8/MqASJY+pSm5ZDtc3YQox+4gsLI7yi1hba6o+eCsGxpHn5+iEVn31/FQ=="], "he": ["he@1.2.0", "", { "bin": { "he": "bin/he" } }, "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="], @@ -573,6 +571,8 @@ "preact": ["preact@10.29.2", "", {}, "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ=="], + "preact-render-to-string": ["preact-render-to-string@6.6.7", "", { "peerDependencies": { "preact": ">=10 || >= 11.0.0-0" } }, "sha512-3XdbsX3+vn9dQW+jJI/FsI9rlkgl6dbeUpqLsChak6jp3j3auFqBCkno7VChbMFs5Q8ylBj6DrUkKRwtVN3nvw=="], + "require-like": ["require-like@0.1.2", "", {}, "sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A=="], "semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], @@ -603,8 +603,6 @@ "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], - "tsconfck": ["tsconfck@3.1.6", "", { "peerDependencies": { "typescript": "^5.0.0" }, "optionalPeers": ["typescript"], "bin": { "tsconfck": "bin/tsconfck.js" } }, "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w=="], - "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "typescript": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], @@ -627,8 +625,6 @@ "vite-prerender-plugin": ["vite-prerender-plugin@0.5.13", "", { "dependencies": { "kolorist": "^1.8.0", "magic-string": "0.x >= 0.26.0", "node-html-parser": "^6.1.12", "simple-code-frame": "^1.3.0", "source-map": "^0.7.4", "stack-trace": "^1.0.0-pre2" }, "peerDependencies": { "vite": "5.x || 6.x || 7.x || 8.x" } }, "sha512-IKSpYkzDBsKAxa05naRbj7GvNVMSdww/Z/E89oO3xndz+gWnOBOKOAbEXv7qDhktY/j3vHgJmoV1pPzqU2tx9g=="], - "vite-tsconfig-paths": ["vite-tsconfig-paths@6.1.1", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" } }, "sha512-2cihq7zliibCCZ8P9cKJrQBkfgdvcFkOOc3Y02o3GWUDLgqjWsZudaoiuOwO/gzTzy17cS5F7ZPo4bsnS4DGkg=="], - "whatwg-mimetype": ["whatwg-mimetype@3.0.0", "", {}, "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q=="], "workerd": ["workerd@1.20260515.1", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20260515.1", "@cloudflare/workerd-darwin-arm64": "1.20260515.1", "@cloudflare/workerd-linux-64": "1.20260515.1", "@cloudflare/workerd-linux-arm64": "1.20260515.1", "@cloudflare/workerd-windows-64": "1.20260515.1" }, "bin": { "workerd": "bin/workerd" } }, "sha512-MjKOJLcvU45xXedQowvuiHtJTxu4WTHYQeIlF7YmjuqhiI6dImTFxWCEoRQHiskztxuVSNEmdO7/0UfDu6OMnQ=="], diff --git a/index.html b/index.html index 6aab48d..2731642 100644 --- a/index.html +++ b/index.html @@ -8,6 +8,6 @@
- + diff --git a/package.json b/package.json index cc62342..76467fd 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "fuzzysort": "^3.1.0", "hono": "^4.11.4", "preact": "^10.29.2", + "preact-render-to-string": "^6.6.7", "zod": "^4.4.3" }, "devDependencies": { @@ -33,7 +34,6 @@ "typescript": "~6.0.2", "vite": "npm:@voidzero-dev/vite-plus-core@latest", "vite-plus": "latest", - "vite-tsconfig-paths": "^6.1.1", "wrangler": "^4.87.0" }, "overrides": { diff --git a/src/hub-app.tsx b/src/hub-app.tsx index 13f1e35..f4dab26 100644 --- a/src/hub-app.tsx +++ b/src/hub-app.tsx @@ -1,4 +1,5 @@ -import {render} from 'preact'; +import {hydrate} from 'preact'; +import renderToString from 'preact-render-to-string'; import {HubApp} from '@/hub/HubApp'; @@ -8,9 +9,16 @@ import '@/hub/global.css'; * Script. */ -const root = document.getElementById('root'); -if (!root) { - throw new Error('hub markup missing #root'); +if (typeof document !== 'undefined') { + const root = document.getElementById('root'); + if (!root) { + throw new Error('hub markup missing #root'); + } + + hydrate(, root); } -render(, root); +/** Build-time hook. Renders `HubApp` to HTML for the prerender pipeline. */ +export async function prerender() { + return {html: renderToString()}; +} diff --git a/vite.config.ts b/vite.config.ts index 98f8c12..3f35a55 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,7 +2,6 @@ import {cloudflare} from '@cloudflare/vite-plugin'; import preact from '@preact/preset-vite'; import {vanillaExtractPlugin} from '@vanilla-extract/vite-plugin'; import {defineConfig} from 'vite-plus'; -import tsconfigPaths from 'vite-tsconfig-paths'; /* * Constants. @@ -26,7 +25,23 @@ const REPO_TS_FMT_OPTIONS = { */ export default defineConfig({ - plugins: isVitest ? [tsconfigPaths()] : [cloudflare(), vanillaExtractPlugin(), preact(), tsconfigPaths()], + resolve: { + tsconfigPaths: true + }, + plugins: isVitest + ? [] + : [ + cloudflare(), + vanillaExtractPlugin(), + preact({ + prerender: { + enabled: true, + renderTarget: '#root', + additionalPrerenderRoutes: ['/'], + previewMiddlewareEnabled: true + } + }) + ], staged: { '*': 'vp check --fix' },