MUI Docs Infra

Transform HTML Code Inline Highlighted

A rehype plugin that applies lightweight syntax highlighting to inline <code> elements without adding line gutters, frame wrappers, or precomputed data attributes.


Overview

The transformHtmlCodeInlineHighlighted plugin transforms plain inline code into beautifully syntax-highlighted code using Starry Night. Unlike transformHtmlCodePrecomputed, this plugin is optimized for inline code snippets and type expressions.

Key Features

  • Lightweight highlighting: Applies syntax highlighting without line numbers, gutters, or frames
  • Prefix support: Handles data-highlighting-prefix attribute for proper type context
  • Automatic cleanup: Removes highlighting prefixes after processing
  • Language detection: Reads language from className attribute (e.g., language-ts)
  • Lazy initialization: Automatically initializes Starry Night on first use

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 transformHtmlCodeInlineHighlighted from '@mui/internal-docs-infra/pipeline/transformHtmlCodeInlineHighlighted';

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

const result = await processor.process('<code class="language-ts">string | number</code>');

console.log(String(result));
// Output: <code class="language-ts"><span class="pl-c1">string</span> | <span class="pl-c1">number</span></code>

With Highlighting Prefix

For type expressions that need context for proper highlighting:

const html = `
  <code class="language-ts" data-highlighting-prefix="type _ = ">
    { foo: string; bar: number }
  </code>
`;

const result = await processor.process(html);

// The prefix is used during highlighting but removed from output:
// <code class="language-ts">
//   <span class="pl-k">{</span> foo<span class="pl-k">:</span> <span class="pl-c1">string</span><span class="pl-k">;</span> ...
// </code>

Configuration

Automatic Initialization

The plugin automatically initializes Starry Night on first use. This means:

  • No manual initialization required
  • First processing takes ~500-1200ms (Starry Night creation)
  • Subsequent processing is fast (reuses singleton instance)
  • Instance stored in globalThis for sharing across all processing

Supported Languages

The plugin supports all languages defined in grammars.ts:

  • TypeScript (.ts, .tsx)
  • JavaScript (.js, .jsx)
  • CSS (.css)
  • HTML (.html)
  • JSON (.json)
  • Markdown (.md)
  • Shell (.sh, .bash)
  • YAML (.yaml, .yml)

How It Works

Processing Pipeline

  1. Initialize: Lazily creates Starry Night instance on first use
  2. Find code elements: Visits all <code> elements in the HAST tree
  3. Extract text content: Gets the raw text from the code element
  4. Check for prefix: Reads data-highlighting-prefix attribute if present
  5. Apply prefix: Temporarily prepends prefix for proper context
  6. Highlight: Uses Starry Night to apply syntax highlighting
  7. Remove prefix: Strips prefix characters from the highlighted output
  8. Replace children: Updates code element with highlighted nodes
  9. Clean up: Removes data-highlighting-prefix attribute

Prefix Removal Algorithm

The removePrefixFromHighlightedNodes helper removes prefix characters by:

  1. Tracking how many characters to remove
  2. Iterating through HAST nodes (text and elements)
  3. Removing complete nodes when prefix exceeds node length
  4. Partially trimming nodes when prefix is shorter
  5. Recursing into element children when needed
  6. Preserving all syntax highlighting structure

Examples

Simple Type Expression

// Input
<code class="language-ts">boolean</code>

// Output
<code class="language-ts"><span class="pl-c1">boolean</span></code>

Union Type

// Input
<code class="language-ts">string | number | boolean</code>

// Output
<code class="language-ts">
  <span class="pl-c1">string</span> <span class="pl-k">|</span> <span class="pl-c1">number</span> <span class="pl-k">|</span> <span class="pl-c1">boolean</span>
</code>

Type with Context Prefix

// Input
<code class="language-ts" data-highlighting-prefix="type _ = ">
  { foo: string }
</code>

// Highlighted with prefix: "type _ = { foo: string }"
// Prefix removed from output: "{ foo: string }"

// Output
<code class="language-ts">
  <span class="pl-k">{</span> foo<span class="pl-k">:</span> <span class="pl-c1">string</span> <span class="pl-k">}</span>
</code>

Function Type

// Input
<code class="language-ts" data-highlighting-prefix="type _ = ">
  (event: React.MouseEvent) => void
</code>

// Output
<code class="language-ts">
  ((<span class="pl-v">event</span><span class="pl-k">:</span> <span class="pl-en">React</span>.<span class="pl-en">MouseEvent</span>) <span class="pl-k">=></span> <span class="pl-c1">void</span>)
</code>

Use Cases

Type Documentation

Highlight type expressions in API documentation:

const processor = unified().use(transformHtmlCodeInlineHighlighted);

// Highlight prop types
await processor.process('<code class="language-ts">string | number</code>');

// Highlight complex types
await processor.process(
  '<code class="language-ts" data-highlighting-prefix="type _ = ">{ foo: string; bar: number }</code>',
);

HTML Tables

Apply highlighting to inline code in HTML tables:

<table>
  <tr>
    <th>Prop</th>
    <th>Type</th>
  </tr>
  <tr>
    <td>variant</td>
    <td><code class="language-ts">'primary' | 'secondary'</code></td>
  </tr>
  <tr>
    <td>onChange</td>
    <td>
      <code class="language-ts" data-highlighting-prefix="type _ = ">
        (event: React.ChangeEvent) => void
      </code>
    </td>
  </tr>
</table>

Generated API Documentation

Highlight type expressions in programmatically generated documentation:

const typeCell = `<code class="language-ts" data-highlighting-prefix="type _ = ">${propType}</code>`;

