@astrojs/starlight 0.5.6 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/404.astro +17 -17
  2. package/CHANGELOG.md +50 -2
  3. package/components/CallToAction.astro +32 -35
  4. package/components/ContentPanel.astro +18 -18
  5. package/components/EditLink.astro +23 -23
  6. package/components/FallbackContentNotice.astro +16 -18
  7. package/components/Footer.astro +28 -34
  8. package/components/HeadSEO.astro +69 -75
  9. package/components/Header.astro +47 -51
  10. package/components/Hero.astro +108 -121
  11. package/components/Icons.ts +81 -71
  12. package/components/LanguageSelect.astro +31 -31
  13. package/components/LastUpdated.astro +16 -18
  14. package/components/MarkdownContent.astro +105 -117
  15. package/components/MobileMenuToggle.astro +80 -80
  16. package/components/PrevNextLinks.astro +60 -60
  17. package/components/RightSidebar.astro +17 -17
  18. package/components/RightSidebarPanel.astro +41 -41
  19. package/components/Search.astro +293 -306
  20. package/components/Select.astro +64 -65
  21. package/components/Sidebar.astro +23 -23
  22. package/components/SidebarSublist.astro +96 -95
  23. package/components/SiteTitle.astro +65 -63
  24. package/components/SkipLink.astro +17 -17
  25. package/components/SocialIcons.astro +40 -34
  26. package/components/TableOfContents/MobileTableOfContents.astro +124 -124
  27. package/components/TableOfContents/TableOfContentsList.astro +67 -69
  28. package/components/TableOfContents/generateToC.ts +41 -43
  29. package/components/TableOfContents/starlight-toc.ts +84 -90
  30. package/components/TableOfContents.astro +8 -8
  31. package/components/ThemeProvider.astro +28 -32
  32. package/components/ThemeSelect.astro +66 -71
  33. package/global.d.ts +3 -3
  34. package/index.astro +1 -1
  35. package/index.ts +52 -59
  36. package/integrations/asides.ts +109 -111
  37. package/integrations/sitemap.ts +10 -13
  38. package/integrations/virtual-user-config.ts +37 -37
  39. package/layout/Page.astro +84 -72
  40. package/layout/PageFrame.astro +67 -69
  41. package/layout/TwoColumnContent.astro +40 -42
  42. package/package.json +2 -2
  43. package/schema.ts +126 -113
  44. package/schemas/favicon.ts +40 -0
  45. package/schemas/head.ts +12 -23
  46. package/schemas/i18n.ts +146 -160
  47. package/schemas/logo.ts +22 -22
  48. package/schemas/prevNextLink.ts +14 -14
  49. package/schemas/tableOfContents.ts +14 -18
  50. package/style/asides.css +27 -27
  51. package/style/props.css +168 -172
  52. package/style/reset.css +13 -13
  53. package/style/shiki.css +11 -11
  54. package/style/util.css +30 -30
  55. package/translations/fr.json +21 -21
  56. package/translations/index.ts +4 -4
  57. package/translations/it.json +20 -20
  58. package/translations/tr.json +1 -1
  59. package/translations/zh.json +0 -1
  60. package/user-components/Card.astro +50 -54
  61. package/user-components/CardGrid.astro +21 -21
  62. package/user-components/Icon.astro +19 -21
  63. package/user-components/TabItem.astro +3 -3
  64. package/user-components/Tabs.astro +113 -117
  65. package/user-components/rehype-tabs.ts +72 -72
  66. package/utils/base.ts +6 -6
  67. package/utils/git.ts +55 -55
  68. package/utils/head.ts +56 -54
  69. package/utils/i18n.ts +3 -3
  70. package/utils/localizedUrl.ts +25 -25
  71. package/utils/navigation.ts +225 -203
  72. package/utils/routing.ts +74 -85
  73. package/utils/slugs.ts +41 -51
  74. package/utils/translations.ts +26 -32
  75. package/utils/user-config.ts +282 -278
  76. package/virtual.d.ts +8 -8
@@ -3,320 +3,324 @@ import { parse as bcpParse, stringify as bcpStringify } from 'bcp-47';
3
3
  import { HeadConfigSchema } from '../schemas/head';
4
4
  import { LogoConfigSchema } from '../schemas/logo';
