UNPKG

isaaccss

Version:

Inline-Style-as-a-Class CSS engine

140 lines (139 loc) 5.36 kB
import { all as __knownCssProperties } from "known-css-properties"; const knownCssPropertySet = new Set(__knownCssProperties); function* deepFlatFilterMap(arrayOrElement, selector, predicate) { if (arrayOrElement) { if (Array.isArray(arrayOrElement)) { for (const element of arrayOrElement) { yield* deepFlatFilterMap(element, selector); } } else { const element = selector(arrayOrElement); if (element !== undefined && element !== null && element !== false && (!predicate || predicate(element))) { yield element; } } } } const applyAliases = (source, tokenPattern, aliases) => { if (!aliases) { return source; } if (!Array.isArray(aliases)) { return source.replaceAll(tokenPattern, token => aliases[token] ?? token); } if (aliases[0] instanceof RegExp) { return source.replace(aliases[0], aliases[1]); } for (const alias of aliases) { source = applyAliases(source, tokenPattern, alias); } return source; }; const applyPropertyAliases = (source, aliases) => { if (!aliases) { return source; } if (!Array.isArray(aliases)) { return [].concat(...source.map(s => aliases[s] ?? s)); } if (aliases[0] instanceof RegExp) { const [search, replacer] = aliases; if (Array.isArray(replacer)) { return source.flatMap(s => (search.test(s) ? replacer.map(r => s.replace(search, r)) : [s])); } else { return source.map(s => s.replace(search, replacer)); } } for (const alias of aliases) { source = applyPropertyAliases(source, alias); } return source; }; const unescapeBackslash = (s) => s.replace(/\\(.)/g, "$1"); const unescapeWhitespace = (s) => s.replace(/(^|[^\\])(\\\\)*_/g, "$1$2 "); const transformMediaOrContainer = (query, aliases, aliasSelector) => { for (const alias of deepFlatFilterMap(aliases, aliasSelector)) { query = applyAliases(query, /[\w$#@-]+/g, alias); } query = unescapeWhitespace(query); query = query.replace(/(^| )([^ ()]+\b[^ ()]+)($| )/g, "$1($2)$3"); query = query.replace(/([^ ])\(/g, "$1 ("); return unescapeBackslash(query); }; const transformSelector = (selector, aliases) => { for (const alias of deepFlatFilterMap(aliases, a => a.selector)) { selector = applyAliases(selector, /([:>+~_]|::)[\w$#@-]+/g, alias); } selector = unescapeWhitespace(selector); return unescapeBackslash(selector); }; const transformProperty = (property, aliases) => { if (property.startsWith("--")) { return [[property], []]; } let properties = [property]; for (const alias of deepFlatFilterMap(aliases, a => a.property)) { properties = applyPropertyAliases(properties, alias); } const known = []; const unknown = []; for (const p of properties) { knownCssPropertySet.has(p) ? known.push(p) : unknown.push(p); } return [known, unknown]; }; const transformValue = (property, value, aliases) => { for (const alias of deepFlatFilterMap(aliases, a => a.value)) { for (const [p, a] of alias) { if (p instanceof RegExp ? p.test(property) : p === property) { value = applyAliases(value, /[\w$#@-]+/g, a); } } } value = unescapeWhitespace(value); value = value.replace(/\$([_a-zA-Z0-9-]*[a-zA-Z0-9])/g, "var(--$1)"); return unescapeBackslash(value); }; export const parseClass = (className, options) => { const aliases = options?.aliases; const match = className.match( // @media/ @^container/ selector/ property ...properties ? * /^(?:@((?:[^/\\]|\\.)+?)\/)?(?:@\^((?:[^/\\]|\\.)+?)\/)?(?:((?:[^/\\]|\\.)+?)\/)?([^:]+:.+?!?(?:;[^:]+:.+?!?)*)(\??)(\**)$/); if (!match) { return { className, properties: [], unknownProperties: [className], specificity: 1 }; } const properties = []; const unknownProperties = []; for (const s of match[4].split(";")) { const match = s.match(/^([^:]+):(.+?)(!?)$/); if (match) { const [known, unknown] = transformProperty(match[1], aliases); known.forEach(name => properties.push({ name, value: transformValue(name, match[2], aliases), important: !!match[3] })); unknownProperties.push(...unknown); } else { unknownProperties.push(s); } } const style = { className, properties, unknownProperties, specificity: (match[5] === "?" ? 0 : 1) + match[6].length, }; if (match[1]?.startsWith("^")) { if (match[2]) { return { className, properties: [], unknownProperties: [className], specificity: 1 }; } style.container = transformMediaOrContainer(match[1].slice(1), aliases, a => a.container); } else { match[1] && (style.media = transformMediaOrContainer(match[1], aliases, a => a.media)); match[2] && (style.container = transformMediaOrContainer(match[2], aliases, a => a.container)); } match[5] === "?" && (style.layer = ""); match[3] && (style.selector = transformSelector(match[3], aliases)); return style; };