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.
  2. Generate extra files before or after compilation. e.g. @vuepress/plugin-pwa
  3. Add extra pages. e.g. @vuepress/plugin-i18n-ui
  4. Inject global UI. e.g. @vuepress/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 which provides some compile-time metadata.

// #1
module.exports = {
   // ...
}
// #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:

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:

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:

module.exports = {
  plugins: [ 'xxx' ]
}

Same with:

module.exports = {
  plugins: [ 'vuepress-plugin-xxx' ]
}

This also works with Scoped Packages:

module.exports = {
  plugins: [ '@org/vuepress-plugin-xxx', '@vuepress/plugin-xxx' ]
}

Shorthand:

module.exports = {
  plugins: [ '@org/xxx', '@vuepress/xxx' ]
}

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:

module.exports = {
  plugins: [
    [
      require('./my-plugin.js'),
      { /* options */ }
    ]
  ]
}

Since this style is consistent with babel's Plugin/Preset Options, we call it Babel Style.

Object Style

VuePress also provides a simpler way to use plugins from a dependency:

module.exports = {
  plugins: {
    'xxx': { /* options */ }
  }
}

Note

The plugin can be disabled when false is explicitly passed as option.

  • Babel style
module.exports = {
  plugins: [
    [ 'xxx', false ] // disabled.
  ]
}
  • Object style
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.

// .vuepress/config.js
module.exports = {
  plugins: [
    [
      (pluginOptions, ctx) => ({
        name: 'my-xxx-plugin'
        // ... the rest of options
      })
    ]
  ]
}

plugins

  • Type: array
  • Default: undefined

A plugin can contain multiple plugins like a preset.

// your plugin
module.exports = {
  plugins: [
    'tag',
    'category'
  ]
}

enabled

  • Type: boolean
  • Default: true

Configure whether to enable this plugin. e.g. if you want to enable a plugin only in development mode:

module.exports = (options, ctx) => {
  return {
    enabled: !ctx.isProd
  }
}

chainWebpack

  • Type: Function
  • Default: undefined

Modify the internal webpack config with webpack-chain.

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:

define

  • Type: Object|Function
  • Default: undefined

Since using DefinePlugin via chainWebpack would be a little complicated:

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:
module.exports = {
  define: {
    SW_BASE_URL: '/',
  }
}
  • Function Usage:
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:

module.exports = (options, ctx) => ({
  chainWebpack (config) {
    config.resolve.alias.set('@theme', ctx.themePath)
  }
})

But alias option makes this process more like configuration:

module.exports = (options, ctx) => ({
  alias: {
    '@theme': ctx.themePath
  }
})

enhanceDevServer

  • Type: Function
  • Default: undefined

Enhance the underlying Koa app.

module.exports = {
  enhanceDevServer (app) {
    // ...
  }
}

A simple plugin to create a sub public directory is as follows:

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.base, '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 instance used to render source files. Example:

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 —— A chaining API like webpack-chain but for markdown-it.

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:

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.

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:

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.

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:

import { SOURCE_DIR } from '@dynamic/constans'

extendPageData

  • Type: Function
  • Default: undefined

A function used to extend or modify the $page object. This function will be invoking once for each page at compile time

module.exports = {
  extendPageData ($page) {
    const {
      _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
    } = $page
   
    // 1. Add extra fields.
    page.xxx = 'xxx'
    
    // 2. Change frontmatter.
    frontmatter.sidebar = 'auto'
  }
}

Note

These fields starting with an _ means you can only access them during build time.

e.g.

module.exports = {
  extendPageData ($page) {
    $page.size = ($page.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.

// plugin's entry
const path = require('path')

module.exports = {
  clientRootMixin: path.resolve(__dirname, 'mixin.js')
}
// mixin.js
export default {
  created () {},
  mounted () {}
}

additionalPages

  • Type: Array|Function
  • Default: undefined

Add a page pointing to a markdown file:

const path = require('path')

module.exports = {
  additionalPages: [
    {
     path: '/readme/',
     filePath: path.resolve(__dirname, '../../README.md')
    }
  ]
}

Add a page with explicit content:

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:

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.

module.exports = {
  globalUIComponents: [
    'Component-1',
    'Component-2'
  ]
}

Then, VuePress will automatically inject these components behind the theme container:

<div id="app">
  <div class="theme-container"> ... </div>
  <div class="global-ui">
    <Component-1/>
    <Component-2/>
</div>
</div>

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.

Note

Context of each plugin is a isolated context, they just inherit from the same app context.

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.

context.writeTemp

  • Type: Function

A utility for writing temporary files to tempPath.

Last Updated: 10/19/2018, 2:12:23 PM