diff options
author | HiDeoo | 2024-06-27 20:38:58 +0200 |
---|---|---|
committer | GitHub | 2024-06-27 20:38:58 +0200 |
commit | 87e9ad029c9730fca8df66e35828b57cd0872a61 (patch) | |
tree | 9f877d104d0240545adacb5db561b1e6e9f1b272 | |
parent | 42aebfd31a800a2f4b9a7697f1c31da9e8a0cfaa (diff) | |
download | IT.starlight-87e9ad029c9730fca8df66e35828b57cd0872a61.tar.gz IT.starlight-87e9ad029c9730fca8df66e35828b57cd0872a61.tar.bz2 IT.starlight-87e9ad029c9730fca8df66e35828b57cd0872a61.zip |
Let remark plugins injected by Starlight plugins handle Markdown text and leaf directives (#2056)
-rw-r--r-- | .changeset/giant-dryers-fetch.md | 5 | ||||
-rw-r--r-- | packages/starlight/__tests__/remark-rehype/asides.test.ts | 49 | ||||
-rw-r--r-- | packages/starlight/index.ts | 6 | ||||
-rw-r--r-- | packages/starlight/integrations/asides.ts | 43 |
4 files changed, 97 insertions, 6 deletions
diff --git a/.changeset/giant-dryers-fetch.md b/.changeset/giant-dryers-fetch.md new file mode 100644 index 00000000..1d630146 --- /dev/null +++ b/.changeset/giant-dryers-fetch.md @@ -0,0 +1,5 @@ +--- +'@astrojs/starlight': patch +--- + +Fixes an issue preventing remark plugins injected by Starlight plugins to handle Markdown text and leaf directives. diff --git a/packages/starlight/__tests__/remark-rehype/asides.test.ts b/packages/starlight/__tests__/remark-rehype/asides.test.ts index cb145e12..a46f8e0d 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(` "<p>::video[Title]{v="xxxxxxxxxxx"} </p>" `); }); + +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(` + "<p>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.</p>" + `); +}); diff --git a/packages/starlight/index.ts b/packages/starlight/index.ts index a7876ab8..c13c03bc 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 2b03b372..3218e509 100644 --- a/packages/starlight/integrations/asides.ts +++ b/packages/starlight/integrations/asides.ts @@ -1,6 +1,6 @@ /// <reference types="mdast-util-directive" /> -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<NonNullable<AstroUserConfig['markdown']>['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], + }, + }); + }, + }, + }; +} |