Refactor the scripts to work as advertised
- Correct docs generation to always fetch its dependency - Don't try to close a handle that's already been closed by other methods - Allow the release script to actually be testable.
This commit is contained in:
parent
30ad45caa1
commit
22e6d37a26
12 changed files with 483 additions and 241 deletions
|
|
@ -1,18 +1,17 @@
|
|||
"use strict"
|
||||
|
||||
const {createReadStream, createWriteStream, promises: fs} = require("fs")
|
||||
const {promises: fs} = require("fs")
|
||||
const path = require("path")
|
||||
const {promisify} = require("util")
|
||||
const pipeline = promisify(require("stream").pipeline)
|
||||
const marked = require("marked")
|
||||
const rimraf = promisify(require("rimraf"))
|
||||
const copy = require("recursive-copy")
|
||||
const {execFileSync} = require("child_process")
|
||||
const escapeRegExp = require("escape-string-regexp")
|
||||
const HTMLMinifier = require("html-minifier")
|
||||
const upstream = require("./_upstream")
|
||||
|
||||
require("./_command").exec(module, () => generate())
|
||||
module.exports = generate
|
||||
const r = (file) => path.resolve(__dirname, "..", file)
|
||||
|
||||
// Minify our docs.
|
||||
const htmlMinifierConfig = {
|
||||
|
|
@ -38,9 +37,12 @@ const htmlMinifierConfig = {
|
|||
useShortDoctype: true,
|
||||
}
|
||||
|
||||
module.exports = generate
|
||||
async function generate() {
|
||||
const r = (file) => path.resolve(__dirname, "..", file)
|
||||
return (await makeGenerator()).generate()
|
||||
}
|
||||
|
||||
async function makeGenerator() {
|
||||
await rimraf(r("dist"))
|
||||
|
||||
const [guides, methods, layout, pkg] = await Promise.all([
|
||||
|
|
@ -53,19 +55,39 @@ async function generate() {
|
|||
|
||||
const version = JSON.parse(pkg).version
|
||||
|
||||
// Make sure we have the latest archive.
|
||||
execFileSync("git", [
|
||||
"fetch", "--depth=1",
|
||||
upstream.fetch.remote, "gh-pages",
|
||||
])
|
||||
|
||||
// Set up archive directories
|
||||
execFileSync("git", ["checkout", "gh-pages", "--", "archive"])
|
||||
execFileSync("git", [
|
||||
"checkout", `${upstream.fetch.remote}/gh-pages`,
|
||||
"--", "archive",
|
||||
])
|
||||
await fs.rename(r("archive"), r("dist/archive"))
|
||||
await fs.mkdir(r(`dist/archive/v${version}`), {recursive: true})
|
||||
// Tell Git to ignore our changes - it's no longer there.
|
||||
execFileSync("git", ["add", "archive"])
|
||||
|
||||
function compilePage(file, markdown) {
|
||||
return new Generator({version, guides, methods, layout})
|
||||
}
|
||||
|
||||
class Generator {
|
||||
constructor(opts) {
|
||||
this._version = opts.version
|
||||
this._guides = opts.guides
|
||||
this._methods = opts.methods
|
||||
this._layout = opts.layout
|
||||
}
|
||||
|
||||
compilePage(file, markdown) {
|
||||
file = path.basename(file)
|
||||
const link = new RegExp(
|
||||
`([ \t]*)(- )(\\[.+?\\]\\(${escapeRegExp(file)}\\))`
|
||||
)
|
||||
const src = link.test(guides) ? guides : methods
|
||||
const src = link.test(this._guides) ? this._guides : this._methods
|
||||
let body = markdown
|
||||
|
||||
// fix pipes in code tags
|
||||
|
|
@ -100,7 +122,7 @@ async function generate() {
|
|||
const markedHtml = marked(body)
|
||||
const title = body.match(/^#([^\n\r]+)/i) || []
|
||||
|
||||
let result = layout
|
||||
let result = this._layout
|
||||
|
||||
result = result.replace(
|
||||
/<title>Mithril\.js<\/title>/,
|
||||
|
|
@ -108,7 +130,7 @@ async function generate() {
|
|||
)
|
||||
|
||||
// update version
|
||||
result = result.replace(/\[version\]/g, version)
|
||||
result = result.replace(/\[version\]/g, this._version)
|
||||
|
||||
// insert parsed HTML
|
||||
result = result.replace(/\[body\]/, markedHtml)
|
||||
|
|
@ -137,46 +159,103 @@ async function generate() {
|
|||
return result
|
||||
}
|
||||
|
||||
async function generate(file) {
|
||||
try {
|
||||
const handle = await fs.open(file, "r")
|
||||
try {
|
||||
const relative = path.relative(r("docs"), file)
|
||||
const archive = r(`dist/archive/v${version}/${relative}`)
|
||||
await fs.mkdir(path.dirname(archive), {recursive: true})
|
||||
async eachTarget(relative, init) {
|
||||
await Promise.all([
|
||||
init(r(`dist/archive/v${this._version}/${relative}`)),
|
||||
init(r(`dist/${relative}`)),
|
||||
])
|
||||
}
|
||||
|
||||
if (file.endsWith(".md")) {
|
||||
const html = compilePage(file, await handle.readFile("utf-8"))
|
||||
const minified = HTMLMinifier.minify(html, htmlMinifierConfig)
|
||||
await fs.writeFile(archive.replace(/\.md$/, ".html"), minified)
|
||||
} else if (file.endsWith(".html")) {
|
||||
const html = await handle.readFile("utf-8")
|
||||
const minified = HTMLMinifier.minify(html, htmlMinifierConfig)
|
||||
await fs.writeFile(archive, minified)
|
||||
} else {
|
||||
await pipeline(
|
||||
createReadStream(null, {fd: handle.fd}),
|
||||
createWriteStream(archive)
|
||||
)
|
||||
}
|
||||
} finally {
|
||||
handle.close()
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.code !== "EISDIR") throw e
|
||||
const files = await fs.readdir(file)
|
||||
const devOnly = /^layout\.html$|^tutorials$|^archive$|^nav-/
|
||||
await Promise.all(
|
||||
files
|
||||
.filter((f) => !devOnly.test(f))
|
||||
.map((f) => path.join(file, f))
|
||||
.map(generate)
|
||||
async generateSingle(file) {
|
||||
const relative = path.relative(r("docs"), file)
|
||||
const archived = (target, init) =>
|
||||
this.eachTarget(target, async (dest) => {
|
||||
await fs.mkdir(path.dirname(dest), {recursive: true})
|
||||
await init(dest)
|
||||
})
|
||||
|
||||
if ((/\.(md|html)$/).test(file)) {
|
||||
await archived(relative, (dest) => fs.copyFile(file, dest))
|
||||
console.log(`Copied: ${relative}`)
|
||||
}
|
||||
else {
|
||||
let html = await fs.readFile(file, "utf-8")
|
||||
if (file.endsWith(".md")) html = this.compilePage(file, html)
|
||||
const minified = HTMLMinifier.minify(html, htmlMinifierConfig)
|
||||
await archived(
|
||||
relative.replace(/\.md$/, ".html"),
|
||||
(dest) => fs.writeFile(dest, minified)
|
||||
)
|
||||
console.log(`Compiled: ${relative}`)
|
||||
}
|
||||
}
|
||||
|
||||
await generate(r("docs"))
|
||||
await copy(r(`dist/archive/v${version}`), r("dist"))
|
||||
// Just ensure it exists.
|
||||
await (await fs.open(r("dist/.nojekyll"), "a")).close()
|
||||
async generateRec(file) {
|
||||
let files
|
||||
try {
|
||||
files = await fs.readdir(file)
|
||||
}
|
||||
catch (e) {
|
||||
if (e.code !== "ENOTDIR") throw e
|
||||
return this.generateSingle(file)
|
||||
}
|
||||
|
||||
const devOnly = /^layout\.html$|^archive$|^nav-/
|
||||
// Don't care about the return value here.
|
||||
await Promise.all(
|
||||
files
|
||||
.filter((f) => !devOnly.test(f))
|
||||
.map((f) => this.generateRec(path.join(file, f)))
|
||||
)
|
||||
}
|
||||
|
||||
async generate() {
|
||||
await this.generateRec(r("docs"))
|
||||
await copy(r(`dist/archive/v${this._version}`), r("dist"))
|
||||
// Just ensure it exists.
|
||||
await (await fs.open(r("dist/.nojekyll"), "a")).close()
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-disable global-require */
|
||||
if (require.main === module) {
|
||||
require("./_command")({
|
||||
exec: generate,
|
||||
async watch() {
|
||||
let timeout, genPromise
|
||||
function updateGenerator() {
|
||||
if (timeout == null) return
|
||||
clearTimeout(timeout)
|
||||
genPromise = new Promise((resolve) => {
|
||||
timeout = setTimeout(function() {
|
||||
timeout = null
|
||||
resolve(makeGenerator().then((g) => g.generate()))
|
||||
}, 100)
|
||||
})
|
||||
}
|
||||
|
||||
async function updateFile(file) {
|
||||
if ((/^layout\.html$|^archive$|^nav-/).test(file)) {
|
||||
updateGenerator()
|
||||
}
|
||||
(await genPromise).generateSingle(file)
|
||||
}
|
||||
|
||||
async function removeFile(file) {
|
||||
(await genPromise).eachTarget(file, (dest) => fs.unlink(dest))
|
||||
}
|
||||
|
||||
require("chokidar").watch(r("docs"), {
|
||||
ignored: ["archive/**", /(^|\\|\/)\../],
|
||||
// This depends on `layout`/etc. existing first.
|
||||
ignoreInitial: true,
|
||||
awaitWriteFinish: true,
|
||||
})
|
||||
.on("ready", updateGenerator)
|
||||
.on("add", updateFile)
|
||||
.on("change", updateFile)
|
||||
.on("unlink", removeFile)
|
||||
.on("unlinkDir", removeFile)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue