CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-css-tree

A tool set for CSS: fast detailed parser (CSS → AST), walker (AST traversal), generator (AST → CSS) and lexer (validation and matching) based on specs and browser implementations

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

utilities.mddocs/

Utility Functions

Collection of utility functions for CSS identifier, string, and URL encoding/decoding, plus AST manipulation helpers.

Capabilities

Identifier Utilities

Functions for encoding and decoding CSS identifiers with proper escaping:

namespace ident {
  /**
   * Encodes a string as a CSS identifier with proper escaping
   * @param str - String to encode as identifier
   * @returns Properly escaped CSS identifier
   */
  function encode(str: string): string;

  /**
   * Decodes a CSS identifier, unescaping escape sequences
   * @param str - CSS identifier to decode
   * @returns Decoded string
   */
  function decode(str: string): string;
}

Usage Examples:

import { ident } from 'css-tree';

// Encode identifiers with special characters
console.log(ident.encode('my-class'));      // "my-class"
console.log(ident.encode('2nd-item'));      // "\\32 nd-item"
console.log(ident.encode('with space'));    // "with\\ space"
console.log(ident.encode('with:colon'));    // "with\\:colon"

// Decode escaped identifiers
console.log(ident.decode('\\32 nd-item'));  // "2nd-item"
console.log(ident.decode('with\\ space'));  // "with space"
console.log(ident.decode('with\\:colon'));  // "with:colon"

// Safe class name generation
function safeClassName(name) {
  return ident.encode(name.replace(/[^a-zA-Z0-9-_]/g, '-'));
}

String Utilities

Functions for encoding and decoding CSS string literals:

namespace string {
  /**
   * Encodes a string as a CSS string literal with proper quotes and escaping
   * @param str - String to encode
   * @param apostrophe - Use single quotes instead of double quotes
   * @returns Properly quoted and escaped CSS string
   */
  function encode(str: string, apostrophe?: boolean): string;

  /**
   * Decodes a CSS string literal, removing quotes and unescaping sequences
   * @param str - CSS string literal to decode
   * @returns Decoded string content
   */
  function decode(str: string): string;
}

Usage Examples:

import { string } from 'css-tree';

// Encode strings with special characters
console.log(string.encode('Hello World'));           // "\"Hello World\""
console.log(string.encode('Hello "World"'));         // "\"Hello \\\"World\\\"\""
console.log(string.encode("Hello 'World'", true));   // "'Hello \\'World\\''"
console.log(string.encode('Line 1\nLine 2'));        // "\"Line 1\\A Line 2\""

// Decode string literals
console.log(string.decode('"Hello World"'));         // "Hello World"
console.log(string.decode('"Hello \\"World\\""'));   // "Hello \"World\""
console.log(string.decode("'Hello \\'World\\''"));   // "Hello 'World'"
console.log(string.decode('"Line 1\\A Line 2"'));    // "Line 1\nLine 2"

// Generate content property values
function generateContent(text) {
  return `content: ${string.encode(text)};`;
}

URL Utilities

Functions for encoding and decoding CSS URL values:

namespace url {
  /**
   * Encodes a URL string as a complete CSS url() function
   * @param str - URL string to encode
   * @returns Complete CSS url() function with encoded URL
   */
  function encode(str: string): string;

  /**
   * Decodes a URL from CSS url() function content
   * @param str - URL content from url() function
   * @returns Decoded URL string
   */
  function decode(str: string): string;
}

Usage Examples:

import { url } from 'css-tree';

// Encode URLs with special characters
console.log(url.encode('image.png'));                    // "url(image.png)"
console.log(url.encode('image with spaces.png'));       // "url(image\\ with\\ spaces.png)"
console.log(url.encode('path/to/image.png'));           // "url(path/to/image.png)"
console.log(url.encode('https://example.com/img.png')); // "url(https://example.com/img.png)"

// Decode URL content
console.log(url.decode('image.png'));                   // "image.png"
console.log(url.decode('image\\ with\\ spaces.png'));   // "image with spaces.png"
console.log(url.decode('path/to/image.png'));          // "path/to/image.png"

// Generate background-image values
function generateBackgroundImage(imagePath) {
  return `background-image: url(${url.encode(imagePath)});`;
}

Name Analysis Functions

Functions for analyzing CSS property and keyword names:

/**
 * Analyzes a CSS keyword name for vendor prefixes and custom properties
 * @param name - Keyword name to analyze
 * @returns Analysis result with prefix information
 */
function keyword(name: string): KeywordAnalysis;

/**
 * Analyzes a CSS property name for vendor prefixes, hacks, and custom properties
 * @param name - Property name to analyze
 * @returns Analysis result with detailed information
 */
function property(name: string): PropertyAnalysis;

interface KeywordAnalysis {
  /** Base name without prefixes */
  basename: string;
  /** Original name */
  name: string;
  /** Vendor prefix if present */
  prefix?: string;
  /** Vendor identifier (webkit, moz, ms, o) */
  vendor?: string;
  /** Whether it's a custom property (starts with --) */
  custom: boolean;
}

interface PropertyAnalysis {
  /** Base name without prefixes or hacks */
  basename: string;
  /** Original name */
  name: string;
  /** CSS hack character if present */
  hack?: string;
  /** Vendor identifier (webkit, moz, ms, o) */
  vendor?: string;
  /** Vendor prefix (-webkit-, -moz-, etc.) */
  prefix?: string;
  /** Whether it's a custom property (starts with --) */
  custom: boolean;
}

