CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-prismjs

Lightweight, robust, elegant syntax highlighting library with support for 280+ languages and multiple themes

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

utilities.mddocs/

Utilities

The utility system provides helper functions for DOM manipulation, language detection, object operations, and other common tasks needed throughout the PrismJS ecosystem. These utilities support both internal operations and external plugin development.

Capabilities

Utility Namespace

The main container for all utility functions and helper methods.

/**
 * Utility methods namespace containing helper functions
 */
Prism.util;

DOM and Language Utilities

getLanguage

Extract language identifier from element's CSS classes.

/**
 * Extract language identifier from element's CSS classes
 * @param {Element} element - DOM element to examine
 * @returns {string} Language identifier or 'none' if not found
 */
Prism.util.getLanguage(element);

Usage Examples:

// Extract language from various class patterns
const jsElement = document.querySelector('.language-javascript');
console.log(Prism.util.getLanguage(jsElement)); // 'javascript'

const pyElement = document.querySelector('.lang-python'); 
console.log(Prism.util.getLanguage(pyElement)); // 'python'

const noLangElement = document.querySelector('.no-language');
console.log(Prism.util.getLanguage(noLangElement)); // 'none'

// Works with multiple classes
const multiElement = document.createElement('code');
multiElement.className = 'line-numbers language-typescript highlight';
console.log(Prism.util.getLanguage(multiElement)); // 'typescript'

// Dynamic language detection
function highlightDynamicCode(element, fallbackLang = 'text') {
    const detectedLang = Prism.util.getLanguage(element);
    const language = detectedLang !== 'none' ? detectedLang : fallbackLang;
    
    if (Prism.languages[language]) {
        Prism.highlightElement(element);
    } else {
        console.warn(`Language '${language}' not available`);
    }
}

setLanguage

Set language classes on DOM elements.

/**
 * Set language classes on DOM element
 * @param {Element} element - DOM element to modify
 * @param {string} language - Language identifier to set
 */
Prism.util.setLanguage(element, language);

Usage Examples:

// Set language on code element
const codeElement = document.createElement('code');
Prism.util.setLanguage(codeElement, 'javascript');
console.log(codeElement.className); // 'language-javascript'

// Change language dynamically
const existingElement = document.querySelector('code.language-python');
Prism.util.setLanguage(existingElement, 'typescript');
console.log(existingElement.className); // 'language-typescript'

// Set language on parent and child
const preElement = document.createElement('pre');
const codeChild = document.createElement('code');
preElement.appendChild(codeChild);

Prism.util.setLanguage(preElement, 'css');
Prism.util.setLanguage(codeChild, 'css');

// Dynamic language switching
function switchLanguage(element, newLanguage) {
    const oldLanguage = Prism.util.getLanguage(element);
    console.log(`Switching from ${oldLanguage} to ${newLanguage}`);
    
    Prism.util.setLanguage(element, newLanguage);
    
    // Re-highlight with new language
    if (Prism.languages[newLanguage]) {
        Prism.highlightElement(element);
    }
}

currentScript

Get reference to currently executing script element.

/**
 * Get reference to currently executing script element
 * @returns {Element|null} Current script element or null
 */
Prism.util.currentScript();

Usage Examples:

// Get current script for path resolution
const currentScript = Prism.util.currentScript();
if (currentScript) {
    const scriptPath = currentScript.src;
    const basePath = scriptPath.substring(0, scriptPath.lastIndexOf('/'));
    console.log('Script base path:', basePath);
}

// Plugin initialization with script detection
(function() {
    const script = Prism.util.currentScript();
    const pluginConfig = {
        scriptSrc: script ? script.src : 'unknown',
        loadTime: Date.now()
    };
    
    Prism.plugins.myPlugin = pluginConfig;
})();

// Conditional loading based on script location
const script = Prism.util.currentScript();
if (script && script.hasAttribute('data-manual')) {
    Prism.manual = true;
}

Object Utilities

clone

Deep clone objects with circular reference handling.

/**
 * Deep clone object with circular reference handling
 * @param {*} o - Object to clone
 * @param {WeakMap} [visited] - Map of visited objects for circular reference detection
 * @returns {*} Cloned object
 */
Prism.util.clone(o, visited);

Usage Examples:

// Clone simple objects
const original = { a: 1, b: { c: 2 } };
const cloned = Prism.util.clone(original);
cloned.b.c = 3;
console.log(original.b.c); // 2 (unchanged)
console.log(cloned.b.c);   // 3