5
5
  import { TableOfContentsSchema } from '../schemas/tableOfContents';
6
+ import { FaviconSchema } from '../schemas/favicon';
6
7
 
7
8
  const LocaleSchema = z.object({
8
- /** The label for this language to show in UI, e.g. `"English"`, `"العربية"`, or `"简体中文"`. */
9
- label: z
10
- .string()
11
- .describe(
12
- 'The label for this language to show in UI, e.g. `"English"`, `"العربية"`, or `"简体中文"`.'
13
- ),
14
- /** The BCP-47 tag for this language, e.g. `"en"`, `"ar"`, or `"zh-CN"`. */
15
- lang: z
16
- .string()
17
- .optional()
18
- .describe(
19
- 'The BCP-47 tag for this language, e.g. `"en"`, `"ar"`, or `"zh-CN"`.'
20
- ),
21
- /** The writing direction of this language; `"ltr"` for left-to-right (the default) or `"rtl"` for right-to-left. */
22
- dir: z
23
- .enum(['rtl', 'ltr'])
24
- .optional()
25
- .default('ltr')
26
- .describe(
27
- 'The writing direction of this language; `"ltr"` for left-to-right (the default) or `"rtl"` for right-to-left.'
28
- ),
9
+ /** The label for this language to show in UI, e.g. `"English"`, `"العربية"`, or `"简体中文"`. */
10
+ label: z
11
+ .string()
12
+ .describe(
13
+ 'The label for this language to show in UI, e.g. `"English"`, `"العربية"`, or `"简体中文"`.'
14
+ ),
15
+ /** The BCP-47 tag for this language, e.g. `"en"`, `"ar"`, or `"zh-CN"`. */
16
+ lang: z
17
+ .string()
18
+ .optional()
19
+ .describe('The BCP-47 tag for this language, e.g. `"en"`, `"ar"`, or `"zh-CN"`.'),
20
+ /** The writing direction of this language; `"ltr"` for left-to-right (the default) or `"rtl"` for right-to-left. */
21
+ dir: z
22
+ .enum(['rtl', 'ltr'])
23
+ .optional()
24
+ .default('ltr')
25
+ .describe(
26
+ 'The writing direction of this language; `"ltr"` for left-to-right (the default) or `"rtl"` for right-to-left.'
27
+ ),
29
28
  });
30
29
 
31
30
  const SidebarBaseSchema = z.object({
32
- /** The visible label for this item in the sidebar. */
33
- label: z.string(),
34
- /** Translations of the `label` for each supported language. */
35
- translations: z.record(z.string()).default({}),
31
+ /** The visible label for this item in the sidebar. */
32
+ label: z.string(),
33
+ /** Translations of the `label` for each supported language. */
34
+ translations: z.record(z.string()).default({}),
36
35
  });
37
36
 
38
37
  const SidebarGroupSchema = SidebarBaseSchema.extend({
39
- /** Whether this item should be collapsed by default. */
40
- collapsed: z.boolean().default(false),
38
+ /** Whether this item should be collapsed by default. */
39
+ collapsed: z.boolean().default(false),
41
40
  });
42
41
 
43
42
  const SidebarLinkItemSchema = SidebarBaseSchema.extend({
44
- /** The link to this item’s content. Can be a relative link to local files or the full URL of an external page. */
45
- link: z.string(),
43
+ /** The link to this item’s content. Can be a relative link to local files or the full URL of an external page. */
44
+ link: z.string(),
46
45
  });
47
46
  export type SidebarLinkItem = z.infer<typeof SidebarLinkItemSchema>;
48
47
 
