From e226f0c7db13083326b30e59cb5a2ab1ae12d7b9 Mon Sep 17 00:00:00 2001 From: sukuwc Date: Wed, 14 Oct 2026 15:39:11 +0200 Subject: [PATCH] Inject extracted CSS into webcomponent JS in watch mode Vite attaches the extracted CSS to a different output format in --watch, so components.js lost the grid-uikit splitter theme and the splitter could not be dragged in the editor. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01619XN25FUTzqut4YbzZPQr --- vite.config.webcomponent.ts | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/vite.config.webcomponent.ts b/vite.config.webcomponent.ts index 2e0bc5d..b6e2f34 100644 --- a/vite.config.webcomponent.ts +++ b/vite.config.webcomponent.ts @@ -3,26 +3,43 @@ import { svelte } from "@sveltejs/vite-plugin-svelte"; import { sveltePreprocess } from "svelte-preprocess"; import { notifyEditorAfterBuildPlugin } from "./vite.postbuild"; import path from "path"; +import { existsSync, readFileSync, writeFileSync } from "fs"; // The host editor only loads components.js, so any CSS vite extracts into a // separate file (e.g. grid-uikit's splitpanes theme) never reaches the page. +// Vite attaches the extracted CSS to a different output format depending on +// the mode (build vs --watch), so inject it into every entry file after all +// formats are written instead of per bundle. function injectCssIntoJsPlugin(): Plugin { + const marker = "/*inject-css-into-js*/"; + let outDir = ""; + let css = ""; + const entryFiles = new Set(); return { name: "inject-css-into-js", apply: "build", enforce: "post", + configResolved(config) { + outDir = path.resolve(config.root, config.build.outDir); + }, generateBundle(_, bundle) { - const cssFiles = Object.keys(bundle).filter((k) => k.endsWith(".css")); - const css = cssFiles - .map((k) => (bundle[k] as { source: string }).source) - .join("\n"); - if (!css) return; - for (const chunk of Object.values(bundle)) { - if (chunk.type === "chunk" && chunk.isEntry) { - chunk.code += `\n;(function(){try{var s=document.createElement("style");s.textContent=${JSON.stringify(css)};document.head.appendChild(s);}catch(e){}})();`; + for (const [name, file] of Object.entries(bundle)) { + if (file.type === "chunk" && file.isEntry) entryFiles.add(name); + if (file.type === "asset" && name.endsWith(".css")) { + css = String(file.source); + delete bundle[name]; } } - cssFiles.forEach((k) => delete bundle[k]); + }, + closeBundle() { + if (!css) return; + const snippet = `\n${marker};(function(){try{var s=document.createElement("style");s.textContent=${JSON.stringify(css)};document.head.appendChild(s);}catch(e){}})();`; + for (const name of entryFiles) { + const file = path.join(outDir, name); + if (!existsSync(file)) continue; + const code = readFileSync(file, "utf8"); + if (!code.includes(marker)) writeFileSync(file, code + snippet); + } }, }; }