UNPKG

@illlia/css-merge

Version:

css-merge is a tiny package to compose css classes on the 'last class wins' basis

99 lines (98 loc) 4.21 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createCssMerge = exports.SPECIAL_SPLIT_CHART = void 0; const log_when_1 = require("./utils/log-when"); const map_values_1 = require("./utils/map-values"); exports.SPECIAL_SPLIT_CHART = ","; const createCssMerge = (compressedConfig) => { if (!compressedConfig) throw "No config"; const config = (0, map_values_1.mapValues)(compressedConfig, (data) => { const obj = {}; // split into 4s const groups = []; data.forEach((curr, i) => { const index = i % 4; // @ts-ignore if (index === 0) groups.push([]); groups.at(-1).push(curr); }); // populate config groups.forEach(([props, value, order, important]) => { props.split(exports.SPECIAL_SPLIT_CHART).forEach((p) => { obj[p] = { v: value, o: order, ...(important && { i: 1 }) }; }); }); return obj; }); return (...allClasses) => { const log = (0, log_when_1.logWhen)(false); const currentStyles = {}; /** TODO: performance test using split & join VS regex replaceAll for conflicting classes Ex. (?:^| +)(?:hello|how) * string.replace(/[/\-\\^$*+?.()|[\]{}]/g, '\\$&'); @link https://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript */ return allClasses .reverse() .filter(isTruthy) .map((classNames, i, arr) => { // return first one unchanged to improve perf and do less work when merging is not required // let TW linter handle duplicates and classes that update the same thing // return classNames if (i === 0) return classNames; // populate current styles only when needed if (i === 1) { getClasses(arr[0]).forEach((c) => Object.assign(currentStyles, config[c])); } const classes = getClasses(classNames); const nonConflictingClasses = classes.reverse().filter((c) => { const styles = config[c]; // propagate unknown styles, assume custom css classes or css modules if (!styles) return true; const entries = Object.entries(styles); // adds some styles without overriding existing let addsStyle = false; let addsImportant = false; const noOverride = entries.every(([prop, map]) => { const noExistingStyle = currentStyles[prop] === undefined; addsStyle = addsStyle || noExistingStyle; addsImportant = !currentStyles[prop]?.i && map.i; return (noExistingStyle || currentStyles[prop].v === map.v || currentStyles[prop].o > map.o); }); const shouldAdd = (addsStyle && noOverride) || addsImportant; log({ shouldAdd, addsStyle, noOverride, class: c, allClasses, currentStyles: { ...currentStyles }, styles, }); return shouldAdd; }); // TODO: add all styles as a batch instead of one by one ? treat each passed string as a checked one? // No performance benefit, but might influence the styles output // Ex. px-4 p-2 + pb-6 --> px-4 p-2 pb-6 OR p-2 pb-6 --> 1st result might be more expected (as batch) nonConflictingClasses.forEach((c) => Object.assign(currentStyles, config[c])); return nonConflictingClasses.reverse().join(" "); }) .reverse() .filter(isTruthy) .join(" "); }; }; exports.createCssMerge = createCssMerge; const getClasses = (classNames) => classNames .split(" ") .map((x) => x.trim()) .filter(Boolean); const isTruthy = (value) => !!value;