` component and a custom `srcDir`', async ({
+ page,
+ starlight,
+}) => {
+ await starlight.goto('/custom');
+
+ await expect(page.getByText('Hello')).toBeVisible();
+});
diff --git a/packages/starlight/__e2e__/fixtures/basics/astro.config.mjs b/packages/starlight/__e2e__/fixtures/basics/astro.config.mjs
index 9e96cbad3d1..1279050de74 100644
--- a/packages/starlight/__e2e__/fixtures/basics/astro.config.mjs
+++ b/packages/starlight/__e2e__/fixtures/basics/astro.config.mjs
@@ -5,6 +5,7 @@ export default defineConfig({
integrations: [
starlight({
title: 'Basics',
+ pagefind: false,
}),
],
});
diff --git a/packages/starlight/__e2e__/fixtures/custom-src-dir/astro.config.mjs b/packages/starlight/__e2e__/fixtures/custom-src-dir/astro.config.mjs
new file mode 100644
index 00000000000..9e759d9ebc9
--- /dev/null
+++ b/packages/starlight/__e2e__/fixtures/custom-src-dir/astro.config.mjs
@@ -0,0 +1,12 @@
+import starlight from '@astrojs/starlight';
+import { defineConfig } from 'astro/config';
+
+export default defineConfig({
+ srcDir: './www',
+ integrations: [
+ starlight({
+ title: 'Custom src directory',
+ pagefind: false,
+ }),
+ ],
+});
diff --git a/packages/starlight/__e2e__/fixtures/custom-src-dir/package.json b/packages/starlight/__e2e__/fixtures/custom-src-dir/package.json
new file mode 100644
index 00000000000..b6d71aca99c
--- /dev/null
+++ b/packages/starlight/__e2e__/fixtures/custom-src-dir/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@e2e/custom-src-dir",
+ "version": "0.0.0",
+ "private": true,
+ "dependencies": {
+ "@astrojs/starlight": "workspace:*",
+ "astro": "^4.10.2"
+ }
+}
diff --git a/packages/starlight/__e2e__/fixtures/custom-src-dir/www/content/config.ts b/packages/starlight/__e2e__/fixtures/custom-src-dir/www/content/config.ts
new file mode 100644
index 00000000000..45f60b01542
--- /dev/null
+++ b/packages/starlight/__e2e__/fixtures/custom-src-dir/www/content/config.ts
@@ -0,0 +1,6 @@
+import { defineCollection } from 'astro:content';
+import { docsSchema } from '@astrojs/starlight/schema';
+
+export const collections = {
+ docs: defineCollection({ schema: docsSchema() }),
+};
diff --git a/packages/starlight/__e2e__/fixtures/custom-src-dir/www/env.d.ts b/packages/starlight/__e2e__/fixtures/custom-src-dir/www/env.d.ts
new file mode 100644
index 00000000000..acef35f175a
--- /dev/null
+++ b/packages/starlight/__e2e__/fixtures/custom-src-dir/www/env.d.ts
@@ -0,0 +1,2 @@
+///
+///
diff --git a/packages/starlight/__e2e__/fixtures/custom-src-dir/www/pages/custom.astro b/packages/starlight/__e2e__/fixtures/custom-src-dir/www/pages/custom.astro
new file mode 100644
index 00000000000..680eaaef6a7
--- /dev/null
+++ b/packages/starlight/__e2e__/fixtures/custom-src-dir/www/pages/custom.astro
@@ -0,0 +1,7 @@
+---
+import StarlightPage from '@astrojs/starlight/components/StarlightPage.astro';
+---
+
+
+ Hello
+
diff --git a/packages/starlight/__e2e__/test-utils.ts b/packages/starlight/__e2e__/test-utils.ts
index e604732357d..bde93502c42 100644
--- a/packages/starlight/__e2e__/test-utils.ts
+++ b/packages/starlight/__e2e__/test-utils.ts
@@ -4,6 +4,8 @@ import { build, preview } from 'astro';
export { expect, type Locator } from '@playwright/test';
+process.env.ASTRO_TELEMETRY_DISABLED = 'true';
+
// Setup a test environment that will build and start a preview server for a given fixture path and
// provide a Starlight Playwright fixture accessible from within all tests.
export async function testFactory(fixturePath: string) {
diff --git a/packages/starlight/__tests__/basics/config-errors.test.ts b/packages/starlight/__tests__/basics/config-errors.test.ts
index e48ea1a74e1..1639bd72e94 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/__tests__/remark-rehype/asides.test.ts b/packages/starlight/__tests__/remark-rehype/asides.test.ts
index cb145e12b5e..a46f8e0d73d 100644
--- a/packages/starlight/__tests__/remark-rehype/asides.test.ts
+++ b/packages/starlight/__tests__/remark-rehype/asides.test.ts
@@ -1,6 +1,8 @@
import { createMarkdownProcessor } from '@astrojs/markdown-remark';
+import type { Root } from 'mdast';
+import { visit } from 'unist-util-visit';
import { describe, expect, test } from 'vitest';
-import { starlightAsides } from '../../integrations/asides';
+import { starlightAsides, remarkDirectivesRestoration } from '../../integrations/asides';
import { createTranslationSystemFromFs } from '../../utils/translations-fs';
import { StarlightConfigSchema, type StarlightUserConfig } from '../../utils/user-config';
@@ -23,6 +25,9 @@ const processor = await createMarkdownProcessor({
astroConfig: { root: new URL(import.meta.url), srcDir: new URL('./_src/', import.meta.url) },
useTranslations,
}),
+ // The restoration plugin is run after the asides and any other plugin that may have been
+ // injected by Starlight plugins.
+ remarkDirectivesRestoration,
],
});
@@ -167,13 +172,14 @@ test('runs without locales config', async () => {
},
useTranslations,
}),
+ remarkDirectivesRestoration,
],
});
const res = await processor.render(':::note\nTest\n::');
expect(res.code.includes('aria-label=Note"'));
});
-test('tranforms back unhandled text directives', async () => {
+test('transforms back unhandled text directives', async () => {
const res = await processor.render(
`This is a:test of a sentence with a text:name[content]{key=val} directive.`
);
@@ -184,10 +190,47 @@ test('tranforms back unhandled text directives', async () => {
`);
});
-test('tranforms back unhandled leaf directives', async () => {
+test('transforms back unhandled leaf directives', async () => {
const res = await processor.render(`::video[Title]{v=xxxxxxxxxxx}`);
expect(res.code).toMatchInlineSnapshot(`
"::video[Title]{v="xxxxxxxxxxx"}
"
`);
});
+
+test('lets remark plugin injected by Starlight plugins handle text and leaf directives', async () => {
+ const processor = await createMarkdownProcessor({
+ remarkPlugins: [
+ ...starlightAsides({
+ starlightConfig,
+ astroConfig: {
+ root: new URL(import.meta.url),
+ srcDir: new URL('./_src/', import.meta.url),
+ },
+ useTranslations,
+ }),
+ // A custom remark plugin injected by a Starlight plugin through an Astro integration would
+ // run before the restoration plugin.
+ function customRemarkPlugin() {
+ return function transformer(tree: Root) {
+ visit(tree, (node, index, parent) => {
+ if (node.type !== 'textDirective' || typeof index !== 'number' || !parent) return;
+ if (node.name === 'abbr') {
+ parent.children.splice(index, 1, { type: 'text', value: 'TEXT FROM REMARK PLUGIN' });
+ }
+ });
+ };
+ },
+ remarkDirectivesRestoration,
+ ],
+ });
+
+ const res = await processor.render(
+ `This is a:test of a sentence with a :abbr[SL]{name="Starlight"} directive handled by another remark plugin and some other text:name[content]{key=val} directives not handled by any plugin.`
+ );
+ expect(res.code).toMatchInlineSnapshot(`
+ "This is a:test
+ of a sentence with a TEXT FROM REMARK PLUGIN directive handled by another remark plugin and some other text:name[content]{key="val"}
+ directives not handled by any plugin.
"
+ `);
+});
diff --git a/packages/starlight/__tests__/remark-rehype/rehype-steps.test.ts b/packages/starlight/__tests__/remark-rehype/rehype-steps.test.ts
index 36f4082c5bf..a7081023c25 100644
--- a/packages/starlight/__tests__/remark-rehype/rehype-steps.test.ts
+++ b/packages/starlight/__tests__/remark-rehype/rehype-steps.test.ts
@@ -70,7 +70,7 @@ test('applies `role="list"` to child list', () => {
test('does not interfere with other attributes on the child list', () => {
const { html } = processSteps('- Step one
');
expect(html).toMatchInlineSnapshot(
- `"- Step one
"`
+ `"- Step one
"`
);
});
@@ -87,3 +87,16 @@ test('applies class name and preserves existing classes on a child list', () =>
`"- Step one
"`
);
});
+
+test('applies custom property if start attribute is used', () => {
+ const start = 10;
+ const { html } = processSteps(`- Step one
`);
+ expect(html).toContain(`style="--sl-steps-start: ${start - 1}"`);
+});
+
+test('custom property for start count does not interfere with custom styles', () => {
+ const { html } = processSteps(`- Step one
`);
+ expect(html).toMatchInlineSnapshot(
+ `"- Step one
"`
+ );
+});
diff --git a/packages/starlight/components/Search.astro b/packages/starlight/components/Search.astro
index 0fd2870ae69..713e9415136 100644
--- a/packages/starlight/components/Search.astro
+++ b/packages/starlight/components/Search.astro
@@ -223,7 +223,7 @@ const pagefindTranslations = {
box-shadow: var(--sl-shadow-lg);
}
dialog[open] {
- display: grid;
+ display: flex;
}
dialog::backdrop {
@@ -234,6 +234,7 @@ const pagefindTranslations = {
.dialog-frame {
flex-direction: column;
+ flex-grow: 1;
gap: 1rem;
padding: 1rem;
}
diff --git a/packages/starlight/components/Select.astro b/packages/starlight/components/Select.astro
index 0975a678939..8be1685985c 100644
--- a/packages/starlight/components/Select.astro
+++ b/packages/starlight/components/Select.astro
@@ -31,6 +31,7 @@ interface Props {
label {
--sl-label-icon-size: 0.875rem;
--sl-caret-size: 1.25rem;
+ --sl-inline-padding: 0.5rem;
position: relative;
display: flex;
align-items: center;
@@ -62,8 +63,10 @@ interface Props {
select {
border: 0;
padding-block: 0.625rem;
- padding-inline: calc(var(--sl-label-icon-size) + 0.25rem) calc(var(--sl-caret-size) + 0.25rem);
- width: var(--sl-select-width);
+ padding-inline: calc(var(--sl-label-icon-size) + var(--sl-inline-padding) + 0.25rem)
+ calc(var(--sl-caret-size) + var(--sl-inline-padding) + 0.25rem);
+ margin-inline: calc(var(--sl-inline-padding) * -1);
+ width: calc(var(--sl-select-width) + var(--sl-inline-padding) * 2);
background-color: transparent;
text-overflow: ellipsis;
color: inherit;
diff --git a/packages/starlight/index.ts b/packages/starlight/index.ts
index a7876ab80dd..c13c03bcd3f 100644
--- a/packages/starlight/index.ts
+++ b/packages/starlight/index.ts
@@ -3,7 +3,7 @@ import type { AstroIntegration } from 'astro';
import { spawn } from 'node:child_process';
import { dirname, relative } from 'node:path';
import { fileURLToPath } from 'node:url';
-import { starlightAsides } from './integrations/asides';
+import { starlightAsides, starlightDirectivesRestorationIntegration } from './integrations/asides';
import { starlightExpressiveCode } from './integrations/expressive-code/index';
import { starlightSitemap } from './integrations/sitemap';
import { vitePluginStarlightUserConfig } from './integrations/virtual-user-config';
@@ -73,6 +73,10 @@ export default function StarlightIntegration({
if (!allIntegrations.find(({ name }) => name === '@astrojs/mdx')) {
integrations.push(mdx({ optimize: true }));
}
+ // Add Starlight directives restoration integration at the end of the list so that remark
+ // plugins injected by Starlight plugins through Astro integrations can handle text and
+ // leaf directives before they are transformed back to their original form.
+ integrations.push(starlightDirectivesRestorationIntegration());
// Add integrations immediately after Starlight in the config array.
// e.g. if a user has `integrations: [starlight(), tailwind()]`, then the order will be
diff --git a/packages/starlight/integrations/asides.ts b/packages/starlight/integrations/asides.ts
index 2b03b372ee5..3218e509c45 100644
--- a/packages/starlight/integrations/asides.ts
+++ b/packages/starlight/integrations/asides.ts
@@ -1,6 +1,6 @@
///
-import type { AstroConfig, AstroUserConfig } from 'astro';
+import type { AstroConfig, AstroIntegration, AstroUserConfig } from 'astro';
import { h as _h, s as _s, type Properties } from 'hastscript';
import type { Node, Paragraph as P, Parent, Root } from 'mdast';
import {
@@ -146,7 +146,6 @@ function remarkAsides(options: AsidesOptions): Plugin<[], Root> {
return;
}
if (node.type === 'textDirective' || node.type === 'leafDirective') {
- transformUnhandledDirective(node, index, parent);
return;
}
const variant = node.name;
@@ -210,3 +209,43 @@ type RemarkPlugins = NonNullable['remar
export function starlightAsides(options: AsidesOptions): RemarkPlugins {
return [remarkDirective, remarkAsides(options)];
}
+
+export function remarkDirectivesRestoration() {
+ return function transformer(tree: Root) {
+ visit(tree, (node, index, parent) => {
+ if (
+ index !== undefined &&
+ parent &&
+ (node.type === 'textDirective' || node.type === 'leafDirective')
+ ) {
+ transformUnhandledDirective(node, index, parent);
+ return;
+ }
+ });
+ };
+}
+
+/**
+ * Directives not handled by Starlight are transformed back to their original form to avoid
+ * breaking user content.
+ * To allow remark plugins injected by Starlight plugins through Astro integrations to handle
+ * such directives, we need to restore unhandled text and leaf directives back to their original
+ * form only after all these other plugins have run.
+ * To do so, we run a remark plugin restoring these directives back to their original form from
+ * another Astro integration that runs after all the ones that may have been injected by Starlight
+ * plugins.
+ */
+export function starlightDirectivesRestorationIntegration(): AstroIntegration {
+ return {
+ name: 'starlight-directives-restoration',
+ hooks: {
+ 'astro:config:setup': ({ updateConfig }) => {
+ updateConfig({
+ markdown: {
+ remarkPlugins: [remarkDirectivesRestoration],
+ },
+ });
+ },
+ },
+ };
+}
diff --git a/packages/starlight/integrations/virtual-user-config.ts b/packages/starlight/integrations/virtual-user-config.ts
index b51f8e3877d..d955ad50c43 100644
--- a/packages/starlight/integrations/virtual-user-config.ts
+++ b/packages/starlight/integrations/virtual-user-config.ts
@@ -50,7 +50,7 @@ export function vitePluginStarlightUserConfig(
: 'export const logos = {};',
'virtual:starlight/collection-config': `let userCollections;
try {
- userCollections = (await import('/src/content/config.ts')).collections;
+ userCollections = (await import('${new URL('./content/config.ts', srcDir).pathname}')).collections;
} catch {}
export const collections = userCollections;`,
...virtualComponentModules,
diff --git a/packages/starlight/package.json b/packages/starlight/package.json
index ed352f4278c..020256f807d 100644
--- a/packages/starlight/package.json
+++ b/packages/starlight/package.json
@@ -1,6 +1,6 @@
{
"name": "@astrojs/starlight",
- "version": "0.24.2",
+ "version": "0.24.5",
"description": "Build beautiful, high-performance documentation websites with Astro",
"scripts": {
"test": "vitest",
@@ -180,7 +180,7 @@
},
"devDependencies": {
"@astrojs/markdown-remark": "^5.1.0",
- "@playwright/test": "^1.44.1",
+ "@playwright/test": "^1.45.0",
"@types/node": "^18.16.19",
"@vitest/coverage-v8": "^1.6.0",
"astro": "^4.10.2",
diff --git a/packages/starlight/playwright.config.ts b/packages/starlight/playwright.config.ts
index 01098ea8b0d..257348a1e66 100644
--- a/packages/starlight/playwright.config.ts
+++ b/packages/starlight/playwright.config.ts
@@ -12,4 +12,6 @@ export default defineConfig({
},
],
testMatch: '__e2e__/*.test.ts',
+ timeout: 40 * 1_000,
+ workers: 1,
});
diff --git a/packages/starlight/schemas/sidebar.ts b/packages/starlight/schemas/sidebar.ts
index 86eb7f87e48..41b1c3ae29f 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,
diff --git a/packages/starlight/translations/cs.json b/packages/starlight/translations/cs.json
index f3a83731b18..7ec5014c05e 100644
--- a/packages/starlight/translations/cs.json
+++ b/packages/starlight/translations/cs.json
@@ -18,12 +18,25 @@
"page.lastUpdated": "Poslední aktualizace:",
"page.previousLink": "Předchozí",
"page.nextLink": "Další",
- "page.draft": "This content is a draft and will not be included in production builds.",
- "404.text": "Stránka nenalezena. Zkontrolujte adresu URL nebo zkuste použít vyhledávací pole.",
- "aside.note": "Note",
+ "page.draft": "Tento obsah je koncept a nebude zahrnutý v produkčním sestavení.",
+ "404.text": "Stránka nenalezena. Zkontrolujte adresu nebo zkuste použít vyhledávač",
+ "aside.note": "Poznámka",
"aside.tip": "Tip",
- "aside.caution": "Caution",
- "aside.danger": "Danger",
- "fileTree.directory": "Directory",
- "builtWithStarlight.label": "Built with Starlight"
+ "aside.caution": "Upozornění",
+ "aside.danger": "Nebezpečí",
+ "fileTree.directory": "Adresář",
+ "builtWithStarlight.label": "Postavené s Starlight",
+ "expressiveCode.copyButtonCopied": "Zkopírováno!",
+ "expressiveCode.copyButtonTooltip": "Kopíruj do schránky",
+ "expressiveCode.terminalWindowFallbackTitle": "Terminál",
+ "pagefind.clear_search": "Vyčistit",
+ "pagefind.load_more": "Načíst další výsledky",
+ "pagefind.search_label": "Vyhledat stránku",
+ "pagefind.filters_label": "Filtry",
+ "pagefind.zero_results": "Žádný výsledek pro: [SEARCH_TERM]",
+ "pagefind.many_results": "počet výsledků: [COUNT] pro: [SEARCH_TERM]",
+ "pagefind.one_result": "[COUNT] výsledek pro: [SEARCH_TERM]",
+ "pagefind.alt_search": "Žádné výsledky pro [SEARCH_TERM]. Namísto toho zobrazuji výsledky pro: [DIFFERENT_TERM]",
+ "pagefind.search_suggestion": "Žádný výsledek pro [SEARCH_TERM]. Zkus nějaké z těchto hledání:",
+ "pagefind.searching": "Hledám [SEARCH_TERM]..."
}
diff --git a/packages/starlight/translations/nb.json b/packages/starlight/translations/nb.json
index f4b05755dab..14099cda092 100644
--- a/packages/starlight/translations/nb.json
+++ b/packages/starlight/translations/nb.json
@@ -20,10 +20,10 @@
"page.nextLink": "Neste",
"page.draft": "This content is a draft and will not be included in production builds.",
"404.text": "Siden ble ikke funnet. Sjekk URL-en eller prøv å bruke søkefeltet.",
- "aside.note": "Note",
- "aside.tip": "Tip",
- "aside.caution": "Caution",
- "aside.danger": "Danger",
- "fileTree.directory": "Directory",
+ "aside.note": "Merknad",
+ "aside.tip": "Tips",
+ "aside.caution": "Advarsel",
+ "aside.danger": "Fare",
+ "fileTree.directory": "Mappe",
"builtWithStarlight.label": "Laget med Starlight"
}
diff --git a/packages/starlight/translations/ru.json b/packages/starlight/translations/ru.json
index 9b91bed3a72..4175f290c4e 100644
--- a/packages/starlight/translations/ru.json
+++ b/packages/starlight/translations/ru.json
@@ -28,5 +28,5 @@
"expressiveCode.copyButtonCopied": "Скопировано!",
"expressiveCode.copyButtonTooltip": "Копировать",
"expressiveCode.terminalWindowFallbackTitle": "Окно терминала",
- "builtWithStarlight.label": "Built with Starlight"
+ "builtWithStarlight.label": "Сделано с помощью Starlight"
}
diff --git a/packages/starlight/translations/uk.json b/packages/starlight/translations/uk.json
index 8f6cf52bfd0..eba7051d7f0 100644
--- a/packages/starlight/translations/uk.json
+++ b/packages/starlight/translations/uk.json
@@ -3,7 +3,7 @@
"search.label": "Пошук",
"search.ctrlKey": "Ctrl",
"search.cancelLabel": "Скасувати",
- "search.devWarning": "Пошук доступний лише у виробничих зборках. \nСпробуйте зібрати та переглянути сайт, щоби протестувати його локально",
+ "search.devWarning": "Пошук доступний лише у виробничих збірках. \nСпробуйте зібрати та переглянути сайт, щоби протестувати його локально",
"themeSelect.accessibleLabel": "Обрати тему",
"themeSelect.dark": "Темна",
"themeSelect.light": "Світла",
@@ -18,12 +18,12 @@
"page.lastUpdated": "Останнє оновлення:",
"page.previousLink": "Назад",
"page.nextLink": "Далі",
- "page.draft": "This content is a draft and will not be included in production builds.",
+ "page.draft": "Цей контент є чернеткою і не буде включений до виробничих збірок.",
"404.text": "Сторінку не знайдено. Перевірте URL або спробуйте скористатися пошуком.",
"aside.note": "Заувага",
"aside.tip": "Порада",
"aside.caution": "Обережно",
"aside.danger": "Небезпечно",
- "fileTree.directory": "Directory",
- "builtWithStarlight.label": "Built with Starlight"
+ "fileTree.directory": "Каталог",
+ "builtWithStarlight.label": "Створено з Starlight"
}
diff --git a/packages/starlight/user-components/Steps.astro b/packages/starlight/user-components/Steps.astro
index ffd65a35c6d..66562555437 100644
--- a/packages/starlight/user-components/Steps.astro
+++ b/packages/starlight/user-components/Steps.astro
@@ -13,10 +13,12 @@ const { html } = processSteps(content);
--bullet-margin: 0.375rem;
list-style: none;
+ counter-reset: steps-counter var(--sl-steps-start, 0);
padding-inline-start: 0;
}
.sl-steps > li {
+ counter-increment: steps-counter;
position: relative;
padding-inline-start: calc(var(--bullet-size) + 1rem);
/* HACK: Keeps any `margin-bottom` inside the ``’s padding box to avoid gaps in the hairline border. */
@@ -31,7 +33,7 @@ const { html } = processSteps(content);
/* Custom list marker element. */
.sl-steps > li::before {
- content: counter(list-item);
+ content: counter(steps-counter);
position: absolute;
top: 0;
inset-inline-start: 0;
diff --git a/packages/starlight/user-components/rehype-steps.ts b/packages/starlight/user-components/rehype-steps.ts
index ff9c9593f85..184df00ef08 100644
--- a/packages/starlight/user-components/rehype-steps.ts
+++ b/packages/starlight/user-components/rehype-steps.ts
@@ -44,6 +44,14 @@ const stepsProcessor = rehype()
} else {
rootElement.properties.className.push('sl-steps');
}
+
+ // Add the `start` attribute as a CSS custom property so we can use it as the starting index
+ // of the steps custom counter.
+ if (typeof rootElement.properties.start === 'number') {
+ const styles = [`--sl-steps-start: ${rootElement.properties.start - 1}`];
+ if (rootElement.properties.style) styles.push(String(rootElement.properties.style));
+ rootElement.properties.style = styles.join(';');
+ }
};
});
diff --git a/packages/starlight/vitest.config.ts b/packages/starlight/vitest.config.ts
index 5440eaa3347..70ec5159a03 100644
--- a/packages/starlight/vitest.config.ts
+++ b/packages/starlight/vitest.config.ts
@@ -1,20 +1,4 @@
-import { defineConfig } from 'vitest/config';
-
-// Copy of https://github.com/vitest-dev/vitest/blob/8693449b412743f20a63fd9bfa1a9054aa74613f/packages/vitest/src/defaults.ts#L13C1-L26C1
-const defaultCoverageExcludes = [
- 'coverage/**',
- 'dist/**',
- 'packages/*/test?(s)/**',
- '**/*.d.ts',
- 'cypress/**',
- 'test?(s)/**',
- 'test?(-*).?(c|m)[jt]s?(x)',
- '**/*{.,-}{test,spec}.?(c|m)[jt]s?(x)',
- '**/__tests__/**',
- '**/__e2e__/**',
- '**/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,playwright}.config.*',
- '**/.{eslint,mocha,prettier}rc.{?(c|m)js,yml}',
-];
+import { coverageConfigDefaults, defineConfig } from 'vitest/config';
export default defineConfig({
test: {
@@ -22,7 +6,9 @@ export default defineConfig({
all: true,
reportsDirectory: './__coverage__',
exclude: [
- ...defaultCoverageExcludes,
+ ...coverageConfigDefaults.exclude,
+ '**/__e2e__/**',
+ 'playwright.config.*',
'**/vitest.*',
'components.ts',
'types.ts',
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f5f0f83a65e..37078d8d1cb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -45,8 +45,8 @@ importers:
specifier: workspace:*
version: link:../packages/starlight
'@lunariajs/core':
- specifier: ^0.1.0
- version: 0.1.0
+ specifier: ^0.1.1
+ version: 0.1.1
'@types/culori':
specifier: ^2.0.0
version: 2.0.0
@@ -73,7 +73,7 @@ importers:
examples/basics:
dependencies:
'@astrojs/starlight':
- specifier: ^0.24.2
+ specifier: ^0.24.5
version: link:../../packages/starlight
astro:
specifier: ^4.10.2
@@ -85,7 +85,7 @@ importers:
examples/tailwind:
dependencies:
'@astrojs/starlight':
- specifier: ^0.24.2
+ specifier: ^0.24.5
version: link:../../packages/starlight
'@astrojs/starlight-tailwind':
specifier: ^2.0.3
@@ -195,8 +195,8 @@ importers:
specifier: ^5.1.0
version: 5.1.0
'@playwright/test':
- specifier: ^1.44.1
- version: 1.44.1
+ specifier: ^1.45.0
+ version: 1.45.0
'@types/node':
specifier: ^18.16.19
version: 18.16.19
@@ -219,6 +219,15 @@ importers:
specifier: ^4.10.2
version: 4.10.2(@types/node@18.16.19)(typescript@5.4.5)
+ packages/starlight/__e2e__/fixtures/custom-src-dir:
+ dependencies:
+ '@astrojs/starlight':
+ specifier: workspace:*
+ version: link:../../..
+ astro:
+ specifier: ^4.10.2
+ version: 4.10.2(@types/node@18.16.19)(typescript@5.4.5)
+
packages/tailwind:
dependencies:
'@astrojs/starlight':
@@ -1725,8 +1734,8 @@ packages:
resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==}
dev: false
- /@lunariajs/core@0.1.0:
- resolution: {integrity: sha512-UP2K3fjgmPP4eN92ZcreAzWitRfeqhMMSHeh2GK2FtzReYWoHeth7cogzNv9Glb64UA2PFJkZ3fU5DAdX53w+g==}
+ /@lunariajs/core@0.1.1:
+ resolution: {integrity: sha512-sAqM9+DVsLe3xHM9wu2pEnKGYMs/bWS9qpR+CGHol3RihOELnOQTzHddXbdB1MtgesbI8dnQuG64Ocd8KkWsng==}
engines: {node: '>=18.17.0'}
hasBin: true
dependencies:
@@ -1856,12 +1865,12 @@ packages:
dev: false
optional: true
- /@playwright/test@1.44.1:
- resolution: {integrity: sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==}
- engines: {node: '>=16'}
+ /@playwright/test@1.45.0:
+ resolution: {integrity: sha512-TVYsfMlGAaxeUllNkywbwek67Ncf8FRGn8ZlRdO291OL3NjG9oMbfVhyP82HQF0CZLMrYsvesqoUekxdWuF9Qw==}
+ engines: {node: '>=18'}
hasBin: true
dependencies:
- playwright: 1.44.1
+ playwright: 1.45.0
dev: true
/@rollup/rollup-android-arm-eabi@4.17.2:
@@ -5841,18 +5850,18 @@ packages:
pathe: 1.1.2
dev: true
- /playwright-core@1.44.1:
- resolution: {integrity: sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==}
- engines: {node: '>=16'}
+ /playwright-core@1.45.0:
+ resolution: {integrity: sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ==}
+ engines: {node: '>=18'}
hasBin: true
dev: true
- /playwright@1.44.1:
- resolution: {integrity: sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==}
- engines: {node: '>=16'}
+ /playwright@1.45.0:
+ resolution: {integrity: sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA==}
+ engines: {node: '>=18'}
hasBin: true
dependencies:
- playwright-core: 1.44.1
+ playwright-core: 1.45.0
optionalDependencies:
fsevents: 2.3.2
dev: true