import fs from "node:fs/promises";
import {constants} from "node:fs";
import path from "path";
import htmlMinifier from "html-minifier-terser";
import esbuild from "esbuild";
export default async (dir)=>{
const indexFile = await findIndex(dir);
if(!indexFile) return;
let data = {};
if(path.extname(indexFile) === ".neovan"){
data = await getNeovanData(indexFile);
}else{
data = await parseHtml(indexFile);
}
const bundle = await createBundle(data);
fs.rm(path.join(dir, "tmp/"), {recursive: true, force: true});
}
const findIndex = async (dir)=>{
const neovanPath = path.join(dir, "index.neovan");
const htmlPath = path.join(dir, "index.html");
const [neovan, html] = await Promise.allSettled([
fs.access(path.join(dir, "index.neovan"), constants.F_OK),
fs.access(path.join(dir, "index.html"), constants.F_OK)
]);
if(neovan.status === "fulfilled") return neovanPath;
if(html.status === "fulfilled") return htmlPath;
return null;
}
const getNeovanData = async (index)=>{
const neovan = await fs.readFile(index, "utf-8");
const parentPath = path.dirname(index);
const html = neovan.slice(neovan.indexOf("") + 10, neovan.indexOf(""));
const css = neovan.slice(neovan.indexOf(""));
const js = neovan.slice(neovan.indexOf(""));
const cssFile = path.join(parentPath, `tmp/${path.basename(index, ".neovan")}.css`);
const jsFile = path.join(parentPath, `tmp/${path.basename(index, ".neovan")}.js`);
await fs.mkdir(path.join(parentPath, "tmp/"));
await Promise.all([
css === "" ? null : fs.writeFile(cssFile, css),
js === "" ? null : fs.writeFile(jsFile, js)
]);
return {
html: html,
css: cssFile,
js: jsFile
};
}
const parseHtml = async (index)=>{
const parentPath = path.dirname(index);
const basename = path.basename(index, ".html");
return {
html: await fs.readFile(index, "utf-8"),
css: path.join(parentPath, `${basename}.css`),
js: path.join(parentPath, `${basename}.js`)
};
}
const createBundle = async (data)=>{
const entryPoints = [];
if(data.css) entryPoints.push(data.css);
if(data.js) entryPoints.push(data.js);
const esbuildProm = esbuild.build({
entryPoints: entryPoints,
bundle: true,
minify: true,
write: false,
outdir: "/"
});
const htmlProm = htmlMinifier.minify(data.html, {
collapseBooleanAttributes: true,
collapseInlineTagWhitespace: true,
collapseWhitespace: true,
decodeEntities: true,
html5: true,
includeAutoGeneratedTags: false,
noNewlinesBeforeTagClose: true,
removeComments: true,
useShortDoctype: true
});
const [buildData, html] = await Promise.all([esbuildProm, htmlProm]);
//potentially replaceable
const comps = {html: html};
for(let i = 0; i < buildData.outputFiles.length; i++){
const ext = path.extname(buildData.outputFiles[i].path).replace(".", "");
comps[ext] = buildData.outputFiles[i].text;
}
return mergeFiles(comps);
}
const mergeFiles = (comps)=>{
const cssIndex = comps.html.indexOf("");
const html = `${comps.html.slice(0, cssIndex)}${comps.css}${comps.html.slice(cssIndex)}`;
const jsIndex = html.indexOf("