// Clone language grammars
const baseGrammar = Prism.languages.javascript;
const customGrammar = Prism.util.clone(baseGrammar);
customGrammar.keyword = /\b(?:const|let|var|custom)\b/;

// Clone with circular references
const circular = { name: 'test' };
circular.self = circular;
const clonedCircular = Prism.util.clone(circular);
console.log(clonedCircular.self === clonedCircular); // true

// Clone arrays and complex structures
const complexObject = {
    array: [1, 2, { nested: true }],
    func: function() { return 'test'; },
    regex: /pattern/gi,
    date: new Date()
};
const clonedComplex = Prism.util.clone(complexObject);

// Safe grammar extension
function extendGrammar(baseId, extensions) {
    const base = Prism.languages[baseId];
    if (!base) {
        throw new Error(`Base language '${baseId}' not found`);
    }
    
    const extended = Prism.util.clone(base);
    Object.assign(extended, extensions);
    return extended;
}

type

Get detailed type information for objects.

/**
 * Get detailed type information for any value
 * @param {*} o - Value to examine
 * @returns {string} Type string (Object, Array, String, etc.)
 */
Prism.util.type(o);

Usage Examples:

// Type detection for various values
console.log(Prism.util.type({}));          // 'Object'
console.log(Prism.util.type([]));          // 'Array'
console.log(Prism.util.type('string'));    // 'String'
console.log(Prism.util.type(42));          // 'Number'
console.log(Prism.util.type(true));        // 'Boolean'
console.log(Prism.util.type(/regex/));     // 'RegExp'
console.log(Prism.util.type(null));        // 'Null'
console.log(Prism.util.type(undefined));   // 'Undefined'

// Type-based processing
function processGrammarRule(rule) {
    const ruleType = Prism.util.type(rule);
    
    switch (ruleType) {
        case 'RegExp':
            return { pattern: rule };
        case 'Object':
            return rule;
        case 'Array':
            return rule.map(processGrammarRule);
        default:
            console.warn('Unexpected rule type:', ruleType);
            return rule;
    }
}

// Safe property access based on type
function safeAccess(obj, property, expectedType) {
    if (Prism.util.type(obj) === 'Object' && property in obj) {
        const value = obj[property];
        return Prism.util.type(value) === expectedType ? value : null;
    }
    return null;
}

objId

Generate unique identifiers for objects (circular reference handling).

/**
 * Get or generate unique identifier for object
 * @param {Object} obj - Object to get ID for
 * @returns {string} Unique object identifier
 */
Prism.util.objId(obj);

Usage Examples:

// Generate unique IDs for objects
const obj1 = { name: 'first' };
const obj2 = { name: 'second' };

console.log(Prism.util.objId(obj1)); // 'O_1' (or similar)
console.log(Prism.util.objId(obj2)); // 'O_2' (or similar)
console.log(Prism.util.objId(obj1)); // Same as first call

// Track object references
const objectTracker = new Map();

function trackObject(obj, metadata) {
    const id = Prism.util.objId(obj);
    objectTracker.set(id, { object: obj, metadata, accessed: Date.now() });
    return id;
}

// Use in grammar processing
function processGrammarWithTracking(grammar) {
    const grammarId = Prism.util.objId(grammar);
    console.log(`Processing grammar ${grammarId}`);
    
    // Process grammar rules...
    return grammarId;
}

Token and Content Utilities

encode

HTML encode tokens and content for safe output.

/**
 * HTML encode tokens and content for safe output
 * @param {*} tokens - Tokens, strings, or mixed content to encode
 * @returns {*} Encoded content with same structure
 */
Prism.util.encode(tokens);

Usage Examples:

// Encode simple strings
const htmlString = '<script>alert("XSS")</script>';
const encoded = Prism.util.encode(htmlString);
console.log(encoded); // '&lt;script&gt;alert("XSS")&lt;/script&gt;'

// Encode token content
const token = new Prism.Token('string', '"<html>"');
const encodedToken = Prism.util.encode(token);
console.log(encodedToken.content); // '"&lt;html&gt;"'

// Encode token arrays
const tokens = [
    'const html = ',
    new Prism.Token('string', '"<div>content</div>"'),
    ';'
];
const encodedTokens = Prism.util.encode(tokens);

// Recursive encoding of nested structures
const nestedToken = new Prism.Token('tag', [
    '<',
    new Prism.Token('tag-name', 'script'),
    '>'
]);
const encodedNested = Prism.util.encode(nestedToken);

