UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

182 lines (171 loc) 7.01 kB
import { createRequire } from "node:module"; import { readFile } from "node:fs/promises"; import { dirname, join } from "node:path"; import { $atom, $context, $module, AlephaError, z } from "alepha"; import { ViteDevServerProvider } from "alepha/cli"; //#region ../../src/cli/devtools/atoms/devtoolsOptions.ts /** * Devtools configuration atom. * * Filled from the `devtools` section of `alepha.config.ts`. */ const devtoolsOptions = $atom({ name: "alepha.cli.devtools.options", description: "Devtools plugin configuration", schema: z.object({ /** * Hide the floating devtools button in the browser. * * The devtools UI is still accessible at `/__devtools/`. */ hideButton: z.boolean().default(false).optional() }).optional() }); //#endregion //#region ../../src/cli/devtools/index.ts const DEVTOOLS_OVERLAY_SCRIPT = ` (function () { if (window.__alepha_devtools_injected) return; window.__alepha_devtools_injected = true; const STORAGE_KEY = "alepha-devtools-open"; // Button const btn = document.createElement("button"); btn.id = "alepha-devtools-btn"; btn.innerHTML = \`<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1Z"/></svg>\`; Object.assign(btn.style, { position: "fixed", bottom: "16px", left: "16px", zIndex: "99998", width: "36px", height: "36px", borderRadius: "50%", background: "rgba(255,255,255,0.85)", color: "#3f3f46", border: "none", backdropFilter: "blur(8px)", WebkitBackdropFilter: "blur(8px)", cursor: "pointer", display: "flex", alignItems: "center", justifyContent: "center", boxShadow: "0 1px 2px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.08)", transition: "transform 0.2s ease, box-shadow 0.2s ease", padding: "0", fontSize: "0", }); btn.addEventListener("mouseenter", () => { btn.style.transform = "translateY(-1px) rotate(45deg)"; btn.style.boxShadow = "0 2px 4px rgba(0,0,0,0.08), 0 8px 20px rgba(0,0,0,0.12)"; }); btn.addEventListener("mouseleave", () => { btn.style.transform = "translateY(0) rotate(0deg)"; btn.style.boxShadow = "0 1px 2px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.08)"; }); // Overlay const overlay = document.createElement("div"); overlay.id = "alepha-devtools-overlay"; Object.assign(overlay.style, { position: "fixed", inset: "0", zIndex: "99999", background: "rgba(0,0,0,0.6)", backdropFilter: "blur(2px)", display: "none", alignItems: "center", justifyContent: "center", }); // Panel const panel = document.createElement("div"); Object.assign(panel.style, { width: "90vw", height: "85vh", maxWidth: "1400px", borderRadius: "12px", overflow: "hidden", boxShadow: "0 8px 32px rgba(0,0,0,0.5)", border: "1px solid #2a2a4a", }); const iframe = document.createElement("iframe"); iframe.style.cssText = "width:100%;height:100%;border:none;"; panel.appendChild(iframe); overlay.appendChild(panel); document.body.appendChild(btn); document.body.appendChild(overlay); function open() { if (!iframe.src) iframe.src = "/__devtools/"; overlay.style.display = "flex"; btn.style.display = "none"; sessionStorage.setItem(STORAGE_KEY, "1"); } function close() { overlay.style.display = "none"; btn.style.display = "flex"; sessionStorage.removeItem(STORAGE_KEY); } btn.addEventListener("click", open); overlay.addEventListener("click", (e) => { if (e.target === overlay) close(); }); document.addEventListener("keydown", (e) => { if (e.key === "Escape" && overlay.style.display === "flex") close(); }); // Restore state after HMR if (sessionStorage.getItem(STORAGE_KEY)) open(); })(); `; /** * CLI plugin that integrates @alepha/devtools into the Vite dev server. * * This module is intentionally lightweight — it does NOT statically import * `@alepha/devtools` (which pulls in `alepha/react` and `.tsx` files). * Instead, it lazy-loads devtools via Vite's SSR module loader at runtime. * * Usage in `alepha.config.ts`: * ```ts * import { devtools } from "alepha/cli/devtools"; * * export default defineConfig({ * plugins: [devtools()], * }); * ``` * * @module alepha.devtools.plugin */ const AlephaCliDevtoolsPlugin = $module({ name: "alepha.cli.plugins.devtools", atoms: [devtoolsOptions], register: (alepha) => { const vite = alepha.inject(ViteDevServerProvider); const assetsPath = join(dirname(createRequire(import.meta.url).resolve("@alepha/devtools/package.json")), "assets/ui"); process.env.VITE_ALEPHA_DEVTOOLS = "true"; vite.addVitePlugin({ name: "alepha-devtools", configureServer: (server) => { server.middlewares.use((req, res, next) => { if (req.url !== "/__devtools/api/reload" || req.method !== "POST") return next(); vite.reload(); res.writeHead(200, { "content-type": "application/json" }); res.end(JSON.stringify({ ok: true })); }); server.middlewares.use(async (req, res, next) => { if (!(req.url || "/").startsWith("/__devtools") || !req.headers.accept?.includes("text/html")) return next(); const indexPath = join(assetsPath, "index.html"); try { let html = await readFile(indexPath, "utf-8"); html = html.replace("<head>", `<head><script type="module" src="/@vite/client"><\/script>`); res.writeHead(200, { "content-type": "text/html" }); res.end(html); } catch { next(); } }); }, transformIndexHtml: () => { if (alepha.store.get(devtoolsOptions)?.hideButton) return []; return [{ tag: "script", attrs: { type: "module" }, children: DEVTOOLS_OVERLAY_SCRIPT, injectTo: "head" }]; } }); vite.onAlephaLoaded(async (appAlepha, server) => { try { const mod = await server.ssrLoadModule("@alepha/devtools"); appAlepha.with(mod.AlephaDevtools); } catch (err) { throw new AlephaError("Failed to load @alepha/devtools. Make sure the package is installed", { cause: err }); } }); } }); const devtools = (options = {}) => { return () => { const { alepha } = $context(); alepha.with(AlephaCliDevtoolsPlugin).set(devtoolsOptions, options); }; }; //#endregion export { AlephaCliDevtoolsPlugin, devtools, devtoolsOptions }; //# sourceMappingURL=index.js.map