Skip to content

Commit

Permalink
feat: avoid fouc for dev mode page reloads (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanPiercey committed May 24, 2023
1 parent 06753c8 commit 29c2f40
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .changeset/beige-books-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@marko/vite": patch
---

Avoid FOUC for dev mode page reloads.
38 changes: 32 additions & 6 deletions src/components/vite.marko
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,35 @@ static function renderAssets(slot) {
const writtenEntries = (this[slotWrittenEntriesKey] = entries.length);
for (let i = lastWrittenEntry; i < writtenEntries; i++) {
const entry = entries[i];
const parts =
typeof __MARKO_MANIFEST__ === "object"
? __MARKO_MANIFEST__[entry]?.[slot]
: entry[slot];
let entry = entries[i];
if (typeof __MARKO_MANIFEST__ === "object") {
entry = __MARKO_MANIFEST__[entry] || {};
} else if (slot === "head") {
// In dev mode we have is a list entries of the top level modules that need to be imported.
// To avoid FOUC we will hide the page until all of these modules are loaded.
const { entries } = entry;
if (entries) {
let sep = "";
html += `<script${this.___viteInjectAttrs}>((root=document.documentElement)=>{`;
html += "root.style.visibility='hidden';";
html += "document.currentScript.remove();";
html += "Promise.allSettled([";
for (const id of entries) {
html += `${sep}import(${JSON.stringify(this.___viteBasePath + id)})`;
sep = ",";
}
html += "]).then(()=>{";
html += "root.style.visibility='';";
html +=
"if(root.getAttribute('style')==='')root.removeAttribute('style')";
html += "})})()</script>";
}
}
const parts = entry[slot];
if (parts) {
for (const part of parts) {
Expand Down Expand Up @@ -40,7 +64,9 @@ $ if (!out.global.___viteRenderAssets) {
<__flush_here_and_after__>
<if(input.base && !out.global.___flushedMBP)>
$ out.global.___flushedMBP = true;
$!{`<script${out.global.___viteInjectAttrs}>${out.global.___viteBaseVar}=${JSON.stringify(input.base)}</script>`}
$!{`<script${out.global.___viteInjectAttrs}>${
out.global.___viteBaseVar
}=${JSON.stringify(input.base)}</script>`}
</if>
$!{out.global.___viteRenderAssets(input.slot)}
</__flush_here_and_after__>
11 changes: 7 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -645,10 +645,13 @@ export default function markoPlugin(opts: Options = {}): vite.Plugin[] {
}

if (chunk?.type === "asset") {
browserManifest[entryId] = await generateDocManifest(
basePath,
chunk.source.toString()
);
browserManifest[entryId] = {
...(await generateDocManifest(
basePath,
chunk.source.toString()
)),
entries: undefined, // clear out entries for prod builds.
} as any;

delete bundle[chunkId];
} else {
Expand Down
15 changes: 9 additions & 6 deletions src/manifest-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import serialize from "./serializer";

type SerializedOrNull = null | ReturnType<typeof serialize>;
export interface DocManifest {
entries: string[];
"head-prepend": SerializedOrNull;
head: SerializedOrNull;
"body-prepend": SerializedOrNull;
Expand All @@ -25,6 +26,7 @@ export function generateDocManifest(
}

const htmlChildren = dom.find(isElement)!.childNodes;
const entries: string[] = [];
const headPrepend: Node[] = [];
const head: Node[] = [];
const bodyPrepend: Node[] = [];
Expand All @@ -49,10 +51,11 @@ export function generateDocManifest(
);

resolve({
"head-prepend": serializeOrNull(basePath, headPrepend),
head: serializeOrNull(basePath, head),
"body-prepend": serializeOrNull(basePath, bodyPrepend),
body: serializeOrNull(basePath, body),
entries,
"head-prepend": serializeOrNull(basePath, headPrepend, entries),
head: serializeOrNull(basePath, head, entries),
"body-prepend": serializeOrNull(basePath, bodyPrepend, entries),
body: serializeOrNull(basePath, body, entries),
});
})
);
Expand All @@ -67,8 +70,8 @@ export function generateInputDoc(entry: string) {
)}></script></body></html>`;
}

function serializeOrNull(basePath: string, nodes: Node[]) {
const result = serialize(basePath, nodes);
function serializeOrNull(basePath: string, nodes: Node[], entries: string[]) {
const result = serialize(basePath, nodes, entries);
if (result.length) {
return result;
}
Expand Down
13 changes: 9 additions & 4 deletions src/serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const voidElements = new Set([
export default function serialize(
basePath: string,
nodes: Node[],
entries: string[],
parts?: (string | InjectType)[]
) {
let curString = parts ? (parts.pop() as string) : "";
Expand Down Expand Up @@ -69,13 +70,17 @@ export default function serialize(
if (attr.value === "") {
curString += ` ${attr.name}`;
} else if (attr.name === urlAttr) {
const id = stripBasePath(basePath, attr.value).replace(/^\.\//, "");

if (tag.name === "script") {
entries.push(id);
}

curString += ` ${attr.name}="`;
parts.push(
curString,
InjectType.PublicPath,
stripBasePath(basePath, attr.value)
.replace(/"/g, "&#39;")
.replace(/^\.\//, "") + '"'
id.replace(/"/g, "&#39;") + '"'
);
curString = "";
} else {
Expand All @@ -87,7 +92,7 @@ export default function serialize(

if (tag.children.length) {
parts.push(curString);
serialize(basePath, tag.children, parts);
serialize(basePath, tag.children, entries, parts);
curString = parts.pop() as string;
}

Expand Down

0 comments on commit 29c2f40

Please sign in to comment.