const { lstatSync, readdirSync, mkdirSync, copyFileSync, writeFileSync, readFileSync, exists, } = require("fs"); const { join, basename, dirname } = require("path"); const isDirectory = (source) => lstatSync(source).isDirectory(); const getDirectories = (source) => readdirSync(source) .map((name) => join(source, name)) .filter(isDirectory); function ensureDir(folder) { try { if (!isDirectory(folder)) mkdirSync(folder); } catch (e) { mkdirSync(folder); } } const fileExists = (filename) => new Promise((yes, no) => exists(filename, (exi) => yes(exi))); ensureDir("./out"); const sass = require("sass"); function findHead(elm) { if (elm.tagName === "head") return elm; for (let i = 0; i < elm.childNodes.length; i++) { let res = findHead(elm.childNodes[i]); if (res) return res; } return undefined; } const rollup = require("rollup"); const includepaths = require("rollup-plugin-includepaths"); const typescript = require("rollup-plugin-typescript2"); const resolve = require("rollup-plugin-node-resolve"); const minify = require("html-minifier").minify; const gzipSize = require("gzip-size"); async function file_name(folder, name, exts) { for (let ext of exts) { let basefile = `${folder}/${name}.${ext}`; if (await fileExists(basefile)) return basefile; } return null; } async function buildPage(folder) { const pagename = basename(folder); const outpath = "./out/" + pagename; ensureDir(outpath); const basefile = await file_name(folder, pagename, ["tsx", "ts", "js"]); let bundle = await rollup.rollup({ input: basefile, plugins: [ includepaths({ paths: ["shared", "node_modules"], }), typescript(), resolve({ // not all files you want to resolve are .js files extensions: [".mjs", ".js", ".jsx", ".json"], // Default: [ '.mjs', '.js', '.json', '.node' ] // whether to prefer built-in modules (e.g. `fs`, `path`) or // local ones with the same names preferBuiltins: false, // Default: true }), ], treeshake: true, }); let { output } = await bundle.generate({ format: "iife", compact: true, }); let { code } = output[0]; let sass_res = sass.renderSync({ file: folder + `/${pagename}.scss`, includePaths: ["./node_modules", folder, "./shared"], outputStyle: "compressed", }); let css = "\n"; let script = "\n"; let html = readFileSync(`${folder}/${pagename}.hbs`).toString("utf8"); let idx = html.indexOf(""); if (idx < 0) throw new Error("No head element found"); let idx2 = html.indexOf(""); if (idx2 < 0) throw new Error("No body element found"); if (idx < idx2) { let part1 = html.slice(0, idx); let part2 = html.slice(idx, idx2); let part3 = html.slice(idx2, html.length); html = part1 + css + part2 + script + part3; } else { let part1 = html.slice(0, idx2); let part2 = html.slice(idx2, idx); let part3 = html.slice(idx, html.length); html = part1 + script + part2 + css + part3; } let result = minify(html, { removeAttributeQuotes: true, collapseWhitespace: true, html5: true, keepClosingSlash: true, minifyCSS: false, minifyJS: false, removeComments: true, useShortDoctype: true, }); let gzips = await gzipSize(result); writeFileSync(`${outpath}/${pagename}.html`, result); let stats = { sass: sass_res.stats, js: { chars: code.length, }, css: { chars: css.length, }, bundle_size: result.length, gzip_size: gzips, }; writeFileSync(outpath + `/stats.json`, JSON.stringify(stats, null, " ")); } async function run() { console.log("Start compiling!"); let pages = getDirectories("./src"); await Promise.all( pages.map(async (e) => { try { await buildPage(e); } catch (er) { console.error("Failed compiling", basename(e)); console.log(er); } }) ); console.log("Finished compiling!"); } const chokidar = require("chokidar"); if (process.argv.join(" ").toLowerCase().indexOf("watch") >= 0) chokidar .watch( ["./src", "./node_modules", "./package.json", "./package-lock.json"], { ignoreInitial: true, } ) .on("all", () => run()); run();