// Safe content processing
function safeHighlight(code, grammar, language) {
    const tokens = Prism.tokenize(code, grammar);
    const encodedTokens = Prism.util.encode(tokens);
    return Prism.Token.stringify(encodedTokens, language);
}

isActive

Check element state based on CSS classes and configuration.

/**
 * Check if element has active state based on classes and configuration
 * @param {Element} element - Element to check
 * @param {string} className - CSS class name to look for
 * @param {boolean} [defaultActivation=false] - Default state if class not found
 * @returns {boolean} Whether element is active for the given class
 */
Prism.util.isActive(element, className, defaultActivation);

Usage Examples:

// Check plugin activation
const codeElement = document.querySelector('code.language-js');

// Check if line numbers should be shown
const hasLineNumbers = Prism.util.isActive(codeElement, 'line-numbers', false);
if (hasLineNumbers) {
    console.log('Line numbers enabled');
}

// Check with default activation
const shouldShowToolbar = Prism.util.isActive(codeElement, 'toolbar', true);

// Plugin conditional activation
function conditionalPluginInit(element, pluginClass, pluginHandler) {
    if (Prism.util.isActive(element, pluginClass)) {
        pluginHandler(element);
    }
}

// Usage in plugin development
Prism.hooks.add('complete', function(env) {
    conditionalPluginInit(env.element, 'show-language', function(el) {
        const language = Prism.util.getLanguage(el);
        const label = document.createElement('span');
        label.textContent = language.toUpperCase();
        label.className = 'language-label';
        el.parentNode.insertBefore(label, el);
    });
});

Advanced Utility Patterns

Utility Combinations

// Combine utilities for complex operations
function smartLanguageDetection(element) {
    const detectedLang = Prism.util.getLanguage(element);
    
    if (detectedLang === 'none') {
        // Try to detect from content or context
        const content = element.textContent;
        if (content.includes('function') && content.includes('{')) {
            Prism.util.setLanguage(element, 'javascript');
            return 'javascript';
        }
    }
    
    return detectedLang;
}

// Safe grammar operations
function safeGrammarOperation(grammarId, operation) {
    const grammar = Prism.languages[grammarId];
    if (!grammar) {
        console.error(`Grammar '${grammarId}' not found`);
        return null;
    }
    
    const grammarType = Prism.util.type(grammar);
    if (grammarType !== 'Object') {
        console.error(`Invalid grammar type: ${grammarType}`);
        return null;
    }
    
    try {
        const cloned = Prism.util.clone(grammar);
        return operation(cloned);
    } catch (error) {
        console.error('Grammar operation failed:', error);
        return null;
    }
}

Performance Utilities

// Efficient type checking
const typeCache = new Map();

function cachedType(obj) {
    const id = Prism.util.objId(obj);
    if (typeCache.has(id)) {
        return typeCache.get(id);
    }
    
    const type = Prism.util.type(obj);
    typeCache.set(id, type);
    return type;
}

// Batch language operations
function batchSetLanguage(elements, language) {
    elements.forEach(element => {
        Prism.util.setLanguage(element, language);
    });
}

// Memory-conscious cloning
function shallowCloneIfNeeded(obj, deep = false) {
    const objType = Prism.util.type(obj);
    
    if (objType === 'Object' || objType === 'Array') {
        return deep ? Prism.util.clone(obj) : Object.assign({}, obj);
    }
    
    return obj; // Primitives don't need cloning
}

Error Handling Utilities

// Safe utility calls with fallbacks
function safeGetLanguage(element, fallback = 'text') {
    try {
        const language = Prism.util.getLanguage(element);
        return language !== 'none' ? language : fallback;
    } catch (error) {
        console.warn('Language detection failed:', error);
        return fallback;
    }
}

function safeClone(obj, fallback = null) {
    try {
        return Prism.util.clone(obj);
    } catch (error) {
        console.error('Cloning failed:', error);
        return fallback;
    }
}

// Validation utilities
function validateElement(element, requiredClasses = []) {
    if (!element || Prism.util.type(element) !== 'Object') {
        return false;
    }
    
    return requiredClasses.every(className => 
        Prism.util.isActive(element, className)
    );
}

Install with Tessl CLI

npx tessl i tessl/npm-prismjs

docs

core-highlighting.md

index.md

language-system.md

plugin-system.md

token-system.md

utilities.md

tile.json