Refactor Shiki Highlighter Architecture with Centralized Language Management

Summary

Refactored the Shiki syntax highlighting architecture to implement centralized language declaration and routing, with robust support for Mermaid diagrams, shell language normalization, and Obsidian-style wikilink sanitization.

Why Care

This refactor eliminates build failures from AI-generated content, ensures consistent language handling across all markdown processors, and provides a scalable foundation for adding new language support. The centralized approach prevents duplicate language definitions and reduces maintenance overhead while improving error handling for complex content scenarios.

Implementation

Changes Made

Core Architecture Files

  • src/utils/shikiHighlighter.ts (NEW FILE) - Centralized language management singleton
    • Implemented getShikiHighlighter() singleton pattern
    • Added SPECIAL_RENDERER_LANGUAGES for components like Mermaid
    • Added CUSTOM_DIRECTIVE_LANGUAGES for directive routing
    • Added LANGUAGE_NORMALIZATION_MAP for shell language aliases
    • Added sanitizeMermaidCode() for Obsidian wikilink handling
    • Added getLanguageRoutingStrategy() for unified routing decisions

Configuration Updates

  • astro.config.mjs - Disabled built-in Shiki in favor of singleton utility
    • Changed syntaxHighlight: false to use custom implementation
    • Added remarkGfm plugin to remarkPlugins array
    • Removed hardcoded language array in favor of dynamic loading
  • package.json - Added @astrojs/check": "^0.9.4" for TypeScript validation

Component Integration

  • src/components/markdown/AstroMarkdown.astro - Updated codeblock routing
    • Added import for getLanguageRoutingStrategy and isSpecialRendererLanguage
    • Replaced hardcoded lang === 'mermaid' with centralized routing strategy
    • Added PortfolioGallery import to fix missing component error
  • src/components/codeblocks/MermaidChart.astro - Enhanced with sanitization
    • Added sanitizeMermaidCode() import and usage
    • Applied sanitization to all Mermaid code before rendering
    • Updated both main chart and modal chart containers

Markdown Processing Pipeline

  • src/utils/markdown/remark-jsoncanvas-codeblocks.ts - Centralized routing integration
    • Added imports for routing strategy functions
    • Replaced hardcoded Mermaid detection with getLanguageRoutingStrategy()
    • Added directive language handling with proper fallback
    • Integrated Mermaid sanitization for JSON Canvas contexts
  • src/utils/markdown/remark-directives.ts - Updated from save/jsoncanvas branch
  • src/components/markdown/callouts/ArticleCallout.astro - Updated from save/jsoncanvas branch

New Utility Pages

  • src/pages/markdown-preview.astro (NEW FILE) - Added from save/jsoncanvas branch

Technical Details

Language Routing Strategy

typescript
// Centralized routing logic in src/utils/shikiHighlighter.ts
export function getLanguageRoutingStrategy(lang: string): 'shiki' | 'special-renderer' | 'directive' | 'plaintext' {
  if (SPECIAL_RENDERER_LANGUAGES.has(lang)) return 'special-renderer';
  if (CUSTOM_DIRECTIVE_LANGUAGES.has(lang)) return 'directive';
  
  const normalizedLang = LANGUAGE_NORMALIZATION_MAP[lang.toLowerCase()] || lang;
  const supportedLanguages = ['javascript', 'typescript', 'json', 'yaml', 'markdown', 'html', 'css', 'shellscript', 'python', 'sql', 'astro', 'svelte', 'jsx', 'tsx', 'plaintext'];
  
  return supportedLanguages.includes(normalizedLang.toLowerCase()) ? 'shiki' : 'plaintext';
}

Shell Language Normalization

typescript
// Maps shell variants to Shiki's shellscript language
const LANGUAGE_NORMALIZATION_MAP: Record<string, string> = {
  'zsh': 'shellscript',
  'bash': 'shellscript', 
  'shell': 'shellscript',
  'sh': 'shellscript'
};

Mermaid Sanitization

typescript
// Handles Obsidian wikilinks and problematic syntax
export function sanitizeMermaidCode(code: string): string {
  return code
    .replace(/\[\[([^\]]+)\]\]/g, '$1')  // [[text]] β†’ text
    .replace(/([A-Z]\s*-->\s*[A-Z]\[[^\]]*)\[([^\]]*)\]/g, '$1$2')
    .replace(/\[\[/g, '[').replace(/\]\]/g, ']')
    .replace(/\s+$/gm, '').trim();
}

Singleton Pattern Implementation

  • Single Shiki highlighter instance prevents memory leaks
  • Lazy initialization with theme and language configuration
  • Proper disposal method for cleanup scenarios

Integration Points

Build Process

  • Eliminated Shiki warnings about unsupported languages
  • Fixed "PortfolioGallery is not defined" build error
  • Resolved Mermaid parsing failures from AI-generated diagrams with [[wikilinks]]

Markdown Processing

  • Unified language validation across all processors:
    • AstroMarkdown.astro (main content)
    • remark-jsoncanvas-codeblocks.ts (JSON Canvas previews)
    • MermaidChart.astro (diagram rendering)

Error Handling

  • Graceful fallback to plaintext for unsupported languages
  • Sanitization prevents build failures from malformed Mermaid syntax
  • Consistent error messaging across all contexts

Documentation

Language Categories

  1. Special Renderers: mermaid β†’ routed to dedicated Astro components
  2. Directive Languages: warning, info, tree, etc. β†’ routed to directive components
  3. Shiki Languages: Standard programming languages β†’ syntax highlighted
  4. Plaintext Fallback: Unknown languages β†’ rendered as plain text

Migration Impact

  • Zero breaking changes - existing functionality preserved
  • Performance improvement - singleton pattern reduces memory usage
  • Enhanced reliability - centralized validation prevents edge case failures

Build Results

  • Before: Build failures from Mermaid parsing errors and missing imports
  • After: Clean builds (54.16s) with no errors or warnings
  • Shiki singleton warning resolved: Proper instance management implemented

File Tree of Changes

text
src/
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ codeblocks/
β”‚   β”‚   └── MermaidChart.astro (modified - sanitization)
β”‚   └── markdown/
β”‚       β”œβ”€β”€ AstroMarkdown.astro (modified - routing integration)
β”‚       └── callouts/
β”‚           └── ArticleCallout.astro (updated from save/jsoncanvas)
β”œβ”€β”€ pages/
β”‚   └── markdown-preview.astro (new - from save/jsoncanvas)
└── utils/
    β”œβ”€β”€ shikiHighlighter.ts (new - centralized language management)
    └── markdown/
        β”œβ”€β”€ remark-directives.ts (updated from save/jsoncanvas)
        └── remark-jsoncanvas-codeblocks.ts (modified - routing integration)

Configuration Files:
β”œβ”€β”€ astro.config.mjs (modified - disabled built-in Shiki)
└── package.json (modified - added @astrojs/check)
This refactor establishes a robust, maintainable foundation for syntax highlighting that handles edge cases gracefully while providing clear extension points for future language support.