UNPKG

htmelt

Version:

Bundle your HTML assets with Esbuild and LightningCSS. Custom plugins, HMR platform, and more.

314 lines (310 loc) 10.4 kB
import { compileSeparateEntry, importGlob_default, importMetaUrl_default } from "./chunk-SE5MUBQP.mjs"; import "./chunk-26E6E5EJ.mjs"; import { findDirectoryUp, resolveDevMapSources } from "./chunk-SGZXFKQT.mjs"; // src/plugins/devModules.mts import { fileToId, parseNamespace, sendFile, uriToFile, uriToId } from "@htmelt/plugin"; import { parse } from "meriyah"; import { nebu } from "nebu"; import { dirname } from "path"; // src/sourceMaps.mts import remapping from "@ampproject/remapping"; import * as convertSourceMap from "convert-source-map"; import * as fs from "fs"; import * as path from "path"; function appendInlineSourceMap(code, map) { return code + "\n//# sourceMappingURL=" + mapToDataUrl(map); } function mapToDataUrl(map) { return "data:application/json;charset=utf-8;base64," + Buffer.from(JSON.stringify(map)).toString("base64"); } // src/plugins/devModules.mts var devModulesPlugin = async (config) => { const modules = config.modules; config.watcher?.on("unlink", (file) => { const id = fileToId(file); modules.delete(id); }); const esbuildDevModules = { name: "dev-modules", setup(build) { build.onTransform({ loaders: ["js"] }, async (args) => { const program = parse(args.code, { next: true, ranges: true, module: true }); const resolutions = /* @__PURE__ */ new Map(); await Promise.all( program.body.map(async (node) => { if (node.type !== "ImportDeclaration") { return; } const id = node.source.value; const resolved = await build.resolve(id, { kind: "import-statement", importer: args.path, resolveDir: dirname(args.path) }); resolutions.set(node, resolved); }) ); const nebuDevModules = { Program(program2) { for (const node of program2.body) { if (node.isImportDeclaration()) { let resolved = resolutions.get(node.n); if (resolved === void 0) { continue; } const resolvedId = fileToId(resolved.path, resolved.namespace); if (!modules.has(resolvedId)) { continue; } if (node.specifiers.length === 1 && node.specifiers[0].isImportNamespaceSpecifier()) { const localName = node.specifiers[0].local.name; node.replace( `const ${localName} = htmelt.import("${resolvedId}")` ); } else { const specifiers = node.specifiers.map((specifier) => { const exported = specifier.isImportDefaultSpecifier() ? "default" : specifier.imported.name; return exported + (exported !== specifier.local.name ? ": " + specifier.local.name : ""); }); node.replace( `const {${specifiers.join( ", " )}} = htmelt.import("${resolvedId}")` ); } } else if (node.isExportAllDeclaration() || node.isExportNamedDeclaration() || node.isExportDefaultDeclaration()) { node.remove(); } } } }; const result = nebu.process(args.code, { filename: args.path, ast: program, sourceMap: true, sourceMapHiRes: true, plugins: [nebuDevModules] }); return { code: result.js, map: result.map }; }); } }; config.loadDevModule = async (entry) => { const sourceRoot = dirname(entry); const { outputFiles: [mapFile, jsFile] } = await compileSeparateEntry(entry, config, { format: "esm", outfile: entry.replace(/\.\w+$/, "." + Date.now() + "$&"), sourcemap: true, sourceRoot, plugins: [ ...config.esbuild.plugins, importMetaUrl_default(), importGlob_default(config.relatedWatcher), esbuildDevModules ] }); const map = JSON.parse(mapFile.text); resolveDevMapSources(map, process.cwd(), sourceRoot); map.sourceRoot = void 0; return appendInlineSourceMap(jsFile.text, map); }; config.esbuild.plugins.push({ name: "dev-exports", setup(build) { const nebuDevExports = { Program(program, { module, resolutions }) { const exports = []; for (let node of program.body) { if (node.isImportDeclaration()) { module.imports.add(node.source.value); } else if (node.isExportNamedDeclaration()) { if (node.source) { const resolved = resolutions.get(node.n); if (!resolved) { continue; } const aliases = node.specifiers.map((specifier) => [ specifier.exported.name, specifier.local.name ]); exports.push({ from: fileToId(resolved.path, resolved.namespace), aliases: Object.fromEntries(aliases) }); continue; } if (node.declaration) { node = node.declaration; if (node.isVariableDeclaration()) { for (const decl of node.declarations) { const { name } = decl.id; if (node.kind === "const") { exports.push([name, name]); } else { exports.push({ name, get: jsonAccessor(name) }); } } } else { const name = node.id.name; exports.push([name, name]); } continue; } exports.push({ values: Object.fromEntries( node.specifiers.map((specifier) => [ specifier.exported.name, jsonAccessor(specifier.local.name) ]) ) }); } else if (node.isExportDefaultDeclaration()) { node.before("let __default;"); node.declaration.before(`__default = `); exports.push(["default", "__default"]); } else if (node.isExportAllDeclaration()) { const resolved = resolutions.get(node.n); if (resolved) { exports.push({ from: fileToId(resolved.path, resolved.namespace) }); } } } program.push( "body", ` htmelt.export("${module.id}", [` + exports.map(stringifyExport).join(",") + "])" ); } }; build.onTransform({ loaders: ["js", "jsx"] }, async (args) => { if (args.code.trim() === "") { return null; } const id = fileToId(args.initialPath || args.path, args.namespace); const program = parse(args.code, { next: true, ranges: true, module: true, jsx: args.loader === "jsx" }); const resolutions = /* @__PURE__ */ new Map(); await Promise.all( program.body.map(async (node) => { if (node.type !== "ExportAllDeclaration" && node.type !== "ExportNamedDeclaration" || !node.source) { return; } const id2 = node.source.value; const resolved = await build.resolve(id2, { kind: "import-statement", importer: args.path, resolveDir: dirname(args.path) }); resolutions.set(node, resolved); }) ); const newId = !modules.has(id); const newModule = { id, imports: /* @__PURE__ */ new Set() }; modules.set(id, newModule); const result = nebu.process(args.code, { ast: program, filename: args.path, sourceMap: true, sourceMapHiRes: true, plugins: [nebuDevExports], state: { module: newModule, resolutions } }); if (newId && id.startsWith("/@fs/") && !id.includes("node_modules")) for (const dir of config.linkedPackages) { if (id.startsWith("/@fs" + dir + "/")) { const file = id.slice(4); config.watcher.add(file); const rootDir = findDirectoryUp( dirname(file), [".git", "package.json"], config.fsAllowedDirs ); if (rootDir && !config.fsAllowedDirs.has(rootDir)) { config.fsAllowedDirs.add(rootDir); } } } return { code: result.js, map: result.map }; }); } }); const moduleRE = /\.([mc]?[tj]s|[tj]sx)$/; return { async serve(request, response) { if (request.searchParams.has("t") && moduleRE.test(request.pathname)) { const id = uriToId(request.pathname); const namespace = parseNamespace(id); const filePath = !namespace ? uriToFile(request.pathname) : void 0; try { const data = await config.loadDevModule(filePath || id); sendFile(request.pathname, response, { path: filePath, data, headers: { "content-type": "application/javascript" } }); } catch { } } } }; }; function jsonAccessor(name) { return new Function("", "return () => " + name)(); } function stringifyExport(e) { if (Array.isArray(e)) { return "[" + JSON.stringify(e[0]) + "," + e[1] + "]"; } if ("from" in e) { return JSON.stringify(e); } if ("values" in e) { return "{values:{" + Object.entries(e.values).map(([name, get]) => JSON.stringify(name) + ":" + get.toString()).join(",") + "}}"; } return "{name:" + JSON.stringify(e.name) + ",get:" + e.get.toString() + "}"; } export { devModulesPlugin };