49
48
  const AutoSidebarGroupSchema = SidebarGroupSchema.extend({
50
- /** Enable autogenerating a sidebar category from a specific docs directory. */
51
- autogenerate: z.object({
52
- /** The directory to generate sidebar items for. */
53
- directory: z.string(),
54
- /**
55
- * Whether the autogenerated subgroups should be collapsed by default.
56
- * Defaults to the `AutoSidebarGroup` `collapsed` value.
57
- */
58
- collapsed: z.boolean().optional(),
59
- // TODO: not supported by Docusaurus but would be good to have
60
- /** How many directories deep to include from this directory in the sidebar. Default: `Infinity`. */
61
- // depth: z.number().optional(),
62
- }),
49
+ /** Enable autogenerating a sidebar category from a specific docs directory. */
50
+ autogenerate: z.object({
51
+ /** The directory to generate sidebar items for. */
52
+ directory: z.string(),
53
+ /**
54
+ * Whether the autogenerated subgroups should be collapsed by default.
55
+ * Defaults to the `AutoSidebarGroup` `collapsed` value.
56
+ */
57
+ collapsed: z.boolean().optional(),
58
+ // TODO: not supported by Docusaurus but would be good to have
59
+ /** How many directories deep to include from this directory in the sidebar. Default: `Infinity`. */
60
+ // depth: z.number().optional(),
61
+ }),
63
62
  });
64
63
  export type AutoSidebarGroup = z.infer<typeof AutoSidebarGroupSchema>;
65
64
 
66
65
  type ManualSidebarGroupInput = z.input<typeof SidebarGroupSchema> & {
67
- /** Array of links and subcategories to display in this category. */
68
- items: Array<
69
- | z.input<typeof SidebarLinkItemSchema>
70
- | z.input<typeof AutoSidebarGroupSchema>
71
- | ManualSidebarGroupInput
72
- >;
66
+ /** Array of links and subcategories to display in this category. */
67
+ items: Array<
68
+ | z.input<typeof SidebarLinkItemSchema>
69
+ | z.input<typeof AutoSidebarGroupSchema>
70
+ | ManualSidebarGroupInput
71
+ >;
73
72
  };
74
73
 
75
74
  type ManualSidebarGroupOutput = z.output<typeof SidebarGroupSchema> & {
76
- /** Array of links and subcategories to display in this category. */
77
- items: Array<
78
- | z.output<typeof SidebarLinkItemSchema>
79
- | z.output<typeof AutoSidebarGroupSchema>
80
- | ManualSidebarGroupOutput
81
- >;
75
+ /** Array of links and subcategories to display in this category. */
76
+ items: Array<
77
+ | z.output<typeof SidebarLinkItemSchema>
78
+ | z.output<typeof AutoSidebarGroupSchema>
79
+ | ManualSidebarGroupOutput
80
+ >;
82
81
  };
83
82
 
84
83
  const ManualSidebarGroupSchema: z.ZodType<
85
- ManualSidebarGroupOutput,
86
- z.ZodTypeDef,
87
- ManualSidebarGroupInput
84
+ ManualSidebarGroupOutput,
85
+ z.ZodTypeDef,
86
+ ManualSidebarGroupInput
88
87
  > = SidebarGroupSchema.extend({
89
- /** Array of links and subcategories to display in this category. */
90
- items: z.lazy(() =>
91
- z
92
- .union([
93
- SidebarLinkItemSchema,
94
- ManualSidebarGroupSchema,
95
- AutoSidebarGroupSchema,
96
- ])
97
- .array()
98
- ),
88
+ /** Array of links and subcategories to display in this category. */
89
+ items: z.lazy(() =>
90
+ z.union([SidebarLinkItemSchema, ManualSidebarGroupSchema, AutoSidebarGroupSchema]).array()
91
+ ),
99
92
  });
100
93
 
101
94
  const SidebarItemSchema = z.union([
102
- SidebarLinkItemSchema,
103
- ManualSidebarGroupSchema,
104
- AutoSidebarGroupSchema,
95
+ SidebarLinkItemSchema,
96
+ ManualSidebarGroupSchema,
97
+ AutoSidebarGroupSchema,
105
98
  ]);
106
99
  export type SidebarItem = z.infer<typeof SidebarItemSchema>;
107
100
 
