@mapcss/config
Version:
Definition of MapCSS config, loader
115 lines (114 loc) • 3.92 kB
JavaScript
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;
}