loading...",
- );
- t.assert.ok(resolve !== null), resolve();
- assertContinue(
- await iterator.next(),
- "",
- );
- t.assert.deepEqual(
- await iterator.next(),
- { done: true, value: {} },
- );
-});
diff --git a/suspense.ts b/suspense.ts
deleted file mode 100644
index ab6b52a941cbc199a36ec3640ec2ff3a22b4900c..0000000000000000000000000000000000000000
--- a/suspense.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-// TODO: revive the suspense renderer
-/**
- * This file implements out-of-order HTML streaming, mimicking the React
- * Suspense API. To use, place Suspense around an expensive async component
- * and render the page with 'renderStreaming'.
- *
- * Implementation of this article:
- * https://lamplightdev.com/blog/2024/01/10/streaming-html-out-of-order-without-javascript/
- *
- * I would link to an article from Next.js or React, but their examples
- * are too verbose and not informative to what they actually do.
- * @module
- */
-const userData = render.userData(() => null);
-
-export interface Input {
- children: render.Node;
- fallback?: render.Node;
-}
-
-/**
- * When used in combination with `Suspense.renderStreaming`, this component
- * displays `fallback` until the async `children` `Node` has resolved..
- *
- * return
- *
page shell
- *
- *
- *
- *
;
- */
-export function Suspense({ children, fallback }: Input): render.Node {
- const state = userData.get();
- if (!state) return children;
- if (state.nested) throw new Error(" cannot be nested");
- const parent = UNWRAP(render.current);
- const r = render.init(true, { [userData.key]: { nested: true } });
- const resolved = render.resolveNode(r, children);
- if (r.async == 0) return render.raw(resolved);
- const name = "suspended_" + ++state.nextId;
- state.nested = true;
- const ip: [render.ResolvedNode] = [
- render.resolvedElement(
- "slot",
- { name },
- fallback ? render.resolveNode(parent, fallback) : "",
- ),
- ];
- state.nested = false;
- r.asyncDone = () => {
- const rejections = r.rejections;
- if (rejections && rejections.length > 0) throw new Error("TODO");
- state.pushChunk?.(name, ip[0] = resolved);
- };
- return render.raw(ip);
-}
-
-// TODO: add a User-Agent parameter, which is used to determine if a
-// fallback path must be used.
-// - Before ~2024 needs to use a JS implementation.
-// - IE should probably bail out entirely.
-export async function* renderStreaming<
- T extends render.Addons = Record,
->(node: render.Node, addon: T = {} as T) {
- const {
- text: begin,
- addon: { [userData.key]: state, ...addonOutput },
- } = await render.async(node, {
- ...addon,
- [userData.key]: {
- nested: false,
- nextId: 0,
- completed: 0,
- pushChunk: () => {},
- } satisfies State as State,
- });
- if (state.nextId === 0) {
- yield begin;
- return addonOutput as unknown as T;
- }
- let resolve: (() => void) | null = null;
- let chunks: string[] = [];
- state.pushChunk = (slot, node) => {
- while (node.length === 1 && Array.isArray(node)) node = UNWRAP(node[0]);
- if (node[0] === render.kElement) {
- (node as render.ResolvedElement)[2].slot = slot;
- } else {
- node = render.resolvedElement("clover-suspense", {
- style: "display:contents",
- slot,
- }, node);
- }
- chunks.push(render.stringifyNode(node));
- resolve?.();
- };
- yield `${begin}`;
- do {
- await new Promise((done) => (resolve = done));
- yield* chunks;
- chunks = [];
- } while (state!.nextId < state!.completed);
- return addonOutput as unknown as T;
-}
-
-interface State {
- nested: boolean;
- nextId: number;
- completed: number;
- pushChunk(name: string, node: render.ResolvedNode): void;
-}
-
-export declare namespace Suspense {
- export type { Input };
-}
-
-import * as render from "lib/render.ts";