// propType might be: "{ value: string; onChange: (v: string) => void }"
// The prefix ensures proper object/function type highlighting

API Reference

transformHtmlCodeInlineHighlighted()

function transformHtmlCodeInlineHighlighted(): (tree: Root) => Promise<void>;

Returns a unified transformer that highlights <code> elements.

Parameters: None

Returns: A transformer function that processes the HAST tree

Side Effects:

  • Automatically initializes Starry Night on first use
  • Stores instance in globalThis.__docs_infra_starry_night_instance__

Architecture

Lazy Singleton Pattern

The plugin uses a lazy singleton Starry Night instance stored in globalThis:

const STARRY_NIGHT_KEY = '__docs_infra_starry_night_instance__';

async function getStarryNight(): Promise<StarryNight> {
  if (!(globalThis as any)[STARRY_NIGHT_KEY]) {
    (globalThis as any)[STARRY_NIGHT_KEY] = await createStarryNight(grammars);
  }
  return (globalThis as any)[STARRY_NIGHT_KEY];
}

Benefits:

  1. No manual initialization: Just use the plugin directly
  2. Performance: Starry Night creation is expensive (~500-1200ms), but only happens once
  3. Memory: Single instance shared across all processing
  4. Consistency: Same highlighting rules everywhere

Prefix Handling

The data-highlighting-prefix attribute solves a key challenge: TypeScript types need context for proper syntax highlighting.

Problem: Highlighting string | number in isolation may not recognize it as a type expression.

Solution: Add context prefix type _ = string | number, highlight the complete string, then remove the prefix.

Example:

// Without prefix: may highlight incorrectly
<code class="language-ts">{ foo: string }</code>

// With prefix: highlights as proper type
<code class="language-ts" data-highlighting-prefix="type _ = ">
  { foo: string }
</code>

// After processing: prefix removed but highlighting preserved
<code class="language-ts">
  <span class="pl-k">{</span> foo<span class="pl-k">:</span> ...
</code>

Performance Considerations

Initialization Cost

  • First call: ~500-1200ms to create Starry Night instance (lazy, automatic)
  • Subsequent calls: ~0ms (singleton pattern)

Processing Cost

  • Per code element: ~1-5ms for typical inline code
  • With prefix: ~2-8ms (includes removal step)

Optimization: The plugin only processes <code> elements with language classes, skipping:

  • Plain code without class="language-*"
  • Empty code elements
  • Code elements with unsupported languages

Comparison with transformHtmlCodePrecomputed

FeaturetransformHtmlCodeInlineHighlightedtransformHtmlCodePrecomputed
Use caseInline code snippetsMulti-line code blocks
Line guttersNoYes
Frame wrapperNoYes
Data attributesNoYes (dataPrecompute)
Prefix supportYesNo
PerformanceFastSlower (more features)
Output sizeSmallerLarger

When to use transformHtmlCodeInlineHighlighted:

  • Inline code in documentation
  • Type expressions in tables
  • Short code snippets (< 5 lines)
  • Minimal HTML overhead needed

When to use transformHtmlCodePrecomputed:

  • Multi-line code examples
  • Interactive demos
  • Code with line numbers
  • Precomputed caching needed

Best Practices

1. Use Appropriate Prefixes

// ✓ Good: Minimal prefix for context
<code class="language-ts" data-highlighting-prefix="type _ = ">
  string | number
</code>

// ✗ Bad: Long unnecessary prefix
<code class="language-ts" data-highlighting-prefix="export type MyVeryLongTypeName = ">
  string | number
</code>

2. Specify Language

// ✓ Good: Explicit language
<code class="language-ts">string</code>

// ✗ Bad: No language (won't highlight)
<code>string</code>

3. Keep Code Inline

// ✓ Good: Short inline code
<code class="language-ts">string | number</code>

// ✗ Bad: Multi-line code (use transformHtmlCodePrecomputed)
<code class="language-ts">
  function example() {
    // 50 lines of code
  }
</code>

Troubleshooting

Code Not Highlighting

Problem: Code remains plain text without highlighting.

Solutions:

  1. Check language class exists:

    <!-- ✓ Good -->
    <code class="language-ts">string</code>
    
    <!-- ✗ Bad: missing class -->
    <code>string</code>
    
  2. Confirm language is supported:

    // Supported: ts, js, tsx, jsx, css, html, json, md, sh, yaml
    // Unsupported: python, ruby, go, rust
    

Prefix Not Removed

Problem: Prefix appears in output.

Solutions:

  1. Verify attribute name is exact:

    <!-- ✓ Good -->
    <code data-highlighting-prefix="type _ = ">string</code>
    
    <!-- ✗ Bad: wrong attribute name -->
    <code data-prefix="type _ = ">string</code>
    
  2. Check prefix length matches actual characters:

    // Prefix removal counts characters, not bytes
    // "type _ = " is 9 characters (including spaces)
    

Unexpected Highlighting

Problem: Highlighting differs from expected.

Solutions:

  1. Add appropriate prefix for context:

    <!-- Without prefix: might highlight as variable -->
    <code class="language-ts">{ foo: string }</code>
    
    <!-- With prefix: highlights as type -->
    <code class="language-ts" data-highlighting-prefix="type _ = "> { foo: string } </code>
    
  2. Check language matches content:

    <!-- ✗ Bad: JSX with wrong language -->
    <code class="language-js"><button /></code>
    
    <!-- ✓ Good: JSX with correct language -->
    <code class="language-tsx"><button /></code>
    

Related Functions


See Also