CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-unocss--core

The core atomic CSS engine for UnoCSS that generates CSS on-demand without any presets or default utilities.

Pending
Overview
Eval results
Files

extraction.mddocs/

Content Extraction

The content extraction system in UnoCSS Core identifies utility classes from source code across different file types. It provides a pluggable architecture for custom extraction strategies and handles various content sources.

Extractor Interface

interface Extractor {
  name: string;
  order?: number;
  extract?: (ctx: ExtractorContext) => Awaitable<Set<string> | CountableSet<string> | string[] | undefined | void>;
}

interface ExtractorContext {
  readonly original: string;
  code: string;
  id?: string;
  extracted: Set<string> | CountableSet<string>;
  envMode?: 'dev' | 'build';
}

Extractor properties:

  • name: Unique identifier for the extractor
  • order: Processing order (lower numbers processed first)
  • extract: Function that identifies utility classes from code

ExtractorContext properties:

  • original: Original unmodified source code
  • code: Current code (may be modified by previous extractors)
  • id: File identifier or path
  • extracted: Set to add found utility classes to
  • envMode: Current environment mode (dev or build)

Default Extractor

const extractorSplit: Extractor = {
  name: '@unocss/core/extractor-split',
  order: 0,
  extract({ code }) {
    return splitCode(code);
  }
};

const extractorDefault: Extractor = extractorSplit;

function splitCode(code: string): string[];

The default extractor splits source code by whitespace and common delimiters to identify potential utility classes.

Split Patterns

const defaultSplitRE: RegExp = /[\\:]?[\s'"`;{}]+/g;
const splitWithVariantGroupRE: RegExp = /([\\:]?[\s"'`;<>]|:\(|\)"|\)\s)/g;
  • defaultSplitRE: Standard splitting pattern for most content
  • splitWithVariantGroupRE: Enhanced pattern that handles variant groups like hover:(text-red bg-blue)

Extractor Application

// From UnoGenerator class
applyExtractors(
  code: string,
  id?: string,
  extracted?: Set<string>
): Promise<Set<string>>;
applyExtractors(
  code: string,
  id?: string,  
  extracted?: CountableSet<string>
): Promise<CountableSet<string>>;

Applies all configured extractors to source code in order, accumulating results.

Custom Extractors

Basic Custom Extractor

const customExtractor: Extractor = {
  name: 'my-custom-extractor',
  order: 10,
  extract({ code, id }) {
    const classes = new Set<string>();
    
    // Extract from class attributes
    const classMatches = code.matchAll(/class="([^"]+)"/g);
    for (const match of classMatches) {
      const classNames = match[1].split(/\s+/);
      classNames.forEach(name => classes.add(name));
    }
    
    return classes;
  }
};

File-Type Specific Extractor

const vueExtractor: Extractor = {
  name: 'vue-extractor',
  extract({ code, id }) {
    // Only process .vue files
    if (!id?.endsWith('.vue')) return;
    
    const classes = new Set<string>();
    
    // Extract from template section
    const templateMatch = code.match(/<template[^>]*>([\s\S]*?)<\/template>/);
    if (templateMatch) {
      const template = templateMatch[1];
      const classMatches = template.matchAll(/(?:class|:class)="([^"]+)"/g);
      for (const match of classMatches) {
        match[1].split(/\s+/).forEach(cls => classes.add(cls));
      }
    }
    
    return classes;
  }
};

Regex-Based Extractor

