Skip to content

Commit a41af3b

Browse files
fix(cli): resolve Markdown snippet references in link targets for broken link checking (#12488)
* fix(cli): resolve Markdown snippet references in link targets for broken link checking Co-Authored-By: ryanstep@buildwithfern.com <ryanstep@umich.edu> * refactor: use existing replaceReferencedMarkdown instead of custom snippet resolution Co-Authored-By: ryanstep@buildwithfern.com <ryanstep@umich.edu> * feat: surface snippet resolution warnings as rule violations Co-Authored-By: ryanstep@buildwithfern.com <ryanstep@umich.edu> * revert: remove snippet warning logging, use NOOP_CONTEXT for snippet resolution Co-Authored-By: ryanstep@buildwithfern.com <ryanstep@umich.edu> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent a9ba95e commit a41af3b

8 files changed

Lines changed: 46 additions & 1 deletion

File tree

packages/cli/cli/versions.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
# yaml-language-server: $schema=../../../fern-versions-yml.schema.json
2+
- version: 3.79.2
3+
changelogEntry:
4+
- summary: |
5+
Fix `fern check --strict-broken-links` to resolve `<Markdown src="..." />` snippets used
6+
inside link targets.
7+
type: fix
8+
createdAt: "2026-02-17"
9+
irVersion: 65
210
- version: 3.79.1
311
changelogEntry:
412
- summary: |

packages/cli/ete-tests/src/tests/broken-links/broken-links.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ describe("fern docs broken-links", () => {
4141
expect(stripAnsi(stdout)).toContain("All checks passed");
4242
}, 20_000);
4343

44+
it("links with Markdown snippet references should be resolved", async () => {
45+
const { stdout } = await runFernCli(["docs", "broken-links"], {
46+
cwd: join(fixturesDir, RelativeFilePath.of("snippet-link")),
47+
reject: false
48+
});
49+
expect(stripAnsi(stdout)).toContain("All checks passed");
50+
}, 20_000);
51+
4452
it("broken links in sections with path property should be detected", async () => {
4553
const { stdout } = await runFernCli(["docs", "broken-links"], {
4654
cwd: join(fixturesDir, RelativeFilePath.of("section-with-path")),
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
instances:
2+
- url: docs.example.com
3+
4+
navigation:
5+
- page: Test
6+
path: test.mdx
7+
- page: Getting Started
8+
path: getting-started.mdx
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"organization": "test-org",
3+
"version": "0.0.0"
4+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Getting Started
2+
3+
Welcome to the getting started guide.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/getting-started
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Test Snippet Links
2+
3+
This page tests that links with Markdown snippet references are resolved correctly.
4+
5+
[Getting Started](<Markdown src="./snippets/valid-link.mdx" />)

packages/cli/yaml/docs-validator/src/rules/valid-markdown-link/valid-markdown-link.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { SourceResolverImpl } from "@fern-api/cli-source-resolver";
22
import { noop } from "@fern-api/core-utils";
3+
import { replaceReferencedMarkdown } from "@fern-api/docs-markdown-utils";
34
import { convertIrToApiDefinition, DocsDefinitionResolver } from "@fern-api/docs-resolver";
45
import { APIV1Read, ApiDefinition, FernNavigation } from "@fern-api/fdr-sdk";
56
import { AbsoluteFilePath, join, RelativeFilePath, relative } from "@fern-api/fs-utils";
@@ -94,8 +95,15 @@ export const ValidMarkdownLinks: Rule = {
9495
return [];
9596
}
9697

98+
const { markdown: resolvedContent } = await replaceReferencedMarkdown({
99+
markdown: content,
100+
absolutePathToFernFolder: workspace.absoluteFilePath,
101+
absolutePathToMarkdownFile: absoluteFilepath,
102+
context: NOOP_CONTEXT
103+
});
104+
97105
// Find all matches in the Markdown text
98-
const { pathnamesToCheck, violations } = collectPathnamesToCheck(content, {
106+
const { pathnamesToCheck, violations } = collectPathnamesToCheck(resolvedContent, {
99107
absoluteFilepath,
100108
instanceUrls
101109
});

0 commit comments

Comments
 (0)