UNPKG

@astrojs/starlight

Version:

Build beautiful, high-performance documentation websites with Astro

174 lines (152 loc) 5.93 kB
import { z } from 'astro/zod'; import type { SchemaContext } from 'astro:content'; import { HeadConfigSchema } from './schemas/head'; import { PrevNextLinkConfigSchema } from './schemas/prevNextLink'; import { TableOfContentsSchema } from './schemas/tableOfContents'; import { BadgeConfigSchema } from './schemas/badge'; import { HeroSchema } from './schemas/hero'; import { SidebarLinkItemHTMLAttributesSchema } from './schemas/sidebar'; export { i18nSchema } from './schemas/i18n'; /** Default content collection schema for Starlight’s `docs` collection. */ const StarlightFrontmatterSchema = (context: SchemaContext) => z.object({ /** The title of the current page. Required. */ title: z.string(), /** * A short description of the current page’s content. Optional, but recommended. * A good description is 150–160 characters long and outlines the key content * of the page in a clear and engaging way. */ description: z.string().optional(), /** * Custom URL where a reader can edit this page. * Overrides the `editLink.baseUrl` global config if set. * * Can also be set to `false` to disable showing an edit link on this page. */ editUrl: z.union([z.string().url(), z.boolean()]).optional().default(true), /** Set custom `<head>` tags just for this page. */ head: HeadConfigSchema(), /** Override global table of contents configuration for this page. */ tableOfContents: TableOfContentsSchema().optional(), /** * Set the layout style for this page. * Can be `'doc'` (the default) or `'splash'` for a wider layout without any sidebars. */ template: z.enum(['doc', 'splash']).default('doc'), /** Display a hero section on this page. */ hero: HeroSchema(context).optional(), /** * The last update date of the current page. * Overrides the `lastUpdated` global config or the date generated from the Git history. */ lastUpdated: z.union([z.date(), z.boolean()]).optional(), /** * The previous navigation link configuration. * Overrides the `pagination` global config or the link text and/or URL. */ prev: PrevNextLinkConfigSchema(), /** * The next navigation link configuration. * Overrides the `pagination` global config or the link text and/or URL. */ next: PrevNextLinkConfigSchema(), sidebar: z .object({ /** * The order of this page in the navigation. * Pages are sorted by this value in ascending order. Then by slug. * If not provided, pages will be sorted alphabetically by slug. * If two pages have the same order value, they will be sorted alphabetically by slug. */ order: z.number().optional(), /** * The label for this page in the navigation. * Defaults to the page `title` if not set. */ label: z.string().optional(), /** * Prevents this page from being included in autogenerated sidebar groups. */ hidden: z.boolean().default(false), /** * Adds a badge to the sidebar link. * Can be a string or an object with a variant and text. * Variants include 'note', 'tip', 'caution', 'danger', 'success', and 'default'. * Passing only a string defaults to the 'default' variant which uses the site accent color. */ badge: BadgeConfigSchema(), /** HTML attributes to add to the sidebar link. */ attrs: SidebarLinkItemHTMLAttributesSchema(), }) .default({}), /** Display an announcement banner at the top of this page. */ banner: z .object({ /** The content of the banner. Supports HTML syntax. */ content: z.string(), }) .optional(), /** Pagefind indexing for this page - set to false to disable. */ pagefind: z.boolean().default(true), /** * Indicates that this page is a draft and will not be included in production builds. * Note that the page will still be available when running Astro in development mode. */ draft: z.boolean().default(false), }); /** Type of Starlight’s default frontmatter schema. */ type DefaultSchema = ReturnType<typeof StarlightFrontmatterSchema>; /** Plain object, union, and intersection Zod types. */ type BaseSchemaWithoutEffects = | z.AnyZodObject | z.ZodUnion<[BaseSchemaWithoutEffects, ...BaseSchemaWithoutEffects[]]> | z.ZodDiscriminatedUnion<string, z.AnyZodObject[]> | z.ZodIntersection<BaseSchemaWithoutEffects, BaseSchemaWithoutEffects>; /** Base subset of Zod types that we support passing to the `extend` option. */ type BaseSchema = BaseSchemaWithoutEffects | z.ZodEffects<BaseSchemaWithoutEffects>; /** Type that extends Starlight’s default schema with an optional, user-defined schema. */ type ExtendedSchema<T extends BaseSchema | never = never> = [T] extends [never] ? DefaultSchema : T extends BaseSchema ? z.ZodIntersection<DefaultSchema, T> : DefaultSchema; interface DocsSchemaOpts<T extends BaseSchema> { /** * Extend Starlight’s schema with additional fields. * * @example * // Extend the built-in schema with a Zod schema. * docsSchema({ * extend: z.object({ * // Add a new field to the schema. * category: z.enum(['tutorial', 'guide', 'reference']).optional(), * }), * }) * * // Use the Astro image helper. * docsSchema({ * extend: ({ image }) => { * return z.object({ * cover: image(), * }); * }, * }) */ extend?: T | ((context: SchemaContext) => T); } /** Content collection schema for Starlight’s `docs` collection. */ export function docsSchema<T extends BaseSchema | never = never>( ...args: [DocsSchemaOpts<T>?] ): (context: SchemaContext) => ExtendedSchema<T> { const [options = {}] = args; const { extend } = options; return (context: SchemaContext) => { const UserSchema = typeof extend === 'function' ? extend(context) : extend; return ( UserSchema ? StarlightFrontmatterSchema(context).and(UserSchema) : StarlightFrontmatterSchema(context) ) as ExtendedSchema<T>; }; }