@astrojs/starlight 0.31.0 → 0.32.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.
- package/CHANGELOG.md +99 -0
- package/components/Banner.astro +1 -3
- package/components/ContentNotice.astro +2 -2
- package/components/ContentPanel.astro +0 -4
- package/components/DraftContentNotice.astro +0 -1
- package/components/EditLink.astro +1 -2
- package/components/FallbackContentNotice.astro +0 -1
- package/components/Footer.astro +3 -5
- package/components/Head.astro +1 -2
- package/components/Header.astro +5 -6
- package/components/Hero.astro +1 -2
- package/components/Icons.ts +2 -0
- package/components/LanguageSelect.astro +2 -3
- package/components/LastUpdated.astro +1 -3
- package/components/MarkdownContent.astro +0 -1
- package/components/MobileMenuFooter.astro +3 -4
- package/components/MobileMenuToggle.astro +0 -1
- package/components/MobileTableOfContents.astro +1 -2
- package/components/Page.astro +32 -32
- package/components/PageFrame.astro +2 -3
- package/components/PageSidebar.astro +3 -5
- package/components/PageTitle.astro +1 -2
- package/components/Pagination.astro +1 -2
- package/components/Search.astro +17 -3
- package/components/Sidebar.astro +3 -5
- package/components/SidebarPersister.astro +1 -2
- package/components/SidebarSublist.astro +2 -1
- package/components/SiteTitle.astro +1 -2
- package/components/SkipLink.astro +0 -1
- package/components/SocialIcons.astro +0 -1
- package/components/StarlightPage.astro +7 -3
- package/components/TableOfContents.astro +1 -2
- package/components/ThemeProvider.astro +0 -1
- package/components/ThemeSelect.astro +0 -1
- package/components/TwoColumnContent.astro +1 -5
- package/index.ts +7 -9
- package/integrations/asides.ts +4 -7
- package/integrations/expressive-code/index.ts +15 -10
- package/integrations/shared/{pathToLocale.ts → absolutePathToLang.ts} +7 -5
- package/integrations/virtual-user-config.ts +27 -0
- package/locals.d.ts +26 -0
- package/locals.ts +36 -2
- package/package.json +5 -3
- package/props.ts +13 -1
- package/route-data.ts +11 -0
- package/routes/common.astro +5 -11
- package/routes/ssr/index.astro +1 -1
- package/routes/static/404.astro +1 -41
- package/routes/static/index.astro +1 -4
- package/schemas/hero.ts +2 -3
- package/schemas/pagefind.ts +97 -33
- package/types.ts +2 -0
- package/user-components/Card.astro +2 -2
- package/user-components/Icon.astro +2 -2
- package/user-components/LinkButton.astro +2 -2
- package/user-components/TabItem.astro +2 -2
- package/user-components/rehype-file-tree.ts +2 -2
- package/user-components/rehype-tabs.ts +3 -3
- package/utils/i18n.ts +0 -20
- package/utils/navigation.ts +19 -36
- package/utils/plugins.ts +316 -141
- package/utils/{route-data.ts → routing/data.ts} +56 -30
- package/utils/{routing.ts → routing/index.ts} +6 -44
- package/utils/routing/middleware.ts +81 -0
- package/utils/routing/types.ts +96 -0
- package/utils/slugs.ts +2 -10
- package/utils/starlight-page.ts +2 -10
- package/utils/user-config.ts +8 -0
- package/virtual-internal.d.ts +4 -0
package/utils/plugins.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import type { AstroIntegration, HookParameters } from 'astro';
|
|
1
|
+
import type { AstroIntegration, HookParameters as AstroHookParameters } from 'astro';
|
|
2
|
+
import { AstroError } from 'astro/errors';
|
|
2
3
|
import { z } from 'astro/zod';
|
|
3
|
-
import { StarlightConfigSchema, type StarlightUserConfig } from '../utils/user-config';
|
|
4
4
|
import { parseWithFriendlyErrors } from '../utils/error-map';
|
|
5
|
+
import {
|
|
6
|
+
StarlightConfigSchema,
|
|
7
|
+
type StarlightConfig,
|
|
8
|
+
type StarlightUserConfig,
|
|
9
|
+
} from '../utils/user-config';
|
|
5
10
|
import type { UserI18nSchema } from './translations';
|
|
11
|
+
import { createTranslationSystemFromFs } from './translations-fs';
|
|
12
|
+
import { absolutePathToLang as getAbsolutePathFromLang } from '../integrations/shared/absolutePathToLang';
|
|
6
13
|
|
|
7
14
|
/**
|
|
8
15
|
* Runs Starlight plugins in the order that they are configured after validating the user-provided
|
|
@@ -30,22 +37,61 @@ export async function runPlugins(
|
|
|
30
37
|
'Invalid plugins config passed to starlight integration'
|
|
31
38
|
);
|
|
32
39
|
|
|
33
|
-
// A list of Astro integrations added by the various plugins.
|
|
34
|
-
const integrations: AstroIntegration[] = [];
|
|
35
40
|
// A list of translations injected by the various plugins keyed by locale.
|
|
36
41
|
const pluginTranslations: PluginTranslations = {};
|
|
42
|
+
// A list of route middleware added by the various plugins.
|
|
43
|
+
const routeMiddlewareConfigs: Array<z.output<typeof routeMiddlewareConfigSchema>> = [];
|
|
44
|
+
|
|
45
|
+
for (const {
|
|
46
|
+
hooks: { 'i18n:setup': i18nSetup },
|
|
47
|
+
} of pluginsConfig) {
|
|
48
|
+
if (i18nSetup) {
|
|
49
|
+
await i18nSetup({
|
|
50
|
+
injectTranslations(translations) {
|
|
51
|
+
// Merge the translations injected by the plugin.
|
|
52
|
+
for (const [locale, localeTranslations] of Object.entries(translations)) {
|
|
53
|
+
pluginTranslations[locale] ??= {};
|
|
54
|
+
Object.assign(pluginTranslations[locale]!, localeTranslations);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const useTranslations = createTranslationSystemFromFs(
|
|
62
|
+
starlightConfig,
|
|
63
|
+
context.config,
|
|
64
|
+
pluginTranslations
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
function absolutePathToLang(path: string) {
|
|
68
|
+
return getAbsolutePathFromLang(path, { astroConfig: context.config, starlightConfig });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// A list of Astro integrations added by the various plugins.
|
|
72
|
+
const integrations: AstroIntegration[] = [];
|
|
37
73
|
|
|
38
74
|
for (const {
|
|
39
75
|
name,
|
|
40
|
-
hooks: { setup },
|
|
76
|
+
hooks: { 'config:setup': configSetup, setup: deprecatedSetup },
|
|
41
77
|
} of pluginsConfig) {
|
|
78
|
+
// A refinement in the schema ensures that at least one of the two hooks is defined.
|
|
79
|
+
const setup = (configSetup ?? deprecatedSetup)!;
|
|
80
|
+
|
|
42
81
|
await setup({
|
|
43
82
|
config: pluginsUserConfig ? { ...userConfig, plugins: pluginsUserConfig } : userConfig,
|
|
44
83
|
updateConfig(newConfig) {
|
|
45
84
|
// Ensure that plugins do not update the `plugins` config key.
|
|
46
85
|
if ('plugins' in newConfig) {
|
|
47
|
-
throw new
|
|
48
|
-
`The
|
|
86
|
+
throw new AstroError(
|
|
87
|
+
`The \`${name}\` plugin tried to update the \`plugins\` config key which is not supported.`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if ('routeMiddleware' in newConfig) {
|
|
91
|
+
throw new AstroError(
|
|
92
|
+
`The \`${name}\` plugin tried to update the \`routeMiddleware\` config key which is not supported.`,
|
|
93
|
+
'Use the `addRouteMiddleware()` utility instead.\n' +
|
|
94
|
+
'See https://starlight.astro.build/reference/plugins/#addroutemiddleware for more details.'
|
|
49
95
|
);
|
|
50
96
|
}
|
|
51
97
|
|
|
@@ -65,6 +111,9 @@ export async function runPlugins(
|
|
|
65
111
|
// Collect any Astro integrations added by the plugin.
|
|
66
112
|
integrations.push(integration);
|
|
67
113
|
},
|
|
114
|
+
addRouteMiddleware(middlewareConfig) {
|
|
115
|
+
routeMiddlewareConfigs.push(middlewareConfig);
|
|
116
|
+
},
|
|
68
117
|
astroConfig: {
|
|
69
118
|
...context.config,
|
|
70
119
|
integrations: [...context.config.integrations, ...integrations],
|
|
@@ -72,22 +121,37 @@ export async function runPlugins(
|
|
|
72
121
|
command: context.command,
|
|
73
122
|
isRestart: context.isRestart,
|
|
74
123
|
logger: context.logger.fork(name),
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
for (const [locale, localeTranslations] of Object.entries(translations)) {
|
|
78
|
-
pluginTranslations[locale] ??= {};
|
|
79
|
-
Object.assign(pluginTranslations[locale]!, localeTranslations);
|
|
80
|
-
}
|
|
81
|
-
},
|
|
124
|
+
useTranslations,
|
|
125
|
+
absolutePathToLang,
|
|
82
126
|
});
|
|
83
127
|
}
|
|
84
128
|
|
|
85
|
-
|
|
129
|
+
applyPluginMiddleware(routeMiddlewareConfigs, starlightConfig);
|
|
130
|
+
|
|
131
|
+
return { integrations, starlightConfig, pluginTranslations, useTranslations, absolutePathToLang };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Updates `routeMiddleware` in the Starlight config to add plugin middlewares in the correct order. */
|
|
135
|
+
function applyPluginMiddleware(
|
|
136
|
+
routeMiddlewareConfigs: { entrypoint: string; order: 'default' | 'pre' | 'post' }[],
|
|
137
|
+
starlightConfig: StarlightConfig
|
|
138
|
+
) {
|
|
139
|
+
const middlewareBuckets = routeMiddlewareConfigs.reduce<
|
|
140
|
+
Record<'pre' | 'default' | 'post', string[]>
|
|
141
|
+
>(
|
|
142
|
+
(buckets, { entrypoint, order = 'default' }) => {
|
|
143
|
+
buckets[order].push(entrypoint);
|
|
144
|
+
return buckets;
|
|
145
|
+
},
|
|
146
|
+
{ pre: [], default: [], post: [] }
|
|
147
|
+
);
|
|
148
|
+
starlightConfig.routeMiddleware.unshift(...middlewareBuckets.pre);
|
|
149
|
+
starlightConfig.routeMiddleware.push(...middlewareBuckets.default, ...middlewareBuckets.post);
|
|
86
150
|
}
|
|
87
151
|
|
|
88
152
|
export function injectPluginTranslationsTypes(
|
|
89
153
|
translations: PluginTranslations,
|
|
90
|
-
injectTypes:
|
|
154
|
+
injectTypes: AstroHookParameters<'astro:config:done'>['injectTypes']
|
|
91
155
|
) {
|
|
92
156
|
const allKeys = new Set<string>();
|
|
93
157
|
|
|
@@ -118,140 +182,246 @@ const astroIntegrationSchema = z.object({
|
|
|
118
182
|
hooks: z.object({}).passthrough().default({}),
|
|
119
183
|
}) as z.Schema<AstroIntegration>;
|
|
120
184
|
|
|
185
|
+
const routeMiddlewareConfigSchema = z.object({
|
|
186
|
+
entrypoint: z.string(),
|
|
187
|
+
order: z.enum(['pre', 'post', 'default']).default('default'),
|
|
188
|
+
});
|
|
189
|
+
|
|
121
190
|
const baseStarlightPluginSchema = z.object({
|
|
122
191
|
/** Name of the Starlight plugin. */
|
|
123
192
|
name: z.string(),
|
|
124
193
|
});
|
|
125
194
|
|
|
195
|
+
const configSetupHookSchema = z
|
|
196
|
+
.function(
|
|
197
|
+
z.tuple([
|
|
198
|
+
z.object({
|
|
199
|
+
/**
|
|
200
|
+
* A read-only copy of the user-supplied Starlight configuration.
|
|
201
|
+
*
|
|
202
|
+
* Note that this configuration may have been updated by other plugins configured
|
|
203
|
+
* before this one.
|
|
204
|
+
*/
|
|
205
|
+
config: z.any() as z.Schema<
|
|
206
|
+
// The configuration passed to plugins should contains the list of plugins.
|
|
207
|
+
StarlightUserConfig & { plugins?: z.input<typeof baseStarlightPluginSchema>[] }
|
|
208
|
+
>,
|
|
209
|
+
/**
|
|
210
|
+
* A callback function to update the user-supplied Starlight configuration.
|
|
211
|
+
*
|
|
212
|
+
* You only need to provide the configuration values that you want to update but no deep
|
|
213
|
+
* merge is performed.
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* {
|
|
217
|
+
* name: 'My Starlight Plugin',
|
|
218
|
+
* hooks: {
|
|
219
|
+
* 'config:setup'({ updateConfig }) {
|
|
220
|
+
* updateConfig({
|
|
221
|
+
* description: 'Custom description',
|
|
222
|
+
* });
|
|
223
|
+
* }
|
|
224
|
+
* }
|
|
225
|
+
* }
|
|
226
|
+
*/
|
|
227
|
+
updateConfig: z.function(
|
|
228
|
+
z.tuple([
|
|
229
|
+
z.record(z.any()) as z.Schema<Partial<Omit<StarlightUserConfig, 'routeMiddleware'>>>,
|
|
230
|
+
]),
|
|
231
|
+
z.void()
|
|
232
|
+
),
|
|
233
|
+
/**
|
|
234
|
+
* A callback function to add an Astro integration required by this plugin.
|
|
235
|
+
*
|
|
236
|
+
* @see https://docs.astro.build/en/reference/integrations-reference/
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* {
|
|
240
|
+
* name: 'My Starlight Plugin',
|
|
241
|
+
* hooks: {
|
|
242
|
+
* 'config:setup'({ addIntegration }) {
|
|
243
|
+
* addIntegration({
|
|
244
|
+
* name: 'My Plugin Astro Integration',
|
|
245
|
+
* hooks: {
|
|
246
|
+
* 'astro:config:setup': () => {
|
|
247
|
+
* // …
|
|
248
|
+
* },
|
|
249
|
+
* },
|
|
250
|
+
* });
|
|
251
|
+
* }
|
|
252
|
+
* }
|
|
253
|
+
* }
|
|
254
|
+
*/
|
|
255
|
+
addIntegration: z.function(z.tuple([astroIntegrationSchema]), z.void()),
|
|
256
|
+
/**
|
|
257
|
+
* A callback function to register additional route middleware handlers.
|
|
258
|
+
*
|
|
259
|
+
* If the order of execution is important, a plugin can use the `order` option to enforce
|
|
260
|
+
* running first or last.
|
|
261
|
+
*
|
|
262
|
+
* @example
|
|
263
|
+
* {
|
|
264
|
+
* name: 'My Starlight Plugin',
|
|
265
|
+
* hooks: {
|
|
266
|
+
* setup({ addRouteMiddleware }) {
|
|
267
|
+
* addRouteMiddleware({ entrypoint: '@me/my-plugin/route-middleware' });
|
|
268
|
+
* },
|
|
269
|
+
* },
|
|
270
|
+
* }
|
|
271
|
+
*/
|
|
272
|
+
addRouteMiddleware: z.function(z.tuple([routeMiddlewareConfigSchema]), z.void()),
|
|
273
|
+
/**
|
|
274
|
+
* A read-only copy of the user-supplied Astro configuration.
|
|
275
|
+
*
|
|
276
|
+
* Note that this configuration is resolved before any other integrations have run.
|
|
277
|
+
*
|
|
278
|
+
* @see https://docs.astro.build/en/reference/integrations-reference/#config-option
|
|
279
|
+
*/
|
|
280
|
+
astroConfig: z.any() as z.Schema<StarlightPluginContext['config']>,
|
|
281
|
+
/**
|
|
282
|
+
* The command used to run Starlight.
|
|
283
|
+
*
|
|
284
|
+
* @see https://docs.astro.build/en/reference/integrations-reference/#command-option
|
|
285
|
+
*/
|
|
286
|
+
command: z.any() as z.Schema<StarlightPluginContext['command']>,
|
|
287
|
+
/**
|
|
288
|
+
* `false` when the dev server starts, `true` when a reload is triggered.
|
|
289
|
+
*
|
|
290
|
+
* @see https://docs.astro.build/en/reference/integrations-reference/#isrestart-option
|
|
291
|
+
*/
|
|
292
|
+
isRestart: z.any() as z.Schema<StarlightPluginContext['isRestart']>,
|
|
293
|
+
/**
|
|
294
|
+
* An instance of the Astro integration logger with all logged messages prefixed with the
|
|
295
|
+
* plugin name.
|
|
296
|
+
*
|
|
297
|
+
* @see https://docs.astro.build/en/reference/integrations-reference/#astrointegrationlogger
|
|
298
|
+
*/
|
|
299
|
+
logger: z.any() as z.Schema<StarlightPluginContext['logger']>,
|
|
300
|
+
/**
|
|
301
|
+
* A callback function to generate a utility function to access UI strings for a given
|
|
302
|
+
* language.
|
|
303
|
+
*
|
|
304
|
+
* @see https://starlight.astro.build/guides/i18n/#using-ui-translations
|
|
305
|
+
*
|
|
306
|
+
* @example
|
|
307
|
+
* {
|
|
308
|
+
* name: 'My Starlight Plugin',
|
|
309
|
+
* hooks: {
|
|
310
|
+
* 'config:setup'({ useTranslations, logger }) {
|
|
311
|
+
* const t = useTranslations('en');
|
|
312
|
+
* logger.info(t('builtWithStarlight.label'));
|
|
313
|
+
* // ^ Logs 'Built with Starlight' to the console.
|
|
314
|
+
* }
|
|
315
|
+
* }
|
|
316
|
+
* }
|
|
317
|
+
*/
|
|
318
|
+
useTranslations: z.any() as z.Schema<ReturnType<typeof createTranslationSystemFromFs>>,
|
|
319
|
+
/**
|
|
320
|
+
* A callback function to get the language for a given absolute file path. The returned
|
|
321
|
+
* language can be used with the `useTranslations` helper to get UI strings for that
|
|
322
|
+
* language.
|
|
323
|
+
*
|
|
324
|
+
* This can be particularly useful in remark or rehype plugins to get the language for
|
|
325
|
+
* the current file being processed and use it to get the appropriate UI strings for that
|
|
326
|
+
* language.
|
|
327
|
+
*
|
|
328
|
+
* @example
|
|
329
|
+
* {
|
|
330
|
+
* name: 'My Starlight Plugin',
|
|
331
|
+
* hooks: {
|
|
332
|
+
* 'config:setup'({ absolutePathToLang, useTranslations, logger }) {
|
|
333
|
+
* const lang = absolutePathToLang('/absolute/path/to/project/src/content/docs/fr/index.mdx');
|
|
334
|
+
* const t = useTranslations(lang);
|
|
335
|
+
* logger.info(t('aside.tip'));
|
|
336
|
+
* // ^ Logs 'Astuce' to the console.
|
|
337
|
+
* }
|
|
338
|
+
* }
|
|
339
|
+
* }
|
|
340
|
+
*/
|
|
341
|
+
absolutePathToLang: z.function(z.tuple([z.string()]), z.string()),
|
|
342
|
+
}),
|
|
343
|
+
]),
|
|
344
|
+
z.union([z.void(), z.promise(z.void())])
|
|
345
|
+
)
|
|
346
|
+
.optional();
|
|
347
|
+
|
|
126
348
|
/**
|
|
127
349
|
* A plugin `config` and `updateConfig` argument are purposely not validated using the Starlight
|
|
128
350
|
* user config schema but properly typed for user convenience because we do not want to run any of
|
|
129
351
|
* the Zod `transform`s used in the user config schema when running plugins.
|
|
130
352
|
*/
|
|
131
|
-
const starlightPluginSchema = baseStarlightPluginSchema
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
),
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
astroConfig: z.any() as z.Schema<StarlightPluginContext['config']>,
|
|
204
|
-
/**
|
|
205
|
-
* The command used to run Starlight.
|
|
206
|
-
*
|
|
207
|
-
* @see https://docs.astro.build/en/reference/integrations-reference/#command-option
|
|
208
|
-
*/
|
|
209
|
-
command: z.any() as z.Schema<StarlightPluginContext['command']>,
|
|
210
|
-
/**
|
|
211
|
-
* `false` when the dev server starts, `true` when a reload is triggered.
|
|
212
|
-
*
|
|
213
|
-
* @see https://docs.astro.build/en/reference/integrations-reference/#isrestart-option
|
|
214
|
-
*/
|
|
215
|
-
isRestart: z.any() as z.Schema<StarlightPluginContext['isRestart']>,
|
|
216
|
-
/**
|
|
217
|
-
* An instance of the Astro integration logger with all logged messages prefixed with the
|
|
218
|
-
* plugin name.
|
|
219
|
-
*
|
|
220
|
-
* @see https://docs.astro.build/en/reference/integrations-reference/#astrointegrationlogger
|
|
221
|
-
*/
|
|
222
|
-
logger: z.any() as z.Schema<StarlightPluginContext['logger']>,
|
|
223
|
-
/**
|
|
224
|
-
* A callback function to add or update translations strings.
|
|
225
|
-
*
|
|
226
|
-
* @see https://starlight.astro.build/guides/i18n/#extend-translation-schema
|
|
227
|
-
*
|
|
228
|
-
* @example
|
|
229
|
-
* {
|
|
230
|
-
* name: 'My Starlight Plugin',
|
|
231
|
-
* hooks: {
|
|
232
|
-
* setup({ injectTranslations }) {
|
|
233
|
-
* injectTranslations({
|
|
234
|
-
* en: {
|
|
235
|
-
* 'myPlugin.doThing': 'Do the thing',
|
|
236
|
-
* },
|
|
237
|
-
* fr: {
|
|
238
|
-
* 'myPlugin.doThing': 'Faire le truc',
|
|
239
|
-
* },
|
|
240
|
-
* });
|
|
241
|
-
* }
|
|
242
|
-
* }
|
|
243
|
-
* }
|
|
244
|
-
*/
|
|
245
|
-
injectTranslations: z.function(
|
|
246
|
-
z.tuple([z.record(z.string(), z.record(z.string(), z.string()))]),
|
|
247
|
-
z.void()
|
|
248
|
-
),
|
|
249
|
-
}),
|
|
250
|
-
]),
|
|
251
|
-
z.union([z.void(), z.promise(z.void())])
|
|
252
|
-
),
|
|
253
|
-
}),
|
|
254
|
-
});
|
|
353
|
+
const starlightPluginSchema = baseStarlightPluginSchema
|
|
354
|
+
.extend({
|
|
355
|
+
/** The different hooks available to the plugin. */
|
|
356
|
+
hooks: z.object({
|
|
357
|
+
/**
|
|
358
|
+
* Plugin internationalization setup function allowing to inject translations strings for the
|
|
359
|
+
* plugin in various locales. These translations will be available in the `config:setup` hook
|
|
360
|
+
* and plugin UI.
|
|
361
|
+
*/
|
|
362
|
+
'i18n:setup': z
|
|
363
|
+
.function(
|
|
364
|
+
z.tuple([
|
|
365
|
+
z.object({
|
|
366
|
+
/**
|
|
367
|
+
* A callback function to add or update translations strings.
|
|
368
|
+
*
|
|
369
|
+
* @see https://starlight.astro.build/guides/i18n/#extend-translation-schema
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* {
|
|
373
|
+
* name: 'My Starlight Plugin',
|
|
374
|
+
* hooks: {
|
|
375
|
+
* 'i18n:setup'({ injectTranslations }) {
|
|
376
|
+
* injectTranslations({
|
|
377
|
+
* en: {
|
|
378
|
+
* 'myPlugin.doThing': 'Do the thing',
|
|
379
|
+
* },
|
|
380
|
+
* fr: {
|
|
381
|
+
* 'myPlugin.doThing': 'Faire le truc',
|
|
382
|
+
* },
|
|
383
|
+
* });
|
|
384
|
+
* }
|
|
385
|
+
* }
|
|
386
|
+
* }
|
|
387
|
+
*/
|
|
388
|
+
injectTranslations: z.function(
|
|
389
|
+
z.tuple([z.record(z.string(), z.record(z.string(), z.string()))]),
|
|
390
|
+
z.void()
|
|
391
|
+
),
|
|
392
|
+
}),
|
|
393
|
+
]),
|
|
394
|
+
z.union([z.void(), z.promise(z.void())])
|
|
395
|
+
)
|
|
396
|
+
.optional(),
|
|
397
|
+
/**
|
|
398
|
+
* Plugin configuration setup function called with an object containing various values that
|
|
399
|
+
* can be used by the plugin to interact with Starlight.
|
|
400
|
+
*/
|
|
401
|
+
'config:setup': configSetupHookSchema,
|
|
402
|
+
/**
|
|
403
|
+
* @deprecated Use the `config:setup` hook instead as `setup` will be removed in a future
|
|
404
|
+
* version.
|
|
405
|
+
*/
|
|
406
|
+
setup: configSetupHookSchema,
|
|
407
|
+
}),
|
|
408
|
+
})
|
|
409
|
+
.superRefine((plugin, ctx) => {
|
|
410
|
+
if (!plugin.hooks['config:setup'] && !plugin.hooks.setup) {
|
|
411
|
+
ctx.addIssue({
|
|
412
|
+
code: z.ZodIssueCode.custom,
|
|
413
|
+
message: 'A plugin must define at least a `config:setup` hook.',
|
|
414
|
+
});
|
|
415
|
+
} else if (plugin.hooks['config:setup'] && plugin.hooks.setup) {
|
|
416
|
+
ctx.addIssue({
|
|
417
|
+
code: z.ZodIssueCode.custom,
|
|
418
|
+
message:
|
|
419
|
+
'A plugin cannot define both a `config:setup` and `setup` hook. ' +
|
|
420
|
+
'As `setup` is deprecated and will be removed in a future version, ' +
|
|
421
|
+
'consider using `config:setup` instead.',
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
});
|
|
255
425
|
|
|
256
426
|
const starlightPluginsConfigSchema = z.array(starlightPluginSchema).default([]);
|
|
257
427
|
|
|
@@ -259,6 +429,11 @@ type StarlightPluginsUserConfig = z.input<typeof starlightPluginsConfigSchema>;
|
|
|
259
429
|
|
|
260
430
|
export type StarlightPlugin = z.input<typeof starlightPluginSchema>;
|
|
261
431
|
|
|
432
|
+
export type HookParameters<
|
|
433
|
+
Hook extends keyof StarlightPlugin['hooks'],
|
|
434
|
+
HookFn = StarlightPlugin['hooks'][Hook],
|
|
435
|
+
> = HookFn extends (...args: any) => any ? Parameters<HookFn>[0] : never;
|
|
436
|
+
|
|
262
437
|
export type StarlightUserConfigWithPlugins = StarlightUserConfig & {
|
|
263
438
|
/**
|
|
264
439
|
* A list of plugins to extend Starlight with.
|
|
@@ -273,7 +448,7 @@ export type StarlightUserConfigWithPlugins = StarlightUserConfig & {
|
|
|
273
448
|
};
|
|
274
449
|
|
|
275
450
|
export type StarlightPluginContext = Pick<
|
|
276
|
-
|
|
451
|
+
AstroHookParameters<'astro:config:setup'>,
|
|
277
452
|
'command' | 'config' | 'isRestart' | 'logger'
|
|
278
453
|
>;
|
|
279
454
|
|
|
@@ -1,39 +1,34 @@
|
|
|
1
|
-
import type { MarkdownHeading } from 'astro';
|
|
1
|
+
import type { APIContext, MarkdownHeading } from 'astro';
|
|
2
|
+
import project from 'virtual:starlight/project-context';
|
|
2
3
|
import config from 'virtual:starlight/user-config';
|
|
3
|
-
import { generateToC
|
|
4
|
+
import { generateToC } from '../generateToC';
|
|
4
5
|
import { getNewestCommitDate } from 'virtual:starlight/git-info';
|
|
5
|
-
import { getPrevNextLinks, getSidebar
|
|
6
|
-
import { ensureTrailingSlash } from '
|
|
7
|
-
import
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
import { getPrevNextLinks, getSidebar } from '../navigation';
|
|
7
|
+
import { ensureTrailingSlash } from '../path';
|
|
8
|
+
import { getRouteBySlugParam, normalizeCollectionEntry } from '../routing';
|
|
9
|
+
import type {
|
|
10
|
+
Route,
|
|
11
|
+
StarlightDocsCollectionEntry,
|
|
12
|
+
StarlightDocsEntry,
|
|
13
|
+
StarlightRouteData,
|
|
14
|
+
} from './types';
|
|
15
|
+
import { formatPath } from '../format-path';
|
|
16
|
+
import { useTranslations } from '../translations';
|
|
17
|
+
import { BuiltInDefaultLocale } from '../i18n';
|
|
18
|
+
import { getEntry, render } from 'astro:content';
|
|
19
|
+
import { getCollectionPathFromRoot } from '../collection';
|
|
11
20
|
|
|
12
21
|
export interface PageProps extends Route {
|
|
13
22
|
headings: MarkdownHeading[];
|
|
14
23
|
}
|
|
15
24
|
|
|
16
|
-
export
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/** Site navigation sidebar entries for this page. */
|
|
24
|
-
sidebar: SidebarEntry[];
|
|
25
|
-
/** Whether or not the sidebar should be displayed on this page. */
|
|
26
|
-
hasSidebar: boolean;
|
|
27
|
-
/** Links to the previous and next page in the sidebar if enabled. */
|
|
28
|
-
pagination: ReturnType<typeof getPrevNextLinks>;
|
|
29
|
-
/** Table of contents for this page if enabled. */
|
|
30
|
-
toc: { minHeadingLevel: number; maxHeadingLevel: number; items: TocItem[] } | undefined;
|
|
31
|
-
/** JS Date object representing when this page was last updated if enabled. */
|
|
32
|
-
lastUpdated: Date | undefined;
|
|
33
|
-
/** URL object for the address where this page can be edited if enabled. */
|
|
34
|
-
editUrl: URL | undefined;
|
|
35
|
-
/** @deprecated Use `Astro.locals.t()` instead. */
|
|
36
|
-
labels: Record<string, never>;
|
|
25
|
+
export async function useRouteData(context: APIContext): Promise<StarlightRouteData> {
|
|
26
|
+
const route =
|
|
27
|
+
('slug' in context.params && getRouteBySlugParam(context.params.slug)) ||
|
|
28
|
+
(await get404Route(context.locals));
|
|
29
|
+
const { Content, headings } = await render(route.entry);
|
|
30
|
+
const routeData = generateRouteData({ props: { ...route, headings }, url: context.url });
|
|
31
|
+
return { ...routeData, Content };
|
|
37
32
|
}
|
|
38
33
|
|
|
39
34
|
export function generateRouteData({
|
|
@@ -56,7 +51,6 @@ export function generateRouteData({
|
|
|
56
51
|
toc: getToC(props),
|
|
57
52
|
lastUpdated: getLastUpdated(props),
|
|
58
53
|
editUrl: getEditUrl(props),
|
|
59
|
-
labels: DeprecatedLabelsPropProxy,
|
|
60
54
|
};
|
|
61
55
|
}
|
|
62
56
|
|
|
@@ -121,3 +115,35 @@ export function getSiteTitle(lang: string): string {
|
|
|
121
115
|
export function getSiteTitleHref(locale: string | undefined): string {
|
|
122
116
|
return formatPath(locale || '/');
|
|
123
117
|
}
|
|
118
|
+
|
|
119
|
+
/** Generate a route object for Starlight’s 404 page. */
|
|
120
|
+
async function get404Route(locals: App.Locals): Promise<Route> {
|
|
121
|
+
const { lang = BuiltInDefaultLocale.lang, dir = BuiltInDefaultLocale.dir } =
|
|
122
|
+
config.defaultLocale || {};
|
|
123
|
+
let locale = config.defaultLocale?.locale;
|
|
124
|
+
if (locale === 'root') locale = undefined;
|
|
125
|
+
|
|
126
|
+
const entryMeta = { dir, lang, locale };
|
|
127
|
+
|
|
128
|
+
const fallbackEntry: StarlightDocsEntry = {
|
|
129
|
+
slug: '404',
|
|
130
|
+
id: '404',
|
|
131
|
+
body: '',
|
|
132
|
+
collection: 'docs',
|
|
133
|
+
data: {
|
|
134
|
+
title: '404',
|
|
135
|
+
template: 'splash',
|
|
136
|
+
editUrl: false,
|
|
137
|
+
head: [],
|
|
138
|
+
hero: { tagline: locals.t('404.text'), actions: [] },
|
|
139
|
+
pagefind: false,
|
|
140
|
+
sidebar: { hidden: false, attrs: {} },
|
|
141
|
+
draft: false,
|
|
142
|
+
},
|
|
143
|
+
filePath: `${getCollectionPathFromRoot('docs', project)}/404.md`,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const userEntry = (await getEntry('docs', '404')) as StarlightDocsCollectionEntry;
|
|
147
|
+
const entry = userEntry ? normalizeCollectionEntry(userEntry) : fallbackEntry;
|
|
148
|
+
return { ...entryMeta, entryMeta, entry, id: entry.id, slug: entry.slug };
|
|
149
|
+
}
|