300 lines
7.4 KiB
JavaScript
300 lines
7.4 KiB
JavaScript
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 <dir>',
|
|
'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 = /(?:<script src=['"]?js\/\w+\.js['"]?><\/script>[\s\r\n]*)+/gm
|
|
const converted = []
|
|
files.html = files.html || []
|
|
for (let file of files.html) {
|
|
const isAbs = path.isAbsolute(file)
|
|
const name = path.resolve(isAbs ? file : `${dir}/${file}`)
|
|
const built = path.resolve(isAbs ? file : `dist/${dir}/${file}`)
|
|
const content = fs.readFileSync(name)
|
|
.toString()
|
|
.replace(
|
|
'<script src="https://cdn.jsdelivr.net/npm/less"></script>',
|
|
''
|
|
)
|
|
.replace(
|
|
'<link rel="stylesheet/less" type="text/css" href="styles.less" />',
|
|
`<link rel="stylesheet" href="${mincss}" />`
|
|
)
|
|
.replace(
|
|
scriptRegex,
|
|
`<script src="${minjs}"></script>`
|
|
)
|
|
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}`)
|
|
}
|