diff --git a/.changeset/great-plums-study.md b/.changeset/great-plums-study.md new file mode 100644 index 0000000000..17765e1809 --- /dev/null +++ b/.changeset/great-plums-study.md @@ -0,0 +1,9 @@ +--- +'@astrojs/starlight': minor +--- + +Makes sidebar entry parsing stricter in Starlight config + +**⚠️ Potentially breaking change:** Previously Starlight would accept a sidebar entry that matched one of its expected shapes, even if it included additional properties. For example, including both `link` and `items` was considered valid, with `items` being ignored. Now, it is an error to include more than one of `link`, `items`, or `autogenerate` in a sidebar entry. + +If you see errors after updating, look for sidebar entries in the Starlight configuration in `astro.config.mjs` that include too many keys and remove the one that was previously ignored. diff --git a/packages/starlight/__tests__/basics/config-errors.test.ts b/packages/starlight/__tests__/basics/config-errors.test.ts index e48ea1a74e..1639bd72e9 100644 --- a/packages/starlight/__tests__/basics/config-errors.test.ts +++ b/packages/starlight/__tests__/basics/config-errors.test.ts @@ -165,7 +165,7 @@ test('errors with bad sidebar config', () => { Invalid config passed to starlight integration Hint: **sidebar.0**: Did not match union. - > Expected type \`{ link: string } | { items: array } | { autogenerate: object }\` + > Expected type \`{ link: string; } | { items: array; } | { autogenerate: object; }\` > Received \`{ "label": "Example", "href": "/" }\`" ` ); @@ -190,7 +190,57 @@ test('errors with bad nested sidebar config', () => { Invalid config passed to starlight integration Hint: **sidebar.0.items.1**: Did not match union. - > Expected type \`{ link: string } | { items: array } | { autogenerate: object }\` + > Expected type \`{ link: string } | { items: array; } | { autogenerate: object; }\` > Received \`{ "label": "Example", "items": [ { "label": "Nested Example 1", "link": "/" }, { "label": "Nested Example 2", "link": true } ] }\`" `); }); + +test('errors with sidebar entry that includes `link` and `items`', () => { + expect(() => + parseStarlightConfigWithFriendlyErrors({ + title: 'Test', + sidebar: [ + { label: 'Parent', link: '/parent', items: [{ label: 'Child', link: '/parent/child' }] }, + ], + }) + ).toThrowErrorMatchingInlineSnapshot(` + "[AstroUserError]: + Invalid config passed to starlight integration + Hint: + **sidebar.0**: Unrecognized key(s) in object: 'items'" + `); +}); + +test('errors with sidebar entry that includes `link` and `autogenerate`', () => { + expect(() => + parseStarlightConfigWithFriendlyErrors({ + title: 'Test', + sidebar: [{ label: 'Parent', link: '/parent', autogenerate: { directory: 'test' } }], + }) + ).toThrowErrorMatchingInlineSnapshot(` + "[AstroUserError]: + Invalid config passed to starlight integration + Hint: + **sidebar.0**: Unrecognized key(s) in object: 'autogenerate'" + `); +}); + +test('errors with sidebar entry that includes `items` and `autogenerate`', () => { + expect(() => + parseStarlightConfigWithFriendlyErrors({ + title: 'Test', + sidebar: [ + { + label: 'Parent', + items: [{ label: 'Child', link: '/parent/child' }], + autogenerate: { directory: 'test' }, + }, + ], + }) + ).toThrowErrorMatchingInlineSnapshot(` + "[AstroUserError]: + Invalid config passed to starlight integration + Hint: + **sidebar.0**: Unrecognized key(s) in object: 'autogenerate'" + `); +}); diff --git a/packages/starlight/schemas/sidebar.ts b/packages/starlight/schemas/sidebar.ts index 86eb7f87e4..41b1c3ae29 100644 --- a/packages/starlight/schemas/sidebar.ts +++ b/packages/starlight/schemas/sidebar.ts @@ -33,7 +33,7 @@ const SidebarLinkItemSchema = SidebarBaseSchema.extend({ link: z.string(), /** HTML attributes to add to the link item. */ attrs: SidebarLinkItemHTMLAttributesSchema(), -}); +}).strict(); export type SidebarLinkItem = z.infer; const AutoSidebarGroupSchema = SidebarGroupSchema.extend({ @@ -50,7 +50,7 @@ const AutoSidebarGroupSchema = SidebarGroupSchema.extend({ /** How many directories deep to include from this directory in the sidebar. Default: `Infinity`. */ // depth: z.number().optional(), }), -}); +}).strict(); export type AutoSidebarGroup = z.infer; type ManualSidebarGroupInput = z.input & { @@ -80,7 +80,7 @@ const ManualSidebarGroupSchema: z.ZodType< items: z.lazy(() => z.union([SidebarLinkItemSchema, ManualSidebarGroupSchema, AutoSidebarGroupSchema]).array() ), -}); +}).strict(); export const SidebarItemSchema = z.union([ SidebarLinkItemSchema,