195 lines
5.5 KiB
JavaScript
195 lines
5.5 KiB
JavaScript
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({
|
||
// use "module" field for ES6 module if possible
|
||
module: true, // Default: true
|
||
|
||
// use "jsnext:main" if possible
|
||
// legacy field pointing to ES6 module in third-party libraries,
|
||
// deprecated in favor of "pkg.module":
|
||
// - see: https://github.com/rollup/rollup/wiki/pkg.module
|
||
jsnext: true, // Default: false
|
||
|
||
// use "main" field or index.js, even if it's not an ES6 module
|
||
// (needs to be converted from CommonJS to ES6
|
||
// – see https://github.com/rollup/rollup-plugin-commonjs
|
||
main: true, // Default: true
|
||
|
||
// some package.json files have a `browser` field which
|
||
// specifies alternative files to load for people bundling
|
||
// for the browser. If that's you, use this option, otherwise
|
||
// pkg.browser will be ignored
|
||
browser: true, // Default: false
|
||
|
||
// 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
|
||
|
||
// If true, inspect resolved files to check that they are
|
||
// ES2015 modules
|
||
modulesOnly: true, // Default: false
|
||
})
|
||
],
|
||
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 = "<style>\n" + sass_res.css.toString("utf8") + "\n</style>\n";
|
||
let script = "<script>\n" + code + "\n</script>\n";
|
||
let html = readFileSync(`${folder}/${pagename}.hbs`).toString("utf8");
|
||
|
||
let idx = html.indexOf("</head>")
|
||
if (idx < 0) throw new Error("No head element found")
|
||
let idx2 = html.indexOf("</body>")
|
||
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()
|