@astrojs/starlight
Version:
Build beautiful, high-performance documentation websites with Astro
174 lines (152 loc) • 5.94 kB
text/typescript
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({ source: 'content' }),
/** 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> = [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>(
...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>;
};
}