CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-highlightjs--cdn-assets

Pre-compiled CDN assets for highlight.js syntax highlighting with language autodetection and theme support.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

plugins.mddocs/

Plugin System

Extensible plugin architecture for customizing highlighting behavior, adding functionality, and hooking into the highlighting lifecycle.

Capabilities

addPlugin()

Adds a plugin to extend highlight.js functionality.

/**
 * Adds a plugin to the highlighter
 * @param plugin - Plugin object with event handler methods
 */
function addPlugin(plugin: HLJSPlugin): void;

interface HLJSPlugin {
  /** Called after highlighting is complete */
  'after:highlight'?: (result: HighlightResult) => void;
  /** Called before highlighting starts */
  'before:highlight'?: (context: BeforeHighlightContext) => void;
  /** Called after highlighting a DOM element */
  'after:highlightElement'?: (data: { 
    el: Element; 
    result: HighlightResult; 
    text: string 
  }) => void;
  /** Called before highlighting a DOM element */
  'before:highlightElement'?: (data: { 
    el: Element; 
    language: string 
  }) => void;
  /** @deprecated Legacy event, use 'after:highlightElement' */
  'after:highlightBlock'?: (data: { 
    block: Element; 
    result: HighlightResult; 
    text: string 
  }) => void;
  /** @deprecated Legacy event, use 'before:highlightElement' */
  'before:highlightBlock'?: (data: { 
    block: Element; 
    language: string 
  }) => void;
}

interface BeforeHighlightContext {
  /** Code string to be highlighted */
  code: string;
  /** Language name for highlighting */
  language: string;
  /** Partial result object (may be modified) */
  result?: HighlightResult;
}

Usage Examples:

import hljs from '@highlightjs/cdn-assets/es/highlight.js';

// Simple logging plugin
const loggingPlugin = {
  'before:highlight': (context) => {
    console.log(`Highlighting ${context.code.length} chars as ${context.language}`);
  },
  'after:highlight': (result) => {
    console.log(`Highlighted with relevance ${result.relevance}`);
  }
};

hljs.addPlugin(loggingPlugin);

// Line numbers plugin
const lineNumbersPlugin = {
  'after:highlightElement': ({ el, result }) => {
    const lines = result.value.split('\n');
    const numberedLines = lines.map((line, i) => 
      `<span class="line-number">${i + 1}</span>${line}`
    ).join('\n');
    el.innerHTML = numberedLines;
  }
};

hljs.addPlugin(lineNumbersPlugin);

removePlugin()

Removes a previously added plugin.

/**
 * Removes a plugin from the highlighter
 * @param plugin - Plugin object to remove (must be same reference)
 */
function removePlugin(plugin: HLJSPlugin): void;

Usage Examples:

// Remove a specific plugin
hljs.removePlugin(loggingPlugin);

// Plugin management system
class PluginManager {
  constructor() {
    this.plugins = new Map();
  }
  
  addPlugin(name, plugin) {
    this.plugins.set(name, plugin);
    hljs.addPlugin(plugin);
  }
  
  removePlugin(name) {
    const plugin = this.plugins.get(name);
    if (plugin) {
      hljs.removePlugin(plugin);
      this.plugins.delete(name);
    }
  }
  
  hasPlugin(name) {
    return this.plugins.has(name);
  }
}

const manager = new PluginManager();
manager.addPlugin('logger', loggingPlugin);
manager.removePlugin('logger');

Plugin Examples

Copy Button Plugin

const copyButtonPlugin = {
  'after:highlightElement': ({ el }) => {
    // Create copy button
    const button = document.createElement('button');
    button.textContent = 'Copy';
    button.className = 'copy-btn';
    
    // Add click handler
    button.addEventListener('click', () => {
      const code = el.textContent;
      navigator.clipboard.writeText(code).then(() => {
        button.textContent = 'Copied!';
        setTimeout(() => button.textContent = 'Copy', 2000);
      });
    });
    
    // Add button to element container
    const container = el.parentElement;
    if (container.tagName === 'PRE') {
      container.style.position = 'relative';
      button.style.position = 'absolute';
      button.style.top = '5px';
      button.style.right = '5px';
      container.appendChild(button);
    }
  }
};

hljs.addPlugin(copyButtonPlugin);

Language Badge Plugin

const languageBadgePlugin = {
  'after:highlightElement': ({ el, result }) => {
    if (result.language) {
      const badge = document.createElement('span');
      badge.className = 'language-badge';
      badge.textContent = result.language;
      badge.style.cssText = `
        position: absolute;
        top: 0;
        right: 0;
        background: #333;
        color: white;
        padding: 2px 6px;
        font-size: 0.8em;
        border-radius: 0 0 0 4px;
      `;
      
      const container = el.parentElement;
      if (container.tagName === 'PRE') {
        container.style.position = 'relative';
        container.appendChild(badge);
      }
    }
  }
};

