-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
57 additions
and
68 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,58 +1,18 @@ | ||
import { normalize } from "https://deno.land/[email protected]/path/mod.ts"; | ||
import { format } from "npm:prettier"; | ||
|
||
// dummy value to put the var name in scope | ||
const component_path = ""; | ||
|
||
// this function is stringified inline in the page | ||
// putting it here gives us type safety etc | ||
export const hydrate_island = async (target: Element) => { | ||
try { | ||
const name = target.getAttribute("name"); | ||
const props = JSON.parse(target.getAttribute("props") ?? "{}"); | ||
const load = performance.now(); | ||
|
||
const Component = | ||
(await import(component_path + name + ".island.js")).default; | ||
console.group(name); | ||
console.info( | ||
`Loaded in %c${Math.round((performance.now() - load) * 1000) / 1000}ms`, | ||
"color: orange", | ||
); | ||
|
||
const hydrate = performance.now(); | ||
new Component({ target, props, hydrate: true }); | ||
target.setAttribute("foraged", ""); | ||
|
||
console.info( | ||
`Hydrated in %c${ | ||
Math.round((performance.now() - hydrate) * 1000) / 1000 | ||
}ms%c with`, | ||
"color: orange", | ||
"color: reset", | ||
props, | ||
); | ||
console.groupEnd(); | ||
} catch (_) { | ||
console.error(_); | ||
} | ||
}; | ||
|
||
interface TemplateOptions { | ||
css: string; | ||
head: string; | ||
html: string; | ||
hydrator: string; | ||
} | ||
const template = ({ css, head, html, hydrator }: TemplateOptions) => ` | ||
const template = ({ css, head, html }: TemplateOptions) => ` | ||
<!DOCTYPE html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="UTF-8" /> | ||
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||
${head} | ||
<script type="module">${hydrator}</script> | ||
<style>${css}</style> | ||
</head> | ||
<body> | ||
|
@@ -61,23 +21,15 @@ const template = ({ css, head, html, hydrator }: TemplateOptions) => ` | |
</html> | ||
`; | ||
|
||
const island_hydrator = (base = "") => ` | ||
const component_path = "${base}"; | ||
const hydrate_island = ${hydrate_island.toString()}; | ||
document.querySelectorAll("one-claw[name]").forEach(hydrate_island); | ||
`; | ||
|
||
export const get_route_html = ({ html, css, head, base_path }: { | ||
export const get_route_html = ({ html, css, head }: { | ||
html: string; | ||
css: string; | ||
head: string; | ||
base_path?: string; | ||
}) => { | ||
const page = template({ | ||
css, | ||
head, | ||
html, | ||
hydrator: island_hydrator(normalize(`/${base_path}/components/`)), | ||
}); | ||
|
||
try { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,26 @@ | ||
import { normalize } from "https://deno.land/[email protected]/path/mod.ts"; | ||
import type { Plugin } from "https://deno.land/x/[email protected]/mod.js"; | ||
import { compile, preprocess } from "npm:svelte/compiler"; | ||
|
||
const filter = /\.svelte$/; | ||
const name = "mononykus/svelte-islands"; | ||
|
||
export const island_wrapper = (mode: "ssr" | "dom", dir: string): Plugin => ({ | ||
export const island_wrapper = ( | ||
mode: "ssr" | "dom", | ||
dir: string, | ||
base_path: string, | ||
): Plugin => ({ | ||
name, | ||
setup(build) { | ||
build.onLoad({ filter }, async ({ path }) => { | ||
const filename = path.split(dir).at(-1) ?? "Undefined.svelte"; | ||
const source = await Deno.readTextFile(path); | ||
const island = filename.match(/\/(\w+).island.svelte/); | ||
|
||
const processed = island && mode === "ssr" | ||
? (await preprocess(source, { | ||
let processed = source; | ||
|
||
if (island && mode === "ssr") { | ||
const preprocessed = await preprocess(source, { | ||
markup: ({ content }) => { | ||
let processed = content; | ||
const non_html = content.match( | ||
|
@@ -25,19 +32,29 @@ export const island_wrapper = (mode: "ssr" | "dom", dir: string): Plugin => ({ | |
for (const el of non_html) { | ||
html = html.replace(el, ""); | ||
} | ||
processed = non_html.join("") + | ||
`<one-claw name="${ | ||
island[1] | ||
}" props={JSON.stringify($$props)} style="display:contents;">${html.trim()}</one-claw>`; | ||
|
||
const src = filename.replace( | ||
"components/", | ||
normalize(`/${base_path}/components/`), | ||
).replace(/svelte$/, "js"); | ||
|
||
processed = non_html.join("") + ` | ||
<svelte:head> | ||
<script type="module" src="${src}" /> | ||
</svelte:head> | ||
<one-claw name="${ | ||
island[1] | ||
}" props={JSON.stringify($$props)} style="display:contents;">${html.trim()}</one-claw>`; | ||
} | ||
return ({ | ||
code: processed, | ||
}); | ||
}, | ||
})).code | ||
: source; | ||
}); | ||
processed = preprocessed.code; | ||
} | ||
|
||
const { js: { code } } = compile(processed, { | ||
let { js: { code } } = compile(processed, { | ||
generate: mode, | ||
css: "injected", | ||
cssHash: ({ hash, css }) => `◖${hash(css)}◗`, | ||
|
@@ -46,6 +63,28 @@ export const island_wrapper = (mode: "ssr" | "dom", dir: string): Plugin => ({ | |
filename, | ||
}); | ||
|
||
if (island && mode === "dom") { | ||
const name = island[1]; | ||
code += ` | ||
const load = performance.now(); | ||
const targets = document.querySelectorAll("one-claw[name='${name}']"); | ||
for (const target of targets) { | ||
const props = JSON.parse(target.getAttribute("props") ?? "{}"); | ||
new ${name}_island({ target, props, hydrate: true }); | ||
console.info( | ||
\`Hydrated %c${name}%c in %c\${ | ||
Math.round((performance.now() - load) * 1000) / 1000 | ||
\}ms%c with\`, | ||
"color: orange", | ||
"color: reset", | ||
"color: orange", | ||
"color: reset", | ||
props, | ||
); | ||
} | ||
`; | ||
} | ||
|
||
return ({ contents: code }); | ||
}); | ||
}, | ||
|