The core atomic CSS engine for UnoCSS that generates CSS on-demand without any presets or default utilities.
—
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.
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:
ExtractorContext properties:
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.
const defaultSplitRE: RegExp = /[\\:]?[\s'"`;{}]+/g;
const splitWithVariantGroupRE: RegExp = /([\\:]?[\s"'`;<>]|:\(|\)"|\)\s)/g;hover:(text-red bg-blue)// 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.
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;
}
};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;
}
};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;
}
};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.
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);
});
}
}
};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:
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\./]
}
}
});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;
}
};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;
}
};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>();
}
}
};// 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