const fs = require('fs') const path = require('path') const cp = require('child_process') const process = require('process') /* Debug option */ const debug = false /* ************ */ const exec = (cmd) => { if (debug) console.log(`Exec: ${cmd}`) return cp.execSync(cmd) } const cwd = process.cwd() const CleanCSS = require('clean-css') const Handlebars = require('handlebars') const html = require('html-minifier') const red = '\033[1;31m' const green = '\033[1;32m' const yellow = '\033[1;33m' const blue = '\033[1;34m' const purple = '\033[1;35m' const bold = '\033[1m' const reset = '\033[0m' const minjs = 'script.min.js' const mincss = 'style.min.css' main() function main() { switch (process.argv[2]) { case 'watch': fs.watch(cwd, {recursive: true}, (_ev, file) => { const rel = path.relative(cwd, file) if (rel.includes('dist/')) return print(`Changed: ${file}`, true, '#', yellow) const dirs = rel.split(path.sep) const dir = (dirs.length > 1) ? dirs[0] : '.' buildOne(dir) }) break case 'build': const dir = process.argv[3] if (dir) buildOne(dir) else buildAll() break case 'archive': archive() break case 'clean': clean() break default: print('Available actions:', true) printList([ 'watch', 'build', 'build ', 'archive', 'clean', ]) break } } function buildAll() { const dirs = fs.readdirSync('.', { withFileTypes: true }) .filter(file => file.isDirectory()) .map(file => file.name) const pages = [ '.', ...dirs, ] for (let page of pages) buildOne(page) print('Completed', true, 'V', green) } function buildOne(dir) { print( `${bold}Building:${reset} ${dir}`, false, '>', purple, ) const config = `${dir}/webpage.json` if (!fs.existsSync(config)) { print( 'Unable to find webpage.json, skipping', false, '!', yellow, ) return } const files = JSON.parse(fs.readFileSync(config)) fs.mkdirSync(`dist/${dir}`, { recursive: true }) print('Minifying JS') minifyJs(dir, files) print('Rendering LESS') renderLess(dir, files) print('Minifying CSS') minifyCss(dir, files) print('Compiling Handlebars HTML') compileHbHtml(dir, files) print('Converting paths in HTML') convertPathsHtml(dir, files) print('Minifying HTML') minifyHtml(dir, files) print('Copying other files') copyOther(dir, files) } function minifyJs(dir, files) { files.js = files.js || [] if (files.js.length == 0) return; exec( 'node_modules/.bin/terser --compress --mangle ' + `-o "dist/${dir}/${minjs}" ` + files.js.map(js => `"${dir}/${js}"`).join(' ') ) } function renderLess(dir, files) { files.less = files.less || [] if (files.less.length == 0) return; const rendered = path.resolve(`dist/${dir}/${mincss}`) exec( 'node_modules/.bin/lessc ' + files.less.map(less => `"${dir}/${less}"`).join(' ') + ` "${rendered}"` ) files.css = [ ...(files.css || []), rendered, ] } function minifyCss(dir, files) { files.css = files.css || [] if (files.css.length == 0) return; const content = new CleanCSS().minify(files.css).styles fs.writeFileSync(`dist/${dir}/${mincss}`, content) } function compileHbHtml(dir, files) { files.hb = files.hb || [] const compiled = [] for (let file of files.hb) { const parts = file.split('.') const base = parts.slice(0, -1).join('.') || '' const name = path.resolve(`${dir}/${file}`) const conf = path.resolve(`${dir}/${base}.json`) const built = path.resolve(`dist/${dir}/${base}.html`) if (!fs.existsSync(conf)) { print( `Unable to find ${conf} \n` + ' for the template, skipping', false, '!', yellow, ) continue } const ctx = JSON.parse(fs.readFileSync(conf)) const template = Handlebars.compile( fs.readFileSync(name).toString() ) fs.writeFileSync(built, template(ctx)) compiled.push(built) } files.html = [ ...(files.html || []), ...compiled, ] } function convertPathsHtml(dir, files) { const scriptRegex = /(?:', '' ) .replace( '', `` ) .replace( scriptRegex, `` ) fs.writeFileSync(built, content) converted.push(built) } files.html = converted } function minifyHtml(dir, files) { files.html = files.html || [] for (let file of files.html) { const isAbs = path.isAbsolute(file) const name = (isAbs ? file : `${dir}/${file}`) const built = (isAbs ? file : `dist/${dir}/${file}`) const content = html.minify( fs.readFileSync(name).toString(), { collapseBooleanAttributes: true, collapseInlineTagWhitespace: true, collapseWhitespace: true, removeAttributeQuotes: true, removeComments: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, useShortDoctype: true, keepClosingSlash: false, minifyCSS: true, minifyJS: true, } ) fs.writeFileSync(built, content) } } function copyOther(dir, files) { files.other = files.other || [] for (let file of files.other) { const isAbs = path.isAbsolute(file) const name = (isAbs ? file : `${dir}/${file}`) const built = (isAbs ? file : `dist/${dir}/${file}`) fs.cpSync(name, built, { recursive: true, errorOnExist: false, }) } } function archive() { exec('7z a dist.zip dist/*') } function clean() { fs.rmSync('dist/', { recursive: true, force: true, }) fs.rmSync('dist.zip', { force: true, }) } function print( str, title = false, symbol = '*', color = blue, ) { console.log( `${color}[${symbol}]${reset} ` + `${title ? bold : ""}${str}${reset}` ) } function printList(arr) { for (let item of arr) console.log(` ${item}`) }