const regexExtractor: Extractor = {
  name: 'regex-extractor',
  extract({ code }) {
    const classes = new Set<string>();
    
    // Multiple regex patterns
    const patterns = [
      /className\s*=\s*["']([^"']+)["']/g,
      /class\s*=\s*["']([^"']+)["']/g,
      /tw`([^`]+)`/g, // Tagged template literals
    ];
    
    for (const pattern of patterns) {
      const matches = code.matchAll(pattern);
      for (const match of matches) {
        match[1].split(/\s+/).filter(Boolean).forEach(cls => classes.add(cls));
      }
    }
    
    return classes;
  }
};

CountableSet for Frequency Tracking

class CountableSet<K> extends Set<K> {
  getCount(key: K): number;
  setCount(key: K, count: number): this;
  add(key: K): this;
  delete(key: K): boolean;
  clear(): void;
}

function isCountableSet<T = string>(value: any): value is CountableSet<T>;

CountableSet tracks how many times each utility class appears, useful for usage analytics and optimization.

Using CountableSet

const frequencyExtractor: Extractor = {
  name: 'frequency-extractor',
  extract({ code, extracted }) {
    if (!isCountableSet(extracted)) return;
    
    const matches = code.matchAll(/class="([^"]+)"/g);
    for (const match of matches) {
      const classes = match[1].split(/\s+/);
      classes.forEach(cls => {
        const current = extracted.getCount(cls);
        extracted.setCount(cls, current + 1);
      });
    }
  }
};

Content Sources Configuration

interface ContentOptions {
  filesystem?: string[];
  inline?: (string | { code: string, id?: string } | (() => Awaitable<string | { code: string, id?: string }>))[];
  pipeline?: false | {
    include?: FilterPattern;
    exclude?: FilterPattern;
  };
}

type FilterPattern = ReadonlyArray<string | RegExp> | string | RegExp | null;

Content source types:

  • filesystem: Glob patterns for file system scanning
  • inline: Inline code strings or functions returning code
  • pipeline: Build tool integration filters

Content Configuration Examples

const uno = await createGenerator({
  content: {
    // Scan specific file patterns
    filesystem: [
      'src/**/*.{js,ts,jsx,tsx}',
      'components/**/*.vue',
      'pages/**/*.html'
    ],
    
    // Include inline content
    inline: [
      'flex items-center justify-between',
      { code: '<div class="p-4 bg-white">Content</div>', id: 'inline-1' },
      () => fetchDynamicContent()
    ],
    
    // Pipeline filters for build tools
    pipeline: {
      include: [/\.(vue|jsx|tsx)$/],
      exclude: [/node_modules/, /\.test\./]
    }
  }
});

Advanced Extraction Patterns

Template Literal Extraction

const templateLiteralExtractor: Extractor = {
  name: 'template-literal',
  extract({ code }) {
    const classes = new Set<string>();
    
    // Extract from various template literal patterns
    const patterns = [
      /tw`([^`]+)`/g,                    // tw`class names`
      /css\s*`[^`]*@apply\s+([^;`]+)/g,  // CSS @apply statements
      /className=\{`([^`]+)`\}/g,        // React template literals
    ];
    
    for (const pattern of patterns) {
      const matches = code.matchAll(pattern);
      for (const match of matches) {
        const utilities = match[1]
          .split(/\s+/)
          .filter(cls => cls && !cls.includes('${'));
        utilities.forEach(cls => classes.add(cls));
      }
    }
    
    return classes;
  }
};

Comment-Based Extraction

const commentExtractor: Extractor = {
  name: 'comment-extractor',
  extract({ code }) {
    const classes = new Set<string>();
    
    // Extract from special comments
    const commentPattern = /\/\*\s*@unocss:\s*([^*]+)\s*\*\//g;
    const matches = code.matchAll(commentPattern);
    
    for (const match of matches) {
      const utilities = match[1].split(/\s+/).filter(Boolean);
      utilities.forEach(cls => classes.add(cls));
    }
    
    return classes;
  }
};

Extractor Best Practices

Performance Optimization

  1. Early Returns: Return early for irrelevant files
  2. Compiled Regex: Pre-compile regex patterns outside the extract function
  3. Set Operations: Use Set for deduplication
  4. Order Matters: Set appropriate order values for processing sequence

Error Handling

const robustExtractor: Extractor = {
  name: 'robust-extractor',
  extract({ code, id }) {
    try {
      const classes = new Set<string>();
      
      // Extraction logic with potential errors
      const parsed = parseComplexSyntax(code);
      
      return classes;
    } catch (error) {
      console.warn(`Extraction failed for ${id}:`, error);
      return new Set<string>();
    }
  }
};

Testing Extractors

// Test utility for extractor development
async function testExtractor(extractor: Extractor, code: string, id?: string) {
  const extracted = new Set<string>();
  const context: ExtractorContext = {
    original: code,
    code,
    id,
    extracted,
    envMode: 'build'
  };
  
  const result = await extractor.extract?.(context);
  return result || extracted;
}

// Usage
const result = await testExtractor(customExtractor, '<div class="flex p-4">Test</div>');
console.log(result); // Set { 'flex', 'p-4' }

Install with Tessl CLI

npx tessl i tessl/npm-unocss--core

docs

config.md

extraction.md

generator.md

index.md

rules-variants.md

types.md

utilities.md

tile.json