108
101
  const UserConfigSchema = z.object({
109
- /** Title for your website. Will be used in metadata and as browser tab title. */
110
- title: z
111
- .string()
112
- .describe(
113
- 'Title for your website. Will be used in metadata and as browser tab title.'
114
- ),
115
-
116
- /** Description metadata for your website. Can be used in page metadata. */
117
- description: z
118
- .string()
119
- .optional()
120
- .describe(
121
- 'Description metadata for your website. Can be used in page metadata.'
122
- ),
123
-
124
- /** Set a logo image to show in the navigation bar alongside or instead of the site title. */
125
- logo: LogoConfigSchema(),
126
-
127
- /** Optional details about the social media accounts for this site. */
128
- social: z
129
- .object({
130
- /** Link to the main Twitter profile for this site, e.g. `'https://twitter.com/astrodotbuild'`. */
131
- twitter: z.string().url().optional(),
132
- /** Link to the main Mastodon profile for this site, e.g. `'https://m.webtoo.ls/@astro'`. */
133
- mastodon: z.string().url().optional(),
134
- /** Link to the main GitHub org or repo for this site, e.g. `'https://github.com/withastro/starlight'`. */
135
- github: z.string().url().optional(),
136
- /** Link to the Discord server for this site, e.g. `'https://astro.build/chat'`. */
137
- discord: z.string().url().optional(),
138
- /** Link to the Codeberg profile or repository for this site, e.g. `'https://codeberg.org/knut/examples'`. */
139
- codeberg: z.string().url().optional(),
140
- /** Link to the Youtube channel for this site, e.g. `'https://www.youtube.com/@astrodotbuild'`. */
141
- youtube: z.string().url().optional(),
142
- /** Link to the Threads profile for this site, e.g. `'https://www.threads.net/@nmoodev'`. */
143
- threads: z.string().url().optional(),
144
- /** Link to the LinkedIn page for this site, e.g. `'https://www.linkedin.com/company/astroinc'`. */
145
- linkedin: z.string().url().optional(),
146
- /** Link to the Twitch profile or repository for this site, e.g. `'https://www.twitch.tv/bholmesdev'`. */
147
- twitch: z.string().url().optional(),
148
- })
149
- .optional(),
150
-
151
- /** The tagline for your website. */
152
- tagline: z.string().optional().describe('The tagline for your website.'),
153
-
154
- /** Configure the defaults for the table of contents on each page. */
155
- tableOfContents: TableOfContentsSchema(),
156
-
157
- /** Enable and configure “Edit this page” links. */
158
- editLink: z
159
- .object({
160
- /** Set the base URL for edit links. The final link will be `baseUrl` + the current page path. */
161
- baseUrl: z.string().url().optional(),
162
- })
163
- .optional()
164
- .default({}),
165
-
166
- /** Configure locales for internationalization (i18n). */
167
- locales: z
168
- .object({
169
- /** Configure aroot” locale to serve a default language from `/`. */
170
- root: LocaleSchema.required({ lang: true }).optional(),
171
- })
172
- .catchall(LocaleSchema)
173
- .transform((locales, ctx) => {
174
- for (const key in locales) {
175
- const locale = locales[key]!;
176
- // Fall back to the key in the locales object as the lang.
177
- let lang = locale.lang || key;
178
-
179
- // Parse the lang tag so we can check it is valid according to BCP-47.
180
- const schema = bcpParse(lang, { forgiving: true });
181
- schema.region = schema.region?.toUpperCase();
182
- const normalizedLang = bcpStringify(schema);
183
-
184
- // Error if parsing the language tag failed.
185
- if (!normalizedLang) {
186
- ctx.addIssue({
187
- code: z.ZodIssueCode.custom,
188
- message: `Could not validate language tag "${lang}" at locales.${key}.lang.`,
189
- });
190
- return z.NEVER;
191
- }
192
-
193
- // Let users know we’re modifying their configured `lang`.
194
- if (normalizedLang !== lang) {
195
- console.warn(
196
- `Warning: using "${normalizedLang}" language tag for locales.${key}.lang instead of "${lang}".`
197
- );
198
- lang = normalizedLang;
199
- }
200
-
201
- // Set the final value as the normalized lang, based on the key if needed.
202
- locale.lang = lang;
203
- }
204
- return locales;
205
- })
206
- .optional()
207
- .describe('Configure locales for internationalization (i18n).'),
208
-
209
- /**
210
- * Specify the default language for this site.
211
- *
212
- * The default locale will be used to provide fallback content where translations are missing.
213
- */
214
- defaultLocale: z.string().optional(),
215
-
216
- /** Configure your site’s sidebar navigation items. */
217
- sidebar: SidebarItemSchema.array().optional(),
218
-
219
- /**
220
- * Add extra tags to your site’s `<head>`.
221
- *
222
- * Can also be set for a single page in a page’s frontmatter.
223
- *
224
- * @example
225
- * // Add Fathom analytics to your site
226
- * starlight({
227
- * head: [
228
- * {
229
- * tag: 'script',
230
- * attrs: {
231
- * src: 'https://cdn.usefathom.com/script.js',
232
- * 'data-site': 'MY-FATHOM-ID',
233
- * defer: true,
234
- * },
235
- * },
236
- * ],
237
- * })
238
- */
239
- head: HeadConfigSchema(),
240
-
241
- /**
242
- * Provide CSS files to customize the look and feel of your Starlight site.
243
- *
244
- * Supports local CSS files relative to the root of your project,
245
- * e.g. `'/src/custom.css'`, and CSS you installed as an npm
246
- * module, e.g. `'@fontsource/roboto'`.
247
- *
248
- * @example
249
- * starlight({
250
- * customCss: ['/src/custom-styles.css', '@fontsource/roboto'],
251
- * })
252
- */
253
- customCss: z.string().array().optional().default([]),
254
-
255
- /** Define if the last update date should be visible in the page footer. */
256
- lastUpdated: z
257
- .boolean()
258
- .default(false)
259
- .describe(
260
- 'Define if the last update date should be visible in the page footer.'
261
- ),
262
-
263
- /** Define if the previous and next page links should be visible in the page footer. */
264
- pagination: z
265
- .boolean()
266
- .default(true)
267
- .describe(
268
- 'Define if the previous and next page links should be visible in the page footer.'
269
- ),
102
+ /** Title for your website. Will be used in metadata and as browser tab title. */
103
+ title: z
104
+ .string()
105
+ .describe('Title for your website. Will be used in metadata and as browser tab title.'),
106
+
107
+ /** Description metadata for your website. Can be used in page metadata. */
108
+ description: z
109
+ .string()
110
+ .optional()
111
+ .describe('Description metadata for your website. Can be used in page metadata.'),
112
+
113
+ /** Set a logo image to show in the navigation bar alongside or instead of the site title. */
114
+ logo: LogoConfigSchema(),
115
+
116
+ /**
117
+ * Optional details about the social media accounts for this site.
118
+ *
119
+ * @example
120
+ * social: {
121
+ * codeberg: 'https://codeberg.org/knut/examples',
122
+ * discord: 'https://astro.build/chat',
123
+ * github: 'https://github.com/withastro/starlight',
124
+ * gitlab: 'https://gitlab.com/delucis',
125
+ * linkedin: 'https://www.linkedin.com/company/astroinc',
126
+ * mastodon: 'https://m.webtoo.ls/@astro',
127
+ * threads: 'https://www.threads.net/@nmoodev',
128
+ * twitch: 'https://www.twitch.tv/bholmesdev',
129
+ * twitter: 'https://twitter.com/astrodotbuild',
130
+ * youtube: 'https://youtube.com/@astrodotbuild',
131
+ * }
132
+ */
133
+ social: z
134
+ .record(
135
+ z.enum([
136
+ 'twitter',
137
+ 'mastodon',
138
+ 'github',
139
+ 'gitlab',
140
+ 'bitbucket',
141
+ 'discord',
142
+ 'gitter',
143
+ 'codeberg',
144
+ 'codePen',
145
+ 'youtube',
146
+ 'threads',
147
+ 'linkedin',
148
+ 'twitch',
149
+ 'microsoftTeams',
150
+ ]),
151
+ // Link to the respective social profile for this site
152
+ z.string().url()
153
+ )
154
+ .optional(),
155
+
156
+ /** The tagline for your website. */
157
+ tagline: z.string().optional().describe('The tagline for your website.'),
158
+
159
+ /** Configure the defaults for the table of contents on each page. */
160
+ tableOfContents: TableOfContentsSchema(),
161
+
162
+ /** Enable and configure Edit this page” links. */
163
+ editLink: z
164
+ .object({
165
+ /** Set the base URL for edit links. The final link will be `baseUrl` + the current page path. */
166
+ baseUrl: z.string().url().optional(),
167
+ })
168
+ .optional()
169
+ .default({}),
170
+
171
+ /** Configure locales for internationalization (i18n). */
172
+ locales: z
173
+ .object({
174
+ /** Configure a “root” locale to serve a default language from `/`. */
175
+ root: LocaleSchema.required({ lang: true }).optional(),
176
+ })
177
+ .catchall(LocaleSchema)
178
+ .transform((locales, ctx) => {
179
+ for (const key in locales) {
180
+ const locale = locales[key]!;
181
+ // Fall back to the key in the locales object as the lang.
182
+ let lang = locale.lang || key;
183
+
184
+ // Parse the lang tag so we can check it is valid according to BCP-47.
185
+ const schema = bcpParse(lang, { forgiving: true });
186
+ schema.region = schema.region?.toUpperCase();
187
+ const normalizedLang = bcpStringify(schema);
188
+
189
+ // Error if parsing the language tag failed.
190
+ if (!normalizedLang) {
191
+ ctx.addIssue({
192
+ code: z.ZodIssueCode.custom,
193
+ message: `Could not validate language tag "${lang}" at locales.${key}.lang.`,
194
+ });
195
+ return z.NEVER;
196
+ }
197
+
198
+ // Let users know we’re modifying their configured `lang`.
199
+ if (normalizedLang !== lang) {
200
+ console.warn(
201
+ `Warning: using "${normalizedLang}" language tag for locales.${key}.lang instead of "${lang}".`
202
+ );
203
+ lang = normalizedLang;
204
+ }
205
+
206
+ // Set the final value as the normalized lang, based on the key if needed.
207
+ locale.lang = lang;
208
+ }
209
+ return locales;
210
+ })
211
+ .optional()
212
+ .describe('Configure locales for internationalization (i18n).'),
213
+
214
+ /**
215
+ * Specify the default language for this site.
216
+ *
217
+ * The default locale will be used to provide fallback content where translations are missing.
218
+ */
219
+ defaultLocale: z.string().optional(),
220
+
221
+ /** Configure your site’s sidebar navigation items. */
222
+ sidebar: SidebarItemSchema.array().optional(),
223
+
224
+ /**
225
+ * Add extra tags to your site’s `<head>`.
226
+ *
227
+ * Can also be set for a single page in a page’s frontmatter.
228
+ *
229
+ * @example
230
+ * // Add Fathom analytics to your site
231
+ * starlight({
232
+ * head: [
233
+ * {
234
+ * tag: 'script',
235
+ * attrs: {
236
+ * src: 'https://cdn.usefathom.com/script.js',
237
+ * 'data-site': 'MY-FATHOM-ID',
238
+ * defer: true,
239
+ * },
240
+ * },
241
+ * ],
242
+ * })
243
+ */
244
+ head: HeadConfigSchema(),
245
+
246
+ /**
247
+ * Provide CSS files to customize the look and feel of your Starlight site.
248
+ *
249
+ * Supports local CSS files relative to the root of your project,
250
+ * e.g. `'/src/custom.css'`, and CSS you installed as an npm
251
+ * module, e.g. `'@fontsource/roboto'`.
252
+ *
253
+ * @example
254
+ * starlight({
255
+ * customCss: ['/src/custom-styles.css', '@fontsource/roboto'],
256
+ * })
257
+ */
258
+ customCss: z.string().array().optional().default([]),
259
+
260
+ /** Define if the last update date should be visible in the page footer. */
261
+ lastUpdated: z
262
+ .boolean()
263
+ .default(false)
264
+ .describe('Define if the last update date should be visible in the page footer.'),
265
+
266
+ /** Define if the previous and next page links should be visible in the page footer. */
267
+ pagination: z
268
+ .boolean()
269
+ .default(true)
270
+ .describe('Define if the previous and next page links should be visible in the page footer.'),
271
+
272
+ /** The default favicon for your site which should be a path to an image in the `public/` directory. */
273
+ favicon: FaviconSchema(),
270
274
  });
