Skip to content

Commit 550f661

Browse files
authored
Validate links to headings inside of partials (#2174)
1 parent a3f322d commit 550f661

File tree

4 files changed

+96
-0
lines changed

4 files changed

+96
-0
lines changed

package-lock.json

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"remark-mdx": "^3.0.1",
3030
"tsx": "^4.19.2",
3131
"typescript": "^5.7.3",
32+
"unist-util-map": "^4.0.0",
3233
"unist-util-visit": "^5.0.0",
3334
"vfile": "^6.0.1",
3435
"vfile-reporter": "^8.0.0",

scripts/build-docs.test.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,53 @@ title: Simple Test
388388

389389
expect(output).toContain(`warning <Include /> prop "src" must start with "_partials/"`)
390390
})
391+
392+
test('Should validate heading within a partial', async () => {
393+
const { tempDir } = await createTempFiles([
394+
{
395+
path: './docs/manifest.json',
396+
content: JSON.stringify({
397+
navigation: [
398+
[{ title: 'Simple Test', href: '/docs/test-page-1' }],
399+
[{ title: 'Test Page 2', href: '/docs/test-page-2' }],
400+
],
401+
}),
402+
},
403+
{
404+
path: './docs/_partials/test-partial.mdx',
405+
content: `# Heading`,
406+
},
407+
{
408+
path: './docs/test-page-1.mdx',
409+
content: `---
410+
title: Test Page 1
411+
description: This is a test page
412+
---
413+
414+
<Include src="_partials/test-partial" />`,
415+
},
416+
{
417+
path: './docs/test-page-2.mdx',
418+
content: `---
419+
title: Test Page 2
420+
description: This is a test page
421+
---
422+
423+
[Test Page](/docs/test-page-1#heading)`,
424+
},
425+
])
426+
427+
const output = await build(
428+
createConfig({
429+
...baseConfig,
430+
basePath: tempDir,
431+
validSdks: ['react'],
432+
}),
433+
)
434+
435+
expect(output).not.toContain(`warning Hash "heading" not found in /docs/test-page-1`)
436+
expect(output).toBe('')
437+
})
391438
})
392439

393440
describe('Link Validation and Processing', () => {

scripts/build-docs.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import path from 'node:path'
1515
import remarkMdx from 'remark-mdx'
1616
import { remark } from 'remark'
1717
import { visit as mdastVisit } from 'unist-util-visit'
18+
import { map as mdastMap } from 'unist-util-map'
1819
import remarkFrontmatter from 'remark-frontmatter'
1920
import yaml from 'yaml'
2021
import { slugifyWithCounter } from '@sindresorhus/slugify'
@@ -682,6 +683,38 @@ const parseInMarkdownFile =
682683
return
683684
})
684685
})
686+
.process({
687+
path: `${href.substring(1)}.mdx`,
688+
value: fileContent,
689+
})
690+
691+
// This needs to be done separately as some further validation expects the partials to not be embedded
692+
// but we need to embed it to get all the headings to check
693+
await markdownProcessor()
694+
// Embed the partial
695+
.use(() => (tree, vfile) => {
696+
return mdastMap(tree, (node) => {
697+
const partialSrc = extractComponentPropValueFromNode(config, node, vfile, 'Include', 'src', true, filePath)
698+
699+
if (partialSrc === undefined) return node
700+
701+
if (partialSrc.startsWith('_partials/') === false) {
702+
safeMessage(config, vfile, filePath, 'include-src-not-partials', [], node.position)
703+
return node
704+
}
705+
706+
const partial = partials.find(
707+
(partial) => `_partials/${partial.path}` === `${removeMdxSuffix(partialSrc)}.mdx`,
708+
)
709+
710+
if (partial === undefined) {
711+
safeMessage(config, vfile, filePath, 'partial-not-found', [removeMdxSuffix(partialSrc)], node.position)
712+
return node
713+
}
714+
715+
return Object.assign(node, partial.node)
716+
})
717+
})
685718
// extract out the headings to check hashes in links
686719
.use(() => (tree) => {
687720
mdastVisit(

0 commit comments

Comments
 (0)