vuejs / vitepress

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

allow site config .extends to be an array #3136

Open jcbhmr opened 1 year ago

jcbhmr commented 1 year ago

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

problem: extending from two premade "plugin-like" presets: blog & jsdoc

// .vitepress/config.ts
export default defineConfig({
  // for blog preset
  extends: blog({ rss: true }),
  // OR
  // for jsdoc preset
  extends: jsdoc({ conf: "jsdoc.json" }),
  // how do i combine these?
})

of course, i know i could do it manually, but it'd be nice to support an array like below

Describe the solution you'd like

// .vitepress/config.ts
export default defineConfig({
  extends: [
    blog({ rss: true }),
    jsdoc({ conf: "jsdoc.json" })
  ]
})

Describe alternatives you've considered

using a vite plugin like

// .vitepress/config.ts
export default defineConfig({
  vite: {
    plugins: [
      blog({ rss: true }),
      jsdoc({ conf: "jsdoc.json" })
    ]
  }
})

problem is this doesn't get you access to the pre-normalized config and only lets you mutate the post-normalized config.vitepress.* config stuffs in the vite plugin hooks (if that's even supported by vitepress officially 😬)

// myplugin.js
return {
  name: "my-vitepress-blog-plugin",
  config(config) {
    console.log(config.vitepress.sidebar)
    // hacky mutations to the vitepress site config object here
  }
}

Additional context

i think extending a premade config is a pretty good way to go one step higher than just a vite plugin in order to configure vitepress-specific things like sidebars and such. being able to mix multiple of these base configs would be nice imo

if theres another good way to go about the pattern demo'ed above 👆 then id be happy to hear it 😊

Validations

jcbhmr commented 1 year ago

https://github.com/vuejs/vitepress/blob/c343d938fccd878134c8b9dbf2b12dce226a1c19/src/node/config.ts#L178-L187

could be something like this idk

async function resolveConfigExtends(
  config: RawConfigExports
): Promise<UserConfig> {
  const resolved = await (typeof config === 'function' ? config() : config)
  if (resolved.extends) {
    const extendsRaw = [resolved.extends].flat();
    const manyBases = await Promise.all(
      extendsRaw.map((config) => resolveConfigExtends(config))
    );
    // or reduceRight() depending on which side gets priority
    const singleBase = manyBases.reduce((prev, curr) => mergeConfig(prev, curr));
    return mergeConfig(singleBase, resolved);
  }
  return resolved
}
jcbhmr commented 1 year ago

also since mergeConfig() isn't exported by vitepress, there's no current way to even do this right now without this "extends can be an array" feature:

// this doesnt work
import { mergeConfig } from "vitepress" // mergeConfig() isnt exported by vitepress
export default defineConfig({
  extends: mergeConfig(
    blog({ rss: true }),
    jsdoc({ conf: "jsdoc.json" }),
  )
})