From 330f7d9d4d6f3355b628879f340f1fb78f4663dc Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Mon, 18 Apr 2022 17:38:09 -0400 Subject: [PATCH 1/3] differentiate title and ports for examples --- ssg.js | 2 +- ssr.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ssg.js b/ssg.js index c954a144..ebe90117 100644 --- a/ssg.js +++ b/ssg.js @@ -37,7 +37,7 @@ for (const entry of entries.filter(entry => entry.endsWith('.js'))) { - WCC + WCC - SSG ${ eagerJs.map(script => { diff --git a/ssr.js b/ssr.js index 7d72e56c..ca5e9f71 100644 --- a/ssr.js +++ b/ssr.js @@ -3,6 +3,7 @@ import fastifyStatic from 'fastify-static'; import { renderToString } from './lib/wcc.js'; const app = fastify({ logger: true }); +const port = 3000; app.register(fastifyStatic, { root: new URL('./www', import.meta.url).pathname, @@ -47,7 +48,7 @@ app.get('/*', async (request, reply) => { - WCC + WCC - SSR ${ eagerJs.map(script => { return `` @@ -112,7 +113,7 @@ app.get('/*', async (request, reply) => { const start = async () => { try { - await app.listen(3000) + await app.listen(port) } catch (err) { app.log.error(err) process.exit(1) From 4c1a27d03dd21669750cb5f271be8da420e025f1 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Mon, 18 Apr 2022 17:40:05 -0400 Subject: [PATCH 2/3] streaming WIP --- package.json | 1 + ssr-streaming.js | 216 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 ssr-streaming.js diff --git a/package.json b/package.json index 03c5567f..09f32115 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "ssg": "node ./ssg.js", "ssg:serve": "node ./ssg.js && http-server ./dist", "ssr": "nodemon --watch lib --watch www ./ssr.js", + "streaming": "nodemon --watch lib --watch www ./ssr-streaming.js", "start": "yarn ssr" }, "dependencies": { diff --git a/ssr-streaming.js b/ssr-streaming.js new file mode 100644 index 00000000..7adbb792 --- /dev/null +++ b/ssr-streaming.js @@ -0,0 +1,216 @@ +import fastify from 'fastify'; +import fastifyStatic from 'fastify-static'; +import path from 'path'; +import { Readable } from 'stream'; +import { renderToString } from './lib/wcc.js'; + +const app = fastify({ logger: true }); +const port = 3001; + +app.register(fastifyStatic, { + root: new URL('./www', import.meta.url).pathname, + prefix: '/www' +}) +app.register(fastifyStatic, { + root: new URL('./lib', import.meta.url).pathname, + prefix: '/lib', + decorateReply: false +}) + +function* renderPageStreaming(html, assets, data) { + const lazyJs = []; + const eagerJs = []; + + for(const asset in assets) { + const a = assets[asset]; + + a.tagName = asset; + + if(a.moduleURL.href.endsWith('.js')) { + if(a.hydrate === 'lazy') { + lazyJs.push(a) + } else { + eagerJs.push(a) + } + } + } + + yield ` + + + + WCC - Streaming (SSR) + `; + yield ` + ${ + eagerJs.map(script => { + return `` + }).join('\n') + } + `; + yield ` + ${ + lazyJs.map(script => { + return ` + + ` + }).join('\n') + } + `; + + yield ` + + + ` + + yield ` + ${html} + ` + + yield ` + + + `; +} + +async function renderPage(html, assets, data) { + const lazyJs = []; + const eagerJs = []; + + for(const asset in assets) { + const a = assets[asset]; + + a.tagName = asset; + + if(a.moduleURL.href.endsWith('.js')) { + if(a.hydrate === 'lazy') { + lazyJs.push(a) + } else { + eagerJs.push(a) + } + } + } + + return ` + + + + WCC - Streaming SSR + ${ + eagerJs.map(script => { + return `` + }).join('\n') + } + + ${ + lazyJs.map(script => { + return ` + + ` + }).join('\n') + } + + + ${html} + + + `; +} + +app.get('/*', async (request, reply) => { + const { url } = request; + + if(path.extname(url) === '') { + const pageRoute = url === '/' ? '/index' : url; + const entryPoint = `./www/pages${pageRoute}.js` + + console.debug({ url }); + console.debug({ pageRoute }) + console.debug({ entryPoint }) + + const { html, assets } = await renderToString(new URL(entryPoint, import.meta.url), false); + // const contents = renderPage(html, assets); + // const chunks = []; + + // for(const chunk of contents) { + // chunks.push(chunk) + // } + + // console.debug({ chunks }); + + // const readable = Readable.from(chunks) + + // console.debug({ readable }); + + // TODO - related to favicon? + // Promise may not be fulfilled with 'undefined' when statusCode is not 204", + // "stack":"FastifyError: Promise may not be fulfilled with 'undefined' when statusCode is not 204 + reply + .header('Content-Type', 'text/html; charset=utf-8') + .header('Transfer-Encoding', 'chunked') + .send(Readable.from((await renderPage(html, assets)))); + } +}); + +const start = async () => { + try { + await app.listen(port) + } catch (err) { + app.log.error(err) + process.exit(1) + } +} + +start() \ No newline at end of file From 172747191897e61aa6a59566580ae1866d265255 Mon Sep 17 00:00:00 2001 From: Owen Buckley Date: Thu, 21 Apr 2022 08:45:33 -0400 Subject: [PATCH 3/3] refactoring and testing --- ssr-streaming.js | 547 ++++++++++++++++++++++++++++++++++------- ssr.js | 617 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 1079 insertions(+), 85 deletions(-) diff --git a/ssr-streaming.js b/ssr-streaming.js index 7adbb792..ee64925a 100644 --- a/ssr-streaming.js +++ b/ssr-streaming.js @@ -1,7 +1,7 @@ import fastify from 'fastify'; import fastifyStatic from 'fastify-static'; import path from 'path'; -import { Readable } from 'stream'; +import stream, { Readable } from 'stream'; import { renderToString } from './lib/wcc.js'; const app = fastify({ logger: true }); @@ -17,7 +17,7 @@ app.register(fastifyStatic, { decorateReply: false }) -function* renderPageStreaming(html, assets, data) { +function* renderPageStreaming(html, assets) { const lazyJs = []; const eagerJs = []; @@ -44,7 +44,10 @@ function* renderPageStreaming(html, assets, data) { yield ` ${ eagerJs.map(script => { - return `` + return ` + + + ` }).join('\n') } `; @@ -87,83 +90,459 @@ function* renderPageStreaming(html, assets, data) { yield ` +

+++++++++++++Start of Stream++++++++++++++

+ ${html} +

+++++++++++++Start of Stream++++++++++++++

` yield ` ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} ` yield ` - - - `; -} - -async function renderPage(html, assets, data) { - const lazyJs = []; - const eagerJs = []; - - for(const asset in assets) { - const a = assets[asset]; - - a.tagName = asset; - - if(a.moduleURL.href.endsWith('.js')) { - if(a.hydrate === 'lazy') { - lazyJs.push(a) - } else { - eagerJs.push(a) - } - } - } - - return ` - - - - WCC - Streaming SSR - ${ - eagerJs.map(script => { - return `` - }).join('\n') - } - - ${ - lazyJs.map(script => { - return ` - - ` - }).join('\n') - } - - - ${html} + yield ` `; @@ -181,26 +560,26 @@ app.get('/*', async (request, reply) => { console.debug({ entryPoint }) const { html, assets } = await renderToString(new URL(entryPoint, import.meta.url), false); - // const contents = renderPage(html, assets); - // const chunks = []; - // for(const chunk of contents) { - // chunks.push(chunk) - // } + // https://github.com/fastify/fastify/issues/805#issuecomment-369172154 + const buffer = new stream.Readable(); + buffer._read = ()=>{}; - // console.debug({ chunks }); + for(const data of renderPageStreaming(html, assets)) { + buffer.push(data); + } - // const readable = Readable.from(chunks) + buffer.push(null) - // console.debug({ readable }); + reply.type('text/html').send(buffer) // TODO - related to favicon? // Promise may not be fulfilled with 'undefined' when statusCode is not 204", // "stack":"FastifyError: Promise may not be fulfilled with 'undefined' when statusCode is not 204 - reply - .header('Content-Type', 'text/html; charset=utf-8') - .header('Transfer-Encoding', 'chunked') - .send(Readable.from((await renderPage(html, assets)))); + // reply + // .header('Content-Type', 'text/html; charset=utf-8') + // .header('Transfer-Encoding', 'chunked') + // .send(Readable.from(await renderPage(html, assets))); } }); diff --git a/ssr.js b/ssr.js index ca5e9f71..4ed166c7 100644 --- a/ssr.js +++ b/ssr.js @@ -91,7 +91,622 @@ app.get('/*', async (request, reply) => { - ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html} + ${html}