From 10b93b336cf4e3500e3003635b5afc430284d1a7 Mon Sep 17 00:00:00 2001 From: HiDeoo Date: Tue, 4 Mar 2025 00:24:54 +0100 Subject: Support for `title`, `frame`, and `meta` Markdoc fence attributes (#2931) --- .changeset/healthy-rivers-divide.md | 28 +++ docs/src/content/docs/guides/authoring-content.mdx | 142 ++++++++++++++ packages/markdoc/__tests__/markdoc.test-d.ts | 212 +++++++++++++-------- packages/markdoc/index.mjs | 31 ++- 4 files changed, 326 insertions(+), 87 deletions(-) create mode 100644 .changeset/healthy-rivers-divide.md diff --git a/.changeset/healthy-rivers-divide.md b/.changeset/healthy-rivers-divide.md new file mode 100644 index 00000000..98988154 --- /dev/null +++ b/.changeset/healthy-rivers-divide.md @@ -0,0 +1,28 @@ +--- +'@astrojs/starlight-markdoc': minor +--- + +Adds support for the `title`, `frame`, and `meta` fence attributes to code blocks. + +These new optional attributes add support for Expressive Code [text & line markers](https://expressive-code.com/key-features/text-markers/). The following example renders a code block using a [terminal frame](https://expressive-code.com/key-features/frames/#terminal-frames) with a [title](https://expressive-code.com/key-features/frames/#code-editor-frames): + +````mdoc +```js {% title="editor.exe" frame="terminal" %} +console.log('Hello, world!'); +``` +```` + +Any other text or line markers should be specified using the `meta` fence attribute. For example, the following code block renders a code block using the `diff` syntax combined with the `js` language syntax highlighting and the `markers` text highlighted: + +````mdoc +```diff {% meta="lang=js 'markers'" %} + function thisIsJavaScript() { + // This entire block gets highlighted as JavaScript, + // and we can still add diff markers to it! +- console.log('Old code to be removed') ++ console.log('New and shiny code!') + } +``` +```` + +To learn more about all the available options, check out the [Expressive Code documentation](https://expressive-code.com/key-features/text-markers/#usage-in-markdown--mdx). diff --git a/docs/src/content/docs/guides/authoring-content.mdx b/docs/src/content/docs/guides/authoring-content.mdx index f59d59fd..6598ceb9 100644 --- a/docs/src/content/docs/guides/authoring-content.mdx +++ b/docs/src/content/docs/guides/authoring-content.mdx @@ -246,6 +246,10 @@ Some of the most common examples are shown below: } ``` + + + + ````md ```js {2-3} function demo() { @@ -255,6 +259,23 @@ Some of the most common examples are shown below: ``` ```` + + + + + ````markdoc + ```js {% meta="{2-3}" %} + function demo() { + // This line (#2) and the next one are highlighted + return 'This is line #3 of this snippet'; + } + ``` + ```` + + + + + - [Mark selections of text using the `" "` marker or regular expressions](https://expressive-code.com/key-features/text-markers/#marking-individual-text-inside-lines): ```js "Individual terms" /Even.*supported/ @@ -264,6 +285,10 @@ Some of the most common examples are shown below: } ``` + + + + ````md ```js "Individual terms" /Even.*supported/ // Individual terms can be highlighted, too @@ -273,6 +298,23 @@ Some of the most common examples are shown below: ``` ```` + + + + + ````markdoc + ```js {% meta="'Individual terms' /Even.*supported/" %} + // Individual terms can be highlighted, too + function demo() { + return 'Even regular expressions are supported'; + } + ``` + ```` + + + + + - [Mark text or lines as inserted or deleted with `ins` or `del`](https://expressive-code.com/key-features/text-markers/#selecting-inline-marker-types-mark-ins-del): ```js "return true;" ins="inserted" del="deleted" @@ -283,6 +325,10 @@ Some of the most common examples are shown below: } ``` + + + + ````md ```js "return true;" ins="inserted" del="deleted" function demo() { @@ -293,6 +339,24 @@ Some of the most common examples are shown below: ``` ```` + + + + + ````markdoc + ```js {% meta="'return true;' ins='inserted' del='deleted'" %} + function demo() { + console.log('These are inserted and deleted marker types'); + // The return statement uses the default marker type + return true; + } + ``` + ```` + + + + + - [Combine syntax highlighting with `diff`-like syntax](https://expressive-code.com/key-features/text-markers/#combining-syntax-highlighting-with-diff-like-syntax): ```diff lang="js" @@ -304,6 +368,10 @@ Some of the most common examples are shown below: } ``` + + + + ````md ```diff lang="js" function thisIsJavaScript() { @@ -315,6 +383,25 @@ Some of the most common examples are shown below: ``` ```` + + + + + ````markdoc + ```diff {% meta="lang='js'" %} + function thisIsJavaScript() { + // This entire block gets highlighted as JavaScript, + // and we can still add diff markers to it! + - console.log('Old code to be removed') + + console.log('New and shiny code!') + } + ``` + ```` + + + + + #### Frames and titles Code blocks can be rendered inside a window-like frame. @@ -330,6 +417,21 @@ A code block’s optional title can be set either with a `title="..."` attribute console.log('Hello World!'); ``` + + + + + ````md + ```js + // my-test-file.js + console.log('Hello World!'); + ``` + ```` + + + + + ````md ```js // my-test-file.js @@ -337,30 +439,70 @@ A code block’s optional title can be set either with a `title="..."` attribute ``` ```` + + + + - [Add a title to a Terminal window](https://expressive-code.com/key-features/frames/#terminal-frames) ```bash title="Installing dependencies…" npm install ``` + + + + ````md ```bash title="Installing dependencies…" npm install ``` ```` + + + + + ````markdoc + ```bash {% title="Installing dependencies…" %} + npm install + ``` + ```` + + + + + - [Disable window frames with `frame="none"`](https://expressive-code.com/key-features/frames/#overriding-frame-types) ```bash frame="none" echo "This is not rendered as a terminal despite using the bash language" ``` + + + + ````md ```bash frame="none" echo "This is not rendered as a terminal despite using the bash language" ``` ```` + + + + + ````markdoc + ```bash {% frame="none" %} + echo "This is not rendered as a terminal despite using the bash language" + ``` + ```` + + + + + ## Details Details (also known as “disclosures” or “accordions”) are useful to hide content that is not immediately relevant. diff --git a/packages/markdoc/__tests__/markdoc.test-d.ts b/packages/markdoc/__tests__/markdoc.test-d.ts index 868f2c97..d0cc0067 100644 --- a/packages/markdoc/__tests__/markdoc.test-d.ts +++ b/packages/markdoc/__tests__/markdoc.test-d.ts @@ -1,5 +1,5 @@ import type { ComponentProps, HTMLAttributes } from 'astro/types'; -import { expectTypeOf, test } from 'vitest'; +import { describe, expectTypeOf, test } from 'vitest'; import { Aside, @@ -22,94 +22,138 @@ type UserComponentProps any> = keyof RemoveIndexSignatu >; type MarkdocPreset = typeof import('../index.mjs').StarlightMarkdocPreset; +type MarkdocNodes = keyof MarkdocPreset['nodes']; +type MarkdocNodeAttributes = keyof MarkdocPreset['nodes'][T]['attributes']; type MarkdocTags = keyof MarkdocPreset['tags']; type MarkdocTagAttributes = keyof MarkdocPreset['tags'][T]['attributes']; -test('defines a tag for each user components', () => { - expectTypeOf().toEqualTypeOf>(); +describe('nodes', () => { + test('defines attributes for fenced code blocks with support for some text markers', () => { + type FenceAttributes = MarkdocNodeAttributes<'fence'>; + + // Markdoc default fence attributes are `content` and `language`. + type MarkdocFenceAttributes = 'content' | 'language'; + + // Ensure Markdoc default fence attributes are always mapped. + expectTypeOf< + Extract + >().toEqualTypeOf(); + + type UnsupportedCodeProps = + /** The `code` and `lang` attributes mapping is tested above. */ + | 'code' + | 'lang' + /** Not all `` component props are supported in code fences. */ + | 'class' + | 'locale' + | 'preserveIndent' + | 'useDiffSyntax' + | 'wrap' + /** + * Some props cannot be described using Markdoc attribute validation syntax. + * @see {@link file://./../index.mjs} + */ + | 'mark' + | 'ins' + | 'del'; + + // Ensure all non-unsupported `` component props are mapped. + expectTypeOf>().toEqualTypeOf< + Exclude, UnsupportedCodeProps> + >(); + }); }); -test('defines all `