Starlight plugins proposal #962
Replies: 14 comments 2 replies
-
Incredibly detailed write-up, thank you @HiDeoo! Overall this looks very much aligned with what I had imagined and I agree on all the various possibilities for enhancement and what can be left until later. Some quick notes:
|
Beta Was this translation helpful? Give feedback.
-
Thanks, glad to hear it looks like we are on the same page.
I think that's a really good idea. It's even more useful if the user want to conditionally add integrations as a plain
Definitely.
I think this could work for basic cases but if I am not missing something, this would not work for some more advanced cases, e.g. |
Beta Was this translation helpful? Give feedback.
-
starlight/packages/starlight/index.ts Lines 57 to 61 in 0c05588 Edit: Actually, I think my example there would be handled. The only issue would be multiple integrations adding clashing integrations dynamically. You’d need to rely on any one integration to be authored responsibly and check before auto-injecting an integration. |
Beta Was this translation helpful? Give feedback.
-
Yeah, that was the example I was thinking about but I guess this would be pretty rare and if this is a pattern supported by Astro, if you are an integration injecting multiple integrations, it would maybe be best practice to check if the integration is already injected. So I guess this would all make sense if this usage is supported by Astro and we could indeed move the async part in |
Beta Was this translation helpful? Give feedback.
-
Opened a PR/proposal that would unlock this: withastro/astro#8672 |
Beta Was this translation helpful? Give feedback.
-
I really like what's written here and it would perfectly work for my future use-case of a Starlight plugin system (no spoilers!) I know it's not adding much but just to be vocal that I am +1 on the suggestions and enhancements 🙌 |
Beta Was this translation helpful? Give feedback.
-
Noting that withastro/astro#8672 was merged and will be released in Astro 3.2! This will unlock:
|
Beta Was this translation helpful? Give feedback.
-
Will this also make it possible to have a custom 404 page? right now I am "patching" the plugin by hand so the call gets redirected to my own site: const patchStarlight = (instance) => {
const [sitemap, plugin, mdx] = instance;
const patched = {
...plugin,
hooks: {
...plugin.hooks,
'astro:config:setup': ({ config, injectRoute, updateConfig }) => {
const injectPatched = (route) => {
if (route.pattern === '404') {
// do not overwrite 404 page
return;
}
return injectRoute(route);
}
plugin.hooks['astro:config:setup']({ config, injectRoute: injectPatched, updateConfig });
},
},
};
return [sitemap, patched, mdx];
}; is this something the new API can help out with? |
Beta Was this translation helpful? Give feedback.
-
Ah, no that's a separate issue @spaceemotion — we need to investigate a) whether we can move to a single injected route instead of injecting 404 separately (last I checked we needed to do this because a 404 route generated from a dynamic route was ignored by Astro's Dev server) and b) if not, probably add an official config option to disable injecting the 404. |
Beta Was this translation helpful? Give feedback.
-
I've been tracking the issue silently, super excited to see this change shipped so quickly, such a great job! 🎉
Posted a bit of my initial investigation regarding the first point on Discord but did not have the time to follow up on it. Cross-posting here for reference:
|
Beta Was this translation helpful? Give feedback.
-
Adding a note while it’s top of my mind: we’ve had quite a few discussions recently about how customising Plugins are no different, but noting they have a particular issue because several plugins trying to override
|
Beta Was this translation helpful? Give feedback.
-
I would also like to have something like this. I want to create something similar to https://github.com/HiDeoo/starlight-openapi ... but only similar. The data should come from an external API and is different... but comparable. Our first step was just doing plain astro pages and dynamic routes. This technically works but I struggle to get that into the Starlight layout.
Additionally I'd like to add a special "hybrid API-document-merge approach":
I already solved this with the full astrojs power in a dynamic route page but I currently do not get it into the starlight layout. Not sure if this really fits this discussion, but the initial proposal sounds like. I hope that my use case description above helps me (astro and starlight newbie) to get some pointers how to approach this. |
Beta Was this translation helpful? Give feedback.
-
We released our first plugin API in Starlight v0.14 🎉 Docs: https://starlight.astro.build/reference/plugins/ Congrats to @HiDeoo for all the amazing work championing, designing, and building this 👏 Will leave this discussion open for now as there are other ideas here that didn’t make it into our initial API. @HiDeoo up to you if you want to keep this thread or prefer to close it and open dedicated discussions for other parts of the API. |
Beta Was this translation helpful? Give feedback.
-
As this discussion is getting fairly large, and most important APIs have already been added or are in progress to be added, I'm going to close this discussion. I think new APIs can be discussed individually in new discussions 👍 |
Beta Was this translation helpful? Give feedback.
-
A plugin system for Starlight is something that has been discussed for a while on Discord. This issue is a proposal to discuss the potential design of such a system, explain why it could be useful, provide a few examples of what it could look like, and discuss / gather feedback on different APIs that could be exposed to plugins.
Feel free to comment on this issue to share your thoughts and ideas.
Motivation
Starlight is built on top of Astro which already supports Astro integrations to add new functionalities to a project using Astro (Starlight is actually an Astro integration).
Starlight plugins are not meant to replace Astro integrations but rather build on top of them to provide additional functionalities that are specific to Starlight. A common use case that has already been requested multiple times is to be able to read the Starlight configuration from another integration to customize its behavior based on it. This is something that is not possible at the moment and that could be achieved with a plugin system.
Usage
Plugins can be added to a Starlight configuration using a new
plugins
option. This option would accept an array of plugins just like Astro integrations do.APIs
A Starlight plugin requires a
name
and aplugin
function that will be called by Starlight with an object containing various helpers that can be used to interact with Starlight. This function can also optionally return an Astro integration that will be automatically used.config
&updateConfig()
A basic plugin that would require reading & updating the Starlight configuration could be similar to starlight-typedoc which generates some Markdown pages on disk, creates some sidebar navigation items for them and updates the Starlight sidebar configuration to display them.
Show advanced example
A more advanced example could be the one of a plugin like starlight-blog that adds a blog to a Starlight websites. Imagining a world where Starlight component customization has landed, such a plugin would need to update the Starlight configuration to override the
<SocialIcons/>
component with a custom one that would add a link to the blog and the<Sidebar/>
component to use a different sidebar for the blog to list all the blog posts.It would also need to return an Astro Integration that would inject some new routes to the website to display the blog posts.
As expected, a plugin can also return a preset by returning an array of Astro integrations.
logger
I would love to be able to expose to plugins the
AstroIntegrationLogger
exposed to integrations in Astro v3:kleur
and creating a custom logger usingIntl.DateTimeFormat
to get a nice colored output matching the Astro one.logLevel
.injectContent()
For plugins that adds dynamic data/pages like starlight-typedoc, having to output files on disk into
src/content/docs/
that would either be committed or ignored by the user is not ideal.Having an API that would allow to add pages to a Starlight website would be a great alternative. This API could be something like:
Note: technically, this would not require a plugin and should be available to any Astro integration but the example is here to show how it could be used in a plugin.
injectTranslations()
With component customization being worked on and some Discord discussions regarding potentially exposing a module using a more advanced version of the existing i18n
t()
helper (potentially usingi18next
), I feel like it would be nice to expose ainjectTranslations()
function to plugins that would allow them to add translations for custom keys (they would be scoped to the plugin name to avoid conflicts).For example, internationalization support for starlight-blog is something that has already been requested, and instead of dealing with everything that would be required to support it, I think it could be nice to be able to do something like this:
and in my plugin custom component I could potentially do something like this:
injectSettings()
For plugins accepting some configuration options that needs to be read in some Astro components, the go-to solution at the moment is to mimic what Starlight is currently doing for its configuration and expose the plugin configuration through a virtual module that can be imported in the components.
This works great and this what starlight-blog is currently doing for example but this adds some complexity, requires some boilerplate and feels like something that would be repeated a lot for all plugins doing something similar. Having a
injectSettings()
function that would allow to define some settings that would be injected automatically by Starlight in the existing settings virtual module could be a nice alternative (they would be scoped to the plugin name to avoid conflicts).astro add
Plugins shipping an Astro integration should not use the
astro-integration
keyword to support automatic installation usingastro add
as this would install the plugin in the Astro configurationintegrations
array instead of the expected Starlight configurationplugins
array.At a later stage, if this is something that is wanted, we could potentially add a new
starlight-plugin
keyword to support this withastro add
but this feels like something that is not needed at the moment and could be added later.Implementation details
This section is mostly here to explain some implementation details so feel free to skip it if you are not interested.
Show implementation details
Object vs function
I decided to choose an object over only a function to define a plugin for my initial prototype as it felt like a more natural choice considering that Astro integrations are also defined as objects.
This also provides an easy way to add a name to the plugin which could be used to automatically scope translations and settings injected with
injectTranslations()
andinjectSettings()
if these APIs are added and potentially support more metadata in the future if ever needed.Architecture
The current architecture of the Starlight integration could be heavily simplified to something like:
Considering that the Starlight user configation needs to be passed down to some other Astro integrations returned by the Starlight integration factory function and that plugins can also returns Astro integrations, running the plugins cannot be done the Starlight integration
astro:config:setup
hook as it would be too late. This means that the plugins need to be run before the Starlight integration is returned.This also explains why the
AstroIntegrationLogger
cannot be exposed to plugins as it is only available in an integration hook.A simplified version of an implementation could be something like:
Considering that a plugin can be asynchronous, this means that the Starlight factory function needs to be asynchronous as well. This means that the basic Starlight setup would need to be updated to support this by awaiting the Starlight factory function.
Note that this could potentially be avoided by an upstream change in Astro to support resolving promises provided to the
integrations
array but this is not something that is currently supported.Another last thing to note with this implementation is that the read-only copy the user-supplied Starlight
config
shared with a plugin would be relying on the order of the plugins array. I guess that if we wanted to expose a copy of the configuration after all plugins have run, we would need to use a hook mechanism similar to the one used in Astro integrations.Beta Was this translation helpful? Give feedback.
All reactions