@astrojs/starlight 0.37.7 → 0.38.0

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 (54) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/components/ContentNotice.astro +1 -1
  3. package/components/MobileTableOfContents.astro +2 -2
  4. package/components/Page.astro +3 -3
  5. package/components/Select.astro +2 -2
  6. package/components/SocialIcons.astro +1 -1
  7. package/components/TableOfContents.astro +2 -2
  8. package/global.d.ts +9 -3
  9. package/index.ts +39 -1
  10. package/integrations/asides.ts +1 -1
  11. package/integrations/remark-rehype.ts +2 -11
  12. package/integrations/virtual-user-config.ts +31 -17
  13. package/integrations/vite-layer-order.ts +38 -35
  14. package/package.json +12 -148
  15. package/schema.ts +3 -9
  16. package/schemas/badge.ts +9 -8
  17. package/schemas/components.ts +1 -1
  18. package/schemas/expressiveCode.ts +0 -3
  19. package/schemas/favicon.ts +4 -6
  20. package/schemas/head.ts +3 -2
  21. package/schemas/hero.ts +1 -1
  22. package/schemas/i18n.ts +148 -153
  23. package/schemas/icon.ts +1 -1
  24. package/schemas/pagefind.ts +5 -4
  25. package/schemas/prevNextLink.ts +6 -8
  26. package/schemas/sidebar.ts +33 -27
  27. package/schemas/site-title.ts +4 -6
  28. package/schemas/social.ts +3 -2
  29. package/schemas/tableOfContents.ts +3 -3
  30. package/style/props.css +7 -2
  31. package/types.ts +1 -1
  32. package/user-components/Aside.astro +1 -1
  33. package/user-components/Card.astro +1 -1
  34. package/user-components/Icon.astro +1 -1
  35. package/user-components/LinkButton.astro +1 -1
  36. package/user-components/TabItem.astro +1 -1
  37. package/user-components/rehype-file-tree.ts +1 -1
  38. package/user-components/rehype-tabs.ts +1 -1
  39. package/utils/createTranslationSystem.ts +1 -1
  40. package/utils/error-map.ts +98 -57
  41. package/utils/navigation.ts +11 -10
  42. package/utils/plugins.ts +75 -40
  43. package/utils/routing/data.ts +3 -9
  44. package/utils/routing/index.ts +16 -24
  45. package/utils/routing/types.ts +5 -17
  46. package/utils/slugs.ts +11 -12
  47. package/utils/starlight-page.ts +13 -23
  48. package/utils/translations.ts +3 -4
  49. package/utils/user-config.ts +28 -65
  50. package/virtual.d.ts +6 -3
  51. /package/{components → components-internals}/Icons.ts +0 -0
  52. /package/{components → components-internals}/SidebarPersistState.ts +0 -0
  53. /package/{components → components-internals}/TableOfContents/TableOfContentsList.astro +0 -0
  54. /package/{components → components-internals}/TableOfContents/starlight-toc.ts +0 -0
package/utils/plugins.ts CHANGED
@@ -183,8 +183,8 @@ export function injectPluginTranslationsTypes(
183
183
  // https://github.com/withastro/astro/blob/910eb00fe0b70ca80bd09520ae100e8c78b675b5/packages/astro/src/core/config/schema.ts#L113
184
184
  const astroIntegrationSchema = z.object({
185
185
  name: z.string(),
186
- hooks: z.object({}).passthrough().default({}),
187
- }) as z.Schema<AstroIntegration>;
186
+ hooks: z.looseObject({}).default({}),
187
+ }) as z.ZodType<AstroIntegration, AstroIntegration>;
188
188
 
