Skip to content

Commit 2ed04f0

Browse files
committed
refactor(tests): update anchor link tests to skip rehypeAutolinkHeadings for remote markdown content
1 parent 66dd7d5 commit 2ed04f0

2 files changed

Lines changed: 81 additions & 74 deletions

File tree

packages/starlight/__tests__/remark-rehype/anchor-links.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,9 @@ test('should correctly autolink headings in remote markdown content using rehype
8181
const res = await renderMarkdown(`
8282
## Some text
8383
`);
84-
expect(res.code).includes('<span class="sr-only">Section titled “Some text”</span>');
84+
// apply rehypeAutolinkHeadings
85+
// expect(res.code).includes('<span class="sr-only">Section titled “Some text”</span>');
86+
87+
// skip rehypeAutolinkHeadings for remote markdown content
88+
expect(res.code).toMatchInlineSnapshot('"<h2 id="some-text">Some text</h2>"')
8589
});

packages/starlight/integrations/heading-links.ts

Lines changed: 76 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -9,96 +9,99 @@ import type { HookParameters, StarlightConfig } from '../types';
99
import { resolveCollectionPath } from '../utils/collection';
1010

1111
const AnchorLinkIcon = h(
12-
'span',
13-
{ ariaHidden: 'true', class: 'sl-anchor-icon' },
14-
h(
15-
'svg',
16-
{ width: 16, height: 16, viewBox: '0 0 24 24' },
17-
h('path', {
18-
fill: 'currentcolor',
19-
d: 'm12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z',
20-
})
21-
)
12+
'span',
13+
{ ariaHidden: 'true', class: 'sl-anchor-icon' },
14+
h(
15+
'svg',
16+
{ width: 16, height: 16, viewBox: '0 0 24 24' },
17+
h('path', {
18+
fill: 'currentcolor',
19+
d: 'm12.11 15.39-3.88 3.88a2.52 2.52 0 0 1-3.5 0 2.47 2.47 0 0 1 0-3.5l3.88-3.88a1 1 0 0 0-1.42-1.42l-3.88 3.89a4.48 4.48 0 0 0 6.33 6.33l3.89-3.88a1 1 0 1 0-1.42-1.42Zm8.58-12.08a4.49 4.49 0 0 0-6.33 0l-3.89 3.88a1 1 0 0 0 1.42 1.42l3.88-3.88a2.52 2.52 0 0 1 3.5 0 2.47 2.47 0 0 1 0 3.5l-3.88 3.88a1 1 0 1 0 1.42 1.42l3.88-3.89a4.49 4.49 0 0 0 0-6.33ZM8.83 15.17a1 1 0 0 0 1.1.22 1 1 0 0 0 .32-.22l4.92-4.92a1 1 0 0 0-1.42-1.42l-4.92 4.92a1 1 0 0 0 0 1.42Z',
20+
})
21+
)
2222
);
2323

2424
/**
2525
* Add anchor links to headings.
2626
*/
2727
export default function rehypeAutolinkHeadings(
28-
docsCollectionPath: string,
29-
useTranslationsForLang: AutolinkHeadingsOptions['useTranslations'],
30-
absolutePathToLang: AutolinkHeadingsOptions['absolutePathToLang']
28+
docsCollectionPath: string,
29+
useTranslationsForLang: AutolinkHeadingsOptions['useTranslations'],
30+
absolutePathToLang: AutolinkHeadingsOptions['absolutePathToLang']
3131
) {
32-
const transformer: Transformer<Root> = (tree, file) => {
33-
// If the document is not part of the Starlight docs collection, skip it.
34-
if (!normalizePath(file.path).startsWith(docsCollectionPath)) return;
32+
const transformer: Transformer<Root> = (tree, file) => {
33+
// If the content is remote Markdown, skip it.
34+
if (!file?.path) return;
3535

36-
const pageLang = absolutePathToLang(file.path);
37-
const t = useTranslationsForLang(pageLang);
36+
// If the document is not part of the Starlight docs collection, skip it.
37+
if (!normalizePath(file.path).startsWith(docsCollectionPath)) return;
3838

39-
visit(tree, 'element', function (node, index, parent) {
40-
if (!headingRank(node) || !node.properties.id || typeof index !== 'number' || !parent) {
41-
return;
42-
}
39+
const pageLang = absolutePathToLang(file.path);
40+
const t = useTranslationsForLang(pageLang);
4341

44-
const accessibleLabel = t('heading.anchorLabel', {
45-
title: toString(node),
46-
interpolation: { escapeValue: false },
47-
});
42+
visit(tree, 'element', function (node, index, parent) {
43+
if (!headingRank(node) || !node.properties.id || typeof index !== 'number' || !parent) {
44+
return;
45+
}
4846

49-
// Wrap the heading in a div and append the anchor link.
50-
parent.children[index] = h(
51-
'div',
52-
{ class: `sl-heading-wrapper level-${node.tagName}` },
53-
// Heading
54-
node,
55-
// Anchor link
56-
{
57-
type: 'element',
58-
tagName: 'a',
59-
properties: { class: 'sl-anchor-link', href: '#' + node.properties.id },
60-
children: [AnchorLinkIcon, h('span', { class: 'sr-only' }, accessibleLabel)],
61-
}
62-
);
47+
const accessibleLabel = t('heading.anchorLabel', {
48+
title: toString(node),
49+
interpolation: { escapeValue: false },
50+
});
6351

64-
return SKIP;
65-
});
66-
};
52+
// Wrap the heading in a div and append the anchor link.
53+
parent.children[index] = h(
54+
'div',
55+
{ class: `sl-heading-wrapper level-${node.tagName}` },
56+
// Heading
57+
node,
58+
// Anchor link
59+
{
60+
type: 'element',
61+
tagName: 'a',
62+
properties: { class: 'sl-anchor-link', href: '#' + node.properties.id },
63+
children: [AnchorLinkIcon, h('span', { class: 'sr-only' }, accessibleLabel)],
64+
}
65+
);
6766

68-
return function attacher() {
69-
return transformer;
70-
};
67+
return SKIP;
68+
});
69+
};
70+
71+
return function attacher() {
72+
return transformer;
73+
};
7174
}
7275