Usage Examples:

import { keyword, property } from 'css-tree';

// Analyze keywords
console.log(keyword('inherit'));           // { basename: 'inherit', name: 'inherit', custom: false }
console.log(keyword('-webkit-fill'));      // { basename: 'fill', name: '-webkit-fill', prefix: '-webkit-', vendor: 'webkit', custom: false }
console.log(keyword('--custom-value'));    // { basename: '--custom-value', name: '--custom-value', custom: true }

// Analyze properties
console.log(property('color'));            // { basename: 'color', name: 'color', custom: false }
console.log(property('-webkit-transform')); // { basename: 'transform', name: '-webkit-transform', prefix: '-webkit-', vendor: 'webkit', custom: false }
console.log(property('*zoom'));            // { basename: 'zoom', name: '*zoom', hack: '*', custom: false }
console.log(property('--main-color'));     // { basename: '--main-color', name: '--main-color', custom: true }

// Group properties by vendor
function groupPropertiesByVendor(properties) {
  const groups = { standard: [], webkit: [], moz: [], ms: [], o: [] };
  
  properties.forEach(prop => {
    const analysis = property(prop);
    const group = analysis.vendor || 'standard';
    groups[group].push(prop);
  });
  
  return groups;
}

AST Manipulation Utilities

Core utilities for AST cloning and conversion:

/**
 * Deep clones an AST node with all children and properties
 * @param node - AST node to clone
 * @returns Deep clone of the node
 */
function clone(node: CssNode): CssNode;

/**
 * Converts AST with List children to plain objects with arrays
 * @param ast - AST to convert
 * @returns Plain object representation
 */
function toPlainObject(ast: CssNode): object;

/**
 * Converts plain object AST to use List instances for children
 * @param ast - Plain object AST
 * @returns AST with List children
 */
function fromPlainObject(ast: object): CssNode;

Usage Examples:

import { parse, clone, toPlainObject, fromPlainObject, generate } from 'css-tree';

const ast = parse('.example { color: red; }');

// Clone AST for safe modification
const clonedAst = clone(ast);

// Modify clone without affecting original
walk(clonedAst, (node) => {
  if (node.type === 'Identifier' && node.name === 'red') {
    node.name = 'blue';
  }
});

console.log(generate(ast));        // ".example{color:red}"
console.log(generate(clonedAst));  // ".example{color:blue}"

// Convert to plain object for serialization
const plainObject = toPlainObject(ast);
const jsonString = JSON.stringify(plainObject, null, 2);

// Convert back from plain object
const restoredAst = fromPlainObject(JSON.parse(jsonString));
console.log(generate(restoredAst)); // ".example{color:red}"

// Deep clone with custom modifications
function cloneAndModify(ast, modifications) {
  const cloned = clone(ast);
  
  walk(cloned, (node) => {
    if (modifications[node.type]) {
      modifications[node.type](node);
    }
  });
  
  return cloned;
}

Advanced Utility Patterns

Complex utility functions for common CSS processing tasks:

// Normalize vendor prefixes
function normalizeVendorPrefixes(ast) {
  const normalized = clone(ast);
  
  walk(normalized, {
    Declaration: (node) => {
      const analysis = property(node.property.name);
      if (analysis.vendor) {
        // Store original vendor-prefixed version
        node.vendorPrefixed = node.property.name;
        // Use standard name
        node.property.name = analysis.basename;
      }
    }
  });
  
  return normalized;
}

// Extract custom properties
function extractCustomProperties(ast) {
  const customProps = [];
  
  walk(ast, {
    Declaration: (node) => {
      const analysis = property(node.property.name);
      if (analysis.custom) {
        customProps.push({
          name: node.property.name,
          value: generate(node.value),
          node: clone(node)
        });
      }
    }
  });
  
  return customProps;
}

// Validate identifiers in selectors
function validateSelectors(ast) {
  const issues = [];
  
  walk(ast, {
    ClassSelector: (node) => {
      try {
        ident.decode(node.name);
      } catch (error) {
        issues.push({
          type: 'InvalidClassSelector',
          name: node.name,
          message: error.message
        });
      }
    },
    IdSelector: (node) => {
      try {
        ident.decode(node.name);
      } catch (error) {
        issues.push({
          type: 'InvalidIdSelector',
          name: node.name,
          message: error.message
        });
      }
    }
  });
  
  return issues;
}

// Safe string generation for content property
function generateContentValue(text, useDoubleQuotes = true) {
  try {
    return string.encode(text, !useDoubleQuotes);
  } catch (error) {
    // Fallback to basic escaping
    const escaped = text.replace(/["'\\]/g, '\\$&');
    return useDoubleQuotes ? `"${escaped}"` : `'${escaped}'`;
  }
}

// URL resolution helper
function resolveUrls(ast, baseUrl) {
  const resolved = clone(ast);
  
  walk(resolved, {
    Url: (node) => {
      try {
        const urlValue = url.decode(node.value);
        if (!urlValue.match(/^https?:\/\//)) {
          // Resolve relative URL
          const resolvedUrl = new URL(urlValue, baseUrl).href;
          node.value = url.encode(resolvedUrl);
        }
      } catch (error) {
        console.warn('Failed to resolve URL:', node.value);
      }
    }
  });
  
  return resolved;
}

Install with Tessl CLI

npx tessl i tessl/npm-css-tree

docs

data-structures.md

generation.md

index.md

parsing.md

tokenization.md

traversal.md

utilities.md

validation.md

tile.json