import { OffersList } from './OffersList.tsx'
export const Offers = async({ search } : { search: string }) => {
const data = await fetch('https://fakestoreapi.com/products').then(r => r.json()).then(data => (
search ? data.filter(({ title } : { title: string }) => title.toLowerCase().startsWith(search.toLowerCase())) : data
))
return (
<OffersList data={ data }/>
)
}
import { OfferActions } from './OfferActions.tsx'
export const OffersList = ({ data } : { data: any }) => {
return (
<ul>
<title>OffersList</title>
<h1>Offers / { data.length } total</h1>
{ data.map((offer: any) => (
<li key={ offer.id }>
<div id="offer">
<h2>{ offer.title }</h2>
<span>{ offer.price }</span>$
<p>{ offer.description }</p>
<OfferActions/>
</div>
</li>
)) }
</ul>
)
}
'use client'
export const OfferActions = () => {
return (
<>
<button onClick={ () => alert(123) } >Click me</button>
</>
)
}
"use client";
// src/offers/OfferActions.tsx
import { Fragment, jsx } from "react/jsx-runtime";
var OfferActions = () => {
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx("button", { onClick: () => alert(123), children: "Click me" }) });
};
export {
OfferActions
};
OfferActions.$$id="FkXBmVQRFwS54BoBzTjf8";OfferActions.$$typeof=Symbol.for("react.client.reference");
export const manifest = {
"FkXBmVQRFwS54BoBzTjf8": {
"id": "/dist/chunks/FkXBmVQRFwS54BoBzTjf8.js",
"name": "OfferActions",
"chunks": [],
"async": false
}
}
import './env.ts'
import { writeFileSync, existsSync, mkdirSync, readdirSync, unlinkSync, readFileSync } from 'fs'
import { build } from 'esbuild'
import { nanoid } from 'nanoid'
const manifest = { /*...*/ }
if(!existsSync('./dist/chunks')) mkdirSync('./dist/chunks', { recursive: true })
else for(const entry of readdirSync('./dist/chunks')) unlinkSync(`./dist/chunks/${ entry }`)
build({
entryPoints: [
'./src/client.ts'
],
outfile: './dist/client.min.js',
bundle: false,
minify: process.env.NODE_ENV === 'production',
packages: 'external',
jsx: 'automatic',
jsxImportSource: 'react',
target: 'esnext',
platform: 'browser',
format: 'esm'
})
build({
entryPoints: [
'./src/*/**'
],
bundle: true,
minify: process.env.NODE_ENV === 'production',
splitting: false,
treeShaking: true,
write: false,
outdir: './dist/chunks',
packages: 'external',
jsx: 'automatic',
legalComments: 'none',
target: 'esnext',
platform: 'browser',
format: 'esm'
}).then(result => {
result.outputFiles.forEach(entry => {
if(entry.text.startsWith(`"use client"`)){
const { text, path } = entry
const [ id, name ] = [ nanoid(), path.substring(path.lastIndexOf('\\') + 1, path.lastIndexOf('.')) ]
const absolute = `/dist/chunks/${ id }.js`
const output = `${ text + name }.$$id="${ id }";${ name }.$$typeof=Symbol.for("react.client.reference");`
manifest[id] = {
id: absolute,
name: name,
chunks: [],
async: true
}
writeFileSync(`.${ absolute }`, output, { encoding: 'utf-8' })
}
})
writeFileSync('./manifest.ts', `export const manifest = ${ JSON.stringify(manifest, null, 2) }`, { encoding: 'utf-8' })
})
import './env.ts'
import { createServer } from 'http'
import { renderToPipeableStream } from 'react-server-dom-webpack/server'
import { Layout } from './src/Layout.tsx'
import { OffersLayout } from './src/offers/OffersLayout.tsx'
import { manifest } from './manifest.ts'
const serializer = createServer(async(request, response) => {
// @ts-ignore
const url = new URL(request.url, `http://${ request.headers.host }`)
renderToPipeableStream(
<Layout>
<OffersLayout search={ url.searchParams.get('search') ?? '' }/>
</Layout>, manifest, {
bootstrapScriptContent: `window.__webpack_require__ = (x) => import(x)`
}
).pipe(response)
})
serializer.listen(4000, () => {
console.log('Watching RSC...')
})
import './env.ts'
import './bundler.ts'
import { createServer } from 'http'
import { readFile } from 'fs/promises'
import { renderToPipeableStream } from 'react-dom/server'
import { Layout } from './src/Layout.tsx'
import { OffersLayout } from './src/offers/OffersLayout.tsx'
const server = createServer(async(request, response) => {
if(request.method === 'GET'){
// @ts-ignore
const url = new URL(request.url, `http://${ request.headers.host }`)
if(url.pathname.startsWith('/favicon.ico')){
response.writeHead(404, 'Not Found').end()
} else if(url.pathname.startsWith('/dist')){
readFile(`.${ url.pathname }`).then(raw => {
response.writeHead(200, {
'content-type': 'text/javascript'
}).end(raw)
}).catch(() => {
response.writeHead(404, 'Not Found').end()
})
} else if(url.searchParams.has('payload')){
fetch('http://127.0.0.1:4000').then(r => r.text()).then(raw => (
response.writeHead(200, {
'content-type': 'text/plain'
}).end(raw)
))
} else {
renderToPipeableStream(
<Layout>
<OffersLayout search={ url.searchParams.get('search') ?? '' }/>
</Layout>, {
bootstrapScriptContent: `window.__webpack_require__ = (x) => import(x)`
}
).pipe(response)
}
} else {
response.writeHead(418, `I'm a teapot `).end()
}
})
server.listen(3000, () => {
console.log('Listening...')
})