From 933cdee1fc3fa87e31c5cb0c44310bdb9d3ee64b Mon Sep 17 00:00:00 2001 From: Karthick Raja Date: Sat, 12 Sep 2026 23:04:05 +0530 Subject: [PATCH] fix(docs): use .md paths so GitHub resolves doc links Internal links inside docs/ were written without a file extension, which the TanStack site resolves but GitHub does not, so every cross-page link 404'd when browsing the repository. Rewrite the 38 affected links to relative .md paths, anchors included, and fail docs verification when a local link omits the extension. Co-Authored-By: Claude Opus 5 (1M context) --- docs/comparison.md | 4 ++-- docs/core-concepts/document-model.md | 2 +- docs/core-concepts/parsing.md | 2 +- docs/core-concepts/security.md | 2 +- docs/core-concepts/syntax-profile.md | 2 +- docs/guides/docs-preset.md | 2 +- docs/installation.md | 2 +- docs/overview.md | 12 ++++++------ docs/project/faq.md | 2 +- docs/quick-start.md | 10 +++++----- docs/reference/default-entry.md | 6 +++--- docs/reference/index.md | 14 +++++++------- docs/reference/octane.md | 2 +- docs/reference/parser.md | 4 ++-- docs/reference/react.md | 2 +- docs/reference/types.md | 4 ++-- scripts/verify-docs.mjs | 6 ++++++ 17 files changed, 42 insertions(+), 36 deletions(-) diff --git a/docs/comparison.md b/docs/comparison.md index e2cb35c..5ca345c 100644 --- a/docs/comparison.md +++ b/docs/comparison.md @@ -58,8 +58,8 @@ The comparison does not represent equivalent feature sets. It shows the cost of The generated [compatibility report](https://github.com/TanStack/markdown/blob/main/reports/conformance.md) tracks matches against all 652 CommonMark 0.31.2 examples after serializer normalization. Every established match is protected individually by the regression suite. The count is accounting, not a conformance claim or a target to maximize. Selected official GFM examples separately cover tables, task lists, and strikethrough. -Use [commonmark.js](https://github.com/commonmark/commonmark.js), micromark, or a unified pipeline when exact specification behavior is a requirement. Use TanStack Markdown when your corpus fits the [documented profile](core-concepts/syntax-profile) and the smaller, controlled renderer is the better product tradeoff. +Use [commonmark.js](https://github.com/commonmark/commonmark.js), micromark, or a unified pipeline when exact specification behavior is a requirement. Use TanStack Markdown when your corpus fits the [documented profile](./core-concepts/syntax-profile.md) and the smaller, controlled renderer is the better product tradeoff. ## Performance -Across the maintained fixtures, TanStack Markdown is competitive with the JavaScript renderers in the suite, but it is not the fastest result in every fixture. Pre-parsed AST rendering is its cheapest path. The defensible advantage is the combined size, output contract, and focused feature set. See [Performance](guides/performance) for methodology and current results. +Across the maintained fixtures, TanStack Markdown is competitive with the JavaScript renderers in the suite, but it is not the fastest result in every fixture. Pre-parsed AST rendering is its cheapest path. The defensible advantage is the combined size, output contract, and focused feature set. See [Performance](./guides/performance.md) for methodology and current results. diff --git a/docs/core-concepts/document-model.md b/docs/core-concepts/document-model.md index ecbc617..1878cce 100644 --- a/docs/core-concepts/document-model.md +++ b/docs/core-concepts/document-model.md @@ -62,7 +62,7 @@ for (const block of document.children) { } ``` -The complete node contracts are in the [Types Reference](../reference/types). +The complete node contracts are in the [Types Reference](../reference/types.md). ## Derived document data diff --git a/docs/core-concepts/parsing.md b/docs/core-concepts/parsing.md index 31fbd2e..d96f14c 100644 --- a/docs/core-concepts/parsing.md +++ b/docs/core-concepts/parsing.md @@ -58,7 +58,7 @@ The `references`, `footnotes`, `footnoteOrder`, and `footnoteCounts` fields in ` ## Extensions -Extensions may claim a block, transform inline nodes, transform the completed document, or render custom HTML. They run in array order. See [Extensions](../guides/extensions) for lifecycle and examples. +Extensions may claim a block, transform inline nodes, transform the completed document, or render custom HTML. They run in array order. See [Extensions](../guides/extensions.md) for lifecycle and examples. ## Complexity bounds diff --git a/docs/core-concepts/security.md b/docs/core-concepts/security.md index 2276d81..1da5001 100644 --- a/docs/core-concepts/security.md +++ b/docs/core-concepts/security.md @@ -61,7 +61,7 @@ renderHtml(source, { highlighter }) Only use a highlighter that escapes source code and returns trusted markup. TanStack Markdown cannot distinguish token markup from an injection in the returned string. -The tested [TanStack Highlight adapter](../guides/syntax-highlighting#tanstack-highlight-adapter) returns escaped inner token markup without duplicating Markdown's `
` containers.
+The tested [TanStack Highlight adapter](../guides/syntax-highlighting.md#tanstack-highlight-adapter) returns escaped inner token markup without duplicating Markdown's `
` containers.
 
 ## Extensions
 
diff --git a/docs/core-concepts/syntax-profile.md b/docs/core-concepts/syntax-profile.md
index 2e378e5..9d1b3af 100644
--- a/docs/core-concepts/syntax-profile.md
+++ b/docs/core-concepts/syntax-profile.md
@@ -61,7 +61,7 @@ The docs extension entry points add:
 - comment-delimited component blocks
 - file, package-manager, bundler, and framework transforms
 
-See the [Docs Preset](../guides/docs-preset) guide.
+See the [Docs Preset](../guides/docs-preset.md) guide.
 
 ## Deliberate limits
 
diff --git a/docs/guides/docs-preset.md b/docs/guides/docs-preset.md
index 9c4db0e..d3613d3 100644
--- a/docs/guides/docs-preset.md
+++ b/docs/guides/docs-preset.md
@@ -93,4 +93,4 @@ The transforms emit custom element names and JSON `data-*` properties. Your appl
 
 `framework` blocks split top-level framework headings into `md-framework-panel` elements. Nested headings receive a framework label, while top-level selector headings are omitted from collected table-of-contents data.
 
-Use the individual [extension entry points](../reference/extensions) when the complete preset is more than your site needs.
+Use the individual [extension entry points](../reference/extensions.md) when the complete preset is more than your site needs.
diff --git a/docs/installation.md b/docs/installation.md
index 38a9879..9ba1326 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -71,4 +71,4 @@ TanStack Markdown ships ESM JavaScript and TypeScript declarations. Its synchron
 
 ## Optional capabilities
 
-Syntax highlighting is supplied as a callback, so install only the highlighter your application uses. Docs-specific behavior is available through separately importable [extensions](guides/extensions).
+Syntax highlighting is supplied as a callback, so install only the highlighter your application uses. Docs-specific behavior is available through separately importable [extensions](./guides/extensions.md).
diff --git a/docs/overview.md b/docs/overview.md
index f814fbb..c06f599 100644
--- a/docs/overview.md
+++ b/docs/overview.md
@@ -43,15 +43,15 @@ Raw HTML is escaped unless `allowHtml` is enabled. Executable URL protocols such
 
 ### Focused compatibility
 
-The supported contract is the [TanStack docs syntax profile](core-concepts/syntax-profile), not full CommonMark or GFM. Compatibility is continuously measured so established behavior cannot regress silently.
+The supported contract is the [TanStack docs syntax profile](./core-concepts/syntax-profile.md), not full CommonMark or GFM. Compatibility is continuously measured so established behavior cannot regress silently.
 
 ### AI streaming without parser state
 
-The optional [AI streaming profile](guides/ai-streaming) reparses accumulated response text and suppresses incomplete trailing block placeholders. It adds 0.2 KB gzip to the React path while leaving the core parser and renderers unchanged.
+The optional [AI streaming profile](./guides/ai-streaming.md) reparses accumulated response text and suppresses incomplete trailing block placeholders. It adds 0.2 KB gzip to the React path while leaving the core parser and renderers unchanged.
 
 ## Choose your starting point
 
-- Continue to [Installation](installation) for package and runtime requirements.
-- Use [Quick Start](quick-start) for HTML, React, and Octane examples.
-- Read [Comparison](comparison) to evaluate the tradeoffs.
-- Review the [Syntax Profile](core-concepts/syntax-profile) before migrating an existing content corpus.
+- Continue to [Installation](./installation.md) for package and runtime requirements.
+- Use [Quick Start](./quick-start.md) for HTML, React, and Octane examples.
+- Read [Comparison](./comparison.md) to evaluate the tradeoffs.
+- Review the [Syntax Profile](./core-concepts/syntax-profile.md) before migrating an existing content corpus.
diff --git a/docs/project/faq.md b/docs/project/faq.md
index e254fdc..ce97f26 100644
--- a/docs/project/faq.md
+++ b/docs/project/faq.md
@@ -42,4 +42,4 @@ It is public and typed, but the package is pre-1.0. Pin a version for persisted
 
 ## How do I decide before migrating?
 
-Review the [Syntax Profile](../core-concepts/syntax-profile), run your content through the [downstream corpus test](../guides/testing), and inspect the generated output. Choose a broader parser when unsupported syntax is a content requirement rather than an incidental edge case.
+Review the [Syntax Profile](../core-concepts/syntax-profile.md), run your content through the [downstream corpus test](../guides/testing.md), and inspect the generated output. Choose a broader parser when unsupported syntax is a content requirement rather than an incidental edge case.
diff --git a/docs/quick-start.md b/docs/quick-start.md
index 708f905..cc6d600 100644
--- a/docs/quick-start.md
+++ b/docs/quick-start.md
@@ -123,16 +123,16 @@ const document = parseMarkdown(source, options)
 const html = renderHtml(document, options)
 ```
 
-The preset adds callouts, heading collection, and TanStack-style comment components without changing the core entry points. See the [Docs Preset](guides/docs-preset) guide.
+The preset adds callouts, heading collection, and TanStack-style comment components without changing the core entry points. See the [Docs Preset](./guides/docs-preset.md) guide.
 
 ## Highlight code
 
 TanStack Markdown owns each `
` tree and accepts trusted inner token markup through its `highlighter` callback. TanStack Highlight provides `createTanStackMarkdownHighlighter()` for this boundary; do not pass a high-level method that returns another code-block wrapper.
 
-See [Syntax Highlighting](guides/syntax-highlighting) for the complete React and HTML setup, explicit language registration, light and dark themes, line numbers, and security boundary.
+See [Syntax Highlighting](./guides/syntax-highlighting.md) for the complete React and HTML setup, explicit language registration, light and dark themes, line numbers, and security boundary.
 
 ## Next steps
 
-- Confirm your content fits the [Syntax Profile](core-concepts/syntax-profile).
-- Review the [Security](core-concepts/security) boundary before enabling HTML or highlighting.
-- Use the [API Reference](reference/index) for complete options and signatures.
+- Confirm your content fits the [Syntax Profile](./core-concepts/syntax-profile.md).
+- Review the [Security](./core-concepts/security.md) boundary before enabling HTML or highlighting.
+- Use the [API Reference](./reference/index.md) for complete options and signatures.
diff --git a/docs/reference/default-entry.md b/docs/reference/default-entry.md
index d8860b7..eb0e9b2 100644
--- a/docs/reference/default-entry.md
+++ b/docs/reference/default-entry.md
@@ -30,7 +30,7 @@ function parseMarkdown(
 ): MarkdownDocument
 ```
 
-Parses a complete Markdown document. See the [Parser Reference](parser).
+Parses a complete Markdown document. See the [Parser Reference](./parser.md).
 
 ### `parseInline`
 
@@ -58,8 +58,8 @@ Renders one `BlockNode` to HTML.
 
 Renders one `InlineNode` to HTML.
 
-Signatures and rendering options are documented in the [HTML Reference](html).
+Signatures and rendering options are documented in the [HTML Reference](./html.md).
 
 ## Type exports
 
-The default entry exports every AST node, parser context, render option, extension contract, and supporting definition from the [Types Reference](types).
+The default entry exports every AST node, parser context, render option, extension contract, and supporting definition from the [Types Reference](./types.md).
diff --git a/docs/reference/index.md b/docs/reference/index.md
index e56eb47..d42922f 100644
--- a/docs/reference/index.md
+++ b/docs/reference/index.md
@@ -23,12 +23,12 @@ TanStack Markdown uses explicit subpath exports so applications can import only
 
 ## Reference pages
 
-- [Default Entry](default-entry)
-- [Parser](parser)
-- [HTML](html)
-- [React](react)
-- [Octane](octane)
-- [Types](types)
-- [Extensions](extensions)
+- [Default Entry](./default-entry.md)
+- [Parser](./parser.md)
+- [HTML](./html.md)
+- [React](./react.md)
+- [Octane](./octane.md)
+- [Types](./types.md)
+- [Extensions](./extensions.md)
 
 Prefer narrow entry points in application code. The default entry is convenient when parser, HTML renderer, and public types are used together.
diff --git a/docs/reference/octane.md b/docs/reference/octane.md
index 837faa6..dc730d5 100644
--- a/docs/reference/octane.md
+++ b/docs/reference/octane.md
@@ -78,4 +78,4 @@ function renderInlineOctane(
 
 Renders one inline node.
 
-See the [Octane Guide](../guides/octane) for TSRX, component mapping, and SSR usage.
+See the [Octane Guide](../guides/octane.md) for TSRX, component mapping, and SSR usage.
diff --git a/docs/reference/parser.md b/docs/reference/parser.md
index 6747c9b..e03f156 100644
--- a/docs/reference/parser.md
+++ b/docs/reference/parser.md
@@ -26,7 +26,7 @@ Normalizes and parses a complete Markdown source string into a deterministic `Ma
 | Option | Default | Behavior |
 | --- | --- | --- |
 | `allowHtml` | `false` | Recognize raw block and inline HTML nodes |
-| `urlTransform` | built-in policy | Override parsed link and image URLs; see [Custom URL policy](../core-concepts/security#custom-url-policy) |
+| `urlTransform` | built-in policy | Override parsed link and image URLs; see [Custom URL policy](../core-concepts/security.md#custom-url-policy) |
 | `frontmatter` | `true` | Extract a leading `---` frontmatter block |
 | `headingIds` | `true` | Generate IDs, disable them, or provide an ID function |
 | `extensions` | `[]` | Run custom parser and transform hooks in array order |
@@ -41,4 +41,4 @@ The last four state fields are public so extension-driven nested parsing can pre
 
 The result always has `type: 'root'` and a `children` array. It may also include raw `frontmatter` and extension-derived `headings`.
 
-See [Parsing](../core-concepts/parsing) for behavior and [Types](types) for complete contracts.
+See [Parsing](../core-concepts/parsing.md) for behavior and [Types](./types.md) for complete contracts.
diff --git a/docs/reference/react.md b/docs/reference/react.md
index c6e2c77..4e2e445 100644
--- a/docs/reference/react.md
+++ b/docs/reference/react.md
@@ -88,4 +88,4 @@ function renderInlineReact(
 
 Renders one inline node.
 
-See the [React Guide](../guides/react) for component mapping and SSR usage.
+See the [React Guide](../guides/react.md) for component mapping and SSR usage.
diff --git a/docs/reference/types.md b/docs/reference/types.md
index fd7a737..b7032d2 100644
--- a/docs/reference/types.md
+++ b/docs/reference/types.md
@@ -126,7 +126,7 @@ Contains raw inline HTML `value`. It is created only when HTML parsing is enable
 
 ### `InlineComponentNode`
 
-Contains `type: 'inlineComponent'`, `name`, source `attributes`, inline `children`, and optional rendered `tagName` and string `properties`. Uses the same component replacements as `ComponentNode`, with a `` fallback when no tag is provided. See [Custom components](../guides/extensions#custom-components).
+Contains `type: 'inlineComponent'`, `name`, source `attributes`, inline `children`, and optional rendered `tagName` and string `properties`. Uses the same component replacements as `ComponentNode`, with a `` fallback when no tag is provided. See [Custom components](../guides/extensions.md#custom-components).
 
 ## Parsing and rendering options
 
@@ -136,7 +136,7 @@ Configures `allowHtml`, `urlTransform`, `frontmatter`, `headingIds`, and `extens
 
 ### `UrlTransform`
 
-Synchronous callback `(url: string, kind: 'link' | 'image', defaultUrl: string) => string | null`. Return the default screened URL, a trusted replacement, or `null` to keep only the label content. Applies during Markdown parsing, not to raw HTML or supplied ASTs. See [Custom URL policy](../core-concepts/security#custom-url-policy).
+Synchronous callback `(url: string, kind: 'link' | 'image', defaultUrl: string) => string | null`. Return the default screened URL, a trusted replacement, or `null` to keep only the label content. Applies during Markdown parsing, not to raw HTML or supplied ASTs. See [Custom URL policy](../core-concepts/security.md#custom-url-policy).
 
 ### `RenderOptions`
 
diff --git a/scripts/verify-docs.mjs b/scripts/verify-docs.mjs
index a6a016d..2b0496b 100644
--- a/scripts/verify-docs.mjs
+++ b/scripts/verify-docs.mjs
@@ -133,6 +133,12 @@ async function validateLinks(file, source) {
       continue
     }
 
+    if (pathPart && !/\.(?:md|mdx)$/.test(pathPart)) {
+      failures.push(
+        `Local link must include the .md extension so GitHub can resolve it in ${toPosix(path.relative(root, file))}: ${target}`,
+      )
+    }
+
     if (fragment) {
       const anchors = await getAnchors(markdownPath)
       const anchor = decodeURIComponent(fragment).toLowerCase()