diff --git a/.babelrc b/.babelrc
deleted file mode 100644
index 427e7a4d01..0000000000
--- a/.babelrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "env": {
- "test": {
- "presets": [
- ["@babel/preset-env", { "targets": { "node": 8 }}]
- ]
- }
- }
-}
diff --git a/.gitignore b/.gitignore
index 96930bdd73..2761c32ce8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,6 @@
node_modules
*.log
.temp
-vuepress
TODOs.md
+vuepress
+packages/blog-example
diff --git a/README.md b/README.md
index 3fc1522b56..2dedbe4f94 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,12 @@
+> This is the branch for `VuePress 1.0`.
+
+## Status: alpha
+
+Certain combinations of plugins may not work properly, and things may change or break until we reach beta phase. Do not use in production yet unless you are adventurous.
+
# VuePress
> Minimalistic docs generator with Vue component based layout system
@@ -78,8 +84,11 @@ Websites built with VuePress:
VuePress is still a work in progress. There are a few things that it currently does not support but are planned:
-- Plugin support
-- Blogging support
+- Migrate the old test.
+- `@vuepress/plugin-test-utils`.
+- `once` option for plugin options, which allows the same plugin only to be applied only once.
+- `theme` name shortcut.
+- `@vuepress/theme-blog`
Contributions are welcome!
diff --git a/__mocks__/@org/vuepress-plugin-a.js b/__mocks__/@org/vuepress-plugin-a.js
new file mode 100644
index 0000000000..4ba52ba2c8
--- /dev/null
+++ b/__mocks__/@org/vuepress-plugin-a.js
@@ -0,0 +1 @@
+module.exports = {}
diff --git a/__mocks__/@org/vuepress-plugin-b.js b/__mocks__/@org/vuepress-plugin-b.js
new file mode 100644
index 0000000000..4ba52ba2c8
--- /dev/null
+++ b/__mocks__/@org/vuepress-plugin-b.js
@@ -0,0 +1 @@
+module.exports = {}
diff --git a/__mocks__/@org/vuepress-theme-a/index.js b/__mocks__/@org/vuepress-theme-a/index.js
new file mode 100644
index 0000000000..4ba52ba2c8
--- /dev/null
+++ b/__mocks__/@org/vuepress-theme-a/index.js
@@ -0,0 +1 @@
+module.exports = {}
diff --git a/__mocks__/@vuepress/plugin-a.js b/__mocks__/@vuepress/plugin-a.js
new file mode 100644
index 0000000000..4ba52ba2c8
--- /dev/null
+++ b/__mocks__/@vuepress/plugin-a.js
@@ -0,0 +1 @@
+module.exports = {}
diff --git a/__mocks__/@vuepress/theme-a/index.js b/__mocks__/@vuepress/theme-a/index.js
new file mode 100644
index 0000000000..4ba52ba2c8
--- /dev/null
+++ b/__mocks__/@vuepress/theme-a/index.js
@@ -0,0 +1 @@
+module.exports = {}
diff --git a/__mocks__/vuepress-plugin-a.js b/__mocks__/vuepress-plugin-a.js
new file mode 100644
index 0000000000..4ba52ba2c8
--- /dev/null
+++ b/__mocks__/vuepress-plugin-a.js
@@ -0,0 +1 @@
+module.exports = {}
diff --git a/__mocks__/vuepress-plugin-b.js b/__mocks__/vuepress-plugin-b.js
new file mode 100644
index 0000000000..0b52ba9543
--- /dev/null
+++ b/__mocks__/vuepress-plugin-b.js
@@ -0,0 +1,3 @@
+module.exports = {
+ multiple: true
+}
diff --git a/__mocks__/vuepress-theme-a/index.js b/__mocks__/vuepress-theme-a/index.js
new file mode 100644
index 0000000000..4ba52ba2c8
--- /dev/null
+++ b/__mocks__/vuepress-theme-a/index.js
@@ -0,0 +1 @@
+module.exports = {}
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 0000000000..87edd47c65
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,9 @@
+module.exports = {
+ 'env': {
+ 'test': {
+ 'presets': [
+ ['@babel/preset-env', { 'targets': { 'node': 'current' }}]
+ ]
+ }
+ }
+}
diff --git a/bin/vuepress.js b/bin/vuepress.js
deleted file mode 100755
index 36433168e6..0000000000
--- a/bin/vuepress.js
+++ /dev/null
@@ -1,110 +0,0 @@
-#!/usr/bin/env node
-
-const chalk = require('chalk')
-const semver = require('semver')
-const requiredVersion = require('../package.json').engines.node
-
-if (!semver.satisfies(process.version, requiredVersion)) {
- console.log(chalk.red(
- `\n[vuepress] minimum Node version not met:` +
- `\nYou are using Node ${process.version}, but VuePress ` +
- `requires Node ${requiredVersion}.\nPlease upgrade your Node version.\n`
- ))
- process.exit(1)
-}
-
-const path = require('path')
-const { dev, build, eject } = require('../lib')
-
-const program = require('commander')
-
-program
- .version(require('../package.json').version)
- .usage(' [options]')
-
-program
- .command('dev [targetDir]')
- .description('start development server')
- .option('-p, --port ', 'use specified port (default: 8080)')
- .option('-h, --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 })
- })
-
-program
- .command('build [targetDir]')
- .description('build dir as static site')
- .option('-d, --dest ', '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('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('')
- .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 --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
- }
- 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)}` : ``
- )
-})
-
-program.parse(process.argv)
-
-if (!process.argv.slice(2).length) {
- program.outputHelp()
-}
-
-function wrapCommand (fn) {
- return (...args) => {
- return fn(...args).catch(err => {
- console.error(chalk.red(err.stack))
- process.exitCode = 1
- })
- }
-}
diff --git a/lerna.json b/lerna.json
new file mode 100644
index 0000000000..757fcfd570
--- /dev/null
+++ b/lerna.json
@@ -0,0 +1,6 @@
+{
+ "lerna": "2.5.1",
+ "npmClient": "yarn",
+ "useWorkspaces": true,
+ "version": "1.0.0"
+}
diff --git a/lib/app/clientEntry.js b/lib/app/clientEntry.js
deleted file mode 100644
index dc86314a97..0000000000
--- a/lib/app/clientEntry.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/* global BASE_URL, GA_ID, ga, SW_ENABLED, VUEPRESS_VERSION, LAST_COMMIT_HASH*/
-
-import { createApp } from './app'
-import SWUpdateEvent from './SWUpdateEvent'
-import { register } from 'register-service-worker'
-
-const { app, router } = createApp()
-
-window.__VUEPRESS_VERSION__ = {
- version: VUEPRESS_VERSION,
- hash: LAST_COMMIT_HASH
-}
-
-// Google analytics integration
-if (process.env.NODE_ENV === 'production' && GA_ID) {
- (function (i, s, o, g, r, a, m) {
- i['GoogleAnalyticsObject'] = r
- i[r] = i[r] || function () {
- (i[r].q = i[r].q || []).push(arguments)
- }
- i[r].l = 1 * new Date()
- a = s.createElement(o)
- m = s.getElementsByTagName(o)[0]
- a.async = 1
- a.src = g
- m.parentNode.insertBefore(a, m)
- })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga')
-
- ga('create', GA_ID, 'auto')
- ga('send', 'pageview')
-
- router.afterEach(function (to) {
- ga('set', 'page', app.$withBase(to.fullPath))
- ga('send', 'pageview')
- })
-}
-
-router.onReady(() => {
- app.$mount('#app')
-
- // Register service worker
- if (process.env.NODE_ENV === 'production' &&
- SW_ENABLED &&
- window.location.protocol === 'https:') {
- register(`${BASE_URL}service-worker.js`, {
- ready () {
- console.log('[vuepress:sw] Service worker is active.')
- app.$refs.layout.$emit('sw-ready')
- },
- cached (registration) {
- console.log('[vuepress:sw] Content has been cached for offline use.')
- app.$refs.layout.$emit('sw-cached', new SWUpdateEvent(registration))
- },
- updated (registration) {
- console.log('[vuepress:sw] Content updated.')
- app.$refs.layout.$emit('sw-updated', new SWUpdateEvent(registration))
- },
- offline () {
- console.log('[vuepress:sw] No internet connection found. App is running in offline mode.')
- app.$refs.layout.$emit('sw-offline')
- },
- error (err) {
- console.error('[vuepress:sw] Error during service worker registration:', err)
- app.$refs.layout.$emit('sw-error', err)
- if (GA_ID) {
- ga('send', 'exception', {
- exDescription: err.message,
- exFatal: false
- })
- }
- }
- })
- }
-})
diff --git a/lib/app/components/Content.js b/lib/app/components/Content.js
deleted file mode 100644
index 46628195e6..0000000000
--- a/lib/app/components/Content.js
+++ /dev/null
@@ -1,17 +0,0 @@
-export default {
- functional: true,
-
- props: {
- custom: {
- type: Boolean,
- default: true
- }
- },
-
- render (h, { parent, props, data }) {
- return h(parent.$page.key, {
- class: [props.custom ? 'custom' : '', data.class, data.staticClass],
- style: data.style
- })
- }
-}
diff --git a/lib/app/dataMixin.js b/lib/app/dataMixin.js
deleted file mode 100644
index fd003d926f..0000000000
--- a/lib/app/dataMixin.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import Vue from 'vue'
-import { findPageForPath } from './util'
-
-export default function dataMixin (siteData) {
- prepare(siteData)
- const store = new Vue({
- data: { siteData }
- })
-
- if (module.hot) {
- module.hot.accept('./.temp/siteData', () => {
- prepare(siteData)
- store.siteData = siteData
- })
- }
-
- return {
- computed: {
- $site () {
- return store.siteData
- },
- $localeConfig () {
- const { locales = {}} = this.$site
- let targetLang
- let defaultLang
- for (const path in locales) {
- if (path === '/') {
- defaultLang = locales[path]
- } else if (this.$page.path.indexOf(path) === 0) {
- targetLang = locales[path]
- }
- }
- return targetLang || defaultLang || {}
- },
- $siteTitle () {
- return this.$localeConfig.title || this.$site.title || ''
- },
- $title () {
- const page = this.$page
- const siteTitle = this.$siteTitle
- const selfTitle = page.frontmatter.home ? null : (
- page.frontmatter.title || // explicit title
- page.title // inferred title
- )
- return siteTitle
- ? selfTitle
- ? (selfTitle + ' | ' + siteTitle)
- : siteTitle
- : selfTitle || 'VuePress'
- },
- $description () {
- // #565 hoist description from meta
- if (this.$page.frontmatter.meta) {
- const descriptionMeta = this.$page.frontmatter.meta.filter(item => item.name === 'description')[0]
- if (descriptionMeta) return descriptionMeta.content
- }
- return this.$page.frontmatter.description || this.$localeConfig.description || this.$site.description || ''
- },
- $lang () {
- return this.$page.frontmatter.lang || this.$localeConfig.lang || 'en-US'
- },
- $localePath () {
- return this.$localeConfig.path || '/'
- },
- $themeLocaleConfig () {
- return (this.$site.themeConfig.locales || {})[this.$localePath] || {}
- },
- $page () {
- return findPageForPath(
- this.$site.pages,
- this.$route.path
- )
- }
- }
- }
-}
-
-function prepare (siteData) {
- siteData.pages.forEach(page => {
- if (!page.frontmatter) {
- page.frontmatter = {}
- }
- })
- if (siteData.locales) {
- Object.keys(siteData.locales).forEach(path => {
- siteData.locales[path].path = path
- })
- }
- Object.freeze(siteData)
-}
diff --git a/lib/app/root-mixins/index.js b/lib/app/root-mixins/index.js
deleted file mode 100644
index fd966f396e..0000000000
--- a/lib/app/root-mixins/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import updateMeta from './updateMeta'
-import activeHeaderLinks from '@activeHeaderLinks'
-
-export default [
- updateMeta, // required
- activeHeaderLinks // optional
-]
diff --git a/lib/app/serverEntry.js b/lib/app/serverEntry.js
deleted file mode 100644
index 715fc95653..0000000000
--- a/lib/app/serverEntry.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { createApp } from './app'
-
-export default context => new Promise((resolve, reject) => {
- const { app, router } = createApp()
- const { url } = context
- const { fullPath } = router.resolve(url).route
-
- if (fullPath !== url) {
- return reject({ url: fullPath })
- }
-
- router.push(url)
- router.onReady(() => resolve(app))
-})
diff --git a/lib/app/store.js b/lib/app/store.js
deleted file mode 100644
index 115fe0b9e6..0000000000
--- a/lib/app/store.js
+++ /dev/null
@@ -1,7 +0,0 @@
-// It is not yet time to use Vuex to manage the global state
-// singleton object as a global store.
-const state = {
- disableScrollBehavior: false
-}
-
-export default state
diff --git a/lib/app/util.js b/lib/app/util.js
deleted file mode 100644
index c11172240e..0000000000
--- a/lib/app/util.js
+++ /dev/null
@@ -1,19 +0,0 @@
-export function injectMixins (options, mixins) {
- if (!options.mixins) {
- options.mixins = []
- }
- options.mixins.push(...mixins)
-}
-
-export function findPageForPath (pages, path) {
- for (let i = 0; i < pages.length; i++) {
- const page = pages[i]
- if (page.path === path) {
- return page
- }
- }
- return {
- path: '',
- frontmatter: {}
- }
-}
diff --git a/lib/default-theme/SWUpdatePopup.vue b/lib/default-theme/SWUpdatePopup.vue
deleted file mode 100644
index b224db31ed..0000000000
--- a/lib/default-theme/SWUpdatePopup.vue
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/lib/markdown/index.js b/lib/markdown/index.js
deleted file mode 100644
index f4942f6ce6..0000000000
--- a/lib/markdown/index.js
+++ /dev/null
@@ -1,78 +0,0 @@
-const highlight = require('./highlight')
-const highlightLines = require('./highlightLines')
-const preWrapper = require('./preWrapper')
-const lineNumbers = require('./lineNumbers')
-const component = require('./component')
-const hoistScriptStyle = require('./hoist')
-const convertRouterLink = require('./link')
-const containers = require('./containers')
-const snippet = require('./snippet')
-const emoji = require('markdown-it-emoji')
-const anchor = require('markdown-it-anchor')
-const toc = require('markdown-it-table-of-contents')
-const _slugify = require('./slugify')
-const { parseHeaders } = require('../util/parseHeaders')
-
-module.exports = ({ markdown = {}} = {}) => {
- // allow user config slugify
- const slugify = markdown.slugify || _slugify
-
- const md = require('markdown-it')({
- html: true,
- highlight
- })
- // custom plugins
- .use(component)
- .use(highlightLines)
- .use(preWrapper)
- .use(snippet)
- .use(convertRouterLink, Object.assign({
- target: '_blank',
- rel: 'noopener noreferrer'
- }, markdown.externalLinks))
- .use(hoistScriptStyle)
- .use(containers)
-
- // 3rd party plugins
- .use(emoji)
- .use(anchor, Object.assign({
- slugify,
- permalink: true,
- permalinkBefore: true,
- permalinkSymbol: '#'
- }, markdown.anchor))
- .use(toc, Object.assign({
- slugify,
- includeLevel: [2, 3],
- format: parseHeaders
- }, markdown.toc))
-
- // apply user config
- if (markdown.config) {
- markdown.config(md)
- }
-
- if (markdown.lineNumbers) {
- md.use(lineNumbers)
- }
-
- module.exports.dataReturnable(md)
-
- // expose slugify
- md.slugify = slugify
-
- return md
-}
-
-module.exports.dataReturnable = function dataReturnable (md) {
- // override render to allow custom plugins return data
- const render = md.render
- md.render = (...args) => {
- md.__data = {}
- const html = render.call(md, ...args)
- return {
- html,
- data: md.__data
- }
- }
-}
diff --git a/lib/prepare/codegen.js b/lib/prepare/codegen.js
deleted file mode 100644
index 52e85355a2..0000000000
--- a/lib/prepare/codegen.js
+++ /dev/null
@@ -1,74 +0,0 @@
-const path = require('path')
-const { fileToComponentName, resolveComponents } = require('./util')
-
-exports.genRoutesFile = async function ({
- siteData: { pages },
- sourceDir,
- pageFiles
-}) {
- function genRoute ({ path: pagePath, key: componentName }, index) {
- const file = pageFiles[index]
- const filePath = path.resolve(sourceDir, file)
- let code = `
- {
- name: ${JSON.stringify(componentName)},
- path: ${JSON.stringify(pagePath)},
- component: ThemeLayout,
- beforeEnter: (to, from, next) => {
- import(${JSON.stringify(filePath)}).then(comp => {
- Vue.component(${JSON.stringify(componentName)}, comp.default)
- next()
- })
- }
- }`
-
- const dncodedPath = decodeURIComponent(pagePath)
- if (dncodedPath !== pagePath) {
- code += `,
- {
- path: ${JSON.stringify(dncodedPath)},
- redirect: ${JSON.stringify(pagePath)}
- }`
- }
-
- if (/\/$/.test(pagePath)) {
- code += `,
- {
- path: ${JSON.stringify(pagePath + 'index.html')},
- redirect: ${JSON.stringify(pagePath)}
- }`
- }
-
- return code
- }
-
- const notFoundRoute = `,
- {
- path: '*',
- component: ThemeNotFound
- }`
-
- return (
- `import ThemeLayout from '@themeLayout'\n` +
- `import ThemeNotFound from '@themeNotFound'\n` +
- `import { injectMixins } from '@app/util'\n` +
- `import rootMixins from '@app/root-mixins'\n\n` +
- `injectMixins(ThemeLayout, rootMixins)\n` +
- `injectMixins(ThemeNotFound, rootMixins)\n\n` +
- `export const routes = [${pages.map(genRoute).join(',')}${notFoundRoute}\n]`
- )
-}
-
-exports.genComponentRegistrationFile = async function ({ sourceDir }) {
- function genImport (file) {
- const name = fileToComponentName(file)
- const baseDir = path.resolve(sourceDir, '.vuepress/components')
- const absolutePath = path.resolve(baseDir, file)
- const code = `Vue.component(${JSON.stringify(name)}, () => import(${JSON.stringify(absolutePath)}))`
- return code
- }
-
- const components = (await resolveComponents(sourceDir)) || []
- return `import Vue from 'vue'\n` + components.map(genImport).join('\n')
-}
-
diff --git a/lib/prepare/index.js b/lib/prepare/index.js
deleted file mode 100644
index 7561f79158..0000000000
--- a/lib/prepare/index.js
+++ /dev/null
@@ -1,51 +0,0 @@
-const path = require('path')
-const fs = require('fs-extra')
-const resolveOptions = require('./resolveOptions')
-const { genRoutesFile, genComponentRegistrationFile } = require('./codegen')
-const { writeTemp, writeEnhanceTemp } = require('./util')
-const logger = require('../util/logger')
-const chalk = require('chalk')
-
-module.exports = async function prepare (sourceDir) {
- // 1. load options
- const options = await resolveOptions(sourceDir)
-
- // 2. generate routes & user components registration code
- const routesCode = await genRoutesFile(options)
- const componentCode = await genComponentRegistrationFile(options)
-
- await writeTemp('routes.js', [
- componentCode,
- routesCode
- ].join('\n'))
-
- // 3. generate siteData
- const dataCode = `export const siteData = ${JSON.stringify(options.siteData, null, 2)}`
- await writeTemp('siteData.js', dataCode)
-
- // 4. handle user override
- const overridePath = path.resolve(sourceDir, '.vuepress/override.styl').replace(/[\\]+/g, '/')
- const hasUserOverride = fs.existsSync(overridePath)
- await writeTemp('override.styl', hasUserOverride ? `@import(${JSON.stringify(overridePath)})` : ``)
-
- const stylePath = path.resolve(sourceDir, '.vuepress/style.styl').replace(/[\\]+/g, '/')
- const hasUserStyle = fs.existsSync(stylePath)
- await writeTemp('style.styl', hasUserStyle ? `@import(${JSON.stringify(stylePath)})` : ``)
-
- // Temporary tip, will be removed at next release.
- if (hasUserOverride && !hasUserStyle) {
- logger.tip(
- `${chalk.magenta('override.styl')} has been split into 2 APIs, we recommend you upgrade to continue.\n` +
- ` See: ${chalk.magenta('https://vuepress.vuejs.org/default-theme-config/#simple-css-override')}`
- )
- }
-
- // 5. handle enhanceApp.js
- const enhanceAppPath = path.resolve(sourceDir, '.vuepress/enhanceApp.js')
- await writeEnhanceTemp('enhanceApp.js', enhanceAppPath)
-
- // 6. handle the theme enhanceApp.js
- await writeEnhanceTemp('themeEnhanceApp.js', options.themeEnhanceAppPath)
-
- return options
-}
diff --git a/lib/prepare/resolveOptions.js b/lib/prepare/resolveOptions.js
deleted file mode 100644
index 9b97ca8e85..0000000000
--- a/lib/prepare/resolveOptions.js
+++ /dev/null
@@ -1,194 +0,0 @@
-const fs = require('fs-extra')
-const path = require('path')
-const globby = require('globby')
-const createMarkdown = require('../markdown')
-const loadConfig = require('./loadConfig')
-const { encodePath, fileToPath, sort, getGitLastUpdatedTimeStamp } = require('./util')
-const {
- inferTitle,
- extractHeaders,
- parseFrontmatter
-} = require('../util/index')
-
-module.exports = async function resolveOptions (sourceDir) {
- const vuepressDir = path.resolve(sourceDir, '.vuepress')
- const siteConfig = loadConfig(vuepressDir)
-
- // normalize head tag urls for base
- const base = siteConfig.base || '/'
- if (base !== '/' && siteConfig.head) {
- siteConfig.head.forEach(tag => {
- const attrs = tag[1]
- if (attrs) {
- for (const name in attrs) {
- if (name === 'src' || name === 'href') {
- const value = attrs[name]
- if (value.charAt(0) === '/') {
- attrs[name] = base + value.slice(1)
- }
- }
- }
- }
- })
- }
-
- // resolve outDir
- const outDir = siteConfig.dest
- ? path.resolve(siteConfig.dest)
- : path.resolve(sourceDir, '.vuepress/dist')
-
- // resolve theme
- const useDefaultTheme = (
- !siteConfig.theme &&
- !fs.existsSync(path.resolve(vuepressDir, 'theme'))
- )
- const defaultThemePath = path.resolve(__dirname, '../default-theme')
- let themePath = null
- let themeLayoutPath = null
- let themeNotFoundPath = null
- let themeEnhanceAppPath = null
-
- if (useDefaultTheme) {
- // use default theme
- themePath = defaultThemePath
- themeLayoutPath = path.resolve(defaultThemePath, 'Layout.vue')
- themeNotFoundPath = path.resolve(defaultThemePath, 'NotFound.vue')
- } else {
- // resolve theme Layout
- if (siteConfig.theme) {
- // use external theme
- try {
- themeLayoutPath = require.resolve(`vuepress-theme-${siteConfig.theme}/Layout.vue`, {
- paths: [
- path.resolve(__dirname, '../../node_modules'),
- path.resolve(sourceDir)
- ]
- })
- themePath = path.dirname(themeLayoutPath)
- } catch (e) {
- throw new Error(`[vuepress] Failed to load custom theme "${
- siteConfig.theme
- }". File vuepress-theme-${siteConfig.theme}/Layout.vue does not exist.`)
- }
- } else {
- // use custom theme
- themePath = path.resolve(vuepressDir, 'theme')
- themeLayoutPath = path.resolve(themePath, 'Layout.vue')
- if (!fs.existsSync(themeLayoutPath)) {
- throw new Error(`[vuepress] Cannot resolve Layout.vue file in .vuepress/theme.`)
- }
- }
-
- // resolve theme NotFound
- themeNotFoundPath = path.resolve(themePath, 'NotFound.vue')
- if (!fs.existsSync(themeNotFoundPath)) {
- themeNotFoundPath = path.resolve(defaultThemePath, 'NotFound.vue')
- }
-
- // resolve theme enhanceApp
- themeEnhanceAppPath = path.resolve(themePath, 'enhanceApp.js')
- if (!fs.existsSync(themeEnhanceAppPath)) {
- themeEnhanceAppPath = null
- }
- }
-
- // resolve theme config
- const themeConfig = siteConfig.themeConfig || {}
-
- // resolve algolia
- const isAlgoliaSearch = (
- themeConfig.algolia ||
- Object.keys(siteConfig.locales && themeConfig.locales || {})
- .some(base => themeConfig.locales[base].algolia)
- )
-
- // resolve markdown
- const markdown = createMarkdown(siteConfig)
-
- // resolve pageFiles
- const patterns = ['**/*.md', '!.vuepress', '!node_modules']
- if (siteConfig.dest) {
- // #654 exclude dest folder when dest dir was set in
- // sourceDir but not in '.vuepress'
- const outDirRelative = path.relative(sourceDir, outDir)
- if (!outDirRelative.includes('..')) {
- patterns.push('!' + outDirRelative)
- }
- }
- const pageFiles = sort(await globby(patterns, { cwd: sourceDir }))
-
- // resolve lastUpdated
- const shouldResolveLastUpdated = (
- themeConfig.lastUpdated ||
- Object.keys(siteConfig.locales && themeConfig.locales || {})
- .some(base => themeConfig.locales[base].lastUpdated)
- )
-
- // resolve pagesData
- const pagesData = await Promise.all(pageFiles.map(async (file) => {
- const filepath = path.resolve(sourceDir, file)
- const key = 'v-' + Math.random().toString(16).slice(2)
- const data = {
- key,
- path: encodePath(fileToPath(file))
- }
-
- if (shouldResolveLastUpdated) {
- data.lastUpdated = getGitLastUpdatedTimeStamp(filepath)
- }
-
- // extract yaml frontmatter
- const content = await fs.readFile(filepath, 'utf-8')
- const frontmatter = parseFrontmatter(content)
- // infer title
- const title = inferTitle(frontmatter)
- if (title) {
- data.title = title
- }
- const headers = extractHeaders(
- frontmatter.content,
- ['h2', 'h3'],
- markdown
- )
- if (headers.length) {
- data.headers = headers
- }
- if (Object.keys(frontmatter.data).length) {
- data.frontmatter = frontmatter.data
- }
- if (frontmatter.excerpt) {
- const { html } = markdown.render(frontmatter.excerpt)
- data.excerpt = html
- }
- return data
- }))
-
- // resolve site data
- const siteData = {
- title: siteConfig.title || '',
- description: siteConfig.description || '',
- base,
- pages: pagesData,
- themeConfig,
- locales: siteConfig.locales
- }
-
- const options = {
- siteConfig,
- siteData,
- sourceDir,
- outDir,
- publicPath: base,
- pageFiles,
- pagesData,
- themePath,
- themeLayoutPath,
- themeNotFoundPath,
- themeEnhanceAppPath,
- useDefaultTheme,
- isAlgoliaSearch,
- markdown
- }
-
- return options
-}
diff --git a/lib/prepare/util.js b/lib/prepare/util.js
deleted file mode 100644
index efa96cc9b7..0000000000
--- a/lib/prepare/util.js
+++ /dev/null
@@ -1,80 +0,0 @@
-const path = require('path')
-const spawn = require('cross-spawn')
-const fs = require('fs-extra')
-const globby = require('globby')
-
-const tempPath = path.resolve(__dirname, '../app/.temp')
-fs.ensureDirSync(tempPath)
-
-const tempCache = new Map()
-exports.writeTemp = async function (file, content) {
- // cache write to avoid hitting the dist if it didn't change
- const cached = tempCache.get(file)
- if (cached !== content) {
- await fs.writeFile(path.join(tempPath, file), content)
- tempCache.set(file, content)
- }
-}
-
-exports.writeEnhanceTemp = async function (destName, srcPath) {
- await exports.writeTemp(
- destName,
- fs.existsSync(srcPath)
- ? `export { default } from ${JSON.stringify(srcPath)}`
- : `export default function () {}`
- )
-}
-
-const indexRE = /(^|.*\/)(index|readme)\.md$/i
-const extRE = /\.(vue|md)$/
-
-exports.fileToPath = function (file) {
- if (exports.isIndexFile(file)) {
- // README.md -> /
- // foo/README.md -> /foo/
- return file.replace(indexRE, '/$1')
- } else {
- // foo.md -> /foo.html
- // foo/bar.md -> /foo/bar.html
- return `/${file.replace(extRE, '').replace(/\\/g, '/')}.html`
- }
-}
-
-exports.fileToComponentName = function (file) {
- let normalizedName = file
- .replace(/\/|\\/g, '-')
- .replace(extRE, '')
- if (exports.isIndexFile(file)) {
- normalizedName = normalizedName.replace(/readme$/i, 'index')
- }
- const pagePrefix = /\.md$/.test(file) ? `page-` : ``
- return `${pagePrefix}${normalizedName}`
-}
-
-exports.isIndexFile = function (file) {
- return indexRE.test(file)
-}
-
-exports.resolveComponents = async function (sourceDir) {
- const componentDir = path.resolve(sourceDir, '.vuepress/components')
- if (!fs.existsSync(componentDir)) {
- return
- }
- return exports.sort(await globby(['**/*.vue'], { cwd: componentDir }))
-}
-
-exports.sort = function (arr) {
- return arr.sort((a, b) => {
- if (a < b) return -1
- if (a > b) return 1
- return 0
- })
-}
-
-exports.encodePath = function (userpath) {
- return userpath.split('/').map(item => encodeURIComponent(item)).join('/')
-}
-
-exports.getGitLastUpdatedTimeStamp = function (filepath) {
- return parseInt(spawn.sync('git', ['log', '-1', '--format=%ct', filepath]).stdout.toString('utf-8')) * 1000
-}
diff --git a/lib/util/index.js b/lib/util/index.js
deleted file mode 100644
index 52044a8fcb..0000000000
--- a/lib/util/index.js
+++ /dev/null
@@ -1,83 +0,0 @@
-const { deeplyParseHeaders } = require('./parseHeaders')
-
-exports.normalizeHeadTag = function (tag) {
- if (typeof tag === 'string') {
- tag = [tag]
- }
- const tagName = tag[0]
- return {
- tagName,
- attributes: tag[1] || {},
- innerHTML: tag[2] || '',
- closeTag: !(tagName === 'meta' || tagName === 'link')
- }
-}
-
-exports.applyUserWebpackConfig = function (userConfig, config, isServer) {
- const merge = require('webpack-merge')
- if (typeof userConfig === 'object') {
- return merge(config, userConfig)
- }
- if (typeof userConfig === 'function') {
- const res = userConfig(config, isServer)
- if (res && typeof res === 'object') {
- return merge(config, res)
- }
- }
- return config
-}
-
-exports.inferTitle = function (frontmatter) {
- if (frontmatter.data.home) {
- return 'Home'
- }
- if (frontmatter.data.title) {
- return deeplyParseHeaders(frontmatter.data.title)
- }
- const match = frontmatter.content.trim().match(/^#+\s+(.*)/m)
- if (match) {
- return deeplyParseHeaders(match[1])
- }
-}
-
-exports.parseFrontmatter = function (content) {
- const matter = require('gray-matter')
- const toml = require('toml')
-
- return matter(content, {
- excerpt_separator: '',
- engines: {
- toml: toml.parse.bind(toml),
- excerpt: false
- }
- })
-}
-
-const LRU = require('lru-cache')
-const cache = LRU({ max: 1000 })
-
-exports.extractHeaders = function (content, include = [], md) {
- const key = content + include.join(',')
- const hit = cache.get(key)
- if (hit) {
- return hit
- }
-
- const tokens = md.parse(content, {})
-
- const res = []
- tokens.forEach((t, i) => {
- if (t.type === 'heading_open' && include.includes(t.tag)) {
- const title = tokens[i + 1].content
- const slug = t.attrs.find(([name]) => name === 'id')[1]
- res.push({
- level: parseInt(t.tag.slice(1), 10),
- title: deeplyParseHeaders(title),
- slug: slug || md.slugify(title)
- })
- }
- })
-
- cache.set(key, res)
- return res
-}
diff --git a/package.json b/package.json
index afdec2156d..5e2bc5b7e9 100644
--- a/package.json
+++ b/package.json
@@ -1,18 +1,22 @@
{
- "name": "vuepress",
- "version": "0.14.4",
+ "private": true,
+ "workspaces": [
+ "packages/@vuepress/*",
+ "packages/vuepress",
+ "packages/docs",
+ "packages/blog-example"
+ ],
"description": "Minimalistic doc generator with Vue component based layout system",
- "main": "lib/index.js",
- "bin": {
- "vuepress": "bin/vuepress.js"
- },
"scripts": {
- "dev": "node bin/vuepress dev docs",
- "build": "node bin/vuepress build docs",
- "lint": "eslint --fix --ext .js,.vue bin/ lib/ test/",
- "prepublishOnly": "conventional-changelog -p angular -r 2 -i CHANGELOG.md -s",
- "release": "/bin/bash scripts/release.sh",
- "test": "node test/prepare.js && jest --config test/jest.config.js"
+ "boot": "node scripts/bootstrap.js",
+ "dev": "yarn workspace docs dev",
+ "build": "yarn workspace docs build",
+ "dev:blog-example": "yarn workspace blog-example dev",
+ "build:blog-example": "yarn workspace blog-example build",
+ "lint": "eslint --fix packages/**/*.js packages/**/*.vue packages/**/bin/*",
+ "release": "yarn --pure-lockfile && node scripts/release.js",
+ "changelog": "node scripts/genChangelog.js run",
+ "test": "node scripts/test.js"
},
"repository": {
"type": "git",
@@ -38,78 +42,17 @@
"git add"
]
},
- "dependencies": {
- "@babel/core": "7.0.0-beta.47",
- "@vue/babel-preset-app": "3.0.0-beta.11",
- "autoprefixer": "^8.2.0",
- "babel-loader": "8.0.0-beta.3",
- "cache-loader": "^1.2.2",
- "chalk": "^2.3.2",
- "chokidar": "^2.0.3",
- "commander": "^2.15.1",
- "connect-history-api-fallback": "^1.5.0",
- "copy-webpack-plugin": "^4.5.1",
- "cross-spawn": "^6.0.5",
- "css-loader": "^0.28.11",
- "diacritics": "^1.3.0",
- "docsearch.js": "^2.5.2",
- "escape-html": "^1.0.3",
- "file-loader": "^1.1.11",
- "fs-extra": "^5.0.0",
- "globby": "^8.0.1",
- "gray-matter": "^4.0.1",
- "js-yaml": "^3.11.0",
- "koa-connect": "^2.0.1",
- "koa-mount": "^3.0.0",
- "koa-range": "^0.3.0",
- "koa-static": "^4.0.2",
- "loader-utils": "^1.1.0",
- "lodash.throttle": "^4.1.1",
- "lru-cache": "^4.1.2",
- "markdown-it": "^8.4.1",
- "markdown-it-anchor": "^5.0.2",
- "markdown-it-container": "^2.0.0",
- "markdown-it-emoji": "^1.4.0",
- "markdown-it-table-of-contents": "^0.4.0",
- "mini-css-extract-plugin": "^0.4.1",
- "nprogress": "^0.2.0",
- "optimize-css-assets-webpack-plugin": "^4.0.0",
- "portfinder": "^1.0.13",
- "postcss-loader": "^2.1.5",
- "prismjs": "^1.13.0",
- "register-service-worker": "^1.5.1",
- "semver": "^5.5.0",
- "stylus": "^0.54.5",
- "stylus-loader": "^3.0.2",
- "toml": "^2.3.3",
- "url-loader": "^1.0.1",
- "vue": "^2.5.16",
- "vue-loader": "^15.2.4",
- "vue-router": "^3.0.1",
- "vue-server-renderer": "^2.5.16",
- "vue-template-compiler": "^2.5.16",
- "vuepress-html-webpack-plugin": "^3.2.0",
- "webpack": "^4.8.1",
- "webpack-chain": "^4.6.0",
- "webpack-merge": "^4.1.2",
- "webpack-serve": "^1.0.2",
- "webpackbar": "^2.6.1",
- "workbox-build": "^3.1.0"
- },
"devDependencies": {
- "@vue/test-utils": "^1.0.0-beta.16",
- "babel-core": "^7.0.0-0",
- "babel-jest": "^23.0.0",
"conventional-changelog-cli": "^1.3.22",
"eslint": "^4.19.1",
"eslint-plugin-jest": "^21.15.1",
"eslint-plugin-vue-libs": "^3.0.0",
- "jest": "^23.0.0",
- "jest-serializer-vue": "^1.0.0",
+ "lerna": "^2.11.0",
"lint-staged": "^7.0.4",
- "vue-jest": "^2.6.0",
- "vuepress-theme-vue": "^1.1.0",
- "yorkie": "^1.0.3"
+ "minimist": "^1.2.0",
+ "yorkie": "^1.0.3",
+ "inquirer": "^6.2.0",
+ "@vue/conventional-changelog": "^0.1.1"
},
"engines": {
"node": ">=8"
diff --git a/packages/@vuepress/cli/.npmignore b/packages/@vuepress/cli/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/cli/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/cli/README.md b/packages/@vuepress/cli/README.md
new file mode 100644
index 0000000000..e2524b698b
--- /dev/null
+++ b/packages/@vuepress/cli/README.md
@@ -0,0 +1,18 @@
+# @vuepress/cli
+
+> cli for vuepress
+
+## APIs
+
+### program
+
+Current instance of [commander.js](https://github.com/tj/commander.js)
+
+### bootstrap(options)
+
+Launch the cli.
+
+#### options.plugins
+
+#### options.theme
+
\ No newline at end of file
diff --git a/packages/@vuepress/cli/index.js b/packages/@vuepress/cli/index.js
new file mode 100644
index 0000000000..9c846e4858
--- /dev/null
+++ b/packages/@vuepress/cli/index.js
@@ -0,0 +1,131 @@
+const { chalk } = require('@vuepress/shared-utils')
+const semver = require('semver')
+
+try {
+ require.resolve('@vuepress/core')
+} catch (err) {
+ console.log(chalk.red(
+ `\n[vuepress] @vuepress/cli ` +
+ `requires @vuepress/core to be installed.\n`
+ ))
+ process.exit(1)
+}
+
+const pkg = require('@vuepress/core/package.json')
+const requiredVersion = pkg.engines.node
+
+if (!semver.satisfies(process.version, requiredVersion)) {
+ console.log(chalk.red(
+ `\n[vuepress] minimum Node version not met:` +
+ `\nYou are using Node ${process.version}, but VuePress ` +
+ `requires Node ${requiredVersion}.\nPlease upgrade your Node version.\n`
+ ))
+ process.exit(1)
+}
+
+const program = require('commander')
+
+exports.program = program
+exports.bootstrap = function ({
+ plugins,
+ theme
+} = {}) {
+ const path = require('path')
+ const { dev, build, eject } = require('@vuepress/core')
+
+ program
+ .version(pkg.version)
+ .usage(' [options]')
+
+ program
+ .command('dev [targetDir]')
+ .description('start development server')
+ .option('-p, --port ', 'use specified port (default: 8080)')
+ .option('-h, --host ', 'use specified host (default: 0.0.0.0)')
+ .option('-t, --temp ', 'set the directory of the temporary file')
+ .option('-c, --cache ', 'set the directory of cache')
+ .option('--no-cache', 'clean the cache before build')
+ .option('--debug', 'start development server in debug mode')
+ .action((dir = '.', { host, port, debug, temp, cache }) => {
+ wrapCommand(dev)(path.resolve(dir), { host, port, debug, temp, cache, plugins, theme })
+ })
+
+ program
+ .command('build [targetDir]')
+ .description('build dir as static site')
+ .option('-d, --dest ', 'specify build output dir (default: .vuepress/dist)')
+ .option('-t, --temp ', 'set the directory of the temporary file')
+ .option('-c, --cache ', 'set the directory of cache')
+ .option('--no-cache', 'clean the cache before build')
+ .option('--debug', 'build in development mode for debugging')
+ .action((dir = '.', { debug, dest, temp, cache }) => {
+ const outDir = dest ? path.resolve(dest) : null
+ wrapCommand(build)(path.resolve(dir), { debug, outDir, plugins, theme, temp, cache })
+ })
+
+ 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('')
+ .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 --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
+ }
+ 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
+ })
+ }
+ }
+
+ program.parse(process.argv)
+ if (!process.argv.slice(2).length) {
+ program.outputHelp()
+ }
+}
diff --git a/packages/@vuepress/cli/package.json b/packages/@vuepress/cli/package.json
new file mode 100644
index 0000000000..f6105c8a68
--- /dev/null
+++ b/packages/@vuepress/cli/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "@vuepress/cli",
+ "version": "1.0.0",
+ "description": "cli for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vue-cli.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "dependencies": {
+ "chalk": "^2.3.2",
+ "semver": "^5.5.0"
+ },
+ "peerDependencies": {
+ "@vuepress/core": "^1.0.0"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/cli#readme"
+}
diff --git a/packages/@vuepress/core/.npmignore b/packages/@vuepress/core/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/core/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/core/README.md b/packages/@vuepress/core/README.md
new file mode 100644
index 0000000000..7829eb5850
--- /dev/null
+++ b/packages/@vuepress/core/README.md
@@ -0,0 +1,9 @@
+# @vuepress/core
+
+## APIs
+
+### dev(sourceDir, options)
+
+### build(sourceDir, options)
+
+### eject(targetDir)
diff --git a/packages/@vuepress/core/__test__/plugin-api/AsyncOption.spec.js b/packages/@vuepress/core/__test__/plugin-api/AsyncOption.spec.js
new file mode 100644
index 0000000000..357f1fb157
--- /dev/null
+++ b/packages/@vuepress/core/__test__/plugin-api/AsyncOption.spec.js
@@ -0,0 +1,24 @@
+const AsyncOption = require('../../lib/plugin-api/abstract/AsyncOption')
+
+describe('AsyncOption', () => {
+ test('parallelApply', async () => {
+ const option = new AsyncOption('option')
+ const handler1 = jest.fn()
+ const handler2 = jest.fn()
+
+ option.add('plugin-a', handler1)
+ option.add('plugin-b', handler2)
+
+ // TODO for now, if a class extends from another class.
+ // the original methods in that class will be lost.
+
+ await option.parallelApply(1, 2)
+ // expect(handler1.mock.calls).toHaveLength(1)
+ // expect(handler2.mock.calls).toHaveLength(1)
+ // expect(handler1.mock.calls[0][0]).toBe(1)
+ // expect(handler1.mock.calls[0][1]).toBe(2)
+ // expect(handler2.mock.calls[0][0]).toBe(1)
+ // expect(handler2.mock.calls[0][1]).toBe(2)
+ })
+})
+
diff --git a/packages/@vuepress/core/__test__/plugin-api/Option.spec.js b/packages/@vuepress/core/__test__/plugin-api/Option.spec.js
new file mode 100644
index 0000000000..e00a73608c
--- /dev/null
+++ b/packages/@vuepress/core/__test__/plugin-api/Option.spec.js
@@ -0,0 +1,90 @@
+import Option from '../../lib/plugin-api/abstract/Option'
+
+describe('Option', () => {
+ test('key', () => {
+ const option = new Option('option')
+ expect(option.key).toBe('option')
+ })
+
+ test('add', () => {
+ const option = new Option('option')
+ option.add('plugin-a', 'a')
+ option.add('plugin-b', 'b')
+
+ expect(option.items).toEqual([
+ { value: 'a', name: 'plugin-a' },
+ { value: 'b', name: 'plugin-b' }
+ ])
+
+ expect(option.values).toEqual(['a', 'b'])
+ })
+
+ test('add - resolve array', () => {
+ const option = new Option('option')
+ option.add('plugin-a', ['a-1', 'a-2'])
+ option.add('plugin-b', 'b')
+
+ expect(option.items).toEqual([
+ { value: 'a-1', name: 'plugin-a' },
+ { value: 'a-2', name: 'plugin-a' },
+ { value: 'b', name: 'plugin-b' }
+ ])
+ })
+
+ test('delete', () => {
+ const option = new Option('option')
+ option.add('plugin-a', ['a-1', 'a-2'])
+ option.add('plugin-b', 'b')
+
+ option.delete('plugin-a')
+
+ expect(option.items).toEqual([
+ { value: 'b', name: 'plugin-b' }
+ ])
+ })
+
+ test('clear', () => {
+ const option = new Option('option')
+ option.add('plugin-a', ['a-1', 'a-2'])
+
+ option.clear()
+
+ expect(option.items).toEqual([])
+ })
+
+ test('syncApply', () => {
+ const option = new Option('option')
+ const handler1 = jest.fn()
+ const handler2 = jest.fn()
+
+ option.add('plugin-a', handler1)
+ option.add('plugin-b', handler2)
+
+ option.syncApply('p1', 'p2')
+ expect(handler1.mock.calls).toHaveLength(1)
+ expect(handler2.mock.calls).toHaveLength(1)
+ expect(handler1.mock.calls[0][0]).toBe('p1')
+ expect(handler1.mock.calls[0][1]).toBe('p2')
+ expect(handler2.mock.calls[0][0]).toBe('p1')
+ expect(handler2.mock.calls[0][1]).toBe('p2')
+ })
+
+ test('appliedItems', () => {
+ const option = new Option('option')
+ const fn1 = () => 'fn1'
+ const fn2 = () => 'fn2'
+ const handler1 = jest.fn(fn1)
+ const handler2 = jest.fn(fn2)
+
+ option.add('plugin-a', handler1)
+ option.add('plugin-b', handler2)
+
+ option.syncApply(1, 2)
+
+ expect(option.appliedItems).toEqual([
+ { value: 'fn1', name: 'plugin-a' },
+ { value: 'fn2', name: 'plugin-b' }
+ ])
+ })
+})
+
diff --git a/packages/@vuepress/core/__test__/plugin-api/PluginAPI.spec.js b/packages/@vuepress/core/__test__/plugin-api/PluginAPI.spec.js
new file mode 100644
index 0000000000..7c580aacde
--- /dev/null
+++ b/packages/@vuepress/core/__test__/plugin-api/PluginAPI.spec.js
@@ -0,0 +1,87 @@
+jest.mock('vuepress-plugin-a')
+jest.mock('vuepress-plugin-b')
+jest.mock('@org/vuepress-plugin-a')
+
+import PluginAPI from '../../lib/plugin-api/index'
+import { PLUGIN_OPTION_MAP } from '../../lib/plugin-api/constants'
+
+describe('Plugin', () => {
+ test('registerOption', () => {
+ const api = new PluginAPI()
+ const readyHandler = () => {}
+ api.registerOption(PLUGIN_OPTION_MAP.READY.key, readyHandler)
+ expect(api.options.ready.values).toHaveLength(1)
+ expect(api.options.ready.values[0]).toBe(readyHandler)
+ })
+
+ test('useByPluginsConfig', () => {
+ [
+ ['a'],
+ [['a']],
+ [['a', true]],
+ { a: true }
+ ].forEach(pluginsConfig => {
+ const api = new PluginAPI()
+ api.useByPluginsConfig(pluginsConfig)
+ expect(api.enabledPlugins).toHaveLength(1)
+ expect(api.enabledPlugins[0].name).toBe('vuepress-plugin-a')
+ expect(api.disabledPlugins).toHaveLength(0)
+ })
+ })
+
+ test('useByPluginsConfig - disable plugin', () => {
+ [
+ [['a', false]],
+ { a: false }
+ ].forEach(pluginsConfig => {
+ const api = new PluginAPI()
+ api.useByPluginsConfig(pluginsConfig)
+ expect(api.enabledPlugins).toHaveLength(0)
+ expect(api.disabledPlugins).toHaveLength(1)
+ expect(api.disabledPlugins[0].name).toBe('vuepress-plugin-a')
+ })
+ })
+
+ test('useByPluginsConfig - get options', () => {
+ const pluginOptions = {};
+ [
+ [['a', pluginOptions]],
+ { a: pluginOptions }
+ ].forEach(pluginsConfig => {
+ const api = new PluginAPI()
+ api.useByPluginsConfig(pluginsConfig)
+ expect(api.enabledPlugins[0].$$options).toBe(pluginOptions)
+ })
+ })
+
+ test('ensure the namesake plugin is only executed once.', () => {
+ const pluginOptions1 = {}
+ const pluginOptions2 = {}
+ const pluginOptions3 = {}
+ const pluginsConfig = [
+ ['a', pluginOptions1],
+ ['a', pluginOptions2],
+ ['a', pluginOptions3]
+ ]
+ const api = new PluginAPI()
+ api.useByPluginsConfig(pluginsConfig)
+ expect(api.enabledPlugins).toHaveLength(1)
+ // using the last one
+ expect(api.enabledPlugins[0].$$options).toBe(pluginOptions3)
+ })
+
+ test('ensure a "multuple" plugin can be applied multuple times.', () => {
+ const pluginOptions1 = { a: 1 }
+ const pluginOptions2 = { b: 1 }
+ const pluginsConfig = [
+ ['b', pluginOptions1],
+ ['b', pluginOptions2]
+ ]
+ const api = new PluginAPI()
+ api.useByPluginsConfig(pluginsConfig)
+ expect(api.enabledPlugins).toHaveLength(2)
+ // using the last one
+ expect(api.enabledPlugins[0].$$options).toBe(pluginOptions1)
+ expect(api.enabledPlugins[1].$$options).toBe(pluginOptions2)
+ })
+})
diff --git a/packages/@vuepress/core/__test__/plugin-api/PluginUtil.spec.js b/packages/@vuepress/core/__test__/plugin-api/PluginUtil.spec.js
new file mode 100644
index 0000000000..2bc618c2d3
--- /dev/null
+++ b/packages/@vuepress/core/__test__/plugin-api/PluginUtil.spec.js
@@ -0,0 +1,45 @@
+import { flattenPlugin } from '../../lib/plugin-api/util'
+
+describe('flattenPlugin', () => {
+ test('shoould hydrate plugin correctly', () => {
+ const plugin = { name: 'a', shortcut: 'a', module: { enhanceAppFiles: 'file' }}
+ const hydratedPlugin = flattenPlugin(plugin, {}, {})
+ expect(hydratedPlugin.name).toBe('a')
+ expect(hydratedPlugin.shortcut).toBe('a')
+ expect(hydratedPlugin.enabled).toBe(true)
+ expect(hydratedPlugin.enhanceAppFiles).toBe('file')
+ })
+
+ test('shoould set \'enabled\' to false when \'pluginOptions\' is set to false.', () => {
+ const plugin = { name: 'a', shortcut: 'a', module: {}}
+ const hydratedPlugin = flattenPlugin(plugin, false, {})
+ expect(hydratedPlugin.name).toBe('a')
+ expect(hydratedPlugin.shortcut).toBe('a')
+ expect(hydratedPlugin.enabled).toBe(false)
+ })
+
+ test('shoould flatten functional plugin correctly.', () => {
+ const config = jest.fn(() => ({ enhanceAppFiles: 'file' }))
+ const plugin = { name: 'a', shortcut: 'a', module: config }
+ const pluginOptions = {}
+ const pluginContext = {}
+ const hydratedPlugin = flattenPlugin(plugin, pluginOptions, pluginContext)
+ expect(hydratedPlugin.name).toBe('a')
+ expect(hydratedPlugin.shortcut).toBe('a')
+ expect(hydratedPlugin.enabled).toBe(true)
+ expect(hydratedPlugin.enhanceAppFiles).toBe('file')
+ expect(config.mock.calls).toHaveLength(1)
+ expect(config.mock.calls[0][0]).toBe(pluginOptions)
+ expect(Object.getPrototypeOf(config.mock.calls[0][1])).toBe(pluginContext)
+ })
+
+ test('shoould flatten functional plugin correctly - options defaults to \'{}\'.', () => {
+ const config = jest.fn(() => ({ enhanceAppFiles: 'file' }))
+ const plugin = { name: 'a', shortcut: 'a', module: config }
+ const pluginOptions = undefined
+ const pluginContext = {}
+ flattenPlugin(plugin, pluginOptions, pluginContext)
+ expect(config.mock.calls[0][0]).toEqual({})
+ expect(Object.getPrototypeOf(config.mock.calls[0][1])).toBe(pluginContext)
+ })
+})
diff --git a/packages/@vuepress/core/__test__/prepare/Page.spec.js b/packages/@vuepress/core/__test__/prepare/Page.spec.js
new file mode 100644
index 0000000000..294ae9c790
--- /dev/null
+++ b/packages/@vuepress/core/__test__/prepare/Page.spec.js
@@ -0,0 +1,98 @@
+const Page = require('../../lib/prepare/Page')
+const {
+ getComputed,
+ getMarkdown,
+ getDocument,
+ readFile
+} = require('./util')
+
+describe('Page', () => {
+ test('pure route', async () => {
+ const page = new Page({ path: '/' })
+
+ expect(page.path).toBe('/')
+ expect(page.regularPath).toBe('/')
+
+ const computed = getComputed()
+ await page.process({ computed })
+
+ expect(page.path).toBe('/')
+ expect(page.regularPath).toBe('/')
+ })
+
+ test('pure route - encodeURI', async () => {
+ const path = '/尤/'
+ const page = new Page({ path })
+
+ expect(page.path).toBe(encodeURI(path))
+ expect(page.regularPath).toBe(encodeURI(path))
+ })
+
+ test('pure route - custom frontmatter', async () => {
+ const frontmatter = { title: 'alpha' }
+ const page = new Page({
+ path: '/',
+ frontmatter
+ })
+ expect(page.frontmatter).toBe(frontmatter)
+ })
+
+ test('pure route - enhancers', async () => {
+ const frontmatter = { title: 'alpha' }
+ const page = new Page({
+ path: '/',
+ frontmatter
+ })
+
+ expect(page.frontmatter.title).toBe('alpha')
+
+ const computed = getComputed()
+ const enhancers = [
+ {
+ name: 'plugin-a',
+ value: page => { page.frontmatter.title = 'beta' }
+ }
+ ]
+ await page.process({ computed, enhancers })
+
+ expect(page.frontmatter.title).toBe('beta')
+ })
+
+ test('markdown page - pointing to a markdown file', async () => {
+ const { relative, filePath } = getDocument('README.md')
+ const page = new Page({ filePath, relative })
+
+ expect(page._filePath).toBe(filePath)
+ expect(page.regularPath).toBe('/')
+ expect(page.path).toBe('/')
+ expect(page.frontmatter).toEqual({})
+
+ const computed = getComputed()
+ const markdown = getMarkdown()
+ await page.process({ computed, markdown })
+
+ expect(page.title).toBe('Home')
+ const content = await readFile(filePath)
+ expect(page._content).toBe(content)
+ expect(page._strippedContent).toBe(content)
+ })
+
+ test('markdown page - pointing to a markdown file with frontmatter', async () => {
+ const { relative, filePath } = getDocument('alpha.md')
+ const page = new Page({ filePath, relative })
+
+ expect(page._filePath).toBe(filePath)
+ expect(page.regularPath).toBe('/alpha.html')
+ expect(page.path).toBe('/alpha.html')
+ expect(page.frontmatter).toEqual({})
+
+ const computed = getComputed()
+ const markdown = getMarkdown()
+ await page.process({ computed, markdown })
+
+ expect(page.title).toBe(page.frontmatter.title)
+ expect(page._content.startsWith('---')).toBe(true)
+ expect(page._strippedContent.startsWith('---')).toBe(false)
+ })
+})
+
diff --git a/test/prepare/fixtures/docs-simple-config/.vuepress/config.js b/packages/@vuepress/core/__test__/prepare/fixtures/docs-config/.vuepress/config.js
similarity index 79%
rename from test/prepare/fixtures/docs-simple-config/.vuepress/config.js
rename to packages/@vuepress/core/__test__/prepare/fixtures/docs-config/.vuepress/config.js
index 70f49c13b3..665e1164b3 100644
--- a/test/prepare/fixtures/docs-simple-config/.vuepress/config.js
+++ b/packages/@vuepress/core/__test__/prepare/fixtures/docs-config/.vuepress/config.js
@@ -1,6 +1,6 @@
module.exports = {
title: 'Hello VuePress',
- description: 'Just playing around',
+ description: '# Hello, VuePress!',
dest: 'vuepress',
base: 'vuepress',
head: [
diff --git a/packages/@vuepress/core/__test__/prepare/fixtures/docs-config/README.md b/packages/@vuepress/core/__test__/prepare/fixtures/docs-config/README.md
new file mode 100644
index 0000000000..aef89369f1
--- /dev/null
+++ b/packages/@vuepress/core/__test__/prepare/fixtures/docs-config/README.md
@@ -0,0 +1 @@
+# Hello, VuePress!
diff --git a/packages/@vuepress/core/__test__/prepare/fixtures/docs-i18n/.vuepress/config.js b/packages/@vuepress/core/__test__/prepare/fixtures/docs-i18n/.vuepress/config.js
new file mode 100644
index 0000000000..1fbdc1cec1
--- /dev/null
+++ b/packages/@vuepress/core/__test__/prepare/fixtures/docs-i18n/.vuepress/config.js
@@ -0,0 +1,34 @@
+module.exports = {
+ dest: 'vuepress',
+ locales: {
+ '/': {
+ lang: 'en-US',
+ title: 'VuePress',
+ description: 'Vue-powered Static Site Generator'
+ },
+ '/zh/': {
+ lang: 'zh-CN',
+ title: 'VuePress',
+ description: 'Vue 驱动的静态网站生成器'
+ }
+ },
+ themeConfig: {
+ repo: 'vuejs/vuepress',
+ editLinks: true,
+ docsDir: 'docs',
+ locales: {
+ '/': {
+ label: 'English',
+ selectText: 'Languages',
+ editLinkText: 'Edit this page on GitHub',
+ lastUpdated: 'Last Updated'
+ },
+ '/zh/': {
+ label: '简体中文',
+ selectText: '选择语言',
+ editLinkText: '在 GitHub 上编辑此页',
+ lastUpdated: '上次更新',
+ }
+ }
+ }
+}
diff --git a/packages/@vuepress/core/__test__/prepare/fixtures/docs-i18n/README.md b/packages/@vuepress/core/__test__/prepare/fixtures/docs-i18n/README.md
new file mode 100644
index 0000000000..aef89369f1
--- /dev/null
+++ b/packages/@vuepress/core/__test__/prepare/fixtures/docs-i18n/README.md
@@ -0,0 +1 @@
+# Hello, VuePress!
diff --git a/packages/@vuepress/core/__test__/prepare/fixtures/docs-i18n/zh/README.md b/packages/@vuepress/core/__test__/prepare/fixtures/docs-i18n/zh/README.md
new file mode 100644
index 0000000000..c526a98742
--- /dev/null
+++ b/packages/@vuepress/core/__test__/prepare/fixtures/docs-i18n/zh/README.md
@@ -0,0 +1 @@
+# 你好, VuePress!
diff --git a/test/prepare/fixtures/docs-custom-theme/.vuepress/config.js b/packages/@vuepress/core/__test__/prepare/fixtures/docs-local-theme/.vuepress/config.js
similarity index 56%
rename from test/prepare/fixtures/docs-custom-theme/.vuepress/config.js
rename to packages/@vuepress/core/__test__/prepare/fixtures/docs-local-theme/.vuepress/config.js
index a5f568cb6d..0a8ba98858 100644
--- a/test/prepare/fixtures/docs-custom-theme/.vuepress/config.js
+++ b/packages/@vuepress/core/__test__/prepare/fixtures/docs-local-theme/.vuepress/config.js
@@ -1,4 +1,4 @@
module.exports = {
title: 'Hello VuePress',
- description: 'Just playing around'
+ description: '# Hello, VuePress!'
}
diff --git a/test/prepare/fixtures/docs-custom-theme/.vuepress/theme/Layout.vue b/packages/@vuepress/core/__test__/prepare/fixtures/docs-local-theme/.vuepress/theme/Layout.vue
similarity index 100%
rename from test/prepare/fixtures/docs-custom-theme/.vuepress/theme/Layout.vue
rename to packages/@vuepress/core/__test__/prepare/fixtures/docs-local-theme/.vuepress/theme/Layout.vue
diff --git a/packages/@vuepress/core/__test__/prepare/fixtures/docs-local-theme/README.md b/packages/@vuepress/core/__test__/prepare/fixtures/docs-local-theme/README.md
new file mode 100644
index 0000000000..aef89369f1
--- /dev/null
+++ b/packages/@vuepress/core/__test__/prepare/fixtures/docs-local-theme/README.md
@@ -0,0 +1 @@
+# Hello, VuePress!
diff --git a/packages/@vuepress/core/__test__/prepare/fixtures/docs/README.md b/packages/@vuepress/core/__test__/prepare/fixtures/docs/README.md
new file mode 100644
index 0000000000..291ca3867f
--- /dev/null
+++ b/packages/@vuepress/core/__test__/prepare/fixtures/docs/README.md
@@ -0,0 +1 @@
+# Home
diff --git a/packages/@vuepress/core/__test__/prepare/fixtures/docs/alpha.md b/packages/@vuepress/core/__test__/prepare/fixtures/docs/alpha.md
new file mode 100644
index 0000000000..67de3e0ace
--- /dev/null
+++ b/packages/@vuepress/core/__test__/prepare/fixtures/docs/alpha.md
@@ -0,0 +1,5 @@
+---
+title: VuePress Alpha
+---
+
+# Alpha
diff --git a/packages/@vuepress/core/__test__/prepare/prepare.spec.js b/packages/@vuepress/core/__test__/prepare/prepare.spec.js
new file mode 100644
index 0000000000..420ebaaddc
--- /dev/null
+++ b/packages/@vuepress/core/__test__/prepare/prepare.spec.js
@@ -0,0 +1,21 @@
+const { fs } = require('@vuepress/shared-utils')
+const path = require('path')
+const prepare = require('../../lib/prepare')
+
+const docsBaseDir = path.resolve(__dirname, 'fixtures')
+const docsModeNames = fs.readdirSync(docsBaseDir)
+const docsModes = docsModeNames.map(name => {
+ const docsPath = path.resolve(docsBaseDir, name)
+ const docsTempPath = path.resolve(docsPath, '.vuepress/.temp')
+ return { name, docsPath, docsTempPath }
+})
+
+describe('prepare', () => {
+ test('should not throw error', async () => {
+ await Promise.all(docsModes.map(async ({ name, docsPath, docsTempPath }) => {
+ await fs.ensureDir(docsTempPath)
+ const context = await prepare(docsPath, { theme: '@vuepress/default', temp: docsTempPath })
+ expect(context.sourceDir).toBe(docsPath)
+ }))
+ })
+})
diff --git a/packages/@vuepress/core/__test__/prepare/util.js b/packages/@vuepress/core/__test__/prepare/util.js
new file mode 100644
index 0000000000..edb2bf37e3
--- /dev/null
+++ b/packages/@vuepress/core/__test__/prepare/util.js
@@ -0,0 +1,34 @@
+const path = require('path')
+const { fs } = require('@vuepress/shared-utils')
+const AppContext = require('../../lib/prepare/AppContext')
+const createMarkdown = require('../../../markdown/lib/index')
+
+function getAppContext () {
+ return new AppContext('.')
+}
+
+function getComputed () {
+ const context = getAppContext()
+ return new context.ClientComputedMixinConstructor()
+}
+
+const docsBaseDir = path.resolve(__dirname, 'fixtures/docs')
+
+function getDocument (relative) {
+ return {
+ filePath: path.join(docsBaseDir, relative),
+ relative
+ }
+}
+
+const getMarkdown = createMarkdown
+
+const readFile = async filePath => await fs.readFile(filePath, 'utf-8')
+
+module.exports = {
+ getAppContext,
+ getComputed,
+ getMarkdown,
+ getDocument,
+ readFile
+}
diff --git a/packages/@vuepress/core/lib/app/Store.js b/packages/@vuepress/core/lib/app/Store.js
new file mode 100644
index 0000000000..cf3e5b60b9
--- /dev/null
+++ b/packages/@vuepress/core/lib/app/Store.js
@@ -0,0 +1,19 @@
+import Vue from 'vue'
+
+export default class Store {
+ constructor () {
+ this.store = new Vue({
+ data: {
+ ob: {}
+ }
+ })
+ }
+
+ get (key) {
+ return this.store.ob[key]
+ }
+
+ set (key, value) {
+ Vue.set(this.store.ob, key, value)
+ }
+}
diff --git a/lib/app/app.js b/packages/@vuepress/core/lib/app/app.js
similarity index 68%
rename from lib/app/app.js
rename to packages/@vuepress/core/lib/app/app.js
index 511abbd472..171cfd2929 100644
--- a/lib/app/app.js
+++ b/packages/@vuepress/core/lib/app/app.js
@@ -1,11 +1,13 @@
+/* global VUEPRESS_TEMP_PATH */
import Vue from 'vue'
import Router from 'vue-router'
import dataMixin from './dataMixin'
-import store from './store'
-import { routes } from '@temp/routes'
-import { siteData } from '@temp/siteData'
-import enhanceApp from '@temp/enhanceApp'
-import themeEnhanceApp from '@temp/themeEnhanceApp'
+import { routes } from '@internal/routes'
+import { siteData } from '@internal/siteData'
+import appEnhancers from '@internal/app-enhancers'
+import globalUIComponents from '@internal/global-ui'
+import ClientComputedMixin from '../prepare/ClientComputedMixin'
+import Store from './Store'
// generated from user config
import('@temp/style.styl')
@@ -18,7 +20,7 @@ import ClientOnly from './components/ClientOnly'
// suggest dev server restart on base change
if (module.hot) {
const prevBase = siteData.base
- module.hot.accept('./.temp/siteData', () => {
+ module.hot.accept(VUEPRESS_TEMP_PATH + '/internal/siteData.js', () => {
if (siteData.base !== prevBase) {
window.alert(
`[vuepress] Site base has changed. ` +
@@ -29,13 +31,15 @@ if (module.hot) {
}
Vue.config.productionTip = false
+
+Vue.$store = new Store()
+
Vue.use(Router)
// mixin for exposing $site and $page
-Vue.mixin(dataMixin(siteData))
+Vue.mixin(dataMixin(ClientComputedMixin, siteData))
// component for rendering markdown content and setting title etc.
Vue.component('Content', Content)
Vue.component('OutboundLink', OutboundLink)
-Vue.component('Badge', () => import('./components/Badge.vue'))
// component for client-only content
Vue.component('ClientOnly', ClientOnly)
@@ -49,7 +53,7 @@ Vue.prototype.$withBase = function (path) {
}
}
-export function createApp () {
+export function createApp (isServer) {
const router = new Router({
base: siteData.base,
mode: 'history',
@@ -59,7 +63,7 @@ export function createApp () {
if (saved) {
return saved
} else if (to.hash) {
- if (store.disableScrollBehavior) {
+ if (Vue.$store.get('disableScrollBehavior')) {
return false
}
return {
@@ -84,15 +88,23 @@ export function createApp () {
const options = {}
- themeEnhanceApp({ Vue, options, router, siteData })
- enhanceApp({ Vue, options, router, siteData })
+ try {
+ appEnhancers.forEach(enhancer => {
+ if (typeof enhancer === 'function') {
+ enhancer({ Vue, options, router, siteData, isServer })
+ }
+ })
+ } catch (e) {
+ console.error(e)
+ }
const app = new Vue(
Object.assign(options, {
router,
render (h) {
return h('div', { attrs: { id: 'app' }}, [
- h('router-view', { ref: 'layout' })
+ h('router-view', { ref: 'layout' }),
+ h('div', { class: 'global-ui' }, globalUIComponents.map(component => h(component)))
])
}
})
diff --git a/packages/@vuepress/core/lib/app/clientEntry.js b/packages/@vuepress/core/lib/app/clientEntry.js
new file mode 100644
index 0000000000..b7f795f613
--- /dev/null
+++ b/packages/@vuepress/core/lib/app/clientEntry.js
@@ -0,0 +1,14 @@
+/* global VUEPRESS_VERSION, LAST_COMMIT_HASH*/
+
+import { createApp } from './app'
+
+const { app, router } = createApp(false /* isServer */)
+
+window.__VUEPRESS_VERSION__ = {
+ version: VUEPRESS_VERSION,
+ hash: LAST_COMMIT_HASH
+}
+
+router.onReady(() => {
+ app.$mount('#app')
+})
diff --git a/lib/app/components/ClientOnly.js b/packages/@vuepress/core/lib/app/components/ClientOnly.js
similarity index 100%
rename from lib/app/components/ClientOnly.js
rename to packages/@vuepress/core/lib/app/components/ClientOnly.js
diff --git a/packages/@vuepress/core/lib/app/components/Content.js b/packages/@vuepress/core/lib/app/components/Content.js
new file mode 100644
index 0000000000..c034a47178
--- /dev/null
+++ b/packages/@vuepress/core/lib/app/components/Content.js
@@ -0,0 +1,34 @@
+import Vue from 'vue'
+import components from '@internal/page-components'
+
+export default {
+ functional: true,
+
+ props: {
+ custom: {
+ type: Boolean,
+ default: true
+ },
+ pageKey: String,
+ slot: String
+ },
+
+ render (h, { parent, props, data }) {
+ const pageKey = props.pageKey || parent.$page.key
+
+ if (components[pageKey]) {
+ // In SSR, if a component is not registered with the component option
+ // vue-server-renderer will not be able to resovle it.
+ if (!parent.$ssrContext) {
+ Vue.component(pageKey, components[pageKey])
+ }
+
+ return h(pageKey, {
+ class: [props.custom ? 'custom' : '', data.class, data.staticClass],
+ style: data.style,
+ slot: props.slot || 'default'
+ })
+ }
+ return h('')
+ }
+}
diff --git a/packages/@vuepress/core/lib/app/components/LayoutDistributor.vue b/packages/@vuepress/core/lib/app/components/LayoutDistributor.vue
new file mode 100644
index 0000000000..82bc8dcf0c
--- /dev/null
+++ b/packages/@vuepress/core/lib/app/components/LayoutDistributor.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/lib/default-theme/NotFound.vue b/packages/@vuepress/core/lib/app/components/NotFound.vue
similarity index 100%
rename from lib/default-theme/NotFound.vue
rename to packages/@vuepress/core/lib/app/components/NotFound.vue
diff --git a/lib/app/components/OutboundLink.vue b/packages/@vuepress/core/lib/app/components/OutboundLink.vue
similarity index 100%
rename from lib/app/components/OutboundLink.vue
rename to packages/@vuepress/core/lib/app/components/OutboundLink.vue
diff --git a/packages/@vuepress/core/lib/app/dataMixin.js b/packages/@vuepress/core/lib/app/dataMixin.js
new file mode 100644
index 0000000000..1f0b2dfa18
--- /dev/null
+++ b/packages/@vuepress/core/lib/app/dataMixin.js
@@ -0,0 +1,37 @@
+/* global VUEPRESS_TEMP_PATH */
+
+import Vue from 'vue'
+
+export default function dataMixin (I18n, siteData) {
+ prepare(siteData)
+ Vue.$store.set('siteData', siteData)
+
+ if (module.hot) {
+ module.hot.accept(VUEPRESS_TEMP_PATH + '/internal/siteData.js', () => {
+ prepare(siteData)
+ Vue.$store.set('siteData', siteData)
+ })
+ }
+
+ const I18nConstructor = I18n(Vue.$store.get('siteData'))
+ const i18n = new I18nConstructor()
+ const descriptors = Object.getOwnPropertyDescriptors(Object.getPrototypeOf(i18n))
+ const computed = {}
+ Object.keys(descriptors).reduce((computed, key) => {
+ if (key.startsWith('$')) {
+ computed[key] = descriptors[key].get
+ }
+ return computed
+ }, computed)
+
+ return { computed }
+}
+
+function prepare (siteData) {
+ if (siteData.locales) {
+ Object.keys(siteData.locales).forEach(path => {
+ siteData.locales[path].path = path
+ })
+ }
+ Object.freeze(siteData)
+}
diff --git a/lib/app/index.dev.html b/packages/@vuepress/core/lib/app/index.dev.html
similarity index 100%
rename from lib/app/index.dev.html
rename to packages/@vuepress/core/lib/app/index.dev.html
diff --git a/lib/app/index.ssr.html b/packages/@vuepress/core/lib/app/index.ssr.html
similarity index 100%
rename from lib/app/index.ssr.html
rename to packages/@vuepress/core/lib/app/index.ssr.html
diff --git a/lib/app/root-mixins/updateMeta.js b/packages/@vuepress/core/lib/app/root-mixins/updateMeta.js
similarity index 100%
rename from lib/app/root-mixins/updateMeta.js
rename to packages/@vuepress/core/lib/app/root-mixins/updateMeta.js
diff --git a/packages/@vuepress/core/lib/app/serverEntry.js b/packages/@vuepress/core/lib/app/serverEntry.js
new file mode 100644
index 0000000000..84c707b354
--- /dev/null
+++ b/packages/@vuepress/core/lib/app/serverEntry.js
@@ -0,0 +1,38 @@
+import Vue from 'vue'
+import { createApp } from './app'
+import pageComponents from '@internal/page-components'
+import layoutComponents from '@internal/layout-components'
+
+export default context => new Promise((resolve, reject) => {
+ const { app, router } = createApp(true /* isServer */)
+ const { url } = context
+ const { fullPath } = router.resolve(url).route
+
+ if (fullPath !== url) {
+ return reject({ url: fullPath })
+ }
+
+ router.push(url)
+
+ // In SSR, if a component is not registered with the component option,
+ // vue-server-renderer will not able to resolve it.
+ //
+ // Build also works after deleting this, but the content of all pages
+ // will not appear to the output html, which is not conducive to SEO.
+ const asyncComponentLoadingPromises = [
+ ...getComponentArr(pageComponents),
+ ...getComponentArr(layoutComponents)
+ ].map(({ name, loadFn }) => {
+ return loadFn().then(comp => {
+ Vue.component(name, comp.default)
+ })
+ })
+
+ router.onReady(() => {
+ Promise.all(asyncComponentLoadingPromises).then(() => resolve(app))
+ })
+})
+
+function getComponentArr (components) {
+ return Object.keys(components).map(name => ({ name, loadFn: components[name] }))
+}
diff --git a/lib/default-theme/styles/config.styl b/packages/@vuepress/core/lib/app/style/config.styl
similarity index 92%
rename from lib/default-theme/styles/config.styl
rename to packages/@vuepress/core/lib/app/style/config.styl
index a57d4484de..1653b37cfd 100644
--- a/lib/default-theme/styles/config.styl
+++ b/packages/@vuepress/core/lib/app/style/config.styl
@@ -19,4 +19,4 @@ $MQMobileNarrow = 419px
$lineNumbersWrapperWidth = 3.5rem
$codeLang = js ts html md vue css sass scss less stylus go java c sh yaml py
-@import '~@temp/override.styl'
+@import '~@temp/palette.styl'
diff --git a/packages/@vuepress/core/lib/app/util.js b/packages/@vuepress/core/lib/app/util.js
new file mode 100644
index 0000000000..8b52325409
--- /dev/null
+++ b/packages/@vuepress/core/lib/app/util.js
@@ -0,0 +1,92 @@
+/**
+ * Inject option to Vue SFC
+ * @param {object} options
+ * @param {string} key
+ * @param {any} value
+ */
+export function injectComponentOption (options, key, value) {
+ const arrayInject = () => {
+ if (!options[key]) options[key] = []
+ options[key].push(...value)
+ }
+ const objectInject = () => {
+ if (!options[key]) options[key] = {}
+ Object.assign(options[key], value)
+ }
+ // const primitiveInject = () => options[key] = value
+
+ switch (key) {
+ case 'components': objectInject(); break
+ case 'mixins': arrayInject(); break
+ default: throw new Error('Unknown option name.')
+ }
+}
+
+export function findPageForPath (pages, path) {
+ for (let i = 0; i < pages.length; i++) {
+ const page = pages[i]
+ if (page.path === path) {
+ return page
+ }
+ }
+ return {
+ path: '',
+ frontmatter: {}
+ }
+}
+
+export function findPageByKey (pages, key) {
+ for (let i = 0; i < pages.length; i++) {
+ const page = pages[i]
+ if (page.key === key) {
+ return page
+ }
+ }
+ return {
+ path: '',
+ frontmatter: {}
+ }
+}
+
+/**
+ * Normalize config.
+ * This utility is mainly for plugin developers. For some
+ * plugins that need internationalize the text. but it's
+ * not recommenbded to let plugin care about to the internal
+ * i18n implementation, so this utility was born.
+ *
+ *
+ * Usage:
+ *
+ * import { normalizeConfig } from '@app/util'
+ * export default {
+ * data () {
+ * return { config }
+ * }
+ * computed: {
+ * normalizedConfig() {
+ * return normalizeConfig(this, config)
+ * }
+ * }
+ * }
+ *
+ *
+ * e.g.
+ *
+ * Config: : 'Text'
+ * Normalized Config: 'Text'
+ *
+ * Config: : { '/': 'Text', '/zh/': '文本' }
+ * Normalized Config: 'Text' or '文本'
+ *
+ * @param {Vue} component
+ * @param {any} rawConfig
+ * @returns {any}
+ */
+export function normalizeConfig (component, rawConfig) {
+ const { $localePath } = component
+ if (typeof rawConfig === 'object' && rawConfig[$localePath]) {
+ return rawConfig[$localePath]
+ }
+ return rawConfig
+}
diff --git a/lib/build.js b/packages/@vuepress/core/lib/build.js
similarity index 86%
rename from lib/build.js
rename to packages/@vuepress/core/lib/build.js
index f7efb49bb0..5c4fe16929 100644
--- a/lib/build.js
+++ b/packages/@vuepress/core/lib/build.js
@@ -1,22 +1,22 @@
+'use strict'
+
module.exports = async function build (sourceDir, cliOptions = {}) {
process.env.NODE_ENV = 'production'
- const fs = require('fs-extra')
const path = require('path')
- const chalk = require('chalk')
const webpack = require('webpack')
const readline = require('readline')
const escape = require('escape-html')
- const logger = require('./util/logger')
- const prepare = require('./prepare')
+ const { chalk, fs, logger } = require('@vuepress/shared-utils')
+ const prepare = require('./prepare/index')
const createClientConfig = require('./webpack/createClientConfig')
const createServerConfig = require('./webpack/createServerConfig')
const { createBundleRenderer } = require('vue-server-renderer')
- const { normalizeHeadTag, applyUserWebpackConfig } = require('./util')
+ const { normalizeHeadTag, applyUserWebpackConfig } = require('./util/index')
logger.wait('\nExtracting site metadata...')
- const options = await prepare(sourceDir)
+ const options = await prepare(sourceDir, cliOptions, true /* isProd */)
if (cliOptions.outDir) {
options.outDir = cliOptions.outDir
}
@@ -26,6 +26,7 @@ module.exports = async function build (sourceDir, cliOptions = {}) {
return console.error(logger.error(chalk.red('Unexpected option: outDir cannot be set to the current working directory.\n'), false))
}
await fs.remove(outDir)
+ logger.debug('Dist directory: ' + chalk.gray(path.resolve(outDir)))
let clientConfig = createClientConfig(options, cliOptions).toConfig()
let serverConfig = createServerConfig(options, cliOptions).toConfig()
@@ -57,7 +58,7 @@ module.exports = async function build (sourceDir, cliOptions = {}) {
runInNewContext: false,
inject: false,
shouldPrefetch: options.siteConfig.shouldPrefetch || (() => true),
- template: await fs.readFile(path.resolve(__dirname, 'app/index.ssr.html'), 'utf-8')
+ template: await fs.readFile(options.ssrTemplate, 'utf-8')
})
// pre-render head tags from user config
@@ -67,32 +68,19 @@ module.exports = async function build (sourceDir, cliOptions = {}) {
// render pages
logger.wait('Rendering static HTML...')
- for (const page of options.siteData.pages) {
+ for (const page of options.pages) {
await renderPage(page)
}
// if the user does not have a custom 404.md, generate the theme's default
- if (!options.siteData.pages.some(p => p.path === '/404.html')) {
+ if (!options.pages.some(p => p.path === '/404.html')) {
await renderPage({ path: '/404.html' })
}
readline.clearLine(process.stdout, 0)
readline.cursorTo(process.stdout, 0)
- if (options.siteConfig.serviceWorker) {
- logger.wait('\nGenerating service worker...')
- const wbb = require('workbox-build')
- await wbb.generateSW({
- swDest: path.resolve(outDir, 'service-worker.js'),
- globDirectory: outDir,
- globPatterns: ['**\/*.{js,css,html,png,jpg,jpeg,gif,svg,woff,woff2,eot,ttf,otf}']
- })
- await fs.writeFile(
- path.resolve(outDir, 'service-worker.js'),
- await fs.readFile(path.resolve(__dirname, 'service-worker/skip-waiting.js'), 'utf8'),
- { flag: 'a' }
- )
- }
+ await options.pluginAPI.options.generated.apply()
// DONE.
const relativeDir = path.relative(process.cwd(), outDir)
diff --git a/lib/dev.js b/packages/@vuepress/core/lib/dev.js
similarity index 83%
rename from lib/dev.js
rename to packages/@vuepress/core/lib/dev.js
index 08f828d06c..a70585bb05 100644
--- a/lib/dev.js
+++ b/packages/@vuepress/core/lib/dev.js
@@ -1,7 +1,7 @@
+'use strict'
+
module.exports = async function dev (sourceDir, cliOptions = {}) {
- const fs = require('fs')
const path = require('path')
- const chalk = require('chalk')
const webpack = require('webpack')
const chokidar = require('chokidar')
const serve = require('webpack-serve')
@@ -11,20 +11,21 @@ module.exports = async function dev (sourceDir, cliOptions = {}) {
const serveStatic = require('koa-static')
const history = require('connect-history-api-fallback')
- const prepare = require('./prepare')
- const logger = require('./util/logger')
+ const prepare = require('./prepare/index')
+ const { chalk, fs, logger } = require('@vuepress/shared-utils')
const HeadPlugin = require('./webpack/HeadPlugin')
const DevLogPlugin = require('./webpack/DevLogPlugin')
const createClientConfig = require('./webpack/createClientConfig')
- const { applyUserWebpackConfig } = require('./util')
- const { frontmatterEmitter } = require('./webpack/markdownLoader')
+ const { applyUserWebpackConfig } = require('./util/index')
+ const { frontmatterEmitter } = require('@vuepress/markdown-loader')
logger.wait('\nExtracting site metadata...')
- const options = await prepare(sourceDir)
+ const options = await prepare(sourceDir, cliOptions, false /* isProd */)
// setup watchers to update options and dynamically generated files
const update = () => {
- prepare(sourceDir).catch(err => {
+ options.pluginAPI.options.updated.syncApply()
+ prepare(sourceDir, cliOptions, false /* isProd */).catch(err => {
console.error(logger.error(chalk.red(err.stack), false))
})
}
@@ -58,14 +59,14 @@ module.exports = async function dev (sourceDir, cliOptions = {}) {
frontmatterEmitter.on('update', update)
// resolve webpack config
- let config = createClientConfig(options, cliOptions)
+ let config = createClientConfig(options)
config
.plugin('html')
// using a fork of html-webpack-plugin to avoid it requiring webpack
// internals from an incompatible version.
.use(require('vuepress-html-webpack-plugin'), [{
- template: path.resolve(__dirname, 'app/index.dev.html')
+ template: options.devTemplate
}])
config
@@ -82,7 +83,7 @@ module.exports = async function dev (sourceDir, cliOptions = {}) {
.use(DevLogPlugin, [{
port,
displayHost,
- publicPath: options.publicPath
+ publicPath: options.base
}])
config = config.toConfig()
@@ -108,6 +109,10 @@ module.exports = async function dev (sourceDir, cliOptions = {}) {
logLevel: 'error',
port,
add: app => {
+ // apply plugin options to extend dev server.
+ const { pluginAPI } = options
+ pluginAPI.options.enhanceDevServer.syncApply(app)
+
const userPublic = path.resolve(sourceDir, '.vuepress/public')
// enable range request
@@ -115,7 +120,7 @@ module.exports = async function dev (sourceDir, cliOptions = {}) {
// respect base when serving static files...
if (fs.existsSync(userPublic)) {
- app.use(mount(options.publicPath, serveStatic(userPublic)))
+ app.use(mount(options.base, serveStatic(userPublic)))
}
app.use(convert(history({
diff --git a/lib/eject.js b/packages/@vuepress/core/lib/eject.js
similarity index 61%
rename from lib/eject.js
rename to packages/@vuepress/core/lib/eject.js
index e96b00fd84..f3cfa5df6b 100644
--- a/lib/eject.js
+++ b/packages/@vuepress/core/lib/eject.js
@@ -1,10 +1,16 @@
-const fs = require('fs-extra')
+'use strict'
+
const path = require('path')
-const chalk = require('chalk')
-const logger = require('./util/logger')
+const { chalk, fs, logger } = require('@vuepress/shared-utils')
module.exports = async (dir) => {
- const source = path.resolve(__dirname, 'default-theme')
+ try {
+ require.resolve('@vuepress/theme-default')
+ } catch (err) {
+ console.log(chalk.red(`\n[vuepress] cannot find '@vuepress/theme-default'\n`))
+ process.exit(1)
+ }
+ const source = require.resolve('@vuepress/theme-default')
const target = path.resolve(dir, '.vuepress/theme')
await fs.copy(source, target)
// remove the import to default theme override
diff --git a/lib/index.js b/packages/@vuepress/core/lib/index.js
similarity index 70%
rename from lib/index.js
rename to packages/@vuepress/core/lib/index.js
index 8e4d4a19ad..43a6d0124b 100644
--- a/lib/index.js
+++ b/packages/@vuepress/core/lib/index.js
@@ -1,4 +1,5 @@
+'use strict'
+
exports.dev = require('./dev')
exports.build = require('./build')
exports.eject = require('./eject')
-Object.assign(exports, require('./util'))
diff --git a/packages/@vuepress/core/lib/internal-plugins/enhanceApp.js b/packages/@vuepress/core/lib/internal-plugins/enhanceApp.js
new file mode 100644
index 0000000000..1163d3187b
--- /dev/null
+++ b/packages/@vuepress/core/lib/internal-plugins/enhanceApp.js
@@ -0,0 +1,15 @@
+const path = require('path')
+
+module.exports = (options, context) => ({
+ name: '@vuepress/internal-enhance-app',
+
+ enhanceAppFiles () {
+ const { sourceDir, themePath } = context
+ const enhanceAppPath = path.resolve(sourceDir, '.vuepress/enhanceApp.js')
+ const themeEnhanceAppPath = path.resolve(themePath, 'enhanceApp.js')
+ return [
+ enhanceAppPath,
+ themeEnhanceAppPath
+ ]
+ }
+})
diff --git a/packages/@vuepress/core/lib/internal-plugins/layoutComponents.js b/packages/@vuepress/core/lib/internal-plugins/layoutComponents.js
new file mode 100644
index 0000000000..97f781b933
--- /dev/null
+++ b/packages/@vuepress/core/lib/internal-plugins/layoutComponents.js
@@ -0,0 +1,22 @@
+module.exports = (options, ctx) => {
+ const { layoutComponentMap } = ctx
+ const componentNames = Object.keys(layoutComponentMap)
+
+ return {
+ name: '@vuepress/internal-layout-components',
+
+ async clientDynamicModules () {
+ const code = `export default {\n${componentNames
+ .map(name => ` ${JSON.stringify(name)}: () => import(${JSON.stringify(layoutComponentMap[name].path)})`)
+ .join(',\n')} \n}`
+ return { name: 'layout-components.js', content: code, dirname: 'internal' }
+ },
+
+ chainWebpack (config, isServer) {
+ const setAlias = (alias, raw) => config.resolve.alias.set(alias, raw)
+ componentNames.forEach(name => {
+ setAlias(`@${name}`, layoutComponentMap[name].path)
+ })
+ }
+ }
+}
diff --git a/packages/@vuepress/core/lib/internal-plugins/overrideCSS.js b/packages/@vuepress/core/lib/internal-plugins/overrideCSS.js
new file mode 100644
index 0000000000..597e74710e
--- /dev/null
+++ b/packages/@vuepress/core/lib/internal-plugins/overrideCSS.js
@@ -0,0 +1,69 @@
+const path = require('path')
+const {
+ fs, logger, chalk,
+ datatypes: {
+ isPlainObject,
+ assertTypes,
+ isString
+ }
+} = require('@vuepress/shared-utils')
+
+module.exports = (options, context) => ({
+ name: '@vuepress/internal-override-css',
+
+ async ready () {
+ const { sourceDir, writeTemp } = context
+
+ const overridePath = path.resolve(sourceDir, '.vuepress/override.styl')
+ const hasUserOverride = fs.existsSync(overridePath)
+
+ if (hasUserOverride) {
+ logger.tip(`${chalk.magenta('override.styl')} has been deprecated from v1.0.0, using ${chalk.cyan('config.palette')} instead.\n`)
+ }
+
+ // palette API.
+ const themePalette = context.themePalette
+ const { palette: userPalette } = context.siteConfig
+ const themePaletteContent = resolvePaletteContent(themePalette)
+ const userPaletteContent = resolvePaletteContent(userPalette)
+ // user's palette can override theme's palette.
+ const paletteContent = themePaletteContent + userPaletteContent
+ await writeTemp('palette.styl', paletteContent)
+
+ // style.styl API.
+ const stylePath = path.resolve(sourceDir, '.vuepress/style.styl').replace(/[\\]+/g, '/')
+ const hasUserStyle = fs.existsSync(stylePath)
+ await writeTemp('style.styl', hasUserStyle ? `@import(${JSON.stringify(stylePath)})` : ``)
+
+ // Temporary tip, will be removed at next release.
+ if (hasUserOverride && !hasUserStyle) {
+ logger.tip(
+ `${chalk.magenta('override.styl')} has been split into 2 APIs, we recommend you upgrade to continue.\n` +
+ ` See: ${chalk.magenta('https://vuepress.vuejs.org/default-theme-config/#simple-css-override')}`
+ )
+ }
+ }
+})
+
+function resolvePaletteContent (palette) {
+ const { valid, warnMsg } = assertTypes(palette, [String, Object])
+ if (!valid) {
+ if (palette !== undefined) {
+ logger.warn(
+ `[vuepress] Invalid value for "palette": ${warnMsg}`
+ )
+ }
+ return ''
+ }
+
+ if (isString(palette)) {
+ if (fs.existsSync(palette)) {
+ return `@import(${JSON.stringify(palette)})\n`
+ }
+ return ''
+ } else if (isPlainObject(palette)) {
+ return Object.keys(palette).map(variableName => {
+ return `${variableName} = ${palette[variableName]}`
+ }).join('\n') + '\n'
+ }
+}
diff --git a/packages/@vuepress/core/lib/internal-plugins/pageComponents.js b/packages/@vuepress/core/lib/internal-plugins/pageComponents.js
new file mode 100644
index 0000000000..e4f1377888
--- /dev/null
+++ b/packages/@vuepress/core/lib/internal-plugins/pageComponents.js
@@ -0,0 +1,16 @@
+module.exports = (options, ctx) => {
+ const { pages } = ctx
+ // const componentNames = Object.keys(layoutComponentMap)
+
+ return {
+ name: '@vuepress/internal-page-components',
+
+ async clientDynamicModules () {
+ const code = `export default {\n${pages
+ .filter(({ _filePath }) => _filePath)
+ .map(({ key, _filePath }) => ` ${JSON.stringify(key)}: () => import(${JSON.stringify(_filePath)})`)
+ .join(',\n')} \n}`
+ return { name: 'page-components.js', content: code, dirname: 'internal' }
+ }
+ }
+}
diff --git a/packages/@vuepress/core/lib/internal-plugins/rootMixins.js b/packages/@vuepress/core/lib/internal-plugins/rootMixins.js
new file mode 100644
index 0000000000..38edf4c52a
--- /dev/null
+++ b/packages/@vuepress/core/lib/internal-plugins/rootMixins.js
@@ -0,0 +1,21 @@
+const path = require('path')
+const { codegen: { pathsToModuleCode }} = require('@vuepress/shared-utils')
+
+module.exports = (options, context, api) => ({
+ name: '@vuepress/internal-root-mixins',
+
+ // @internal/root-mixins
+ async clientDynamicModules () {
+ const builtInRootMixins = [
+ path.resolve(__dirname, '../app/root-mixins/updateMeta.js')
+ ]
+
+ const rootMixins = [
+ ...builtInRootMixins,
+ ...api.options.clientRootMixin.values
+ ]
+
+ const rootMixinsCode = pathsToModuleCode(rootMixins)
+ return { name: 'root-mixins.js', content: rootMixinsCode, dirname: 'internal' }
+ }
+})
diff --git a/packages/@vuepress/core/lib/internal-plugins/routes.js b/packages/@vuepress/core/lib/internal-plugins/routes.js
new file mode 100644
index 0000000000..799610f079
--- /dev/null
+++ b/packages/@vuepress/core/lib/internal-plugins/routes.js
@@ -0,0 +1,84 @@
+module.exports = (options, ctx) => ({
+ name: '@vuepress/internal-routes',
+
+ // @internal/routes
+ async clientDynamicModules () {
+ const code = importCode() + routesCode(ctx.pages)
+ return { name: 'routes.js', content: code, dirname: 'internal' }
+ }
+})
+
+/**
+ * Import utilities
+ * @returns {string}
+ */
+function importCode () {
+ return `
+import { injectComponentOption } from '@app/util'
+import rootMixins from '@internal/root-mixins'
+import components from '@internal/layout-components'
+import LayoutDistributor from '@app/components/LayoutDistributor.vue'
+
+injectComponentOption(LayoutDistributor, 'mixins', rootMixins)
+injectComponentOption(LayoutDistributor, 'components', components)
+`
+}
+
+/**
+ * Get Vue routes code.
+ * @param {array} pages
+ * @returns {string}
+ */
+function routesCode (pages) {
+ function genRoute ({
+ path: pagePath,
+ key: componentName,
+ regularPath,
+ _meta
+ }) {
+ let code = `
+ {
+ name: ${JSON.stringify(componentName)},
+ path: ${JSON.stringify(pagePath)},
+ component: LayoutDistributor,${_meta ? `\n meta: ${JSON.stringify(_meta)}` : ''}
+ }`
+
+ const dncodedPath = decodeURIComponent(pagePath)
+ if (dncodedPath !== pagePath) {
+ code += `,
+ {
+ path: ${JSON.stringify(dncodedPath)},
+ redirect: ${JSON.stringify(pagePath)}
+ }`
+ }
+
+ if (/\/$/.test(pagePath)) {
+ code += `,
+ {
+ path: ${JSON.stringify(pagePath + 'index.html')},
+ redirect: ${JSON.stringify(pagePath)}
+ }`
+ }
+
+ if (regularPath !== pagePath) {
+ code += `,
+ {
+ path: ${JSON.stringify(regularPath)},
+ redirect: ${JSON.stringify(pagePath)}
+ }`
+ }
+
+ return code
+ }
+
+ const notFoundRoute = `,
+ {
+ path: '*',
+ component: LayoutDistributor
+ }`
+
+ return (
+ `export const routes = [${pages.map(genRoute).join(',')}${notFoundRoute}\n]`
+ )
+}
+
diff --git a/packages/@vuepress/core/lib/internal-plugins/siteData.js b/packages/@vuepress/core/lib/internal-plugins/siteData.js
new file mode 100644
index 0000000000..91a99baf4f
--- /dev/null
+++ b/packages/@vuepress/core/lib/internal-plugins/siteData.js
@@ -0,0 +1,9 @@
+module.exports = (options, context) => ({
+ name: '@vuepress/internal-site-data',
+
+ // @internal/siteData
+ async clientDynamicModules () {
+ const code = `export const siteData = ${JSON.stringify(context.getSiteData(), null, 2)}`
+ return { name: 'siteData.js', content: code, dirname: 'internal' }
+ }
+})
diff --git a/packages/@vuepress/core/lib/plugin-api/abstract/AsyncOption.js b/packages/@vuepress/core/lib/plugin-api/abstract/AsyncOption.js
new file mode 100644
index 0000000000..2685ef09af
--- /dev/null
+++ b/packages/@vuepress/core/lib/plugin-api/abstract/AsyncOption.js
@@ -0,0 +1,94 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const { logger, chalk, datatypes: { isFunction }} = require('@vuepress/shared-utils')
+const Option = require('./Option')
+
+/**
+ * Expose asynchronous option class.
+ */
+
+class AsyncOption extends Option {
+ /**
+ * Asynchronous serial running
+ *
+ * @param args
+ * @param {Array} args
+ * @api public
+ */
+
+ async asyncApply (...args) {
+ const rawItems = this.items
+ this.items = []
+ this.appliedItems = this.items
+
+ for (const { name, value } of rawItems) {
+ try {
+ this.add(
+ name,
+ isFunction(value)
+ ? await value(...args)
+ : value
+ )
+ } catch (error) {
+ logger.error(`${chalk.cyan(name)} apply ${chalk.cyan(this.key)} failed.`)
+ throw error
+ }
+ }
+
+ this.items = rawItems
+ }
+
+ /**
+ * Asynchronous serial running
+ *
+ * @param args
+ * @param {Array} args
+ * @api public
+ */
+
+ async parallelApply (...args) {
+ const rawItems = this.items
+ this.items = []
+ this.appliedItems = this.items
+
+ await Promise.all(rawItems.map(async ({ name, value }) => {
+ try {
+ this.add(
+ name,
+ isFunction(value)
+ ? await value(...args)
+ : value
+ )
+ } catch (error) {
+ logger.error(`${chalk.cyan(name)} apply ${chalk.cyan(this.key)} failed.`)
+ throw error
+ }
+ })).catch(error => {
+ throw error
+ })
+
+ this.items = rawItems
+ }
+
+ /**
+ * Process a value via a pipeline.
+ *
+ * @param input
+ * @returns {any}
+ * @api public
+ */
+
+ async pipeline (input) {
+ for (const fn of this.values) {
+ input = await fn(input)
+ }
+ return input
+ }
+}
+
+AsyncOption.prototype.apply = AsyncOption.prototype.asyncApply
+module.exports = AsyncOption
diff --git a/packages/@vuepress/core/lib/plugin-api/abstract/Option.js b/packages/@vuepress/core/lib/plugin-api/abstract/Option.js
new file mode 100644
index 0000000000..6ffd7d48f6
--- /dev/null
+++ b/packages/@vuepress/core/lib/plugin-api/abstract/Option.js
@@ -0,0 +1,135 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const { logger, chalk, compose, datatypes: { isFunction }} = require('@vuepress/shared-utils')
+
+/**
+ * Expose synchronous option class.
+ */
+
+class Option {
+ constructor (key) {
+ this.key = key
+ this.items = []
+ }
+
+ /**
+ * Set value with name.
+ *
+ * @param {string} name
+ * @param {T} value
+ * @api public
+ */
+
+ add (name, value) {
+ if (Array.isArray(value)) {
+ return this.items.push(...value.map(i => ({ value: i, name })))
+ }
+ this.items.push({ value, name })
+ }
+
+ /**
+ * Delete value with name.
+ *
+ * @param {string} name
+ * @api public
+ */
+
+ delete (name) {
+ let index = this.items.findIndex(({ name: _name }) => _name === name)
+ while (index !== -1) {
+ this.items.splice(index, 1)
+ index = this.items.findIndex(({ name: _name }) => _name === name)
+ }
+ }
+
+ /**
+ * Clean option store
+ *
+ * @param {string} name
+ * @api public
+ */
+
+ clear (name) {
+ this.items = []
+ }
+
+ /**
+ * Get values.
+ *
+ * @returns {any}
+ * @api public
+ */
+
+ get values () {
+ return this.items.map(item => item.value)
+ }
+
+ /**
+ * Get applied values
+ *
+ * @returns {Array|*|any[]}
+ * @api public
+ */
+
+ get appliedValues () {
+ return this.appliedItems && this.appliedItems.map(item => item.value)
+ }
+
+ /**
+ * Get entries.
+ *
+ * @returns {any}
+ * @api public
+ */
+
+ get entries () {
+ return this.items.map(({ name, value }) => ([name, value]))
+ }
+
+ /**
+ * Synchronous running
+ *
+ * @param {Array} args
+ * @api public
+ */
+
+ syncApply (...args) {
+ const rawItems = this.items
+ this.items = []
+ this.appliedItems = this.items
+
+ for (const { name, value } of rawItems) {
+ try {
+ this.add(
+ name,
+ isFunction(value)
+ ? value(...args)
+ : value
+ )
+ } catch (error) {
+ logger.error(`${chalk.cyan(name)} apply ${chalk.cyan(this.key)} failed.`)
+ throw error
+ }
+ }
+
+ this.items = rawItems
+ }
+
+ /**
+ * Process a value via a pipeline.
+ * @param input
+ * @returns {*}
+ */
+
+ pipeline (input) {
+ const fn = compose(this.values)
+ return fn(input)
+ }
+}
+
+Option.prototype.apply = Option.prototype.syncApply
+module.exports = Option
diff --git a/packages/@vuepress/core/lib/plugin-api/constants.js b/packages/@vuepress/core/lib/plugin-api/constants.js
new file mode 100644
index 0000000000..b7c1d66ace
--- /dev/null
+++ b/packages/@vuepress/core/lib/plugin-api/constants.js
@@ -0,0 +1,33 @@
+'use strict'
+
+const PLUGIN_OPTION_META_MAP = {
+ // hooks
+ READY: { name: 'ready', types: [Function] },
+ COMPILED: { name: 'compiled', types: [Function] },
+ UPDATED: { name: 'updated', types: [Function] },
+ GENERATED: { name: 'generated', types: [Function] },
+ // options
+ CHAIN_WEBPACK: { name: 'chainWebpack', types: [Function] },
+ ENHANCE_DEV_SERVER: { name: 'enhanceDevServer', types: [Function] },
+ ENHANCE_APP_FILES: { name: 'enhanceAppFiles', types: [Array, Function] },
+ OUT_FILES: { name: 'outFiles', types: [Object] },
+ EXTEND_PAGE_DATA: { name: 'extendPageData', types: [Function] },
+ EXTEND_MARKDOWN: { name: 'extendMarkdown', types: [Function] },
+ CHAIN_MARKDOWN: { name: 'chainMarkdown', types: [Function] },
+ CLIENT_DYNAMIC_MODULES: { name: 'clientDynamicModules', types: [Function] },
+ CLIENT_ROOT_MIXIN: { name: 'clientRootMixin', types: [String] },
+ ADDITIONAL_PAGES: { name: 'additionalPages', types: [Function, Array] },
+ GLOBAL_UI_COMPONENTS: { name: 'globalUIComponents', types: [String, Array] },
+ DEFINE: { name: 'define', types: [Function, Object] },
+ ALIAS: { name: 'alias', types: [Function, Object] }
+}
+
+const PLUGIN_OPTION_MAP = {}
+Object.keys(PLUGIN_OPTION_META_MAP).forEach(key => {
+ PLUGIN_OPTION_MAP[key] = Object.assign({ key }, PLUGIN_OPTION_META_MAP[key])
+})
+
+const OPTION_NAMES = Object.keys(PLUGIN_OPTION_META_MAP).map(key => PLUGIN_OPTION_META_MAP[key].name)
+
+exports.PLUGIN_OPTION_MAP = PLUGIN_OPTION_MAP
+exports.OPTION_NAMES = OPTION_NAMES
diff --git a/packages/@vuepress/core/lib/plugin-api/index.js b/packages/@vuepress/core/lib/plugin-api/index.js
new file mode 100644
index 0000000000..bea3dd85ef
--- /dev/null
+++ b/packages/@vuepress/core/lib/plugin-api/index.js
@@ -0,0 +1,205 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const instantiateOption = require('./override/instantiateOption')
+const { flattenPlugin, normalizePluginsConfig } = require('./util')
+const { PLUGIN_OPTION_MAP } = require('./constants')
+const {
+ shortcutPackageResolver: { resolvePlugin },
+ datatypes: { assertTypes },
+ env: { isDebug },
+ logger, chalk
+} = require('@vuepress/shared-utils')
+
+/**
+ * Expose PluginAPI class.
+ */
+
+module.exports = class PluginAPI {
+ constructor (context) {
+ this.options = {}
+ this._pluginContext = context
+ this._pluginQueue = []
+ this.initializeOptions(PLUGIN_OPTION_MAP)
+ }
+
+ /**
+ * Get enabled plugins
+ *
+ * @returns {array}
+ * @api public
+ */
+
+ get enabledPlugins () {
+ return this._pluginQueue.filter(({ enabled }) => enabled)
+ }
+
+ /**
+ * Get disabled plugins
+ *
+ * @returns {array}
+ * @api public
+ */
+
+ get disabledPlugins () {
+ return this._pluginQueue.filter(({ enabled }) => !enabled)
+ }
+
+ /**
+ * apply plugin.
+ *
+ * @api public
+ */
+
+ apply () {
+ this._pluginQueue.forEach(plugin => {
+ if (plugin.enabled) {
+ this.applyPlugin(plugin)
+ } else {
+ logger.debug(`\n${chalk.gray(`[${plugin.name}]`)} disabled.`)
+ }
+ })
+ }
+
+ /**
+ * Normalize plugin and push it to the plugin queue.
+ *
+ * @param {object} pluginRaw
+ * @param {object} pluginOptions
+ * @returns {module.PluginAPI}
+ * @api public
+ */
+
+ use (pluginRaw, pluginOptions = {}) {
+ let plugin = resolvePlugin(pluginRaw)
+ if (!plugin.module) {
+ console.warn(`[vuepress] cannot resolve plugin "${pluginRaw}"`)
+ return this
+ }
+ plugin = flattenPlugin(plugin, pluginOptions, this._pluginContext, this)
+ if (plugin.multiple !== true) {
+ const duplicateIndex = this._pluginQueue.findIndex(({ name }) => name === plugin.name)
+ if (duplicateIndex !== -1) {
+ this._pluginQueue.splice(duplicateIndex, 1)
+ }
+ }
+ this._pluginQueue.push(plugin)
+ return this
+ }
+
+ /**
+ * Use plugin by config.
+ *
+ * @param pluginsConfig
+ * @returns {module.PluginAPI}
+ * @api public
+ */
+
+ useByPluginsConfig (pluginsConfig) {
+ pluginsConfig = normalizePluginsConfig(pluginsConfig)
+ pluginsConfig.forEach(([pluginRaw, pluginOptions]) => {
+ this.use(pluginRaw, pluginOptions)
+ })
+ return this
+ }
+
+ /**
+ * initialize plugin options.
+ *
+ * @api private
+ */
+
+ initializeOptions () {
+ Object.keys(PLUGIN_OPTION_MAP).forEach(key => {
+ const option = PLUGIN_OPTION_MAP[key]
+ this.options[option.name] = instantiateOption(option.name)
+ })
+ }
+
+ /**
+ * Register plugin option.
+ *
+ * @param {string} key
+ * @param {any} value
+ * @param {string} pluginName
+ * @returns {module.PluginAPI}
+ * @api private
+ */
+
+ registerOption (key, value, pluginName) {
+ const option = PLUGIN_OPTION_MAP[key]
+ const types = option.types
+ const { valid, warnMsg } = assertTypes(value, types)
+ if (valid) {
+ this.options[option.name].add(pluginName, value)
+ } else if (value !== undefined) {
+ logger.warn(
+ `${chalk.gray(pluginName)} ` +
+ `Invalid value for "option" ${chalk.cyan(option.name)}: ${warnMsg}`
+ )
+ }
+ return this
+ }
+
+ /**
+ * apply plugin.
+ *
+ * @api private
+ */
+
+ applyPlugin ({
+ // info
+ name: pluginName,
+ shortcut,
+
+ // hooks
+ ready,
+ compiled,
+ updated,
+ generated,
+
+ // options
+ chainWebpack,
+ enhanceDevServer,
+ extendMarkdown,
+ chainMarkdown,
+ enhanceAppFiles,
+ outFiles,
+ extendPageData,
+ clientDynamicModules,
+ clientRootMixin,
+ additionalPages,
+ globalUIComponents,
+ define,
+ alias
+ }) {
+ const isInternalPlugin = pluginName.startsWith('@vuepress/internal-')
+ if (shortcut) {
+ logger.tip(`\nApply plugin ${chalk.magenta(shortcut)} ${chalk.gray(`(i.e. "${pluginName}")`)} ...`, !isInternalPlugin)
+ } else if (!isInternalPlugin || isDebug) {
+ logger.tip(`\nApply plugin ${chalk.magenta(pluginName)} ...`)
+ }
+
+ this
+ .registerOption(PLUGIN_OPTION_MAP.READY.key, ready, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.COMPILED.key, compiled, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.UPDATED.key, updated, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.GENERATED.key, generated, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.CHAIN_WEBPACK.key, chainWebpack, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.ENHANCE_DEV_SERVER.key, enhanceDevServer, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.EXTEND_MARKDOWN.key, extendMarkdown, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.CHAIN_MARKDOWN.key, chainMarkdown, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.EXTEND_PAGE_DATA.key, extendPageData, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.ENHANCE_APP_FILES.key, enhanceAppFiles, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.OUT_FILES.key, outFiles, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.CLIENT_DYNAMIC_MODULES.key, clientDynamicModules, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.CLIENT_ROOT_MIXIN.key, clientRootMixin, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.ADDITIONAL_PAGES.key, additionalPages, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.GLOBAL_UI_COMPONENTS.key, globalUIComponents, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.DEFINE.key, define, pluginName)
+ .registerOption(PLUGIN_OPTION_MAP.ALIAS.key, alias, pluginName)
+ }
+}
diff --git a/packages/@vuepress/core/lib/plugin-api/override/AliasOption.js b/packages/@vuepress/core/lib/plugin-api/override/AliasOption.js
new file mode 100644
index 0000000000..fd3c2f555b
--- /dev/null
+++ b/packages/@vuepress/core/lib/plugin-api/override/AliasOption.js
@@ -0,0 +1,23 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const Option = require('../abstract/Option')
+
+/**
+ * define option.
+ */
+
+module.exports = class DefineOption extends Option {
+ apply (config) {
+ super.syncApply()
+ const aliases = this.appliedValues
+ aliases.forEach((alias) => {
+ Object.keys(alias).forEach(key => {
+ config.resolve.alias.set(key, alias[key])
+ })
+ })
+ }
+}
diff --git a/packages/@vuepress/core/lib/plugin-api/override/ClientDynamicModulesOption.js b/packages/@vuepress/core/lib/plugin-api/override/ClientDynamicModulesOption.js
new file mode 100644
index 0000000000..7dae23d628
--- /dev/null
+++ b/packages/@vuepress/core/lib/plugin-api/override/ClientDynamicModulesOption.js
@@ -0,0 +1,29 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const AsyncOption = require('../abstract/AsyncOption')
+
+/**
+ * clientDynamicModules option.
+ */
+
+module.exports = class ClientDynamicModulesOption extends AsyncOption {
+ async apply (ctx) {
+ await super.asyncApply()
+
+ for (const { value, name: pluginName } of this.appliedItems) {
+ const { name, content, dirname = 'dynamic' } = value
+ await ctx.writeTemp(
+ `${dirname}/${name}`,
+ `
+/**
+ * Generated by "${pluginName}"
+ */
+${content}\n\n
+ `.trim())
+ }
+ }
+}
diff --git a/packages/@vuepress/core/lib/plugin-api/override/DefineOption.js b/packages/@vuepress/core/lib/plugin-api/override/DefineOption.js
new file mode 100644
index 0000000000..006f0883e5
--- /dev/null
+++ b/packages/@vuepress/core/lib/plugin-api/override/DefineOption.js
@@ -0,0 +1,26 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const Option = require('../abstract/Option')
+
+/**
+ * define option.
+ */
+
+module.exports = class DefineOption extends Option {
+ apply (config) {
+ super.syncApply()
+ const defines = this.appliedValues
+ defines.forEach(define => {
+ Object.keys(define).forEach(key => {
+ define[key] = JSON.stringify(define[key])
+ })
+ config.plugin('injections').tap(([options]) => [
+ Object.assign(options, define)
+ ])
+ })
+ }
+}
diff --git a/packages/@vuepress/core/lib/plugin-api/override/EnhanceAppFilesOption.js b/packages/@vuepress/core/lib/plugin-api/override/EnhanceAppFilesOption.js
new file mode 100644
index 0000000000..9e5b3f5ac5
--- /dev/null
+++ b/packages/@vuepress/core/lib/plugin-api/override/EnhanceAppFilesOption.js
@@ -0,0 +1,88 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const AsyncOption = require('../abstract/AsyncOption')
+const {
+ fs,
+ chalk,
+ logger,
+ codegen: { pathsToModuleCode },
+ datatypes: { isPlainObject }
+} = require('@vuepress/shared-utils')
+
+/**
+ * enhanceAppFiles option.
+ */
+
+module.exports = class EnhanceAppFilesOption extends AsyncOption {
+ async apply (ctx) {
+ await super.asyncApply()
+
+ const manifest = []
+ let moduleId = 0
+
+ async function writeEnhancer (name, content, hasDefaultExport = true) {
+ return await ctx.writeTemp(
+ `app-enhancers/${name}.js`,
+ hasDefaultExport
+ ? content
+ : content + '\nexport default {}'
+ )
+ }
+
+ // 1. write enhance app files.
+ for (const { value: enhanceAppFile, name: pluginName } of this.appliedItems) {
+ let destPath
+
+ // 1.1 dynamic code
+ if (isPlainObject(enhanceAppFile)) {
+ const { content } = enhanceAppFile
+ let { name } = enhanceAppFile
+ name = name.replace(/.js$/, '')
+
+ if (hasDefaultExport(content)) {
+ destPath = await writeEnhancer(name, content)
+ } else {
+ destPath = await writeEnhancer(name, content, false /* do not contain default export*/)
+ }
+ // 1.2 local file
+ } else {
+ if (fs.existsSync(enhanceAppFile)) {
+ const content = await fs.readFile(enhanceAppFile, 'utf-8')
+
+ if (hasDefaultExport(content)) {
+ destPath = await writeEnhancer(
+ moduleId++,
+ `export { default } from ${JSON.stringify(enhanceAppFile)}`
+ )
+ } else {
+ destPath = await writeEnhancer(
+ moduleId++,
+ `import ${JSON.stringify(enhanceAppFile)}`,
+ false /* do not contain default export*/
+ )
+ }
+ } else {
+ logger.debug(
+ chalk.gray(`[${pluginName}] `) +
+ `${chalk.cyan(enhanceAppFile)} Not Found.`
+ )
+ }
+ }
+
+ if (destPath) {
+ manifest.push(destPath)
+ }
+ }
+
+ // 2. write entry file.
+ await ctx.writeTemp('internal/app-enhancers.js', pathsToModuleCode(manifest))
+ }
+}
+
+function hasDefaultExport (content) {
+ return content.includes('export default') || content.includes('module.exports')
+}
diff --git a/packages/@vuepress/core/lib/plugin-api/override/GlobalUIComponentsOption.js b/packages/@vuepress/core/lib/plugin-api/override/GlobalUIComponentsOption.js
new file mode 100644
index 0000000000..fb9f18e2c8
--- /dev/null
+++ b/packages/@vuepress/core/lib/plugin-api/override/GlobalUIComponentsOption.js
@@ -0,0 +1,20 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const Option = require('../abstract/Option')
+
+/**
+ * globalUIComponents option.
+ */
+
+module.exports = class GlobalUIComponentsOption extends Option {
+ async apply (ctx) {
+ await ctx.writeTemp(
+ `internal/global-ui.js`,
+ `export default ${JSON.stringify(this.values, null, 2)}`
+ )
+ }
+}
diff --git a/packages/@vuepress/core/lib/plugin-api/override/instantiateOption.js b/packages/@vuepress/core/lib/plugin-api/override/instantiateOption.js
new file mode 100644
index 0000000000..ad0fac2c7a
--- /dev/null
+++ b/packages/@vuepress/core/lib/plugin-api/override/instantiateOption.js
@@ -0,0 +1,28 @@
+const EnhanceAppFilesOption = require('./EnhanceAppFilesOption')
+const ClientDynamicModulesOption = require('./ClientDynamicModulesOption')
+const GlobalUIComponentsOption = require('./GlobalUIComponentsOption')
+const DefineOption = require('./DefineOption')
+const AliasOption = require('./AliasOption')
+const Option = require('../abstract/Option')
+const { PLUGIN_OPTION_MAP } = require('../constants')
+
+module.exports = function instantiateOption (name) {
+ switch (name) {
+ case PLUGIN_OPTION_MAP.ENHANCE_APP_FILES.name:
+ return new EnhanceAppFilesOption(name)
+
+ case PLUGIN_OPTION_MAP.CLIENT_DYNAMIC_MODULES.name:
+ return new ClientDynamicModulesOption(name)
+
+ case PLUGIN_OPTION_MAP.GLOBAL_UI_COMPONENTS.name:
+ return new GlobalUIComponentsOption(name)
+
+ case PLUGIN_OPTION_MAP.DEFINE.name:
+ return new DefineOption(name)
+
+ case PLUGIN_OPTION_MAP.ALIAS.name:
+ return new AliasOption(name)
+
+ default: return new Option(name)
+ }
+}
diff --git a/packages/@vuepress/core/lib/plugin-api/util.js b/packages/@vuepress/core/lib/plugin-api/util.js
new file mode 100644
index 0000000000..c7ff8123f0
--- /dev/null
+++ b/packages/@vuepress/core/lib/plugin-api/util.js
@@ -0,0 +1,85 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const { logger, chalk, datatypes: { assertTypes }} = require('@vuepress/shared-utils')
+
+/**
+ * flatten your plugin config by passing in name, options and context.
+ * @param {function|object} module
+ * @param {string} name
+ * @param {string} hortcut
+ * @param {object} pluginOptions
+ * @param {object} pluginContext
+ */
+
+exports.flattenPlugin = function (
+ { module: config, name, shortcut, isLocal },
+ pluginOptions,
+ pluginContext,
+ self
+) {
+ const { valid, warnMsg } = assertTypes(pluginOptions, [Object, Boolean])
+ if (!valid) {
+ if (pluginOptions !== undefined) {
+ logger.warn(
+ `[${chalk.gray(shortcut)}] ` +
+ `Invalid value for "pluginOptions" ${chalk.cyan(name)}: ${warnMsg}`
+ )
+ }
+ pluginOptions = {}
+ }
+
+ let enabled = true
+ if (typeof pluginOptions === 'boolean') {
+ enabled = pluginOptions
+ pluginOptions = {}
+ }
+
+ if (typeof config === 'function') {
+ // 'Object.create' here is to give each plugin a separate context,
+ // but also own the inheritance context.
+ config = config(pluginOptions, Object.create(pluginContext), self)
+ }
+
+ // respect name in local plugin config
+ name = isLocal && config.name || name
+ return Object.assign({}, config, {
+ name,
+ shortcut: isLocal ? null : shortcut,
+ enabled,
+ $$options: pluginOptions /* used for test */
+ })
+}
+
+/**
+ * Normalize plugins config in `.vuepress/config.js`
+ * @param pluginsConfig
+ */
+
+exports.normalizePluginsConfig = function (pluginsConfig) {
+ const { valid, warnMsg } = assertTypes(pluginsConfig, [Object, Array])
+ if (!valid) {
+ if (pluginsConfig !== undefined) {
+ logger.warn(
+ `[${chalk.gray('config')}] ` +
+ `Invalid value for "plugin" field : ${warnMsg}`
+ )
+ }
+ pluginsConfig = []
+ return pluginsConfig
+ }
+
+ if (Array.isArray(pluginsConfig)) {
+ pluginsConfig = pluginsConfig.map(item => {
+ return Array.isArray(item) ? item : [item]
+ })
+ } else if (typeof pluginsConfig === 'object') {
+ pluginsConfig = Object.keys(pluginsConfig).map(item => {
+ return [item, pluginsConfig[item]]
+ })
+ }
+ return pluginsConfig
+}
diff --git a/packages/@vuepress/core/lib/prepare/AppContext.js b/packages/@vuepress/core/lib/prepare/AppContext.js
new file mode 100644
index 0000000000..6b1d945899
--- /dev/null
+++ b/packages/@vuepress/core/lib/prepare/AppContext.js
@@ -0,0 +1,307 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const path = require('path')
+const createMarkdown = require('./createMarkdown')
+const loadConfig = require('./loadConfig')
+const loadTheme = require('./loadTheme')
+const { fs, logger, chalk, globby, sort, datatypes: { isFunction }} = require('@vuepress/shared-utils')
+
+const Page = require('./Page')
+const ClientComputedMixin = require('./ClientComputedMixin')
+const PluginAPI = require('../plugin-api/index')
+
+/**
+ * Expose AppContext.
+ */
+
+module.exports = class AppContext {
+ /**
+ * Instantiate the app context with a new API
+ *
+ * @param {string} sourceDir
+ * @param {{
+ * isProd: boolean,
+ * plugins: pluginsConfig,
+ * theme: themeNameConfig
+ * temp: string
+ * }} options
+ */
+
+ constructor (sourceDir, cliOptions = {}, isProd) {
+ this.sourceDir = sourceDir
+ this.cliOptions = cliOptions
+ this.isProd = isProd
+
+ const { tempPath, writeTemp } = createTemp(cliOptions.temp)
+ this.tempPath = tempPath
+ this.writeTemp = writeTemp
+
+ this.vuepressDir = path.resolve(sourceDir, '.vuepress')
+ this.siteConfig = loadConfig(this.vuepressDir)
+ if (isFunction(this.siteConfig)) {
+ this.siteConfig = this.siteConfig(this)
+ }
+
+ this.base = this.siteConfig.base || '/'
+ this.themeConfig = this.siteConfig.themeConfig || {}
+ this.outDir = this.siteConfig.dest
+ ? path.resolve(this.siteConfig.dest)
+ : path.resolve(sourceDir, '.vuepress/dist')
+
+ this.pluginAPI = new PluginAPI(this)
+ this.pages = [] // Array
+ this.ClientComputedMixinConstructor = ClientComputedMixin(this.getSiteData())
+ }
+
+ /**
+ * Load pages, load plugins, apply plugins / plugin options, etc.
+ *
+ * @returns {Promise}
+ * @api private
+ */
+
+ async process () {
+ this.normalizeHeadTagUrls()
+ this.resolveTemplates()
+ await this.resolveTheme()
+ this.resolvePlugins()
+ this.markdown = createMarkdown(this)
+
+ await this.resolvePages()
+ await Promise.all(
+ this.pluginAPI.options.additionalPages.values.map(async (options) => {
+ await this.addPage(options)
+ })
+ )
+
+ await this.pluginAPI.options.ready.apply()
+ await this.pluginAPI.options.clientDynamicModules.apply(this)
+ await this.pluginAPI.options.globalUIComponents.apply(this)
+ await this.pluginAPI.options.enhanceAppFiles.apply(this)
+ }
+
+ /**
+ * Apply internal and user plugins
+ *
+ * @api private
+ */
+
+ resolvePlugins () {
+ const themeConfig = this.themeConfig
+ const siteConfig = this.siteConfig
+
+ const shouldUseLastUpdated = (
+ themeConfig.lastUpdated ||
+ Object.keys(siteConfig.locales && themeConfig.locales || {})
+ .some(base => themeConfig.locales[base].lastUpdated)
+ )
+
+ this.pluginAPI
+ // internl core plugins
+ .use(Object.assign({}, siteConfig, { name: '@vuepress/internal-site-config' }))
+ .use(require('../internal-plugins/siteData'))
+ .use(require('../internal-plugins/routes'))
+ .use(require('../internal-plugins/rootMixins'))
+ .use(require('../internal-plugins/enhanceApp'))
+ .use(require('../internal-plugins/overrideCSS'))
+ .use(require('../internal-plugins/layoutComponents'))
+ .use(require('../internal-plugins/pageComponents'))
+ // user plugin
+ .useByPluginsConfig(this.cliOptions.plugins)
+ .useByPluginsConfig(this.siteConfig.plugins)
+ .useByPluginsConfig(this.themePlugins)
+ // built-in plugins
+ .use('@vuepress/last-updated', shouldUseLastUpdated)
+ .use('@vuepress/register-components', {
+ componentsDir: [
+ path.resolve(this.sourceDir, '.vuepress/components'),
+ path.resolve(this.themePath, 'components')
+ ]
+ })
+ .apply()
+ }
+
+ /**
+ * normalize head tag urls for base
+ *
+ * @api private
+ */
+
+ normalizeHeadTagUrls () {
+ if (this.base !== '/' && this.siteConfig.head) {
+ this.siteConfig.head.forEach(tag => {
+ const attrs = tag[1]
+ if (attrs) {
+ for (const name in attrs) {
+ if (name === 'src' || name === 'href') {
+ const value = attrs[name]
+ if (value.charAt(0) === '/') {
+ attrs[name] = this.base + value.slice(1)
+ }
+ }
+ }
+ }
+ })
+ }
+ }
+
+ /**
+ * Make template configurable
+ *
+ * @api private
+ */
+
+ resolveTemplates () {
+ let { ssrTemplate, devTemplate } = this.siteConfig
+ const templateDir = path.resolve(this.vuepressDir, 'templates')
+ if (!devTemplate) {
+ devTemplate = path.resolve(templateDir, 'dev.html')
+ if (!fs.existsSync(devTemplate)) {
+ devTemplate = path.resolve(__dirname, '../app/index.dev.html')
+ }
+ }
+ if (!ssrTemplate) {
+ ssrTemplate = path.resolve(templateDir, 'ssr.html')
+ if (!fs.existsSync(ssrTemplate)) {
+ ssrTemplate = path.resolve(__dirname, '../app/index.ssr.html')
+ }
+ }
+ logger.debug('SSR Template File: ' + chalk.gray(ssrTemplate))
+ logger.debug('DEV Template File: ' + chalk.gray(devTemplate))
+ this.devTemplate = devTemplate
+ this.ssrTemplate = ssrTemplate
+ }
+
+ /**
+ * Find all page source files located in sourceDir
+ *
+ * @returns {Promise}
+ * @api private
+ */
+
+ async resolvePages () {
+ // resolve pageFiles
+ const patterns = ['**/*.md', '!.vuepress', '!node_modules']
+ if (this.siteConfig.dest) {
+ // #654 exclude dest folder when dest dir was set in
+ // sourceDir but not in '.vuepress'
+ const outDirRelative = path.relative(this.sourceDir, this.outDir)
+ if (!outDirRelative.includes('..')) {
+ patterns.push('!' + outDirRelative)
+ }
+ }
+ const pageFiles = sort(await globby(patterns, { cwd: this.sourceDir }))
+
+ await Promise.all(pageFiles.map(async (relative) => {
+ const filePath = path.resolve(this.sourceDir, relative)
+ await this.addPage({ filePath, relative })
+ }))
+ }
+
+ /**
+ * Add a page
+ *
+ * @returns {Promise}
+ * @api public
+ */
+
+ async addPage (options) {
+ options.permalinkPattern = this.siteConfig.permalink
+ const page = new Page(options, this)
+ await page.process({
+ markdown: this.markdown,
+ computed: new this.ClientComputedMixinConstructor(),
+ enhancers: this.pluginAPI.options.extendPageData.items
+ })
+ this.pages.push(page)
+ }
+
+ /**
+ * Resolve theme
+ *
+ * @returns {Promise}
+ * @api private
+ */
+
+ async resolveTheme () {
+ const theme = this.siteConfig.theme || this.cliOptions.theme
+ Object.assign(this, (await loadTheme(theme, this.sourceDir, this.vuepressDir)))
+ }
+
+ /**
+ * Get the data to be delivered to the client.
+ *
+ * @returns {{
+ * title: string,
+ * description: string,
+ * base: string,
+ * pages: Page[],
+ * themeConfig: ThemeConfig,
+ * locales: Locales
+ * }}
+ * @api public
+ */
+
+ getSiteData () {
+ const { locales } = this.siteConfig
+ if (locales) {
+ Object.keys(locales).forEach(path => {
+ locales[path].path = path
+ })
+ }
+
+ return {
+ title: this.siteConfig.title || '',
+ description: this.siteConfig.description || '',
+ base: this.base,
+ pages: this.pages.map(page => page.toJson()),
+ themeConfig: this.siteConfig.themeConfig || {},
+ locales
+ }
+ }
+}
+
+/**
+ * Create a dynamic temp utility context that allow to lanuch
+ * multiple apps with isolated context at the same time.
+ * @param tempPath
+ * @returns {{
+ * writeTemp: (function(file: string, content: string): string),
+ * tempPath: string
+ * }}
+ */
+
+function createTemp (tempPath) {
+ if (!tempPath) {
+ tempPath = path.resolve(__dirname, '../../.temp')
+ } else {
+ tempPath = path.resolve(tempPath)
+ }
+
+ if (!fs.existsSync(tempPath)) {
+ fs.ensureDirSync(tempPath)
+ } else {
+ fs.emptyDirSync(tempPath)
+ }
+
+ logger.tip(`Temp directory: ${chalk.gray(tempPath)}`)
+ const tempCache = new Map()
+
+ async function writeTemp (file, content) {
+ const destPath = path.join(tempPath, file)
+ await fs.ensureDir(path.parse(destPath).dir)
+ // cache write to avoid hitting the dist if it didn't change
+ const cached = tempCache.get(file)
+ if (cached !== content) {
+ await fs.writeFile(destPath, content)
+ tempCache.set(file, content)
+ }
+ return destPath
+ }
+
+ return { writeTemp, tempPath }
+}
diff --git a/packages/@vuepress/core/lib/prepare/ClientComputedMixin.js b/packages/@vuepress/core/lib/prepare/ClientComputedMixin.js
new file mode 100644
index 0000000000..5922819432
--- /dev/null
+++ b/packages/@vuepress/core/lib/prepare/ClientComputedMixin.js
@@ -0,0 +1,128 @@
+'use strict'
+
+/**
+ * Get page data via path (permalink).
+ *
+ * @param {array} pages
+ * @param {string} path
+ * @returns {object}
+ */
+
+function findPageForPath (pages, path) {
+ for (let i = 0; i < pages.length; i++) {
+ const page = pages[i]
+ if (page.path === path) {
+ return page
+ }
+ }
+ return {
+ path: '',
+ frontmatter: {}
+ }
+}
+
+/**
+ * Expose a function to get ClientComputedMixin constructor.
+ * Note that this file will run in both server and client side.
+ *
+ * @param {object} siteData
+ * @returns {ClientComputedMixin}
+ */
+
+module.exports = siteData => {
+ // We cannot use class here. webpack will throw error.
+ function ClientComputedMixin () {}
+
+ ClientComputedMixin.prototype = {
+ setPage (page) {
+ this.__page = page
+ },
+
+ get $site () {
+ return siteData
+ },
+
+ get $themeConfig () {
+ return this.$site.themeConfig
+ },
+
+ get $localeConfig () {
+ const { locales = {}} = this.$site
+ let targetLang
+ let defaultLang
+ for (const path in locales) {
+ if (path === '/') {
+ defaultLang = locales[path]
+ } else if (this.$page.path.indexOf(path) === 0) {
+ targetLang = locales[path]
+ }
+ }
+ return targetLang || defaultLang || {}
+ },
+
+ get $siteTitle () {
+ return this.$localeConfig.title || this.$site.title || ''
+ },
+
+ get $title () {
+ const page = this.$page
+ const siteTitle = this.$siteTitle
+ const selfTitle = page.frontmatter.home ? null : (
+ page.frontmatter.title || // explicit title
+ page.title // inferred title
+ )
+ return siteTitle
+ ? selfTitle
+ ? (selfTitle + ' | ' + siteTitle)
+ : siteTitle
+ : selfTitle || 'VuePress'
+ },
+
+ get $description () {
+ // #565 hoist description from meta
+ const description = getMetaDescription(this.$page.frontmatter.meta)
+ if (description) {
+ return description
+ }
+ // if (this.$page.frontmatter.meta) {
+ // const descriptionMeta = this.$page.frontmatter.meta.filter(item => item.name === 'description')[0]
+ // if (descriptionMeta) return descriptionMeta.content
+ // }
+ return this.$page.frontmatter.description || this.$localeConfig.description || this.$site.description || ''
+ },
+
+ get $lang () {
+ return this.$page.frontmatter.lang || this.$localeConfig.lang || 'en-US'
+ },
+
+ get $localePath () {
+ return this.$localeConfig.path || '/'
+ },
+
+ get $themeLocaleConfig () {
+ return (this.$site.themeConfig.locales || {})[this.$localePath] || {}
+ },
+
+ get $page () {
+ if (this.__page) {
+ return this.__page
+ }
+ return findPageForPath(
+ this.$site.pages,
+ this.$route.path
+ )
+ }
+ }
+
+ return ClientComputedMixin
+}
+
+function getMetaDescription (meta) {
+ if (meta) {
+ // Why '(() => 'name')()' ?
+ // You can use item.name directly and see what happened.
+ // "How many pits did webpack bury?"
+ const descriptionMeta = meta.filter(item => item[(() => 'name')()] === 'description')[0]
+ if (descriptionMeta) return descriptionMeta.content
+ }
+}
diff --git a/packages/@vuepress/core/lib/prepare/Page.js b/packages/@vuepress/core/lib/prepare/Page.js
new file mode 100644
index 0000000000..af4cdba7f5
--- /dev/null
+++ b/packages/@vuepress/core/lib/prepare/Page.js
@@ -0,0 +1,223 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const path = require('path')
+const slugify = require('../../../markdown/lib/slugify')
+const { inferDate, DATE_RE } = require('../util/index')
+const { extractHeaders, fs, fileToPath, parseFrontmatter, getPermalink, inferTitle } = require('@vuepress/shared-utils')
+
+/**
+ * Expose Page class.
+ */
+
+module.exports = class Page {
+ /**
+ * @param {string} path the URL (excluding the domain name) for your page/post.
+ * @param {string} title markdown title
+ * @param {string} content markdown file content
+ * @param {string} filePath absolute file path of source markdown file.
+ * @param {string} relative relative file path of source markdown file.
+ * @param {string} permalink same to path, the URL (excluding the domain name) for your page/post.
+ * @param {object} frontmatter
+ * @param {string} permalinkPattern
+ */
+
+ constructor ({
+ path,
+ meta,
+ title,
+ content,
+ filePath,
+ relative,
+ permalink,
+ frontmatter = {},
+ permalinkPattern
+ }) {
+ this.title = title
+ this._meta = meta
+ this._filePath = filePath
+ this._content = content
+ this._permalink = permalink
+ this.frontmatter = frontmatter
+ this._permalinkPattern = permalinkPattern
+
+ if (relative) {
+ this.regularPath = encodeURI(fileToPath(relative))
+ } else if (path) {
+ this.regularPath = encodeURI(path)
+ } else if (permalink) {
+ this.regularPath = encodeURI(permalink)
+ }
+
+ this.key = 'v-' + Math.random().toString(16).slice(2)
+ // Using regularPath first, would be override by permalink later.
+ this.path = this.regularPath
+ }
+
+ /**
+ * Process a page.
+ *
+ * 1. If it's a page pointing to a md file, this method will try
+ * to resolve the page's title / headers from the content.
+ * 2. If it's a pure route. this method will only enhance it.
+ *
+ * @api public
+ */
+
+ async process ({
+ computed,
+ markdown,
+ enhancers = []
+ }) {
+ if (this._filePath) {
+ this._content = await fs.readFile(this._filePath, 'utf-8')
+ }
+
+ if (this._content) {
+ const { excerpt, data, content } = parseFrontmatter(this._content)
+ this._strippedContent = content
+ this.frontmatter = data
+
+ // infer title
+ const title = inferTitle(this.frontmatter, this._strippedContent)
+ if (title) {
+ this.title = title
+ }
+
+ // headers
+ const headers = extractHeaders(
+ this._strippedContent,
+ ['h2', 'h3'],
+ markdown
+ )
+ if (headers.length) {
+ this.headers = headers
+ }
+
+ if (excerpt) {
+ const { html } = markdown.render(excerpt)
+ this.excerpt = html
+ }
+ }
+
+ // resolve i18n
+ computed.setPage(this)
+ this._computed = computed
+ this._localePath = computed.$localePath
+
+ this.enhance(enhancers)
+ this.buildPermalink()
+ }
+
+ /**
+ * file name of page's source markdown file, or the last cut of regularPath.
+ *
+ * @returns {string}
+ * @api public
+ */
+
+ get filename () {
+ return path.parse(this._filePath || this.regularPath).name
+ }
+
+ /**
+ * slugified file name.
+ *
+ * @returns {string}
+ * @api public
+ */
+
+ get slug () {
+ return slugify(this.strippedFilename)
+ }
+
+ /**
+ * stripped file name.
+ *
+ * If filename was yyyy-MM-dd-[title], the date prefix will be stripped.
+ * If filename was yyyy-MM-[title], the date prefix will be stripped.
+ *
+ * @returns {string}
+ * @api public
+ */
+
+ get strippedFilename () {
+ const match = this.filename.match(DATE_RE)
+ return match ? match[3] : this.filename
+ }
+
+ /**
+ * get date of a page.
+ *
+ * @returns {null|string}
+ * @api public
+ */
+
+ get date () {
+ return inferDate(this.frontmatter, this.filename)
+ }
+
+ /**
+ * Convert page's metadata to JSON, note that all fields beginning
+ * with an underscore will not be serialized.
+ *
+ * @returns {object}
+ * @api public
+ */
+
+ toJson () {
+ const json = {}
+ Object.keys(this).reduce((json, key) => {
+ if (!key.startsWith('_')) {
+ json[key] = this[key]
+ }
+ return json
+ }, json)
+ return json
+ }
+
+ /**
+ * Build permalink via permalink pattern and page's metadata.
+ *
+ * @api private
+ */
+
+ buildPermalink () {
+ if (!this._permalink) {
+ this._permalink = getPermalink({
+ pattern: this.frontmatter.permalink || this._permalinkPattern,
+ slug: this.slug,
+ date: this.date,
+ localePath: this._localePath,
+ regularPath: this.regularPath
+ })
+ }
+
+ if (this._permalink) {
+ this.path = this._permalink
+ }
+ }
+
+ /**
+ * Execute the page enhancers. A enhancer could do following things:
+ *
+ * 1. Modify page's frontmetter.
+ * 2. Add extra field to the page.
+ *
+ * @api private
+ */
+
+ enhance (enhancers) {
+ for (const { name: pluginName, value: enhancer } of enhancers) {
+ try {
+ enhancer(this)
+ } catch (error) {
+ console.log(error)
+ throw new Error(`[${pluginName}] excuete extendPageData failed.`)
+ }
+ }
+ }
+}
diff --git a/packages/@vuepress/core/lib/prepare/createMarkdown.js b/packages/@vuepress/core/lib/prepare/createMarkdown.js
new file mode 100644
index 0000000000..6f399f66d4
--- /dev/null
+++ b/packages/@vuepress/core/lib/prepare/createMarkdown.js
@@ -0,0 +1,33 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const createMarkdown = require('@vuepress/markdown')
+
+/**
+ * Expose createMarkdown.
+ */
+
+module.exports = function (ctx) {
+ const { markdown: markdownConfig = {}} = ctx.siteConfig
+ const { chainMarkdown, extendMarkdown } = markdownConfig
+
+ const beforeInstantiate = config => {
+ chainMarkdown && chainMarkdown(config)
+ ctx.pluginAPI.options.chainMarkdown.syncApply(config)
+ }
+
+ const afterInstantiate = md => {
+ extendMarkdown && extendMarkdown(md)
+ ctx.pluginAPI.options.extendMarkdown.syncApply(md)
+ }
+
+ return createMarkdown(
+ Object.assign(markdownConfig, {
+ beforeInstantiate,
+ afterInstantiate
+ })
+ )
+}
diff --git a/packages/@vuepress/core/lib/prepare/index.js b/packages/@vuepress/core/lib/prepare/index.js
new file mode 100644
index 0000000000..06bd77ed83
--- /dev/null
+++ b/packages/@vuepress/core/lib/prepare/index.js
@@ -0,0 +1,17 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const AppContext = require('./AppContext')
+
+/**
+ * Expose prepare.
+ */
+
+module.exports = async function prepare (sourceDir, cliOptions, isProd) {
+ const appContext = new AppContext(sourceDir, cliOptions, isProd)
+ await appContext.process()
+ return appContext
+}
diff --git a/lib/prepare/loadConfig.js b/packages/@vuepress/core/lib/prepare/loadConfig.js
similarity index 91%
rename from lib/prepare/loadConfig.js
rename to packages/@vuepress/core/lib/prepare/loadConfig.js
index aa913ba013..a6f264bf7e 100644
--- a/lib/prepare/loadConfig.js
+++ b/packages/@vuepress/core/lib/prepare/loadConfig.js
@@ -1,8 +1,18 @@
-const fs = require('fs-extra')
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const { fs } = require('@vuepress/shared-utils')
const path = require('path')
const yamlParser = require('js-yaml')
const tomlParser = require('toml')
+/**
+ * Expose loadConfig.
+ */
+
module.exports = function loadConfig (vuepressDir, bustCache = true) {
const configPath = path.resolve(vuepressDir, 'config.js')
const configYmlPath = path.resolve(vuepressDir, 'config.yml')
diff --git a/packages/@vuepress/core/lib/prepare/loadTheme.js b/packages/@vuepress/core/lib/prepare/loadTheme.js
new file mode 100644
index 0000000000..3a8eedec21
--- /dev/null
+++ b/packages/@vuepress/core/lib/prepare/loadTheme.js
@@ -0,0 +1,126 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const path = require('path')
+const fs = require('fs')
+const {
+ shortcutPackageResolver: { resolveTheme },
+ datatypes: { isString },
+ logger, chalk
+} = require('@vuepress/shared-utils')
+
+/**
+ * Resolve theme.
+ *
+ * Resolving Priority:
+ *
+ * 1. If the theme was a absolute path and that path exists, respect it
+ * as the theme directory.
+ * 2. If 'theme' directory located at vuepressDir exists, respect it as
+ * the theme directory.
+ * 3. If 'theme' was a shortcut string, resolve it from deps.
+ *
+ * @param {string} theme
+ * @param {string} sourceDir
+ * @param {string} vuepressDir
+ * @returns {Promise}
+ */
+
+module.exports = async function loadTheme (theme, sourceDir, vuepressDir) {
+ const localThemePath = path.resolve(vuepressDir, 'theme')
+ const useLocalTheme =
+ !fs.existsSync(theme) &&
+ fs.existsSync(localThemePath) &&
+ (fs.readdirSync(localThemePath)).length > 0
+
+ let themePath = null // Mandatory
+ let themeIndexFile = null // Optional
+ let themeName
+ let themeShortcut
+
+ if (useLocalTheme) {
+ themePath = localThemePath
+ logger.tip(`\nApply theme located at ${chalk.gray(themePath)}...`)
+ } else if (isString(theme)) {
+ const { module: modulePath, name, shortcut } = resolveTheme(theme, sourceDir)
+ if (modulePath.endsWith('.js') || modulePath.endsWith('.vue')) {
+ themePath = path.parse(modulePath).dir
+ } else {
+ themePath = modulePath
+ }
+ themeName = name
+ themeShortcut = shortcut
+ logger.tip(`\nApply theme ${chalk.gray(themeName)}`)
+ } 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`)
+ }
+
+ try {
+ themeIndexFile = require(themePath)
+ } catch (error) {
+ themeIndexFile = {}
+ }
+
+ // handle theme api
+ const {
+ plugins: themePlugins,
+ palette: themePalette,
+ layoutDir = useLocalTheme
+ ? '.'
+ : 'layouts'
+ } = themeIndexFile
+
+ const layoutDirPath = path.resolve(themePath, layoutDir)
+
+ // normalize component name
+ const getComponentName = filename => {
+ filename = filename.slice(0, -4)
+ if (filename === '404') {
+ filename = 'NotFound'
+ }
+ return filename
+ }
+
+ // built-in named layout or not.
+ const isInternal = componentName => componentName === 'Layout' ||
+ componentName === 'NotFound'
+
+ const layoutComponentMap = fs.readdirSync(layoutDirPath)
+ .filter(filename => filename.endsWith('.vue'))
+ .reduce((map, filename) => {
+ const componentName = getComponentName(filename)
+ const componentPath = path.resolve(layoutDirPath, filename)
+ map[componentName] = { filename, componentName, path: componentPath }
+ if (isInternal(componentName)) {
+ map[componentName].isInternal = true
+ }
+ return map
+ }, {})
+
+ if (!layoutComponentMap.Layout && !fs.existsSync(layoutComponentMap.Layout.path)) {
+ throw new Error(`[vuepress] Cannot resolve Layout.vue file in \n ${layoutComponentMap.Layout.path}`)
+ }
+
+ // use default 404 component.
+ if (!layoutComponentMap.NotFound || !fs.existsSync(layoutComponentMap.NotFound.path)) {
+ layoutComponentMap['404'] = {
+ filename: 'Layout.vue',
+ componentName: 'NotFound',
+ path: path.resolve(__dirname, '../app/components/NotFound.vue'),
+ isInternal: true
+ }
+ }
+
+ return {
+ themePath,
+ layoutComponentMap,
+ themeIndexFile,
+ themePlugins,
+ themePalette,
+ themeName,
+ themeShortcut
+ }
+}
diff --git a/packages/@vuepress/core/lib/util/index.js b/packages/@vuepress/core/lib/util/index.js
new file mode 100644
index 0000000000..20b97a5154
--- /dev/null
+++ b/packages/@vuepress/core/lib/util/index.js
@@ -0,0 +1,66 @@
+'use strict'
+
+/**
+ * Normalize head tag config.
+ *
+ * @param {string|array} tag
+ * @returns {object}
+ */
+
+exports.normalizeHeadTag = function (tag) {
+ if (typeof tag === 'string') {
+ tag = [tag]
+ }
+ const tagName = tag[0]
+ return {
+ tagName,
+ attributes: tag[1] || {},
+ innerHTML: tag[2] || '',
+ closeTag: !(tagName === 'meta' || tagName === 'link')
+ }
+}
+
+/**
+ * Use webpack-merge to merge user's config into default config.
+ *
+ * @param {object} userConfig
+ * @param {object} config
+ * @param {boolean} isServer
+ * @returns {object}
+ */
+
+exports.applyUserWebpackConfig = function (userConfig, config, isServer) {
+ const merge = require('webpack-merge')
+ if (typeof userConfig === 'object') {
+ return merge(config, userConfig)
+ }
+ if (typeof userConfig === 'function') {
+ const res = userConfig(config, isServer)
+ if (res && typeof res === 'object') {
+ return merge(config, res)
+ }
+ }
+ return config
+}
+
+/**
+ * Infer date.
+ *
+ * @param {object} frontmatter
+ * @param {string} filename
+ * @returns {null|string}
+ */
+
+const DATE_RE = /(\d{4}-\d{1,2}(-\d{1,2})?)-(.*)/
+exports.DATE_RE = DATE_RE
+
+exports.inferDate = function (frontmatter = {}, filename) {
+ if (frontmatter.date) {
+ return frontmatter.date
+ }
+ const match = filename.match(DATE_RE)
+ if (match) {
+ return match[1]
+ }
+ return null
+}
diff --git a/lib/webpack/ClientPlugin.js b/packages/@vuepress/core/lib/webpack/ClientPlugin.js
similarity index 99%
rename from lib/webpack/ClientPlugin.js
rename to packages/@vuepress/core/lib/webpack/ClientPlugin.js
index 82028475bb..80aeb8927d 100644
--- a/lib/webpack/ClientPlugin.js
+++ b/packages/@vuepress/core/lib/webpack/ClientPlugin.js
@@ -1,3 +1,5 @@
+'use strict'
+
// Temporarily hacked dev build
var isJS = function (file) { return /\.js(\?[^.]+)?$/.test(file) }
diff --git a/lib/webpack/DevLogPlugin.js b/packages/@vuepress/core/lib/webpack/DevLogPlugin.js
similarity index 87%
rename from lib/webpack/DevLogPlugin.js
rename to packages/@vuepress/core/lib/webpack/DevLogPlugin.js
index 8fd18d4a0a..254f0bd4dd 100644
--- a/lib/webpack/DevLogPlugin.js
+++ b/packages/@vuepress/core/lib/webpack/DevLogPlugin.js
@@ -1,5 +1,14 @@
-const chalk = require('chalk')
-const logger = require('../util/logger')
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const { chalk, logger } = require('@vuepress/shared-utils')
+
+/**
+ * Expose DevLogPlugin class.
+ */
module.exports = class DevLogPlugin {
constructor (options) {
diff --git a/lib/webpack/HeadPlugin.js b/packages/@vuepress/core/lib/webpack/HeadPlugin.js
similarity index 72%
rename from lib/webpack/HeadPlugin.js
rename to packages/@vuepress/core/lib/webpack/HeadPlugin.js
index 2ccb46e07d..828c2f2212 100644
--- a/lib/webpack/HeadPlugin.js
+++ b/packages/@vuepress/core/lib/webpack/HeadPlugin.js
@@ -1,6 +1,16 @@
-const { normalizeHeadTag } = require('../util')
+'use strict'
-module.exports = class SiteDataPlugin {
+/**
+ * Module dependencies.
+ */
+
+const { normalizeHeadTag } = require('../util/index')
+
+/**
+ * Expose HeadPlugin class.
+ */
+
+module.exports = class HeadPlugin {
constructor ({ tags }) {
this.tags = tags
}
diff --git a/lib/webpack/createBaseConfig.js b/packages/@vuepress/core/lib/webpack/createBaseConfig.js
similarity index 83%
rename from lib/webpack/createBaseConfig.js
rename to packages/@vuepress/core/lib/webpack/createBaseConfig.js
index dcf7fab328..dd7969203c 100644
--- a/lib/webpack/createBaseConfig.js
+++ b/packages/@vuepress/core/lib/webpack/createBaseConfig.js
@@ -1,17 +1,30 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
const path = require('path')
+const { fs, logger, chalk } = require('@vuepress/shared-utils')
+
+/**
+ * Expose createBaseConfig method.
+ */
module.exports = function createBaseConfig ({
siteConfig,
- siteData,
sourceDir,
outDir,
- publicPath,
+ base: publicPath,
themePath,
- themeLayoutPath,
- themeNotFoundPath,
- isAlgoliaSearch,
- markdown
-}, { debug } = {}, isServer) {
+ markdown,
+ tempPath,
+ cliOptions: {
+ debug,
+ cache
+ },
+ pluginAPI
+}, isServer) {
const Config = require('webpack-chain')
const { VueLoaderPlugin } = require('vue-loader')
const CSSExtractPlugin = require('mini-css-extract-plugin')
@@ -38,15 +51,11 @@ module.exports = function createBaseConfig ({
.set('symlinks', true)
.alias
.set('@theme', themePath)
- .set('@themeLayout', themeLayoutPath)
- .set('@themeNotFound', themeNotFoundPath)
.set('@source', sourceDir)
.set('@app', path.resolve(__dirname, '../app'))
- .set('@temp', path.resolve(__dirname, '../app/.temp'))
- .set('@default-theme', path.resolve(__dirname, '../default-theme'))
- .set('@AlgoliaSearchBox', isAlgoliaSearch
- ? path.resolve(__dirname, '../default-theme/AlgoliaSearchBox.vue')
- : path.resolve(__dirname, './noopModule.js'))
+ .set('@temp', tempPath)
+ .set('@dynamic', path.resolve(tempPath, 'dynamic'))
+ .set('@internal', path.resolve(tempPath, 'internal'))
.end()
.extensions
.merge(['.js', '.jsx', '.vue', '.json', '.styl'])
@@ -57,18 +66,6 @@ module.exports = function createBaseConfig ({
.add(path.resolve(__dirname, '../../../'))
.add('node_modules')
- // Load extra root mixins on demand.
- const { activeHeaderLinks = true } = siteData.themeConfig
- const rootMixinsLoadingConfig = { activeHeaderLinks }
- for (const mixinName in rootMixinsLoadingConfig) {
- const enabled = rootMixinsLoadingConfig[mixinName]
- config.resolve
- .alias.set(`@${mixinName}`, enabled
- ? path.resolve(__dirname, `../app/root-mixins/${mixinName}.js`)
- : path.resolve(__dirname, './noopModule.js')
- )
- }
-
config.resolveLoader
.set('symlinks', true)
.modules
@@ -80,7 +77,17 @@ module.exports = function createBaseConfig ({
config.module
.noParse(/^(vue|vue-router|vuex|vuex-router-sync)$/)
- const cacheDirectory = path.resolve(__dirname, '../../node_modules/.cache/vuepress')
+ let cacheDirectory
+ if (cache && typeof cache === 'string') {
+ cacheDirectory = path.resolve(cache)
+ } else {
+ cacheDirectory = path.resolve(__dirname, '../../node_modules/.cache/vuepress')
+ }
+ logger.debug('Cache directory: ' + chalk.gray(cacheDirectory))
+ if (!cache) {
+ logger.tip('\nClean cache...\n')
+ fs.emptyDirSync(cacheDirectory)
+ }
const cacheIdentifier = JSON.stringify({
vuepress: require('../../package.json').version,
'cache-loader': require('cache-loader').version,
@@ -129,7 +136,7 @@ module.exports = function createBaseConfig ({
mdRule
.use('markdown-loader')
- .loader(require.resolve('./markdownLoader'))
+ .loader(require.resolve('@vuepress/markdown-loader'))
.options({ sourceDir, markdown })
config.module
@@ -144,17 +151,17 @@ module.exports = function createBaseConfig ({
config.module
.rule('js')
.test(/\.js$/)
- .exclude.add(filepath => {
+ .exclude.add(filePath => {
// Always transpile lib directory
- if (filepath.startsWith(libDir)) {
+ if (filePath.startsWith(libDir)) {
return false
}
// always transpile js in vue files
- if (/\.vue\.js$/.test(filepath)) {
+ if (/\.vue\.js$/.test(filePath)) {
return false
}
// Don't transpile node_modules
- return /node_modules/.test(filepath)
+ return /node_modules/.test(filePath)
}).end()
.use('cache-loader')
.loader('cache-loader')
@@ -295,13 +302,14 @@ module.exports = function createBaseConfig ({
config
.plugin('injections')
.use(require('webpack/lib/DefinePlugin'), [{
- BASE_URL: JSON.stringify(siteConfig.base || '/'),
- GA_ID: siteConfig.ga ? JSON.stringify(siteConfig.ga) : false,
- SW_ENABLED: !!siteConfig.serviceWorker,
VUEPRESS_VERSION: JSON.stringify(require('../../package.json').version),
+ VUEPRESS_TEMP_PATH: JSON.stringify(tempPath),
LAST_COMMIT_HASH: JSON.stringify(getLastCommitHash())
}])
+ pluginAPI.options.define.apply(config)
+ pluginAPI.options.alias.apply(config)
+
return config
}
diff --git a/lib/webpack/createClientConfig.js b/packages/@vuepress/core/lib/webpack/createClientConfig.js
similarity index 82%
rename from lib/webpack/createClientConfig.js
rename to packages/@vuepress/core/lib/webpack/createClientConfig.js
index 8c27663301..c818c29310 100644
--- a/lib/webpack/createClientConfig.js
+++ b/packages/@vuepress/core/lib/webpack/createClientConfig.js
@@ -1,9 +1,15 @@
-module.exports = function createClientConfig (options, cliOptions) {
+'use strict'
+
+/**
+ * Expose createClientConfig method.
+ */
+
+module.exports = function createClientConfig (ctx) {
const path = require('path')
const WebpackBar = require('webpackbar')
const createBaseConfig = require('./createBaseConfig')
- const config = createBaseConfig(options, cliOptions)
+ const config = createBaseConfig(ctx)
config
.entry('app')
@@ -51,7 +57,7 @@ module.exports = function createClientConfig (options, cliOptions) {
}])
}
- if (!cliOptions.debug) {
+ if (!ctx.cliOptions.debug) {
config
.plugin('bar')
.use(WebpackBar, [{
@@ -61,9 +67,11 @@ module.exports = function createClientConfig (options, cliOptions) {
}])
}
- if (options.siteConfig.chainWebpack) {
- options.siteConfig.chainWebpack(config, false /* isServer */)
+ if (ctx.siteConfig.chainWebpack) {
+ ctx.siteConfig.chainWebpack(config, false /* isServer */)
}
+ ctx.pluginAPI.options.chainWebpack.syncApply(config, false /* isServer */)
+
return config
}
diff --git a/lib/webpack/createServerConfig.js b/packages/@vuepress/core/lib/webpack/createServerConfig.js
similarity index 73%
rename from lib/webpack/createServerConfig.js
rename to packages/@vuepress/core/lib/webpack/createServerConfig.js
index e9f098354b..b8592a98fa 100644
--- a/lib/webpack/createServerConfig.js
+++ b/packages/@vuepress/core/lib/webpack/createServerConfig.js
@@ -1,4 +1,10 @@
-module.exports = function createServerConfig (options, cliOptions) {
+'use strict'
+
+/**
+ * Expose createServerConfig method.
+ */
+
+module.exports = function createServerConfig (ctx) {
const fs = require('fs')
const path = require('path')
const WebpackBar = require('webpackbar')
@@ -6,8 +12,8 @@ module.exports = function createServerConfig (options, cliOptions) {
const VueSSRServerPlugin = require('vue-server-renderer/server-plugin')
const CopyPlugin = require('copy-webpack-plugin')
- const config = createBaseConfig(options, cliOptions, true /* isServer */)
- const { sourceDir, outDir } = options
+ const config = createBaseConfig(ctx, true /* isServer */)
+ const { sourceDir, outDir } = ctx
config
.target('node')
@@ -40,7 +46,7 @@ module.exports = function createServerConfig (options, cliOptions) {
]])
}
- if (!cliOptions.debug) {
+ if (!ctx.cliOptions.debug) {
config
.plugin('bar')
.use(WebpackBar, [{
@@ -50,9 +56,11 @@ module.exports = function createServerConfig (options, cliOptions) {
}])
}
- if (options.siteConfig.chainWebpack) {
- options.siteConfig.chainWebpack(config, true /* isServer */)
+ if (ctx.siteConfig.chainWebpack) {
+ ctx.siteConfig.chainWebpack(config, true /* isServer */)
}
+ ctx.pluginAPI.options.chainWebpack.syncApply(config, true /* isServer */)
+
return config
}
diff --git a/lib/webpack/noopModule.js b/packages/@vuepress/core/lib/webpack/noopModule.js
similarity index 100%
rename from lib/webpack/noopModule.js
rename to packages/@vuepress/core/lib/webpack/noopModule.js
diff --git a/packages/@vuepress/core/package.json b/packages/@vuepress/core/package.json
new file mode 100644
index 0000000000..e88364f6fb
--- /dev/null
+++ b/packages/@vuepress/core/package.json
@@ -0,0 +1,71 @@
+{
+ "name": "@vuepress/core",
+ "version": "1.0.0",
+ "description": "Minimalistic doc generator with Vue component based layout system",
+ "main": "lib/index.js",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "generator"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress#readme",
+ "dependencies": {
+ "@babel/core": "7.0.0-beta.47",
+ "@vue/babel-preset-app": "3.0.0-beta.11",
+ "@vuepress/markdown": "^1.0.0",
+ "@vuepress/markdown-loader": "^1.0.0",
+ "@vuepress/shared-utils": "^1.0.0",
+ "@vuepress/plugin-last-updated": "^1.0.0",
+ "@vuepress/plugin-register-components": "^1.0.0",
+ "autoprefixer": "^8.2.0",
+ "babel-loader": "8.0.0-beta.3",
+ "cache-loader": "^1.2.2",
+ "chokidar": "^2.0.3",
+ "commander": "^2.15.1",
+ "connect-history-api-fallback": "^1.5.0",
+ "copy-webpack-plugin": "^4.5.1",
+ "cross-spawn": "^6.0.5",
+ "css-loader": "^0.28.11",
+ "file-loader": "^1.1.11",
+ "globby": "^8.0.1",
+ "gray-matter": "^4.0.1",
+ "js-yaml": "^3.11.0",
+ "koa-connect": "^2.0.1",
+ "koa-mount": "^3.0.0",
+ "koa-range": "^0.3.0",
+ "koa-static": "^4.0.2",
+ "lru-cache": "^4.1.2",
+ "mini-css-extract-plugin": "^0.4.1",
+ "optimize-css-assets-webpack-plugin": "^4.0.0",
+ "portfinder": "^1.0.13",
+ "postcss-loader": "^2.1.5",
+ "toml": "^2.3.3",
+ "url-loader": "^1.0.1",
+ "vue": "^2.5.16",
+ "vue-loader": "^15.2.4",
+ "vue-router": "^3.0.1",
+ "vue-server-renderer": "^2.5.16",
+ "vue-template-compiler": "^2.5.16",
+ "vuepress-html-webpack-plugin": "^3.2.0",
+ "webpack": "^4.8.1",
+ "webpack-chain": "^4.6.0",
+ "webpack-merge": "^4.1.2",
+ "webpack-serve": "^1.0.2",
+ "webpackbar": "^2.6.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "browserslist": [
+ ">1%"
+ ]
+}
diff --git a/packages/@vuepress/markdown-loader/.npmignore b/packages/@vuepress/markdown-loader/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/markdown-loader/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/markdown-loader/README.md b/packages/@vuepress/markdown-loader/README.md
new file mode 100644
index 0000000000..90ad547ca5
--- /dev/null
+++ b/packages/@vuepress/markdown-loader/README.md
@@ -0,0 +1,24 @@
+# @vuepress/markdown-loader
+
+> markdown-loader for vuepress
+
+## Usage
+
+```js
+const rule = config.module
+ .rule('markdown')
+ .test(/\.md$/)
+
+rule
+ .use('vue-loader')
+ .loader('vue-loader')
+ .options({ /* ... */ })
+
+rule
+ .use('markdown-loader')
+ .loader(require.resolve('@vuepress/markdown-loader'))
+ .options({
+ markdown: /* instance created by @vuepress/markdown */,
+ sourceDir: /* root source directory of your docs */,
+ })
+```
diff --git a/lib/webpack/markdownLoader.js b/packages/@vuepress/markdown-loader/index.js
similarity index 86%
rename from lib/webpack/markdownLoader.js
rename to packages/@vuepress/markdown-loader/index.js
index 3c49b747f6..fd35030157 100644
--- a/lib/webpack/markdownLoader.js
+++ b/packages/@vuepress/markdown-loader/index.js
@@ -1,18 +1,32 @@
-const fs = require('fs')
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
const path = require('path')
-const hash = require('hash-sum')
const { EventEmitter } = require('events')
const { getOptions } = require('loader-utils')
-const { inferTitle, extractHeaders, parseFrontmatter } = require('../util')
+const { fs, hash, parseFrontmatter, inferTitle, extractHeaders } = require('@vuepress/shared-utils')
const LRU = require('lru-cache')
+const md = require('@vuepress/markdown')
const cache = LRU({ max: 1000 })
const devCache = LRU({ max: 1000 })
+/**
+ * Expose markdown loader.
+ */
+
module.exports = function (src) {
const isProd = process.env.NODE_ENV === 'production'
const isServer = this.target === 'node'
- const { markdown, sourceDir } = getOptions(this)
+ const options = getOptions(this)
+ const { sourceDir } = options
+ let { markdown } = options
+ if (!markdown) {
+ markdown = md()
+ }
// we implement a manual cache here because this loader is chained before
// vue-loader, and will be applied on the same file multiple times when
@@ -28,7 +42,7 @@ module.exports = function (src) {
const content = frontmatter.content
if (!isProd && !isServer) {
- const inferredTitle = inferTitle(frontmatter)
+ const inferredTitle = inferTitle(frontmatter.data, frontmatter.content)
const headers = extractHeaders(content, ['h2', 'h3'], markdown)
delete frontmatter.content
@@ -58,18 +72,23 @@ module.exports = function (src) {
// check if relative links are valid
links && links.forEach(link => {
link = decodeURIComponent(link)
+
const shortname = link
.replace(/#.*$/, '')
.replace(/\.html$/, '.md')
+
const filename = shortname
.replace(/\/$/, '/README.md')
.replace(/^\//, sourceDir + '/')
+
const altname = shortname
.replace(/\/$/, '/index.md')
.replace(/^\//, sourceDir + '/')
+
const dir = path.dirname(this.resourcePath)
const file = path.resolve(dir, filename)
const altfile = altname !== filename ? path.resolve(dir, altname) : null
+
if (!fs.existsSync(file) && (!altfile || !fs.existsSync(altfile))) {
this.emitWarning(
new Error(
diff --git a/packages/@vuepress/markdown-loader/package.json b/packages/@vuepress/markdown-loader/package.json
new file mode 100644
index 0000000000..7aaa5bafc8
--- /dev/null
+++ b/packages/@vuepress/markdown-loader/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "@vuepress/markdown-loader",
+ "version": "1.0.0",
+ "description": "markdown-loader for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "dependencies": {
+ "@vuepress/markdown": "1.0.0",
+ "loader-utils": "^1.1.0"
+ },
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/markdown-loader#readme"
+}
diff --git a/packages/@vuepress/markdown/.npmignore b/packages/@vuepress/markdown/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/markdown/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/markdown/README.md b/packages/@vuepress/markdown/README.md
new file mode 100644
index 0000000000..5be9b185d7
--- /dev/null
+++ b/packages/@vuepress/markdown/README.md
@@ -0,0 +1,3 @@
+# @vuepress/markdown
+
+> markdown for vuepress
\ No newline at end of file
diff --git a/test/markdown/__snapshots__/containers.spec.js.snap b/packages/@vuepress/markdown/__tests__/__snapshots__/containers.spec.js.snap
similarity index 100%
rename from test/markdown/__snapshots__/containers.spec.js.snap
rename to packages/@vuepress/markdown/__tests__/__snapshots__/containers.spec.js.snap
diff --git a/test/markdown/__snapshots__/highlight.spec.js.snap b/packages/@vuepress/markdown/__tests__/__snapshots__/highlight.spec.js.snap
similarity index 100%
rename from test/markdown/__snapshots__/highlight.spec.js.snap
rename to packages/@vuepress/markdown/__tests__/__snapshots__/highlight.spec.js.snap
diff --git a/test/markdown/__snapshots__/highlightLines.spec.js.snap b/packages/@vuepress/markdown/__tests__/__snapshots__/highlightLines.spec.js.snap
similarity index 100%
rename from test/markdown/__snapshots__/highlightLines.spec.js.snap
rename to packages/@vuepress/markdown/__tests__/__snapshots__/highlightLines.spec.js.snap
diff --git a/test/markdown/__snapshots__/hoist.spec.js.snap b/packages/@vuepress/markdown/__tests__/__snapshots__/hoist.spec.js.snap
similarity index 100%
rename from test/markdown/__snapshots__/hoist.spec.js.snap
rename to packages/@vuepress/markdown/__tests__/__snapshots__/hoist.spec.js.snap
diff --git a/test/markdown/__snapshots__/lineNumers.spec.js.snap b/packages/@vuepress/markdown/__tests__/__snapshots__/lineNumers.spec.js.snap
similarity index 100%
rename from test/markdown/__snapshots__/lineNumers.spec.js.snap
rename to packages/@vuepress/markdown/__tests__/__snapshots__/lineNumers.spec.js.snap
diff --git a/test/markdown/__snapshots__/link.spec.js.snap b/packages/@vuepress/markdown/__tests__/__snapshots__/link.spec.js.snap
similarity index 100%
rename from test/markdown/__snapshots__/link.spec.js.snap
rename to packages/@vuepress/markdown/__tests__/__snapshots__/link.spec.js.snap
diff --git a/test/markdown/__snapshots__/snippet.spec.js.snap b/packages/@vuepress/markdown/__tests__/__snapshots__/snippet.spec.js.snap
similarity index 100%
rename from test/markdown/__snapshots__/snippet.spec.js.snap
rename to packages/@vuepress/markdown/__tests__/__snapshots__/snippet.spec.js.snap
diff --git a/test/markdown/containers.spec.js b/packages/@vuepress/markdown/__tests__/containers.spec.js
similarity index 89%
rename from test/markdown/containers.spec.js
rename to packages/@vuepress/markdown/__tests__/containers.spec.js
index dd4fdb4281..e2c0071a31 100644
--- a/test/markdown/containers.spec.js
+++ b/packages/@vuepress/markdown/__tests__/containers.spec.js
@@ -1,5 +1,5 @@
import { Md, getFragment } from './util'
-import containers from '@/markdown/containers.js'
+import containers from '../lib/containers.js'
const mdC = Md().use(containers)
diff --git a/test/markdown/fragments/code-highlightLines-multiple.md b/packages/@vuepress/markdown/__tests__/fragments/code-highlightLines-multiple.md
similarity index 100%
rename from test/markdown/fragments/code-highlightLines-multiple.md
rename to packages/@vuepress/markdown/__tests__/fragments/code-highlightLines-multiple.md
diff --git a/test/markdown/fragments/code-highlightLines-single.md b/packages/@vuepress/markdown/__tests__/fragments/code-highlightLines-single.md
similarity index 100%
rename from test/markdown/fragments/code-highlightLines-single.md
rename to packages/@vuepress/markdown/__tests__/fragments/code-highlightLines-single.md
diff --git a/packages/@vuepress/markdown/__tests__/fragments/code-snippet-highlightLines-multiple.md b/packages/@vuepress/markdown/__tests__/fragments/code-snippet-highlightLines-multiple.md
new file mode 100644
index 0000000000..c06bf83f85
--- /dev/null
+++ b/packages/@vuepress/markdown/__tests__/fragments/code-snippet-highlightLines-multiple.md
@@ -0,0 +1 @@
+<<< @/packages/@vuepress/core/__test__/markdown/fragments/snippet.js{1-3}
diff --git a/packages/@vuepress/markdown/__tests__/fragments/code-snippet-highlightLines-single.md b/packages/@vuepress/markdown/__tests__/fragments/code-snippet-highlightLines-single.md
new file mode 100644
index 0000000000..d98c6ce859
--- /dev/null
+++ b/packages/@vuepress/markdown/__tests__/fragments/code-snippet-highlightLines-single.md
@@ -0,0 +1 @@
+<<< @/packages/@vuepress/core/__test__/markdown/fragments/snippet.js{1,3}
diff --git a/packages/@vuepress/markdown/__tests__/fragments/code-snippet.md b/packages/@vuepress/markdown/__tests__/fragments/code-snippet.md
new file mode 100644
index 0000000000..5b87126e7f
--- /dev/null
+++ b/packages/@vuepress/markdown/__tests__/fragments/code-snippet.md
@@ -0,0 +1 @@
+<<< @/packages/@vuepress/core/__test__/markdown/fragments/snippet.js
\ No newline at end of file
diff --git a/test/markdown/fragments/code.md b/packages/@vuepress/markdown/__tests__/fragments/code.md
similarity index 100%
rename from test/markdown/fragments/code.md
rename to packages/@vuepress/markdown/__tests__/fragments/code.md
diff --git a/test/markdown/fragments/container-danger.md b/packages/@vuepress/markdown/__tests__/fragments/container-danger.md
similarity index 100%
rename from test/markdown/fragments/container-danger.md
rename to packages/@vuepress/markdown/__tests__/fragments/container-danger.md
diff --git a/test/markdown/fragments/container-tip-override.md b/packages/@vuepress/markdown/__tests__/fragments/container-tip-override.md
similarity index 100%
rename from test/markdown/fragments/container-tip-override.md
rename to packages/@vuepress/markdown/__tests__/fragments/container-tip-override.md
diff --git a/test/markdown/fragments/container-tip.md b/packages/@vuepress/markdown/__tests__/fragments/container-tip.md
similarity index 100%
rename from test/markdown/fragments/container-tip.md
rename to packages/@vuepress/markdown/__tests__/fragments/container-tip.md
diff --git a/test/markdown/fragments/container-v-pre.md b/packages/@vuepress/markdown/__tests__/fragments/container-v-pre.md
similarity index 100%
rename from test/markdown/fragments/container-v-pre.md
rename to packages/@vuepress/markdown/__tests__/fragments/container-v-pre.md
diff --git a/test/markdown/fragments/container-warning.md b/packages/@vuepress/markdown/__tests__/fragments/container-warning.md
similarity index 100%
rename from test/markdown/fragments/container-warning.md
rename to packages/@vuepress/markdown/__tests__/fragments/container-warning.md
diff --git a/test/markdown/fragments/hoist.md b/packages/@vuepress/markdown/__tests__/fragments/hoist.md
similarity index 100%
rename from test/markdown/fragments/hoist.md
rename to packages/@vuepress/markdown/__tests__/fragments/hoist.md
diff --git a/test/markdown/fragments/snippet.js b/packages/@vuepress/markdown/__tests__/fragments/snippet.js
similarity index 100%
rename from test/markdown/fragments/snippet.js
rename to packages/@vuepress/markdown/__tests__/fragments/snippet.js
diff --git a/test/markdown/highlight.spec.js b/packages/@vuepress/markdown/__tests__/highlight.spec.js
similarity index 88%
rename from test/markdown/highlight.spec.js
rename to packages/@vuepress/markdown/__tests__/highlight.spec.js
index 19522235f8..0ca9cb3cd3 100644
--- a/test/markdown/highlight.spec.js
+++ b/packages/@vuepress/markdown/__tests__/highlight.spec.js
@@ -1,5 +1,5 @@
import { Md, getFragment } from './util'
-import highlight from '@/markdown/highlight.js'
+import highlight from '../lib/highlight.js'
const md = Md()
const mdH = Md().set({ highlight })
diff --git a/test/markdown/highlightLines.spec.js b/packages/@vuepress/markdown/__tests__/highlightLines.spec.js
similarity index 92%
rename from test/markdown/highlightLines.spec.js
rename to packages/@vuepress/markdown/__tests__/highlightLines.spec.js
index 49fe7065c4..d242c9a4bb 100644
--- a/test/markdown/highlightLines.spec.js
+++ b/packages/@vuepress/markdown/__tests__/highlightLines.spec.js
@@ -1,5 +1,5 @@
import { Md, getFragment } from './util'
-import highlightLines from '@/markdown/highlightLines.js'
+import highlightLines from '../lib/highlightLines.js'
const md = Md()
const mdH = Md().use(highlightLines)
diff --git a/test/markdown/hoist.spec.js b/packages/@vuepress/markdown/__tests__/hoist.spec.js
similarity index 86%
rename from test/markdown/hoist.spec.js
rename to packages/@vuepress/markdown/__tests__/hoist.spec.js
index b2c5281b27..ae89a6f452 100644
--- a/test/markdown/hoist.spec.js
+++ b/packages/@vuepress/markdown/__tests__/hoist.spec.js
@@ -1,6 +1,6 @@
import { Md, getFragment } from './util'
-import hoist from '@/markdown/hoist.js'
-import { dataReturnable } from '@/markdown/index.js'
+import hoist from '../lib/hoist.js'
+import { dataReturnable } from '../lib/index.js'
const md = Md().set({ html: true })
const mdH = Md().set({ html: true }).use(hoist)
diff --git a/test/markdown/lineNumers.spec.js b/packages/@vuepress/markdown/__tests__/lineNumers.spec.js
similarity index 81%
rename from test/markdown/lineNumers.spec.js
rename to packages/@vuepress/markdown/__tests__/lineNumers.spec.js
index f7efafb85f..0f3f013525 100644
--- a/test/markdown/lineNumers.spec.js
+++ b/packages/@vuepress/markdown/__tests__/lineNumers.spec.js
@@ -1,7 +1,7 @@
import { Md, getFragment } from './util'
-import preWrapper from '@/markdown/preWrapper.js'
-import lineNumbers from '@/markdown/lineNumbers.js'
-import highlightLines from '@/markdown/highlightLines.js'
+import preWrapper from '../lib/preWrapper.js'
+import lineNumbers from '../lib/lineNumbers.js'
+import highlightLines from '../lib/highlightLines.js'
// lineNumbers must be chained after preWrapper.
// since lineNumbers needs to add extra stateful class to its block wrapper.
diff --git a/test/markdown/link.spec.js b/packages/@vuepress/markdown/__tests__/link.spec.js
similarity index 94%
rename from test/markdown/link.spec.js
rename to packages/@vuepress/markdown/__tests__/link.spec.js
index 620c9fa488..87f56f1029 100644
--- a/test/markdown/link.spec.js
+++ b/packages/@vuepress/markdown/__tests__/link.spec.js
@@ -1,6 +1,6 @@
import { Md } from './util'
-import link from '@/markdown/link.js'
-import { dataReturnable } from '@/markdown/index.js'
+import link from '../lib/link.js'
+import { dataReturnable } from '../lib/index.js'
const mdL = Md().use(link, {
target: '_blank',
diff --git a/test/markdown/slugify.spec.js b/packages/@vuepress/markdown/__tests__/slugify.spec.js
similarity index 93%
rename from test/markdown/slugify.spec.js
rename to packages/@vuepress/markdown/__tests__/slugify.spec.js
index 7782eaeea0..2c6a77b6c5 100644
--- a/test/markdown/slugify.spec.js
+++ b/packages/@vuepress/markdown/__tests__/slugify.spec.js
@@ -1,6 +1,6 @@
import { Md } from './util'
import anchor from 'markdown-it-anchor'
-import slugify from '@/markdown/slugify.js'
+import slugify from '../lib/slugify.js'
const mdS = Md().use(anchor, {
slugify,
diff --git a/test/markdown/snippet.spec.js b/packages/@vuepress/markdown/__tests__/snippet.spec.js
similarity index 88%
rename from test/markdown/snippet.spec.js
rename to packages/@vuepress/markdown/__tests__/snippet.spec.js
index 0131132916..5e05b481ed 100644
--- a/test/markdown/snippet.spec.js
+++ b/packages/@vuepress/markdown/__tests__/snippet.spec.js
@@ -1,6 +1,6 @@
import { Md, getFragment } from './util'
-import snippet from '@/markdown/snippet.js'
-import highlightLines from '@/markdown/highlightLines.js'
+import snippet from '../lib/snippet.js'
+import highlightLines from '../lib/highlightLines.js'
const md = Md().use(snippet)
const mdH = Md().use(snippet).use(highlightLines)
diff --git a/test/markdown/util.js b/packages/@vuepress/markdown/__tests__/util.js
similarity index 100%
rename from test/markdown/util.js
rename to packages/@vuepress/markdown/__tests__/util.js
diff --git a/lib/markdown/component.js b/packages/@vuepress/markdown/lib/component.js
similarity index 100%
rename from lib/markdown/component.js
rename to packages/@vuepress/markdown/lib/component.js
diff --git a/lib/markdown/containers.js b/packages/@vuepress/markdown/lib/containers.js
similarity index 100%
rename from lib/markdown/containers.js
rename to packages/@vuepress/markdown/lib/containers.js
diff --git a/lib/markdown/highlight.js b/packages/@vuepress/markdown/lib/highlight.js
similarity index 90%
rename from lib/markdown/highlight.js
rename to packages/@vuepress/markdown/lib/highlight.js
index 02d5e3ff55..dbb6437206 100644
--- a/lib/markdown/highlight.js
+++ b/packages/@vuepress/markdown/lib/highlight.js
@@ -1,8 +1,6 @@
-const chalk = require('chalk')
const prism = require('prismjs')
const loadLanguages = require('prismjs/components/index')
-const escapeHtml = require('escape-html')
-const logger = require('../util/logger')
+const { logger, chalk, escapeHtml } = require('@vuepress/shared-utils')
// required to make embedded highlighting work...
loadLanguages(['markup', 'css', 'javascript'])
diff --git a/lib/markdown/highlightLines.js b/packages/@vuepress/markdown/lib/highlightLines.js
similarity index 100%
rename from lib/markdown/highlightLines.js
rename to packages/@vuepress/markdown/lib/highlightLines.js
diff --git a/lib/markdown/hoist.js b/packages/@vuepress/markdown/lib/hoist.js
similarity index 100%
rename from lib/markdown/hoist.js
rename to packages/@vuepress/markdown/lib/hoist.js
diff --git a/packages/@vuepress/markdown/lib/index.js b/packages/@vuepress/markdown/lib/index.js
new file mode 100644
index 0000000000..728aacbe9c
--- /dev/null
+++ b/packages/@vuepress/markdown/lib/index.js
@@ -0,0 +1,131 @@
+'use strict'
+
+/**
+ * Module dependencies.
+ */
+
+const Config = require('markdown-it-chain')
+const highlight = require('./highlight')
+const highlightLinesPlugin = require('./highlightLines')
+const preWrapperPlugin = require('./preWrapper')
+const lineNumbersPlugin = require('./lineNumbers')
+const componentPlugin = require('./component')
+const hoistScriptStylePlugin = require('./hoist')
+const convertRouterLinkPlugin = require('./link')
+const containersPlugin = require('./containers')
+const snippetPlugin = require('./snippet')
+const emojiPlugin = require('markdown-it-emoji')
+const anchorPlugin = require('markdown-it-anchor')
+const tocPlugin = require('markdown-it-table-of-contents')
+const _slugify = require('./slugify')
+const { parseHeaders } = require('@vuepress/shared-utils')
+
+/**
+ * Create markdown by config.
+ */
+
+module.exports = ({
+ slugify,
+ externalLinks,
+ anchor,
+ toc,
+ lineNumbers,
+ beforeInstantiate,
+ afterInstantiate
+} = {}) => {
+ // allow user config slugify
+ slugify = slugify || _slugify
+
+ // using chainedAPI
+ const config = new Config()
+
+ config
+ .options
+ .html(true)
+ .highlight(highlight)
+ .end()
+
+ .plugin('component')
+ .use(componentPlugin)
+ .end()
+
+ .plugin('highlight-lines')
+ .use(highlightLinesPlugin)
+ .end()
+
+ .plugin('pre-wrapper')
+ .use(preWrapperPlugin)
+ .end()
+
+ .plugin('snippet')
+ .use(snippetPlugin)
+ .end()
+
+ .plugin('convert-router-link')
+ .use(convertRouterLinkPlugin, [Object.assign({
+ target: '_blank',
+ rel: 'noopener noreferrer'
+ }, externalLinks)])
+ .end()
+
+ .plugin('hoist-script-style')
+ .use(hoistScriptStylePlugin)
+ .end()
+
+ .plugin('containers')
+ .use(containersPlugin)
+ .end()
+
+ .plugin('emoji')
+ .use(emojiPlugin)
+ .end()
+
+ .plugin('anchor')
+ .use(anchorPlugin, [Object.assign({
+ slugify,
+ permalink: true,
+ permalinkBefore: true,
+ permalinkSymbol: '#'
+ }, anchor)])
+ .end()
+
+ .plugin('toc')
+ .use(tocPlugin, [Object.assign({
+ slugify,
+ includeLevel: [2, 3],
+ format: parseHeaders
+ }, toc)])
+ .end()
+
+ if (lineNumbers) {
+ config
+ .plugin('line-numbers')
+ .use(lineNumbersPlugin)
+ }
+
+ beforeInstantiate && beforeInstantiate(config)
+
+ const md = config.toMd()
+
+ afterInstantiate && afterInstantiate(md)
+
+ module.exports.dataReturnable(md)
+
+ // expose slugify
+ md.slugify = slugify
+
+ return md
+}
+
+module.exports.dataReturnable = function dataReturnable (md) {
+ // override render to allow custom plugins return data
+ const render = md.render
+ md.render = (...args) => {
+ md.__data = {}
+ const html = render.call(md, ...args)
+ return {
+ html,
+ data: md.__data
+ }
+ }
+}
diff --git a/lib/markdown/lineNumbers.js b/packages/@vuepress/markdown/lib/lineNumbers.js
similarity index 100%
rename from lib/markdown/lineNumbers.js
rename to packages/@vuepress/markdown/lib/lineNumbers.js
diff --git a/lib/markdown/link.js b/packages/@vuepress/markdown/lib/link.js
similarity index 100%
rename from lib/markdown/link.js
rename to packages/@vuepress/markdown/lib/link.js
diff --git a/lib/markdown/preWrapper.js b/packages/@vuepress/markdown/lib/preWrapper.js
similarity index 100%
rename from lib/markdown/preWrapper.js
rename to packages/@vuepress/markdown/lib/preWrapper.js
diff --git a/lib/markdown/slugify.js b/packages/@vuepress/markdown/lib/slugify.js
similarity index 100%
rename from lib/markdown/slugify.js
rename to packages/@vuepress/markdown/lib/slugify.js
diff --git a/lib/markdown/snippet.js b/packages/@vuepress/markdown/lib/snippet.js
similarity index 96%
rename from lib/markdown/snippet.js
rename to packages/@vuepress/markdown/lib/snippet.js
index a677e12f77..834ff496a1 100644
--- a/lib/markdown/snippet.js
+++ b/packages/@vuepress/markdown/lib/snippet.js
@@ -1,4 +1,4 @@
-const fs = require('fs')
+const { fs } = require('@vuepress/shared-utils')
module.exports = function snippet (md, options = {}) {
const root = options.root || process.cwd()
diff --git a/packages/@vuepress/markdown/package.json b/packages/@vuepress/markdown/package.json
new file mode 100644
index 0000000000..5e9ff0d41c
--- /dev/null
+++ b/packages/@vuepress/markdown/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "@vuepress/markdown",
+ "version": "1.0.0",
+ "description": "markdown for vuepress",
+ "main": "lib/index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator",
+ "markdown"
+ ],
+ "dependencies": {
+ "@vuepress/shared-utils": "^1.0.0",
+ "markdown-it": "^8.4.1",
+ "markdown-it-anchor": "^5.0.2",
+ "markdown-it-container": "^2.0.0",
+ "markdown-it-emoji": "^1.4.0",
+ "markdown-it-table-of-contents": "^0.4.0",
+ "markdown-it-chain": "^1.2.0",
+ "prismjs": "^1.13.0",
+ "diacritics": "^1.3.0"
+ },
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/markdown#readme"
+}
diff --git a/packages/@vuepress/plugin-active-header-links/.npmignore b/packages/@vuepress/plugin-active-header-links/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/plugin-active-header-links/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-active-header-links/README.md b/packages/@vuepress/plugin-active-header-links/README.md
new file mode 100644
index 0000000000..5063050105
--- /dev/null
+++ b/packages/@vuepress/plugin-active-header-links/README.md
@@ -0,0 +1,3 @@
+# @vuepress/plugin-active-header-links
+
+> active-header-links plugin for vuepress
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-active-header-links/index.js b/packages/@vuepress/plugin-active-header-links/index.js
new file mode 100644
index 0000000000..7831662368
--- /dev/null
+++ b/packages/@vuepress/plugin-active-header-links/index.js
@@ -0,0 +1,5 @@
+const path = require('path')
+
+module.exports = {
+ clientRootMixin: path.resolve(__dirname, 'mixin.js')
+}
diff --git a/lib/app/root-mixins/activeHeaderLinks.js b/packages/@vuepress/plugin-active-header-links/mixin.js
similarity index 75%
rename from lib/app/root-mixins/activeHeaderLinks.js
rename to packages/@vuepress/plugin-active-header-links/mixin.js
index 6cad69d5cc..4692f76afe 100644
--- a/lib/app/root-mixins/activeHeaderLinks.js
+++ b/packages/@vuepress/plugin-active-header-links/mixin.js
@@ -1,18 +1,20 @@
-import store from '@app/store'
+import Vue from 'vue'
import throttle from 'lodash.throttle'
export default {
mounted () {
window.addEventListener('scroll', this.onScroll)
},
+
methods: {
onScroll: throttle(function () {
this.setActiveHash()
}, 300),
+
setActiveHash () {
const sidebarLinks = [].slice.call(document.querySelectorAll('.sidebar-link'))
const anchors = [].slice.call(document.querySelectorAll('.header-anchor'))
- .filter(anchor => sidebarLinks.some(sidebarLink => sidebarLink.hash === anchor.hash))
+ .filter(anchor => sidebarLinks.some(sidebarLink => sidebarLink.hash === anchor.hash))
const scrollTop = Math.max(
window.pageYOffset,
@@ -25,15 +27,15 @@ export default {
const nextAnchor = anchors[i + 1]
const isActive = i === 0 && scrollTop === 0 ||
- (scrollTop >= anchor.parentElement.offsetTop + 10 &&
- (!nextAnchor || scrollTop < nextAnchor.parentElement.offsetTop - 10))
+ (scrollTop >= anchor.parentElement.offsetTop + 10 &&
+ (!nextAnchor || scrollTop < nextAnchor.parentElement.offsetTop - 10))
if (isActive && decodeURIComponent(this.$route.hash) !== decodeURIComponent(anchor.hash)) {
- store.disableScrollBehavior = true
+ Vue.$store.set('disableScrollBehavior', true)
this.$router.replace(decodeURIComponent(anchor.hash), () => {
// execute after scrollBehavior handler.
this.$nextTick(() => {
- store.disableScrollBehavior = false
+ Vue.$store.set('disableScrollBehavior', false)
})
})
return
@@ -41,6 +43,7 @@ export default {
}
}
},
+
beforeDestroy () {
window.removeEventListener('scroll', this.onScroll)
}
diff --git a/packages/@vuepress/plugin-active-header-links/package.json b/packages/@vuepress/plugin-active-header-links/package.json
new file mode 100644
index 0000000000..e3f613584a
--- /dev/null
+++ b/packages/@vuepress/plugin-active-header-links/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "@vuepress/plugin-active-header-links",
+ "version": "1.0.0",
+ "description": "active-header-links plugin for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "dependencies": {
+ "lodash.throttle": "^4.1.1"
+ },
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/plugin-active-header-links#readme"
+}
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-back-to-top/.npmignore b/packages/@vuepress/plugin-back-to-top/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/plugin-back-to-top/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-back-to-top/BackToTop.vue b/packages/@vuepress/plugin-back-to-top/BackToTop.vue
new file mode 100644
index 0000000000..35877254a0
--- /dev/null
+++ b/packages/@vuepress/plugin-back-to-top/BackToTop.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/@vuepress/plugin-back-to-top/README.md b/packages/@vuepress/plugin-back-to-top/README.md
new file mode 100644
index 0000000000..9e8260ae87
--- /dev/null
+++ b/packages/@vuepress/plugin-back-to-top/README.md
@@ -0,0 +1,15 @@
+# @vuepress/plugin-back-to-top
+
+> back-to-top plugin for vuepress
+
+## Usage
+
+```javascript
+module.exports = {
+ plugins: ['back-to-top']
+}
+```
+
+## Options
+
+No options for now.
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-back-to-top/client.js b/packages/@vuepress/plugin-back-to-top/client.js
new file mode 100644
index 0000000000..2770de013f
--- /dev/null
+++ b/packages/@vuepress/plugin-back-to-top/client.js
@@ -0,0 +1,5 @@
+import BackToTop from './BackToTop.vue'
+
+export default ({ Vue }) => {
+ Vue.component('BackToTop', BackToTop)
+}
diff --git a/packages/@vuepress/plugin-back-to-top/index.js b/packages/@vuepress/plugin-back-to-top/index.js
new file mode 100644
index 0000000000..ecfc0b64cf
--- /dev/null
+++ b/packages/@vuepress/plugin-back-to-top/index.js
@@ -0,0 +1,9 @@
+const path = require('path')
+
+module.exports = (options, context) => ({
+ enhanceAppFiles: [
+ path.resolve(__dirname, 'client.js')
+ ],
+
+ globalUIComponents: 'BackToTop'
+})
diff --git a/packages/@vuepress/plugin-back-to-top/package.json b/packages/@vuepress/plugin-back-to-top/package.json
new file mode 100644
index 0000000000..a402612ed1
--- /dev/null
+++ b/packages/@vuepress/plugin-back-to-top/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "@vuepress/plugin-back-to-top",
+ "version": "1.0.0",
+ "description": "back-to-top plugin for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/back-to-top#readme",
+ "dependencies": {
+ "lodash.debounce": "^4.0.8"
+ }
+}
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-blog/.npmignore b/packages/@vuepress/plugin-blog/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/plugin-blog/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-blog/README.md b/packages/@vuepress/plugin-blog/README.md
new file mode 100644
index 0000000000..ed7755e68a
--- /dev/null
+++ b/packages/@vuepress/plugin-blog/README.md
@@ -0,0 +1,3 @@
+# @vuepress/plugin-blog
+
+> theme plugin for vuepress
diff --git a/packages/@vuepress/plugin-blog/clientPlugin.js b/packages/@vuepress/plugin-blog/clientPlugin.js
new file mode 100644
index 0000000000..98b88de794
--- /dev/null
+++ b/packages/@vuepress/plugin-blog/clientPlugin.js
@@ -0,0 +1,63 @@
+import { findPageByKey } from '@app/util'
+import tagMeta from '@dynamic/tag'
+import categoryMeta from '@dynamic/category'
+
+class Classifiable {
+ constructor (metaMap, pages) {
+ this._metaMap = Object.assign({}, metaMap)
+ Object.keys(this._metaMap).forEach(name => {
+ const { pageKeys } = this._metaMap[name]
+ this._metaMap[name].posts = pageKeys.map(key => findPageByKey(pages, key))
+ })
+ }
+
+ get length () {
+ return Object.keys(this._metaMap).length
+ }
+
+ get map () {
+ return this._metaMap
+ }
+
+ get list () {
+ return this.toArray()
+ }
+
+ toArray () {
+ const tags = []
+ Object.keys(this._metaMap).forEach(name => {
+ const { posts, path } = this._metaMap[name]
+ tags.push({ name, posts, path })
+ })
+ return tags
+ }
+
+ getItemByName (name) {
+ return this._metaMap[name]
+ }
+}
+
+export default ({ Vue }) => {
+ Vue.mixin({
+ computed: {
+ $tags () {
+ const { pages } = this.$site
+ const tags = new Classifiable(tagMeta, pages)
+ return tags
+ },
+ $tag () {
+ const tagName = this.$route.meta.tagName
+ return this.$tags.getItemByName(tagName)
+ },
+ $categories () {
+ const { pages } = this.$site
+ const categories = new Classifiable(categoryMeta, pages)
+ return categories
+ },
+ $category () {
+ const categoryName = this.$route.meta.categoryName
+ return this.$categories.getItemByName(categoryName)
+ }
+ }
+ })
+}
diff --git a/packages/@vuepress/plugin-blog/index.js b/packages/@vuepress/plugin-blog/index.js
new file mode 100644
index 0000000000..d09ddc6d2b
--- /dev/null
+++ b/packages/@vuepress/plugin-blog/index.js
@@ -0,0 +1,163 @@
+const path = require('path')
+const { datatypes: { isString }} = require('@vuepress/shared-utils')
+
+module.exports = (options, ctx) => {
+ const { layoutComponentMap } = ctx
+ const {
+ pageEnhancers = [],
+ categoryIndexPageUrl = '/category/',
+ tagIndexPageUrl = '/tag/'
+ } = options
+
+ const isLayoutExists = name => layoutComponentMap[name] !== undefined
+ const getLayout = (name, fallback) => isLayoutExists(name) ? name : fallback
+ const isDirectChild = regularPath => path.parse(regularPath).dir === '/'
+
+ const enhancers = [
+ {
+ when: ({ regularPath }) => isDirectChild(regularPath),
+ frontmatter: { layout: getLayout('Page', 'Layout') },
+ data: { type: 'page' }
+ },
+ {
+ when: ({ regularPath }) => regularPath.startsWith('/category/'),
+ frontmatter: { layout: getLayout('Category', 'Page') }
+ },
+ {
+ when: ({ regularPath }) => regularPath === categoryIndexPageUrl,
+ frontmatter: { layout: getLayout('Categories', 'Page') }
+ },
+ {
+ when: ({ regularPath }) => regularPath.startsWith('/tag/'),
+ frontmatter: { layout: getLayout('Tag', 'Page') }
+ },
+ {
+ when: ({ regularPath }) => regularPath === tagIndexPageUrl,
+ frontmatter: { layout: getLayout('Tags', 'Page') }
+ },
+ {
+ when: ({ regularPath }) => regularPath === '/',
+ frontmatter: { layout: getLayout('Layout') }
+ },
+ {
+ when: ({ regularPath }) => regularPath.startsWith('/_posts/'),
+ frontmatter: {
+ layout: getLayout('Post', 'Page'),
+ permalink: '/:year/:month/:day/:slug'
+ },
+ data: { type: 'post' }
+ },
+ ...pageEnhancers
+ ]
+
+ return {
+ /**
+ * Modify page's metadata according to the habits of blog users.
+ */
+ extendPageData (pageCtx) {
+ const { frontmatter: rawFrontmatter } = pageCtx
+
+ enhancers.forEach(({
+ when,
+ data = {},
+ frontmatter = {}
+ }) => {
+ if (when(pageCtx)) {
+ Object.assign(rawFrontmatter, frontmatter)
+ Object.assign(pageCtx, data)
+ }
+ })
+ },
+
+ /**
+ * Create tag page and category page.
+ */
+ ready () {
+ const { pages } = ctx
+ const tagMap = {}
+ const categoryMap = {}
+
+ const curryHandler = (scope, map) => (key, pageKey) => {
+ if (key) {
+ if (!map[key]) {
+ map[key] = {}
+ map[key].path = `/${scope}/${key}.html`
+ map[key].pageKeys = []
+ }
+ map[key].pageKeys.push(pageKey)
+ }
+ }
+
+ const handleTag = curryHandler('tag', tagMap)
+ const handleCategory = curryHandler('category', categoryMap)
+
+ pages.forEach(({
+ key,
+ frontmatter: {
+ tag,
+ tags,
+ category,
+ categories
+ }
+ }) => {
+ if (isString(tag)) {
+ handleTag(tag, key)
+ }
+ if (Array.isArray(tags)) {
+ tags.forEach(tag => handleTag(tag, key))
+ }
+ if (isString(category)) {
+ handleCategory(categories, key)
+ }
+ if (Array.isArray(categories)) {
+ categories.forEach(category => handleCategory(category, key))
+ }
+ })
+
+ ctx.tagMap = tagMap
+ ctx.categoryMap = categoryMap
+
+ const extraPages = [
+ {
+ permalink: tagIndexPageUrl,
+ frontmatter: { title: `Tags` }
+ },
+ {
+ permalink: categoryIndexPageUrl,
+ frontmatter: { title: `Categories` }
+ },
+ ...Object.keys(tagMap).map(tagName => ({
+ permalink: tagMap[tagName].path,
+ meta: { tagName },
+ frontmatter: { title: `${tagName} | Tag` }
+ })),
+ ...Object.keys(categoryMap).map(categoryName => ({
+ permalink: categoryMap[categoryName].path,
+ meta: { categoryName },
+ frontmatter: { title: `${categoryName} | Category` }
+ }))
+ ]
+ extraPages.forEach(page => ctx.addPage(page))
+ },
+
+ /**
+ * Generate tag and category metadata.
+ */
+ async clientDynamicModules () {
+ return [
+ {
+ name: 'tag.js',
+ content: `export default ${JSON.stringify(ctx.tagMap, null, 2)}`
+ },
+ {
+ name: 'category.js',
+ content: `export default ${JSON.stringify(ctx.categoryMap, null, 2)}`
+ }
+ ]
+ },
+
+ enhanceAppFiles: [
+ path.resolve(__dirname, 'clientPlugin.js')
+ ]
+ }
+}
diff --git a/packages/@vuepress/plugin-blog/package.json b/packages/@vuepress/plugin-blog/package.json
new file mode 100644
index 0000000000..503103e0a0
--- /dev/null
+++ b/packages/@vuepress/plugin-blog/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@vuepress/plugin-blog",
+ "version": "1.0.0",
+ "description": "blog plugin for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/plugin-blog#readme"
+}
diff --git a/packages/@vuepress/plugin-google-analytics/.npmignore b/packages/@vuepress/plugin-google-analytics/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/plugin-google-analytics/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-google-analytics/README.md b/packages/@vuepress/plugin-google-analytics/README.md
new file mode 100644
index 0000000000..be05a08e8b
--- /dev/null
+++ b/packages/@vuepress/plugin-google-analytics/README.md
@@ -0,0 +1,12 @@
+# @vuepress/plugin-google-analytics
+
+> google-analytics plugin for vuepress
+
+## Options
+
+### ga
+
+- Type: `string`
+- Default: `/i18n/`
+
+Provide the Google Analytics ID to enable integration.
diff --git a/packages/@vuepress/plugin-google-analytics/index.js b/packages/@vuepress/plugin-google-analytics/index.js
new file mode 100644
index 0000000000..cb94b78ed8
--- /dev/null
+++ b/packages/@vuepress/plugin-google-analytics/index.js
@@ -0,0 +1,14 @@
+const path = require('path')
+
+module.exports = (options = {}, context) => ({
+ define () {
+ const { siteConfig = {}} = context
+ const ga = options.ga || siteConfig.ga
+ const GA_ID = ga || false
+ return { GA_ID }
+ },
+
+ enhanceAppFiles: [
+ path.resolve(__dirname, 'inject.js')
+ ]
+})
diff --git a/packages/@vuepress/plugin-google-analytics/inject.js b/packages/@vuepress/plugin-google-analytics/inject.js
new file mode 100644
index 0000000000..a2e1be9de4
--- /dev/null
+++ b/packages/@vuepress/plugin-google-analytics/inject.js
@@ -0,0 +1,28 @@
+/* global GA_ID, ga */
+
+export default ({ router }) => {
+// Google analytics integration
+ if (process.env.NODE_ENV === 'production' && GA_ID) {
+ (function (i, s, o, g, r, a, m) {
+ i['GoogleAnalyticsObject'] = r
+ i[r] = i[r] || function () {
+ (i[r].q = i[r].q || []).push(arguments)
+ }
+ i[r].l = 1 * new Date()
+ a = s.createElement(o)
+ m = s.getElementsByTagName(o)[0]
+ a.async = 1
+ a.src = g
+ m.parentNode.insertBefore(a, m)
+ })(window, document, 'script', 'https://www.google-analytics.com/analytics.js', 'ga')
+
+ ga('create', GA_ID, 'auto')
+ ga('send', 'pageview')
+
+ router.afterEach(function (to) {
+ ga('set', 'page', to.fullPath)
+ ga('send', 'pageview')
+ })
+ }
+}
+
diff --git a/packages/@vuepress/plugin-google-analytics/package.json b/packages/@vuepress/plugin-google-analytics/package.json
new file mode 100644
index 0000000000..5763de58f3
--- /dev/null
+++ b/packages/@vuepress/plugin-google-analytics/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@vuepress/plugin-google-analytics",
+ "version": "1.0.0",
+ "description": "google-analytics plugin for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/plugin-google-analytics#readme"
+}
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-i18n-ui/.npmignore b/packages/@vuepress/plugin-i18n-ui/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/plugin-i18n-ui/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-i18n-ui/README.md b/packages/@vuepress/plugin-i18n-ui/README.md
new file mode 100644
index 0000000000..f748c745b0
--- /dev/null
+++ b/packages/@vuepress/plugin-i18n-ui/README.md
@@ -0,0 +1,12 @@
+# @vuepress/plugin-i18n-ui
+
+> i18n-ui plugin for vuepress
+
+## Plugin Options
+
+### route
+
+- Type: `string`
+- Default: `/i18n/`
+
+Path to the i18n ui page.
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-i18n-ui/client.js b/packages/@vuepress/plugin-i18n-ui/client.js
new file mode 100644
index 0000000000..41651afa7a
--- /dev/null
+++ b/packages/@vuepress/plugin-i18n-ui/client.js
@@ -0,0 +1,7 @@
+import Layout from './index.vue'
+// TODO move default theme styl to core.
+import '@theme/styles/theme.styl'
+
+export default ({ Vue }) => {
+ Vue.component('I18nUILayout', Layout)
+}
diff --git a/packages/@vuepress/plugin-i18n-ui/index.js b/packages/@vuepress/plugin-i18n-ui/index.js
new file mode 100644
index 0000000000..2639f197db
--- /dev/null
+++ b/packages/@vuepress/plugin-i18n-ui/index.js
@@ -0,0 +1,21 @@
+const path = require('path')
+
+module.exports = (pluginOptions = {}, context) => ({
+ name: 'i18n-ui',
+
+ // This plugin will be enabled only at development mode.
+ enabled: !context.isProd,
+
+ enhanceAppFiles: [
+ path.resolve(__dirname, 'client.js')
+ ],
+
+ additionalPages: [
+ {
+ permalink: pluginOptions.permalink || '/i18n/',
+ frontmatter: {
+ 'layout': 'I18nUILayout'
+ }
+ }
+ ]
+})
diff --git a/packages/@vuepress/plugin-i18n-ui/index.vue b/packages/@vuepress/plugin-i18n-ui/index.vue
new file mode 100644
index 0000000000..185c00e8b2
--- /dev/null
+++ b/packages/@vuepress/plugin-i18n-ui/index.vue
@@ -0,0 +1,154 @@
+
+
+
+ Language:
+
+
+ {{ locale.label + '(' + locale.url + ')'}}
+
+
+ Page:
+
+ {{ page.path }}
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/@vuepress/plugin-i18n-ui/package.json b/packages/@vuepress/plugin-i18n-ui/package.json
new file mode 100644
index 0000000000..ed3407529b
--- /dev/null
+++ b/packages/@vuepress/plugin-i18n-ui/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@vuepress/plugin-i18n-ui",
+ "version": "1.0.0",
+ "description": "i18n-ui plugin for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/plugin-i18n-ui#readme"
+}
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-last-updated/.npmignore b/packages/@vuepress/plugin-last-updated/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/plugin-last-updated/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-last-updated/README.md b/packages/@vuepress/plugin-last-updated/README.md
new file mode 100644
index 0000000000..e05d30bf6f
--- /dev/null
+++ b/packages/@vuepress/plugin-last-updated/README.md
@@ -0,0 +1,27 @@
+# @vuepress/plugin-last-updated
+
+> last-updated plugin for vuepress
+
+> Note that this plugin has been included in the core.
+
+## Options
+
+### transformer
+
+- Type: `function`
+- Default: `undefined`
+
+By default, this plugin produces a 13-bit timestamp for each page, you can also pass in a transformer to convert it to any format that you want.
+
+``` javascript
+const timeago = require("timeago.js");
+
+module.exports = {
+ plugins: [
+ [
+ 'last-updated',
+ { transformer: timeago.format }
+ ]
+ ]
+}
+```
diff --git a/packages/@vuepress/plugin-last-updated/index.js b/packages/@vuepress/plugin-last-updated/index.js
new file mode 100644
index 0000000000..2324244f30
--- /dev/null
+++ b/packages/@vuepress/plugin-last-updated/index.js
@@ -0,0 +1,14 @@
+const spawn = require('cross-spawn')
+
+module.exports = (options = {}, context) => ({
+ extendPageData ({ _filePath }) {
+ const { transformer } = options
+ const timestamp = getGitLastUpdatedTimeStamp(_filePath)
+ const lastUpdated = typeof transformer === 'function' ? transformer(timestamp) : timestamp
+ return { lastUpdated }
+ }
+})
+
+function getGitLastUpdatedTimeStamp (filePath) {
+ return parseInt(spawn.sync('git', ['log', '-1', '--format=%ct', filePath]).stdout.toString('utf-8')) * 1000
+}
diff --git a/packages/@vuepress/plugin-last-updated/package.json b/packages/@vuepress/plugin-last-updated/package.json
new file mode 100644
index 0000000000..ff57f71051
--- /dev/null
+++ b/packages/@vuepress/plugin-last-updated/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "@vuepress/plugin-last-updated",
+ "version": "1.0.0",
+ "description": "last-updated plugin for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/plugin-last-updated#readme",
+ "dependencies": {
+ "cross-spawn": "^6.0.5"
+ }
+}
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-medium-zoom/.npmignore b/packages/@vuepress/plugin-medium-zoom/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/plugin-medium-zoom/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-medium-zoom/README.md b/packages/@vuepress/plugin-medium-zoom/README.md
new file mode 100644
index 0000000000..89e7bf7a23
--- /dev/null
+++ b/packages/@vuepress/plugin-medium-zoom/README.md
@@ -0,0 +1,3 @@
+# @vuepress/plugin-medium-zoom
+
+> medium-zoom plugin for vuepress
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-medium-zoom/index.js b/packages/@vuepress/plugin-medium-zoom/index.js
new file mode 100644
index 0000000000..7831662368
--- /dev/null
+++ b/packages/@vuepress/plugin-medium-zoom/index.js
@@ -0,0 +1,5 @@
+const path = require('path')
+
+module.exports = {
+ clientRootMixin: path.resolve(__dirname, 'mixin.js')
+}
diff --git a/packages/@vuepress/plugin-medium-zoom/mixin.js b/packages/@vuepress/plugin-medium-zoom/mixin.js
new file mode 100644
index 0000000000..24369a681c
--- /dev/null
+++ b/packages/@vuepress/plugin-medium-zoom/mixin.js
@@ -0,0 +1,10 @@
+import './style.css'
+import zoom from 'medium-zoom'
+
+export default {
+ mounted () {
+ setTimeout(() => {
+ zoom('.content img')
+ }, 1000)
+ }
+}
diff --git a/packages/@vuepress/plugin-medium-zoom/package.json b/packages/@vuepress/plugin-medium-zoom/package.json
new file mode 100644
index 0000000000..2c28d99bf8
--- /dev/null
+++ b/packages/@vuepress/plugin-medium-zoom/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "@vuepress/plugin-medium-zoom",
+ "version": "1.0.0",
+ "description": "medium-zoom plugin for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "dependencies": {
+ "medium-zoom": "^0.4.0"
+ },
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/plugin-medium-zoom#readme"
+}
diff --git a/packages/@vuepress/plugin-medium-zoom/style.css b/packages/@vuepress/plugin-medium-zoom/style.css
new file mode 100644
index 0000000000..50437531a8
--- /dev/null
+++ b/packages/@vuepress/plugin-medium-zoom/style.css
@@ -0,0 +1,7 @@
+.medium-zoom-overlay {
+ z-index: 100;
+}
+
+.medium-zoom-overlay ~ img {
+ z-index: 101;
+}
diff --git a/packages/@vuepress/plugin-pagination/.npmignore b/packages/@vuepress/plugin-pagination/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/plugin-pagination/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-pagination/README.md b/packages/@vuepress/plugin-pagination/README.md
new file mode 100644
index 0000000000..a4eabe695e
--- /dev/null
+++ b/packages/@vuepress/plugin-pagination/README.md
@@ -0,0 +1,4 @@
+# @vuepress/plugin-pagination
+
+> pagination plugin for vuepress
+
diff --git a/packages/@vuepress/plugin-pagination/clientPlugin.js b/packages/@vuepress/plugin-pagination/clientPlugin.js
new file mode 100644
index 0000000000..f61fcbf7b0
--- /dev/null
+++ b/packages/@vuepress/plugin-pagination/clientPlugin.js
@@ -0,0 +1,71 @@
+import paginationMeta from '@dynamic/pagination'
+
+class Pagination {
+ constructor (pagination, { pages, route }) {
+ let { postsFilter, postsSorter } = pagination
+
+ /* eslint-disable no-eval */
+ postsFilter = eval(postsFilter)
+ postsSorter = eval(postsSorter)
+
+ const { path } = route
+ const { paginationPages } = pagination
+
+ paginationPages.forEach((page, index) => {
+ if (page.path === path) {
+ this.paginationIndex = index
+ }
+ })
+
+ if (!this.paginationIndex) {
+ this.paginationIndex = 0
+ }
+
+ this._paginationPages = paginationPages
+ this._currentPage = paginationPages[this.paginationIndex]
+ this._posts = pages.filter(postsFilter).sort(postsSorter)
+ }
+
+ get length () {
+ return this._paginationPages.length
+ }
+
+ get posts () {
+ const [start, end] = this._currentPage.interval
+ return this._posts.slice(start, end)
+ }
+
+ get hasPrev () {
+ return this.paginationIndex !== 0
+ }
+
+ get prevLink () {
+ if (this.hasPrev) {
+ return this._paginationPages[this.paginationIndex - 1].path
+ }
+ }
+
+ get hasNext () {
+ return this.paginationIndex !== this.length - 1
+ }
+
+ get nextLink () {
+ if (this.hasNext) {
+ return this._paginationPages[this.paginationIndex + 1].path
+ }
+ }
+}
+
+export default ({ Vue }) => {
+ Vue.mixin({
+ computed: {
+ $pagination () {
+ const { pages } = this.$site
+ const pagination = new Pagination(paginationMeta, {
+ pages, route: this.$route
+ })
+ return pagination
+ }
+ }
+ })
+}
diff --git a/packages/@vuepress/plugin-pagination/index.js b/packages/@vuepress/plugin-pagination/index.js
new file mode 100644
index 0000000000..407ff4a904
--- /dev/null
+++ b/packages/@vuepress/plugin-pagination/index.js
@@ -0,0 +1,69 @@
+const path = require('path')
+
+function getIntervallers (max, interval) {
+ const count = Math.floor(max / interval)
+ const arr = [...Array(count + 1)]
+ return arr.map((v, index) => {
+ const start = index * interval
+ const end = (index + 1) * interval - 1
+ return [start, end > max ? max : end]
+ })
+}
+
+module.exports = (options, ctx) => ({
+ enhanceAppFiles: [
+ path.resolve(__dirname, 'clientPlugin.js')
+ ],
+
+ ready () {
+ let { postsFilter, postsSorter } = options
+ postsFilter = postsFilter || (({ type }) => type === 'post')
+ postsSorter = postsSorter || ((prev, next) => {
+ const prevTime = new Date(prev.frontmatter.date).getTime()
+ const nextTime = new Date(next.frontmatter.date).getTime()
+ return prevTime - nextTime > 0 ? -1 : 1
+ })
+
+ const { pages } = ctx
+ const posts = pages.filter(postsFilter)
+ const {
+ perPagePosts = 10,
+ paginationDir = 'page',
+ firstPagePath = '/',
+ layout = 'Layout'
+ } = options
+
+ const intervallers = getIntervallers(posts.length, perPagePosts)
+ const pagination = {
+ paginationPages: intervallers.map((interval, index) => {
+ const path = index === 0
+ ? firstPagePath
+ : `/${paginationDir}/${index + 1}/`
+ return { path, interval }
+ }),
+ postsFilter: postsFilter.toString(),
+ postsSorter: postsSorter.toString()
+ }
+
+ ctx.pagination = pagination
+ pagination.paginationPages.forEach(({ path }, index) => {
+ if (path === '/') {
+ return
+ }
+ ctx.addPage({
+ permalink: path,
+ frontmatter: {
+ layout,
+ title: `Page ${index + 1}`
+ }
+ })
+ })
+ },
+
+ async clientDynamicModules () {
+ return {
+ name: 'pagination.js',
+ content: `export default ${JSON.stringify(ctx.pagination, null, 2)}`
+ }
+ }
+})
diff --git a/packages/@vuepress/plugin-pagination/package.json b/packages/@vuepress/plugin-pagination/package.json
new file mode 100644
index 0000000000..926150129f
--- /dev/null
+++ b/packages/@vuepress/plugin-pagination/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@vuepress/plugin-pagination",
+ "version": "1.0.0",
+ "description": "pagination plugin for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/plugin-pagination#readme"
+}
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-pwa/.npmignore b/packages/@vuepress/plugin-pwa/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/plugin-pwa/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-pwa/README.md b/packages/@vuepress/plugin-pwa/README.md
new file mode 100644
index 0000000000..9939adfad4
--- /dev/null
+++ b/packages/@vuepress/plugin-pwa/README.md
@@ -0,0 +1,199 @@
+# @vuepress/plugin-pwa
+
+> PWA plugin for vuepress
+
+## Options
+
+### serviceWorker
+
+- Type: `boolean`
+- Default: `true`
+
+If set to `true`, VuePress will automatically generate and register a service worker that caches the content for offline use (only enabled in production).
+
+### updatePopup
+
+- Type: `boolean|popupConfig`
+- Default: `undefined`
+
+```typescript
+interface normalPopupConfig {
+ message: string; // defaults to 'New content is available.'
+ buttonText: string; // defaults to 'Refresh'
+}
+
+interface localedPopupConfig {
+ [localePath: string]: normalPopupConfig
+}
+
+type popupConfig = normalPopupConfig | localedPopupConfig
+```
+
+This option enables the popup to refresh contents. The popup will be shown when the site is updated (i.e. service worker is updated). It provides `refresh` button to allow users to refresh contents immediately.
+
+> If without the `refresh` button, the new service worker will be active after all [clients](https://developer.mozilla.org/en-US/docs/Web/API/Clients) are closed. This means that visitors cannot see new contents until they close all tabs of your site. But the `refresh` button activates the new service worker immediately.
+
+### popupComponent
+
+- Type: `string`
+- Default: `undefined`
+
+A custom component to replace the default popup component.
+
+**Also see:**
+
+- [Customize the SW-Update Popup UI](#customize-the-sw-update-popup-ui)
+
+## Migration from 0.x.x
+
+Now that we have plugin API, all features' options that are in plugin's areas will become plugin options.
+
+### Service Worker
+
+``` diff
+module.exports = {
+- serviceWorker: true,
++ plugins: ['@vuepress/pwa']
+}
+```
+
+### SW-Update Popup
+
+``` diff
+module.exports = {
+ themeConfig: {
+- serviceWorker: {
+- updatePopup: {
+- message: "New content is available.",
+- buttonText: "Refresh"
+- }
+- }
+ },
++ plugins: {
++ '@vuepress/pwa': {
++ serviceWorker: true,
++ updatePopup: {
++ message: "New content is available.",
++ buttonText: "Refresh"
++ }
++ }
++ }
+}
+```
+
+For i18n user:
+
+``` diff
+module.exports = {
+ themeConfig: {
+ '/': {
+- serviceWorker: {
+- updatePopup: {
+- message: "New content is available.",
+- buttonText: "Refresh"
+- }
+- }
+ },
+ '/zh/': {
+- serviceWorker: {
+- updatePopup: {
+- message: "发现新内容可用",
+- buttonText: "刷新"
+- }
+- }
+ }
+ },
++ plugins: {
++ '@vuepress/pwa': {
++ serviceWorker: true,
++ updatePopup: {
++ '/': {
++ message: "New content is available.",
++ buttonText: "Refresh"
++ },
++ '/zh/': {
++ message: "发现新内容可用",
++ buttonText: "刷新"
++ }
++ }
++ }
++ }
+```
+
+It's worth mentioning that the PWA plugin has above i18n built in, so if you want to use the default i18n directly, you can abbreviate the above configuration as:
+
+```js
+module.exports = {
+ plugins: {
+ '@vuepress/pwa': {
+ serviceWorker: true,
+ updatePopup: true
+ }
+ }
+}
+```
+
+Feel free to submit PRs to improve the default [i18n configuration](https://github.com/vuejs/vuepress/blob/next/packages/%40vuepress/plugin-pwa/lib/i18n.js).
+
+## Customize the SW-Update Popup UI
+
+The default sw-update popup component provides a default slot which gives you the ability to fully control the appearence of the popup.
+
+First, you need to create a global component (e.g. `MySWUpdatePopup`) at `.vuepress/components`. A simple component created based on the default component is as follows:
+
+```vue
+
+
+
+
+
+
+
+
+
+```
+
+Then, update your plugin:
+
+``` diff
+module.exports = {
+ plugins: {
+ '@vuepress/pwa': {
+ serviceWorker: true,
++ popupComponent: 'MySWUpdatePopup',
+ updatePopup: true
+ }
+ }
+}
+```
+
+**Also see:**
+
+- [VuePress > Using Components](https://vuepress.vuejs.org/guide/using-vue.html#using-components)
+- [Vue > Scoped Slots](https://vuejs.org/v2/guide/components-slots.html#Scoped-Slots)
diff --git a/packages/@vuepress/plugin-pwa/index.js b/packages/@vuepress/plugin-pwa/index.js
new file mode 100644
index 0000000000..a7d17140b7
--- /dev/null
+++ b/packages/@vuepress/plugin-pwa/index.js
@@ -0,0 +1,49 @@
+const path = require('path')
+const { logger, fs } = require('@vuepress/shared-utils')
+
+module.exports = (options, context) => ({
+ ready () {
+ options = Object.assign({
+ serviceWorker: true
+ }, options)
+ },
+
+ define () {
+ const { serviceWorker, updatePopup } = options
+ const base = context.base || '/'
+ return {
+ SW_BASE_URL: base,
+ SW_ENABLED: !!serviceWorker,
+ SW_UPDATE_POPUP: updatePopup || false
+ }
+ },
+
+ // TODO support components option
+ // components: [
+ // { name: 'SWUpdatePopup', path: path.resolve(__dirname, 'lib/SWUpdatePopup.vue') }
+ // ],
+
+ globalUIComponents: options.popupComponent || 'SWUpdatePopup',
+
+ enhanceAppFiles: [path.resolve(__dirname, 'lib/inject.js')],
+
+ async generated () {
+ const { serviceWorker } = options
+ const { outDir } = context
+ const swFilePath = path.resolve(outDir, 'service-worker.js')
+ if (serviceWorker) {
+ logger.wait('\nGenerating service worker...')
+ const wbb = require('workbox-build')
+ await wbb.generateSW({
+ swDest: swFilePath,
+ globDirectory: outDir,
+ globPatterns: ['**\/*.{js,css,html,png,jpg,jpeg,gif,svg,woff,woff2,eot,ttf,otf}']
+ })
+ await fs.writeFile(
+ swFilePath,
+ await fs.readFile(path.resolve(__dirname, 'lib/skip-waiting.js'), 'utf8'),
+ { flag: 'a' }
+ )
+ }
+ }
+})
diff --git a/lib/app/SWUpdateEvent.js b/packages/@vuepress/plugin-pwa/lib/SWUpdateEvent.js
similarity index 100%
rename from lib/app/SWUpdateEvent.js
rename to packages/@vuepress/plugin-pwa/lib/SWUpdateEvent.js
diff --git a/packages/@vuepress/plugin-pwa/lib/SWUpdatePopup.vue b/packages/@vuepress/plugin-pwa/lib/SWUpdatePopup.vue
new file mode 100644
index 0000000000..f7d04aa716
--- /dev/null
+++ b/packages/@vuepress/plugin-pwa/lib/SWUpdatePopup.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/@vuepress/plugin-pwa/lib/event.js b/packages/@vuepress/plugin-pwa/lib/event.js
new file mode 100644
index 0000000000..8853b4d295
--- /dev/null
+++ b/packages/@vuepress/plugin-pwa/lib/event.js
@@ -0,0 +1,3 @@
+import Vue from 'vue'
+
+export default new Vue()
diff --git a/packages/@vuepress/plugin-pwa/lib/i18n.js b/packages/@vuepress/plugin-pwa/lib/i18n.js
new file mode 100644
index 0000000000..adc33bbc8e
--- /dev/null
+++ b/packages/@vuepress/plugin-pwa/lib/i18n.js
@@ -0,0 +1,10 @@
+export const popupConfig = {
+ '/': {
+ message: 'New content is available.',
+ buttonText: 'Refresh'
+ },
+ '/zh/': {
+ message: '发现新内容可用',
+ buttonText: '刷新'
+ }
+}
diff --git a/packages/@vuepress/plugin-pwa/lib/inject.js b/packages/@vuepress/plugin-pwa/lib/inject.js
new file mode 100644
index 0000000000..44e0f98f0f
--- /dev/null
+++ b/packages/@vuepress/plugin-pwa/lib/inject.js
@@ -0,0 +1,52 @@
+/* global SW_BASE_URL, SW_ENABLED, GA_ID, ga, SW_UPDATE_POPUP */
+
+import Vue from 'vue'
+import { register } from 'register-service-worker'
+import SWUpdateEvent from './SWUpdateEvent'
+import event from './event'
+
+if (SW_UPDATE_POPUP) {
+ Vue.component('SWUpdatePopup', () => import('./SWUpdatePopup.vue'))
+}
+
+export default ({ router, isServer }) => {
+ // Register service worker
+ router.onReady(() => {
+ if (process.env.NODE_ENV === 'production' &&
+ !isServer &&
+ SW_ENABLED) {
+ register(`${SW_BASE_URL}service-worker.js`, {
+ ready () {
+ console.log('[vuepress:sw] Service worker is active.')
+ event.$emit('sw-ready')
+ },
+
+ cached (registration) {
+ console.log('[vuepress:sw] Content has been cached for offline use.')
+ event.$emit('sw-cached', new SWUpdateEvent(registration))
+ },
+
+ updated (registration) {
+ console.log('[vuepress:sw] Content updated.')
+ event.$emit('sw-updated', new SWUpdateEvent(registration))
+ },
+
+ offline () {
+ console.log('[vuepress:sw] No internet connection found. App is running in offline mode.')
+ event.$emit('sw-offline')
+ },
+
+ error (err) {
+ console.error('[vuepress:sw] Error during service worker registration:', err)
+ event.$emit('sw-error', err)
+ if (GA_ID) {
+ ga('send', 'exception', {
+ exDescription: err.message,
+ exFatal: false
+ })
+ }
+ }
+ })
+ }
+ })
+}
diff --git a/lib/service-worker/skip-waiting.js b/packages/@vuepress/plugin-pwa/lib/skip-waiting.js
similarity index 100%
rename from lib/service-worker/skip-waiting.js
rename to packages/@vuepress/plugin-pwa/lib/skip-waiting.js
diff --git a/packages/@vuepress/plugin-pwa/package.json b/packages/@vuepress/plugin-pwa/package.json
new file mode 100644
index 0000000000..c78734afad
--- /dev/null
+++ b/packages/@vuepress/plugin-pwa/package.json
@@ -0,0 +1,30 @@
+{
+ "name": "@vuepress/plugin-pwa",
+ "version": "1.0.0",
+ "description": "pwa plugin for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "dependencies": {
+ "register-service-worker": "^1.5.2",
+ "@vuepress/shared-utils": "^1.0.0",
+ "workbox-build": "^3.1.0"
+ },
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/plugin-pwa#readme"
+}
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-register-components/.npmignore b/packages/@vuepress/plugin-register-components/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/plugin-register-components/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-register-components/README.md b/packages/@vuepress/plugin-register-components/README.md
new file mode 100644
index 0000000000..a89d595a27
--- /dev/null
+++ b/packages/@vuepress/plugin-register-components/README.md
@@ -0,0 +1,54 @@
+# @vuepress/plugin-register-components
+
+> register-components plugin for vuepress
+
+## Plugin Options
+
+### componentsDir
+
+- Type: `Array | String`
+- Default: `[]`
+
+All components in this directory will be registered as global components, naming of components will follow the components found in [.vuepress/components](https://vuepress.vuejs.org/guide/using-vue.html#using-components).
+
+- Usage:
+
+``` js
+module.exports = {
+ plugins: [
+ [
+ 'register-components',
+ {
+ componentDir: somepath
+ }
+ ]
+ ]
+}
+```
+
+### components
+
+- Type: `{ name: string, path: string }`
+- Default: `[]`
+
+Register global components by explicit name and path.
+
+- Usage:
+
+``` js
+module.exports = {
+ plugins: [
+ [
+ 'register-components',
+ {
+ components: [
+ {
+ name: 'V-Card',
+ path: 'path/to/card.vue'
+ }
+ ]
+ }
+ ]
+ ]
+}
+```
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-register-components/index.js b/packages/@vuepress/plugin-register-components/index.js
new file mode 100644
index 0000000000..3496d86a4d
--- /dev/null
+++ b/packages/@vuepress/plugin-register-components/index.js
@@ -0,0 +1,60 @@
+const { fs, globby } = require('@vuepress/shared-utils')
+const path = require('path')
+
+function fileToComponentName (file) {
+ return file
+ .replace(/\/|\\/g, '-')
+ .replace(/\.vue$/, '')
+}
+
+async function resolveComponents (componentDir) {
+ if (!fs.existsSync(componentDir)) {
+ return
+ }
+ return (await globby(['**/*.vue'], { cwd: componentDir }))
+}
+
+// Since this plugin can ben used by multiple times, we need to
+// give each generated files a uid or the previous file would be
+// overwritten.
+let moduleId = 0
+
+module.exports = (options, context) => ({
+ multiple: true,
+
+ async enhanceAppFiles () {
+ const { componentsDir = [], components = [] } = options
+ const baseDirs = Array.isArray(componentsDir) ? componentsDir : [componentsDir]
+
+ function importCode (name, absolutePath) {
+ return `Vue.component(${JSON.stringify(name)}, () => import(${JSON.stringify(absolutePath)}))`
+ }
+
+ function genImport (baseDir, file) {
+ const name = fileToComponentName(file)
+ const absolutePath = path.resolve(baseDir, file)
+ const code = importCode(name, absolutePath)
+ return code
+ }
+
+ let code = ''
+
+ // 1. Register components in specified directories
+ for (const baseDir of baseDirs) {
+ const files = await resolveComponents(baseDir) || []
+ code += files.map(file => genImport(baseDir, file)).join('\n') + '\n'
+ }
+
+ // 2. Register named components.
+ code += components.map(({ name, path: absolutePath }) => importCode(name, absolutePath))
+
+ code = `import Vue from 'vue'\n` + code + '\n'
+
+ return [
+ {
+ name: `global-components-${++moduleId}.js`,
+ content: code
+ }
+ ]
+ }
+})
diff --git a/packages/@vuepress/plugin-register-components/package.json b/packages/@vuepress/plugin-register-components/package.json
new file mode 100644
index 0000000000..766a22a6f8
--- /dev/null
+++ b/packages/@vuepress/plugin-register-components/package.json
@@ -0,0 +1,28 @@
+{
+ "name": "@vuepress/plugin-register-components",
+ "version": "1.0.0",
+ "description": "register-global-components plugin for vuepress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/plugin-register-components#readme",
+ "dependencies": {
+ "@vuepress/shared-utils": "^1.0.0"
+ }
+}
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-search/.npmignore b/packages/@vuepress/plugin-search/.npmignore
new file mode 100644
index 0000000000..18f0a334a4
--- /dev/null
+++ b/packages/@vuepress/plugin-search/.npmignore
@@ -0,0 +1,2 @@
+__tests__
+__mocks__
\ No newline at end of file
diff --git a/packages/@vuepress/plugin-search/README.md b/packages/@vuepress/plugin-search/README.md
new file mode 100644
index 0000000000..98f77fcfb5
--- /dev/null
+++ b/packages/@vuepress/plugin-search/README.md
@@ -0,0 +1,42 @@
+# @vuepress/plugin-search
+
+> header-based search plugin for vuepress
+
+## Usage
+
+1. Enable this plugin:
+
+```js
+// .vuepress/config.js or themedir/index.js
+
+module.exports = {
+ plugins: [
+ ['@vuepress/search', {
+ searchMaxSuggestions: 10
+ }]
+ ],
+ // Tweak the default color via palette.
+ palette: {
+ $accentColor: '#b58900',
+ $textColor: '#586e75',
+ $borderColor: '#eaecef',
+ $codeBgColor: '#282c34',
+ $arrowBgColor: '#ccc'
+ }
+}
+```
+
+2. Using search component:
+
+```vue
+import SearchBox from '@SearchBox'
+```
+
+## Options
+
+### searchMaxSuggestions
+
+- Type: `number`
+- Default: `true`
+
+Set the maximum number of results for search
diff --git a/lib/default-theme/SearchBox.vue b/packages/@vuepress/plugin-search/SearchBox.vue
similarity index 96%
rename from lib/default-theme/SearchBox.vue
rename to packages/@vuepress/plugin-search/SearchBox.vue
index 9860855244..76016dd96a 100644
--- a/lib/default-theme/SearchBox.vue
+++ b/packages/@vuepress/plugin-search/SearchBox.vue
@@ -36,6 +36,7 @@
diff --git a/lib/default-theme/Layout.vue b/packages/@vuepress/theme-default/layouts/Layout.vue
similarity index 86%
rename from lib/default-theme/Layout.vue
rename to packages/@vuepress/theme-default/layouts/Layout.vue
index ee2e6abedb..0ab5760482 100644
--- a/lib/default-theme/Layout.vue
+++ b/packages/@vuepress/theme-default/layouts/Layout.vue
@@ -51,28 +51,24 @@
slot="bottom"
/>
-
-
-
+
diff --git a/lib/default-theme/util.js b/packages/@vuepress/theme-default/layouts/util.js
similarity index 86%
rename from lib/default-theme/util.js
rename to packages/@vuepress/theme-default/layouts/util.js
index ef95bea552..01937e9888 100644
--- a/lib/default-theme/util.js
+++ b/packages/@vuepress/theme-default/layouts/util.js
@@ -59,10 +59,10 @@ export function resolvePage (pages, rawPath, base) {
}
const path = normalize(rawPath)
for (let i = 0; i < pages.length; i++) {
- if (normalize(pages[i].path) === path) {
+ if (normalize(pages[i].regularPath) === path) {
return Object.assign({}, pages[i], {
type: 'page',
- path: ensureExt(rawPath)
+ path: ensureExt(pages[i].path)
})
}
}
@@ -108,7 +108,14 @@ function resolvePath (relative, base, append) {
return stack.join('/')
}
-export function resolveSidebarItems (page, route, site, localePath) {
+/**
+ * @param { Page } page
+ * @param { string } regularPath
+ * @param { SiteData } site
+ * @param { string } localePath
+ * @returns { SidebarGroup }
+ */
+export function resolveSidebarItems (page, regularPath, site, localePath) {
const { pages, themeConfig } = site
const localeConfig = localePath && themeConfig.locales
@@ -124,13 +131,17 @@ export function resolveSidebarItems (page, route, site, localePath) {
if (!sidebarConfig) {
return []
} else {
- const { base, config } = resolveMatchingConfig(route, sidebarConfig)
+ const { base, config } = resolveMatchingConfig(regularPath, sidebarConfig)
return config
? config.map(item => resolveItem(item, pages, base))
: []
}
}
+/**
+ * @param { Page } page
+ * @returns { SidebarGroup }
+ */
function resolveHeaders (page) {
const headers = groupHeaders(page.headers || [])
return [{
@@ -167,7 +178,12 @@ export function resolveNavLinkItem (linkItem) {
})
}
-export function resolveMatchingConfig (route, config) {
+/**
+ * @param { Route } route
+ * @param { Array | Array | [link: string]: SidebarConfig } config
+ * @returns { base: string, config: SidebarConfig }
+ */
+export function resolveMatchingConfig (regularPath, config) {
if (Array.isArray(config)) {
return {
base: '/',
@@ -175,7 +191,7 @@ export function resolveMatchingConfig (route, config) {
}
}
for (const base in config) {
- if (ensureEndingSlash(route.path).indexOf(base) === 0) {
+ if (ensureEndingSlash(regularPath).indexOf(base) === 0) {
return {
base,
config: config[base]
diff --git a/packages/@vuepress/theme-default/noopModule.js b/packages/@vuepress/theme-default/noopModule.js
new file mode 100644
index 0000000000..b1c6ea436a
--- /dev/null
+++ b/packages/@vuepress/theme-default/noopModule.js
@@ -0,0 +1 @@
+export default {}
diff --git a/packages/@vuepress/theme-default/package.json b/packages/@vuepress/theme-default/package.json
new file mode 100644
index 0000000000..f5676fa02d
--- /dev/null
+++ b/packages/@vuepress/theme-default/package.json
@@ -0,0 +1,33 @@
+{
+ "name": "@vuepress/theme-default",
+ "version": "1.0.0",
+ "description": "Default theme for VuePress",
+ "main": "index.js",
+ "publishConfig": {
+ "access": "public"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "vuepress",
+ "generator"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress/packages/@vuepress/theme-default#readme",
+ "dependencies": {
+ "@vuepress/plugin-active-header-links": "1.0.0",
+ "@vuepress/plugin-search": "1.0.0",
+ "docsearch.js": "^2.5.2",
+ "nprogress": "^0.2.0",
+ "stylus": "^0.54.5",
+ "stylus-loader": "^3.0.2"
+ }
+}
diff --git a/packages/@vuepress/theme-default/plugin.js b/packages/@vuepress/theme-default/plugin.js
new file mode 100644
index 0000000000..1aee3b5bbf
--- /dev/null
+++ b/packages/@vuepress/theme-default/plugin.js
@@ -0,0 +1,23 @@
+const path = require('path')
+
+module.exports = (options, context) => ({
+ name: 'default-theme',
+
+ chainWebpack (config, isServer) {
+ const { themeConfig, siteConfig } = context
+
+ // resolve algolia
+ const isAlgoliaSearch = (
+ themeConfig.algolia ||
+ Object.keys(siteConfig.locales && themeConfig.locales || {})
+ .some(base => themeConfig.locales[base].algolia)
+ )
+
+ config.resolve
+ .alias
+ .set('@default-theme', path.resolve('.'))
+ .set('@AlgoliaSearchBox', isAlgoliaSearch
+ ? path.resolve(__dirname, 'src/AlgoliaSearchBox.vue')
+ : path.resolve(__dirname, 'noopModule.js'))
+ }
+})
diff --git a/lib/default-theme/styles/arrow.styl b/packages/@vuepress/theme-default/styles/arrow.styl
similarity index 100%
rename from lib/default-theme/styles/arrow.styl
rename to packages/@vuepress/theme-default/styles/arrow.styl
diff --git a/lib/default-theme/styles/code.styl b/packages/@vuepress/theme-default/styles/code.styl
similarity index 98%
rename from lib/default-theme/styles/code.styl
rename to packages/@vuepress/theme-default/styles/code.styl
index 8383c6e3d7..f032225e5f 100644
--- a/lib/default-theme/styles/code.styl
+++ b/packages/@vuepress/theme-default/styles/code.styl
@@ -1,4 +1,4 @@
-@require './config'
+@import '~@app/style/config'
.content
code
diff --git a/lib/default-theme/styles/custom-blocks.styl b/packages/@vuepress/theme-default/styles/custom-blocks.styl
similarity index 100%
rename from lib/default-theme/styles/custom-blocks.styl
rename to packages/@vuepress/theme-default/styles/custom-blocks.styl
diff --git a/lib/default-theme/styles/mobile.styl b/packages/@vuepress/theme-default/styles/mobile.styl
similarity index 100%
rename from lib/default-theme/styles/mobile.styl
rename to packages/@vuepress/theme-default/styles/mobile.styl
diff --git a/lib/default-theme/styles/nprogress.styl b/packages/@vuepress/theme-default/styles/nprogress.styl
similarity index 100%
rename from lib/default-theme/styles/nprogress.styl
rename to packages/@vuepress/theme-default/styles/nprogress.styl
diff --git a/lib/default-theme/styles/theme.styl b/packages/@vuepress/theme-default/styles/theme.styl
similarity index 98%
rename from lib/default-theme/styles/theme.styl
rename to packages/@vuepress/theme-default/styles/theme.styl
index a733861fa6..dfc58636db 100644
--- a/lib/default-theme/styles/theme.styl
+++ b/packages/@vuepress/theme-default/styles/theme.styl
@@ -1,4 +1,4 @@
-@require './config'
+@require '~@app/style/config'
@require './nprogress'
@require './code'
@require './custom-blocks'
@@ -187,4 +187,4 @@ th, td
.page
padding-left 0
-@require './mobile.styl'
+@require 'mobile.styl'
diff --git a/lib/default-theme/styles/toc.styl b/packages/@vuepress/theme-default/styles/toc.styl
similarity index 100%
rename from lib/default-theme/styles/toc.styl
rename to packages/@vuepress/theme-default/styles/toc.styl
diff --git a/lib/default-theme/styles/wrapper.styl b/packages/@vuepress/theme-default/styles/wrapper.styl
similarity index 100%
rename from lib/default-theme/styles/wrapper.styl
rename to packages/@vuepress/theme-default/styles/wrapper.styl
diff --git a/docs/.vuepress/components/Bit.vue b/packages/docs/docs/.vuepress/components/Bit.vue
similarity index 100%
rename from docs/.vuepress/components/Bit.vue
rename to packages/docs/docs/.vuepress/components/Bit.vue
diff --git a/docs/.vuepress/components/Foo/Bar.vue b/packages/docs/docs/.vuepress/components/Foo/Bar.vue
similarity index 100%
rename from docs/.vuepress/components/Foo/Bar.vue
rename to packages/docs/docs/.vuepress/components/Foo/Bar.vue
diff --git a/docs/.vuepress/components/OtherComponent.vue b/packages/docs/docs/.vuepress/components/OtherComponent.vue
similarity index 100%
rename from docs/.vuepress/components/OtherComponent.vue
rename to packages/docs/docs/.vuepress/components/OtherComponent.vue
diff --git a/docs/.vuepress/components/demo-1.vue b/packages/docs/docs/.vuepress/components/demo-1.vue
similarity index 100%
rename from docs/.vuepress/components/demo-1.vue
rename to packages/docs/docs/.vuepress/components/demo-1.vue
diff --git a/docs/.vuepress/config.js b/packages/docs/docs/.vuepress/config.js
similarity index 85%
rename from docs/.vuepress/config.js
rename to packages/docs/docs/.vuepress/config.js
index f56003051c..346ff55bc2 100644
--- a/docs/.vuepress/config.js
+++ b/packages/docs/docs/.vuepress/config.js
@@ -1,5 +1,5 @@
module.exports = {
- dest: 'vuepress',
+ dest: '../../vuepress',
locales: {
'/': {
lang: 'en-US',
@@ -23,29 +23,21 @@ module.exports = {
['meta', { name: 'msapplication-TileImage', content: '/icons/msapplication-icon-144x144.png' }],
['meta', { name: 'msapplication-TileColor', content: '#000000' }]
],
- serviceWorker: true,
- theme: 'vue',
themeConfig: {
repo: 'vuejs/vuepress',
editLinks: true,
docsDir: 'docs',
// #697 Provided by the official algolia team.
- algolia: {
- apiKey: '3a539aab83105f01761a137c61004d85',
- indexName: 'vuepress'
- },
+ // algolia: {
+ // apiKey: '3a539aab83105f01761a137c61004d85',
+ // indexName: 'vuepress'
+ // },
locales: {
'/': {
label: 'English',
selectText: 'Languages',
editLinkText: 'Edit this page on GitHub',
lastUpdated: 'Last Updated',
- serviceWorker: {
- updatePopup: {
- message: "New content is available.",
- buttonText: "Refresh"
- }
- },
nav: [
{
text: 'Guide',
@@ -55,6 +47,14 @@ module.exports = {
text: 'Config Reference',
link: '/config/'
},
+ {
+ text: 'Plugin',
+ link: '/plugin/'
+ },
+ {
+ text: 'Theme',
+ link: '/theme/'
+ },
{
text: 'Default Theme Config',
link: '/default-theme-config/'
@@ -73,12 +73,6 @@ module.exports = {
selectText: '选择语言',
editLinkText: '在 GitHub 上编辑此页',
lastUpdated: '上次更新',
- serviceWorker: {
- updatePopup: {
- message: "发现新内容可用",
- buttonText: "刷新"
- }
- },
nav: [
{
text: '指南',
@@ -102,6 +96,15 @@ module.exports = {
}
}
}
+ },
+ plugins: {
+ '@vuepress/i18n-ui': true,
+ '@vuepress/back-to-top': true,
+ '@vuepress/pwa': {
+ serviceWorker: true,
+ updatePopup: true
+ },
+ '@vuepress/plugin-medium-zoom': true
}
}
diff --git a/docs/.vuepress/public/logo.png b/packages/docs/docs/.vuepress/images/logo.png
similarity index 100%
rename from docs/.vuepress/public/logo.png
rename to packages/docs/docs/.vuepress/images/logo.png
diff --git a/docs/.vuepress/public/hero.png b/packages/docs/docs/.vuepress/public/hero.png
similarity index 100%
rename from docs/.vuepress/public/hero.png
rename to packages/docs/docs/.vuepress/public/hero.png
diff --git a/docs/.vuepress/public/icons/android-chrome-192x192.png b/packages/docs/docs/.vuepress/public/icons/android-chrome-192x192.png
similarity index 100%
rename from docs/.vuepress/public/icons/android-chrome-192x192.png
rename to packages/docs/docs/.vuepress/public/icons/android-chrome-192x192.png
diff --git a/docs/.vuepress/public/icons/android-chrome-512x512.png b/packages/docs/docs/.vuepress/public/icons/android-chrome-512x512.png
similarity index 100%
rename from docs/.vuepress/public/icons/android-chrome-512x512.png
rename to packages/docs/docs/.vuepress/public/icons/android-chrome-512x512.png
diff --git a/docs/.vuepress/public/icons/apple-touch-icon-120x120.png b/packages/docs/docs/.vuepress/public/icons/apple-touch-icon-120x120.png
similarity index 100%
rename from docs/.vuepress/public/icons/apple-touch-icon-120x120.png
rename to packages/docs/docs/.vuepress/public/icons/apple-touch-icon-120x120.png
diff --git a/docs/.vuepress/public/icons/apple-touch-icon-152x152.png b/packages/docs/docs/.vuepress/public/icons/apple-touch-icon-152x152.png
similarity index 100%
rename from docs/.vuepress/public/icons/apple-touch-icon-152x152.png
rename to packages/docs/docs/.vuepress/public/icons/apple-touch-icon-152x152.png
diff --git a/docs/.vuepress/public/icons/apple-touch-icon-180x180.png b/packages/docs/docs/.vuepress/public/icons/apple-touch-icon-180x180.png
similarity index 100%
rename from docs/.vuepress/public/icons/apple-touch-icon-180x180.png
rename to packages/docs/docs/.vuepress/public/icons/apple-touch-icon-180x180.png
diff --git a/docs/.vuepress/public/icons/apple-touch-icon-60x60.png b/packages/docs/docs/.vuepress/public/icons/apple-touch-icon-60x60.png
similarity index 100%
rename from docs/.vuepress/public/icons/apple-touch-icon-60x60.png
rename to packages/docs/docs/.vuepress/public/icons/apple-touch-icon-60x60.png
diff --git a/docs/.vuepress/public/icons/apple-touch-icon-76x76.png b/packages/docs/docs/.vuepress/public/icons/apple-touch-icon-76x76.png
similarity index 100%
rename from docs/.vuepress/public/icons/apple-touch-icon-76x76.png
rename to packages/docs/docs/.vuepress/public/icons/apple-touch-icon-76x76.png
diff --git a/docs/.vuepress/public/icons/apple-touch-icon.png b/packages/docs/docs/.vuepress/public/icons/apple-touch-icon.png
similarity index 100%
rename from docs/.vuepress/public/icons/apple-touch-icon.png
rename to packages/docs/docs/.vuepress/public/icons/apple-touch-icon.png
diff --git a/docs/.vuepress/public/icons/favicon-16x16.png b/packages/docs/docs/.vuepress/public/icons/favicon-16x16.png
similarity index 100%
rename from docs/.vuepress/public/icons/favicon-16x16.png
rename to packages/docs/docs/.vuepress/public/icons/favicon-16x16.png
diff --git a/docs/.vuepress/public/icons/favicon-32x32.png b/packages/docs/docs/.vuepress/public/icons/favicon-32x32.png
similarity index 100%
rename from docs/.vuepress/public/icons/favicon-32x32.png
rename to packages/docs/docs/.vuepress/public/icons/favicon-32x32.png
diff --git a/docs/.vuepress/public/icons/msapplication-icon-144x144.png b/packages/docs/docs/.vuepress/public/icons/msapplication-icon-144x144.png
similarity index 100%
rename from docs/.vuepress/public/icons/msapplication-icon-144x144.png
rename to packages/docs/docs/.vuepress/public/icons/msapplication-icon-144x144.png
diff --git a/docs/.vuepress/public/icons/mstile-150x150.png b/packages/docs/docs/.vuepress/public/icons/mstile-150x150.png
similarity index 100%
rename from docs/.vuepress/public/icons/mstile-150x150.png
rename to packages/docs/docs/.vuepress/public/icons/mstile-150x150.png
diff --git a/docs/.vuepress/public/icons/safari-pinned-tab.svg b/packages/docs/docs/.vuepress/public/icons/safari-pinned-tab.svg
similarity index 100%
rename from docs/.vuepress/public/icons/safari-pinned-tab.svg
rename to packages/docs/docs/.vuepress/public/icons/safari-pinned-tab.svg
diff --git a/docs/.vuepress/public/line-numbers-desktop.png b/packages/docs/docs/.vuepress/public/line-numbers-desktop.png
similarity index 100%
rename from docs/.vuepress/public/line-numbers-desktop.png
rename to packages/docs/docs/.vuepress/public/line-numbers-desktop.png
diff --git a/docs/.vuepress/public/line-numbers-mobile.gif b/packages/docs/docs/.vuepress/public/line-numbers-mobile.gif
similarity index 100%
rename from docs/.vuepress/public/line-numbers-mobile.gif
rename to packages/docs/docs/.vuepress/public/line-numbers-mobile.gif
diff --git a/packages/docs/docs/.vuepress/public/logo.png b/packages/docs/docs/.vuepress/public/logo.png
new file mode 100644
index 0000000000..60e17006ad
Binary files /dev/null and b/packages/docs/docs/.vuepress/public/logo.png differ
diff --git a/docs/.vuepress/public/manifest.json b/packages/docs/docs/.vuepress/public/manifest.json
similarity index 100%
rename from docs/.vuepress/public/manifest.json
rename to packages/docs/docs/.vuepress/public/manifest.json
diff --git a/packages/docs/docs/.vuepress/public/plugin.png b/packages/docs/docs/.vuepress/public/plugin.png
new file mode 100644
index 0000000000..e22cec9922
Binary files /dev/null and b/packages/docs/docs/.vuepress/public/plugin.png differ
diff --git a/docs/README.md b/packages/docs/docs/README.md
similarity index 100%
rename from docs/README.md
rename to packages/docs/docs/README.md
diff --git a/docs/config/README.md b/packages/docs/docs/config/README.md
similarity index 100%
rename from docs/config/README.md
rename to packages/docs/docs/config/README.md
diff --git a/docs/default-theme-config/README.md b/packages/docs/docs/default-theme-config/README.md
similarity index 100%
rename from docs/default-theme-config/README.md
rename to packages/docs/docs/default-theme-config/README.md
diff --git a/docs/guide/README.md b/packages/docs/docs/guide/README.md
similarity index 75%
rename from docs/guide/README.md
rename to packages/docs/docs/guide/README.md
index 2ab95d5003..5551339e04 100644
--- a/docs/guide/README.md
+++ b/packages/docs/docs/guide/README.md
@@ -2,7 +2,7 @@
-VuePress is composed of two parts: a minimalistic static site generator with a Vue-powered theming system, and a default theme optimized for writing technical documentation. It was created to support the documentation needs of Vue's own sub projects.
+VuePress is composed of two parts: a [minimalistic static site generator](https://github.com/vuejs/vuepress/tree/next/packages/%40vuepress/core) with a Vue-powered theming system and [Plugin API](../plugin/README.md), and a [default theme](../default-theme-config/README.md) optimized for writing technical documentation. It was created to support the documentation needs of Vue's own sub projects.
Each page generated by VuePress has its own pre-rendered static HTML, providing great loading performance and is SEO-friendly. Once the page is loaded, however, Vue takes over the static content and turns it into a full Single-Page Application (SPA). Additional pages are fetched on demand as the user navigates around the site.
@@ -14,15 +14,28 @@ During the build, we create a server-rendered version of the app and render the
Each markdown file is compiled into HTML with [markdown-it](https://github.com/markdown-it/markdown-it) and then processed as the template of a Vue component. This allows you to directly use Vue inside your markdown files and is great when you need to embed dynamic content.
+## Life cycle
+
+Below is a diagram for the VuePress application lifecycle in dev and build modes.
+
+
+
+It is worth noting that during the `prepare` phase, VuePress does the following:
+
+1. Resolve config
+2. Initialize and apply [Plugin API](../plugin/README.md)
+3. Initialize [webpack](http://webpack.js.org/) configuration and create [markdown-it](https://github.com/markdown-it/markdown-it) instance
+
## Features
-- [Built-in markdown extensions](./markdown.md) optimized for technical documentation
-- [Ability to leverage Vue inside markdown files](./using-vue.md)
-- [Vue-powered custom theme system](./custom-themes.md)
+- [Powerful Plugin API](../plugin/README.md)
+- [Built-in markdown extensions](markdown.md) optimized for technical documentation
+- [Ability to leverage Vue inside markdown files](using-vue.md)
+- [Vue-powered custom theme system](custom-themes.md)
- [Automatic Service Worker generation](../config/README.md#serviceworker)
- [Google Analytics Integration](../config/README.md#ga)
- ["Last Updated" based on Git](../default-theme-config/README.md#last-updated)
-- [Multi-language support](./i18n.md)
+- [Multi-language support](i18n.md)
- A default theme with:
- Responsive layout
- [Optional Homepage](../default-theme-config/README.md#homepage)
@@ -31,15 +44,6 @@ Each markdown file is compiled into HTML with [markdown-it](https://github.com/m
- Customizable [navbar](../default-theme-config/README.md#navbar) and [sidebar](../default-theme-config/README.md#sidebar)
- [Auto-generated GitHub link and page edit links](../default-theme-config/README.md#git-repo-and-edit-links)
-## Todo
-
-VuePress is still a work in progress. There are a few things that it currently does not support but are planned:
-
-- Plugin support
-- Blogging support
-
-Contributions are welcome!
-
## Why Not ...?
### Nuxt
diff --git a/docs/guide/assets.md b/packages/docs/docs/guide/assets.md
similarity index 96%
rename from docs/guide/assets.md
rename to packages/docs/docs/guide/assets.md
index 04a6eb31fe..1d4b8f5571 100644
--- a/docs/guide/assets.md
+++ b/packages/docs/docs/guide/assets.md
@@ -1,3 +1,8 @@
+---
+permalink: :year/:month/:day/:slug
+date: 2018-08-15 11:22:33
+---
+
# Asset Handling
## Relative URLs
diff --git a/docs/guide/basic-config.md b/packages/docs/docs/guide/basic-config.md
similarity index 96%
rename from docs/guide/basic-config.md
rename to packages/docs/docs/guide/basic-config.md
index e333472466..3c38da0633 100644
--- a/docs/guide/basic-config.md
+++ b/packages/docs/docs/guide/basic-config.md
@@ -34,7 +34,7 @@ You can also use YAML (`.vuepress/config.yml`) or TOML (`.vuepress/config.toml`)
A VuePress theme is responsible for all the layout and interactivity details of your site. VuePress ships with a default theme (you are looking at it right now) which is designed for technical documentation. It exposes a number of options that allow you to customize the navbar, sidebar and homepage, etc. For details, check out the [Default Theme Config](../default-theme-config/README.md) page.
-If you wish to develop a custom theme, see [Custom Themes](./custom-themes.md).
+If you wish to develop a custom theme, see [Custom Themes](custom-themes.md).
## App Level Enhancements
diff --git a/docs/guide/custom-themes.md b/packages/docs/docs/guide/custom-themes.md
similarity index 99%
rename from docs/guide/custom-themes.md
rename to packages/docs/docs/guide/custom-themes.md
index ffdbcf520e..374597dba0 100644
--- a/docs/guide/custom-themes.md
+++ b/packages/docs/docs/guide/custom-themes.md
@@ -58,7 +58,6 @@ Finally, don't forget that `this.$route` and `this.$router` are also available a
::: tip
`lastUpdated` is the UNIX timestamp of this file's last git commit, for more details, refer to [Last Updated](../default-theme-config/README.md#last-updated).
-
:::
## Content Excerpt
diff --git a/docs/guide/deploy.md b/packages/docs/docs/guide/deploy.md
similarity index 100%
rename from docs/guide/deploy.md
rename to packages/docs/docs/guide/deploy.md
diff --git a/docs/guide/getting-started.md b/packages/docs/docs/guide/getting-started.md
similarity index 95%
rename from docs/guide/getting-started.md
rename to packages/docs/docs/guide/getting-started.md
index ab48f0ad6b..9432ce2d15 100644
--- a/docs/guide/getting-started.md
+++ b/packages/docs/docs/guide/getting-started.md
@@ -63,4 +63,4 @@ To generate static assets, run:
yarn docs:build # Or npm run docs:build
```
-By default the built files will be in `.vuepress/dist`, which can be configured via the `dest` field in `.vuepress/config.js`. The built files can be deployed to any static file server. See [Deployment Guide](./deploy.md) for guides on deploying to popular services.
+By default the built files will be in `.vuepress/dist`, which can be configured via the `dest` field in `.vuepress/config.js`. The built files can be deployed to any static file server. See [Deployment Guide](deploy.md) for guides on deploying to popular services.
diff --git a/docs/guide/i18n.md b/packages/docs/docs/guide/i18n.md
similarity index 100%
rename from docs/guide/i18n.md
rename to packages/docs/docs/guide/i18n.md
diff --git a/docs/guide/markdown.md b/packages/docs/docs/guide/markdown.md
similarity index 100%
rename from docs/guide/markdown.md
rename to packages/docs/docs/guide/markdown.md
diff --git a/docs/guide/using-vue.md b/packages/docs/docs/guide/using-vue.md
similarity index 100%
rename from docs/guide/using-vue.md
rename to packages/docs/docs/guide/using-vue.md
diff --git a/packages/docs/docs/plugin/README.md b/packages/docs/docs/plugin/README.md
new file mode 100644
index 0000000000..b310662af1
--- /dev/null
+++ b/packages/docs/docs/plugin/README.md
@@ -0,0 +1,646 @@
+---
+sidebar: auto
+---
+
+# Plugins
+
+## Writing a Plugin
+
+Plugins usually add global-level functionality to VuePress. There is no strictly defined scope for a plugin - there are typically several types of plugins:
+
+1. Extend the data generated at compile time. e.g. [@vuepress/plugin-last-updated](https://github.com/vuejs/vuepress/tree/next/packages/@vuepress/plugin-last-updated).
+2. Generate extra files before or after compilation. e.g. [@vuepress/plugin-pwa](https://github.com/vuejs/vuepress/tree/next/packages/%40vuepress/plugin-pwa)
+3. Add extra pages. e.g. [@vuepress/plugin-i18n-ui](https://github.com/vuejs/vuepress/tree/next/packages/@vuepress/plugin-i18n-ui)
+4. Inject global UI. e.g. [@vuepress/plugin-back-to-top](https://github.com/vuejs/vuepress/tree/next/packages/%40vuepress/plugin-back-to-top).
+
+A plugin should export a `plain object`(`#1`). If the plugin needs to take options, it can be a function that exports a plain object(`#2`). The function will be called with the plugin's options as the first argument, along with [context](#plugin-context) which provides some compile-time metadata.
+
+``` js
+// #1
+module.exports = {
+ // ...
+}
+```
+
+``` js
+// #2
+module.exports = (options, ctx) => {
+ return {
+ // ...
+ }
+}
+```
+
+::: tip
+A VuePress plugin module should leverage `CommonJS Module` because VuePress plugins runs on the Node side.
+:::
+
+## Using a plugin
+
+You can use plugins by doing some configuration at `.vuepress/config.js`:
+
+``` js
+module.exports = {
+ plugins: [
+ require('./my-plugin.js')
+ ]
+}
+```
+
+### Use plugins from a dependency
+
+A plugin can be published on npm in `CommonJS` format as `vuepress-plugin-xxx`. then you can use it:
+
+``` js
+module.exports = {
+ plugins: [ 'vuepress-plugin-xx' ]
+}
+```
+
+### Plugin Shorthand
+
+If you prefix the plugin with `vuepress-plugin-`, you can use a shorthand to leave out that prefix:
+
+``` js
+module.exports = {
+ plugins: [ 'xxx' ]
+}
+```
+
+Same with:
+
+``` js
+module.exports = {
+ plugins: [ 'vuepress-plugin-xxx' ]
+}
+```
+
+This also works with [Scoped Packages](https://docs.npmjs.com/misc/scope):
+
+``` js
+module.exports = {
+ plugins: [ '@org/vuepress-plugin-xxx', '@vuepress/plugin-xxx' ]
+}
+```
+
+Shorthand:
+
+``` js
+module.exports = {
+ plugins: [ '@org/xxx', '@vuepress/xxx' ]
+}
+```
+
+::: warning Note
+The plugin whose name starts with `@vuepress/plugin-` is an officially maintained plugin.
+:::
+
+### Plugin options
+
+#### Babel Style
+
+Plugins can have options specified by wrapping the name and an options object in an array inside your config:
+
+``` js
+module.exports = {
+ plugins: [
+ [
+ require('./my-plugin.js'),
+ { /* options */ }
+ ]
+ ]
+}
+```
+
+Since this style is consistent with [babel's Plugin/Preset Options](https://babeljs.io/docs/en/plugins#plugin-preset-options), we call it `Babel Style`.
+
+#### Object Style
+
+VuePress also provides a simpler way to use plugins from a dependency:
+
+``` js
+module.exports = {
+ plugins: {
+ 'xxx': { /* options */ }
+ }
+}
+```
+
+::: warning Note
+The plugin can be disabled when `false` is explicitly passed as option.
+
+- Babel style
+
+``` js
+module.exports = {
+ plugins: [
+ [ 'xxx', false ] // disabled.
+ ]
+}
+```
+
+- Object style
+
+``` js
+module.exports = {
+ plugins: {
+ 'xxx': false // disabled.
+ }
+}
+```
+
+:::
+
+## Options
+
+### name
+
+- Type: `string`
+- Default: undefined
+
+The name of the plugin.
+
+Internally, vuepress will use the plugin's package name as the plugin name. When your plugin is a local plugin (i.e. using a pure plugin function directly), please be sure to configure this option, that is good for debug tracking.
+
+```js
+module.exports = {
+ plugins: [
+ [
+ (pluginOptions, ctx) => ({
+ name: 'my-xxx-plugin'
+ // ... the rest of options
+ })
+ ]
+ ]
+}
+```
+
+### enabled
+
+- Type: `boolean`
+- Default: true
+
+Configure whether to enable this plugin. e.g. if you want to enable a plugin only in development mode:
+
+```js
+module.exports = (options, ctx) => {
+ return {
+ enabled: !ctx.isProd
+ }
+}
+```
+
+### chainWebpack
+
+- Type: `Function`
+- Default: undefined
+
+Modify the internal webpack config with [webpack-chain](https://github.com/mozilla-neutrino/webpack-chain).
+
+```js
+module.exports = {
+ chainWebpack (config, isServer) {
+ // config is an instance of ChainableConfig
+ }
+}
+```
+
+::: tip
+Since VuePress is a Vue-SSR based application, there will be two webpack configurations, `isServer` is used to determine whether the current webpack config is applied to the server or client.
+
+**Also see:**
+
+- [Vue SSR > Build Configuration](https://ssr.vuejs.org/guide/build-config.html)
+:::
+
+### define
+
+- Type: `Object|Function`
+- Default: undefined
+
+Since using [DefinePlugin](https://webpack.js.org/plugins/define-plugin/) via [chainWebpack](chainwebpack) would be a little complicated:
+
+```js
+module.exports = {
+ chainWebpack (config) {
+ config.plugin('injections').tap(([options]) => [
+ Object.assign(options, {
+ SW_BASE_URL: JSON.stringify('/')
+ })
+ ])
+ }
+}
+```
+
+VuePress specifically opened up a more concise `define` option, note that the values has been automatically processed by `JSON.stringify`.
+
+- Object Usage:
+
+```js
+module.exports = {
+ define: {
+ SW_BASE_URL: '/',
+ }
+}
+```
+
+- Function Usage:
+
+```js
+module.exports = (options, ctx) => ({
+ define () {
+ return {
+ SW_BASE_URL: ctx.base || '/',
+ SW_ENABLED: !!options.enabled,
+ }
+ }
+})
+```
+
+### alias
+
+- Type: `Object|Function`
+- Default: undefined
+
+We can set aliases via [chainWebpack](chainwebpack):
+
+```js
+module.exports = (options, ctx) => ({
+ chainWebpack (config) {
+ config.resolve.alias.set('@theme', ctx.themePath)
+ }
+})
+```
+
+But `alias` option makes this process more like configuration:
+
+```js
+module.exports = (options, ctx) => ({
+ alias: {
+ '@theme': ctx.themePath
+ }
+})
+```
+
+### enhanceDevServer
+
+- Type: `Function`
+- Default: undefined
+
+Enhance the underlying [Koa](https://github.com/koajs/koa) app.
+
+``` js
+module.exports = {
+ enhanceDevServer (app) {
+ // ...
+ }
+}
+```
+
+A simple plugin to create a sub public directory is as follows:
+
+```js
+const path = require('path')
+
+module.exports = (options, ctx) => {
+ const imagesAssetsPath = path.resolve(ctx.sourceDir, '.vuepress/images')
+
+ return {
+ // For development
+ enhanceDevServer (app) {
+ const mount = require('koa-mount')
+ const serveStatic = require('koa-static')
+ app.use(mount(path.join(ctx.publicPath, 'images'), serveStatic(imagesAssetsPath)))
+ },
+
+ // For production
+ async generated () {
+ const { fs } = require('@vuepress/shared-utils')
+ await fs.copy(imagesAssetsPath, path.resolve(ctx.outDir, 'images'))
+ }
+ }
+}
+```
+
+### extendMarkdown
+
+- Type: `Function`
+- Default: `undefined`
+
+A function to modify default config or apply additional plugins to the [markdown-it](https://github.com/markdown-it/markdown-it) instance used to render source files. Example:
+
+```js
+module.exports = {
+ extendMarkdown: md => {
+ md.set({ breaks: true })
+ md.use(require('markdown-it-xxx'))
+ }
+}
+```
+
+### chainMarkdown
+
+- Type: `Function`
+- Default: `undefined`
+
+Modify the internal markdown config with [markdown-it-chain](https://github.com/ulivz/markdown-it-chain) —— A chaining API like [webpack-chain](https://github.com/mozilla-neutrino/webpack-chain) but for [markdown-it](https://github.com/markdown-it/markdown-it).
+
+```js
+module.exports = {
+ chainMarkdown (config) {
+ // Interact with 'options' in new MarkdownIt
+ // Ref: https://markdown-it.github.io/markdown-it/#MarkdownIt.new
+ config
+ .options
+ .link(true)
+ .breaks(true)
+
+ // Modify the arguments of internal plugin.
+ config
+ .plugin('anchor')
+ .tap(([options]) => [
+ Object.assign(options, { permalinkSymbol: '#' })
+ ])
+
+ // Add extra markdown-it plugin
+ config
+ .plugin('sup')
+ .add(require('markdown-it-sup'))
+
+ // Remove internal plugin
+ config.plugins.delete('snippet')
+ }
+}
+```
+
+**Also see:**
+
+- [Internal plugins in VuePress](https://github.com/vuejs/vuepress/blob/next/packages/%40vuepress/core/lib/markdown/index.js)
+- [Config plugins](https://github.com/neutrinojs/webpack-chain#config-plugins)
+
+### enhanceAppFiles
+
+- Type: `Array | AsyncFunction`
+- Default: `undefined`
+
+This option accepts an array containing the file paths, or a function that returns this array, which allows you to do some [App Level Enhancements](../guide/basic-config.md#theme-configuration).
+
+``` js
+module.exports = {
+ enhanceAppFiles: [
+ path.resolve(__dirname, 'client.js')
+ ]
+}
+```
+
+The file can `export default` a hook function which will work like `.vuepress/enhanceApp.js`, or any client side code snippets.
+
+It's worth mentioning that in order for plugin developers to be able to do more things at compile time, this option also supports dynamic code:
+
+``` js
+module.exports = (option, context) => {
+ return {
+ enhanceAppFiles: [{
+ name: 'dynamic-code',
+ content: `export default ({ Vue }) => { Vue.mixin('$source', '${context.sourceDir}') }`
+ }]
+ }
+}
+```
+
+### clientDynamicModules
+
+- Type: `Function`
+- Default: `undefined`
+
+Sometimes, you may want to generate some client modules at compile time.
+
+``` js
+module.exports = (options, context) => ({
+ clientDynamicModules() {
+ return {
+ name: 'constans.js',
+ content: `export const SOURCE_DIR = '${context.sourceDir}'`
+ }
+ }
+})
+```
+
+Then you can use this module at client side code by:
+
+``` js
+import { SOURCE_DIR } from '@dynamic/constans'
+```
+
+::: tip Q & A
+**Q**: Both `clientDynamicModules` and `enhanceAppFiles` can generate dynamic javascript code during build time, so what is the difference between the two?
+
+**A**: The files generated by `clientDynamicModules` needs to be imported as `@dynamic/xxx` by the consumers themselves. But all the files generated by `enhanceAppFiles` will be loaded automatically when the APP is initialized on the client side.
+:::
+
+### extendPageData
+
+- Type: `Function`
+- Default: `undefined`
+
+A function that exports a plain object which will be merged into each page's data object. This function will be invoking once for each page at compile time.
+
+``` js
+module.exports = {
+ extendPageData ({
+ _filePath, // file's absolute path
+ _i18n, // access the client global mixins at build time, e.g _i18n.$localePath.
+ _content, // file's raw content string
+ _strippedContent, // file's content string without frontmatter
+ key, // page's unique hash key
+ frontmatter, // page's frontmatter object
+ regularPath, // current page's default link (follow the file hierarchy)
+ path, // current page's permalink
+ }) {
+ return {
+ // ...
+ }
+ }
+}
+```
+
+::: warning Note
+These fields starting with an `_` means you can only access them during build time.
+:::
+
+e.g.
+
+``` js
+module.exports = {
+ extendPageData ({ content }) {
+ return {
+ size: (content.length / 1024).toFixed(2) + 'kb'
+ }
+ }
+}
+```
+
+Then you can use this value via `this.$page.size` in any Vue component.
+
+### clientRootMixin
+
+- Type: `String`
+- Default: `undefined`
+
+A path to the mixin file which allow you to control the life cycle of root component.
+
+``` js
+// plugin's entry
+const path = require('path')
+
+module.exports = {
+ clientRootMixin: path.resolve(__dirname, 'mixin.js')
+}
+```
+
+``` js
+// mixin.js
+export default {
+ created () {},
+ mounted () {}
+}
+```
+
+### additionalPages
+
+- Type: `Array|Function`
+- Default: `undefined`
+
+Add a page pointing to a markdown file:
+
+```js
+const path = require('path')
+
+module.exports = {
+ additionalPages: [
+ {
+ path: '/readme/',
+ filePath: path.resolve(__dirname, '../../README.md')
+ }
+ ]
+}
+```
+
+Add a page with explicit content:
+
+```js
+module.exports = {
+ async additionalPages () {
+ const rp = require('request-promise');
+
+ // VuePress doesn't have request library built-in
+ // you need to install it yourself.
+ const content = await rp('https://github.com/vuejs/vuepress/blob/master/CHANGELOG.md');
+ return [
+ {
+ path: '/readme/',
+ content
+ }
+ ]
+ }
+}
+```
+
+Add a pure route:
+
+```js
+module.exports = {
+ additionalPages: [
+ {
+ path: '/alpha/',
+ frontmatter: {
+ layout: 'MyLayout'
+ }
+ }
+ ]
+}
+```
+
+### globalUIComponents
+
+- Type: `Array|String`
+- Default: `undefined`
+
+You might want to inject some global UI fixed somewhere on the page, e.g. `back-to-top`, `popup`. In VuePress, **a global UI is a Vue component**, you can define the component's name(s) in the plugin, e.g.
+
+``` js
+module.exports = {
+ globalUIComponents: [
+ 'Component-1',
+ 'Component-2'
+ ]
+}
+```
+
+Then, VuePress will automatically inject these components behind the theme container:
+
+```html
+
+```
+
+## Context
+
+Starting with VuePress 1.x.x, VuePress provides an `AppContext` object that stores all the state of the current app and can be accessed through the plugin API.
+
+::: warning Note
+Context of each plugin is a isolated context, they just inherit from the same app context.
+:::
+
+```js
+module.exports = (options, context) => {
+ // ...
+}
+```
+
+### context.isProd
+
+- Type: `boolean`
+
+Whether vuepress run in production environment mode.
+
+### context.sourceDir
+
+- Type: `string`
+
+Root directory where the documents are located.
+
+### context.tempPath
+
+- Type: `string`
+
+Root directory where the temporary files are located.
+
+### context.outDir
+
+- Type: `string`
+
+Output path.
+
+### context.themePath
+
+- Type: `string`
+
+The path of the currently active theme.
+
+### context.base
+
+- Type: `string`
+
+See: [base](../config/README.md#base).
+
+### context.writeTemp
+
+- Type: `Function`
+
+A utility for writing temporary files to tempPath.
diff --git a/packages/docs/docs/theme/README.md b/packages/docs/docs/theme/README.md
new file mode 100644
index 0000000000..d28cf8373a
--- /dev/null
+++ b/packages/docs/docs/theme/README.md
@@ -0,0 +1,96 @@
+---
+sidebar: auto
+---
+
+# Theme
+
+## Background
+
+Before 1.x.x, vuepress retrieves all markdown files in the documents source directory and defines the page links based on the file hierarchy. e.g. if you have the following file structure:
+
+```
+├── package.json
+└── source
+ ├── _post
+ │ └── intro-vuepress.md
+ ├── index.md
+ └── tags.md
+```
+
+Then you will get following available pages:
+
+```
+/source/
+/source/tags.html
+/source/_post/intro-vuepress.html
+```
+
+However, for a blog system, we hope that the link of a post can be customized. VuePress started supporting this feature from `1.0.0`. which is known as `permalink`. Then, the actual pages would be:
+
+```
+/source/
+/source/tags/
+/source/2018/4/1/intro-vuepress.html
+```
+
+It seems that we have seen the shadow of the blog. Let's continue to look down.
+
+## Permalinks
+
+A permalink is a URL that is intended to remain unchanged for many years into the future, yielding a hyperlink that is less susceptible to link rot[1] . VuePress supports a flexible way to build permalinks, allowing you to leverage various template variables.
+
+The default permalink is `/:regular`.
+
+### Configure Permalinks
+
+You can configure globally to apply it for all pages:
+
+```js
+// .vuepress/config.js
+module.exports = {
+ permalink: '/:year/:month/:day/:slug'
+}
+```
+
+Alternatively, you can also set permalink on a page only, and it will have a higher priority than the global settings.
+
+📝 __hello.md__:
+
+```markdown
+---
+title: Hello World
+permalink: /hello-world
+---
+
+Hello!
+```
+
+### Template Variables
+
+| Variable | Description |
+|---|---|
+|:year|Published year of posts (4-digit)|
+|:month|Published month of posts (2-digit)|
+|:i_month|Published month of posts (Without leading zeros)|
+|:day|Published day of posts (2-digit)|
+|:i_day|Published day of posts (Without leading zeros)|
+|:slug|Slugified file path (Without extension)|
+|:regular| Permalink generated by VuePress by default, for implementation see [here](https://github.com/vuejs/vuepress/blob/next/packages/%40vuepress/shared-utils/lib/fileToPath.js) |
+
+## Writing a theme
+
+TODO. integrate with the old docs.
+
+## Theme API
+
+### layout
+
+TODO
+
+### notFound
+
+TODO
+
+### plugins
+
+TODO
diff --git a/docs/zh/README.md b/packages/docs/docs/zh/README.md
similarity index 100%
rename from docs/zh/README.md
rename to packages/docs/docs/zh/README.md
diff --git a/docs/zh/config/README.md b/packages/docs/docs/zh/config/README.md
similarity index 100%
rename from docs/zh/config/README.md
rename to packages/docs/docs/zh/config/README.md
diff --git a/docs/zh/default-theme-config/README.md b/packages/docs/docs/zh/default-theme-config/README.md
similarity index 100%
rename from docs/zh/default-theme-config/README.md
rename to packages/docs/docs/zh/default-theme-config/README.md
diff --git a/docs/zh/guide/README.md b/packages/docs/docs/zh/guide/README.md
similarity index 93%
rename from docs/zh/guide/README.md
rename to packages/docs/docs/zh/guide/README.md
index 28dbfb1919..e48bd4c9f1 100644
--- a/docs/zh/guide/README.md
+++ b/packages/docs/docs/zh/guide/README.md
@@ -12,13 +12,13 @@ VuePress 由两部分组成:一部分是支持用 Vue 开发主题的极简静
## 特性
-- 为技术文档而优化的 [内置 Markdown 拓展](./markdown.md)
-- [在 Markdown 文件中使用 Vue 组件的能力](./using-vue.md)
-- [Vue 驱动的自定义主题系统](./custom-themes.md)
+- 为技术文档而优化的 [内置 Markdown 拓展](markdown.md)
+- [在 Markdown 文件中使用 Vue 组件的能力](using-vue.md)
+- [Vue 驱动的自定义主题系统](custom-themes.md)
- [自动生成 Service Worker](../config/README.md#serviceworker)
- [Google Analytics 集成](../config/README.md#ga)
- [基于 Git 的 “最后更新时间”](../default-theme-config/README.md#最后更新时间)
-- [多语言支持](./i18n.md)
+- [多语言支持](i18n.md)
- 默认主题包含:
- 响应式布局
- [可选的主页](../default-theme-config/README.md#首页)
diff --git a/docs/zh/guide/assets.md b/packages/docs/docs/zh/guide/assets.md
similarity index 100%
rename from docs/zh/guide/assets.md
rename to packages/docs/docs/zh/guide/assets.md
diff --git a/docs/zh/guide/basic-config.md b/packages/docs/docs/zh/guide/basic-config.md
similarity index 98%
rename from docs/zh/guide/basic-config.md
rename to packages/docs/docs/zh/guide/basic-config.md
index 3ac5f459d1..686ff82940 100644
--- a/docs/zh/guide/basic-config.md
+++ b/packages/docs/docs/zh/guide/basic-config.md
@@ -34,7 +34,7 @@ module.exports = {
一个 VuePress 主题应该负责整个网站的布局和交互细节。在 VuePress 中,目前自带了一个默认的主题(正是你现在所看到的),它是为技术文档而设计的。同时,默认主题提供了一些选项,让你可以去自定义导航栏(navbar)、 侧边栏(sidebar)和 首页(homepage) 等,详情请参见 [默认主题](../default-theme-config/README.md) 。
-如果你想开发一个自定义主题,可以参考 [自定义主题](./custom-themes.md)。
+如果你想开发一个自定义主题,可以参考 [自定义主题](custom-themes.md)。
## 应用级别的配置
diff --git a/docs/zh/guide/custom-themes.md b/packages/docs/docs/zh/guide/custom-themes.md
similarity index 100%
rename from docs/zh/guide/custom-themes.md
rename to packages/docs/docs/zh/guide/custom-themes.md
diff --git a/docs/zh/guide/deploy.md b/packages/docs/docs/zh/guide/deploy.md
similarity index 100%
rename from docs/zh/guide/deploy.md
rename to packages/docs/docs/zh/guide/deploy.md
diff --git a/docs/zh/guide/getting-started.md b/packages/docs/docs/zh/guide/getting-started.md
similarity index 96%
rename from docs/zh/guide/getting-started.md
rename to packages/docs/docs/zh/guide/getting-started.md
index 402a03530b..6040e71dab 100644
--- a/docs/zh/guide/getting-started.md
+++ b/packages/docs/docs/zh/guide/getting-started.md
@@ -67,4 +67,4 @@ yarn docs:dev # 或者:npm run docs:dev
yarn docs:build # 或者:npm run docs:build
```
-默认情况下,文件将会被生成在 `.vuepress/dist`,当然,你也可以通过 `.vuepress/config.js` 中的 `dest` 字段来修改,生成的文件可以部署到任意的静态文件服务器上,参考 [部署](./deploy.md) 来了解更多。
+默认情况下,文件将会被生成在 `.vuepress/dist`,当然,你也可以通过 `.vuepress/config.js` 中的 `dest` 字段来修改,生成的文件可以部署到任意的静态文件服务器上,参考 [部署](deploy.md) 来了解更多。
diff --git a/docs/zh/guide/i18n.md b/packages/docs/docs/zh/guide/i18n.md
similarity index 100%
rename from docs/zh/guide/i18n.md
rename to packages/docs/docs/zh/guide/i18n.md
diff --git a/docs/zh/guide/markdown.md b/packages/docs/docs/zh/guide/markdown.md
similarity index 100%
rename from docs/zh/guide/markdown.md
rename to packages/docs/docs/zh/guide/markdown.md
diff --git a/docs/zh/guide/using-vue.md b/packages/docs/docs/zh/guide/using-vue.md
similarity index 100%
rename from docs/zh/guide/using-vue.md
rename to packages/docs/docs/zh/guide/using-vue.md
diff --git a/packages/docs/package.json b/packages/docs/package.json
new file mode 100644
index 0000000000..411af4f07f
--- /dev/null
+++ b/packages/docs/package.json
@@ -0,0 +1,32 @@
+{
+ "private": true,
+ "version": "1.0.0",
+ "name": "docs",
+ "description": "docs of VuePress",
+ "scripts": {
+ "dev": "vuepress dev docs --temp .temp",
+ "build": "vuepress build docs --temp .temp"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vuejs/vuepress.git"
+ },
+ "keywords": [
+ "documentation",
+ "vue",
+ "generator"
+ ],
+ "author": "Evan You",
+ "license": "MIT",
+ "bugs": {
+ "url": "https://github.com/vuejs/vuepress/issues"
+ },
+ "homepage": "https://github.com/vuejs/vuepress#readme",
+ "devDependencies": {
+ "vuepress": "^1.0.0",
+ "@vuepress/plugin-back-to-top": "^1.0.0",
+ "@vuepress/plugin-pwa": "^1.0.0",
+ "@vuepress/plugin-i18n-ui": "^1.0.0",
+ "@vuepress/plugin-medium-zoom": "^1.0.0"
+ }
+}
diff --git a/packages/vuepress/package.json b/packages/vuepress/package.json
new file mode 100644
index 0000000000..eb1fcb7ba4
--- /dev/null
+++ b/packages/vuepress/package.json
@@ -0,0 +1,29 @@
+{
+ "name": "vuepress",
+ "version": "1.0.0",
+ "description": "Minimalistic doc generator with Vue component based layout system",
+ "main": "vuepress.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",
+ "dependencies": {
+ "@vuepress/core": "^1.0.0",
+ "@vuepress/cli": "^1.0.0",
+ "@vuepress/theme-default": "^1.0.0"
+ }
+}
diff --git a/packages/vuepress/vuepress.js b/packages/vuepress/vuepress.js
new file mode 100755
index 0000000000..c885579271
--- /dev/null
+++ b/packages/vuepress/vuepress.js
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+
+require('@vuepress/cli').bootstrap({ theme: '@vuepress/default' })
diff --git a/scripts/bootstrap.js b/scripts/bootstrap.js
new file mode 100644
index 0000000000..65e8599121
--- /dev/null
+++ b/scripts/bootstrap.js
@@ -0,0 +1,57 @@
+// create package.json and README for packages that don't have one yet
+
+const fs = require('fs')
+const path = require('path')
+const baseVersion = require('../packages/@vuepress/core/package.json').version
+
+const packagesDir = path.resolve(__dirname, '../packages/@vuepress')
+const files = fs.readdirSync(packagesDir)
+
+files.forEach(pkg => {
+ if (pkg.charAt(0) === '.') return
+
+ const isPlugin = /^plugin-/.test(pkg)
+ const desc = isPlugin
+ ? `${pkg.replace('plugin-', '')} plugin for vuepress`
+ : `${pkg} for vuepress`
+
+ const pkgPath = path.join(packagesDir, pkg, `package.json`)
+ if (!fs.existsSync(pkgPath)) {
+ const json = {
+ 'name': `@vuepress/${pkg}`,
+ 'version': baseVersion,
+ 'description': desc,
+ 'main': 'index.js',
+ 'publishConfig': {
+ 'access': 'public'
+ },
+ 'repository': {
+ 'type': 'git',
+ 'url': 'git+https://github.com/vuejs/vuepress.git'
+ },
+ 'keywords': [
+ 'documentation',
+ 'vue',
+ 'vuepress',
+ 'generator'
+ ],
+ 'author': 'Evan You',
+ 'license': 'MIT',
+ 'bugs': {
+ 'url': 'https://github.com/vuejs/vuepress/issues'
+ },
+ 'homepage': `https://github.com/vuejs/vuepress/packages/@vuepress/${pkg}#readme`
+ }
+ fs.writeFileSync(pkgPath, JSON.stringify(json, null, 2))
+ }
+
+ const readmePath = path.join(packagesDir, pkg, `README.md`)
+ if (!fs.existsSync(readmePath)) {
+ fs.writeFileSync(readmePath, `# @vuepress/${pkg}\n\n> ${desc}`)
+ }
+
+ const npmIgnorePath = path.join(packagesDir, pkg, `.npmignore`)
+ if (!fs.existsSync(npmIgnorePath)) {
+ fs.writeFileSync(npmIgnorePath, `__tests__\n__mocks__`)
+ }
+})
diff --git a/scripts/genChangelog.js b/scripts/genChangelog.js
new file mode 100644
index 0000000000..544ab5cd82
--- /dev/null
+++ b/scripts/genChangelog.js
@@ -0,0 +1,27 @@
+const execa = require('execa')
+const cc = require('conventional-changelog')
+const config = require('@vue/conventional-changelog')
+
+const gen = module.exports = version => {
+ const fileStream = require('fs').createWriteStream(`CHANGELOG.md`)
+
+ cc({
+ config,
+ releaseCount: 0,
+ pkg: {
+ transform (pkg) {
+ pkg.version = `v${version}`
+ return pkg
+ }
+ }
+ }).pipe(fileStream).on('close', async () => {
+ delete process.env.PREFIX
+ await execa('git', ['add', '-A'], { stdio: 'inherit' })
+ await execa('git', ['commit', '-m', `chore: ${version} changelog`], { stdio: 'inherit' })
+ })
+}
+
+if (process.argv[2] === 'run') {
+ const version = require('../lerna.json').version
+ gen(version)
+}
diff --git a/scripts/jest.config.js b/scripts/jest.config.js
new file mode 100644
index 0000000000..dcb002f565
--- /dev/null
+++ b/scripts/jest.config.js
@@ -0,0 +1,14 @@
+const path = require('path')
+const createJestConfig = require('@vuepress/test-utils/createJestConfig')
+
+module.exports = createJestConfig({
+ rootDir: path.resolve(__dirname, '..'),
+ moduleNameMapper: {
+ '^@/(.*)$': '/$1',
+ '^@core/(.*)$': '/packages/@vuepress/core/$1'
+ },
+ modulePathIgnorePatterns: [
+ '/packages/@vuepress/core/__test__/prepare/prepare.spec.js',
+ '/packages/@vuepress/core/__test__/plugin-api/AsyncOption.spec.js'
+ ]
+})
diff --git a/scripts/release.js b/scripts/release.js
new file mode 100644
index 0000000000..96fd96b55a
--- /dev/null
+++ b/scripts/release.js
@@ -0,0 +1,104 @@
+/**
+
+ How to do a release:
+
+ 1. Make sure you have publish access for all packages:
+ - You must be in the VuePress team in the npm @vuepress organization
+ - Make sure you DO NOT have npm per-publish 2-factor / OTP enabled, as it
+ does not work with Lerna (which we use for batch publishing).
+
+ 2. Run `yarn release`, follow prompts
+
+ 3A. If everything works properly, the tag should have been auto-pushed and a
+ local changelog commit should have been generated. Go to 4.
+
+ 3B. If the publish fails half-way, things have gotten hairy. Now you need to
+ go to npm to check which packages have been published and manually publish
+ the ones that have not been published yet. After all have been published:
+
+ 3B.1. Push the release git tag to GitHub.
+ 3B.2. Run `yarn changelog` to generate changelog commit.
+
+ 4. Push the changelog commit to `next` branch.
+
+ 5. Go to GitHub and verify that the changelog is live.
+
+ 6. Go to GitHub releases page and publish the release.
+
+ */
+
+process.env.VUE_CLI_RELEASE = true
+
+const execa = require('execa')
+const semver = require('semver')
+const inquirer = require('inquirer')
+
+const curVersion = require('../lerna.json').version
+
+const release = async () => {
+ console.log(`Current version: ${curVersion}`)
+
+ const bumps = ['patch', 'minor', 'major', 'prerelease', 'premajor']
+ const versions = {}
+ bumps.forEach(b => {
+ versions[b] = semver.inc(curVersion, b)
+ })
+ const bumpChoices = bumps.map(b => ({ name: `${b} (${versions[b]})`, value: b }))
+
+ const { bump, customVersion } = await inquirer.prompt([
+ {
+ name: 'bump',
+ message: 'Select release type:',
+ type: 'list',
+ choices: [
+ ...bumpChoices,
+ { name: 'custom', value: 'custom' }
+ ]
+ },
+ {
+ name: 'customVersion',
+ message: 'Input version:',
+ type: 'input',
+ when: answers => answers.bump === 'custom'
+ }
+ ])
+
+ const version = customVersion || versions[bump]
+
+ const { yes } = await inquirer.prompt([{
+ name: 'yes',
+ message: `Confirm releasing ${version}?`,
+ type: 'list',
+ choices: ['N', 'Y']
+ }])
+
+ if (yes === 'N') {
+ console.log('[release] cancelled.')
+ return
+ }
+
+ const releaseArguments = [
+ 'publish',
+ '--repo-version',
+ version,
+ '--force-publish',
+ '*'
+ ]
+
+ console.log(`lerna ${releaseArguments.join(' ')}`)
+
+ await execa(require.resolve('lerna/bin/lerna'), [
+ 'publish',
+ '--repo-version',
+ version,
+ '--force-publish',
+ '*'
+ ], { stdio: 'inherit' })
+
+ require('./genChangelog')(version)
+}
+
+release().catch(err => {
+ console.error(err)
+ process.exit(1)
+})
diff --git a/scripts/release.sh b/scripts/release.sh
deleted file mode 100755
index 13cc002e9c..0000000000
--- a/scripts/release.sh
+++ /dev/null
@@ -1,52 +0,0 @@
-#!/bin/bash
-
-echo "Select a option to release (input a serial number):"
-echo
-
-select VERSION in patch minor major "Specific Version"
- do
- echo
- if [[ $REPLY =~ ^[1-4]$ ]]; then
- if [[ $REPLY == 4 ]]; then
- read -p "Enter a specific version: " -r VERSION
- echo
- if [[ -z $REPLY ]]; then
- VERSION=$REPLY
- fi
- fi
-
- read -p "Release $VERSION - are you sure? (y/n) " -n 1 -r
- echo
-
- if [[ $REPLY =~ ^[Yy]$ || -z $REPLY ]]; then
- # pre release task
- npm run lint
- npm run test
-
- # bump version
- npm version $VERSION
- NEW_VERSION=$(node -p "require('./package.json').version")
- echo Releasing ${NEW_VERSION} ...
-
- # npm release
- npm whoami
- npm publish
- echo "✅ Released to npm."
-
- # github release
- git add CHANGELOG.md
- git commit -m "chore: changelog"
- git push
- git push origin refs/tags/v${NEW_VERSION}
- echo "✅ Released to Github."
- else
- echo Cancelled
- fi
- break
- else
- echo Invalid \"${REPLY}\"
- echo "To continue, please input a serial number(1-4) of an option."
- echo
- fi
- done
-
diff --git a/scripts/test.js b/scripts/test.js
new file mode 100644
index 0000000000..489d67d4f6
--- /dev/null
+++ b/scripts/test.js
@@ -0,0 +1,26 @@
+const minimist = require('minimist')
+const createJestRunner = require('@vuepress/test-utils/createJestRunner')
+
+const rawArgs = process.argv.slice(2)
+const args = minimist(rawArgs)
+
+let regex
+if (args.p) {
+ const packages = (args.p || args.package).split(',').join('|')
+ regex = `.*@vuepress/(${packages}|plugin-(${packages}))/.*\\.spec\\.js$`
+ const i = rawArgs.indexOf('-p')
+ rawArgs.splice(i, 2)
+}
+
+const jestRunner = createJestRunner(
+ [
+ '--config', 'scripts/jest.config.js',
+ '--runInBand',
+ ...(regex ? [regex] : [])
+ ])
+
+;(jestRunner)().catch(err => {
+ console.error(err)
+ process.exit(1)
+})
+
diff --git a/test/app/Content.spec.js b/test/app/Content.spec.js
deleted file mode 100644
index 8049befc88..0000000000
--- a/test/app/Content.spec.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import Content from '@/app/components/Content'
-import { mount } from '@vue/test-utils'
-import { getRouter, modeTestRunner } from '../util'
-
-function test (mode, localVue) {
- it(`${mode} - add custom class by default.`, () => {
- const wrapper = mount(Content, {
- localVue,
- router: getRouter()
- })
- expect(wrapper.contains('.custom')).toBe(true)
- })
-
- it(`${mode} - remove custom when custom set to false.`, () => {
- const wrapper = mount(Content, {
- // https://vue-test-utils.vuejs.org/api/options.html#context
- context: {
- props: {
- custom: false
- }
- },
- localVue,
- router: getRouter()
- })
- expect(wrapper.contains('.custom')).toBe(false)
- })
-}
-
-modeTestRunner('Content', test)
-
diff --git a/test/default-theme/__snapshots__/DropdownLink.spec.js.snap b/test/default-theme/__snapshots__/DropdownLink.spec.js.snap
deleted file mode 100644
index a302d2112a..0000000000
--- a/test/default-theme/__snapshots__/DropdownLink.spec.js.snap
+++ /dev/null
@@ -1,33 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`DropdownLink $i18n - renders dropdown link. 1`] = `
-
-`;
-
-exports[`DropdownLink $simple - renders dropdown link. 1`] = `
-
-`;
diff --git a/test/default-theme/__snapshots__/NavLink.spec.js.snap b/test/default-theme/__snapshots__/NavLink.spec.js.snap
deleted file mode 100644
index 4b9cd02ccc..0000000000
--- a/test/default-theme/__snapshots__/NavLink.spec.js.snap
+++ /dev/null
@@ -1,19 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`NavLink $i18n - renders nav link with external link 1`] = `
-
- Vue
- outbound-link
-
-`;
-
-exports[`NavLink $i18n - renders nav link with internal link 1`] = `VuePress `;
-
-exports[`NavLink $simple - renders nav link with external link 1`] = `
-
- Vue
- outbound-link
-
-`;
-
-exports[`NavLink $simple - renders nav link with internal link 1`] = `VuePress `;
diff --git a/test/docs-simple/README.md b/test/docs-simple/README.md
deleted file mode 100644
index 9e126f919b..0000000000
--- a/test/docs-simple/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Hello Simple
diff --git a/test/markdown/fragments/code-snippet-highlightLines-multiple.md b/test/markdown/fragments/code-snippet-highlightLines-multiple.md
deleted file mode 100644
index 01aef2de27..0000000000
--- a/test/markdown/fragments/code-snippet-highlightLines-multiple.md
+++ /dev/null
@@ -1 +0,0 @@
-<<< @/test/markdown/fragments/snippet.js{1-3}
diff --git a/test/markdown/fragments/code-snippet-highlightLines-single.md b/test/markdown/fragments/code-snippet-highlightLines-single.md
deleted file mode 100644
index 1916b68411..0000000000
--- a/test/markdown/fragments/code-snippet-highlightLines-single.md
+++ /dev/null
@@ -1 +0,0 @@
-<<< @/test/markdown/fragments/snippet.js{1,3}
diff --git a/test/markdown/fragments/code-snippet.md b/test/markdown/fragments/code-snippet.md
deleted file mode 100644
index 65e9148549..0000000000
--- a/test/markdown/fragments/code-snippet.md
+++ /dev/null
@@ -1 +0,0 @@
-<<< @/test/markdown/fragments/snippet.js
diff --git a/test/prepare.js b/test/prepare.js
deleted file mode 100644
index 9960d5dfb1..0000000000
--- a/test/prepare.js
+++ /dev/null
@@ -1,21 +0,0 @@
-const path = require('path')
-const fs = require('fs-extra')
-const prepare = require('../lib/prepare')
-const logger = require('../lib/util/logger')
-
-const tempPath = path.resolve(__dirname, '.temp')
-const siteDatePath = path.resolve(__dirname, '../lib/app/.temp/siteData.js')
-
-async function prepareForTest () {
- await fs.ensureDir(tempPath)
-
- await prepare(path.resolve(__dirname, '../docs'))
- await fs.copy(siteDatePath, path.join(tempPath, 'siteData-i18n.js'))
-
- await prepare(path.resolve(__dirname, 'docs-simple'))
- await fs.copy(siteDatePath, path.join(tempPath, 'siteData-simple.js'))
-}
-
-prepareForTest().then(() => {
- logger.wait('Preparing for testing ...')
-})
diff --git a/test/prepare/fixtures/docs-custom-theme/README.md b/test/prepare/fixtures/docs-custom-theme/README.md
deleted file mode 100644
index fdb4d2b582..0000000000
--- a/test/prepare/fixtures/docs-custom-theme/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Simple Docs
diff --git a/test/prepare/fixtures/docs-simple-config/README.md b/test/prepare/fixtures/docs-simple-config/README.md
deleted file mode 100644
index fdb4d2b582..0000000000
--- a/test/prepare/fixtures/docs-simple-config/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Simple Docs
diff --git a/test/prepare/fixtures/docs-simple/README.md b/test/prepare/fixtures/docs-simple/README.md
deleted file mode 100644
index fdb4d2b582..0000000000
--- a/test/prepare/fixtures/docs-simple/README.md
+++ /dev/null
@@ -1 +0,0 @@
-# Simple Docs
diff --git a/test/prepare/resolveOptions.spec.js b/test/prepare/resolveOptions.spec.js
deleted file mode 100644
index 433c565214..0000000000
--- a/test/prepare/resolveOptions.spec.js
+++ /dev/null
@@ -1,95 +0,0 @@
-import path from 'path'
-import resolveOptions from '@/prepare/resolveOptions.js'
-
-const DEFAULT_THEME_PATH = path.resolve(__dirname, '../../lib/default-theme')
-const DEFAULT_THEME_LAYOUT_PATH = path.resolve(DEFAULT_THEME_PATH, 'Layout.vue')
-const DEFAULT_THEME_NOT_FOOUND_PATH = path.resolve(DEFAULT_THEME_PATH, 'NotFound.vue')
-
-describe('prepare - resolveOptions', () => {
- test('single file docs without config.', async () => {
- const { docsDir } = getDocsPaths('docs-simple')
- const options = await resolveOptions(docsDir)
- const {
- siteConfig,
- siteData,
- sourceDir,
- outDir,
- publicPath,
- pageFiles,
- pagesData,
- themePath,
- themeLayoutPath,
- themeNotFoundPath,
- themeEnhanceAppPath,
- useDefaultTheme,
- isAlgoliaSearch,
- markdown
- } = options
- expect(siteConfig).toEqual({})
- expect(siteData).toEqual({
- title: '',
- description: '',
- base: '/',
- pages: pagesData,
- themeConfig: {},
- locales: undefined
- })
- expect(sourceDir).toBe(docsDir)
- expect(outDir).toBe(path.resolve(docsDir, '.vuepress/dist'))
- expect(publicPath).toBe('/')
- expect(pageFiles).toHaveLength(1)
- expect(pageFiles[0]).toBe('README.md')
- expect(themePath).toBe(DEFAULT_THEME_PATH)
- expect(themeLayoutPath).toBe(DEFAULT_THEME_LAYOUT_PATH)
- expect(themeNotFoundPath).toBe(DEFAULT_THEME_NOT_FOOUND_PATH)
- expect(themeEnhanceAppPath).toBe(null)
- expect(useDefaultTheme).toBe(true)
- expect(isAlgoliaSearch).toBe(false)
- expect(typeof markdown).toBe('object')
- })
-
- test('single file docs with config', async () => {
- const { docsDir, configPath } = getDocsPaths('docs-simple-config')
- const options = await resolveOptions(docsDir)
- const {
- siteConfig,
- outDir,
- publicPath
- } = options
- expect(siteConfig).toEqual(require(configPath))
- expect(siteConfig.base).toBe('vuepress')
- expect(siteConfig.dest).toBe('vuepress')
- expect(outDir).toBe(path.resolve('vuepress'))
- expect(publicPath).toBe('vuepress')
- })
-
- test('simple docs with custom theme', async () => {
- const paths = getDocsPaths('docs-custom-theme')
- const options = await resolveOptions(paths.docsDir)
- const {
- themePath,
- themeLayoutPath,
- themeNotFoundPath,
- useDefaultTheme
- } = options
- expect(useDefaultTheme).toBe(false)
- expect(themePath).toBe(paths.themePath)
- expect(themeLayoutPath).toBe(paths.themeLayoutPath)
- expect(themeNotFoundPath).toBe(DEFAULT_THEME_NOT_FOOUND_PATH) // fallbacks to default theme's NotFound component.
- })
-})
-
-function getDocsPaths (name) {
- const docsDir = path.join(__dirname, `fixtures/${name}`)
- const configPath = path.join(docsDir, '.vuepress/config.js')
- const themePath = path.join(docsDir, '.vuepress/theme')
- const themeLayoutPath = path.join(themePath, 'Layout.vue')
- const themeNotFoundPath = path.join(themePath, 'NotFound.vue')
- return {
- docsDir,
- configPath,
- themePath,
- themeLayoutPath,
- themeNotFoundPath
- }
-}
diff --git a/test/prepare/util.spec.js b/test/prepare/util.spec.js
deleted file mode 100644
index ca8e1691a4..0000000000
--- a/test/prepare/util.spec.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import {
- isIndexFile,
- fileToPath
-} from '@/prepare/util.js'
-
-describe('prepare - util', () => {
- test('isIndexFile', () => {
- [
- 'README.md',
- 'readme.md',
- 'INDEX.md',
- 'index.md',
- 'foo/README.md',
- 'foo/index.md'
- ].forEach(file => {
- expect(isIndexFile(file)).toBe(true)
- });
- [
- 'foo/one.md',
- 'one.md'
- ].forEach(file => {
- expect(isIndexFile(file)).toBe(false)
- })
- })
-
- test('fileToPath', () => {
- const asserts = {
- 'README.md': '/',
- 'foo/README.md': '/foo/',
- 'foo.md': '/foo.html',
- 'foo/bar.md': '/foo/bar.html'
- }
- Object.keys(asserts).forEach(file => {
- expect(fileToPath(file)).toBe(asserts[file])
- })
- })
-})
diff --git a/test/util.js b/test/util.js
deleted file mode 100644
index 1696c5c552..0000000000
--- a/test/util.js
+++ /dev/null
@@ -1,42 +0,0 @@
-import { createLocalVue } from '@vue/test-utils'
-import dataMixin from '@/app/dataMixin'
-import Router from 'vue-router'
-import { mockComponent } from './hoc'
-import { siteData as i18nSiteData } from './.temp/siteData-i18n'
-import { siteData as simpleSiteData } from './.temp/siteData-simple'
-
-export function getRouter () {
- return new Router()
-}
-
-export function getLocalVueByMode (mode) {
- const localVue = createLocalVue()
- localVue.use(Router)
-
- // register global component
- localVue.component('OutboundLink', mockComponent('outbound-link'))
-
- // register page component in root route.
- localVue.component(i18nSiteData.pages[0].key, mockComponent('page-component'))
- localVue.component(simpleSiteData.pages[0].key, mockComponent('page-component'))
-
- // data mixin
- if (mode === 'i18n') {
- localVue.mixin(dataMixin(i18nSiteData))
- } else {
- localVue.mixin(dataMixin(simpleSiteData))
- }
-
- return localVue
-}
-
-export function modeTestRunner (description, testFn, modes = ['simple', 'i18n']) {
- if (!Array.isArray(modes)) {
- modes = [modes]
- }
- modes.forEach(mode => {
- describe(description, () => {
- testFn(mode, getLocalVueByMode(mode))
- })
- })
-}
diff --git a/yarn.lock b/yarn.lock
index 3fa3cf59df..7a718c207d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -14,6 +14,12 @@
dependencies:
"@babel/highlight" "7.0.0-beta.47"
+"@babel/code-frame@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-rc.1.tgz#5c2154415d6c09959a71845ef519d11157e95d10"
+ dependencies:
+ "@babel/highlight" "7.0.0-rc.1"
+
"@babel/code-frame@^7.0.0-beta.35":
version "7.0.0-beta.54"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.54.tgz#0024f96fdf7028a21d68e273afd4e953214a1ead"
@@ -40,6 +46,25 @@
semver "^5.4.1"
source-map "^0.5.0"
+"@babel/core@^7.0.0-beta.47":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.0.0-rc.1.tgz#53c84fd562e13325f123d5951184eec97b958204"
+ dependencies:
+ "@babel/code-frame" "7.0.0-rc.1"
+ "@babel/generator" "7.0.0-rc.1"
+ "@babel/helpers" "7.0.0-rc.1"
+ "@babel/parser" "7.0.0-rc.1"
+ "@babel/template" "7.0.0-rc.1"
+ "@babel/traverse" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+ convert-source-map "^1.1.0"
+ debug "^3.1.0"
+ json5 "^0.5.0"
+ lodash "^4.17.10"
+ resolve "^1.3.2"
+ semver "^5.4.1"
+ source-map "^0.5.0"
+
"@babel/generator@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.44.tgz#c7e67b9b5284afcf69b309b50d7d37f3e5033d42"
@@ -60,12 +85,28 @@
source-map "^0.5.0"
trim-right "^1.0.1"
+"@babel/generator@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-rc.1.tgz#739c87d70b31aeed802bd6bc9fd51480065c45e8"
+ dependencies:
+ "@babel/types" "7.0.0-rc.1"
+ jsesc "^2.5.1"
+ lodash "^4.17.10"
+ source-map "^0.5.0"
+ trim-right "^1.0.1"
+
"@babel/helper-annotate-as-pure@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0-beta.47.tgz#354fb596055d9db369211bf075f0d5e93904d6f6"
dependencies:
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-annotate-as-pure@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0-rc.1.tgz#4a9042a4a35f835d45c649f68f364cc7ed7dcb05"
+ dependencies:
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-builder-binary-assignment-operator-visitor@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.0.0-beta.47.tgz#d5917c29ee3d68abc2c72f604bc043f6e056e907"
@@ -73,6 +114,13 @@
"@babel/helper-explode-assignable-expression" "7.0.0-beta.47"
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-builder-binary-assignment-operator-visitor@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.0.0-rc.1.tgz#df64de2375585e23a0aaa5708ea137fb21157374"
+ dependencies:
+ "@babel/helper-explode-assignable-expression" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-call-delegate@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.0.0-beta.47.tgz#96b7804397075f722a4030d3876f51ec19d8829b"
@@ -81,6 +129,14 @@
"@babel/traverse" "7.0.0-beta.47"
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-call-delegate@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-call-delegate/-/helper-call-delegate-7.0.0-rc.1.tgz#7516f71b13c81560bb91fb6b1fae3a1e0345d37d"
+ dependencies:
+ "@babel/helper-hoist-variables" "7.0.0-rc.1"
+ "@babel/traverse" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-define-map@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.0.0-beta.47.tgz#43a9def87c5166dc29630d51b3da9cc4320c131c"
@@ -89,6 +145,14 @@
"@babel/types" "7.0.0-beta.47"
lodash "^4.17.5"
+"@babel/helper-define-map@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-define-map/-/helper-define-map-7.0.0-rc.1.tgz#a7f920b33651bc540253313b336864754926e75b"
+ dependencies:
+ "@babel/helper-function-name" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+ lodash "^4.17.10"
+
"@babel/helper-explode-assignable-expression@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.0.0-beta.47.tgz#56b688e282a698f4d1cf135453a11ae8af870a19"
@@ -96,6 +160,13 @@
"@babel/traverse" "7.0.0-beta.47"
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-explode-assignable-expression@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.0.0-rc.1.tgz#114359f835a2d97161a895444e45b80317c6d765"
+ dependencies:
+ "@babel/traverse" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-function-name@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.44.tgz#e18552aaae2231100a6e485e03854bc3532d44dd"
@@ -112,6 +183,14 @@
"@babel/template" "7.0.0-beta.47"
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-function-name@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-rc.1.tgz#20b2cc836a53c669f297c8d309fc553385c5cdde"
+ dependencies:
+ "@babel/helper-get-function-arity" "7.0.0-rc.1"
+ "@babel/template" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-get-function-arity@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.44.tgz#d03ca6dd2b9f7b0b1e6b32c56c72836140db3a15"
@@ -124,18 +203,36 @@
dependencies:
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-get-function-arity@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-rc.1.tgz#60185957f72ed73766ce74c836ac574921743c46"
+ dependencies:
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-hoist-variables@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0-beta.47.tgz#ce295d1d723fe22b2820eaec748ed701aa5ae3d0"
dependencies:
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-hoist-variables@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.0.0-rc.1.tgz#6d0ff35d599fc7dd9dadaac444e99b7976238aec"
+ dependencies:
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-member-expression-to-functions@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0-beta.47.tgz#35bfcf1d16dce481ef3dec66d5a1ae6a7d80bb45"
dependencies:
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-member-expression-to-functions@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.0.0-rc.1.tgz#03a3b200fc00f8100dbcef9a351b69cfc0234b4f"
+ dependencies:
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-module-imports@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.47.tgz#5af072029ffcfbece6ffbaf5d9984c75580f3f04"
@@ -143,6 +240,13 @@
"@babel/types" "7.0.0-beta.47"
lodash "^4.17.5"
+"@babel/helper-module-imports@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.0.0-rc.1.tgz#c6269fa9dc451152895f185f0339d45f32c52e75"
+ dependencies:
+ "@babel/types" "7.0.0-rc.1"
+ lodash "^4.17.10"
+
"@babel/helper-module-transforms@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.0.0-beta.47.tgz#7eff91fc96873bd7b8d816698f1a69bbc01f3c38"
@@ -154,22 +258,49 @@
"@babel/types" "7.0.0-beta.47"
lodash "^4.17.5"
+"@babel/helper-module-transforms@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.0.0-rc.1.tgz#15aa371352a37d527b233bd22d25f709ae5feaba"
+ dependencies:
+ "@babel/helper-module-imports" "7.0.0-rc.1"
+ "@babel/helper-simple-access" "7.0.0-rc.1"
+ "@babel/helper-split-export-declaration" "7.0.0-rc.1"
+ "@babel/template" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+ lodash "^4.17.10"
+
"@babel/helper-optimise-call-expression@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0-beta.47.tgz#085d864d0613c5813c1b7c71b61bea36f195929e"
dependencies:
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-optimise-call-expression@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.0.0-rc.1.tgz#482d8251870f61d88c9800fd3e58128e14ff8c98"
+ dependencies:
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-plugin-utils@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0-beta.47.tgz#4f564117ec39f96cf60fafcde35c9ddce0e008fd"
+"@babel/helper-plugin-utils@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0-rc.1.tgz#3e277eae59818e7d4caf4174f58a7a00d441336e"
+
"@babel/helper-regex@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.0.0-beta.47.tgz#b8e3b53132c4edbb04804242c02ffe4d60316971"
dependencies:
lodash "^4.17.5"
+"@babel/helper-regex@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-regex/-/helper-regex-7.0.0-rc.1.tgz#591bf828846d91fea8c93d1bf3030bd99dbd94ce"
+ dependencies:
+ lodash "^4.17.10"
+
"@babel/helper-remap-async-to-generator@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.0.0-beta.47.tgz#444dc362f61470bd61a745ebb364431d9ca186c2"
@@ -180,6 +311,16 @@
"@babel/traverse" "7.0.0-beta.47"
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-remap-async-to-generator@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.0.0-rc.1.tgz#cc32d270ca868245d0ac0a32d70dc83a6ce77db9"
+ dependencies:
+ "@babel/helper-annotate-as-pure" "7.0.0-rc.1"
+ "@babel/helper-wrap-function" "7.0.0-rc.1"
+ "@babel/template" "7.0.0-rc.1"
+ "@babel/traverse" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-replace-supers@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.0.0-beta.47.tgz#310b206a302868a792b659455ceba27db686cbb7"
@@ -189,6 +330,15 @@
"@babel/traverse" "7.0.0-beta.47"
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-replace-supers@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.0.0-rc.1.tgz#cab8d7a6c758e4561fb285f4725c850d68c1c3db"
+ dependencies:
+ "@babel/helper-member-expression-to-functions" "7.0.0-rc.1"
+ "@babel/helper-optimise-call-expression" "7.0.0-rc.1"
+ "@babel/traverse" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-simple-access@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.0.0-beta.47.tgz#234d754acbda9251a10db697ef50181eab125042"
@@ -197,6 +347,14 @@
"@babel/types" "7.0.0-beta.47"
lodash "^4.17.5"
+"@babel/helper-simple-access@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.0.0-rc.1.tgz#ab3b179b5f009a1e17207b227c37410ad8d73949"
+ dependencies:
+ "@babel/template" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+ lodash "^4.17.10"
+
"@babel/helper-split-export-declaration@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-beta.44.tgz#c0b351735e0fbcb3822c8ad8db4e583b05ebd9dc"
@@ -209,6 +367,12 @@
dependencies:
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-split-export-declaration@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.0.0-rc.1.tgz#b00323834343fd0210f1f46c7a53521ad53efa5e"
+ dependencies:
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helper-wrap-function@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.0.0-beta.47.tgz#6528b44a3ccb4f3aeeb79add0a88192f7eb81161"
@@ -218,6 +382,15 @@
"@babel/traverse" "7.0.0-beta.47"
"@babel/types" "7.0.0-beta.47"
+"@babel/helper-wrap-function@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.0.0-rc.1.tgz#168454fe350e9ead8d91cdc581597ea506e951ff"
+ dependencies:
+ "@babel/helper-function-name" "7.0.0-rc.1"
+ "@babel/template" "7.0.0-rc.1"
+ "@babel/traverse" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/helpers@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.0.0-beta.47.tgz#f9b42ed2e4d5f75ec0fb2e792c173e451e8d40fd"
@@ -226,6 +399,14 @@
"@babel/traverse" "7.0.0-beta.47"
"@babel/types" "7.0.0-beta.47"
+"@babel/helpers@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.0.0-rc.1.tgz#e59092cdf4b28026b3fc9d272e27e0ef152b4bee"
+ dependencies:
+ "@babel/template" "7.0.0-rc.1"
+ "@babel/traverse" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+
"@babel/highlight@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.44.tgz#18c94ce543916a80553edcdcf681890b200747d5"
@@ -250,6 +431,18 @@
esutils "^2.0.2"
js-tokens "^3.0.0"
+"@babel/highlight@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-rc.1.tgz#e0ca4731fa4786f7b9500421d6ff5e5a7753e81e"
+ dependencies:
+ chalk "^2.0.0"
+ esutils "^2.0.2"
+ js-tokens "^3.0.0"
+
+"@babel/parser@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.0.0-rc.1.tgz#d009a9bba8175d7b971e30cd03535b278c44082d"
+
"@babel/plugin-proposal-async-generator-functions@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.0.0-beta.47.tgz#571142284708c5ad4ec904d9aa705461a010be53"
@@ -258,6 +451,14 @@
"@babel/helper-remap-async-to-generator" "7.0.0-beta.47"
"@babel/plugin-syntax-async-generators" "7.0.0-beta.47"
+"@babel/plugin-proposal-async-generator-functions@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.0.0-rc.1.tgz#70d4ca787485487370a82e380c39c8c233bca639"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/helper-remap-async-to-generator" "7.0.0-rc.1"
+ "@babel/plugin-syntax-async-generators" "7.0.0-rc.1"
+
"@babel/plugin-proposal-class-properties@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.0.0-beta.47.tgz#08c1a1dfc92d0f5c37b39096c6fb883e1ca4b0f5"
@@ -303,6 +504,13 @@
"@babel/helper-plugin-utils" "7.0.0-beta.47"
"@babel/plugin-syntax-object-rest-spread" "7.0.0-beta.47"
+"@babel/plugin-proposal-object-rest-spread@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0-rc.1.tgz#bc7ce898a48831fd733b251fd5ae46f986c905d8"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/plugin-syntax-object-rest-spread" "7.0.0-rc.1"
+
"@babel/plugin-proposal-optional-catch-binding@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.0.0-beta.47.tgz#8c6453919537517ea773bb8f3fceda4250795efa"
@@ -310,6 +518,13 @@
"@babel/helper-plugin-utils" "7.0.0-beta.47"
"@babel/plugin-syntax-optional-catch-binding" "7.0.0-beta.47"
+"@babel/plugin-proposal-optional-catch-binding@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.0.0-rc.1.tgz#4ee80c9e4b6feb4c0c737bd996da3ee3fb9837d2"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/plugin-syntax-optional-catch-binding" "7.0.0-rc.1"
+
"@babel/plugin-proposal-throw-expressions@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-throw-expressions/-/plugin-proposal-throw-expressions-7.0.0-beta.47.tgz#9a67f8b0852b4b0b255eff5d6d25fa436928424f"
@@ -325,12 +540,26 @@
"@babel/helper-regex" "7.0.0-beta.47"
regexpu-core "^4.1.4"
+"@babel/plugin-proposal-unicode-property-regex@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.0.0-rc.1.tgz#02d0c33839eb52c93164907fb43b36c5a4afbc6c"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/helper-regex" "7.0.0-rc.1"
+ regexpu-core "^4.2.0"
+
"@babel/plugin-syntax-async-generators@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0-beta.47.tgz#8ab94852bf348badc866af85bd852221f0961256"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-syntax-async-generators@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.0.0-rc.1.tgz#71d016f1a241d5e735b120f6cb94b8c57d53d255"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-syntax-class-properties@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.0.0-beta.47.tgz#de52bed12fd472c848e1562f57dd4a202fe27f11"
@@ -385,12 +614,24 @@
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-syntax-object-rest-spread@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0-rc.1.tgz#42032fd87fb3b18f5686a0ab957d7f6f0db26618"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-syntax-optional-catch-binding@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.0.0-beta.47.tgz#0b1c52b066aa36893c41450773a5adb904cd4024"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-syntax-optional-catch-binding@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.0.0-rc.1.tgz#c125fedf2fe59e4b510c202b1a912634d896fbb8"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-syntax-throw-expressions@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-throw-expressions/-/plugin-syntax-throw-expressions-7.0.0-beta.47.tgz#8ca197bab3534f443eecd7eb79da47e199dafaf7"
@@ -403,6 +644,12 @@
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-arrow-functions@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.0.0-rc.1.tgz#95b369e6ded8425a00464609d29e1fd017b331b0"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-async-to-generator@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.0.0-beta.47.tgz#5723816ea1e91fa313a84e6ee9cc12ff31d46610"
@@ -411,12 +658,26 @@
"@babel/helper-plugin-utils" "7.0.0-beta.47"
"@babel/helper-remap-async-to-generator" "7.0.0-beta.47"
+"@babel/plugin-transform-async-to-generator@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.0.0-rc.1.tgz#9e22abec137ded152e83c3aebb4d4fb1ad7cba59"
+ dependencies:
+ "@babel/helper-module-imports" "7.0.0-rc.1"
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/helper-remap-async-to-generator" "7.0.0-rc.1"
+
"@babel/plugin-transform-block-scoped-functions@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0-beta.47.tgz#e422278e06c797b43c45f459d83c7af9d6237002"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-block-scoped-functions@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.0.0-rc.1.tgz#1b23adf0fb3a7395f6f0596a80039cfba6516750"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-block-scoping@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.0.0-beta.47.tgz#b737cc58a81bea57efd5bda0baef9a43a25859ad"
@@ -424,6 +685,13 @@
"@babel/helper-plugin-utils" "7.0.0-beta.47"
lodash "^4.17.5"
+"@babel/plugin-transform-block-scoping@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.0.0-rc.1.tgz#1a61565131ffd1022c04f9d3bcc4bdececf17859"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ lodash "^4.17.10"
+
"@babel/plugin-transform-classes@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.0.0-beta.47.tgz#7aff9cbe7b26fd94d7a9f97fa90135ef20c93fb6"
@@ -437,18 +705,43 @@
"@babel/helper-split-export-declaration" "7.0.0-beta.47"
globals "^11.1.0"
+"@babel/plugin-transform-classes@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.0.0-rc.1.tgz#1d73cbceb4b4adca4cdad5f8f84a5c517fc0e06d"
+ dependencies:
+ "@babel/helper-annotate-as-pure" "7.0.0-rc.1"
+ "@babel/helper-define-map" "7.0.0-rc.1"
+ "@babel/helper-function-name" "7.0.0-rc.1"
+ "@babel/helper-optimise-call-expression" "7.0.0-rc.1"
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/helper-replace-supers" "7.0.0-rc.1"
+ "@babel/helper-split-export-declaration" "7.0.0-rc.1"
+ globals "^11.1.0"
+
"@babel/plugin-transform-computed-properties@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0-beta.47.tgz#56ef2a021769a2b65e90a3e12fd10b791da9f3e0"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-computed-properties@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.0.0-rc.1.tgz#767c6e54e6928de6f1f4de341cee1ec58edce1cf"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-destructuring@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.0.0-beta.47.tgz#452b607775fd1c4d10621997837189efc0a6d428"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-destructuring@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.0.0-rc.1.tgz#d72932088542ae1c11188cb36d58cd18ddd55aa8"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-dotall-regex@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.0.0-beta.47.tgz#d8da9b706d4bfc68dec9d565661f83e6e8036636"
@@ -457,12 +750,26 @@
"@babel/helper-regex" "7.0.0-beta.47"
regexpu-core "^4.1.3"
+"@babel/plugin-transform-dotall-regex@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.0.0-rc.1.tgz#3209d77c7905883482ff9d527c2f96d0db83df0a"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/helper-regex" "7.0.0-rc.1"
+ regexpu-core "^4.1.3"
+
"@babel/plugin-transform-duplicate-keys@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0-beta.47.tgz#4aabeda051ca3007e33a207db08f1a0cf9bd253b"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-duplicate-keys@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.0.0-rc.1.tgz#59d0c76877720446f83f1fbbad7c33670c5b19b9"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-exponentiation-operator@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.0.0-beta.47.tgz#930e1abf5db9f4db5b63dbf97f3581ad0be1e907"
@@ -470,12 +777,25 @@
"@babel/helper-builder-binary-assignment-operator-visitor" "7.0.0-beta.47"
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-exponentiation-operator@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.0.0-rc.1.tgz#b8a7b7862a1e3b14510ad60e496ce5b54c2220d1"
+ dependencies:
+ "@babel/helper-builder-binary-assignment-operator-visitor" "7.0.0-rc.1"
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-for-of@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0-beta.47.tgz#527d5dc24e4a4ad0fc1d0a3990d29968cb984e76"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-for-of@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.0.0-rc.1.tgz#1ad4f8986003f38db9251fb694c4f86657e9ec18"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-function-name@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.0.0-beta.47.tgz#fb443c81cc77f3206a863b730b35c8c553ce5041"
@@ -483,12 +803,25 @@
"@babel/helper-function-name" "7.0.0-beta.47"
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-function-name@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.0.0-rc.1.tgz#e61149309db0d74df4ea3a566aac7b8794520e2d"
+ dependencies:
+ "@babel/helper-function-name" "7.0.0-rc.1"
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-literals@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0-beta.47.tgz#448fad196f062163684a38f10f14e83315892e9c"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-literals@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.0.0-rc.1.tgz#314e118e99574ab5292aea92136c26e3dc8c4abb"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-modules-amd@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.0.0-beta.47.tgz#84564419b11c1be6b9fcd4c7b3a6737f2335aac4"
@@ -496,6 +829,13 @@
"@babel/helper-module-transforms" "7.0.0-beta.47"
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-modules-amd@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.0.0-rc.1.tgz#3f7d83c9ecf0bf5733748e119696cc50ae05987f"
+ dependencies:
+ "@babel/helper-module-transforms" "7.0.0-rc.1"
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-modules-commonjs@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.0.0-beta.47.tgz#dfe5c6d867aa9614e55f7616736073edb3aab887"
@@ -504,6 +844,14 @@
"@babel/helper-plugin-utils" "7.0.0-beta.47"
"@babel/helper-simple-access" "7.0.0-beta.47"
+"@babel/plugin-transform-modules-commonjs@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.0.0-rc.1.tgz#475bd3e6c3b86bb38307f715e0cbdb6cb2f431c2"
+ dependencies:
+ "@babel/helper-module-transforms" "7.0.0-rc.1"
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/helper-simple-access" "7.0.0-rc.1"
+
"@babel/plugin-transform-modules-systemjs@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.0.0-beta.47.tgz#8514dbcdfca3345abd690059e7e8544e16ecbf05"
@@ -511,6 +859,13 @@
"@babel/helper-hoist-variables" "7.0.0-beta.47"
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-modules-systemjs@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.0.0-rc.1.tgz#6aca100a57c49e2622f29f177a3e088cc50ecd2e"
+ dependencies:
+ "@babel/helper-hoist-variables" "7.0.0-rc.1"
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-modules-umd@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.0.0-beta.47.tgz#6dcfb9661fdd131b20b721044746a7a309882918"
@@ -518,12 +873,25 @@
"@babel/helper-module-transforms" "7.0.0-beta.47"
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-modules-umd@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.0.0-rc.1.tgz#1a584cb37d252de63c90030f76c3d7d3d0ea1241"
+ dependencies:
+ "@babel/helper-module-transforms" "7.0.0-rc.1"
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-new-target@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.0.0-beta.47.tgz#4b5cb7ce30d7bffa105a1f43ed07d6ae206a4155"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-new-target@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.0.0-rc.1.tgz#e5839320686b3c97b82bd24157282565503ae569"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-object-super@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.0.0-beta.47.tgz#ca8e5f326c5011c879f3a6ed749e58bd10fff05d"
@@ -531,6 +899,13 @@
"@babel/helper-plugin-utils" "7.0.0-beta.47"
"@babel/helper-replace-supers" "7.0.0-beta.47"
+"@babel/plugin-transform-object-super@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.0.0-rc.1.tgz#03ffbcce806af7546fead73cecb43c0892b809f3"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/helper-replace-supers" "7.0.0-rc.1"
+
"@babel/plugin-transform-parameters@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.0.0-beta.47.tgz#46a4236040a6552a5f165fb3ddd60368954b0ddd"
@@ -539,12 +914,26 @@
"@babel/helper-get-function-arity" "7.0.0-beta.47"
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-parameters@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.0.0-rc.1.tgz#c3f2f1fe179b58c968b3253cb412c8d83a3d5abc"
+ dependencies:
+ "@babel/helper-call-delegate" "7.0.0-rc.1"
+ "@babel/helper-get-function-arity" "7.0.0-rc.1"
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-regenerator@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0-beta.47.tgz#86500e1c404055fb98fc82b73b09bd053cacb516"
dependencies:
regenerator-transform "^0.12.3"
+"@babel/plugin-transform-regenerator@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.0.0-rc.1.tgz#8c5488ab75b7c9004d8bcf3f48a5814f946b5bb0"
+ dependencies:
+ regenerator-transform "^0.13.3"
+
"@babel/plugin-transform-runtime@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.0.0-beta.47.tgz#1700938fa8710909cbf28f7dd39f9b40688b09fd"
@@ -558,12 +947,24 @@
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-shorthand-properties@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.0.0-rc.1.tgz#21724d2199d988ffad690de8dbdce8b834a7f313"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-spread@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0-beta.47.tgz#3feadb02292ed1e9b75090d651b9df88a7ab5c50"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-spread@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.0.0-rc.1.tgz#3ad6d96f42175ecf7c03d92313fa1f5c24a69637"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-sticky-regex@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0-beta.47.tgz#c0aa347d76b5dc87d3b37ac016ada3f950605131"
@@ -571,6 +972,13 @@
"@babel/helper-plugin-utils" "7.0.0-beta.47"
"@babel/helper-regex" "7.0.0-beta.47"
+"@babel/plugin-transform-sticky-regex@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.0.0-rc.1.tgz#88079689a70d80c8e9b159572979a9c2b80f7c38"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/helper-regex" "7.0.0-rc.1"
+
"@babel/plugin-transform-template-literals@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0-beta.47.tgz#5f7b5badf64c4c5da79026aeab03001e62a6ee5f"
@@ -578,12 +986,25 @@
"@babel/helper-annotate-as-pure" "7.0.0-beta.47"
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-template-literals@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.0.0-rc.1.tgz#c22533ce23554a0d596b208158b34b9975feb9e6"
+ dependencies:
+ "@babel/helper-annotate-as-pure" "7.0.0-rc.1"
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-typeof-symbol@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0-beta.47.tgz#03c612ec09213eb386a81d5fa67c234ee4b2034c"
dependencies:
"@babel/helper-plugin-utils" "7.0.0-beta.47"
+"@babel/plugin-transform-typeof-symbol@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.0.0-rc.1.tgz#51c628dfcd2a5b6c1792b90e4f2f24b7eb993389"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+
"@babel/plugin-transform-unicode-regex@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0-beta.47.tgz#efed0b2f1dfbf28283502234a95b4be88f7fdcb6"
@@ -592,6 +1013,14 @@
"@babel/helper-regex" "7.0.0-beta.47"
regexpu-core "^4.1.3"
+"@babel/plugin-transform-unicode-regex@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.0.0-rc.1.tgz#b6c77bdb9a2823108210a174318ddd3c1ab6f3ce"
+ dependencies:
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/helper-regex" "7.0.0-rc.1"
+ regexpu-core "^4.1.3"
+
"@babel/preset-env@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.0.0-beta.47.tgz#a3dab3b5fac4de56e3510bdbcb528f1cbdedbe2d"
@@ -636,6 +1065,51 @@
invariant "^2.2.2"
semver "^5.3.0"
+"@babel/preset-env@^7.0.0-beta.47":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.0.0-rc.1.tgz#cb87a82fd3e44005219cd9f1cb3e9fdba907aae5"
+ dependencies:
+ "@babel/helper-module-imports" "7.0.0-rc.1"
+ "@babel/helper-plugin-utils" "7.0.0-rc.1"
+ "@babel/plugin-proposal-async-generator-functions" "7.0.0-rc.1"
+ "@babel/plugin-proposal-object-rest-spread" "7.0.0-rc.1"
+ "@babel/plugin-proposal-optional-catch-binding" "7.0.0-rc.1"
+ "@babel/plugin-proposal-unicode-property-regex" "7.0.0-rc.1"
+ "@babel/plugin-syntax-async-generators" "7.0.0-rc.1"
+ "@babel/plugin-syntax-object-rest-spread" "7.0.0-rc.1"
+ "@babel/plugin-syntax-optional-catch-binding" "7.0.0-rc.1"
+ "@babel/plugin-transform-arrow-functions" "7.0.0-rc.1"
+ "@babel/plugin-transform-async-to-generator" "7.0.0-rc.1"
+ "@babel/plugin-transform-block-scoped-functions" "7.0.0-rc.1"
+ "@babel/plugin-transform-block-scoping" "7.0.0-rc.1"
+ "@babel/plugin-transform-classes" "7.0.0-rc.1"
+ "@babel/plugin-transform-computed-properties" "7.0.0-rc.1"
+ "@babel/plugin-transform-destructuring" "7.0.0-rc.1"
+ "@babel/plugin-transform-dotall-regex" "7.0.0-rc.1"
+ "@babel/plugin-transform-duplicate-keys" "7.0.0-rc.1"
+ "@babel/plugin-transform-exponentiation-operator" "7.0.0-rc.1"
+ "@babel/plugin-transform-for-of" "7.0.0-rc.1"
+ "@babel/plugin-transform-function-name" "7.0.0-rc.1"
+ "@babel/plugin-transform-literals" "7.0.0-rc.1"
+ "@babel/plugin-transform-modules-amd" "7.0.0-rc.1"
+ "@babel/plugin-transform-modules-commonjs" "7.0.0-rc.1"
+ "@babel/plugin-transform-modules-systemjs" "7.0.0-rc.1"
+ "@babel/plugin-transform-modules-umd" "7.0.0-rc.1"
+ "@babel/plugin-transform-new-target" "7.0.0-rc.1"
+ "@babel/plugin-transform-object-super" "7.0.0-rc.1"
+ "@babel/plugin-transform-parameters" "7.0.0-rc.1"
+ "@babel/plugin-transform-regenerator" "7.0.0-rc.1"
+ "@babel/plugin-transform-shorthand-properties" "7.0.0-rc.1"
+ "@babel/plugin-transform-spread" "7.0.0-rc.1"
+ "@babel/plugin-transform-sticky-regex" "7.0.0-rc.1"
+ "@babel/plugin-transform-template-literals" "7.0.0-rc.1"
+ "@babel/plugin-transform-typeof-symbol" "7.0.0-rc.1"
+ "@babel/plugin-transform-unicode-regex" "7.0.0-rc.1"
+ browserslist "^3.0.0"
+ invariant "^2.2.2"
+ js-levenshtein "^1.1.3"
+ semver "^5.3.0"
+
"@babel/preset-stage-2@7.0.0-beta.47":
version "7.0.0-beta.47"
resolved "https://registry.yarnpkg.com/@babel/preset-stage-2/-/preset-stage-2-7.0.0-beta.47.tgz#deb930c44d7d6e519a33174bba121a2a630ed654"
@@ -686,6 +1160,15 @@
babylon "7.0.0-beta.47"
lodash "^4.17.5"
+"@babel/template@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-rc.1.tgz#5f9c0a481c9f22ecdb84697b3c3a34eadeeca23c"
+ dependencies:
+ "@babel/code-frame" "7.0.0-rc.1"
+ "@babel/parser" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+ lodash "^4.17.10"
+
"@babel/traverse@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.44.tgz#a970a2c45477ad18017e2e465a0606feee0d2966"
@@ -716,6 +1199,20 @@
invariant "^2.2.0"
lodash "^4.17.5"
+"@babel/traverse@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-rc.1.tgz#867b4b45ada2d51ae2d0076f1c1d5880f8557158"
+ dependencies:
+ "@babel/code-frame" "7.0.0-rc.1"
+ "@babel/generator" "7.0.0-rc.1"
+ "@babel/helper-function-name" "7.0.0-rc.1"
+ "@babel/helper-split-export-declaration" "7.0.0-rc.1"
+ "@babel/parser" "7.0.0-rc.1"
+ "@babel/types" "7.0.0-rc.1"
+ debug "^3.1.0"
+ globals "^11.1.0"
+ lodash "^4.17.10"
+
"@babel/types@7.0.0-beta.44":
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.44.tgz#6b1b164591f77dec0a0342aca995f2d046b3a757"
@@ -732,6 +1229,14 @@
lodash "^4.17.5"
to-fast-properties "^2.0.0"
+"@babel/types@7.0.0-rc.1":
+ version "7.0.0-rc.1"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-rc.1.tgz#6abf6d14ddd9fc022617e5b62e6b32f4fa6526ad"
+ dependencies:
+ esutils "^2.0.2"
+ lodash "^4.17.10"
+ to-fast-properties "^2.0.0"
+
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -800,6 +1305,14 @@
source-map "^0.5.6"
vue-template-es2015-compiler "^1.6.0"
+"@vue/conventional-changelog@^0.1.1":
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/@vue/conventional-changelog/-/conventional-changelog-0.1.1.tgz#48d2227ca65c354cba4be60754ea531afd0c3718"
+ dependencies:
+ compare-func "^1.3.2"
+ execa "^0.10.0"
+ q "^1.5.1"
+
"@vue/test-utils@^1.0.0-beta.16":
version "1.0.0-beta.21"
resolved "https://registry.yarnpkg.com/@vue/test-utils/-/test-utils-1.0.0-beta.21.tgz#fe1ee11ce16072da7ef29420df4aa5c11f4560ff"
@@ -1265,7 +1778,7 @@ async-limiter@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
-async@^1.4.0, async@^1.5.2:
+async@^1.4.0, async@^1.5.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
@@ -1390,9 +1903,9 @@ babel-helpers@^6.24.1:
babel-runtime "^6.22.0"
babel-template "^6.24.1"
-babel-jest@^23.0.0, babel-jest@^23.4.0:
- version "23.4.0"
- resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.4.0.tgz#22c34c392e2176f6a4c367992a7fcff69d2e8557"
+babel-jest@^23.4.0, babel-jest@^23.4.2:
+ version "23.4.2"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.4.2.tgz#f276de67798a5d68f2d6e87ff518c2f6e1609877"
dependencies:
babel-plugin-istanbul "^4.1.6"
babel-preset-jest "^23.2.0"
@@ -1735,6 +2248,10 @@ builtin-status-codes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
+byline@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1"
+
cacache@^10.0.4:
version "10.0.4"
resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460"
@@ -1888,6 +2405,10 @@ chardet@^0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
+chardet@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
+
chokidar@^2.0.2, chokidar@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26"
@@ -2007,6 +2528,14 @@ cliui@^2.1.0:
right-align "^0.1.1"
wordwrap "0.0.2"
+cliui@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wrap-ansi "^2.0.0"
+
cliui@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49"
@@ -2023,6 +2552,13 @@ clone@^1.0.2:
version "1.0.4"
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+cmd-shim@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/cmd-shim/-/cmd-shim-2.0.2.tgz#6fcbda99483a8fd15d7d30a196ca69d688a2efdb"
+ dependencies:
+ graceful-fs "^4.1.2"
+ mkdirp "~0.5.0"
+
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@@ -2084,12 +2620,23 @@ colors@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
+columnify@^1.5.4:
+ version "1.5.4"
+ resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb"
+ dependencies:
+ strip-ansi "^3.0.0"
+ wcwidth "^1.0.0"
+
combined-stream@1.0.6, combined-stream@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
dependencies:
delayed-stream "~1.0.0"
+command-join@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/command-join/-/command-join-2.0.0.tgz#52e8b984f4872d952ff1bdc8b98397d27c7144cf"
+
commander@2.15.x, commander@^2.14.1, commander@^2.9.0, commander@~2.15.0:
version "2.15.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f"
@@ -2110,7 +2657,7 @@ commondir@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
-compare-func@^1.3.1:
+compare-func@^1.3.1, compare-func@^1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-1.3.2.tgz#99dd0ba457e1f9bc722b12c08ec33eeab31fa648"
dependencies:
@@ -2129,7 +2676,7 @@ concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
-concat-stream@^1.5.0, concat-stream@^1.6.0:
+concat-stream@^1.4.10, concat-stream@^1.5.0, concat-stream@^1.6.0:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
dependencies:
@@ -2218,7 +2765,7 @@ conventional-changelog-atom@^0.2.8:
dependencies:
q "^1.5.1"
-conventional-changelog-cli@^1.3.22:
+conventional-changelog-cli@^1.3.13, conventional-changelog-cli@^1.3.22:
version "1.3.22"
resolved "https://registry.yarnpkg.com/conventional-changelog-cli/-/conventional-changelog-cli-1.3.22.tgz#13570fe1728f56f013ff7a88878ff49d5162a405"
dependencies:
@@ -2324,14 +2871,14 @@ conventional-changelog@^1.1.24:
conventional-changelog-jshint "^0.3.8"
conventional-changelog-preset-loader "^1.1.8"
-conventional-commits-filter@^1.1.6:
+conventional-commits-filter@^1.1.1, conventional-commits-filter@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/conventional-commits-filter/-/conventional-commits-filter-1.1.6.tgz#4389cd8e58fe89750c0b5fb58f1d7f0cc8ad3831"
dependencies:
is-subset "^0.1.1"
modify-values "^1.0.0"
-conventional-commits-parser@^2.1.7:
+conventional-commits-parser@^2.1.1, conventional-commits-parser@^2.1.7:
version "2.1.7"
resolved "https://registry.yarnpkg.com/conventional-commits-parser/-/conventional-commits-parser-2.1.7.tgz#eca45ed6140d72ba9722ee4132674d639e644e8e"
dependencies:
@@ -2343,6 +2890,18 @@ conventional-commits-parser@^2.1.7:
through2 "^2.0.0"
trim-off-newlines "^1.0.0"
+conventional-recommended-bump@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/conventional-recommended-bump/-/conventional-recommended-bump-1.2.1.tgz#1b7137efb5091f99fe009e2fe9ddb7cc490e9375"
+ dependencies:
+ concat-stream "^1.4.10"
+ conventional-commits-filter "^1.1.1"
+ conventional-commits-parser "^2.1.1"
+ git-raw-commits "^1.3.0"
+ git-semver-tags "^1.3.0"
+ meow "^3.3.0"
+ object-assign "^4.0.1"
+
convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
@@ -2449,7 +3008,7 @@ cross-spawn@^5.0.1, cross-spawn@^5.1.0:
shebang-command "^1.2.0"
which "^1.2.9"
-cross-spawn@^6.0.5:
+cross-spawn@^6.0.0, cross-spawn@^6.0.5:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
dependencies:
@@ -2699,6 +3258,12 @@ default-require-extensions@^2.0.0:
dependencies:
strip-bom "^3.0.0"
+defaults@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
+ dependencies:
+ clone "^1.0.2"
+
define-properties@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
@@ -2774,6 +3339,10 @@ detect-indent@^4.0.0:
dependencies:
repeating "^2.0.0"
+detect-indent@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d"
+
detect-libc@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
@@ -2890,6 +3459,10 @@ duplexer3@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
+duplexer@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
+
duplexify@^3.4.2, duplexify@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.0.tgz#592903f5d80b38d037220541264d69a198fb3410"
@@ -3195,6 +3768,18 @@ exec-sh@^0.2.0:
dependencies:
merge "^1.2.0"
+execa@^0.10.0:
+ version "0.10.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50"
+ dependencies:
+ cross-spawn "^6.0.0"
+ get-stream "^3.0.0"
+ is-stream "^1.1.0"
+ npm-run-path "^2.0.0"
+ p-finally "^1.0.0"
+ signal-exit "^3.0.0"
+ strip-eof "^1.0.0"
+
execa@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
@@ -3263,14 +3848,14 @@ expand-range@^1.8.1:
dependencies:
fill-range "^2.1.0"
-expect@^23.4.0:
- version "23.4.0"
- resolved "https://registry.yarnpkg.com/expect/-/expect-23.4.0.tgz#6da4ecc99c1471253e7288338983ad1ebadb60c3"
+expect@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/expect/-/expect-23.5.0.tgz#18999a0eef8f8acf99023fde766d9c323c2562ed"
dependencies:
ansi-styles "^3.2.0"
- jest-diff "^23.2.0"
+ jest-diff "^23.5.0"
jest-get-type "^22.1.0"
- jest-matcher-utils "^23.2.0"
+ jest-matcher-utils "^23.5.0"
jest-message-util "^23.4.0"
jest-regex-util "^23.3.0"
@@ -3299,6 +3884,14 @@ external-editor@^2.0.4:
iconv-lite "^0.4.17"
tmp "^0.0.33"
+external-editor@^3.0.0:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27"
+ dependencies:
+ chardet "^0.7.0"
+ iconv-lite "^0.4.24"
+ tmp "^0.0.33"
+
extglob@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
@@ -3521,7 +4114,7 @@ from2@^2.1.0:
inherits "^2.0.1"
readable-stream "^2.0.0"
-fs-extra@^4.0.2:
+fs-extra@^4.0.1, fs-extra@^4.0.2:
version "4.0.3"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94"
dependencies:
@@ -3624,7 +4217,7 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"
-git-raw-commits@^1.3.6:
+git-raw-commits@^1.3.0, git-raw-commits@^1.3.6:
version "1.3.6"
resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-1.3.6.tgz#27c35a32a67777c1ecd412a239a6c19d71b95aff"
dependencies:
@@ -3641,7 +4234,7 @@ git-remote-origin-url@^2.0.0:
gitconfiglocal "^1.0.0"
pify "^2.3.0"
-git-semver-tags@^1.3.6:
+git-semver-tags@^1.3.0, git-semver-tags@^1.3.6:
version "1.3.6"
resolved "https://registry.yarnpkg.com/git-semver-tags/-/git-semver-tags-1.3.6.tgz#357ea01f7280794fe0927f2806bee6414d2caba5"
dependencies:
@@ -3732,6 +4325,16 @@ globby@^5.0.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
+globby@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c"
+ dependencies:
+ array-union "^1.0.1"
+ glob "^7.0.3"
+ object-assign "^4.0.1"
+ pify "^2.0.0"
+ pinkie-promise "^2.0.0"
+
globby@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680"
@@ -3918,7 +4521,7 @@ home-or-tmp@^2.0.0:
os-homedir "^1.0.0"
os-tmpdir "^1.0.1"
-hosted-git-info@^2.1.4:
+hosted-git-info@^2.1.4, hosted-git-info@^2.5.0:
version "2.7.1"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047"
@@ -3991,6 +4594,12 @@ iconv-lite@^0.4.17, iconv-lite@^0.4.4:
dependencies:
safer-buffer ">= 2.1.2 < 3"
+iconv-lite@^0.4.24:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
icss-replace-symbols@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
@@ -4091,7 +4700,7 @@ ini@^1.3.2, ini@^1.3.4, ini@~1.3.0:
version "1.3.5"
resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
-inquirer@^3.0.6:
+inquirer@^3.0.6, inquirer@^3.2.2:
version "3.3.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
dependencies:
@@ -4110,7 +4719,25 @@ inquirer@^3.0.6:
strip-ansi "^4.0.0"
through "^2.3.6"
-invariant@^2.2.0, invariant@^2.2.2:
+inquirer@^6.2.0:
+ version "6.2.0"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8"
+ dependencies:
+ ansi-escapes "^3.0.0"
+ chalk "^2.0.0"
+ cli-cursor "^2.1.0"
+ cli-width "^2.0.0"
+ external-editor "^3.0.0"
+ figures "^2.0.0"
+ lodash "^4.17.10"
+ mute-stream "0.0.7"
+ run-async "^2.2.0"
+ rxjs "^6.1.0"
+ string-width "^2.1.0"
+ strip-ansi "^4.0.0"
+ through "^2.3.6"
+
+invariant@^2.2.0, invariant@^2.2.2, invariant@^2.2.4:
version "2.2.4"
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
dependencies:
@@ -4521,15 +5148,15 @@ javascript-stringify@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3"
-jest-changed-files@^23.4.0:
- version "23.4.0"
- resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-23.4.0.tgz#f1b304f98c235af5d9a31ec524262c5e4de3c6ff"
+jest-changed-files@^23.4.2:
+ version "23.4.2"
+ resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-23.4.2.tgz#1eed688370cd5eebafe4ae93d34bb3b64968fe83"
dependencies:
throat "^4.0.0"
-jest-cli@^23.4.1:
- version "23.4.1"
- resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-23.4.1.tgz#c1ffd33254caee376990aa2abe2963e0de4ca76b"
+jest-cli@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-23.5.0.tgz#d316b8e34a38a610a1efc4f0403d8ef8a55e4492"
dependencies:
ansi-escapes "^3.0.0"
chalk "^2.0.1"
@@ -4542,19 +5169,19 @@ jest-cli@^23.4.1:
istanbul-lib-coverage "^1.2.0"
istanbul-lib-instrument "^1.10.1"
istanbul-lib-source-maps "^1.2.4"
- jest-changed-files "^23.4.0"
- jest-config "^23.4.1"
+ jest-changed-files "^23.4.2"
+ jest-config "^23.5.0"
jest-environment-jsdom "^23.4.0"
jest-get-type "^22.1.0"
- jest-haste-map "^23.4.1"
+ jest-haste-map "^23.5.0"
jest-message-util "^23.4.0"
jest-regex-util "^23.3.0"
- jest-resolve-dependencies "^23.4.1"
- jest-runner "^23.4.1"
- jest-runtime "^23.4.1"
- jest-snapshot "^23.4.1"
+ jest-resolve-dependencies "^23.5.0"
+ jest-runner "^23.5.0"
+ jest-runtime "^23.5.0"
+ jest-snapshot "^23.5.0"
jest-util "^23.4.0"
- jest-validate "^23.4.0"
+ jest-validate "^23.5.0"
jest-watcher "^23.4.0"
jest-worker "^23.2.0"
micromatch "^2.3.11"
@@ -4568,32 +5195,33 @@ jest-cli@^23.4.1:
which "^1.2.12"
yargs "^11.0.0"
-jest-config@^23.4.1:
- version "23.4.1"
- resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.4.1.tgz#3172fa21f0507d7f8a088ed1dbe4157057f201e9"
+jest-config@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.5.0.tgz#3770fba03f7507ee15f3b8867c742e48f31a9773"
dependencies:
babel-core "^6.0.0"
- babel-jest "^23.4.0"
+ babel-jest "^23.4.2"
chalk "^2.0.1"
glob "^7.1.1"
jest-environment-jsdom "^23.4.0"
jest-environment-node "^23.4.0"
jest-get-type "^22.1.0"
- jest-jasmine2 "^23.4.1"
+ jest-jasmine2 "^23.5.0"
jest-regex-util "^23.3.0"
- jest-resolve "^23.4.1"
+ jest-resolve "^23.5.0"
jest-util "^23.4.0"
- jest-validate "^23.4.0"
- pretty-format "^23.2.0"
+ jest-validate "^23.5.0"
+ micromatch "^2.3.11"
+ pretty-format "^23.5.0"
-jest-diff@^23.2.0:
- version "23.2.0"
- resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-23.2.0.tgz#9f2cf4b51e12c791550200abc16b47130af1062a"
+jest-diff@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-23.5.0.tgz#250651a433dd0050290a07642946cc9baaf06fba"
dependencies:
chalk "^2.0.1"
diff "^3.2.0"
jest-get-type "^22.1.0"
- pretty-format "^23.2.0"
+ pretty-format "^23.5.0"
jest-docblock@^23.2.0:
version "23.2.0"
@@ -4601,12 +5229,12 @@ jest-docblock@^23.2.0:
dependencies:
detect-newline "^2.1.0"
-jest-each@^23.4.0:
- version "23.4.0"
- resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-23.4.0.tgz#2fa9edd89daa1a4edc9ff9bf6062a36b71345143"
+jest-each@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-23.5.0.tgz#77f7e2afe6132a80954b920006e78239862b10ba"
dependencies:
chalk "^2.0.1"
- pretty-format "^23.2.0"
+ pretty-format "^23.5.0"
jest-environment-jsdom@^23.4.0:
version "23.4.0"
@@ -4627,47 +5255,49 @@ jest-get-type@^22.1.0:
version "22.4.3"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4"
-jest-haste-map@^23.4.1:
- version "23.4.1"
- resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-23.4.1.tgz#43a174ba7ac079ae1dd74eaf5a5fe78989474dd2"
+jest-haste-map@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-23.5.0.tgz#d4ca618188bd38caa6cb20349ce6610e194a8065"
dependencies:
fb-watchman "^2.0.0"
graceful-fs "^4.1.11"
+ invariant "^2.2.4"
jest-docblock "^23.2.0"
jest-serializer "^23.0.1"
jest-worker "^23.2.0"
micromatch "^2.3.11"
sane "^2.0.0"
-jest-jasmine2@^23.4.1:
- version "23.4.1"
- resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-23.4.1.tgz#fa192262430d418e827636e4a98423e5e7ff0fce"
+jest-jasmine2@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-23.5.0.tgz#05fe7f1788e650eeb5a03929e6461ea2e9f3db53"
dependencies:
+ babel-traverse "^6.0.0"
chalk "^2.0.1"
co "^4.6.0"
- expect "^23.4.0"
+ expect "^23.5.0"
is-generator-fn "^1.0.0"
- jest-diff "^23.2.0"
- jest-each "^23.4.0"
- jest-matcher-utils "^23.2.0"
+ jest-diff "^23.5.0"
+ jest-each "^23.5.0"
+ jest-matcher-utils "^23.5.0"
jest-message-util "^23.4.0"
- jest-snapshot "^23.4.1"
+ jest-snapshot "^23.5.0"
jest-util "^23.4.0"
- pretty-format "^23.2.0"
+ pretty-format "^23.5.0"
-jest-leak-detector@^23.2.0:
- version "23.2.0"
- resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-23.2.0.tgz#c289d961dc638f14357d4ef96e0431ecc1aa377d"
+jest-leak-detector@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-23.5.0.tgz#14ac2a785bd625160a2ea968fd5d98b7dcea3e64"
dependencies:
- pretty-format "^23.2.0"
+ pretty-format "^23.5.0"
-jest-matcher-utils@^23.2.0:
- version "23.2.0"
- resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-23.2.0.tgz#4d4981f23213e939e3cedf23dc34c747b5ae1913"
+jest-matcher-utils@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-23.5.0.tgz#0e2ea67744cab78c9ab15011c4d888bdd3e49e2a"
dependencies:
chalk "^2.0.1"
jest-get-type "^22.1.0"
- pretty-format "^23.2.0"
+ pretty-format "^23.5.0"
jest-message-util@^23.4.0:
version "23.4.0"
@@ -4687,42 +5317,42 @@ jest-regex-util@^23.3.0:
version "23.3.0"
resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-23.3.0.tgz#5f86729547c2785c4002ceaa8f849fe8ca471bc5"
-jest-resolve-dependencies@^23.4.1:
- version "23.4.1"
- resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-23.4.1.tgz#a1d85247e2963f8b3859f6b0ec61b741b359378e"
+jest-resolve-dependencies@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-23.5.0.tgz#10c4d135beb9d2256de1fedc7094916c3ad74af7"
dependencies:
jest-regex-util "^23.3.0"
- jest-snapshot "^23.4.1"
+ jest-snapshot "^23.5.0"
-jest-resolve@^23.4.1:
- version "23.4.1"
- resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.4.1.tgz#7f3c17104732a2c0c940a01256025ed745814982"
+jest-resolve@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.5.0.tgz#3b8e7f67e84598f0caf63d1530bd8534a189d0e6"
dependencies:
browser-resolve "^1.11.3"
chalk "^2.0.1"
realpath-native "^1.0.0"
-jest-runner@^23.4.1:
- version "23.4.1"
- resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-23.4.1.tgz#d41fd1459b95d35d6df685f1468c09e617c8c260"
+jest-runner@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-23.5.0.tgz#570f7a044da91648b5bb9b6baacdd511076c71d7"
dependencies:
exit "^0.1.2"
graceful-fs "^4.1.11"
- jest-config "^23.4.1"
+ jest-config "^23.5.0"
jest-docblock "^23.2.0"
- jest-haste-map "^23.4.1"
- jest-jasmine2 "^23.4.1"
- jest-leak-detector "^23.2.0"
+ jest-haste-map "^23.5.0"
+ jest-jasmine2 "^23.5.0"
+ jest-leak-detector "^23.5.0"
jest-message-util "^23.4.0"
- jest-runtime "^23.4.1"
+ jest-runtime "^23.5.0"
jest-util "^23.4.0"
jest-worker "^23.2.0"
source-map-support "^0.5.6"
throat "^4.0.0"
-jest-runtime@^23.4.1:
- version "23.4.1"
- resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-23.4.1.tgz#c1822eba5eb19294debe6b25b2760d0a8c532fd1"
+jest-runtime@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-23.5.0.tgz#eb503525a196dc32f2f9974e3482d26bdf7b63ce"
dependencies:
babel-core "^6.0.0"
babel-plugin-istanbul "^4.1.6"
@@ -4731,14 +5361,14 @@ jest-runtime@^23.4.1:
exit "^0.1.2"
fast-json-stable-stringify "^2.0.0"
graceful-fs "^4.1.11"
- jest-config "^23.4.1"
- jest-haste-map "^23.4.1"
+ jest-config "^23.5.0"
+ jest-haste-map "^23.5.0"
jest-message-util "^23.4.0"
jest-regex-util "^23.3.0"
- jest-resolve "^23.4.1"
- jest-snapshot "^23.4.1"
+ jest-resolve "^23.5.0"
+ jest-snapshot "^23.5.0"
jest-util "^23.4.0"
- jest-validate "^23.4.0"
+ jest-validate "^23.5.0"
micromatch "^2.3.11"
realpath-native "^1.0.0"
slash "^1.0.0"
@@ -4756,20 +5386,19 @@ jest-serializer@^23.0.1:
version "23.0.1"
resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-23.0.1.tgz#a3776aeb311e90fe83fab9e533e85102bd164165"
-jest-snapshot@^23.4.1:
- version "23.4.1"
- resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.4.1.tgz#090de9acae927f6a3af3005bda40d912b83e9c96"
+jest-snapshot@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.5.0.tgz#cc368ebd8513e1175e2a7277f37a801b7358ae79"
dependencies:
- babel-traverse "^6.0.0"
babel-types "^6.0.0"
chalk "^2.0.1"
- jest-diff "^23.2.0"
- jest-matcher-utils "^23.2.0"
+ jest-diff "^23.5.0"
+ jest-matcher-utils "^23.5.0"
jest-message-util "^23.4.0"
- jest-resolve "^23.4.1"
+ jest-resolve "^23.5.0"
mkdirp "^0.5.1"
natural-compare "^1.4.0"
- pretty-format "^23.2.0"
+ pretty-format "^23.5.0"
semver "^5.5.0"
jest-util@^23.4.0:
@@ -4794,14 +5423,14 @@ jest-validate@^23.0.0:
leven "^2.1.0"
pretty-format "^23.0.1"
-jest-validate@^23.4.0:
- version "23.4.0"
- resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.4.0.tgz#d96eede01ef03ac909c009e9c8e455197d48c201"
+jest-validate@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.5.0.tgz#f5df8f761cf43155e1b2e21d6e9de8a2852d0231"
dependencies:
chalk "^2.0.1"
jest-get-type "^22.1.0"
leven "^2.1.0"
- pretty-format "^23.2.0"
+ pretty-format "^23.5.0"
jest-watcher@^23.4.0:
version "23.4.0"
@@ -4817,12 +5446,12 @@ jest-worker@^23.2.0:
dependencies:
merge-stream "^1.0.1"
-jest@^23.0.0:
- version "23.4.1"
- resolved "https://registry.yarnpkg.com/jest/-/jest-23.4.1.tgz#39550c72f3237f63ae1b434d8d122cdf6fa007b6"
+jest@^23.4.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/jest/-/jest-23.5.0.tgz#80de353d156ea5ea4a7332f7962ac79135fbc62e"
dependencies:
import-local "^1.0.0"
- jest-cli "^23.4.1"
+ jest-cli "^23.5.0"
joi@^11.1.1:
version "11.4.0"
@@ -4845,6 +5474,10 @@ js-beautify@^1.6.12, js-beautify@^1.6.14:
mkdirp "~0.5.0"
nopt "~3.0.1"
+js-levenshtein@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.3.tgz#3ef627df48ec8cf24bacf05c0f184ff30ef413c5"
+
js-tokens@^3.0.0, js-tokens@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
@@ -5113,6 +5746,50 @@ left-pad@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e"
+lerna@^2.11.0:
+ version "2.11.0"
+ resolved "https://registry.yarnpkg.com/lerna/-/lerna-2.11.0.tgz#89b5681e286d388dda5bbbdbbf6b84c8094eff65"
+ dependencies:
+ async "^1.5.0"
+ chalk "^2.1.0"
+ cmd-shim "^2.0.2"
+ columnify "^1.5.4"
+ command-join "^2.0.0"
+ conventional-changelog-cli "^1.3.13"
+ conventional-recommended-bump "^1.2.1"
+ dedent "^0.7.0"
+ execa "^0.8.0"
+ find-up "^2.1.0"
+ fs-extra "^4.0.1"
+ get-port "^3.2.0"
+ glob "^7.1.2"
+ glob-parent "^3.1.0"
+ globby "^6.1.0"
+ graceful-fs "^4.1.11"
+ hosted-git-info "^2.5.0"
+ inquirer "^3.2.2"
+ is-ci "^1.0.10"
+ load-json-file "^4.0.0"
+ lodash "^4.17.4"
+ minimatch "^3.0.4"
+ npmlog "^4.1.2"
+ p-finally "^1.0.0"
+ package-json "^4.0.1"
+ path-exists "^3.0.0"
+ read-cmd-shim "^1.0.1"
+ read-pkg "^3.0.0"
+ rimraf "^2.6.1"
+ safe-buffer "^5.1.1"
+ semver "^5.4.1"
+ signal-exit "^3.0.2"
+ slash "^1.0.0"
+ strong-log-transformer "^1.0.6"
+ temp-write "^3.3.0"
+ write-file-atomic "^2.3.0"
+ write-json-file "^2.2.0"
+ write-pkg "^3.1.0"
+ yargs "^8.0.2"
+
leven@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
@@ -5215,6 +5892,15 @@ load-json-file@^1.0.0:
pinkie-promise "^2.0.0"
strip-bom "^2.0.0"
+load-json-file@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8"
+ dependencies:
+ graceful-fs "^4.1.2"
+ parse-json "^2.2.0"
+ pify "^2.0.0"
+ strip-bom "^3.0.0"
+
load-json-file@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
@@ -5423,6 +6109,12 @@ markdown-it-anchor@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-5.0.2.tgz#cdd917a05b7bf92fb736a6dae3385c6d0d0fa552"
+markdown-it-chain@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/markdown-it-chain/-/markdown-it-chain-1.2.0.tgz#b11db6755a3d2ab3da89e9566fc3ac384f75fdac"
+ dependencies:
+ webpack-chain "^4.9.0"
+
markdown-it-container@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/markdown-it-container/-/markdown-it-container-2.0.0.tgz#0019b43fd02eefece2f1960a2895fba81a404695"
@@ -5468,6 +6160,10 @@ media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+medium-zoom@^0.4.0:
+ version "0.4.0"
+ resolved "http://registry.npmjs.org/medium-zoom/-/medium-zoom-0.4.0.tgz#8e13c9b754903c0c903220611af0d3cd373a4222"
+
mem@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
@@ -5660,6 +6356,10 @@ minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
+minimist@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.1.0.tgz#99df657a52574c21c9057497df742790b2b4c0de"
+
minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
@@ -5717,6 +6417,10 @@ modify-values@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022"
+moment@^2.6.0:
+ version "2.22.2"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66"
+
move-concurrently@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
@@ -5971,7 +6675,7 @@ npm-which@^3.0.1:
npm-path "^2.0.2"
which "^1.2.10"
-npmlog@^4.0.2:
+npmlog@^4.0.2, npmlog@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
dependencies:
@@ -6178,7 +6882,7 @@ p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
-package-json@^4.0.0:
+package-json@^4.0.0, package-json@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed"
dependencies:
@@ -6295,6 +6999,12 @@ path-type@^1.0.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
+path-type@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73"
+ dependencies:
+ pify "^2.0.0"
+
path-type@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
@@ -6669,13 +7379,20 @@ pretty-error@^2.0.2:
renderkid "^2.0.1"
utila "~0.4"
-pretty-format@^23.0.1, pretty-format@^23.2.0:
+pretty-format@^23.0.1:
version "23.2.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.2.0.tgz#3b0aaa63c018a53583373c1cb3a5d96cc5e83017"
dependencies:
ansi-regex "^3.0.0"
ansi-styles "^3.2.0"
+pretty-format@^23.5.0:
+ version "23.5.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.5.0.tgz#0f9601ad9da70fe690a269cd3efca732c210687c"
+ dependencies:
+ ansi-regex "^3.0.0"
+ ansi-styles "^3.2.0"
+
pretty-time@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.0.0.tgz#544784adecaa2cd7d045ff8a8f1d4791c8e06e23"
@@ -6842,6 +7559,12 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
+read-cmd-shim@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/read-cmd-shim/-/read-cmd-shim-1.0.1.tgz#2d5d157786a37c055d22077c32c53f8329e91c7b"
+ dependencies:
+ graceful-fs "^4.1.2"
+
read-pkg-up@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
@@ -6849,6 +7572,13 @@ read-pkg-up@^1.0.1:
find-up "^1.0.0"
read-pkg "^1.0.0"
+read-pkg-up@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be"
+ dependencies:
+ find-up "^2.0.0"
+ read-pkg "^2.0.0"
+
read-pkg-up@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07"
@@ -6864,6 +7594,14 @@ read-pkg@^1.0.0, read-pkg@^1.1.0:
normalize-package-data "^2.3.2"
path-type "^1.0.0"
+read-pkg@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8"
+ dependencies:
+ load-json-file "^2.0.0"
+ normalize-package-data "^2.3.2"
+ path-type "^2.0.0"
+
read-pkg@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
@@ -6962,6 +7700,12 @@ regenerator-transform@^0.12.3:
dependencies:
private "^0.1.6"
+regenerator-transform@^0.13.3:
+ version "0.13.3"
+ resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.13.3.tgz#264bd9ff38a8ce24b06e0636496b2c856b57bcbb"
+ dependencies:
+ private "^0.1.6"
+
regex-cache@^0.4.2:
version "0.4.4"
resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
@@ -6987,7 +7731,7 @@ regexpu-core@^1.0.0:
regjsgen "^0.2.0"
regjsparser "^0.1.4"
-regexpu-core@^4.1.3, regexpu-core@^4.1.4:
+regexpu-core@^4.1.3, regexpu-core@^4.1.4, regexpu-core@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.2.0.tgz#a3744fa03806cffe146dea4421a3e73bdcc47b1d"
dependencies:
@@ -6998,9 +7742,9 @@ regexpu-core@^4.1.3, regexpu-core@^4.1.4:
unicode-match-property-ecmascript "^1.0.4"
unicode-match-property-value-ecmascript "^1.0.2"
-register-service-worker@^1.5.1:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/register-service-worker/-/register-service-worker-1.5.1.tgz#4f91d8915f6019b083433b83ffe4bfd6f976f342"
+register-service-worker@^1.5.2:
+ version "1.5.2"
+ resolved "https://registry.yarnpkg.com/register-service-worker/-/register-service-worker-1.5.2.tgz#a4631896c38d6ec5597358f44988cc46a911912d"
registry-auth-token@^3.0.1:
version "3.3.2"
@@ -7277,6 +8021,10 @@ schema-utils@^0.4.0, schema-utils@^0.4.2, schema-utils@^0.4.3, schema-utils@^0.4
ajv "^6.1.0"
ajv-keywords "^3.1.0"
+scifi@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/scifi/-/scifi-0.1.4.tgz#4d514ea11e0b2a6a350a33c6c56addcc9c4e676e"
+
section-matter@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167"
@@ -7420,6 +8168,12 @@ sort-keys@^1.0.0:
dependencies:
is-plain-obj "^1.0.0"
+sort-keys@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128"
+ dependencies:
+ is-plain-obj "^1.0.0"
+
source-list-map@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085"
@@ -7693,6 +8447,16 @@ strip-json-comments@^2.0.0, strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
+strong-log-transformer@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/strong-log-transformer/-/strong-log-transformer-1.0.6.tgz#f7fb93758a69a571140181277eea0c2eb1301fa3"
+ dependencies:
+ byline "^5.0.0"
+ duplexer "^0.1.1"
+ minimist "^0.1.0"
+ moment "^2.6.0"
+ through "^2.3.4"
+
stylus-loader@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-3.0.2.tgz#27a706420b05a38e038e7cacb153578d450513c6"
@@ -7786,6 +8550,21 @@ tar@^4:
safe-buffer "^5.1.2"
yallist "^3.0.2"
+temp-dir@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/temp-dir/-/temp-dir-1.0.0.tgz#0a7c0ea26d3a39afa7e0ebea9c1fc0bc4daa011d"
+
+temp-write@^3.3.0:
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/temp-write/-/temp-write-3.4.0.tgz#8cff630fb7e9da05f047c74ce4ce4d685457d492"
+ dependencies:
+ graceful-fs "^4.1.2"
+ is-stream "^1.1.0"
+ make-dir "^1.0.0"
+ pify "^3.0.0"
+ temp-dir "^1.0.0"
+ uuid "^3.0.1"
+
tempfile@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/tempfile/-/tempfile-1.1.1.tgz#5bcc4eaecc4ab2c707d8bc11d99ccc9a2cb287f2"
@@ -7840,7 +8619,7 @@ through2@^2.0.0, through2@^2.0.2:
readable-stream "^2.1.5"
xtend "~4.0.1"
-through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3.4:
+through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@~2.3.4:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@@ -8217,7 +8996,7 @@ uuid@^2.0.1:
version "2.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
-uuid@^3.1.0:
+uuid@^3.0.1, uuid@^3.1.0:
version "3.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
@@ -8345,10 +9124,6 @@ vuepress-html-webpack-plugin@^3.2.0:
toposort "^1.0.0"
util.promisify "1.0.0"
-vuepress-theme-vue@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/vuepress-theme-vue/-/vuepress-theme-vue-1.1.0.tgz#8b109e3db8cad7b18459f4d77076808110c2bcaa"
-
w3c-hr-time@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045"
@@ -8376,6 +9151,12 @@ watchpack@^1.5.0:
graceful-fs "^4.1.2"
neo-async "^2.5.0"
+wcwidth@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
+ dependencies:
+ defaults "^1.0.3"
+
webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
@@ -8387,6 +9168,13 @@ webpack-chain@^4.6.0:
deepmerge "^1.5.2"
javascript-stringify "^1.6.0"
+webpack-chain@^4.9.0:
+ version "4.11.0"
+ resolved "https://registry.yarnpkg.com/webpack-chain/-/webpack-chain-4.11.0.tgz#41b57773d2dcdcbfd43c9df28a05b40705ae421c"
+ dependencies:
+ deepmerge "^1.5.2"
+ javascript-stringify "^1.6.0"
+
webpack-dev-middleware@^3.0.0:
version "3.1.3"
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.1.3.tgz#8b32aa43da9ae79368c1bf1183f2b6cf5e1f39ed"
@@ -8696,7 +9484,7 @@ wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
-write-file-atomic@^2.0.0, write-file-atomic@^2.1.0:
+write-file-atomic@^2.0.0, write-file-atomic@^2.1.0, write-file-atomic@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab"
dependencies:
@@ -8704,6 +9492,24 @@ write-file-atomic@^2.0.0, write-file-atomic@^2.1.0:
imurmurhash "^0.1.4"
signal-exit "^3.0.2"
+write-json-file@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-2.3.0.tgz#2b64c8a33004d54b8698c76d585a77ceb61da32f"
+ dependencies:
+ detect-indent "^5.0.0"
+ graceful-fs "^4.1.2"
+ make-dir "^1.0.0"
+ pify "^3.0.0"
+ sort-keys "^2.0.0"
+ write-file-atomic "^2.0.0"
+
+write-pkg@^3.1.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-3.2.0.tgz#0e178fe97820d389a8928bc79535dbe68c2cff21"
+ dependencies:
+ sort-keys "^2.0.0"
+ write-json-file "^2.2.0"
+
write@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757"
@@ -8757,6 +9563,12 @@ yargs-parser@^10.0.0:
dependencies:
camelcase "^4.1.0"
+yargs-parser@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9"
+ dependencies:
+ camelcase "^4.1.0"
+
yargs-parser@^9.0.2:
version "9.0.2"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077"
@@ -8780,6 +9592,24 @@ yargs@^11.0.0:
y18n "^3.2.1"
yargs-parser "^9.0.2"
+yargs@^8.0.2:
+ version "8.0.2"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360"
+ dependencies:
+ camelcase "^4.1.0"
+ cliui "^3.2.0"
+ decamelize "^1.1.1"
+ get-caller-file "^1.0.1"
+ os-locale "^2.0.0"
+ read-pkg-up "^2.0.0"
+ require-directory "^2.1.1"
+ require-main-filename "^1.0.1"
+ set-blocking "^2.0.0"
+ string-width "^2.0.0"
+ which-module "^2.0.0"
+ y18n "^3.2.1"
+ yargs-parser "^7.0.0"
+
yargs@~3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"