Centralized Debug System for Markdown Processing

Summary

Implemented a centralized debugging system for markdown processing to reduce console output during builds and provide configurable debug levels through environment variables and URL parameters.

Why Care

Excessive debug output was causing noise during builds and development. This refactoring provides a cleaner, more organized approach to debugging with granular control over what gets logged, making development more efficient and builds cleaner.

Implementation

Changes Made

  • Created a new centralized debugging utility in site/src/utils/debug/markdown-debugger.ts
  • Added environment variable configuration in site/.env.example
  • Updated all remark plugins to use the new debugging utility:
    • site/src/utils/markdown/remarkCitations.ts
    • site/src/utils/markdown/remark-backlinks.ts
    • site/src/utils/markdown/remark-images.ts
    • site/src/utils/markdown/rehype-callout-handler.ts
  • Modified layout components to use the new debugging utility:
    • site/src/layouts/OneArticle.astro
    • site/src/components/markdown/DebugMarkdown.astro

Technical Details

  • Created a MarkdownDebugger class with methods for different levels of debugging:
    typescript
    // site/src/utils/debug/markdown-debugger.ts
    class MarkdownDebugger {
      private isEnabled: boolean = false;
      private isVerbose: boolean = false;
    
      constructor() {
        // Check for environment variables
        this.isEnabled = process.env.DEBUG_MARKDOWN === 'true';
        this.isVerbose = process.env.DEBUG_MARKDOWN_VERBOSE === 'true';
        
        // Also enable if URL has debug-markdown parameter
        if (typeof window !== 'undefined') {
          const url = new URL(window.location.href);
          if (url.searchParams.has('debug-markdown')) {
            this.isEnabled = true;
          }
          if (url.searchParams.has('debug-markdown-verbose')) {
            this.isEnabled = true;
            this.isVerbose = true;
          }
        }
      }
    
      log(message: string, ...args: any[]): void {
        if (!this.isEnabled) return;
        console.log(`[Markdown Debug] ${message}`, ...args);
      }
    
      verbose(message: string, ...args: any[]): void {
        if (!this.isEnabled || !this.isVerbose) return;
        console.log(`[Markdown Debug Verbose] ${message}`, ...args);
      }
      
      // Additional methods for plugin start/end and AST debugging
    }
  • Replaced direct console.log calls in remark plugins with the new utility:
    typescript
    // Before
    console.log('Found citation:', trimmedLine);
    
    // After
    markdownDebugger.verbose('Found citation:', trimmedLine);
  • Added environment variable configuration for controlling debug output:
    bash
    # site/.env.example
    # Debug settings
    # Set to 'true' to enable markdown debug output
    DEBUG_MARKDOWN=false
    
    # Set to 'true' to enable verbose markdown debug output (includes full AST dumps)
    DEBUG_MARKDOWN_VERBOSE=false
    
    # Set to 'true' to enable AST debug file output
    DEBUG_AST=false
  • Updated the DebugMarkdown component to conditionally process ASTs only when debugging is enabled:
    typescript
    // site/src/components/markdown/DebugMarkdown.astro
    // Only process if debugging is enabled
    if (enabled) {
      // Process with our custom remark plugins in discrete steps
      parsedAst = await unified()
        .use(remarkParse)
        .parse(content);
      markdownDebugger.writeDebugFile('1-parsed-ast', parsedAst);
      
      // Additional processing steps...
    }

Integration Points

  • The debug system integrates with the existing AST debugger for file output
  • Debug output can be controlled through:
    1. Environment variables (DEBUG_MARKDOWN=true, DEBUG_MARKDOWN_VERBOSE=true)
    2. URL parameters (?debug-markdown, ?debug-markdown-verbose)
    3. The existing DEBUG_AST=true for AST file output

Documentation

  • Added comments throughout the code explaining the purpose and usage of the debug utility
  • Created a .env.example file with documentation on the available debug options
  • Debug output now includes prefixes to clearly identify the source of each log message

Future Considerations

  • Consider adding more granular control over which plugins generate debug output
  • Implement a debug log file option to capture debug output without console noise
  • Add visual indicators in the UI when debugging is enabled

Lessons Learned from Import Path Refactoring

Import Path Patterns

  • Remark Plugins: Use relative imports within the utils/markdown directory
    typescript
    // In remark plugins
    import markdownDebugger from './markdownDebugger';
  • Astro Components: Use alias paths for imports from utility directories
    typescript
    // In Astro components
    import markdownDebugger from '@utils/markdown/markdownDebugger';

Module Export Patterns

  • Provide both default and named exports for maximum flexibility:
    typescript
    // Create a singleton instance
    const markdownDebugger = new MarkdownDebugger();
    
    // Export as default (consistent with remark plugins)
    export default markdownDebugger;
    
    // Also provide named export for flexibility
    export { markdownDebugger };

Path Resolution Considerations

  • Astro's build process handles alias paths differently from relative paths
  • Remark plugins imported in astro.config.mjs use relative paths, so their internal imports should follow the same pattern
  • Components using the @utils alias can continue to use alias paths for consistency

Debugging Improvements

  • Added client-side URL parameter detection for easier debugging in the browser
  • Implemented safeguards to prevent debug code from running during SSG builds
  • Created a more structured plugin debugging approach with start/end logging