vuepress / core

Vue-Powered Static Site Generator
https://vuepress.vuejs.org
MIT License
2.17k stars 923 forks source link

[Feature request] Allow to programmatically modify the navbar #1304

Closed Radiergummi closed 1 year ago

Radiergummi commented 1 year ago

Clear and concise description of the problem

I'm working on a plugin to load pages from Notion dynamically during build. Those pages will be fetched and placed as markdown files in the sources directory. Obviously, it would be great to add those pages to the navigation bar, but that doesn't seem possible right now: There is no way to modify the theme configuration.

How is the app.addPage method intended to be used if there's no way to add the page to the navigation?

Suggested solution

It would be good to have a way to push entries onto the navbar items defined in the theme config; this will be a must-have feature for any kind of CMS integration via plugins.

Alternative

I'm currently considering to separate out the content fetching into a separate script that fetches pages, modifies the config file, and only invoke the VuePress build when that's done, but that's a horrible and brittle workaround.

Additional context

No response

Radiergummi commented 1 year ago

For anyone else struggling with this, I found a workaround, namely creating a custom theme that inherits from the default theme and modifies the theme config. As top-level await is available in more recent versions, you can await the theme module:

export default defineUserConfig( {
  // ...

  theme: await childTheme( {
    // Pass theme config and your own options
  } ),
} );

In the child theme module, you can do whatever async work you need, then patch the navbar config, and return it:

export const childTheme = async ( options: ThemeOptions ): Promise<Theme> => {
  // ...

  const { pages } = await renderer.render( options.notion );
  const additionalItems: NavbarConfig = inferNavigationItems( pages );

  options.navbar = patchNavbarConfig( options, additionalItems );

  return {
    name: 'child-theme',
    extends: defaultTheme( options ),
    // ...
  }
} );

It shouldn't be that hard, though.