@mapcss/preset-svg
Version:
SVG as CSS for MapCSS
236 lines (235 loc) • 8.35 kB
JavaScript
import { deepMerge, distinctBy, head, init, isEmptyObject, isFunction, isLength0, isRegExp, isString, isUndefined, last, prop, propPath, Root, Rule, tail, } from "../deps.js";
import { toAST } from "../deps.js";
import { isCSSDefinition, isCSSObject } from "./utils/assert.js";
function firstSplit(value, separator) {
const arr = new RegExp(`(.+?)${separator}(.+)`).exec(value);
if (!arr)
return;
const [_, ...rest] = arr;
return rest;
}
function leftSplit(value, separator) {
const _value = isString(value) ? [value] : value;
const _last = last(_value);
if (!_last)
return [_value];
const result = firstSplit(_last, separator);
if (!result)
return [_value];
return [_value, ...leftSplit([...init(_value), ...result], separator)];
}
class MockRegExpExecArray extends Array {
constructor(input = "") {
super();
Object.defineProperty(this, "input", {
enumerable: true,
configurable: true,
writable: true,
value: input
});
Object.defineProperty(this, "index", {
enumerable: true,
configurable: true,
writable: true,
value: 0
});
}
}
export function resolveDeepMapIdentifier(value, deepMapCSS, context) {
const paths = leftSplit(value, context.separator);
for (const path of paths) {
const first = head(path) ?? "DEFAULT";
const rest = tail(path);
const identifierContext = {
...context,
parentKey: context.key,
key: first,
path,
};
const has = deepMapCSS.has(first);
if (has) {
const definition = deepMapCSS.get(first);
if (isFunction(definition)) {
if (!isLength0(rest))
continue;
const result = definition(new MockRegExpExecArray(), identifierContext);
if (isUndefined(result))
continue;
return handleCSSObject(result, identifierContext.className);
}
if (definition instanceof Map) {
return resolveDeepMapIdentifier(rest, definition, identifierContext);
}
if (isEmptyObject(definition))
return;
return handleCSSObject(definition, identifierContext.className);
}
for (const [key, m] of deepMapCSS) {
if (isRegExp(key)) {
const regExpExecResult = key.exec(first);
if (!regExpExecResult)
continue;
if (isFunction(m)) {
const result = m(regExpExecResult, identifierContext);
if (isUndefined(result))
continue;
return handleCSSObject(result, identifierContext.className);
}
if (m instanceof Map) {
return resolveDeepMapIdentifier(rest, m, identifierContext);
}
if (isEmptyObject(m))
return;
return handleCSSObject(m, identifierContext.className);
}
}
}
}
function handleCSSObject(cssObject, selector) {
if (cssObject instanceof Root) {
return cssObject;
}
else if (isCSSDefinition(cssObject)) {
return toAST(cssObject.value);
}
else {
return new Root({
nodes: [new Rule({ selector, nodes: toAST(cssObject).nodes })],
});
}
}
/** resolve theme via propPath safety */
export function resolveTheme(identifier, themeRoot, { separator, theme }) {
const paths = leftSplit(identifier, separator);
for (const path of paths) {
const result = propPath([themeRoot, ...path], theme);
if (isString(result)) {
return result;
}
}
}
/** new version for theme resolver */
export function $resolveTheme(identifier, themeRoot, { separator, theme }) {
const recursive = (path, theme) => {
const first = head(path);
if (isUndefined(first))
return theme;
const result = prop(first, theme);
if (isString(result) || isUndefined(result))
return result;
return recursive(tail(path), result);
};
const paths = leftSplit(identifier, separator);
for (const path of paths) {
const rootResult = prop(themeRoot, theme);
if (isUndefined(rootResult))
continue;
if (isString(rootResult))
return rootResult;
const result = recursive(path, rootResult);
if (!isUndefined(result)) {
return result;
}
}
}
function pickByName({ name }) {
return name;
}
export function resolvePreProcessor(...postProcessors) {
return distinctBy(postProcessors, pickByName);
}
export function resolveSyntax(...syntaxes) {
return distinctBy(syntaxes, pickByName);
}
export function resolvePreset(preset, context) {
return distinctBy(preset, pickByName).map(({ fn }) => {
const { syntax = [], cssMap = {}, modifierMap = {}, theme = {}, preProcess = [], postcssPlugin = [], css = {}, } = fn(context);
return {
syntax,
cssMap,
modifierMap,
theme,
preProcess,
postcssPlugin,
css,
};
});
}
/** resolve config to deep merge */
export function resolveConfig({ syntax: _syntax = [], preset = [], cssMap: _cssMap = {}, theme: _theme = {}, preProcess: _postProcess = [], modifierMap: _modifierMap = {}, postcssPlugin: _postcssPlugin = [], css: _css = {}, }, context) {
const _presets = resolvePreset(preset, context);
const modifierMap = _presets.map(({ modifierMap }) => modifierMap)
.reduce((acc, cur) => {
return deepMerge(acc, cur);
}, _modifierMap);
const theme = _presets.map(({ theme }) => theme).reduce((acc, cur) => {
return deepMerge(acc, cur);
}, _theme);
const syntax = resolveSyntax(..._syntax, ..._presets.map(({ syntax }) => syntax).flat());
const deepMapCSS = mergeCSSMap([..._presets.map(({ cssMap }) => cssMap), _cssMap]);
const preProcess = resolvePreProcessor(..._postProcess, ..._presets.map(({ preProcess }) => preProcess).flat());
const css = [..._presets.map(({ css }) => css), _css].reduce((acc, cur) => deepMerge(acc, cur), {});
const postcssPlugin = [
..._postcssPlugin,
..._presets.map(({ postcssPlugin }) => postcssPlugin).flat(),
];
return {
deepMapCSS,
theme,
modifierMap,
syntax,
preProcess,
css,
postcssPlugin,
};
}
export function mergeCSSMap(cssMaps) {
const recursive = (id, map) => {
const entries = Array.isArray(id) ? id : Object.entries(id);
entries.forEach(([key, value]) => {
const _key = isRegExp(key) ? key : String(key);
if (isFunction(value) || isCSSObject(value)) {
map.set(_key, value);
}
else {
map.set(_key, recursive(value, new Map()));
}
});
return map;
};
return cssMaps.reduce((acc, cur) => recursive(cur, acc), new Map());
}
export function resolveModifierMap(modifier, modifierMap, parentNode, context) {
const { separator } = context;
const paths = leftSplit(modifier, separator);
for (const path of paths) {
const _head = head(path);
const first = _head ?? "DEFAULT";
if (isUndefined(first))
continue;
if (!context.path) {
context.path = path;
}
const ctx = context.path
? context
: { ...context, path };
const modifier = prop(first, modifierMap);
if (isUndefined(modifier)) {
context.path = undefined;
continue;
}
if (isFunction(modifier)) {
const maybeRoot = modifier(parentNode, ctx);
context.path = undefined;
if (isUndefined(maybeRoot))
continue;
return maybeRoot;
}
const rest = tail(path);
const result = resolveModifierMap(rest, modifier, parentNode, ctx);
if (result) {
return result;
}
context.path = undefined;
}
}