MUI Docs Infra

Warning

This is an internal project, and is not intended for public use. No support or stability guarantees are provided.

Enhance Code Export Links

A rehype plugin that transforms code identifiers into clickable anchor links, allowing users to navigate to type documentation by clicking on type references in code snippets.


Overview

The enhanceCodeExportLinks plugin scans syntax-highlighted code for type names and converts matching spans into anchor links. It supports both inline <code> elements and code blocks within <pre> elements, and handles dotted chains like Accordion.Trigger.State.

Key Features

  • Type linking: Converts matching type names to clickable anchor links
  • Dotted chain support: Links chains like Component.Root.Props as a single anchor
  • Multiple class support: Works with both pl-c1 (constants) and pl-en (entity names/types)
  • Nested structure handling: Recursively processes frame/line spans in code blocks
  • Case-sensitive matching: Only links exact matches from the anchor map
  • Non-destructive: Preserves original styling classes on single-span links

Installation

This plugin is part of @mui/internal-docs-infra and doesn't need separate installation.


Usage

Basic Usage

import { unified } from 'unified';
import rehypeParse from 'rehype-parse';
import rehypeStringify from 'rehype-stringify';
import enhanceCodeExportLinks from '@mui/internal-docs-infra/pipeline/enhanceCodeExportLinks';

const anchorMap = {
  Trigger: '#trigger',
  'Accordion.Trigger': '#trigger',
  'Accordion.Trigger.Props': '#trigger.props',
};

const processor = unified()
  .use(rehypeParse, { fragment: true })
  .use(enhanceCodeExportLinks, { anchorMap })
  .use(rehypeStringify);

const result = await processor.process('<code><span class="pl-en">Trigger</span></code>');

console.log(String(result));
// Output: <code><a href="#trigger" class="pl-en">Trigger</a></code>

With Type Documentation

This plugin is typically used with abstractCreateTypes to link type references to their documentation:

import enhanceCodeExportLinks from '@mui/internal-docs-infra/pipeline/enhanceCodeExportLinks';

// The anchorMap is automatically generated from your type exports
const anchorMap = {
  Root: '#root',
  'Accordion.Root': '#root',
  'Accordion.Root.Props': '#root.props',
  'Accordion.Trigger': '#trigger',
  AccordionTrigger: '#trigger', // Flat name mapping
};

// Used as a rehype enhancer in type processing
const enhancers = [[enhanceCodeExportLinks, { anchorMap }]];

Transformation Examples

Single Type Name (pl-en)

<!-- Input (after syntax highlighting) -->
<code><span class="pl-en">InputType</span></code>

<!-- Output -->
<code><a href="#inputtype" class="pl-en">InputType</a></code>

Single Type Name (pl-c1)

<!-- Input -->
<code><span class="pl-c1">Trigger</span></code>

<!-- Output -->
<code><a href="#trigger" class="pl-c1">Trigger</a></code>

Dotted Chain

<!-- Input -->
<code><span class="pl-en">Accordion</span>.<span class="pl-en">Trigger</span></code>

<!-- Output -->
<code
  ><a href="#trigger"
    ><span class="pl-en">Accordion</span>.<span class="pl-en">Trigger</span></a
  ></code
>

Three-Part Chain

<!-- Input -->
<code
  ><span class="pl-en">Component</span>.<span class="pl-en">Root</span>.<span class="pl-en"
    >ChangeEventDetails</span
  ></code
>

<!-- Output -->
<code
  ><a href="#root.changeeventdetails"
    ><span class="pl-en">Component</span>.<span class="pl-en">Root</span>.<span class="pl-en"
      >ChangeEventDetails</span
    ></a
  ></code
>

Nested Structure (Code Blocks)

Code blocks with frame/line spans are processed recursively:

<!-- Input -->
<code class="language-ts">
  <span class="frame">
    <span class="line"> <span class="pl-en">Component</span>.<span class="pl-en">Root</span> </span>
  </span>
</code>

<!-- Output -->
<code class="language-ts">
  <span class="frame">
    <span class="line">
      <a href="#root"><span class="pl-en">Component</span>.<span class="pl-en">Root</span></a>
    </span>
  </span>
</code>

Multiple Matches

