hydration abstraction
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
"@tinyhttp/app": "^2.0.15",
|
||||
"@tinyhttp/logger": "^1.3.0",
|
||||
"clsx": "^1.1.1",
|
||||
"esbuild": "^0.14.10",
|
||||
"express": "^4.17.2",
|
||||
"gray-matter": "^4.0.3",
|
||||
"http-terminator": "^3.0.4",
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import * as React from "react"
|
||||
import { mainLinks } from "../data/main-links"
|
||||
import type { AppLinkProps } from "./app-link"
|
||||
import { AppLink } from "./app-link"
|
||||
import { PopoverMenu } from "./popover-menu"
|
||||
|
||||
export type MainNavigationMobileMenuData = {
|
||||
guideLinks: AppLinkProps[]
|
||||
}
|
||||
|
||||
export function render(data: MainNavigationMobileMenuData) {
|
||||
return (
|
||||
<PopoverMenu>
|
||||
{mainLinks.map((link) => (
|
||||
<AppLink {...link} key={link.to} className={PopoverMenu.itemClass} />
|
||||
))}
|
||||
<hr className="border-0 h-[2px] bg-black/50" />
|
||||
{data.guideLinks.map((link) => (
|
||||
<AppLink {...link} key={link.to} className={PopoverMenu.itemClass} />
|
||||
))}
|
||||
</PopoverMenu>
|
||||
)
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import * as React from "react"
|
||||
import { createRoot } from "react-dom"
|
||||
import { mainLinks } from "../data/main-links"
|
||||
import { AppLink } from "./app-link"
|
||||
import type { MainNavigationClientData } from "./main-navigation"
|
||||
import { PopoverMenu } from "./popover-menu"
|
||||
|
||||
const dataScript = document.querySelector("#main-navigation-popover-data")!
|
||||
const data: MainNavigationClientData = JSON.parse(dataScript.innerHTML)
|
||||
|
||||
createRoot(document.querySelector("#main-navigation-popover")!).render(
|
||||
<PopoverMenu>
|
||||
{mainLinks.map((link) => (
|
||||
<AppLink {...link} key={link.to} className={PopoverMenu.itemClass} />
|
||||
))}
|
||||
<hr className="border-0 h-[2px] bg-black/50" />
|
||||
{data.guideLinks.map((link) => (
|
||||
<AppLink {...link} key={link.to} className={PopoverMenu.itemClass} />
|
||||
))}
|
||||
</PopoverMenu>,
|
||||
)
|
||||
@@ -1,38 +1,14 @@
|
||||
import { build } from "esbuild"
|
||||
import { readFile } from "node:fs/promises"
|
||||
import { dirname } from "node:path"
|
||||
import React from "react"
|
||||
import { guideLinks } from "../data/guide-links"
|
||||
import { mainLinks } from "../data/main-links"
|
||||
import { createHydrater } from "../helpers/hydration"
|
||||
import { linkClass } from "../styles/components"
|
||||
import type { AppLinkProps } from "./app-link"
|
||||
import { AppLink } from "./app-link"
|
||||
import { PopoverMenu } from "./popover-menu"
|
||||
import { Script } from "./script"
|
||||
import type { MainNavigationMobileMenuData } from "./main-navigation-mobile-menu"
|
||||
|
||||
const clientSourcePath = new URL(
|
||||
"./main-navigation.client.tsx",
|
||||
import.meta.url,
|
||||
).pathname
|
||||
|
||||
const clientOutput = await build({
|
||||
bundle: true,
|
||||
stdin: {
|
||||
contents: await readFile(clientSourcePath, "utf-8"),
|
||||
sourcefile: clientSourcePath,
|
||||
loader: "tsx",
|
||||
resolveDir: dirname(clientSourcePath),
|
||||
},
|
||||
target: ["chrome89", "firefox89"],
|
||||
format: "esm",
|
||||
write: false,
|
||||
})
|
||||
|
||||
export type MainNavigationClientData = {
|
||||
guideLinks: AppLinkProps[]
|
||||
}
|
||||
|
||||
const data: MainNavigationClientData = { guideLinks }
|
||||
const MenuHydrater = await createHydrater<MainNavigationMobileMenuData>(
|
||||
new URL("./main-navigation-mobile-menu.tsx", import.meta.url).pathname,
|
||||
)
|
||||
|
||||
export function MainNavigation() {
|
||||
return (
|
||||
@@ -46,28 +22,8 @@ export function MainNavigation() {
|
||||
))}
|
||||
</div>
|
||||
<div className="md:hidden" id="main-navigation-popover">
|
||||
<PopoverMenu>
|
||||
{mainLinks.map((link) => (
|
||||
<AppLink
|
||||
{...link}
|
||||
key={link.to}
|
||||
className={PopoverMenu.itemClass}
|
||||
/>
|
||||
))}
|
||||
<hr className="border-0 h-[2px] bg-black/50" />
|
||||
{data.guideLinks.map((link) => (
|
||||
<AppLink
|
||||
{...link}
|
||||
key={link.to}
|
||||
className={PopoverMenu.itemClass}
|
||||
/>
|
||||
))}
|
||||
</PopoverMenu>
|
||||
<MenuHydrater data={{ guideLinks }} />
|
||||
</div>
|
||||
<Script id="main-navigation-popover-data" type="application/json">
|
||||
{JSON.stringify(data)}
|
||||
</Script>
|
||||
<Script>{clientOutput.outputFiles[0]?.text!}</Script>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
|
||||
51
packages/docs-new/src/helpers/hydration.tsx
Normal file
51
packages/docs-new/src/helpers/hydration.tsx
Normal file
@@ -0,0 +1,51 @@
|
||||
import { build } from "esbuild"
|
||||
import { readFile } from "node:fs/promises"
|
||||
import { dirname } from "node:path"
|
||||
import React from "react"
|
||||
import { Script } from "../components/script"
|
||||
|
||||
let nextId = 0
|
||||
|
||||
export async function createHydrater<Data>(scriptFilePath: string) {
|
||||
const id = `hydrate-root-${nextId}`
|
||||
nextId += 1
|
||||
|
||||
const scriptSource = await readFile(scriptFilePath, "utf-8")
|
||||
|
||||
const scriptBuild = await build({
|
||||
bundle: true,
|
||||
stdin: {
|
||||
contents: [scriptSource, clientBootstrap(id)].join(";\n"),
|
||||
sourcefile: scriptFilePath,
|
||||
loader: "tsx",
|
||||
resolveDir: dirname(scriptFilePath),
|
||||
},
|
||||
target: ["chrome89", "firefox89"],
|
||||
format: "esm",
|
||||
write: false,
|
||||
})
|
||||
|
||||
const serverModule = await import(scriptFilePath)
|
||||
|
||||
return function Hydrater({ data }: { data: Data }) {
|
||||
return (
|
||||
<>
|
||||
<div id={id} data-server-data={JSON.stringify(data)}>
|
||||
{serverModule.render(data)}
|
||||
</div>
|
||||
<Script>{scriptBuild.outputFiles[0]?.text!}</Script>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function clientBootstrap(id: string) {
|
||||
return /* ts */ `
|
||||
import { createRoot } from "react-dom"
|
||||
|
||||
const rootElement = document.querySelector("#${id}")
|
||||
const data = JSON.parse(rootElement.dataset.serverData)
|
||||
|
||||
createRoot(rootElement).render(render(data))
|
||||
`
|
||||
}
|
||||
21
pnpm-lock.yaml
generated
21
pnpm-lock.yaml
generated
@@ -133,6 +133,7 @@ importers:
|
||||
clsx: ^1.1.1
|
||||
compression: ^1.7.4
|
||||
debounce-fn: ^5.1.0
|
||||
esbuild: ^0.14.10
|
||||
esno: ^0.13.0
|
||||
execa: ^6.0.0
|
||||
express: ^4.17.2
|
||||
@@ -182,6 +183,7 @@ importers:
|
||||
'@tinyhttp/app': 2.0.15
|
||||
'@tinyhttp/logger': 1.3.0
|
||||
clsx: 1.1.1
|
||||
esbuild: 0.14.10
|
||||
express: 4.17.2
|
||||
gray-matter: 4.0.3
|
||||
http-terminator: 3.0.4
|
||||
@@ -3826,7 +3828,6 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-darwin-64/0.14.10:
|
||||
@@ -3834,7 +3835,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-darwin-arm64/0.14.10:
|
||||
@@ -3842,7 +3842,6 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-freebsd-64/0.14.10:
|
||||
@@ -3850,7 +3849,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-freebsd-arm64/0.14.10:
|
||||
@@ -3858,7 +3856,6 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-jest/0.5.0_esbuild@0.14.10:
|
||||
@@ -3879,7 +3876,6 @@ packages:
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-64/0.14.10:
|
||||
@@ -3887,7 +3883,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-arm/0.14.10:
|
||||
@@ -3895,7 +3890,6 @@ packages:
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-arm64/0.14.10:
|
||||
@@ -3903,7 +3897,6 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-mips64le/0.14.10:
|
||||
@@ -3911,7 +3904,6 @@ packages:
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-ppc64le/0.14.10:
|
||||
@@ -3919,7 +3911,6 @@ packages:
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-linux-s390x/0.14.10:
|
||||
@@ -3927,7 +3918,6 @@ packages:
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-netbsd-64/0.14.10:
|
||||
@@ -3935,7 +3925,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-node-loader/0.6.3_typescript@4.5.4:
|
||||
@@ -3952,7 +3941,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-register/3.3.1_esbuild@0.14.10:
|
||||
@@ -3968,7 +3956,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-windows-32/0.14.10:
|
||||
@@ -3976,7 +3963,6 @@ packages:
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-windows-64/0.14.10:
|
||||
@@ -3984,7 +3970,6 @@ packages:
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild-windows-arm64/0.14.10:
|
||||
@@ -3992,7 +3977,6 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
requiresBuild: true
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/esbuild/0.14.10:
|
||||
@@ -4018,7 +4002,6 @@ packages:
|
||||
esbuild-windows-32: 0.14.10
|
||||
esbuild-windows-64: 0.14.10
|
||||
esbuild-windows-arm64: 0.14.10
|
||||
dev: true
|
||||
|
||||
/escalade/3.1.1:
|
||||
resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
|
||||
|
||||
Reference in New Issue
Block a user