189
189
  const routeMiddlewareConfigSchema = z.object({
190
190
  entrypoint: z.string(),
@@ -196,9 +196,12 @@ const baseStarlightPluginSchema = z.object({
196
196
  name: z.string(),
197
197
  });
198
198
 
199
+ type StarlightConfigUpdate = Partial<Omit<StarlightUserConfig, 'routeMiddleware'>>;
200
+ type StarlightI18nTFactory = Awaited<ReturnType<typeof createTranslationSystemFromFs>>;
201
+
199
202
  const configSetupHookSchema = z
200
- .function(
201
- z.tuple([
203
+ .function({
204
+ input: [
202
205
  z.object({
203
206
  /**
204
207
  * A read-only copy of the user-supplied Starlight configuration.
@@ -206,9 +209,10 @@ const configSetupHookSchema = z
206
209
  * Note that this configuration may have been updated by other plugins configured
207
210
  * before this one.
208
211
  */
209
- config: z.any() as z.Schema<
212
+ config: z.any() as z.ZodType<
210
213
  // The configuration passed to plugins should contains the list of plugins.
211
- StarlightUserConfig & { plugins?: z.input<typeof baseStarlightPluginSchema>[] }
214
+ StarlightUserConfigWithPlugins,
215
+ StarlightUserConfigWithPlugins
212
216
  >,
213
217
  /**
214
218
  * A callback function to update the user-supplied Starlight configuration.
@@ -228,12 +232,15 @@ const configSetupHookSchema = z
228
232
  * }
229
233
  * }
230
234
  */
231
- updateConfig: z.function(
232
- z.tuple([
233
- z.record(z.any()) as z.Schema<Partial<Omit<StarlightUserConfig, 'routeMiddleware'>>>,
234
- ]),
235
- z.void()
236
- ),
235
+ updateConfig: z.function({
236
+ input: [
237
+ z.record(z.string(), z.any()) as z.ZodType<
238
+ StarlightConfigUpdate,
239
+ StarlightConfigUpdate
240
+ >,
241
+ ],
242
+ output: z.void(),
243
+ }),
237
244
  /**
238
245
  * A callback function to add an Astro integration required by this plugin.
239
246
  *
@@ -256,7 +263,10 @@ const configSetupHookSchema = z
256
263
  * }
257
264
  * }
258
265
  */
259
- addIntegration: z.function(z.tuple([astroIntegrationSchema]), z.void()),
266
+ addIntegration: z.function({
267
+ input: [astroIntegrationSchema],
268
+ output: z.void(),
269
+ }),
260
270
  /**
261
271
  * A callback function to register additional route middleware handlers.
262
272
  *
@@ -273,7 +283,10 @@ const configSetupHookSchema = z
273
283
  * },
274
284
  * }
275
285
  */
276
- addRouteMiddleware: z.function(z.tuple([routeMiddlewareConfigSchema]), z.void()),
286
+ addRouteMiddleware: z.function({
287
+ input: [routeMiddlewareConfigSchema],
288
+ output: z.void(),
289
+ }),
277
290
  /**
278
291
  * A read-only copy of the user-supplied Astro configuration.
279
292
  *
@@ -281,26 +294,38 @@ const configSetupHookSchema = z
281
294
  *
282
295
  * @see https://docs.astro.build/en/reference/integrations-reference/#config-option
283
296
  */
284
- astroConfig: z.any() as z.Schema<StarlightPluginContext['config']>,
297
+ astroConfig: z.any() as z.ZodType<
298
+ StarlightPluginContext['config'],
299
+ StarlightPluginContext['config']
300
+ >,
285
301
  /**
286
302
  * The command used to run Starlight.
287
303
  *
288
304
  * @see https://docs.astro.build/en/reference/integrations-reference/#command-option
289
305
  */
290
- command: z.any() as z.Schema<StarlightPluginContext['command']>,
306
+ command: z.any() as z.ZodType<
307
+ StarlightPluginContext['command'],
308
+ StarlightPluginContext['command']
309
+ >,
291
310
  /**
292
311
  * `false` when the dev server starts, `true` when a reload is triggered.
293
312
  *
294
313
  * @see https://docs.astro.build/en/reference/integrations-reference/#isrestart-option
295
314
  */
296
- isRestart: z.any() as z.Schema<StarlightPluginContext['isRestart']>,
315
+ isRestart: z.any() as z.ZodType<
316
+ StarlightPluginContext['isRestart'],
317
+ StarlightPluginContext['isRestart']
318
+ >,
297
319
  /**
298
320
  * An instance of the Astro integration logger with all logged messages prefixed with the
299
321
  * plugin name.
300
322
  *
301
323
  * @see https://docs.astro.build/en/reference/integrations-reference/#astrointegrationlogger
302
324
  */
303
- logger: z.any() as z.Schema<StarlightPluginContext['logger']>,
325
+ logger: z.any() as z.ZodType<
326
+ StarlightPluginContext['logger'],
327
+ StarlightPluginContext['logger']
328
+ >,
304
329
  /**
305
330
  * A callback function to generate a utility function to access UI strings for a given
306
331
  * language.
@@ -319,9 +344,7 @@ const configSetupHookSchema = z
319
344
  * }
320
345
  * }
321
346
  */
322
- useTranslations: z.any() as z.Schema<
323
- Awaited<ReturnType<typeof createTranslationSystemFromFs>>
324
- >,
347
+ useTranslations: z.any() as z.ZodType<StarlightI18nTFactory, StarlightI18nTFactory>,
325
348
  /**
326
349
  * A callback function to get the language for a given absolute file path. The returned
327
350
  * language can be used with the `useTranslations` helper to get UI strings for that
@@ -344,11 +367,17 @@ const configSetupHookSchema = z
344
367
  * }
345
368
  * }
346
369
  */
347
- absolutePathToLang: z.function(z.tuple([z.string()]), z.string()),
370
+ absolutePathToLang: z.function({
371
+ input: [z.string()],
372
+ output: z.string(),
373
+ }),
348
374
  }),
349
- ]),
350
- z.union([z.void(), z.promise(z.void())])
351
- )
375
+ ],
376
+ // We still rely on the deprecated `z.promise()` to define the function output as the only
377
+ // non-deprecated way to define an async function is to use `.implementAsync()` but we don't
378
+ // want to implement the function using Zod.
379
+ output: z.promise(z.void()),
380
+ })
352
381
  .optional();
353
382
 
354
383
  /**
@@ -356,8 +385,9 @@ const configSetupHookSchema = z
356
385
  * user config schema but properly typed for user convenience because we do not want to run any of
357
386
  * the Zod `transform`s used in the user config schema when running plugins.
358
387
  */
359
- const starlightPluginSchema = baseStarlightPluginSchema
360
- .extend({
388
+ const starlightPluginSchema = z
389
+ .object({
390
+ ...baseStarlightPluginSchema.shape,
361
391
  /** The different hooks available to the plugin. */
362
392
  hooks: z.object({
363
393
  /**
@@ -366,8 +396,8 @@ const starlightPluginSchema = baseStarlightPluginSchema
366
396
  * and plugin UI.
367
397
  */
368
398
  'i18n:setup': z
369
- .function(
370
- z.tuple([
399
+ .function({
400
+ input: [
371
401
  z.object({
372
402
  /**
373
403
  * A callback function to add or update translations strings.
@@ -391,14 +421,17 @@ const starlightPluginSchema = baseStarlightPluginSchema
391
421
  * }
392
422
  * }
393
423
  */
394
- injectTranslations: z.function(
395
- z.tuple([z.record(z.string(), z.record(z.string(), z.string()))]),
396
- z.void()
397
- ),
424
+ injectTranslations: z.function({
425
+ input: [z.record(z.string(), z.record(z.string(), z.string()))],
426
+ output: z.void(),
427
+ }),
398
428
  }),
399
- ]),
400
- z.union([z.void(), z.promise(z.void())])
401
- )
429
+ ],
430
+ // We still rely on the deprecated `z.promise()` to define the function output as the
431
+ // only non-deprecated way to define an async function is to use `.implementAsync()` but
432
+ // we don't want to implement the function using Zod.
433
+ output: z.promise(z.void()),
434
+ })
402
435
  .optional(),
403
436
  /**
404
437
  * Plugin configuration setup function called with an object containing various values that
@@ -414,17 +447,19 @@ const starlightPluginSchema = baseStarlightPluginSchema
414
447
  })
415
448
  .superRefine((plugin, ctx) => {
416
449
  if (!plugin.hooks['config:setup'] && !plugin.hooks.setup) {
417
- ctx.addIssue({
418
- code: z.ZodIssueCode.custom,
450
+ ctx.issues.push({
451
+ code: 'custom',
419
452
  message: 'A plugin must define at least a `config:setup` hook.',
453
+ input: plugin,
420
454
  });
421
455
  } else if (plugin.hooks['config:setup'] && plugin.hooks.setup) {
422
- ctx.addIssue({
423
- code: z.ZodIssueCode.custom,
456
+ ctx.issues.push({
457
+ code: 'custom',
424
458
  message:
425
459
  'A plugin cannot define both a `config:setup` and `setup` hook. ' +
426
460
  'As `setup` is deprecated and will be removed in a future version, ' +
427
461
  'consider using `config:setup` instead.',
462
+ input: plugin,
428
463
  });
429
464
  }
430
465
  });
@@ -6,12 +6,7 @@ import { getNewestCommitDate } from 'virtual:starlight/git-info';
6
6
  import { getPrevNextLinks, getSidebar } from '../navigation';
7
7
  import { ensureTrailingSlash } from '../path';
8
8
  import { getRouteBySlugParam, normalizeCollectionEntry } from '../routing';
9
- import type {
10
- Route,
11
- StarlightDocsCollectionEntry,
12
- StarlightDocsEntry,
13
- StarlightRouteData,
14
- } from './types';
9
+ import type { Route, StarlightDocsEntry, StarlightRouteData } from './types';
15
10
  import { formatPath } from '../format-path';
16
11
  import { useTranslations } from '../translations';
17
12
  import { BuiltInDefaultLocale } from '../i18n';
@@ -137,7 +132,6 @@ async function get404Route(locals: App.Locals): Promise<Route> {
137
132
  const entryMeta = { dir, lang, locale };
138
133
 
139
134
  const fallbackEntry: StarlightDocsEntry = {
140
- slug: '404',
141
135
  id: '404',
142
136
  body: '',
143
137
  collection: 'docs',
@@ -154,7 +148,7 @@ async function get404Route(locals: App.Locals): Promise<Route> {
154
148
  filePath: `${getCollectionPathFromRoot('docs', project)}/404.md`,
155
149
  };
156
150
 
157
- const userEntry = (await getEntry('docs', '404')) as StarlightDocsCollectionEntry;
151
+ const userEntry = await getEntry('docs', '404');
158
152
  const entry = userEntry ? normalizeCollectionEntry(userEntry) : fallbackEntry;
159
- return { ...entryMeta, entryMeta, entry, id: entry.id, slug: entry.slug };
153
+ return { ...entryMeta, entryMeta, entry, id: entry.id };
160
154
  }
@@ -1,9 +1,7 @@
1
1
  import type { GetStaticPathsItem } from 'astro';
2
2
  import { getCollection } from 'astro:content';
3
3
  import config from 'virtual:starlight/user-config';
4
- import project from 'virtual:starlight/project-context';
5
- import { getCollectionPathFromRoot } from '../collection';
6
- import { localizedId, localizedSlug, slugToLocaleData, slugToParam } from '../slugs';
4
+ import { localizedSlug, slugToLocaleData, slugToParam } from '../slugs';
7
5
  import { validateLogoImports } from '../validateLogoImports';
8
6
  import { BuiltInDefaultLocale } from '../i18n';
9
7
  import type { Route, StarlightDocsCollectionEntry, StarlightDocsEntry } from './types';
@@ -24,17 +22,14 @@ interface Path extends GetStaticPathsItem {
24
22
  */
25
23
  const normalizeIndexSlug = (slug: string) => (slug === 'index' ? '' : slug);
26
24
 
27
- /** Normalize the different collection entry we can get from a legacy collection or a loader. */
25
+ /** Normalize a collection entry. */
28
26
  export function normalizeCollectionEntry(entry: StarlightDocsCollectionEntry): StarlightDocsEntry {
29
- const slug = normalizeIndexSlug(entry.slug ?? entry.id);
30
27
  return {
31
28
  ...entry,
32
- // In a collection with a loader, the `id` is a slug and should be normalized.
33
- id: entry.slug ? entry.id : slug,
34
- // In a legacy collection, the `filePath` property doesn't exist.
35
- filePath: entry.filePath ?? `${getCollectionPathFromRoot('docs', project)}/${entry.id}`,
36
- // In a collection with a loader, the `slug` property is replaced by the `id`.
37
- slug: normalizeIndexSlug(entry.slug ?? entry.id),
29
+ // The `id` is a slug and should be normalized.
30
+ id: normalizeIndexSlug(entry.id),
31
+ // At the moment, Starlight only supports file-based loaders which always include a `filePath`.
32
+ filePath: entry.filePath!,
38
33
  };
39
34
  }
40
35
 
@@ -49,10 +44,9 @@ const docs: StarlightDocsEntry[] = (
49
44
  function getRoutes(): Route[] {
50
45
  const routes: Route[] = docs.map((entry) => ({
51
46
  entry,
52
- slug: entry.slug,
53
47
  id: entry.id,
54
- entryMeta: slugToLocaleData(entry.slug),
55
- ...slugToLocaleData(entry.slug),
48
+ entryMeta: slugToLocaleData(entry.id),
49
+ ...slugToLocaleData(entry.id),
56
50
  }));
57
51
 
58
52
  // In multilingual sites, add required fallback routes.
@@ -68,19 +62,17 @@ function getRoutes(): Route[] {
68
62
  const locale = key === 'root' ? undefined : key;
69
63
  const localeDocs = getLocaleDocs(locale);
70
64
  for (const fallback of defaultLocaleDocs) {
71
- const slug = localizedSlug(fallback.slug, locale);
72
- const id = project.legacyCollections ? localizedId(fallback.id, locale) : slug;
73
- const doesNotNeedFallback = localeDocs.some((doc) => doc.slug === slug);
65
+ const id = localizedSlug(fallback.id, locale);
66
+ const doesNotNeedFallback = localeDocs.some((doc) => doc.id === id);
74
67
  if (doesNotNeedFallback) continue;
75
68
  routes.push({
76
69
  entry: fallback,
77
- slug,
78
70
  id,
79
71
  isFallback: true,
80
72
  lang: localeConfig.lang || BuiltInDefaultLocale.lang,
81
73
  locale,
82
74
  dir: localeConfig.dir,
83
- entryMeta: slugToLocaleData(fallback.slug),
75
+ entryMeta: slugToLocaleData(fallback.id),
84
76
  });
85
77
  }
86
78
  }
@@ -93,7 +85,7 @@ export const routes = getRoutes();
93
85
  function getParamRouteMapping(): ReadonlyMap<string | undefined, Route> {
94
86
  const map = new Map<string | undefined, Route>();
95
87
  for (const route of routes) {
96
- map.set(slugToParam(route.slug), route);
88
+ map.set(slugToParam(route.id), route);
97
89
  }
98
90
  return map;
99
91
  }
@@ -105,7 +97,7 @@ export function getRouteBySlugParam(slugParam: string | undefined): Route | unde
105
97
 
106
98
  function getPaths(): Path[] {
107
99
  return routes.map((route) => ({
108
- params: { slug: slugToParam(route.slug) },
100
+ params: { slug: slugToParam(route.id) },
109
101
  props: route,
110
102
  }));
111
103
  }
@@ -128,15 +120,15 @@ function getLocaleDocs(locale: string | undefined): StarlightDocsEntry[] {
128
120
  }
129
121
 
130
122
  /** Filter an array to find items whose slug matches the passed locale. */
131
- function filterByLocale<T extends { slug: string }>(items: T[], locale: string | undefined): T[] {
123
+ function filterByLocale<T extends { id: string }>(items: T[], locale: string | undefined): T[] {
132
124
  if (config.locales) {
133
125
  if (locale && locale in config.locales) {
134
- return items.filter((i) => i.slug === locale || i.slug.startsWith(locale + '/'));
126
+ return items.filter((i) => i.id === locale || i.id.startsWith(locale + '/'));
135
127
  } else if (config.locales.root) {
136
128
  const langKeys = Object.keys(config.locales).filter((k) => k !== 'root');
137
129
  const isLangIndex = new RegExp(`^(${langKeys.join('|')})$`);
138
130
  const isLangDir = new RegExp(`^(${langKeys.join('|')})/`);
139
- return items.filter((i) => !isLangIndex.test(i.slug) && !isLangDir.test(i.slug));
131
+ return items.filter((i) => !isLangIndex.test(i.id) && !isLangDir.test(i.id));
140
132
  }
141
133
  }
142
134
  return items;
@@ -40,23 +40,13 @@ export interface PaginationLinks {
40
40
  next: SidebarLink | undefined;
41
41
  }
42
42
 
43
- // The type returned from `CollectionEntry` is different for legacy collections and collections
44
- // using a loader. This type is a common subset of both types.
45
- export type StarlightDocsCollectionEntry = Omit<
46
- CollectionEntry<'docs'>,
47
- 'id' | 'filePath' | 'render' | 'slug'
48
- > & {
49
- // Update the `id` property to be a string like in the loader type.
50
- id: string;
51
- // Add the `filePath` property which is only present in the loader type.
52
- filePath?: string;
53
- // Add the `slug` property which is only present in the legacy type.
54
- slug?: string;
55
- };
43
+ // A type representing a docs collection entry returned by the `docsLoader()`.
44
+ export type StarlightDocsCollectionEntry = CollectionEntry<'docs'>;
56
45
 
46
+ // A normalized type representing a docs collection entry used throughout Starlight.
57
47
  export type StarlightDocsEntry = StarlightDocsCollectionEntry & {
48
+ // At the moment, Starlight only supports file-based loaders which always include a `filePath`.
58
49
  filePath: string;
59
- slug: string;
60
50
  };
61
51
 
62
52
  export interface Route extends LocaleData {
@@ -64,9 +54,7 @@ export interface Route extends LocaleData {
64
54
  entry: StarlightDocsEntry;
65
55
  /** Locale metadata for the page content. Can be different from top-level locale values when a page is using fallback content. */
66
56
  entryMeta: LocaleData;
67
- /** @deprecated Migrate to the new Content Layer API and use `id` instead. */
68
- slug: string;
69
- /** The slug or unique ID if using the `legacy.collections` flag. */
57
+ /** The slug. */
70
58
  id: string;
71
59
  /** Whether this page is untranslated in the current language and using fallback content from the default locale. */
72
60
  isFallback?: boolean;
package/utils/slugs.ts CHANGED
@@ -80,24 +80,23 @@ export function localizedSlug(slug: string, locale: string | undefined): string
80
80
  }
81
81
 
82
82
  /**
83
- * Convert a legacy collection entry ID or filePath relative to the collection root to a different
84
- * locale.
85
- * For example, passing an ID of `en/home.md` and a locale of `fr` results in `fr/home.md`.
83
+ * Convert a file path relative to the collection root to a different locale.
84
+ * For example, passing a file path of `en/home.md` and a locale of `fr` results in `fr/home.md`.
86
85
  * An undefined locale is treated as the root locale, resulting in `home.md`.
87
- * @param id A collection entry ID
86
+ * @param filePath A collection entry file path relative to the collection root
88
87
  * @param locale The target locale
89
88
  * @example
90
- * localizedSlug('en/home.md', 'fr') // => 'fr/home.md'
91
- * localizedSlug('en/home.md', undefined) // => 'home.md'
89
+ * localizedFilePath('en/home.md', 'fr') // => 'fr/home.md'
90
+ * localizedFilePath('en/home.md', undefined) // => 'home.md'
92
91
  */
93
- export function localizedId(id: string, locale: string | undefined): string {
94
- const idLocale = slugToLocale(id);
95
- if (idLocale) {
96
- return id.replace(idLocale + '/', locale ? locale + '/' : '');
92
+ export function localizedFilePath(filePath: string, locale: string | undefined): string {
93
+ const filePathLocale = slugToLocale(filePath);
94
+ if (filePathLocale) {
95
+ return filePath.replace(filePathLocale + '/', locale ? locale + '/' : '');
97
96
  } else if (locale) {
98
- return locale + '/' + id;
97
+ return locale + '/' + filePath;
99
98
  } else {
100
- return id;
99
+ return filePath;
101
100
  }
102
101
  }
103
102
 
@@ -83,7 +83,7 @@ const validateSidebarProp = (
83
83
  */
84
84
  export type StarlightPageProps = Prettify<
85
85
  // Remove the index signature from `Route`, omit undesired properties and make the rest optional.
86
- Partial<Omit<RemoveIndexSignature<PageProps>, 'entry' | 'entryMeta' | 'id' | 'locale' | 'slug'>> &
86
+ Partial<Omit<RemoveIndexSignature<PageProps>, 'entry' | 'entryMeta' | 'id' | 'locale'>> &
87
87
  // Add the sidebar definitions for a Starlight page.
88
88
  Partial<Pick<StarlightRouteData, 'hasSidebar'>> & {
89
89
  sidebar?: StarlightUserConfig['sidebar'];
@@ -97,13 +97,7 @@ export type StarlightPageProps = Prettify<
97
97
  * to a `StarlightDocsEntry`.
98
98
  * A Starlight page docs entry cannot be rendered like a content collection entry.
99
99
  */
100
- type StarlightPageDocsEntry = Omit<StarlightDocsEntry, 'id' | 'render'> & {
101
- /**
102
- * The unique ID if using the `legacy.collections` for this Starlight page which cannot be
103
- * inferred from codegen like content collection entries or the slug.
104
- */
105
- id: string;
106
- };
100
+ type StarlightPageDocsEntry = Omit<StarlightDocsEntry, 'render'>;
107
101
 
108
102
  export async function generateStarlightPageRouteData({
109
103
  props,
@@ -114,20 +108,18 @@ export async function generateStarlightPageRouteData({
114
108
  }): Promise<StarlightRouteData> {
115
109
  const { frontmatter, ...routeProps } = props;
116
110
  const { url } = context;
117
- const slug = urlToSlug(url);
111
+ const id = urlToSlug(url);
118
112
  const pageFrontmatter = await getStarlightPageFrontmatter(frontmatter);
119
- const id = project.legacyCollections ? `${stripLeadingAndTrailingSlashes(slug)}.md` : slug;
120
- const localeData = slugToLocaleData(slug);
113
+ const localeData = slugToLocaleData(id);
121
114
  const sidebar = props.sidebar
122
115
  ? getSidebarFromConfig(validateSidebarProp(props.sidebar), url.pathname, localeData.locale)
123
116
  : getSidebar(url.pathname, localeData.locale);
124
117
  const headings = props.headings ?? [];
125
118
  const pageDocsEntry: StarlightPageDocsEntry = {
126
119
  id,
127
- slug,
128
120
  body: '',
129
121
  collection: 'docs',
130
- filePath: `${getCollectionPathFromRoot('docs', project)}/${stripLeadingAndTrailingSlashes(slug)}.md`,
122
+ filePath: `${getCollectionPathFromRoot('docs', project)}/${stripLeadingAndTrailingSlashes(id)}.md`,
131
123
  data: {
132
124
  ...pageFrontmatter,
133
125
  sidebar: {
@@ -153,7 +145,6 @@ export async function generateStarlightPageRouteData({
153
145
  headings,
154
146
  id,
155
147
  locale: localeData.locale,
156
- slug,
157
148
  };
158
149
  const siteTitle = getSiteTitle(localeData.lang);
159
150
  const routeData: StarlightRouteData = {
@@ -171,7 +162,6 @@ export async function generateStarlightPageRouteData({
171
162
  sidebar,
172
163
  siteTitle,
173
164
  siteTitleHref: getSiteTitleHref(localeData.locale),
174
- slug,
175
165
  toc: getToC(pageProps),
176
166
  };
177
167
  return routeData;
@@ -185,16 +175,16 @@ async function getStarlightPageFrontmatter(frontmatter: StarlightPageFrontmatter
185
175
  // https://github.com/withastro/astro/blob/cf993bc263b58502096f00d383266cd179f331af/packages/astro/src/assets/types.ts#L32
186
176
  // It uses a custom validation approach because imported SVGs have a type of `function` as
187
177
  // well as containing the metadata properties and this ensures we handle those correctly.
188
- z.custom(
178
+ z.custom<ReturnType<ImageFunction>>(
189
179
  (value) =>
190
- (value &&
191
- (typeof value === 'function' || typeof value === 'object') &&
192
- 'src' in value &&
193
- 'width' in value &&
194
- 'height' in value &&
195
- 'format' in value) as ReturnType<ImageFunction>,
180
+ value &&
181
+ (typeof value === 'function' || typeof value === 'object') &&
182
+ 'src' in value &&
183
+ 'width' in value &&
184
+ 'height' in value &&
185
+ 'format' in value,
196
186
  'Invalid image passed to `<StarlightPage>` component. Expected imported `ImageMetadata` object.'
197
- )) as ImageFunction,
187
+ )) as unknown as ImageFunction,
198
188
  });
199
189
 
200
190
  // Starting with Astro 4.14.0, a frontmatter schema that contains collection references will
@@ -32,10 +32,9 @@ async function loadTranslations() {
32
32
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
33
33
  // @ts-ignore — may be a type error in projects without an i18n collection
34
34
  (await getCollection('i18n')).map(({ id, data, filePath }) => {
35
- const lang =
36
- project.legacyCollections || !filePath
37
- ? id
38
- : stripExtension(stripLeadingSlash(filePath.replace(i18nCollectionPathFromRoot, '')));
35
+ const lang = !filePath
36
+ ? id
37
+ : stripExtension(stripLeadingSlash(filePath.replace(i18nCollectionPathFromRoot, '')));
39
38
  return [lang, data] as const;
40
39
  })
41
40
  );