diff --git a/.changeset/tricky-dolls-cover.md b/.changeset/tricky-dolls-cover.md new file mode 100644 index 00000000000..5574c309b9f --- /dev/null +++ b/.changeset/tricky-dolls-cover.md @@ -0,0 +1,5 @@ +--- +'@astrojs/starlight': patch +--- + +Fixes an issue where aside icons were rendered incorrectly in projects where Astro’s MDX integration had optimization disabled diff --git a/packages/starlight/__e2e__/fixtures/no-mdx-optimization/astro.config.mjs b/packages/starlight/__e2e__/fixtures/no-mdx-optimization/astro.config.mjs new file mode 100644 index 00000000000..5d063eb8fca --- /dev/null +++ b/packages/starlight/__e2e__/fixtures/no-mdx-optimization/astro.config.mjs @@ -0,0 +1,15 @@ +// @ts-check +import mdx from '@astrojs/mdx'; +import starlight from '@astrojs/starlight'; +import { defineConfig } from 'astro/config'; + +export default defineConfig({ + integrations: [ + starlight({ + title: 'Basics', + pagefind: false, + }), + // Starlight sets `optimize: true`, so we provide our own copy of the MDX integration to test without optimization. + mdx({ optimize: false }), + ], +}); diff --git a/packages/starlight/__e2e__/fixtures/no-mdx-optimization/package.json b/packages/starlight/__e2e__/fixtures/no-mdx-optimization/package.json new file mode 100644 index 00000000000..390b6be783c --- /dev/null +++ b/packages/starlight/__e2e__/fixtures/no-mdx-optimization/package.json @@ -0,0 +1,9 @@ +{ + "name": "@e2e/no-mdx-optimization", + "version": "0.0.0", + "private": true, + "dependencies": { + "@astrojs/starlight": "workspace:*", + "astro": "^7.0.2" + } +} diff --git a/packages/starlight/__e2e__/fixtures/no-mdx-optimization/src/content.config.ts b/packages/starlight/__e2e__/fixtures/no-mdx-optimization/src/content.config.ts new file mode 100644 index 00000000000..080e5813f72 --- /dev/null +++ b/packages/starlight/__e2e__/fixtures/no-mdx-optimization/src/content.config.ts @@ -0,0 +1,7 @@ +import { docsLoader } from '@astrojs/starlight/loaders'; +import { docsSchema } from '@astrojs/starlight/schema'; +import { defineCollection } from 'astro:content'; + +export const collections = { + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), +}; diff --git a/packages/starlight/__e2e__/fixtures/no-mdx-optimization/src/content/docs/asides.mdx b/packages/starlight/__e2e__/fixtures/no-mdx-optimization/src/content/docs/asides.mdx new file mode 100644 index 00000000000..a9f631bd73e --- /dev/null +++ b/packages/starlight/__e2e__/fixtures/no-mdx-optimization/src/content/docs/asides.mdx @@ -0,0 +1,19 @@ +--- +title: Asides +--- + +:::note +Directive aside +::: + +:::tip{icon="heart"} +Directive aside with custom icon +::: + +import { Aside } from '@astrojs/starlight/components'; + + + + diff --git a/packages/starlight/__e2e__/no-mdx-optimization.test.ts b/packages/starlight/__e2e__/no-mdx-optimization.test.ts new file mode 100644 index 00000000000..d66b86ce5d5 --- /dev/null +++ b/packages/starlight/__e2e__/no-mdx-optimization.test.ts @@ -0,0 +1,19 @@ +import { expect, testFactory } from './test-utils'; + +const test = testFactory('./fixtures/no-mdx-optimization/'); + +test.describe('no MDX optimization', () => { + test.describe('asides', () => { + test('asides render an SVG icon', async ({ page, getProdServer }) => { + const starlight = await getProdServer(); + await starlight.goto('/asides/'); + + const asideIconsLocator = page.locator('.starlight-aside__title svg'); + + // All four aside instances should have an icon, so we expect four icons to be rendered. + // This checks an issue where SVGs were escaped and rendered as text with MDX optimization + // disabled. See https://github.com/withastro/starlight/pull/3967#issuecomment-4833368829 + await expect(asideIconsLocator).toHaveCount(4); + }); + }); +}); diff --git a/packages/starlight/integrations/satteri.ts b/packages/starlight/integrations/satteri.ts index 32a0907d892..7fc25a006ae 100644 --- a/packages/starlight/integrations/satteri.ts +++ b/packages/starlight/integrations/satteri.ts @@ -103,6 +103,15 @@ function satteriAsidesPlugin( const icon = getAsideIcon(variant, node.attributes?.['icon']); const iconSvg = `${icon}`; + // Markdown and MDX require different AST shapes for raw HTML content. + // TODO: replace endsWith check with an official Sätteri API once available. + const iconNode = ctx.fileURL?.pathname.endsWith('.mdx') + ? { + type: 'mdxJsxTextElement', + name: 'Fragment', + attributes: [{ type: 'mdxJsxAttribute', name: 'set:html', value: iconSvg }], + } + : { type: 'html', value: iconSvg }; return paragraphElement( 'aside', @@ -112,7 +121,7 @@ function satteriAsidesPlugin( }, [ paragraphElement('p', { class: 'starlight-aside__title', 'aria-hidden': 'true' }, [ - { type: 'html', value: iconSvg }, + iconNode, ...titleNode, ]), paragraphElement('div', { class: 'starlight-aside__content' }, children), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 281db62031b..f1bee5817ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -322,6 +322,15 @@ importers: specifier: ^7.0.2 version: 7.0.2(@astrojs/markdown-remark@7.2.0)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.6.1)(yaml@2.8.3) + packages/starlight/__e2e__/fixtures/no-mdx-optimization: + dependencies: + '@astrojs/starlight': + specifier: workspace:* + version: link:../../.. + astro: + specifier: ^7.0.2 + version: 7.0.2(@astrojs/markdown-remark@7.2.0)(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)(@types/node@24.12.4)(jiti@2.6.1)(yaml@2.8.3) + packages/starlight/__e2e__/fixtures/ssr: dependencies: '@astrojs/node':