Skip to content

Commit

Permalink
feat: support client reorder attribute
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanPiercey committed Apr 15, 2024
1 parent 159e273 commit 3514bad
Show file tree
Hide file tree
Showing 10 changed files with 173 additions and 21 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,19 @@ Optional `style` attribute which works the same way as [Marko style attribute](h
<micro-frame src="..." style=["display:block", null, { marginRight: 16 }]/>
```

## `client-reorder`

Similar to the [<await> tag client-reorder attribute](https://markojs.com/docs/core-tags/#await) this tells the micro-frame to avoid blocking content later in the document.

> Note when this is used the micro-frame will be buffered instead of streamed and inserted once it's ready.
```marko
<!--
This example will disable the default 30s timeout.
-->
<micro-frame src="..." timeout=0/>
```

# Communicating between host and child

Communicating with the embedded application happens primarily in one of two ways, either you want to do a full reload of and get new HTML, or you want to orchestrate a client side rendered update.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<div>
Host app
</div>
<div
data-src="embed"
id="GENERATED-0"
>
<span
id="GENERATED-1"
>
Loading...
</span>
</div>
<div>
Host footer
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<div>
Host app
</div>
<div
data-src="embed"
id="GENERATED-0"
>
<span
id="GENERATED-1"
>
Loading...
</span>
</div>
<div>
Host footer
</div>
<div
id="GENERATED-2"
style="display:none"
>
<div>
<h1>
Hello
</h1>
<h2>
World
</h2>
</div>
</div>
<script>
function $af(d,a,e,l,g,h,k,b,f,c){c=$af;if(a&&!c[a])(c[a+="$"]||(c[a]=[])).push(d);else{e=document;l=e.getElementById("af"+d);g=e.getElementById("afph"+d);h=e.createDocumentFragment();k=l.childNodes;b=0;for(f=k.length;b&lt;f;b++)h.appendChild(k.item(0));g&&g.parentNode.replaceChild(h,g);c[d]=1;if(a=c[d+"$"])for(b=0,f=a.length;b&lt;f;b++)c(a[b])}};$af(0)
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<div>
Host app
</div>
<div
data-src="embed"
id="GENERATED-0"
>
<div>
<h1>
Hello
</h1>
<h2>
World
</h2>
</div>
</div>
<div>
Host footer
</div>
<div
id="GENERATED-1"
style="display:none"
/>
<script>
function $af(d,a,e,l,g,h,k,b,f,c){c=$af;if(a&&!c[a])(c[a+="$"]||(c[a]=[])).push(d);else{e=document;l=e.getElementById("af"+d);g=e.getElementById("afph"+d);h=e.createDocumentFragment();k=l.childNodes;b=0;for(f=k.length;b&lt;f;b++)h.appendChild(k.item(0));g&&g.parentNode.replaceChild(h,g);c[d]=1;if(a=c[d+"$"])for(b=0,f=a.length;b&lt;f;b++)c(a[b])}};$af(0)
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { wait } from "../../../../../__tests__/queue";
<esbuild-assets/>

<div>
<h1>Hello</h1>
<await(wait())>
<@then>
<h2>World</h2>
</@then>
</await>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simple Example</title>
<esbuild-assets/>
</head>
<body>
<div>Host app</div>
<micro-frame client-reorder src="embed">
<@loading>
Loading...
</@loading>
</micro-frame>
<div>Host footer</div>
</body>
</html>
5 changes: 5 additions & 0 deletions src/components/micro-frame/__tests__/server.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ describe(
fixture(path.join(__dirname, "fixtures/ssr-stream-loading"))
);

describe(
"ssr client-reorder loading",
fixture(path.join(__dirname, "fixtures/ssr-stream-client-reorder"))
);

describe("csr 404", fixture(path.join(__dirname, "fixtures/csr-404")));

describe("ssr 404", fixture(path.join(__dirname, "fixtures/ssr-404")));
Expand Down
7 changes: 7 additions & 0 deletions src/components/micro-frame/marko-tag.json
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@
}
]
},
"@client-reorder": {
"autocomplete": [
{
"description": "Wether the micro-frame should should block/buffer content below the micro-frame."
}
]
},
"@class": {
"autocomplete": [
{
Expand Down
51 changes: 35 additions & 16 deletions src/node_modules/@internal/micro-frame-component/node.marko
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ static const strictSSL = process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0";
static function internalFetch(url, options) {
return fetch(url, { ...options, ca, cachePath, strictSSL });
}
static async function fetchStream(input, out) {
static async function fetchBody(input, out, buffer) {
const global = out.global;
let origin;
let incomingHeaders;
Expand Down Expand Up @@ -79,6 +79,8 @@ static async function fetchStream(input, out) {
if (!res.ok) throw new Error(res.statusText);
if (buffer) return res.text();
if (!res.body || !res.body[Symbol.asyncIterator]) {
throw new Error("Response body must be a stream.");
}
Expand All @@ -87,25 +89,42 @@ static async function fetchStream(input, out) {
}

<div id=component.id class=input.class style=input.style data-src=input.src>
<if(input.loading)>
<${input.loading}/>
<!-- output a comment used as a marker to detect where the loading content starts so it can be removed -->$!{`<!--${component.id}-->`}
</if>
<!--
We put the streamed html in a preserved fragment.
This allows Marko to avoid diffing that section.
-->
$ out.bf("@_", component, true);
<await(fetchStream(input, out)) timeout=input.timeout catch=input.catch>
<@then|iter|>
<ssr-wait
id=component.id
iter=iter
timeout=input.timeout
loading=input.loading
catch=input.catch
/>
</@then>
</await>
<if(input.clientReorder)>
<await(fetchBody(input, out, true))
client-reorder
placeholder=input.loading
timeout=input.timeout
catch=input.catch
>
<@then|html|>
$!{html}
</@then>
</await>
</if>
<else>
<if(input.loading)>
<${input.loading}/>
<!-- output a comment used as a marker to detect where the loading content starts so it can be removed -->$!{`<!--${component.id}-->`}
</if>
<await(fetchBody(input, out, false))
timeout=input.timeout
catch=input.catch
>
<@then|iter|>
<ssr-wait
id=component.id
iter=iter
timeout=input.timeout
loading=input.loading
catch=input.catch
/>
</@then>
</await>
</else>
$ out.ef();
</div>
15 changes: 10 additions & 5 deletions src/node_modules/@internal/micro-frame-component/web.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,22 @@ export = {
? this.input.fetch(this.src, options, fetch)
: fetch(this.src, options));
if (!res.ok) throw new Error(res.statusText);
const reader = res.body!.getReader();
const decoder = new TextDecoder();
writable = getWritableDOM(
this.el,
// references the start of the preserved Marko fragment.
this.el.lastChild!.previousSibling
);

let value: Uint8Array | undefined;
while ((value = (await reader.read()).value)) {
writable.write(decoder.decode(value));
if (this.input.clientReorder) {
writable.write(await res.text());
} else {
const reader = res.body!.getReader();
const decoder = new TextDecoder();

let value: Uint8Array | undefined;
while ((value = (await reader.read()).value)) {
writable.write(decoder.decode(value));
}
}

await writable.close();
Expand Down

0 comments on commit 3514bad

Please sign in to comment.