import { isSatteriProcessor } from '@astrojs/markdown-satteri' import type { StarlightConfig, StarlightPlugin, StarlightUserConfig, } from '@astrojs/starlight/types' import type { AstroConfig } from 'astro' import { satteriCustomHeaderId } from 'satteri-custom-header-id' import { createShikiConfig } from './shiki-config' import type { ThemeNovaOptions } from './user-options' import { vitePluginUserConfig } from './virtual-user-config' const components = { Header: 'starlight-theme-nova/components/Header.astro', Search: 'starlight-theme-nova/components/Search.astro', ThemeProvider: 'starlight-theme-nova/components/ThemeProvider.astro', ThemeSelect: 'starlight-theme-nova/components/ThemeSelect.astro', SocialIcons: 'starlight-theme-nova/components/SocialIcons.astro', SiteTitle: 'starlight-theme-nova/components/SiteTitle.astro', PageFrame: 'starlight-theme-nova/components/PageFrame.astro', Pagination: 'starlight-theme-nova/components/Pagination.astro', MobileMenuToggle: 'starlight-theme-nova/components/MobileMenuToggle.astro', TwoColumnContent: 'starlight-theme-nova/components/TwoColumnContent.astro', MarkdownContent: 'starlight-theme-nova/components/MarkdownContent.astro', Hero: 'starlight-theme-nova/components/Hero.astro', MobileTableOfContents: 'starlight-theme-nova/components/MobileTableOfContents.astro', MobileMenuFooter: 'starlight-theme-nova/components/MobileMenuFooter.astro', LanguageSelect: 'starlight-theme-nova/components/LanguageSelect.astro', } as const satisfies Partial export type { ThemeNovaOptions } export default function starlightThemeNova( options: ThemeNovaOptions = {}, ): StarlightPlugin { return { name: 'starlight-theme-nova', hooks: { setup: async ({ config, updateConfig, addIntegration, astroConfig }) => { let useTailwind: boolean if (options.stylingSystem === 'css') { useTailwind = false } else if (options.stylingSystem === 'tailwind') { useTailwind = true } else { const hasTailwindcss = await checkHasTailwindcss( astroConfig.vite?.plugins, ) useTailwind = hasTailwindcss } const newConfig = { customCss: [ 'starlight-theme-nova/layer.css', useTailwind ? '' : 'starlight-theme-nova/tailwind.gen.css', ...(config.customCss || []), 'starlight-theme-nova/styles.css', ].filter(Boolean), components: { // Including any user components *after* our own. ...components, ...config.components, }, expressiveCode: config.expressiveCode ?? false, } satisfies Partial updateConfig(newConfig) addIntegration({ name: 'starlight-theme-nova-integration', hooks: { 'astro:config:setup': ({ config, updateConfig }) => { updateConfig({ markdown: { shikiConfig: createShikiConfig({ twoslash: true }), }, vite: { plugins: [ vitePluginUserConfig({ nav: options.nav, rootHref: astroConfig.root.href, }), ], }, }) const processor = config.markdown.processor if (processor && isSatteriProcessor(processor)) { processor.options.hastPlugins.unshift(satteriCustomHeaderId()) } }, }, }) }, }, } } type ViteUserConfig = AstroConfig['vite'] type VitePlugin = NonNullable[number] async function checkHasTailwindcss( plugin: VitePlugin | Promise, ): Promise { const awaited = await plugin if (!awaited) { return false } if (Array.isArray(awaited)) { for (const p of awaited) { if (await checkHasTailwindcss(p)) { return true } } return false } const name = awaited.name || '' return name.includes('tailwind') }