1
0
mirror of https://github.com/osmarks/website synced 2025-08-30 09:17:56 +00:00

compiler parallelization

This commit is contained in:
2021-02-24 10:32:10 +00:00
parent 0748ec1289
commit a81f814bf7
3 changed files with 154 additions and 76 deletions

View File

@@ -15,6 +15,7 @@ const htmlMinifier = require("html-minifier").minify
const terser = require("terser")
const util = require("util")
const childProcess = require("child_process")
const chalk = require("chalk")
dayjs.extend(customParseFormat)
@@ -99,12 +100,13 @@ const applyTemplate = async (template, input, getOutput, options = {}) => {
return page.data
}
const processExperiments = templates => {
return loadDir(experimentDir, (subdirectory, basename) => {
return applyTemplate(
templates.experiment,
path.join(subdirectory, "index.html"),
async page => {
const processExperiments = async () => {
const templates = globalData.templates
const experiments = await loadDir(experimentDir, (subdirectory, basename) => {
return applyTemplate(
templates.experiment,
path.join(subdirectory, "index.html"),
async page => {
const out = path.join(outDir, page.data.slug)
await fse.ensureDir(out)
const allFiles = await fsp.readdir(subdirectory)
@@ -112,24 +114,30 @@ const processExperiments = templates => {
if (file !== "index.html") {
return fse.copy(path.join(subdirectory, file), path.join(out, file))
}
}))
return path.join(out, "index.html")
},
{ processMeta: meta => { meta.slug = meta.slug || basename }})
}))
return path.join(out, "index.html")
},
{ processMeta: meta => { meta.slug = meta.slug || basename }})
})
console.log(chalk.yellow(`${Object.keys(experiments).length} experiments`))
globalData.experiments = R.sortBy(x => x.title, R.values(experiments))
}
const processBlog = templates => {
return loadDir(blogDir, async (file, basename) => {
const processBlog = async () => {
const templates = globalData.templates
const blog = await loadDir(blogDir, async (file, basename) => {
return applyTemplate(templates.blogPost, file, async page => {
const out = path.join(outDir, page.data.slug)
await fse.ensureDir(out)
return path.join(out, "index.html")
}, { processMeta: meta => { meta.slug = meta.slug || removeExtension(basename) }, processContent: renderMarkdown })
})
console.log(chalk.yellow(`${Object.keys(blog).length} blog entries`))
globalData.blog = R.sortBy(x => x.updated ? -x.updated.valueOf() : 0, R.values(blog))
}
const processErrorPages = templates => {
const processErrorPages = () => {
const templates = globalData.templates
return loadDir(errorPagesDir, async (file, basename) => {
return applyTemplate(templates.experiment, file, async page => {
return path.join(outDir, basename)
@@ -137,76 +145,102 @@ const processErrorPages = templates => {
})
}
const processAssets = async templates => {
const outAssets = path.join(outDir, "assets")
await fse.ensureDir(outAssets)
// Write out the web manifest after templating it using somewhat misapplied frontend templating stuff
const runManifest = async () => {
const m = mustache.render(await readFile(path.join(assetsDir, "manifest.webmanifest")), globalData)
fsp.writeFile(path.join(outAssets, "manifest.webmanifest"), m)
}
const runJS = async () => {
const jsDir = path.join(assetsDir, "js")
const jsOutDir = path.join(outAssets, "js")
await Promise.all((await fsp.readdir(jsDir)).map(async file => {
const fullpath = path.join(jsDir, file)
await minifyJSFile(await readFile(fullpath), file, path.join(jsOutDir, file))
}))
const serviceWorker = mustache.render(await readFile(path.join(assetsDir, "sw.js")), globalData)
await minifyJSFile(serviceWorker, "sw.js", path.join(outDir, "sw.js"))
}
const copyAsset = subpath => fse.copy(path.join(assetsDir, subpath), path.join(outAssets, subpath))
// Directly copy images, JS, CSS
await Promise.all([
copyAsset("images"),
copyAsset("css"),
runManifest,
runJS,
applyTemplate(templates.experiment, path.join(assetsDir, "offline.html"), () => path.join(outAssets, "offline.html"), {})
])
}
const outAssets = path.join(outDir, "assets")
globalData.renderDate = date => date.format("DD/MM/YYYY")
const runOpenring = async () => {
// wildly unsafe but only runs on input from me anyway
const arg = `./openring -n6 ${globalData.feeds.map(x => '-s "' + x + '"').join(" ")} < openring.html`
console.log(arg)
const out = await util.promisify(childProcess.exec)(arg)
console.log(out.stderr)
return minifyHTML(out.stdout)
const writeBuildID = () => fsp.writeFile(path.join(outDir, "buildID.txt"), buildID)
const index = async () => {
const index = globalData.templates.index({ ...globalData, title: "Index", posts: globalData.blog })
await fsp.writeFile(path.join(outDir, "index.html"), index)
}
const run = async () => {
const css = await sass.renderSync({
const compileCSS = async () => {
const css = sass.renderSync({
data: await readFile(path.join(root, "style.sass")),
outputStyle: "compressed",
indentedSyntax: true
}).css
globalData.css = css
const templates = await loadDir(templateDir, async fullPath => pug.compile(await readFile(fullPath), { filename: fullPath }))
const [exp, blg, opr, ..._] = await Promise.all([
processExperiments(templates),
processBlog(templates),
runOpenring(),
processAssets(templates),
processErrorPages(templates)
])
const experimentsList = R.sortBy(x => x.title, R.values(exp))
const blogList = R.sortBy(x => x.updated ? -x.updated.valueOf() : 0, R.values(blg))
const index = templates.index({ ...globalData, title: "Index", experiments: experimentsList, posts: blogList, openring: opr })
await fsp.writeFile(path.join(outDir, "index.html"), index)
const rssFeed = templates.rss({ ...globalData, items: blogList, lastUpdate: new Date() })
}
const loadTemplates = async () => {
globalData.templates = await loadDir(templateDir, async fullPath => pug.compile(await readFile(fullPath), { filename: fullPath }))
}
const runOpenring = async () => {
// wildly unsafe but only runs on input from me anyway
const arg = `./openring -n6 ${globalData.feeds.map(x => '-s "' + x + '"').join(" ")} < openring.html`
console.log(chalk.keyword("orange")("Openring:") + " " + arg)
const out = await util.promisify(childProcess.exec)(arg)
console.log(chalk.keyword("orange")("Openring:") + "\n" + out.stderr.trim())
globalData.openring = minifyHTML(out.stdout)
}
const genRSS = async () => {
const rssFeed = globalData.templates.rss({ ...globalData, items: globalData.blog, lastUpdate: new Date() })
await fsp.writeFile(path.join(outDir, "rss.xml"), rssFeed)
}
const genManifest = async () => {
const m = mustache.render(await readFile(path.join(assetsDir, "manifest.webmanifest")), globalData)
fsp.writeFile(path.join(outAssets, "manifest.webmanifest"), m)
}
const minifyJSTask = async () => {
const jsDir = path.join(assetsDir, "js")
const jsOutDir = path.join(outAssets, "js")
await Promise.all((await fsp.readdir(jsDir)).map(async file => {
const fullpath = path.join(jsDir, file)
await minifyJSFile(await readFile(fullpath), file, path.join(jsOutDir, file))
}))
}
const genServiceWorker = async () => {
const serviceWorker = mustache.render(await readFile(path.join(assetsDir, "sw.js")), globalData)
await minifyJSFile(serviceWorker, "sw.js", path.join(outDir, "sw.js"))
}
const copyAsset = subpath => fse.copy(path.join(assetsDir, subpath), path.join(outAssets, subpath))
await fsp.writeFile(path.join(outDir, "buildID.txt"), buildID)
const tasks = {
errorPages: { deps: ["pagedeps"], fn: processErrorPages },
templates: { deps: [], fn: loadTemplates },
pagedeps: { deps: ["templates", "css"] },
css: { deps: [], fn: compileCSS },
writeBuildID: { deps: [], fn: writeBuildID },
index: { deps: ["openring", "pagedeps", "blog", "experiments"], fn: index },
openring: { deps: [], fn: runOpenring },
rss: { deps: ["blog"], fn: genRSS },
blog: { deps: ["pagedeps"], fn: processBlog },
experiments: { deps: ["pagedeps"], fn: processExperiments },
assetsDir: { deps: [], fn: () => fse.ensureDir(outAssets) },
manifest: { deps: ["assetsDir"], fn: genManifest },
minifyJS: { deps: ["assetsDir"], fn: minifyJSTask },
serviceWorker: { deps: [], fn: genServiceWorker },
images: { deps: ["assetsDir"], fn: () => copyAsset("images") },
offlinePage: { deps: ["assetsDir", "pagedeps"], fn: () => applyTemplate(globalData.templates.experiment, path.join(assetsDir, "offline.html"), () => path.join(outAssets, "offline.html"), {}) },
assets: { deps: ["manifest", "minifyJS", "serviceWorker", "images"] },
main: { deps: ["writeBuildID", "index", "errorPages", "assets", "experiments", "blog", "rss"] }
}
run()
const compile = async () => {
const done = new Set()
const inProgress = new Set()
const go = async finished => {
if (finished) {
inProgress.delete(finished)
done.add(finished)
console.log(`[${done.size}/${Object.keys(tasks).length}] ` + chalk.green(`Done ${finished}`))
}
for (const [task, conf] of Object.entries(tasks)) {
if (!inProgress.has(task) && !done.has(task) && conf.deps.every(x => done.has(x))) { // dependencies now satisfied for task, execute it
inProgress.add(task)
const callback = () => go(task)
if (conf.fn) {
console.log(chalk.cyanBright(`Executing ${task}`))
Promise.resolve(conf.fn()).then(callback, err => {
console.error(`Error in ${task}: ${err.stack}`)
process.exit(1)
})
} else {
setImmediate(callback)
}
}
}
}
go()
}
compile()