sitegen/transformers/js.js

83 lines
2.5 KiB
JavaScript

import { nanoid } from "nanoid";
import { build } from "esbuild";
import { mkdir, readFile, rm, writeFile } from "fs/promises";
import { cwd } from "process";
import { basename, dirname, join } from "path";
import h from "vhtml";
globalThis.vhtml = h;
export default class JsTransformer {
/**
* @param {string} inputPath
* @param {string} outputPath
*/
static async build(inputPath, outputPath) {
outputPath = join(
dirname(outputPath),
basename(outputPath, ".s.jsx") + ".html"
);
const outfile = join(cwd(), `${nanoid()}.mjs`);
await mkdir(dirname(outfile), { recursive: true });
try {
await build({
entryPoints: [join(cwd(), inputPath)],
outfile,
format: "esm",
loader: {
".json": "json",
},
bundle: true,
platform: "node",
plugins: [
{
name: "svg-jsx",
setup(build) {
build.onLoad({ filter: /\.svg$/ }, async (args) => {
const f = await readFile(args.path, "utf-8");
const js = `
export default (props) => {
let s = \`${f}\`;
if (props.class)
s = s.replace("<svg", \`<svg class="\${props.class}"\`);
return (
<div
class={props.containerClass}
dangerouslySetInnerHTML={{ __html: s }}
></div>
);
};
`;
return { contents: js, loader: "jsx" };
});
},
},
{
name: "relative-import-path",
setup(build) {
build.onResolve({ filter: /./ }, (args) => {
if (args.path.endsWith(".json") || args.path.endsWith(".svg"))
return { path: join(args.resolveDir, args.path) };
else if (args.path.startsWith("."))
return {
path: join(args.resolveDir, args.path),
external: true,
};
else return { path: args.path, external: !!args.importer };
});
},
},
],
jsxFactory: "h",
banner: { js: `const h = globalThis.vhtml;` },
});
const file = await import(outfile);
const html = "<!doctype html>" + (await file.render());
await writeFile(outputPath, html);
} finally {
}
await rm(outfile);
}
}