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

Lifecycle-based Plugin Support #240

Closed
wants to merge 5 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ module.exports = {
description: 'Vue 驱动的静态网站生成器'
}
},
plugins: [
[
require('./vuepress-plugin-custom-domain'),
{
domain: 'www.vuejs.org'
}
]
],
head: [
['link', { rel: 'icon', href: `/logo.png` }],
['link', { rel: 'manifest', href: '/manifest.json' }],
Expand Down
23 changes: 23 additions & 0 deletions docs/.vuepress/vuepress-plugin-custom-domain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const path = require('path')
const fs = require('fs-extra')

module.exports = async function vuepressPluginCustomDomain({ domain }) {
this

.ready(() => {
console.log('Used vuepress-plugin-custom-domain')
})

.compiled(async ({ outDir }) => {
if (process.env.NODE_ENV !== 'production') {
return
}
await fs.ensureDir(outDir)
await fs.writeFile(path.resolve(outDir, 'CHAME'), domain, 'utf-8')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'CNAME' maybe

console.log('Generated CHAME file.')
})

.updated(() => {
console.log('Updated')
})
}
5 changes: 5 additions & 0 deletions lib/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ module.exports = async function build (sourceDir, cliOptions = {}) {
const createServerConfig = require('./webpack/createServerConfig')
const { createBundleRenderer } = require('vue-server-renderer')
const { normalizeHeadTag, applyUserWebpackConfig } = require('./util')
const lifecycle = require('./lifecycle')

process.stdout.write('Extracting site metadata...')
const options = await prepare(sourceDir)
if (cliOptions.outDir) {
options.outDir = cliOptions.outDir
}

lifecycle.notifyReady(Object.assign({}, options))

const { outDir } = options
await fs.remove(outDir)

Expand All @@ -36,6 +39,8 @@ module.exports = async function build (sourceDir, cliOptions = {}) {
// compile!
const stats = await compile([clientConfig, serverConfig])

await lifecycle.notifyCompiled(Object.assign({}, options))

const serverBundle = require(path.resolve(outDir, 'manifest/server.json'))
const clientManifest = require(path.resolve(outDir, 'manifest/client.json'))

Expand Down
31 changes: 23 additions & 8 deletions lib/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,30 @@ module.exports = async function dev (sourceDir, cliOptions = {}) {
const createClientConfig = require('./webpack/createClientConfig')
const { applyUserWebpackConfig } = require('./util')
const { frontmatterEmitter } = require('./webpack/markdownLoader')
const lifecycle = require('./lifecycle')

process.stdout.write('Extracting site metadata...')
process.stdout.write('Extracting site metadata...\n')
const options = await prepare(sourceDir)

lifecycle.notifyReady(Object.assign({}, options))

if (lifecycle.isEmpty('compiled')) {
lifecycle.compiled(({ port, displayHost }) => {
console.log(
`\n VuePress dev server listening at ${
chalk.cyan(`http://${displayHost}:${port}${options.publicPath}`)
}\n`
)
})
}

if (lifecycle.isEmpty('updated')) {
lifecycle.updated(() => {
const time = new Date().toTimeString().match(/^[\d:]+/)[0]
console.log(` ${chalk.gray(`[${time}]`)} ${chalk.green('✔')} successfully compiled.`)
})
}

// setup watchers to update options and dynamically generated files
const update = () => {
prepare(sourceDir).catch(err => {
Expand Down Expand Up @@ -87,14 +107,9 @@ module.exports = async function dev (sourceDir, cliOptions = {}) {
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`
)
lifecycle.notifyCompiled(Object.assign({ port, displayHost }, options))
} else {
const time = new Date().toTimeString().match(/^[\d:]+/)[0]
console.log(` ${chalk.gray(`[${time}]`)} ${chalk.green('✔')} successfully compiled.`)
lifecycle.notifyUpdated(Object.assign({ port, displayHost }, options))
}
})

Expand Down
34 changes: 34 additions & 0 deletions lib/lifecycle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const { EventEmitter } = require('events')

const HOOK_NAMES = ['ready', 'compiled', 'updated']

class VuePressHook extends EventEmitter {
constructor () {
super()
HOOK_NAMES.forEach(hook => {
const handlerKey = `_${hook}Handlers`
const execKey = `notify${hook.charAt(0).toUpperCase() + hook.slice(1)}`

this[handlerKey] = []

// public, Add hook handler
this[hook] = (handler) => {
this[handlerKey].push(handler.bind(this))
return this
}

// private, Execute hook handler
this[execKey] = async (...args) => {
for (const handler of this[handlerKey]) {
await handler(...args)
}
}
})
}

isEmpty (hook) {
return this[`_${hook}Handlers`].length === 0
}
}

module.exports = new VuePressHook()
33 changes: 33 additions & 0 deletions lib/prepare.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const yamlParser = require('js-yaml')
const tomlParser = require('toml')
const createMarkdown = require('./markdown')
const tempPath = path.resolve(__dirname, 'app/.temp')
const lifecycle = require('./lifecycle')
const { inferTitle, extractHeaders, parseFrontmatter } = require('./util')

fs.ensureDirSync(tempPath)
Expand Down Expand Up @@ -74,6 +75,14 @@ if (!Object.assign) Object.assign = require('object-assign')`
fs.existsSync(options.themeEnhanceAppPath)
)

// 8. handle plugins
for (const [pluginConfig, pluginOptions] of options.plugins) {
const pluginFn = resolvePlugin(pluginConfig)
if (pluginFn) {
pluginFn.bind(lifecycle)(pluginOptions)
}
}

return options
}

Expand Down Expand Up @@ -111,6 +120,12 @@ async function resolveOptions (sourceDir) {
})
}

// resolve plugins
const plugins = (Array.isArray(siteConfig.plugins)
? siteConfig.plugins
: []
)

// resolve theme
const useDefaultTheme = (
!siteConfig.theme &&
Expand Down Expand Up @@ -138,6 +153,7 @@ async function resolveOptions (sourceDir) {
notFoundPath: null,
useDefaultTheme,
isAlgoliaSearch,
plugins,
markdown: createMarkdown(siteConfig)
}

Expand Down Expand Up @@ -341,3 +357,20 @@ async function parseConfig (file) {

return data || {}
}

function resolvePlugin (pluginConfig) {
if (typeof pluginConfig === 'function') {
return pluginConfig
}
if (typeof pluginConfig === 'string') {
try {
return require(pluginConfig.startsWith('vuepress-plugin-')
? pluginConfig
: `vuepress-plugin-${pluginConfig}`
)
} catch (err) {
throw new Error(`[vuepress] Cannot resolve plugin: ${pluginConfig}`)
}
}
throw new Error(`[vuepress] Cannot resolve plugin: ${pluginConfig}`)
}