提交 0d7fce7c 编写于 作者: U ULIVZ

feat: pass plugins and theme via cli

上级 c004fb34
......@@ -2,5 +2,4 @@
node_modules
*.log
.temp
vuepress
TODOs.md
......@@ -23,98 +23,101 @@ if (!semver.satisfies(process.version, requiredVersion)) {
process.exit(1)
}
const path = require('path')
const { dev, build, eject } = require('@vuepress/core')
const program = require('commander')
program
.version(pkg.version)
.usage('<command> [options]')
program
.command('dev [targetDir]')
.description('start development server')
.option('-p, --port <port>', 'use specified port (default: 8080)')
.option('-h, --host <host>', 'use specified host (default: 0.0.0.0)')
.option('--debug', 'start development server in debug mode')
.action((dir = '.', { host, port, debug }) => {
wrapCommand(dev)(path.resolve(dir), { host, port, debug })
})
exports.program = program
exports.bootstrap = function ({
plugins,
theme
} = {}) {
const path = require('path')
const { dev, build, eject } = require('@vuepress/core')
program
.version(pkg.version)
.usage('<command> [options]')
program
.command('dev [targetDir]')
.description('start development server')
.option('-p, --port <port>', 'use specified port (default: 8080)')
.option('-h, --host <host>', 'use specified host (default: 0.0.0.0)')
.option('--debug', 'start development server in debug mode')
.action((dir = '.', { host, port, debug }) => {
wrapCommand(dev)(path.resolve(dir), { host, port, debug, plugins, theme })
})
program
.command('build [targetDir]')
.description('build dir as static site')
.option('-d, --dest <outDir>', 'specify build output dir (default: .vuepress/dist)')
.option('--debug', 'build in development mode for debugging')
.action((dir = '.', { debug, dest }) => {
const outDir = dest ? path.resolve(dest) : null
wrapCommand(build)(path.resolve(dir), { debug, outDir })
})
program
.command('build [targetDir]')
.description('build dir as static site')
.option('-d, --dest <outDir>', 'specify build output dir (default: .vuepress/dist)')
.option('--debug', 'build in development mode for debugging')
.action((dir = '.', { debug, dest }) => {
const outDir = dest ? path.resolve(dest) : null
wrapCommand(build)(path.resolve(dir), { debug, outDir, plugins, theme })
})
program
.command('eject [targetDir]')
.description('copy the default theme into .vuepress/theme for customization.')
.action((dir = '.') => {
wrapCommand(eject)(path.resolve(dir))
})
program
.command('eject [targetDir]')
.description('copy the default theme into .vuepress/theme for customization.')
.action((dir = '.') => {
wrapCommand(eject)(path.resolve(dir))
})
// output help information on unknown commands
program
.arguments('<command>')
.action((cmd) => {
program.outputHelp()
console.log(` ` + chalk.red(`Unknown command ${chalk.yellow(cmd)}.`))
// output help information on unknown commands
program
.arguments('<command>')
.action((cmd) => {
program.outputHelp()
console.log(` ` + chalk.red(`Unknown command ${chalk.yellow(cmd)}.`))
console.log()
})
// add some useful info on help
program.on('--help', () => {
console.log()
console.log(` Run ${chalk.cyan(`vuepress <command> --help`)} for detailed usage of given command.`)
console.log()
})
// add some useful info on help
program.on('--help', () => {
console.log()
console.log(` Run ${chalk.cyan(`vuepress <command> --help`)} for detailed usage of given command.`)
console.log()
})
program.commands.forEach(c => c.on('--help', () => console.log()))
// enhance common error messages
const enhanceErrorMessages = (methodName, log) => {
program.Command.prototype[methodName] = function (...args) {
if (methodName === 'unknownOption' && this._allowUnknownOption) {
return
program.commands.forEach(c => c.on('--help', () => console.log()))
// enhance common error messages
const enhanceErrorMessages = (methodName, log) => {
program.Command.prototype[methodName] = function (...args) {
if (methodName === 'unknownOption' && this._allowUnknownOption) {
return
}
this.outputHelp()
console.log(` ` + chalk.red(log(...args)))
console.log()
process.exit(1)
}
this.outputHelp()
console.log(` ` + chalk.red(log(...args)))
console.log()
process.exit(1)
}
}
enhanceErrorMessages('missingArgument', argName => {
return `Missing required argument ${chalk.yellow(`<${argName}>`)}.`
})
enhanceErrorMessages('unknownOption', optionName => {
return `Unknown option ${chalk.yellow(optionName)}.`
})
enhanceErrorMessages('optionMissingArgument', (option, flag) => {
return `Missing required argument for option ${chalk.yellow(option.flags)}` + (
flag ? `, got ${chalk.yellow(flag)}` : ``
)
})
function wrapCommand (fn) {
return (...args) => {
return fn(...args).catch(err => {
console.error(chalk.red(err.stack))
process.exitCode = 1
})
enhanceErrorMessages('missingArgument', argName => {
return `Missing required argument ${chalk.yellow(`<${argName}>`)}.`
})
enhanceErrorMessages('unknownOption', optionName => {
return `Unknown option ${chalk.yellow(optionName)}.`
})
enhanceErrorMessages('optionMissingArgument', (option, flag) => {
return `Missing required argument for option ${chalk.yellow(option.flags)}` + (
flag ? `, got ${chalk.yellow(flag)}` : ``
)
})
function wrapCommand (fn) {
return (...args) => {
return fn(...args).catch(err => {
console.error(chalk.red(err.stack))
process.exitCode = 1
})
}
}
}
exports.program = program
exports.bootstrap = function () {
program.parse(process.argv)
if (!process.argv.slice(2).length) {
program.outputHelp()
......
......@@ -16,7 +16,7 @@ module.exports = async function build (sourceDir, cliOptions = {}) {
const { normalizeHeadTag, applyUserWebpackConfig } = require('./util/index')
logger.wait('\nExtracting site metadata...')
const options = await prepare(sourceDir, true /* isProd */)
const options = await prepare({ sourceDir, cliOptions, isProd: true })
if (cliOptions.outDir) {
options.outDir = cliOptions.outDir
}
......
......@@ -20,7 +20,7 @@ module.exports = async function dev (sourceDir, cliOptions = {}) {
const { frontmatterEmitter } = require('./webpack/markdownLoader')
logger.wait('\nExtracting site metadata...')
const options = await prepare(sourceDir, false /* isProd */)
const options = await prepare({ sourceDir, cliOptions, isProd: false })
// setup watchers to update options and dynamically generated files
const update = () => {
......
......@@ -104,7 +104,7 @@ module.exports = class Plugin {
clientRootMixin,
additionalPages
}) {
logger.tip(`\nApply plugin ${chalk.cyan(name)}...`)
logger.tip(`\nApply plugin ${chalk.gray(name)}...`)
this
.registerHook(HOOK.READY, ready, name, [Function])
......
......@@ -8,9 +8,13 @@ const { writeTemp } = require('./util')
const logger = require('../util/logger')
const chalk = require('chalk')
module.exports = async function prepare (sourceDir, isProd) {
module.exports = async function prepare ({
sourceDir,
isProd,
cliOptions
}) {
// 1. load options
const options = await resolveOptions(sourceDir)
const options = await resolveOptions(sourceDir, cliOptions)
options.isProd = isProd
const { markdown } = options
......
......@@ -4,7 +4,6 @@ const yamlParser = require('js-yaml')
const tomlParser = require('toml')
module.exports = function loadConfig (vuepressDir, bustCache = true) {
console.log(vuepressDir)
const configPath = path.resolve(vuepressDir, 'config.js')
const configYmlPath = path.resolve(vuepressDir, 'config.yml')
const configTomlPath = path.resolve(vuepressDir, 'config.toml')
......
const fs = require('fs-extra')
const path = require('path')
const chalk = require('chalk')
const fs = require('fs-extra')
const globby = require('globby')
const createMarkdown = require('../markdown/index')
const loadConfig = require('./loadConfig')
const { sort } = require('./util')
const logger = require('../util/logger')
module.exports = async function resolveOptions (sourceDir) {
module.exports = async function resolveOptions (sourceDir, cliOptions) {
function requireResolve (target) {
return require.resolve(target, {
paths: [
......@@ -44,6 +46,8 @@ module.exports = async function resolveOptions (sourceDir) {
// resolve theme
const localThemePath = path.resolve(vuepressDir, 'theme')
const useLocalTheme = fs.existsSync(localThemePath)
const theme = siteConfig.theme || cliOptions.theme
let themePath = null
let themeLayoutPath = null
let themeNotFoundPath = null
......@@ -51,6 +55,8 @@ module.exports = async function resolveOptions (sourceDir) {
let themePlugins = []
if (useLocalTheme) {
logger.tip(`\nApply theme located at ${localThemePath}...`)
// use local custom theme
themePath = localThemePath
themeLayoutPath = path.resolve(localThemePath, 'Layout.vue')
......@@ -61,29 +67,30 @@ module.exports = async function resolveOptions (sourceDir) {
if (!fs.existsSync(themeNotFoundPath)) {
throw new Error(`[vuepress] Cannot resolve NotFound.vue file in .vuepress/theme.`)
}
} else if (siteConfig.theme) {
} else if (theme) {
// use external theme
try {
// backward-compatible 0.x.x.
themeLayoutPath = requireResolve(`vuepress-theme-${siteConfig.theme}/Layout.vue`)
themeLayoutPath = requireResolve(`vuepress-theme-${theme}/Layout.vue`)
themePath = path.dirname(themeLayoutPath)
themeNotFoundPath = path.resolve(themeLayoutPath, 'NotFound.vue')
} catch (e) {
try {
themeIndexFile = requireResolve(`vuepress-theme-${siteConfig.theme}/index.js`)
themeIndexFile = requireResolve(`vuepress-theme-${theme}/index.js`)
} catch (e) {
try {
themeIndexFile = requireResolve(`@vuepress/theme-${siteConfig.theme}`)
themeIndexFile = requireResolve(`@vuepress/theme-${theme}`)
themePath = path.dirname(themeIndexFile)
themeIndexFile = require(themeIndexFile)
themeLayoutPath = themeIndexFile.layout
themeNotFoundPath = themeIndexFile.notFound
themePlugins = themeIndexFile.plugins
} catch (e) {
throw new Error(`[vuepress] Failed to load custom theme "${siteConfig.theme}". File vuepress-theme-${siteConfig.theme}/Layout.vue does not exist.`)
throw new Error(`[vuepress] Failed to load custom theme "${theme}". File vuepress-theme-${theme}/Layout.vue does not exist.`)
}
}
}
logger.tip(`\nApply theme ${chalk.gray(theme)}`)
} else {
throw new Error(`[vuepress] You must specify a theme, or create a local custom theme. \n For more details, refer to https://vuepress.vuejs.org/guide/custom-themes.html#custom-themes. \n`)
}
......@@ -117,6 +124,7 @@ module.exports = async function resolveOptions (sourceDir) {
themeLayoutPath,
themeNotFoundPath,
themePlugins,
cliPlugins: cliOptions.plugins || [],
markdown
}
......
......@@ -7,12 +7,13 @@ const enhanceAppPlugin = require('@vuepress/plugin-enhance-app')
const registerGlobalComponentsPlugin = require('@vuepress/plugin-register-global-components')
module.exports = function (options) {
const { siteConfig, themeConfig, sourceDir, themePath, themePlugins } = options
const { siteConfig, themeConfig, sourceDir, themePath, themePlugins, cliPlugins } = options
const pluginContext = new PluginContext(options)
const plugin = new Plugin(pluginContext)
plugin
// user plugin
.useByConfigs(cliPlugins)
.useByConfigs(siteConfig.plugins)
.useByConfigs(themePlugins)
// built-in plugins
......
{
"name": "vuepress",
"version": "1.0.0",
"description": "Minimalistic doc generator with Vue component based layout system",
"main": "index.js",
"repository": {
"type": "git",
"url": "git+https://github.com/vuejs/vuepress.git"
},
"keywords": [
"documentation",
"vue",
"generator"
],
"bin": {
"vuepress": "vuepress.js"
},
"author": "Evan You",
"license": "MIT",
"bugs": {
"url": "https://github.com/vuejs/vuepress/issues"
},
"homepage": "https://github.com/vuejs/vuepress#readme",
"gitHooks": {
"pre-commit": "lint-staged"
},
"lint-staged": {
"*.{js,vue}": [
"eslint --fix",
"git add"
]
},
"dependencies": {
"@vuepress/core": "^1.0.0",
"@vuepress/cli": "^1.0.0",
"@vuepress/theme-default": "^1.0.0",
"@vuepress/plugin-test": "^1.0.0",
"@vuepress/plugin-translation-ui": "^1.0.0"
}
}
#!/usr/bin/env node
require('@vuepress/cli').bootstrap({
theme: 'default',
plugins: [
'test',
'translation-ui'
]
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册