From 6b4ae2c7aad8db8f397624737317fbaa3962d104 Mon Sep 17 00:00:00 2001 From: ULIVZ <472590061@qq.com> Date: Sat, 26 May 2018 16:43:34 +0800 Subject: [PATCH 1/6] feat: init --- lib/dev.js | 57 +++++++++++++++++++---------------- lib/util/logger.js | 36 ++++++++++++++++++++++ lib/webpack/LogPlugin.js | 64 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 25 deletions(-) create mode 100644 lib/util/logger.js create mode 100644 lib/webpack/LogPlugin.js diff --git a/lib/dev.js b/lib/dev.js index 8e91c32d9c..0e0ac6f27d 100644 --- a/lib/dev.js +++ b/lib/dev.js @@ -9,10 +9,10 @@ module.exports = async function dev (sourceDir, cliOptions = {}) { const mount = require('koa-mount') const serveStatic = require('koa-static') const history = require('connect-history-api-fallback') - const portfinder = require('portfinder') const prepare = require('./prepare') const HeadPlugin = require('./webpack/HeadPlugin') + const LogPlugin = require('./webpack/LogPlugin') const createClientConfig = require('./webpack/createClientConfig') const { applyUserWebpackConfig } = require('./util') const { frontmatterEmitter } = require('./webpack/markdownLoader') @@ -67,6 +67,17 @@ module.exports = async function dev (sourceDir, cliOptions = {}) { tags: options.siteConfig.head || [] }]) + const port = await resolvePort(cliOptions.port || options.siteConfig.port) + const { host, displayHost } = await resolveHost(cliOptions.host || options.siteConfig.host) + + config + .plugin('fancy-log') + .use(LogPlugin, [{ + port, + displayHost, + publicPath: options.publicPath + }]) + config = config.toConfig() const userConfig = options.siteConfig.configureWebpack if (userConfig) { @@ -74,30 +85,6 @@ module.exports = async function dev (sourceDir, cliOptions = {}) { } const compiler = webpack(config) - // webpack-serve hot updates doesn't work properly over 0.0.0.0 on Windows, - // but localhost does not allow visiting over network :/ - const defaultHost = process.platform === 'win32' ? 'localhost' : '0.0.0.0' - const host = cliOptions.host || options.siteConfig.host || defaultHost - const displayHost = host === defaultHost && process.platform !== 'win32' - ? 'localhost' - : host - portfinder.basePort = cliOptions.port || options.siteConfig.port || 8080 - const port = await portfinder.getPortPromise() - - let isFirst = true - compiler.hooks.done.tap('vuepress', () => { - if (isFirst) { - isFirst = false - console.log( - `\n VuePress dev server listening at ${ - chalk.cyan(`http://${displayHost}:${port}${options.publicPath}`) - }\n` - ) - } else { - const time = new Date().toTimeString().match(/^[\d:]+/)[0] - console.log(` ${chalk.gray(`[${time}]`)} ${chalk.green('✔')} successfully compiled.`) - } - }) const nonExistentDir = path.resolve(__dirname, 'non-existent') await serve({ @@ -128,3 +115,23 @@ module.exports = async function dev (sourceDir, cliOptions = {}) { } }) } + +function resolveHost (host) { + // webpack-serve hot updates doesn't work properly over 0.0.0.0 on Windows, + // but localhost does not allow visiting over network :/ + const defaultHost = process.platform === 'win32' ? 'localhost' : '0.0.0.0' + host = host || defaultHost + const displayHost = host === defaultHost && process.platform !== 'win32' + ? 'localhost' + : host + return { + host, displayHost + } +} + +async function resolvePort (port) { + const portfinder = require('portfinder') + portfinder.basePort = port || 8080 + port = await portfinder.getPortPromise() + return port +} diff --git a/lib/util/logger.js b/lib/util/logger.js new file mode 100644 index 0000000000..fe66a01ee6 --- /dev/null +++ b/lib/util/logger.js @@ -0,0 +1,36 @@ +const chalk = require('chalk') + +const logger = {} + +const logTypes = { + success: { + color: 'green', + label: 'DONE' + }, + error: { + color: 'red', + label: 'FAIL' + }, + warn: { + color: 'yellow', + label: 'WARN' + }, + tip: { + color: 'cyan', + label: 'TIP' + } +} + +const getLoggerFn = (color, label) => (msg, log = true) => { + msg = chalk.reset.inverse.bold[color](` ${label} `) + ' ' + msg + return log ? console.log(msg) : msg +} + +for (const type in logTypes) { + const { color, label } = logTypes[type] + logger[type] = getLoggerFn(color, label) +} + +logger.label = (label, msg, color = 'blue', log = true) => getLoggerFn(color, label)(msg, log) + +module.exports = logger diff --git a/lib/webpack/LogPlugin.js b/lib/webpack/LogPlugin.js new file mode 100644 index 0000000000..472259fb0d --- /dev/null +++ b/lib/webpack/LogPlugin.js @@ -0,0 +1,64 @@ +const chalk = require('chalk') +const logger = require('../util/logger') + +module.exports = class LogPlugin { + constructor (options) { + this.options = options + } + + apply (compiler) { + if (this.options.mode === 'production') { + compiler.hooks.compile.tap('vuepress-log', clearScreen) + } else { + let isFirst = true + compiler.hooks.done.tap('vuepress-log', stats => { + clearScreen() + + if (stats.hasErrors()) { + process.exitCode = 1 + const { errors } = stats.compilation + console.log(errors) + console.log() + logger.error('Compiled with errors!') + return + } + + if (stats.hasWarnings()) { + process.exitCode = 1 + outputStats(stats) + console.log() + logger.error('Compiled with warnings!') + console.log() + return + } + + const { displayHost, port, publicPath } = this.options + const time = new Date().toTimeString().match(/^[\d:]+/)[0] + + logger.success(`${chalk.gray(`[${time}]`)} Build ${chalk.italic(stats.hash.slice(0, 6))} finished in ${stats.endTime - stats.startTime} ms!`) + if (isFirst) { + isFirst = false + console.log(`\nVuePress dev server listening at ${chalk.cyan(`http://${displayHost}:${port}${publicPath}`)}`) + } + }) + } + + compiler.hooks.invalid.tap('vuepress-log', clearScreen) + } +} + +function outputStats (stats) { + console.log(stats.toString({ + colors: true, + chunks: false, + modules: false, + children: false, + version: false, + hash: false, + timings: false + })) +} + +function clearScreen () { + process.stdout.write('\x1Bc') +} From d6bb15c81fadb8e8dbee3f02ce0cfe01a5cd1928 Mon Sep 17 00:00:00 2001 From: ULIVZ <472590061@qq.com> Date: Sat, 26 May 2018 17:31:59 +0800 Subject: [PATCH 2/6] refactor: use unified log --- lib/build.js | 11 ++++++----- lib/dev.js | 3 ++- lib/util/logger.js | 18 +++++++++++++++--- lib/webpack/LogPlugin.js | 33 ++------------------------------- 4 files changed, 25 insertions(+), 40 deletions(-) diff --git a/lib/build.js b/lib/build.js index b96f1096b4..78f0e44923 100644 --- a/lib/build.js +++ b/lib/build.js @@ -8,13 +8,14 @@ module.exports = async function build (sourceDir, cliOptions = {}) { const readline = require('readline') const escape = require('escape-html') + const logger = require('./util/logger') const prepare = require('./prepare') const createClientConfig = require('./webpack/createClientConfig') const createServerConfig = require('./webpack/createServerConfig') const { createBundleRenderer } = require('vue-server-renderer') const { normalizeHeadTag, applyUserWebpackConfig } = require('./util') - process.stdout.write('Extracting site metadata...') + logger.wait('Extracting site metadata...') const options = await prepare(sourceDir) if (cliOptions.outDir) { options.outDir = cliOptions.outDir @@ -22,7 +23,7 @@ module.exports = async function build (sourceDir, cliOptions = {}) { const { outDir } = options if (path.resolve() === outDir) { - return console.error(chalk.red('Unexpected option: outDir cannot be set to the current working directory.\n')) + return console.error(logger.error(chalk.red('Unexpected option: outDir cannot be set to the current working directory.\n'), false)) } await fs.remove(outDir) @@ -64,7 +65,7 @@ module.exports = async function build (sourceDir, cliOptions = {}) { .join('\n ') // render pages - console.log('Rendering static HTML...') + logger.wait('Rendering static HTML...') for (const page of options.siteData.pages) { await renderPage(page) } @@ -78,7 +79,7 @@ module.exports = async function build (sourceDir, cliOptions = {}) { readline.cursorTo(process.stdout, 0) if (options.siteConfig.serviceWorker) { - console.log('Generating service worker...') + logger.wait('Generating service worker...') const wbb = require('workbox-build') wbb.generateSW({ swDest: path.resolve(outDir, 'service-worker.js'), @@ -89,7 +90,7 @@ module.exports = async function build (sourceDir, cliOptions = {}) { // DONE. const relativeDir = path.relative(process.cwd(), outDir) - console.log(`\n${chalk.green('Success!')} Generated static files in ${chalk.cyan(relativeDir)}.`) + logger.success(`\n${chalk.green('Success!')} Generated static files in ${chalk.cyan(relativeDir)}.`) // --- helpers --- diff --git a/lib/dev.js b/lib/dev.js index 0e0ac6f27d..45bcbb775d 100644 --- a/lib/dev.js +++ b/lib/dev.js @@ -11,13 +11,14 @@ module.exports = async function dev (sourceDir, cliOptions = {}) { const history = require('connect-history-api-fallback') const prepare = require('./prepare') + const logger = require('./util/logger') const HeadPlugin = require('./webpack/HeadPlugin') const LogPlugin = require('./webpack/LogPlugin') const createClientConfig = require('./webpack/createClientConfig') const { applyUserWebpackConfig } = require('./util') const { frontmatterEmitter } = require('./webpack/markdownLoader') - process.stdout.write('Extracting site metadata...') + logger.wait('Extracting site metadata...') const options = await prepare(sourceDir) // setup watchers to update options and dynamically generated files diff --git a/lib/util/logger.js b/lib/util/logger.js index fe66a01ee6..9fd1a9a296 100644 --- a/lib/util/logger.js +++ b/lib/util/logger.js @@ -18,12 +18,25 @@ const logTypes = { tip: { color: 'cyan', label: 'TIP' + }, + wait: { + color: 'blue', + label: 'WAIT' } } const getLoggerFn = (color, label) => (msg, log = true) => { + let newLine = false + if (msg.startsWith('\n')) { + if (log) msg = msg.slice(1) + newLine = true + } msg = chalk.reset.inverse.bold[color](` ${label} `) + ' ' + msg - return log ? console.log(msg) : msg + if (log) { + console.log(newLine ? '\n' + msg : msg) + } else { + return msg + } } for (const type in logTypes) { @@ -31,6 +44,5 @@ for (const type in logTypes) { logger[type] = getLoggerFn(color, label) } -logger.label = (label, msg, color = 'blue', log = true) => getLoggerFn(color, label)(msg, log) - module.exports = logger +module.exports.getLoggerFn = getLoggerFn diff --git a/lib/webpack/LogPlugin.js b/lib/webpack/LogPlugin.js index 472259fb0d..0fa56659db 100644 --- a/lib/webpack/LogPlugin.js +++ b/lib/webpack/LogPlugin.js @@ -14,31 +14,14 @@ module.exports = class LogPlugin { compiler.hooks.done.tap('vuepress-log', stats => { clearScreen() - if (stats.hasErrors()) { - process.exitCode = 1 - const { errors } = stats.compilation - console.log(errors) - console.log() - logger.error('Compiled with errors!') - return - } - - if (stats.hasWarnings()) { - process.exitCode = 1 - outputStats(stats) - console.log() - logger.error('Compiled with warnings!') - console.log() - return - } - const { displayHost, port, publicPath } = this.options const time = new Date().toTimeString().match(/^[\d:]+/)[0] + console.log() logger.success(`${chalk.gray(`[${time}]`)} Build ${chalk.italic(stats.hash.slice(0, 6))} finished in ${stats.endTime - stats.startTime} ms!`) if (isFirst) { isFirst = false - console.log(`\nVuePress dev server listening at ${chalk.cyan(`http://${displayHost}:${port}${publicPath}`)}`) + console.log(`\n${chalk.gray('>')} VuePress dev server listening at ${chalk.cyan(`http://${displayHost}:${port}${publicPath}`)}`) } }) } @@ -47,18 +30,6 @@ module.exports = class LogPlugin { } } -function outputStats (stats) { - console.log(stats.toString({ - colors: true, - chunks: false, - modules: false, - children: false, - version: false, - hash: false, - timings: false - })) -} - function clearScreen () { process.stdout.write('\x1Bc') } From 8ebb7f7cbda2a5897b98952faf9b57bab4ce2104 Mon Sep 17 00:00:00 2001 From: ULIVZ <472590061@qq.com> Date: Sat, 26 May 2018 17:40:26 +0800 Subject: [PATCH 3/6] chore: tweaks --- lib/build.js | 8 ++++---- lib/dev.js | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/build.js b/lib/build.js index 78f0e44923..9587da5542 100644 --- a/lib/build.js +++ b/lib/build.js @@ -15,7 +15,7 @@ module.exports = async function build (sourceDir, cliOptions = {}) { const { createBundleRenderer } = require('vue-server-renderer') const { normalizeHeadTag, applyUserWebpackConfig } = require('./util') - logger.wait('Extracting site metadata...') + logger.wait('\nExtracting site metadata...') const options = await prepare(sourceDir) if (cliOptions.outDir) { options.outDir = cliOptions.outDir @@ -79,7 +79,7 @@ module.exports = async function build (sourceDir, cliOptions = {}) { readline.cursorTo(process.stdout, 0) if (options.siteConfig.serviceWorker) { - logger.wait('Generating service worker...') + logger.wait('\nGenerating service worker...') const wbb = require('workbox-build') wbb.generateSW({ swDest: path.resolve(outDir, 'service-worker.js'), @@ -90,7 +90,7 @@ module.exports = async function build (sourceDir, cliOptions = {}) { // DONE. const relativeDir = path.relative(process.cwd(), outDir) - logger.success(`\n${chalk.green('Success!')} Generated static files in ${chalk.cyan(relativeDir)}.`) + logger.success(`\n${chalk.green('Success!')} Generated static files in ${chalk.cyan(relativeDir)}.\n`) // --- helpers --- @@ -151,7 +151,7 @@ module.exports = async function build (sourceDir, cliOptions = {}) { try { html = await renderer.renderToString(context) } catch (e) { - console.error(chalk.red(`Error rendering ${pagePath}:`)) + console.error(logger.error(chalk.red(`Error rendering ${pagePath}:`), false)) throw e } const filename = decodeURIComponent(pagePath.replace(/\/$/, '/index.html').replace(/^\//, '')) diff --git a/lib/dev.js b/lib/dev.js index 45bcbb775d..30e439cc99 100644 --- a/lib/dev.js +++ b/lib/dev.js @@ -18,7 +18,7 @@ module.exports = async function dev (sourceDir, cliOptions = {}) { const { applyUserWebpackConfig } = require('./util') const { frontmatterEmitter } = require('./webpack/markdownLoader') - logger.wait('Extracting site metadata...') + logger.wait('\nExtracting site metadata...') const options = await prepare(sourceDir) // setup watchers to update options and dynamically generated files From a1e1e03b57f9d181d7b41cfafc37100cfa9fd677 Mon Sep 17 00:00:00 2001 From: ULIVZ <472590061@qq.com> Date: Sat, 26 May 2018 17:49:57 +0800 Subject: [PATCH 4/6] chore: rename LogPlugin to DevLogPlugin --- lib/dev.js | 6 +++--- lib/webpack/DevLogPlugin.js | 29 +++++++++++++++++++++++++++++ lib/webpack/LogPlugin.js | 35 ----------------------------------- 3 files changed, 32 insertions(+), 38 deletions(-) create mode 100644 lib/webpack/DevLogPlugin.js delete mode 100644 lib/webpack/LogPlugin.js diff --git a/lib/dev.js b/lib/dev.js index 30e439cc99..0f7fd35153 100644 --- a/lib/dev.js +++ b/lib/dev.js @@ -13,7 +13,7 @@ module.exports = async function dev (sourceDir, cliOptions = {}) { const prepare = require('./prepare') const logger = require('./util/logger') const HeadPlugin = require('./webpack/HeadPlugin') - const LogPlugin = require('./webpack/LogPlugin') + const DevLogPlugin = require('./webpack/DevLogPlugin') const createClientConfig = require('./webpack/createClientConfig') const { applyUserWebpackConfig } = require('./util') const { frontmatterEmitter } = require('./webpack/markdownLoader') @@ -24,7 +24,7 @@ module.exports = async function dev (sourceDir, cliOptions = {}) { // setup watchers to update options and dynamically generated files const update = () => { prepare(sourceDir).catch(err => { - console.error(chalk.red(err.stack)) + console.error(logger.error(chalk.red(err.stack), false)) }) } @@ -73,7 +73,7 @@ module.exports = async function dev (sourceDir, cliOptions = {}) { config .plugin('fancy-log') - .use(LogPlugin, [{ + .use(DevLogPlugin, [{ port, displayHost, publicPath: options.publicPath diff --git a/lib/webpack/DevLogPlugin.js b/lib/webpack/DevLogPlugin.js new file mode 100644 index 0000000000..8481b54dde --- /dev/null +++ b/lib/webpack/DevLogPlugin.js @@ -0,0 +1,29 @@ +const chalk = require('chalk') +const logger = require('../util/logger') + +module.exports = class DevLogPlugin { + constructor (options) { + this.options = options + } + + apply (compiler) { + let isFirst = true + compiler.hooks.done.tap('vuepress-log', stats => { + clearScreen() + + const { displayHost, port, publicPath } = this.options + const time = new Date().toTimeString().match(/^[\d:]+/)[0] + + logger.success(`\n${chalk.gray(`[${time}]`)} Build ${chalk.italic(stats.hash.slice(0, 6))} finished in ${stats.endTime - stats.startTime} ms!`) + if (isFirst) { + isFirst = false + console.log(`\n${chalk.gray('>')} VuePress dev server listening at ${chalk.cyan(`http://${displayHost}:${port}${publicPath}`)}`) + } + }) + compiler.hooks.invalid.tap('vuepress-log', clearScreen) + } +} + +function clearScreen () { + process.stdout.write('\x1Bc') +} diff --git a/lib/webpack/LogPlugin.js b/lib/webpack/LogPlugin.js deleted file mode 100644 index 0fa56659db..0000000000 --- a/lib/webpack/LogPlugin.js +++ /dev/null @@ -1,35 +0,0 @@ -const chalk = require('chalk') -const logger = require('../util/logger') - -module.exports = class LogPlugin { - constructor (options) { - this.options = options - } - - apply (compiler) { - if (this.options.mode === 'production') { - compiler.hooks.compile.tap('vuepress-log', clearScreen) - } else { - let isFirst = true - compiler.hooks.done.tap('vuepress-log', stats => { - clearScreen() - - const { displayHost, port, publicPath } = this.options - const time = new Date().toTimeString().match(/^[\d:]+/)[0] - - console.log() - logger.success(`${chalk.gray(`[${time}]`)} Build ${chalk.italic(stats.hash.slice(0, 6))} finished in ${stats.endTime - stats.startTime} ms!`) - if (isFirst) { - isFirst = false - console.log(`\n${chalk.gray('>')} VuePress dev server listening at ${chalk.cyan(`http://${displayHost}:${port}${publicPath}`)}`) - } - }) - } - - compiler.hooks.invalid.tap('vuepress-log', clearScreen) - } -} - -function clearScreen () { - process.stdout.write('\x1Bc') -} From e152559b4f954b4e7d21339b62c1583043c316e9 Mon Sep 17 00:00:00 2001 From: ULIVZ <472590061@qq.com> Date: Sat, 26 May 2018 17:57:46 +0800 Subject: [PATCH 5/6] refactor: use logger at where needed --- lib/eject.js | 3 ++- lib/markdown/highlight.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/eject.js b/lib/eject.js index 4d242f44d1..e96b00fd84 100644 --- a/lib/eject.js +++ b/lib/eject.js @@ -1,6 +1,7 @@ const fs = require('fs-extra') const path = require('path') const chalk = require('chalk') +const logger = require('./util/logger') module.exports = async (dir) => { const source = path.resolve(__dirname, 'default-theme') @@ -11,5 +12,5 @@ module.exports = async (dir) => { const content = await fs.readFile(styleConfig, 'utf-8') const transformed = content.split('\n').slice(0, -2).join('\n') await fs.writeFile(styleConfig, transformed) - console.log(`Copied default theme into ${chalk.cyan(target)}.`) + logger.success(`\nCopied default theme into ${chalk.cyan(target)}.\n`) } diff --git a/lib/markdown/highlight.js b/lib/markdown/highlight.js index 1c429ee2b1..ea739d4d54 100644 --- a/lib/markdown/highlight.js +++ b/lib/markdown/highlight.js @@ -2,6 +2,7 @@ const chalk = require('chalk') const prism = require('prismjs') const loadLanguages = require('prismjs/components/index') const escapeHtml = require('escape-html') +const logger = require('../util/logger') // required to make embedded highlighting work... loadLanguages(['markup', 'css', 'javascript']) @@ -32,7 +33,7 @@ module.exports = (str, lang) => { try { loadLanguages([lang]) } catch (e) { - console.log(chalk.yellow(`[vuepress] Syntax highlight for language "${lang}" is not supported.`)) + logger.warn(chalk.yellow(`[vuepress] Syntax highlight for language "${lang}" is not supported.`)) } } if (prism.languages[lang]) { From 88fc0388499c18af9c1f5ee2ae03d0d92a70ca5c Mon Sep 17 00:00:00 2001 From: ULIVZ <472590061@qq.com> Date: Sat, 26 May 2018 18:01:08 +0800 Subject: [PATCH 6/6] chore: tweaks --- lib/dev.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/dev.js b/lib/dev.js index 0f7fd35153..dbc7c1c187 100644 --- a/lib/dev.js +++ b/lib/dev.js @@ -72,7 +72,7 @@ module.exports = async function dev (sourceDir, cliOptions = {}) { const { host, displayHost } = await resolveHost(cliOptions.host || options.siteConfig.host) config - .plugin('fancy-log') + .plugin('vuepress-log') .use(DevLogPlugin, [{ port, displayHost, @@ -126,7 +126,8 @@ function resolveHost (host) { ? 'localhost' : host return { - host, displayHost + displayHost, + host } }