Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix($core): render static html via stream pipe (fix #1819) #2454

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
82 changes: 74 additions & 8 deletions packages/@vuepress/core/lib/node/build/index.js
Expand Up @@ -11,6 +11,7 @@ const createServerConfig = require('../webpack/createServerConfig')
const { createBundleRenderer } = require('vue-server-renderer')
const { normalizeHeadTag, applyUserWebpackConfig } = require('../util/index')
const { version } = require('../../../package')
const pLimit = require('p-limit')

/**
* Expose Build Process Class.
Expand Down Expand Up @@ -90,11 +91,25 @@ module.exports = class Build extends EventEmitter {

// render pages
logger.wait('Rendering static HTML...')
console.log()

// Keep track of progress
const total = this.context.pages.length
this.counter = 0
this.active = 0
const RENDER_LIMIT = 50

// start with empty progress bar
console.log()
renderProgress(this.counter, total, RENDER_LIMIT - 1, `Rendering ${total} pages`)

// Use p-limit to throttle number of files done at the same time
const limit = pLimit(RENDER_LIMIT)
const pagePaths = await Promise.all(
this.context.pages.map(page => this.renderPage(page))
this.context.pages.map(page => limit(() => this.renderPage(page, total)))
)

// Wipe progress bar
readline.clearLine(process.stdout, 0)
readline.cursorTo(process.stdout, 0)

Expand Down Expand Up @@ -129,11 +144,12 @@ module.exports = class Build extends EventEmitter {
* Render page
*
* @param {Page} page
* @param {Number} total total number of static pages we're rendering
* @returns {Promise<string>}
* @api private
*/

async renderPage (page) {
async renderPage (page, total) {
const pagePath = decodeURIComponent(page.path)

const context = {
Expand All @@ -145,21 +161,71 @@ module.exports = class Build extends EventEmitter {
version
}

let html
this.active++
const filename = pagePath.replace(/\/$/, '/index.html').replace(/^\//, '')
const filePath = path.resolve(this.outDir, filename)
try {
html = await this.renderer.renderToString(context)
const readable = this.renderer.renderToStream(context)
await fs.ensureDir(path.dirname(filePath))
await pipe(filePath, readable)
} catch (e) {
console.error(logger.error(chalk.red(`Error rendering ${pagePath}:`), false))
throw e
} finally {
this.counter++
this.active--
}
const filename = pagePath.replace(/\/$/, '/index.html').replace(/^\//, '')
const filePath = path.resolve(this.outDir, filename)
await fs.ensureDir(path.dirname(filePath))
await fs.writeFile(filePath, html)
renderProgress(this.counter, total, this.active, pagePath)
return filePath
}
}

const BAR_LENGTH = 50
const BAR_BG = chalk.white('█')
const BAR_FG = chalk.blueBright('█')
/**
* Renders a progres sbar of static html pages, like this:
*
* ██████████████████████████████████████████████████ (44%) 721/1618 files, 50 active
* ...cations/Managing_Non-Titanium_Client_Applications_in_Dashboard.html
*
* @param {Number} count current count of files done
* @param {Number} total total number of files to process
* @param {Number} active number of files being actively processed
* @param {string} filename last file finished
*/
function renderProgress (count, total, active, filename) {
const progress = count / total // as a [0.0, 1.0] float
const blocks = Math.floor(progress * BAR_LENGTH)

readline.moveCursor(process.stdout, 0, -1) // move up to
readline.cursorTo(process.stdout, 0) // start at beginning of line
readline.clearScreenDown(process.stdout) // clear everything below

const bar = BAR_FG.repeat(blocks) + BAR_BG.repeat(BAR_LENGTH - blocks)
const percent = Math.floor(progress * 100) // as a 0-100 integer
const totals = chalk.gray(`${count}/${total} files, ${active + 1} active`)
console.log(`${bar} (${percent}%) ${totals}`) // print the bar, progress
const shortFilename = filename.length > 70 ? `...${filename.slice(-67)}` : filename
process.stdout.write(`${chalk.gray(shortFilename)}`) // print the filename
}

/**
* Pipes rendered static HTML to a file
*
* @param {string} filePath
* @param {Stream.Readable} readable
* @returns {Promise<void>}
*/
function pipe (filePath, readable) {
return new Promise((resolve, reject) => {
const outStream = fs.createWriteStream(filePath)
readable.pipe(outStream)
outStream.on('finish', resolve(filePath))
readable.on('error', reject)
})
}

/**
* Compile a webpack application and return stats json.
*
Expand Down
1 change: 1 addition & 0 deletions packages/@vuepress/core/package.json
Expand Up @@ -44,6 +44,7 @@
"lru-cache": "^5.1.1",
"mini-css-extract-plugin": "0.6.0",
"optimize-css-assets-webpack-plugin": "^5.0.1",
"p-limit": "^2.2.0",
"portfinder": "^1.0.13",
"postcss-loader": "^3.0.0",
"postcss-safe-parser": "^4.0.1",
Expand Down