271
275
 
272
276
  export const StarlightConfigSchema = UserConfigSchema.strict().transform(
273
- ({ locales, defaultLocale, ...config }, ctx) => {
274
- if (locales !== undefined && Object.keys(locales).length > 1) {
275
- // This is a multilingual site (more than one locale configured).
276
- // Make sure we can find the default locale and if not, help the user set it.
277
- // We treat the root locale as the default if present and no explicit default is set.
278
- const defaultLocaleConfig = locales[defaultLocale || 'root'];
279
-
280
- if (!defaultLocaleConfig) {
281
- const availableLocales = Object.keys(locales)
282
- .map((l) => `"${l}"`)
283
- .join(', ');
284
- ctx.addIssue({
285
- code: 'custom',
286
- message:
287
- 'Could not determine the default locale. ' +
288
- 'Please make sure `defaultLocale` in your Starlight config is one of ' +
289
- availableLocales,
290
- });
291
- return z.NEVER;
292
- }
293
-
294
- return {
295
- ...config,
296
- /** Flag indicating if this site has multiple locales set up. */
297
- isMultilingual: true,
298
- /** Full locale object for this site’s default language. */
299
- defaultLocale: { ...defaultLocaleConfig, locale: defaultLocale },
300
- locales,
301
- } as const;
302
- }
303
-
304
- // This is a monolingual site, so things are pretty simple.
305
- return {
306
- ...config,
307
- /** Flag indicating if this site has multiple locales set up. */
308
- isMultilingual: false,
309
- /** Full locale object for this site’s default language. */
310
- defaultLocale: {
311
- label: 'English',
312
- lang: 'en',
313
- dir: 'ltr',
314
- locale: undefined,
315
- ...locales?.root,
316
- },
317
- locales: undefined,
318
- } as const;
319
- }
277
+ ({ locales, defaultLocale, ...config }, ctx) => {
278
+ if (locales !== undefined && Object.keys(locales).length > 1) {
279
+ // This is a multilingual site (more than one locale configured).
280
+ // Make sure we can find the default locale and if not, help the user set it.
281
+ // We treat the root locale as the default if present and no explicit default is set.
282
+ const defaultLocaleConfig = locales[defaultLocale || 'root'];
283
+
284
+ if (!defaultLocaleConfig) {
285
+ const availableLocales = Object.keys(locales)
286
+ .map((l) => `"${l}"`)
287
+ .join(', ');
288
+ ctx.addIssue({
289
+ code: 'custom',
290
+ message:
291
+ 'Could not determine the default locale. ' +
292
+ 'Please make sure `defaultLocale` in your Starlight config is one of ' +
293
+ availableLocales,
294
+ });
295
+ return z.NEVER;
296
+ }
297
+
298
+ return {
299
+ ...config,
300
+ /** Flag indicating if this site has multiple locales set up. */
301
+ isMultilingual: true,
302
+ /** Full locale object for this site’s default language. */
303
+ defaultLocale: { ...defaultLocaleConfig, locale: defaultLocale },
304
+ locales,
305
+ } as const;
306
+ }
307
+
308
+ // This is a monolingual site, so things are pretty simple.
309
+ return {
310
+ ...config,
311
+ /** Flag indicating if this site has multiple locales set up. */
312
+ isMultilingual: false,
313
+ /** Full locale object for this site’s default language. */
314
+ defaultLocale: {
315
+ label: 'English',
316
+ lang: 'en',
317
+ dir: 'ltr',
318
+ locale: undefined,
319
+ ...locales?.root,
320
+ },
321
+ locales: undefined,
322
+ } as const;
323
+ }
320
324
  );
321
325
 
322
326
  export type StarlightConfig = z.infer<typeof StarlightConfigSchema>;
package/virtual.d.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  declare module 'virtual:starlight/user-config' {
2
- const Config: import('./types').StarlightConfig;
3
- export default Config;
2
+ const Config: import('./types').StarlightConfig;
3
+ export default Config;
4
4
  }
5
5
  declare module 'virtual:starlight/project-context' {
6
- export default { root: string };
6
+ export default { root: string };
7
7
  }
8
8
 
9
9
  declare module 'virtual:starlight/user-css' {}
10
10
 
11
11
  declare module 'virtual:starlight/user-images' {
12
- type ImageMetadata = import('astro').ImageMetadata;
13
- export const logos: {
14
- dark?: ImageMetadata;
15
- light?: ImageMetadata;
16
- };
12
+ type ImageMetadata = import('astro').ImageMetadata;
13
+ export const logos: {
14
+ dark?: ImageMetadata;
15
+ light?: ImageMetadata;
16
+ };
17
17
  }