├── .gitignore ├── README.md ├── package-lock.json ├── package.json └── src ├── server.js ├── templates └── list.js └── utils.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .wrangler/ 3 | wrangler.toml 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Out of order streaming without JavaScript 2 | 3 | A technique for streaming HTML out of order without JavaScript. [See the blog post](https://lamplightdev.com/blog/2024/01/10/streaming-html-out-of-order-without-javascript/) for more information. 4 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ooo", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "@hono/node-server": "^1.4.0", 9 | "hono": "^3.12.0", 10 | "swtl": "^0.0.22" 11 | }, 12 | "engines": { 13 | "node": ">=18" 14 | } 15 | }, 16 | "node_modules/@hono/node-server": { 17 | "version": "1.4.0", 18 | "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.4.0.tgz", 19 | "integrity": "sha512-bhDkhldW7w9VgjrX0gG1vJ2YyvTxFWd5WG9nHjSR4UauhVECQZC3qy7mVVuQ054I5NWhKttHfKzYfoPzmUzAjw==", 20 | "engines": { 21 | "node": ">=18.14.1" 22 | } 23 | }, 24 | "node_modules/hono": { 25 | "version": "3.12.0", 26 | "resolved": "https://registry.npmjs.org/hono/-/hono-3.12.0.tgz", 27 | "integrity": "sha512-UPEtZuLY7Wo7g0mqKWSOjLFdT8t7wJ60IYEcxKl3AQNU4u+R2QqU2fJMPmSu24C+/ag20Z8mOTQOErZzK4DMvA==", 28 | "engines": { 29 | "node": ">=16.0.0" 30 | } 31 | }, 32 | "node_modules/swtl": { 33 | "version": "0.0.22", 34 | "resolved": "https://registry.npmjs.org/swtl/-/swtl-0.0.22.tgz", 35 | "integrity": "sha512-dw2QKaDCCx5yp1+UhDNi4OhwaYfgSCmrK26ckD9PeXn5tsES11+ETMgYX0d2RzTggxFAlvmTaPisrD188P2FOw==" 36 | } 37 | }, 38 | "dependencies": { 39 | "@hono/node-server": { 40 | "version": "1.4.0", 41 | "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.4.0.tgz", 42 | "integrity": "sha512-bhDkhldW7w9VgjrX0gG1vJ2YyvTxFWd5WG9nHjSR4UauhVECQZC3qy7mVVuQ054I5NWhKttHfKzYfoPzmUzAjw==" 43 | }, 44 | "hono": { 45 | "version": "3.12.0", 46 | "resolved": "https://registry.npmjs.org/hono/-/hono-3.12.0.tgz", 47 | "integrity": "sha512-UPEtZuLY7Wo7g0mqKWSOjLFdT8t7wJ60IYEcxKl3AQNU4u+R2QqU2fJMPmSu24C+/ag20Z8mOTQOErZzK4DMvA==" 48 | }, 49 | "swtl": { 50 | "version": "0.0.22", 51 | "resolved": "https://registry.npmjs.org/swtl/-/swtl-0.0.22.tgz", 52 | "integrity": "sha512-dw2QKaDCCx5yp1+UhDNi4OhwaYfgSCmrK26ckD9PeXn5tsES11+ETMgYX0d2RzTggxFAlvmTaPisrD188P2FOw==" 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "engines": { 3 | "node": ">=18" 4 | }, 5 | "type": "module", 6 | "scripts": { 7 | "start": "node server.js" 8 | }, 9 | "dependencies": { 10 | "@hono/node-server": "^1.4.0", 11 | "hono": "^3.12.0", 12 | "swtl": "^0.0.22" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | import { Hono } from 'hono'; 2 | import { stream } from 'hono/streaming'; 3 | import { render } from 'swtl'; 4 | 5 | import { createReadableStreamFromAsyncGenerator } from './utils.js'; 6 | import { template as listTemplate } from './templates/list.js'; 7 | 8 | const app = new Hono(); 9 | 10 | app.get('/', (ctx) => { 11 | return stream(ctx, async (stream) => { 12 | ctx.res.headers.set('Content-Type', 'text/html'); 13 | await stream.pipe( 14 | createReadableStreamFromAsyncGenerator(render(listTemplate())) 15 | ); 16 | }); 17 | }); 18 | 19 | export default app; 20 | -------------------------------------------------------------------------------- /src/templates/list.js: -------------------------------------------------------------------------------- 1 | import { html } from 'swtl'; 2 | import { 3 | renderInResolvedOrder, 4 | delayed, 5 | globalStyles, 6 | appStyles, 7 | } from '../utils.js'; 8 | 9 | export const template = () => html` 10 | 11 |
12 | 13 | 14 |