Skip to content

Commit ad86ee5

Browse files
committed
feat(react): replace unihead with unhead
1 parent b6a1b6d commit ad86ee5

File tree

12 files changed

+114
-249
lines changed

12 files changed

+114
-249
lines changed

packages/fastify-react/context.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { createHead } from '@unhead/react/server'
2+
13
const routeContextInspect = Symbol.for('nodejs.util.inspect.custom')
24

35
export default class RouteContext {
@@ -15,10 +17,12 @@ export default class RouteContext {
1517
}
1618

1719
constructor(server, req, reply, route) {
20+
this.app = null
1821
this.server = server
1922
this.req = req
2023
this.reply = reply
2124
this.head = {}
25+
this.useHead = createHead()
2226
this.actionData = {}
2327
this.state = null
2428
this.data = route.data
@@ -46,6 +50,7 @@ export default class RouteContext {
4650
actionData: this.actionData,
4751
state: this.state,
4852
data: this.data,
53+
head: this.head,
4954
layout: this.layout,
5055
getMeta: this.getMeta,
5156
getData: this.getData,

packages/fastify-react/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
},
4242
"dependencies": {
4343
"@fastify/vite": "workspace:^",
44+
"@unhead/react": "^2.0.8",
4445
"acorn": "^8.14.1",
4546
"acorn-strip-function": "^1.2.0",
4647
"acorn-walk": "^8.3.4",

packages/fastify-react/rendering.js

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Minipass } from 'minipass'
66
// which enables the combination of React.lazy() and Suspense
77
import { renderToPipeableStream } from 'react-dom/server'
88
import * as devalue from 'devalue'
9-
import Head from 'unihead'
9+
import { transformHtmlTemplate } from '@unhead/react/server'
1010
import { createHtmlTemplates } from './templating.js'
1111

1212
// Helper function to get an AsyncIterable (via PassThrough)
@@ -46,6 +46,7 @@ export function onAllReady(app) {
4646
export async function createRenderFunction ({ routes, create }) {
4747
// Used when hydrating React Router on the client
4848
const routeMap = Object.fromEntries(routes.map(_ => [_.path, _]))
49+
4950
// Registered as reply.render()
5051
return function () {
5152
if (this.request.route.streaming) {
@@ -80,6 +81,7 @@ export async function createHtmlFunction (source, _, config) {
8081
return async function () {
8182
const { routes, context, body } = await this.render()
8283

84+
context.useHead.push(context.head)
8385
this.type('text/html')
8486

8587
// Use template with client module import removed
@@ -116,24 +118,35 @@ export async function createHtmlFunction (source, _, config) {
116118
}
117119
}
118120

119-
export function sendClientOnlyShell (templates, context, body) {
120-
context.head = new Head(context.head).render()
121-
return `${
122-
templates.beforeElement(context)
123-
}${
124-
templates.afterElement(context)
125-
}`
121+
export async function sendClientOnlyShell (templates, context) {
122+
return await transformHtmlTemplate(
123+
context.useHead,
124+
`${
125+
templates.beforeElement(context)
126+
}${
127+
templates.afterElement(context)
128+
}`
129+
)
126130
}
127131

128132
export function streamShell (templates, context, body) {
129-
context.head = new Head(context.head).render()
130133
return Readable.from(createShellStream(templates, context, body))
131134
}
132135

133136
async function * createShellStream (templates, context, body) {
134-
yield templates.beforeElement(context)
137+
yield await transformHtmlTemplate(
138+
context.useHead,
139+
templates.beforeElement(context)
140+
)
141+
135142
for await (const chunk of body) {
136-
yield chunk
143+
yield await transformHtmlTemplate(
144+
context.useHead,
145+
chunk.toString()
146+
)
137147
}
138-
yield templates.afterElement(context)
148+
yield await transformHtmlTemplate(
149+
context.useHead,
150+
templates.afterElement(context)
151+
)
139152
}

packages/fastify-react/virtual/core.jsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export function useServerAction(action, options = {}) {
3333
return actionData[action]
3434
}
3535

36-
export function AppRoute({ head, ctxHydration, ctx, children }) {
36+
export function AppRoute({ ctxHydration, ctx, children }) {
3737
// If running on the server, assume all data
3838
// functions have already ran through the preHandler hook
3939
if (isServer) {
@@ -100,7 +100,8 @@ export function AppRoute({ head, ctxHydration, ctx, children }) {
100100
if (!ctx.firstRender && ctx.getMeta) {
101101
const updateMeta = async () => {
102102
const { getMeta } = await ctx.loader()
103-
head.update(await getMeta(ctx))
103+
ctx.head = await getMeta(ctx)
104+
ctxHydration.useHead.push(ctx.head)
104105
}
105106
waitResource(path, 'updateMeta', updateMeta)
106107
}
Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
1+
import {
2+
UnheadProvider as ClientUnheadProvider
3+
} from '@unhead/react/client'
4+
import {
5+
UnheadProvider as ServerUnheadProvider
6+
} from '@unhead/react/server?server'
7+
18
import Root from '$app/root.jsx'
29

310
export default function create({ url, ...serverInit }) {
4-
return <Root url={url} {...serverInit} />
11+
const UnheadProvider = import.meta.env.SSR
12+
? ServerUnheadProvider
13+
: ClientUnheadProvider
14+
return (
15+
<UnheadProvider value={serverInit.ctxHydration.useHead}>
16+
<Root url={url} {...serverInit} />
17+
</UnheadProvider>
18+
)
519
}

packages/fastify-react/virtual/mount.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
import { createRoot, hydrateRoot } from 'react-dom/client'
2-
import Head from 'unihead/client'
32
import { hydrateRoutes } from '@fastify/react/client'
3+
import { createHead } from '@unhead/react/client'
44
import routes from '$app/routes.js'
55
import create from '$app/create.jsx'
66
import * as context from '$app/context.js'
77

88
async function mountApp (...targets) {
99
const ctxHydration = await extendContext(window.route, context)
10-
const head = new Head(window.route.head, window.document)
1110
const resolvedRoutes = await hydrateRoutes(routes)
1211
const routeMap = Object.fromEntries(
1312
resolvedRoutes.map((route) => [route.path, route]),
1413
)
14+
const useHead = createHead()
15+
ctxHydration.useHead = useHead
16+
ctxHydration.useHead.push(window.route.head)
17+
1518
const app = create({
16-
head,
1719
ctxHydration,
1820
routes: window.routes,
1921
routeMap,
2022
})
23+
24+
2125
let mountTargetFound = false
2226
for (const target of targets) {
2327
const targetElem = document.querySelector(target)

0 commit comments

Comments
 (0)