vuejs / vitepress

Vite & Vue powered static site generator.
https://vitepress.dev
MIT License
12.46k stars 2.03k forks source link

Streamline extension API #3180

Open elringus opened 10 months ago

elringus commented 10 months ago

Is your feature request related to a problem? Please describe.

What is the current stance on plugins for vitepress? I'm aware that it was decided to not maintain a plugin system similar to vuepress, but how'd you generally advice authoring an extension for vitepress, which has to access both config and app context?

I'm working on a plugin that needs to inject an md parser (which is done via vitepress config) and attach a route changed listener (which is done via enhanceApp).

It's all fine as long as you're performing this one-time in your own project, but sharing such extension as a package/plugin is bothersome, as the user has to both inject it in the config and create a boilerplate theme/index.js to inject the route listener.

Unless I'm mussing something (like, maybe it's somehow possible to attach the listener via a header script specified in the config?), could you please consider exposing an enhanceApp hook in the config? This way we would be able to install vitepress plugins by overriding the config, eg:

import { defineConfig } from "vitepress";
import plugin from "plugin";

export default defineConfig(plugin({
    ...
}));

Event better would be adding a prop to the config with a (config) => config array, so it'd feel more familiar to how we inject plugins in general:

import { defineConfig } from "vitepress";
import plugin from "plugin";

export default defineConfig({
    plugins: [plugin]
});

Describe the solution you'd like

-

Describe alternatives you've considered

No response

Additional context

No response

Validations

jcbhmr commented 9 months ago

fwiw you can already kind of do this with extends: existingConfig

this works iirc: #3143

import { defineConfig, mergeConfig } from "vitepress"

function plugin1() {
  return {
    themeConfig: {
      sidebar: [
        { ... }
      ]
    }
  }
}

// plugin2 ...

export default defineConfig({
  extends: [
    plugin1({ doThing: false }),
    plugin2({ isDev: true }),
  ].reduce(mergeConfig), // as long as plugin1() and plugin2() return plain objects lol not promises or arrays

  // ... normal config; can override plugn config from extends
})

related #3136

elringus commented 9 months ago

I'm aware of this option; this request is about exposing enhanceApp to the config.

jcbhmr commented 9 months ago

ive been playing around and got this to work https://github.com/vuejs/vitepress/compare/vuejs:85bd7e8...jcbhmr:dd326de forked tarball to npm install https:///...tgz is in https://github.com/jcbhmr/vitepress/releases/tag/v1.0.0-rc.29

https://stackblitz.com/edit/vite-fmx81c?file=docs%2F.vitepress%2Fconfig.ts,docs%2F.vitepress%2Fmy-plugin.ts,docs%2F.vitepress%2Fmy-plugin-theme.ts

// config.ts
  extends: [myPlugin()],
// then
export default async function myPlugin(): UserConfig {
  return {
    additionalThemeFiles: [
      fileURLToPath(import.meta.resolve('./my-plugin-theme.ts')),
    ],
```js // then in the theme export default { enhanceApp({ app }) { // ... }, }; ```
elringus commented 8 months ago

https://github.com/vuejs/vitepress/issues/3314 would resolve this issue as well, as it'd be possible to inject scripts from vite plugin.