hljs.addPlugin(languageBadgePlugin);

Code Statistics Plugin

const statsPlugin = {
  'after:highlight': (result) => {
    // Track statistics
    if (!window.hljsStats) {
      window.hljsStats = {
        totalHighlights: 0,
        languageCount: {},
        averageRelevance: []
      };
    }
    
    const stats = window.hljsStats;
    stats.totalHighlights++;
    stats.averageRelevance.push(result.relevance);
    
    if (result.language) {
      stats.languageCount[result.language] = 
        (stats.languageCount[result.language] || 0) + 1;
    }
  }
};

hljs.addPlugin(statsPlugin);

// View statistics
function getHighlightingStats() {
  const stats = window.hljsStats;
  if (!stats) return null;
  
  return {
    total: stats.totalHighlights,
    languages: stats.languageCount,
    avgRelevance: stats.averageRelevance.reduce((a, b) => a + b, 0) / stats.averageRelevance.length
  };
}

Theme Switcher Plugin

const themeSwitcherPlugin = {
  'after:highlightElement': ({ el }) => {
    // Add theme class for styling
    el.classList.add('hljs-themed');
    
    // Add data attribute for theme switching
    el.dataset.theme = document.body.dataset.theme || 'default';
  }
};

hljs.addPlugin(themeSwitcherPlugin);

// Theme switching function
function switchTheme(themeName) {
  document.body.dataset.theme = themeName;
  
  // Update all highlighted elements
  document.querySelectorAll('.hljs-themed').forEach(el => {
    el.dataset.theme = themeName;
  });
  
  // Load theme CSS
  const existingLink = document.querySelector('link[data-hljs-theme]');
  if (existingLink) {
    existingLink.remove();
  }
  
  const link = document.createElement('link');
  link.rel = 'stylesheet';
  link.href = `@highlightjs/cdn-assets/styles/${themeName}.css`;
  link.dataset.hljsTheme = 'true';
  document.head.appendChild(link);
}

Performance Monitoring Plugin

const performancePlugin = {
  'before:highlight': (context) => {
    context.startTime = performance.now();
  },
  'after:highlight': (result) => {
    if (result.startTime) {
      const duration = performance.now() - result.startTime;
      console.log(`Highlighting took ${duration.toFixed(2)}ms`);
      
      // Track slow highlights
      if (duration > 50) {
        console.warn(`Slow highlighting detected: ${duration.toFixed(2)}ms for ${result.language}`);
      }
    }
  }
};

hljs.addPlugin(performancePlugin);

Custom Highlighting Plugin

const customHighlightPlugin = {
  'before:highlight': (context) => {
    // Pre-process code before highlighting
    if (context.language === 'sql') {
      // Normalize SQL keywords to uppercase
      context.code = context.code.replace(/\b(select|from|where|insert|update|delete)\b/gi, 
        (match) => match.toUpperCase()
      );
    }
  },
  'after:highlight': (result) => {
    // Post-process highlighted HTML
    if (result.language === 'javascript') {
      // Add special styling for console.log
      result.value = result.value.replace(
        /(console\.log)/g, 
        '<span class="console-method">$1</span>'
      );
    }
  }
};

hljs.addPlugin(customHighlightPlugin);

Plugin Development Best Practices

// Plugin template with error handling
const robustPlugin = {
  'after:highlightElement': ({ el, result, text }) => {
    try {
      // Plugin logic here
      console.log('Processing element:', el);
    } catch (error) {
      console.error('Plugin error:', error);
      // Don't break highlighting for other plugins
    }
  }
};

// Plugin with configuration options
function createConfigurablePlugin(options = {}) {
  const config = {
    showLineNumbers: true,
    showCopyButton: true,
    ...options
  };
  
  return {
    'after:highlightElement': ({ el, result }) => {
      if (config.showLineNumbers) {
        // Add line numbers
      }
      
      if (config.showCopyButton) {
        // Add copy button
      }
    }
  };
}

// Usage
const myPlugin = createConfigurablePlugin({
  showLineNumbers: false,
  showCopyButton: true
});
hljs.addPlugin(myPlugin);

Plugin Event Order

1. before:highlightElement (for DOM highlighting)
2. before:highlight (for all highlighting)
3. [highlighting process]
4. after:highlight (for all highlighting)
5. after:highlightElement (for DOM highlighting)

Multiple plugins are called in the order they were added.

docs

configuration.md

core-highlighting.md

index.md

language-assets.md

language-management.md

plugins.md

utilities.md

tile.json