Last active
May 3, 2026 11:29
-
-
Save grayfallstown/16a26b1c1148c2e2a94dd1d35e3cb5d6 to your computer and use it in GitHub Desktop.
self contained index.html with svelte app being compiled via cdn at page load. Single file svelte apps without build process for rapid llm prototyping
This file contains hidden or 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
| <!doctype html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <title>Svelte 5 Browser Compiler Barebones Readable Robust</title> | |
| <script type="importmap"> | |
| { | |
| "imports": { | |
| "svelte": "https://esm.sh/svelte@5", | |
| "svelte/": "https://esm.sh/svelte@5/" | |
| } | |
| } | |
| </script> | |
| <style> | |
| body { font-family: system-ui, sans-serif; margin: 2rem; } | |
| .boot-error { white-space: pre-wrap; color: #b00020; } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="app">Loading...</div> | |
| <!-- | |
| READABLE VIRTUAL FILES, NO BASE64 | |
| Rules for LLMs and humans who still want readable code: | |
| ✅ Put virtual files into <textarea hidden data-file="...">. | |
| ✅ Svelte <script>...</script> blocks are safe inside the textarea. | |
| ✅ Use uppercase component names: <Greet /> not <greet />. | |
| ✅ Relative imports work for .svelte and .js files: import X from "./X.svelte"; | |
| ✅ If your virtual Svelte code itself contains a closing textarea tag: | |
| Do NOT write a raw </textarea> inside the virtual file. | |
| Write </textarea> instead. | |
| The browser decodes it back to </textarea> when reading el.value. | |
| ❌ Do not use <template>: the HTML parser breaks Svelte syntax like <Greet {name} />. | |
| ❌ Do not use <script type="text/plain">: an inner </script> still closes the outer script block. | |
| ❌ Do not use base64 files when an LLM without tool access should edit this. | |
| --> | |
| <!-- Virtual file: ./src/utils.js --> | |
| <textarea hidden data-file="./src/utils.js"> | |
| export function cleanName(value) { | |
| return String(value || "").trim() || "World"; | |
| } | |
| </textarea> | |
| <!-- Virtual file: ./src/Greet.svelte --> | |
| <textarea hidden data-file="./src/Greet.svelte"> | |
| <script> | |
| let { name } = $props(); | |
| </script> | |
| <p>Hello {name}!</p> | |
| </textarea> | |
| <!-- Virtual file: ./src/EditorExample.svelte --> | |
| <textarea hidden data-file="./src/EditorExample.svelte"> | |
| <script> | |
| let text = $state("This component contains a real textarea."); | |
| </script> | |
| <label> | |
| Notes: | |
| <textarea bind:value={text}></textarea> | |
| </label> | |
| <p>Length: {text.length}</p> | |
| </textarea> | |
| <!-- Virtual file: ./src/App.svelte --> | |
| <textarea hidden data-file="./src/App.svelte"> | |
| <script> | |
| import Greet from "./Greet.svelte"; | |
| import EditorExample from "./EditorExample.svelte"; | |
| import { cleanName } from "./utils.js"; | |
| let name = $state("World"); | |
| </script> | |
| <input bind:value={name}> | |
| <Greet name={cleanName(name)} /> | |
| <hr> | |
| <EditorExample /> | |
| </textarea> | |
| <script type="module"> | |
| import { compile } from "https://esm.sh/svelte@5/compiler"; | |
| import { mount } from "svelte"; | |
| const files = new Map(); | |
| const compiled = new Map(); | |
| for (const el of document.querySelectorAll("textarea[data-file]")) { | |
| files.set(normalizePath(el.dataset.file), el.value.trim()); | |
| } | |
| function normalizePath(path) { | |
| const raw = String(path).replace(/\\/g, "/").trim(); | |
| const parts = []; | |
| for (const part of raw.split("/")) { | |
| if (!part || part === ".") continue; | |
| if (part === "..") parts.pop(); | |
| else parts.push(part); | |
| } | |
| return "./" + parts.join("/"); | |
| } | |
| function dirname(path) { | |
| const normalized = normalizePath(path); | |
| const index = normalized.lastIndexOf("/"); | |
| return index <= 1 ? "." : normalized.slice(0, index); | |
| } | |
| function resolveImport(fromFile, specifier) { | |
| if (!specifier.startsWith(".")) return specifier; | |
| return normalizePath(dirname(fromFile) + "/" + specifier); | |
| } | |
| async function compileVirtualModule(filePath) { | |
| const normalizedPath = normalizePath(filePath); | |
| if (compiled.has(normalizedPath)) { | |
| return compiled.get(normalizedPath); | |
| } | |
| const source = files.get(normalizedPath); | |
| if (!source) { | |
| throw new Error("Virtual file not found: " + normalizedPath); | |
| } | |
| let js; | |
| if (normalizedPath.endsWith(".svelte")) { | |
| js = compile(source, { | |
| filename: normalizedPath, | |
| generate: "client", | |
| dev: true, | |
| css: "injected" | |
| }).js.code; | |
| } else { | |
| js = source; | |
| } | |
| js = await rewriteRelativeImports(js, normalizedPath); | |
| const url = URL.createObjectURL(new Blob([js], { | |
| type: "text/javascript" | |
| })); | |
| compiled.set(normalizedPath, url); | |
| return url; | |
| } | |
| async function rewriteRelativeImports(js, fromFile) { | |
| const replacements = []; | |
| // import X from "./X.js"; | |
| // import { x } from "./x.js"; | |
| // import * as x from "./x.js"; | |
| const fromImportRegex = /import\s+([\s\S]*?)\s+from\s+["']([^"']+)["'];?/g; | |
| for (const match of js.matchAll(fromImportRegex)) { | |
| const [fullImport, importClause, specifier] = match; | |
| if (!specifier.startsWith(".")) continue; | |
| const childUrl = await compileVirtualModule(resolveImport(fromFile, specifier)); | |
| replacements.push({ | |
| from: fullImport, | |
| to: `import ${importClause} from "${childUrl}";` | |
| }); | |
| } | |
| // import "./side-effect.js"; | |
| const sideEffectImportRegex = /import\s+["']([^"']+)["'];?/g; | |
| for (const match of js.matchAll(sideEffectImportRegex)) { | |
| const [fullImport, specifier] = match; | |
| if (!specifier.startsWith(".")) continue; | |
| const childUrl = await compileVirtualModule(resolveImport(fromFile, specifier)); | |
| replacements.push({ | |
| from: fullImport, | |
| to: `import "${childUrl}";` | |
| }); | |
| } | |
| // export { x } from "./x.js"; | |
| // export * from "./x.js"; | |
| const exportFromRegex = /export\s+([\s\S]*?)\s+from\s+["']([^"']+)["'];?/g; | |
| for (const match of js.matchAll(exportFromRegex)) { | |
| const [fullExport, exportClause, specifier] = match; | |
| if (!specifier.startsWith(".")) continue; | |
| const childUrl = await compileVirtualModule(resolveImport(fromFile, specifier)); | |
| replacements.push({ | |
| from: fullExport, | |
| to: `export ${exportClause} from "${childUrl}";` | |
| }); | |
| } | |
| for (const replacement of replacements) { | |
| js = js.replace(replacement.from, replacement.to); | |
| } | |
| return js; | |
| } | |
| try { | |
| const appUrl = await compileVirtualModule("./src/App.svelte"); | |
| const appModule = await import(appUrl); | |
| document.querySelector("#app").textContent = ""; | |
| mount(appModule.default, { | |
| target: document.querySelector("#app") | |
| }); | |
| } catch (error) { | |
| const app = document.querySelector("#app"); | |
| app.className = "boot-error"; | |
| app.textContent = | |
| "Svelte browser prototype crashed.\n\n" + | |
| String(error.stack || error); | |
| console.error(error); | |
| } | |
| </script> | |
| </body> | |
| </html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment