UNPKG

@mapcss/config

Version:

Definition of MapCSS config, loader

115 lines (114 loc) 3.92 kB
import { isString, isUndefined, postcss, prop, Root, wrap } from "./deps.js"; import { resolveConfig, resolveCSSMap, resolveModifierMap } from "./resolve.js"; import { escapeSelector } from "./utils/escape.js"; import { minify as postcssMinify, orderProp, orderStatement, } from "./postcss/mod.js"; import { createInjectCSS } from "./preprocess.js"; import { CHAR_MAP, SEPARATOR, VARIABLE_PREFIX } from "./constant.js"; const defaultSyntax = { name: "mapcss/default-syntax", fn: (identifier) => ({ identifier }), }; function rootKeys(value) { return Array.from(value.reduceRight((acc, cur) => { Object.keys(cur).forEach((key) => { acc.add(key); }); return acc; }, new Set())); } /** Generate output of CSS Style Sheet */ export function generate( /** Input token */ input, { separator = SEPARATOR, variablePrefix = VARIABLE_PREFIX, charMap = CHAR_MAP, minify = false, ...staticConfig }, {} = {}) { const ctx = { separator, variablePrefix, charMap, minify, }; const { syntax, modifierMaps, theme, cssMaps, preProcess, postcssPlugin, css, } = resolveConfig(staticConfig, ctx); const staticContext = { ...ctx, theme, }; const tokens = isSet(input) ? input : new Set(wrap(input)); const matched = new Set(); const unmatched = new Set(); const results = Array.from(tokens).map((token) => { let rootCache; const mappedToken = mapChar(token, charMap); for (const { fn } of [...syntax, defaultSyntax]) { const parseResult = fn(mappedToken, { ...staticContext, modifierRoots: rootKeys(modifierMaps), identifierRoots: rootKeys(cssMaps), }); if (!parseResult) return; const { identifier, modifiers = [] } = parseResult; const className = `.${escapeSelector(token)}`; const runtimeContext = { token, mappedToken, className, }; const identifierRoot = resolveCSSMap(identifier, cssMaps.reverse(), { ...staticContext, ...runtimeContext, }); if (isUndefined(identifierRoot)) continue; const results = modifiers.reduceRight((acc, cur) => { if (isUndefined(acc)) return; return resolveModifierMap(cur, modifierMaps, acc, { ...staticContext, ...runtimeContext, }); }, identifierRoot); if (results instanceof Root) { unmatched.delete(token); matched.add(token); rootCache = results; break; } unmatched.add(token); } return rootCache; }).filter(Boolean); const rootNode = results.reduce((acc, cur) => { acc.append(cur.nodes); return acc; }, new Root()); const final = [createInjectCSS(css), ...preProcess].reduce((acc, cur) => cur.fn(acc, staticContext), rootNode); const corePostcssPlugins = [orderStatement(), orderProp()]; const plugins = minify ? [...corePostcssPlugins, postcssMinify()] : corePostcssPlugins; const ast = postcss(...plugins, ...postcssPlugin).process(final).root; const output = { ast, get css() { return ast.toString(); }, matched, unmatched, }; return output; } export function mapChar(character, charMap) { let value = ""; for (const char of character) { const c = prop(char, charMap); if (isString(c)) { value += c; } else { value += char; } } return value; } function isSet(value) { return value instanceof Set; }