<!-- Input -->
<code><span class="pl-en">TypeA</span> and <span class="pl-en">TypeB</span></code>

<!-- Output -->
<code><a href="#typea" class="pl-en">TypeA</a> and <a href="#typeb" class="pl-en">TypeB</a></code>

How It Works

Pattern Matching

The plugin looks for these patterns in code elements:

  1. Single linkable span: A <span> with class pl-c1 or pl-en containing a type name
  2. Dotted chain: Multiple linkable spans separated by . text nodes

Processing Flow

Single span:
  span.pl-en("Trigger")  →  a.pl-en[href="#trigger"]("Trigger")

Dotted chain:
  span.pl-en("Accordion") + text(".") + span.pl-en("Trigger")
       ↓
  a[href="#trigger"](span.pl-en("Accordion") + "." + span.pl-en("Trigger"))

Anchor Map

The anchorMap is a Record<string, string> that maps type names to anchor hrefs:

const anchorMap = {
  // Direct type names
  Root: '#root',
  Trigger: '#trigger',

  // Dotted names (full namespace)
  'Accordion.Root': '#root',
  'Accordion.Trigger': '#trigger',
  'Accordion.Trigger.Props': '#trigger.props',

  // Flat names (for backward compatibility)
  AccordionRoot: '#root',
  AccordionTrigger: '#trigger',
};

Recursive Processing

The plugin recursively processes nested elements (like frame and line spans) to find linkable spans at any depth, making it work with both simple inline code and complex code block structures.


Edge Cases

Non-Matching Types

Types not in the anchor map are left unchanged:

<!-- Input -->
<code><span class="pl-en">UnknownType</span></code>

<!-- anchorMap: { Trigger: '#trigger' } -->

<!-- Output: unchanged -->
<code><span class="pl-en">UnknownType</span></code>

Case Sensitivity

Matching is case-sensitive:

<!-- Input -->
<code><span class="pl-en">trigger</span></code>

<!-- anchorMap: { Trigger: '#trigger' } -->

<!-- Output: unchanged (case doesn't match) -->
<code><span class="pl-en">trigger</span></code>

Partial Chain Matches

Only exact chain matches are linked:

<!-- Input -->
<code><span class="pl-en">Accordion</span>.<span class="pl-en">Unknown</span></code>

<!-- anchorMap: { 'Accordion.Trigger': '#trigger' } -->

<!-- Output: unchanged (chain doesn't match) -->
<code><span class="pl-en">Accordion</span>.<span class="pl-en">Unknown</span></code>

Non-Dot Separators

Spans separated by text other than . are treated as separate matches:

<!-- Input -->
<code><span class="pl-en">Accordion</span>: <span class="pl-en">Trigger</span></code>

<!-- anchorMap: { Accordion: '#accordion', Trigger: '#trigger' } -->

<!-- Output: linked separately -->
<code
  ><a href="#accordion" class="pl-en">Accordion</a>:
  <a href="#trigger" class="pl-en">Trigger</a></code
>

Integration with Type Documentation

Automatic Anchor Map Generation

When used with loadPrecomputedTypes, the anchor map is automatically generated from your type exports:

// Generated automatically from your types
const anchorMap = buildAnchorMap(exports, additionalTypes, typeNameMap);
// {
//   'Root': '#root',
//   'Accordion.Root': '#root',
//   'Accordion.Root.Props': '#root.props',
//   'AccordionRoot': '#root',  // from typeNameMap
// }

Type Slug Property

Each type in the processed output includes a slug property that matches the anchor:

{
  type: 'component',
  name: 'Accordion.Trigger',
  slug: 'trigger',  // Used for <details id="trigger">
  data: { /* ... */ }
}

Plugin Order

This plugin should run after syntax highlighting plugins:

  1. transformHtmlCodeInlineHighlighted - Applies syntax highlighting
  2. enhanceCodeExportLinks - Links type references (this plugin)
  3. enhanceCodeInlineElements - Consolidates tag brackets

Running it earlier would prevent the pattern from matching since the highlighting spans wouldn't exist yet.


Types

EnhanceCodeExportLinksOptions

Options for the enhanceCodeExportLinks plugin.

type EnhanceCodeExportLinksOptions = { anchorMap: Record<string, string> };

Related