tw-merge
Version:
Merge CSS utility classes without style conflicts - small and zero config
360 lines (351 loc) • 15.9 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
// LRU cache inspired from hashlru (https://github.com/dominictarr/hashlru/blob/v1.0.4/index.js) but object replaced with Map to improve performance
function createLruCache(maxCacheSize) {
if (maxCacheSize < 1) return {
get: function get() {
return undefined;
},
set: function set(_, value) {
return value;
}
};
var cacheSize = 0;
var cache = new Map();
var previousCache = new Map();
function update(key, value) {
cache.set(key, value);
cacheSize++;
if (cacheSize > maxCacheSize) {
cacheSize = 0;
previousCache = cache;
cache = new Map();
}
}
return {
get: function get(key) {
var value = cache.get(key);
if (value !== undefined) return value;
if ((value = previousCache.get(key)) !== undefined) {
update(key, value);
return value;
}
},
set: function set(key, value) {
if (cache.has(key)) cache.set(key, value);else update(key, value);
return value;
}
};
}
function isNumericValue(value) {
var _value$match;
if (!value) return true;
var arbitraryValue = (_value$match = value.match(/^\[(.*)\]$/)) == null ? void 0 : _value$match[1];
return !isNaN(parseInt(arbitraryValue != null ? arbitraryValue : value));
}
function sortContextSection(section, separator) {
return section.sort(function (a, b) {
if (a.startsWith("[") || b.startsWith("[")) return 0;
return a.localeCompare(b);
}).join(separator);
}
function normalizeContext(context, separator) {
if (!context) return context;
var important = context.endsWith("!");
var variants = context.replace(/:!?$/, "").split(separator);
var section = [];
var normalizedSections = [];
function commitSection() {
if (section.length > 0) normalizedSections.push(sortContextSection(section, separator));
}
for (var i = 0; i < variants.length; i++) {
var variant = variants[i];
if (variant.startsWith("[")) {
// is arbitrary variant
commitSection();
normalizedSections.push(variant);
section = [];
} else section.push(variant);
}
commitSection();
return "" + normalizedSections.join(separator) + separator + (important ? "!" : "");
}
function createMerge(ruleSet, _temp) {
var _ref = _temp === void 0 ? {} : _temp,
_ref$cacheSize = _ref.cacheSize,
cacheSize = _ref$cacheSize === void 0 ? 500 : _ref$cacheSize,
_ref$separator = _ref.separator,
separator = _ref$separator === void 0 ? ":" : _ref$separator,
prefix = _ref.prefix;
var cache = createLruCache(cacheSize);
var parsedRuleSet = ruleSet.map(function (_ref2) {
var regExp = _ref2[0],
handler = _ref2[1];
return [new RegExp("^(?<c>.*" + separator + "!?|!?)?-?" + (prefix ? prefix + "-" : "") + regExp), handler];
});
function merge(className) {
var cached = cache.get(className);
if (cached !== undefined) return cached;
var memoryStore = [];
var classes = className.split(" ");
var outputClasses = [];
// - for each class from right to left
for (var classI = classes.length - 1; classI >= 0; classI--) {
var currentClass = classes[classI];
var didNotMatchOrWasContinued = true;
// - for each rule
for (var ruleI = 0; ruleI < parsedRuleSet.length; ruleI++) {
var rule = parsedRuleSet[ruleI];
var regexp = rule[0];
var match = currentClass.match(regexp);
// - if class matches rule, execute it
if (match) {
var _groups$c, _memoryStore$ruleI, _memoryStore$ruleI$co, _ruleI, _memoryStore$_ruleI;
didNotMatchOrWasContinued = false;
var groups = match.groups;
var context = normalizeContext((_groups$c = groups == null ? void 0 : groups.c) != null ? _groups$c : "", separator);
var handler = rule[1];
var memory = (_memoryStore$ruleI$co = (_memoryStore$ruleI = (_memoryStore$_ruleI = memoryStore[_ruleI = ruleI]) != null ? _memoryStore$_ruleI : memoryStore[_ruleI] = {})[context]) != null ? _memoryStore$ruleI$co : _memoryStore$ruleI[context] = {};
var result = handler(memory, groups);
var keepClass = result === true;
var continueToNextRule = result === "c";
if (keepClass) outputClasses.unshift(currentClass);
// - finish with the class unless the rule says so
if (!continueToNextRule) break;
didNotMatchOrWasContinued = true;
}
}
if (didNotMatchOrWasContinued) outputClasses.unshift(currentClass);
}
return cache.set(className, outputClasses.join(" "));
}
return merge;
}
var TRAILING_SLASH_REGEXP = "(\\/\\d+)?";
var VALUE_REGEXP = "(-(?<v>.+?)" + TRAILING_SLASH_REGEXP + ")?";
function createSimpleHandler(_temp) {
var _ref = _temp === void 0 ? {} : _temp,
byType = _ref.byType;
var simpleHandler = function simpleHandler(memory, _ref2) {
var _memory$target;
var value = _ref2.v,
target = _ref2.t;
var type = byType && isNumericValue(value) ? "number" : "other";
var mem = (_memory$target = memory[target]) != null ? _memory$target : memory[target] = {};
// seen before
if (mem[type]) return false;
// never seen
return mem[type] = true;
};
return simpleHandler;
}
function simpleRule(target, _temp2) {
var _ref3 = _temp2 === void 0 ? {} : _temp2,
byType = _ref3.byType;
var regExp = "(?<t>" + target + ")" + VALUE_REGEXP + "$";
return [regExp, createSimpleHandler({
byType: byType
})];
}
var CARDINAL_OVERRIDES = {
t: ",y,tl,tr",
r: ",x,tr,br",
b: ",y,br,bl",
l: ",x,bl,tl",
x: "",
y: "",
s: "",
e: "",
ss: ",e,s",
se: ",e,s",
es: ",e,s",
ee: ",e,s"
};
var CARDINAL_DIRECTIONS = /*#__PURE__*/Object.keys(CARDINAL_OVERRIDES).join("|") + "|tl|tr|br|bl";
function createCardinalHandler(_temp3) {
var _ref4 = _temp3 === void 0 ? {} : _temp3,
byType = _ref4.byType;
var cardinalHandler = function cardinalHandler(memory, _ref5) {
var _memory$direction, _memory$_, _memory$_$type, _memory$_2, _CARDINAL_OVERRIDES$d;
var value = _ref5.v,
_ref5$d = _ref5.d,
direction = _ref5$d === void 0 ? "" : _ref5$d;
var type = byType && isNumericValue(value) ? "number" : "other";
var mem = (_memory$direction = memory[direction]) != null ? _memory$direction : memory[direction] = {};
// seen before
if (mem[type]) return false;
// apply override
var memOverriders = (_memory$_$type = (_memory$_ = (_memory$_2 = memory._) != null ? _memory$_2 : memory._ = {})[type]) != null ? _memory$_$type : _memory$_[type] = new Set();
if ((_CARDINAL_OVERRIDES$d = CARDINAL_OVERRIDES[direction]) != null && _CARDINAL_OVERRIDES$d.split(",").some(function (dir) {
return memOverriders.has(dir);
})) return false;
// remember overrider
memOverriders.add(direction);
// never seen
mem[type] = true;
return true;
};
return cardinalHandler;
}
function cardinalRule(target, _temp4) {
var _ref6 = _temp4 === void 0 ? {} : _temp4,
_ref6$dash = _ref6.dash,
dash = _ref6$dash === void 0 ? true : _ref6$dash,
byType = _ref6.byType;
var _target = target + "(" + (dash ? "-" : "") + "(?<d>" + CARDINAL_DIRECTIONS + "))?";
var regExp = "" + _target + VALUE_REGEXP + "$";
return [regExp, createCardinalHandler({
byType: byType
})];
}
function cardinalRules(targets, options) {
var _targets = targets.split("|");
return _targets.map(function (target) {
return cardinalRule(target, options);
});
}
// unique rule
// -----------
function createUniqueHandler() {
var uniqueHandler = function uniqueHandler(memory, groups) {
var key = Object.entries(groups).find(function (x) {
return x[1];
})[0];
return memory[key] ? false : memory[key] = true;
};
return uniqueHandler;
}
function uniqueRule(targets) {
var regExp = "(" + targets.map(function (target, targetI) {
return Array.isArray(target) ? target.slice(1).map(function (subtarget, subtargetI) {
return "(?<i" + targetI + "_" + subtargetI + ">" + (target[0] + "-(" + subtarget + ")") + ")";
}) : "(?<i" + targetI + ">" + target + ")";
}).flat().join("|") + ")" + TRAILING_SLASH_REGEXP + "$";
return [regExp, createUniqueHandler()];
}
// arbitrary rule
// --------------
function createArbitraryHandler() {
var arbitraryHandler = function arbitraryHandler(memory, _ref7) {
var _memory$property;
var property = _ref7.p;
var mem = (_memory$property = memory[property]) != null ? _memory$property : memory[property] = {};
// seen before
if (mem.done) return false;
// never seen
return mem.done = true;
};
return arbitraryHandler;
}
function arbitraryRule() {
return ["\\[(?<p>.+?):.*\\]$", createArbitraryHandler()];
}
function createConflictHandler(targets) {
var overridableMap = {};
Object.entries(targets).forEach(function (_ref8) {
var overridingUtility = _ref8[0],
overridableUtilities = _ref8[1];
return overridableUtilities.split("|").forEach(function (value) {
var _overridableMap$value;
(_overridableMap$value = overridableMap[value]) != null ? _overridableMap$value : overridableMap[value] = [];
overridableMap[value].push(overridingUtility);
});
});
var conflictHandler = function conflictHandler(memory, _ref9) {
var utility = _ref9.u;
// is overridable utility and overriding utility has been seen
var skipClass = Boolean(utility in overridableMap && overridableMap[utility].some(function (u) {
return memory[u];
}));
if (skipClass) return false;
// is overriding utility
if (utility in targets) memory[utility] = true;
// continue evaluating other rules
return "c";
};
return conflictHandler;
}
function conflictRule(targets) {
var overridingUtilities = Object.keys(targets);
var overridableUtilities = Object.values(targets).join("|").split("|");
var matchingClasses = [].concat(overridingUtilities, overridableUtilities);
var utility = "(?<u>" + matchingClasses.join("|") + ")";
var regExp = "" + utility + VALUE_REGEXP + "$";
return [regExp, createConflictHandler(targets)];
}
var DISPLAY = "block|inline-block|inline-flex|inline-table|inline-grid|inline|flex|table-caption|table-cell|table-column-group|table-column|table-footer-group|table-header-group|table-row-group|table-row|table|flow-root|grid|contents|list-item|hidden";
var ISOLATION = "isolate|isolation-auto";
var OBJECT_FIT = "contain|cover|fill|none|scale-down";
var BG_AND_OBJECT_POSITION = "bottom|center|left|left-bottom|left-top|right|right-bottom|right-top|top";
var POSITION = "static|fixed|absolute|relative|sticky";
var VISIBILITY = "visible|invisible|collapse";
var FLEX_DIRECTION = "row|row-reverse|col|col-reverse";
var FLEX_WRAP = "wrap|wrap-reverse|nowrap";
var ALIGN_CONTENT = "normal|center|start|end|between|around|evenly|baseline|stretch";
var FONT_AND_SHADOW_SIZE = "xs|sm|base|md|lg|xl|[\\d.]+xl|inner|none";
var FONT_SMOOTHING = "antialiased|subpixel-antialiased";
var FONT_STYLE = "italic|not-italic";
var FONT_WEIGHT = "thin|extralight|light|normal|medium|semibold|bold|extrabold|black";
var LIST_STYLE_POSITION = "inside|outside";
var TEXT_ALIGN = "left|center|right|justify|start|end";
var TEXT_DECORATION = "underline|overline|line-through|no-underline";
var TEXT_DECORATION_STYLE = "solid|double|dotted|dashed|wavy";
var TEXT_TRANSFORM = "uppercase|lowercase|capitalize|normal-case";
var TEXT_OVERFLOW = "truncate|text-ellipsis|text-clip";
var BG_ATTACHMENT = "fixed|local|scroll";
var BG_REPEAT = "repeat|no-repeat|repeat-x|repeat-y|repeat-round|repeat-space";
var BG_SIZE = "auto|cover|contain";
var BORDER_AND_OUTLINE_STYLE = "solid|dashed|dotted|double|hidden|none";
var FVN_FIGURE = "lining-nums|oldstyle-nums";
var FVN_SPACING = "proportional-nums|tabular-nums";
var FVN_FRACTION = "diagonal-fractions|stacked-fractions";
var SCROLL_BEHAVIOR = "auto|smooth";
var SCROLL_SNAP_ALIGN = "start|end|center|none";
var SCROLL_SNAP_STOP = "normal|always";
var SCROLL_SNAP_TYPE = "none|x|y|both|mandatory|proximity";
// TODO: text-<something>/20 should override line-height (leading)
// TODO: ^ same with opacities and other trailing slash values
// TODO: text-decoration-thickness (conflicts with text-decoration-color and there are custom values: auto and from-font)
function tailwind() {
return [
// these rules are at the top because they need to run before others
conflictRule({
"inset-x": "left|right",
"inset-y": "top|bottom",
inset: "inset-x|inset-y|start|end|left|right|top|bottom",
"sr-only": "not-sr-only",
"not-sr-only": "sr-only",
"normal-nums": "ordinal|slashed-zero|lining-nums|oldstyle-nums|proportional-nums|tabular-nums|diagonal-fractions|stacked-fractons",
ordinal: "normal-nums",
"slashed-zero": "normal-nums",
"lining-nums": "normal-nums",
"oldstyle-nums": "normal-nums",
"proportional-nums": "normal-nums",
"tabular-nums": "normal-nums",
"diagonal-fractions": "normal-nums",
"stacked-fractons": "normal-nums",
"bg-gradient": "bg-none",
"bg-none": "bg-gradient"
}), uniqueRule([DISPLAY, ISOLATION, POSITION, VISIBILITY, FONT_SMOOTHING, FONT_STYLE, FVN_FIGURE, FVN_SPACING, FVN_FRACTION, TEXT_DECORATION, TEXT_TRANSFORM, TEXT_OVERFLOW]), uniqueRule([["content", ALIGN_CONTENT], ["list", LIST_STYLE_POSITION], ["decoration", TEXT_DECORATION_STYLE], ["border", BORDER_AND_OUTLINE_STYLE], ["divide", BORDER_AND_OUTLINE_STYLE], ["outline|outline", BORDER_AND_OUTLINE_STYLE], ["shadow", FONT_AND_SHADOW_SIZE], ["font", FONT_WEIGHT], ["object", OBJECT_FIT, BG_AND_OBJECT_POSITION]]), uniqueRule([["scroll", SCROLL_BEHAVIOR, SCROLL_SNAP_ALIGN, SCROLL_SNAP_STOP, SCROLL_SNAP_TYPE], ["bg", BG_ATTACHMENT, BG_AND_OBJECT_POSITION, BG_REPEAT, BG_SIZE], ["text", TEXT_ALIGN, FONT_AND_SHADOW_SIZE], ["flex", FLEX_DIRECTION, FLEX_WRAP]]), conflictRule({
flex: "basis|grow|shrink"
}),
// -----------------------------------------------------------------
simpleRule("accent|align|animate|aspect|auto-cols|auto-rows|backdrop-blur|backdrop-brightness|backdrop-contrast|backdrop-grayscale|backdrop-hue-rotate|backdrop-invert|backdrop-opacity|backdrop-saturate|backdrop-sepia|basis|bg-blend|bg-clip|bg-origin|bg-none|bg-gradient|bg|blur|border-collapse|border-spacing|bottom|box-decoration|box|break-after|break-before|break-inside|break|brightness|caption|caret|clear|col-end|col-start|columns|col|content|contrast|cursor|decoration|delay|divide-x-reverse|divide-x|divide-y-reverse|divide-y|divide|drop-shadow|duration|ease|end|fill|flex|float|grayscale|grid-cols|grid-flow|grid-rows|grow|hue-rotate|hyphens|h|indent|invert|items|justify-items|justify-self|justify|leading|left|line-clamp|list-image|list|max-h|max-w|min-h|min-w|mix-blend|opacity|order|origin|outline-offset|place-content|place-items|place-self|pointer-events|resize|right|ring-inset|rotate|row-end|row-start|row|saturate|select|self|sepia|shadow|shrink|skew-x|skew-y|space-x-reverse|space-x|space-y-reverse|space-y|start|table|top|touch|tracking|transition|translate-x|translate-y|underline-offset|whitespace|will-change|w|z"), simpleRule("text|outline|ring-offset|ring|from|via|to|stroke|font", {
byType: true
}), cardinalRule("border", {
byType: true
})].concat(cardinalRules("rounded|gap|inset|scale|overflow|overscroll"), cardinalRules("p|m|scroll-m|scroll-p", {
dash: false
}), [arbitraryRule()]);
}
var twMerge = /*#__PURE__*/createMerge( /*#__PURE__*/tailwind());
exports.arbitraryRule = arbitraryRule;
exports.cardinalRule = cardinalRule;
exports.cardinalRules = cardinalRules;
exports.conflictRule = conflictRule;
exports.createMerge = createMerge;
exports.simpleRule = simpleRule;
exports.tailwind = tailwind;
exports.twMerge = twMerge;
exports.uniqueRule = uniqueRule;
//# sourceMappingURL=tw-merge.cjs.development.js.map