7376
interface AutolinkHeadingsOptions {
74-
starlightConfig: Pick<StarlightConfig, 'markdown'>;
75-
astroConfig: Pick<AstroConfig, 'srcDir'> & {
76-
experimental: Pick<AstroConfig['experimental'], 'headingIdCompat'>;
77-
};
78-
useTranslations: HookParameters<'config:setup'>['useTranslations'];
79-
absolutePathToLang: HookParameters<'config:setup'>['absolutePathToLang'];
77+
starlightConfig: Pick<StarlightConfig, 'markdown'>;
78+
astroConfig: Pick<AstroConfig, 'srcDir'> & {
79+
experimental: Pick<AstroConfig['experimental'], 'headingIdCompat'>;
80+
};
81+
useTranslations: HookParameters<'config:setup'>['useTranslations'];
82+
absolutePathToLang: HookParameters<'config:setup'>['absolutePathToLang'];
8083
}
8184
type RehypePlugins = NonNullable<NonNullable<AstroUserConfig['markdown']>['rehypePlugins']>;
8285

8386
export const starlightAutolinkHeadings = ({
84-
starlightConfig,
85-
astroConfig,
86-
useTranslations,
87-
absolutePathToLang,
87+
starlightConfig,
88+
astroConfig,
89+
useTranslations,
90+
absolutePathToLang,
8891
}: AutolinkHeadingsOptions): RehypePlugins =>
89-
starlightConfig.markdown.headingLinks
90-
? [
91-
[
92-
rehypeHeadingIds,
93-
{ experimentalHeadingIdCompat: astroConfig.experimental?.headingIdCompat },
94-
],
95-
rehypeAutolinkHeadings(
96-
normalizePath(resolveCollectionPath('docs', astroConfig.srcDir)),
97-
useTranslations,
98-
absolutePathToLang
99-
),
100-
]
101-
: [];
92+
starlightConfig.markdown.headingLinks
93+
? [
94+
[
95+
rehypeHeadingIds,
96+
{ experimentalHeadingIdCompat: astroConfig.experimental?.headingIdCompat },
97+
],
98+
rehypeAutolinkHeadings(
99+
normalizePath(resolveCollectionPath('docs', astroConfig.srcDir)),
100+
useTranslations,
101+
absolutePathToLang
102+
),
103+
]
104+
: [];
102105

103106
/**
104107
* File path separators seems to be inconsistent on Windows when the rehype plugin is used on
@@ -107,7 +110,7 @@ export const starlightAutolinkHeadings = ({
107110
*/
108111
const backSlashRegex = /\\/g;
109112
function normalizePath(path: string) {
110-
return path.replace(backSlashRegex, '/');
113+
return path.replace(backSlashRegex, '/');
111114
}
112115

113116
// This utility is inlined from https://github.com/syntax-tree/hast-util-heading-rank
@@ -119,7 +122,7 @@ function normalizePath(path: string) {
119122
* @returns Rank of the heading or `undefined` if not a heading.
120123
*/
121124
function headingRank(node: Nodes): number | undefined {
122-
const name = node.type === 'element' ? node.tagName.toLowerCase() : '';
123-
const code = name.length === 2 && name.charCodeAt(0) === 104 /* `h` */ ? name.charCodeAt(1) : 0;
124-
return code > 48 /* `0` */ && code < 55 /* `7` */ ? code - 48 /* `0` */ : undefined;
125+
const name = node.type === 'element' ? node.tagName.toLowerCase() : '';
126+
const code = name.length === 2 && name.charCodeAt(0) === 104 /* `h` */ ? name.charCodeAt(1) : 0;
127+
return code > 48 /* `0` */ && code < 55 /* `7` */ ? code - 48 /* `0` */ : undefined;
125128
}

0 commit comments

Comments
 (0)