join2
Version:
combine stream chunks pairwise
170 lines (148 loc) • 5.12 kB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const { execFileSync } = require('child_process')
const fsx = require('fs-extra')
const getopts = require('getopts')
const path = require('path')
const pump = require('pump')
const pumpify = require('pumpify')
const { Transform } = require('readable-stream')
const snippin = require('snippin')
const split2 = require('split2')
const tmp = require('tmp')
const join2 = require('../index.js')
const ERR_USAGE = 1
const usage = `Usage: render-readme.js [-h] [FORMAT]
Render the README file to the given FORMAT:
- markdown write the output to README.md
- html write the output to public/index.html
The default format is "markdown".
Options:
-h, --help do nothing and show this usage message instead`
const fatalUsage = () => {
console.warn(usage)
process.exit(ERR_USAGE)
}
const opts = getopts(process.argv.slice(2), {
alias: {
'h': 'help'
},
boolean: ['h'],
unknown: opt => {
console.warn(`Unknown option ${JSON.stringify(opt)}`)
fatalUsage()
}
})
if (opts._.length > 1) {
fatalUsage()
}
if (opts.h) {
console.log(usage)
process.exit(0)
}
let doccoLayout
const fmt = opts._.length === 1 ? opts._[0] : 'markdown'
switch (fmt) {
case 'html':
doccoLayout = 'linear'
break
case 'markdown':
doccoLayout = 'plain-markdown'
break
default:
fatalUsage()
}
const repoRoot = path.normalize(path.join(__dirname, '..'))
const tmpDir = tmp.dirSync()
const doccoOutDir = `${tmpDir.name}/out`
// Docco doesn't expose an API, and only works on whole files, so we have to
// do some gymnastics to work around its limitations. Here's what we'll do:
//
// 1. Convert the source README.in document into "javascript" by adding
// "// " in front of every line, i.e. converting the whole file to one big
// javascript comment.
// 2. Expand snippets
// 3. Rewrite relative requires to something copy-pastable
// 4. Call docco on this "javascript" file to get the final document we want.
const commentOut = new Transform({
transform (line, encoding, callback) {
line = line.toString()
const space = line.length ? ' ' : ''
callback(null, '//' + space + line)
}
})
// There's some require expressions in the examples that reference files via
// relative paths; those can't be copy-pasted as-is by users of this library.
// We'll replace those by what they would look like to the rest of the world
// installing this package from npmjs.com.
const rewriteRelativeRequires = new Transform({
transform (line, encoding, callback) {
line = line.toString()
const m = /require\('[./]+index\.js'\)/.exec(line)
if (m !== null) {
line = line.replace(m[0], "require('join2')")
}
callback(null, line)
}
})
pump(
fsx.createReadStream(`${repoRoot}/README.in`),
split2(/(\r\n|[\n\v\f\r\x85\u2028\u2029])/),
join2(),
commentOut,
snippin(repoRoot),
rewriteRelativeRequires,
fsx.createWriteStream(`${tmpDir.name}/join2`),
err => {
if (err) throw err
const doccoExec = `${repoRoot}/node_modules/.bin/docco`
const doccoArgs = ['-e', '.js', '-o', doccoOutDir, '-l', doccoLayout, 'join2']
execFileSync(doccoExec, doccoArgs, { cwd: tmpDir.name })
if (fmt === 'html') {
fsx.renameSync(`${doccoOutDir}/join2.html`, `${doccoOutDir}/index.html`)
fsx.copySync(doccoOutDir, `${repoRoot}/public`)
} else {
// Docco puts code blocks between "```" fences. But gitub/gitlab/npm will
// only do syntax hightlighting if they start with "```javascript". Syntax
// hightlighting is nice, so we'll pipe the docco output through a
// transform that adds it before writing to the final destination, i.e.
// README.md.
//
// Docco also adds a lot of empty lines, so we'll collapse multiple
// consecutive empty lines into one while we're at it.
const addSyntaxHighlighting = pumpify(
split2(/(\r\n|[\n\v\f\r\x85\u2028\u2029])/),
join2(),
new Transform({
transform (line, encoding, callback) {
line = line.toString()
// Add syntax highlighting
if (/^```\s*$/.test(line)) {
if (!this.even) line = line.replace('```', '```javascript')
this.even = !this.even
}
// Collapse blank lines
if (/^\s*$/.test(line)) {
if (this.contentStarted && !this.blankLine) this.blankLine = line
line = ''
} else {
this.contentStarted = true
if (this.blankLine) line = this.blankLine + line
this.blankLine = ''
}
callback(null, line)
}
})
)
pump(
fsx.createReadStream(`${doccoOutDir}/join2.html`),
addSyntaxHighlighting,
fsx.createWriteStream(`${repoRoot}/README.md`),
err => { if (err) throw err }
)
}
}
)