Skip to content

Commit

Permalink
feat: Inject state at end of body
Browse files Browse the repository at this point in the history
  • Loading branch information
Fryuni committed Aug 19, 2024
1 parent 358dfed commit 8340b7c
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 74 deletions.
3 changes: 1 addition & 2 deletions packages/request-state/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@
"@inox-tools/utils": "workspace:^",
"astro-integration-kit": "catalog:",
"content-type": "catalog:",
"devalue": "catalog:",
"parse5-html-rewriting-stream": "catalog:"
"devalue": "catalog:"
},
"devDependencies": {
"@types/content-type": "catalog:",
Expand Down
74 changes: 24 additions & 50 deletions packages/request-state/src/runtime/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { defineMiddleware } from 'astro/middleware';
import { collectState } from './serverState.js';
import { parse } from 'content-type';
import { RewritingStream } from 'parse5-html-rewriting-stream';

export const onRequest = defineMiddleware(async (_, next) => {
const { getState, result } = await collectState(next);
Expand All @@ -14,56 +13,31 @@ export const onRequest = defineMiddleware(async (_, next) => {

if (mediaType !== 'text/html' && !mediaType.startsWith('text/html+')) return result;

async function render() {
const rewriter = new RewritingStream();
const newBody = result.body
?.pipeThrough(new TextDecoderStream())
.pipeThrough(injectState(getState))
.pipeThrough(new TextEncoderStream());

let injected = false;
return new Response(newBody, result);
});

rewriter.on('endTag', (tag) => {
if (!injected && tag.tagName === 'html') {
const state = getState();
if (state) {
rewriter.emitRaw(
`<script id="it-astro-state" type="application/json+devalue">${state}</script>`
);
function injectState(getState: () => string | false) {
let injected = false;
return new TransformStream({
transform(chunk, controller) {
if (!injected) {
const bodyCloseIndex = chunk.indexOf('</body>');
if (bodyCloseIndex > -1) {
const state = getState();
if (state) {
const stateScript = `<script id="it-astro-state" type="application/json+devalue">${state}</script>`;

chunk = chunk.slice(0, bodyCloseIndex) + stateScript + chunk.slice(bodyCloseIndex);
}
injected = true;
}
injected = true;
}

rewriter.emitEndTag(tag);
});

const writeStream = new WritableStream({
write(chunk) {
return new Promise<void>((resolve) => {
rewriter.write(chunk, () => {
resolve();
});
});
},
close() {
rewriter.end();
},
});

const readStream = new ReadableStream({
pull(controller) {
if (rewriter.closed) {
controller.close();
}

const chunk = rewriter.read();
controller.enqueue(chunk);
},
});

const blob = await result.blob();

return blob.stream().pipeThrough({
writable: writeStream,
readable: readStream,
});
}

return new Response(await render(), result);
});
controller.enqueue(chunk);
},
});
}
22 changes: 0 additions & 22 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 8340b7c

Please sign in to comment.