Skip to content

Commit

Permalink
bit a mess in themiddle
Browse files Browse the repository at this point in the history
  • Loading branch information
kentcdodds committed Mar 27, 2024
1 parent 3c41d4b commit 7bd6b65
Show file tree
Hide file tree
Showing 61 changed files with 175 additions and 2,399 deletions.
4 changes: 1 addition & 3 deletions exercises/01.start/01.problem.ssr/src/ship-details.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ export function ShipFallback({ shipId }) {
'div',
{ className: 'ship-info__img-wrapper' },
h('img', {
src: getImageUrlForShip(shipId, {
size: 200,
}),
src: getImageUrlForShip(shipId, { size: 200 }),
// TODO: handle this better
alt: shipId,
}),
Expand Down
4 changes: 1 addition & 3 deletions exercises/01.start/01.solution.ssr/src/ship-details.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ export function ShipFallback({ shipId }) {
'div',
{ className: 'ship-info__img-wrapper' },
h('img', {
src: getImageUrlForShip(shipId, {
size: 200,
}),
src: getImageUrlForShip(shipId, { size: 200 }),
// TODO: handle this better
alt: shipId,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ export function ShipFallback({ shipId }) {
'div',
{ className: 'ship-info__img-wrapper' },
h('img', {
src: getImageUrlForShip(shipId, {
size: 200,
}),
src: getImageUrlForShip(shipId, { size: 200 }),
// TODO: handle this better
alt: shipId,
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ export function ShipFallback() {
'div',
{ className: 'ship-info__img-wrapper' },
h('img', {
src: getImageUrlForShip(shipId, {
size: 200,
}),
src: getImageUrlForShip(shipId, { size: 200 }),
// TODO: handle this better
alt: shipId,
}),
Expand Down
4 changes: 1 addition & 3 deletions exercises/01.start/03.problem.url/src/ship-details.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ export function ShipFallback() {
'div',
{ className: 'ship-info__img-wrapper' },
h('img', {
src: getImageUrlForShip(shipId, {
size: 200,
}),
src: getImageUrlForShip(shipId, { size: 200 }),
// TODO: handle this better
alt: shipId,
}),
Expand Down
4 changes: 1 addition & 3 deletions exercises/01.start/03.solution.url/src/ship-details.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ export function ShipFallback() {
'div',
{ className: 'ship-info__img-wrapper' },
h('img', {
src: getImageUrlForShip(shipId, {
size: 200,
}),
src: getImageUrlForShip(shipId, { size: 200 }),
// TODO: handle this better
alt: shipId,
}),
Expand Down
8 changes: 7 additions & 1 deletion exercises/05.middle/01.problem.middle/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,13 @@ const ssrServer = spawnScript(

const rscServer = spawnScript(
'node',
['--watch', '--conditions=react-server', 'server/rsc.js'],
[
'--watch',
'--import',
'./server/register-rsc-loader.js',
'--conditions=react-server',
'server/rsc.js',
],
{ PORT: RSC_PORT },
chalk.green.bgBlack('RSC'),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { register } from 'node:module'

register('./rsc-loader.js', import.meta.url)
23 changes: 23 additions & 0 deletions exercises/05.middle/01.problem.middle/server/rsc-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { resolve, load as reactLoad } from 'react-server-dom-esm/node-loader'

export { resolve }

async function textLoad(url, context, defaultLoad) {
const result = await defaultLoad(url, context, defaultLoad)
if (result.format === 'module') {
if (typeof result.source === 'string') {
return result
}
return {
source: Buffer.from(result.source).toString('utf8'),
format: 'module',
}
}
return result
}

export async function load(url, context, defaultLoad) {
return await reactLoad(url, context, (u, c) => {
return textLoad(u, c, defaultLoad)
})
}
21 changes: 10 additions & 11 deletions exercises/05.middle/01.problem.middle/server/rsc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import express from 'express'
import { createElement as h } from 'react'
import { renderToPipeableStream } from 'react-server-dom-esm/server'
import { Document } from '../src/app.js'
import { shipDataStorage } from './async-storage.js'

const PORT = process.env.PORT || 3001

Expand All @@ -13,17 +14,15 @@ app.use(compress())

const moduleBasePath = new URL('../src', import.meta.url).href

async function renderApp(res) {
const shipId = '6c86fca8b9086'
const search = ''
const root = h(Document, { shipId, search })
// For client-invoked server actions we refresh the tree and return a return value.
const { pipe } = renderToPipeableStream(root, moduleBasePath)
pipe(res)
}

app.get('/', async function (req, res) {
await renderApp(res)
app.get('/:shipId?', function (req, res) {
const shipId = req.params.shipId || null
const search = req.query.search || ''
shipDataStorage.run({ shipId, search }, () => {
const root = h(Document)
const payload = { root }
const { pipe } = renderToPipeableStream(payload, moduleBasePath)
pipe(res)
})
})

const server = app.listen(PORT, () => {
Expand Down
113 changes: 91 additions & 22 deletions exercises/05.middle/01.problem.middle/server/ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import express from 'express'
import { createElement as h, use } from 'react'
import { renderToPipeableStream } from 'react-dom/server'
import { createFromNodeStream } from 'react-server-dom-esm/client'
import { RouterContext } from '../src/router.js'

const moduleBasePath = new URL('../src', import.meta.url).href

Expand Down Expand Up @@ -34,7 +35,20 @@ app.head('/', async (req, res) => {
res.status(200).end()
})

app.all('/', async function (req, res) {
app.use(express.static('public'))
app.use('/js/src', express.static('src'))
app.use('/js/react-server-dom-esm/client', (req, res) => {
const require = createRequire(import.meta.url)
const pkgPath = require.resolve('react-server-dom-esm')
const modulePath = path.join(
path.dirname(pkgPath),
'esm',
'react-server-dom-esm-client.browser.development.js',
)
res.sendFile(modulePath)
})

app.all('/:shipId?', async function (req, res) {
// Proxy the request to the rsc server.
const proxiedHeaders = {
'X-Forwarded-Host': req.hostname,
Expand All @@ -57,31 +71,86 @@ app.all('/', async function (req, res) {
req,
)

try {
const rscResponse = await promiseForData
const moduleBaseURL = '/js/src'
if (req.accepts('text/html')) {
try {
const rscResponse = await promiseForData
const moduleBaseURL = '/js/src'

// For HTML, we're a "client" emulator that runs the client code,
// so we start by consuming the RSC payload. This needs the local file path
// to load the source files from as well as the URL path for preloads.
// For HTML, we're a "client" emulator that runs the client code,
// so we start by consuming the RSC payload. This needs the local file path
// to load the source files from as well as the URL path for preloads.

let rootPromise
function Root() {
rootPromise ??= createFromNodeStream(
rscResponse,
moduleBasePath,
moduleBaseURL,
let contentPromise
function Root() {
contentPromise ??= createFromNodeStream(
rscResponse,
moduleBasePath,
moduleBaseURL,
)
const content = use(contentPromise)
return content.root
}
const location = req.url
const navigate = () => {
throw new Error('navigate cannot be called on the server')
}
const isPending = false
const routerValue = {
location,
nextLocation: location,
navigate,
isPending,
}
const { pipe } = renderToPipeableStream(
h(RouterContext.Provider, { value: routerValue }, h(Root)),
{
bootstrapModules: ['/js/src/index.js'],
importMap: {
imports: {
react:
'https://esm.sh/[email protected]?pin=v126&dev',
'react-dom':
'https://esm.sh/[email protected]?pin=v126&dev',
'react-dom/':
'https://esm.sh/[email protected]&pin=v126&dev/',
'react-error-boundary':
'https://esm.sh/[email protected]?pin=126&dev',
'react-server-dom-esm/client': '/js/react-server-dom-esm/client',
},
},
},
)
const root = use(rootPromise)
return root
pipe(res)
} catch (e) {
console.error(`Failed to SSR: ${e.stack}`)
res.statusCode = 500
res.end(`Failed to SSR: ${e.stack}`)
}
} else {
try {
const rscResponse = await promiseForData

// Forward all headers from the RSC response to the client response
Object.entries(rscResponse.headers).forEach(([header, value]) => {
res.set(header, value)
})

if (req.get('rsc-action')) {
res.set('Content-type', 'text/x-component')
}

rscResponse.on('data', data => {
res.write(data)
res.flush()
})
rscResponse.on('end', () => {
res.end()
})
} catch (e) {
console.error(`Failed to proxy request: ${e.stack}`)
res.statusCode = 500
res.end(`Failed to proxy request: ${e.stack}`)
}
res.set('Content-type', 'text/html')
const { pipe } = renderToPipeableStream(h(Root))
pipe(res)
} catch (e) {
console.error(`Failed to SSR: ${e.stack}`)
res.statusCode = 500
res.end(`Failed to SSR: ${e.stack}`)
}
})

Expand Down
42 changes: 18 additions & 24 deletions exercises/05.middle/01.problem.middle/src/app.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Fragment, createElement as h, Suspense } from 'react'
import { shipDataStorage } from '../server/async-storage.js'
import { ShipDetails, ShipFallback } from './ship-details.js'
import { SearchResults, SearchResultsFallback } from './ship-search-results.js'
import { SearchResults } from './ship-search-results.js'

const shipFallbackSrc = '/img/fallback-ship.png'

export async function Document({ shipId, search }) {
export async function Document() {
return h(
'html',
{ lang: 'en' },
Expand All @@ -24,15 +25,12 @@ export async function Document({ shipId, search }) {
href: '/favicon.svg',
}),
),
h(
'body',
null,
h('div', { className: 'app-wrapper' }, h(App, { shipId, search })),
),
h('body', null, h('div', { className: 'app-wrapper' }, h(App))),
)
}

export function App({ shipId, search }) {
export function App() {
const { shipId, search } = shipDataStorage.getStore()
return h(
'div',
{ className: 'app' },
Expand All @@ -50,30 +48,26 @@ export function App({ shipId, search }) {
h(
Fragment,
null,
h('input', {
placeholder: 'Filter ships...',
type: 'search',
defaultValue: search,
}),
h(
'ul',
'form',
null,
h(
Suspense,
{ fallback: h(SearchResultsFallback) },
h(SearchResults, { shipId, search }),
),
h('input', {
placeholder: 'Filter ships...',
type: 'search',
name: 'search',
defaultValue: search,
autoFocus: true,
}),
),
h('ul', null, h(SearchResults)),
),
),
h(
'div',
{ className: 'details' },
h(
Suspense,
{ fallback: h(ShipFallback, { shipId }) },
h(ShipDetails, { shipId }),
),
shipId
? h(Suspense, { fallback: h(ShipFallback) }, h(ShipDetails))
: h('p', null, 'Select a ship from the list to see details'),
),
),
)
Expand Down
11 changes: 6 additions & 5 deletions exercises/05.middle/01.problem.middle/src/ship-details.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { createElement as h } from 'react'
import { getShip } from '../db/ship-api.js'
import { shipDataStorage } from '../server/async-storage.js'
import { getImageUrlForShip } from './img-utils.js'

export async function ShipDetails({ shipId }) {
export async function ShipDetails() {
const { shipId } = shipDataStorage.getStore()
const ship = await getShip({ shipId })
const shipImgSrc = getImageUrlForShip(ship.id, { size: 200 })
return h(
Expand Down Expand Up @@ -44,17 +46,16 @@ export async function ShipDetails({ shipId }) {
)
}

export function ShipFallback({ shipId }) {
export function ShipFallback() {
const { shipId } = shipDataStorage.getStore()
return h(
'div',
{ className: 'ship-info' },
h(
'div',
{ className: 'ship-info__img-wrapper' },
h('img', {
src: getImageUrlForShip(shipId, {
size: 200,
}),
src: getImageUrlForShip(shipId, { size: 200 }),
// TODO: handle this better
alt: shipId,
}),
Expand Down
Loading

0 comments on commit 7bd6b65

Please sign in to comment.