Skip to content

Improve performance by removing map/filter/join methods in hot path (failed)#20

Merged
dcastil merged 6 commits intomainfrom
feature/17/improve-performance-by-removing-map-filter-join-in-hot-path
Aug 24, 2021
Merged

Improve performance by removing map/filter/join methods in hot path (failed)#20
dcastil merged 6 commits intomainfrom
feature/17/improve-performance-by-removing-map-filter-join-in-hot-path

Conversation

@dcastil
Copy link
Owner

@dcastil dcastil commented Aug 24, 2021

Closes #17

I created a baseline benchmark which resulted in around 62k operations per second. The optimization of the hot path led to an increase to 63k operations per second. Because the improvements are negligible, I decided not to implement them as it reduces readability of the code.

I used following benchmark

import Benchmark from 'benchmark' // v2.1.4

import { createTailwindMerge } from '../dist/index.js'

const twMerge = createTailwindMerge((getDefaultConfig) => ({
    ...getDefaultConfig(),
    cacheSize: 0,
}))

twMerge('')

new Benchmark.Suite()
    .add('twMerge', () =>
        twMerge(
            'px-2 py-1 bg-red hover:bg-dark-red border text-white non-tailwind-class rounded-t-sm right-4',
            'p-3 bg-[#B91C1C] border-2 text-black other-non-tailwind-class',
            undefined,
            'rounded-lg inset-2',
            null
        )
    )
    .on('cycle', (e) => console.log('  ' + e.target))
    .run()

and following change to mergeClassList()

export function mergeClassList(classList: string, configUtils: ConfigUtils) {
    const { isPrefixValid, getClassGroupId, comparePrefixes, getConflictingClassGroupIds } =
        configUtils

    /**
     * Set of classGroupIds in following format:
     * `{importantModifier}{variantPrefixes}{classGroupId}`
     * @example ':standaloneClasses.1'
     * @example 'hover:focus:dynamicClasses.bg.2'
     * @example '!md:dynamicClasses.bg.0'
     */
    const classGroupsInConflict = new Set<string>()
    const classes = classList.trim().split(SPLIT_CLASSES_REGEX)

    let mergedClassList = ''

    for (let index = classes.length - 1; index >= 0; index -= 1) {
        const originalClassName = classes[index]!

        const hasImportantModifier = originalClassName.startsWith(IMPORTANT_MODIFIER)
        const classNameWithoutImportant = hasImportantModifier
            ? originalClassName.substring(1)
            : originalClassName

        const prefixes = classNameWithoutImportant.split(PREFIX_SEPARATOR_REGEX)
        const className = prefixes.pop()!

        const arePrefixesValid = prefixes.every(isPrefixValid)
        const classGroupId = arePrefixesValid ? getClassGroupId(className) : undefined

        // Not a Tailwind class
        if (!classGroupId) {
            mergedClassList = originalClassName + ' ' + mergedClassList
            continue
        }

        const variantPrefix =
            prefixes.length === 0
                ? ''
                : prefixes.sort(comparePrefixes).concat('').join(PREFIX_SEPARATOR)

        const fullPrefix = hasImportantModifier ? IMPORTANT_MODIFIER + variantPrefix : variantPrefix

        const classId = `${fullPrefix}:${classGroupId}`

        // Conflicting with succeeding class
        if (classGroupsInConflict.has(classId)) {
            continue
        }

        classGroupsInConflict.add(classId)

        getConflictingClassGroupIds(classGroupId).forEach((group) =>
            classGroupsInConflict.add(`${fullPrefix}:${group}`)
        )

        mergedClassList = originalClassName + ' ' + mergedClassList
    }

    return mergedClassList.trim()
}

@github-actions github-actions bot added the feature Is new feature label Aug 24, 2021
@dcastil dcastil changed the title Improve performance by removing map/filter/join methods in hot path Improve performance by removing map/filter/join methods in hot path (failed) Aug 24, 2021
@dcastil dcastil added the needs changelog edit Needs edit in changelog before release label Aug 24, 2021
@dcastil
Copy link
Owner Author

dcastil commented Aug 24, 2021

Changelog edit:

  • twMerge() allows passing null as argument

@dcastil dcastil marked this pull request as ready for review August 24, 2021 20:54
@dcastil dcastil merged commit 89ce322 into main Aug 24, 2021
@dcastil dcastil deleted the feature/17/improve-performance-by-removing-map-filter-join-in-hot-path branch August 24, 2021 20:55
@dcastil
Copy link
Owner Author

dcastil commented Aug 25, 2021

Also tested in Safari and Chrome. Unoptimized and optimized runs both have similar performance. → https://jsben.ch/EbZBz

Boilerplate block

const twMerge1 = () => {
  function e(){return(e=Object.assign||function(e){for(var r=1;r<arguments.length;r++){var t=arguments[r];for(var o in t)Object.prototype.hasOwnProperty.call(t,o)&&(e[o]=t[o])}return e}).apply(this,arguments)}function r(e){const r=function(e){const r={nextPart:{},validators:[]};return Object.entries(e.classGroups).forEach(([e,t])=>{o(t,r,e)}),r}(e);return{getClassGroupId:function(e){const o=e.split("-");return""===o[0]&&1!==o.length&&o.shift(),t(o,r)},getConflictingClassGroupIds:function(r){return e.conflictingClassGroups[r]||[]}}}function t(e,r){var o;if(0===e.length)return r.classGroupId;const n=r.nextPart[e[0]],i=n?t(e.slice(1),n):void 0;if(i)return i;if(0===r.validators.length)return;const a=e.join("-");return null==(o=r.validators.find(({validator:e})=>e(a)))?void 0:o.classGroupId}function o(e,r,t){e.forEach(e=>{"string"==typeof e?(""===e?r:n(r,e)).classGroupId=t:"function"==typeof e?r.validators.push({validator:e,classGroupId:t}):Object.entries(e).forEach(([e,i])=>{o(i,n(r,e),t)})})}function n(e,r){let t=e;return r.split("-").forEach(e=>{void 0===t.nextPart[e]&&(t.nextPart[e]={nextPart:{},validators:[]}),t=t.nextPart[e]}),t}function i(e){const r=Object.fromEntries(e.prefixes.flatMap(e=>a("",e)).map((e,r)=>[e,r]));return{isPrefixValid:function(e){return void 0!==r[e]},comparePrefixes:function(e,t){return r[e]-r[t]}}}function a(e,r){return"string"==typeof r?e.concat(r):Object.entries(r).flatMap(([r,t])=>t.flatMap(t=>a(`${e}${r}-`,t)))}const l=/^\[(.+)\]$/,s=/^\d+\/\d+$/,d=new Set(["px","full","screen"]),c=/\d+(%|px|em|rem|vh|vw|pt|pc|in|cm|mm|cap|ch|ex|lh|rlh|vi|vb|vmin|vmax)/;function u(e){return p(e)||!Number.isNaN(Number(e))||d.has(e)||s.test(e)}function p(e){var r;const t=null==(r=l.exec(e))?void 0:r[1];return!!t&&(t.startsWith("length:")||c.test(t))}function f(e){var r;const t=null==(r=l.exec(e))?void 0:r[1];return Number.isInteger(Number(t||e))}function b(e){return l.test(e)}const g=["sm","md","lg","xl","2xl"],m=["3xl","4xl","5xl","6xl","7xl"],v=["auto","contain","none"],x=["auto","hidden","visible","scroll"],h=[u],y=["auto",u],w=["",u],k=[f],j=["auto",f],z=[function(){return!0}],C=["bottom","center","left","left-bottom","left-top","right","right-bottom","right-top","top"],G=["none","",...g,"3xl","full",p],O=["solid","dashed","dotted","double","none"],I=[{blend:["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"]}],P=["start","end","center","between","around","evenly"],N=["","0"],$=["first","last","only","odd","even","first-of-type","last-of-type","only-of-type","visited","target","default","checked","indeterminate","placeholder-shown","autofill","required","valid","invalid","in-range","out-of-range","read-only","empty","focus-within","hover","focus","focus-visible","active","disabled"];function E(){return{cacheSize:500,prefixes:[...g,"dark","motion-safe","motion-reduce","before","after","first-letter","first-line","selection","marker",...$,{group:$,peer:$}],classGroups:{container:["container"],decoration:[{decoration:["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],float:[{float:["right","left","none"]}],clear:[{clear:["left","right","both","none"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:C}],overflow:[{overflow:x}],"overflow-x":[{"overflow-x":x}],"overflow-y":[{"overflow-y":x}],overscroll:[{overscroll:v}],"overscroll-x":[{"overscroll-x":v}],"overscroll-y":[{"overscroll-y":v}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:y}],"inset-x":[{"inset-x":y}],"inset-y":[{"inset-y":y}],top:[{top:y}],right:[{right:y}],bottom:[{bottom:y}],left:[{left:y}],visibility:["visible","invisible"],z:[{z:h}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["wrap","wrap-reverse","nowrap"]}],flex:[{flex:["1","auto","initial","none",b]}],"flex-grow":[{"flex-grow":["",f]}],"flex-shrink":[{"flex-shrink":["",f]}],order:[{order:["first","last","none",f]}],"grid-cols":[{"grid-cols":z}],"col-start-end":[{col:["auto",{span:k}]}],"col-start":[{"col-start":j}],"col-end":[{"col-end":j}],"grid-rows":[{"grid-rows":z}],"row-start-end":[{row:["auto",{span:k}]}],"row-start":[{"row-start":j}],"row-end":[{"row-end":j}],"grid-flow":[{"grid-flow":["row","col","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":["auto","min","max","fr"]}],"auto-rows":[{"auto-rows":["auto","min","max","fr"]}],gap:[{gap:h}],"gap-x":[{"gap-x":h}],"gap-y":[{"gap-y":h}],"justify-content":[{justify:P}],"justify-items":[{"justify-items":["start","end","center","stretch"]}],"justify-self":[{"justify-self":["auto","start","end","center","stretch"]}],"align-content":[{content:P}],"align-items":[{items:["start","end","center","baseline","stretch"]}],"align-self":[{self:["auto","start","end","center","stretch","baseline"]}],"place-content":[{"place-content":[...P,"stretch"]}],"place-items":[{"place-items":["start","end","center","stretch"]}],"place-self":[{"place-self":["auto","start","end","center","stretch"]}],p:[{p:h}],px:[{px:h}],py:[{py:h}],pt:[{pt:h}],pr:[{pr:h}],pb:[{pb:h}],pl:[{pl:h}],m:[{m:y}],mx:[{mx:y}],my:[{my:y}],mt:[{mt:y}],mr:[{mr:y}],mb:[{mb:y}],ml:[{ml:y}],"space-x":[{"space-x":h}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":h}],"space-y-reverse":["space-y-reverse"],w:[{w:["auto","min","max",u]}],"min-w":[{"min-w":["full","min","max",u]}],"max-w":[{"max-w":["0","none",...g,...m,"full","min","max","prose",{screen:g}]}],h:[{h:y}],"min-h":[{"min-h":["full","screen",u]}],"max-h":[{"max-h":h}],"font-family":[{font:z}],"font-size":[{text:["xs",...g,"base",...m,"8xl","9xl",p]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractons"],tracking:[{tracking:["tighter","tight","normal","wide","wider","widest",p]}],leading:[{leading:["none","tight","snug","normal","relaxed","loose",u]}],"list-style-type":[{list:["none","disc","decimal"]}],"list-style-position":[{list:["inside","outside"]}],"placeholder-color":[{placeholder:z}],"placeholder-opacity":[{"placeholder-opacity":k}],"text-alignment":[{text:["left","center","right","justify"]}],"text-color":[{text:z}],"text-opacity":[{"text-opacity":k}],"text-decoration":["underline","line-through","no-underline"],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","overflow-ellipsis","overflow-clip"],"vertival-alignment":[{align:["baseline","top","middle","bottom","text-top","text-bottom"]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap"]}],break:[{break:["normal","words","all"]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-opacity":[{"bg-opacity":k}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:C}],"bg-repeeat":[{bg:["no-repeat",{repeat:["","x","y","round","space"]}]}],"bg-size":[{bg:["auto","cover","contain"]}],"bg-image":[{bg:["none",{"gradient-to":["t","tr","r","br","b","bl","l","tl"]}]}],"bg-blend":[{bg:I}],"bg-color":[{bg:z}],"gradient-from":[{from:z}],"gradient-via":[{via:z}],"gradient-to":[{to:z}],rounded:[{rounded:G}],"rounded-t":[{"rounded-t":G}],"rounded-r":[{"rounded-r":G}],"rounded-b":[{"rounded-b":G}],"rounded-l":[{"rounded-l":G}],"rounded-tl":[{"rounded-tl":G}],"rounded-tr":[{"rounded-tr":G}],"rounded-br":[{"rounded-br":G}],"rounded-bl":[{"rounded-bl":G}],"border-w":[{border:w}],"border-w-t":[{"border-t":w}],"border-w-r":[{"border-r":w}],"border-w-b":[{"border-b":w}],"border-w-l":[{"border-l":w}],"border-opacity":[{"border-opacity":k}],"border-style":[{border:O}],"divide-x":[{"divide-x":w}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":w}],"divide-y-reverse":["divide-y-reverse"],"divide-opacity":[{"divide-opacity":k}],"divide-style":[{divide:O}],"border-color":[{border:z}],"border-color-t":[{"border-t":z}],"border-color-r":[{"border-r":z}],"border-color-b":[{"border-b":z}],"border-color-l":[{"border-l":z}],"divide-color":[{divide:z}],"ring-w":[{ring:w}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:z}],"ring-opacity":[{"ring-opacity":k}],"ring-offset-w":[{"ring-offset":h}],"ring-offset-color":[{"ring-offset":z}],shadow:[{shadow:["",...g,"inner","none"]}],opacity:[{opacity:k}],"mix-blend":[{"mix-blend":I}],filter:[{filter:["","none"]}],blur:[{blur:["none","",...g,"3xl",p]}],brightness:[{brightness:k}],contrast:[{contrast:k}],"drop-shadow":[{"drop-shadow":["",...g,"none"]}],grayscale:[{grayscale:N}],"hue-rotate":[{"hue-rotate":k}],invert:[{invert:N}],saturate:[{saturate:k}],sepia:[{sepia:N}],"backdrop-filter":[{"backdrop-filter":["","none"]}],"backdrop-blur":[{"backdrop-blur":["none","",...g,"3xl"]}],"backdrop-brightness":[{"backdrop-brightness":k}],"backdrop-contrast":[{"backdrop-contrast":k}],"backdrop-grayscale":[{"backdrop-grayscale":N}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":k}],"backdrop-invert":[{"backdrop-invert":N}],"backdrop-opacity":[{"backdrop-opacity":k}],"backdrop-saturate":[{"backdrop-saturate":k}],"backdrop-sepia":[{"backdrop-sepia":N}],"border-collapse":[{border:["collapse","separate"]}],"table-layout":[{table:["auto","fixed"]}],transition:[{transition:["none","all","","colors","opacity","shadow","transform"]}],duration:[{duration:k}],ease:[{ease:["linear","in","out","in-out"]}],delay:[{delay:k}],animate:[{animate:["none","spin","ping","pulse","bounce"]}],transform:[{transform:["","gpu","none"]}],"transform-origin":[{origin:["center","top","top-right","right","bottom-right","bottom","bottom-left","left","top-left"]}],scale:[{scale:k}],"scale-x":[{"scale-x":k}],"scale-y":[{"scale-y":k}],rotate:[{rotate:k}],"translate-x":[{"translate-x":h}],"translate-y":[{"translate-y":h}],"skew-x":[{"skew-x":k}],"skew-y":[{"skew-y":k}],appearance:["appearance-none"],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed",b]}],outline:[{outline:["none","white","black"]}],"pointer-events":[{"pointer-events":["none","auto"]}],resize:[{resize:["none","y","x",""]}],select:[{select:["none","text","all","auto"]}],fill:["fill-current"],stroke:[{stroke:["current"]}],"stroke-w":[{stroke:h}],sr:["sr-only","not-sr-only"],content:[{content:[b]}],"caret-color":[{caret:z}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["flex-grow","flex-shrink"],"col-start-end":["col-start","col-end"],"row-start-end":["row-start","row-end"],gap:["gap-x","gap-y"],p:["px","py","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],rounded:["rounded-t","rounded-r","rounded-b","rounded-l","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-w":["border-w-t","border-w-r","border-w-b","border-w-l"],"border-color":["border-color-t","border-color-r","border-color-b","border-color-l"]}}}const S=/\s+/,M=/:(?![^[]*\])/;function T(t){let o,n,a,l=function(d){return c=t(E),o=e({cache:(u=c.cacheSize,u>=1?function(e){if(!e)throw Error("hashlru must have a max value, of type number, greater than 0");var r=0,t=Object.create(null),o=Object.create(null);function n(n,i){t[n]=i,++r>=e&&(r=0,o=t,t=Object.create(null))}return{has:function(e){return void 0!==t[e]||void 0!==o[e]},remove:function(e){void 0!==t[e]&&(t[e]=void 0),void 0!==o[e]&&(o[e]=void 0)},get:function(e){var r=t[e];return void 0!==r?r:void 0!==(r=o[e])?(n(e,r),r):void 0},set:function(e,r){void 0!==t[e]?t[e]=r:n(e,r)},clear:function(){t=Object.create(null),o=Object.create(null)}}}(u):{get:()=>{},set:()=>{}})},i(c),r(c)),n=o.cache.get,a=o.cache.set,l=s,s(d);var c,u};function s(e){const r=n(e);if(r)return r;const t=function(e,r){const{isPrefixValid:t,getClassGroupId:o,comparePrefixes:n,getConflictingClassGroupIds:i}=r,a=new Set;return e.trim().split(S).map(e=>{const r=e.startsWith("!"),i=(r?e.substring(1):e).split(M),a=i.pop(),l=i.every(t)?o(a):void 0;if(!l)return{isTailwindClass:!1,originalClassName:e};const s=0===i.length?"":i.sort(n).concat("").join(":");return{isTailwindClass:!0,prefix:r?"!"+s:s,classGroupId:l,originalClassName:e}}).reverse().filter(e=>{if(!e.isTailwindClass)return!0;const{prefix:r,classGroupId:t}=e,o=`${r}:${t}`;return!a.has(o)&&(a.add(o),i(t).forEach(e=>a.add(`${r}:${e}`)),!0)}).reverse().map(e=>e.originalClassName).join(" ")}(e,o);return a(e,t),t}return function(...e){return l(e.join(" "))}}const V=T(e=>e());
  const createTailwindMerge = T;
	const twMerge = createTailwindMerge(c => {
		const config = c();
  	config.cacheSize = 0;
  	return config;
	});
	twMerge('');
  return twMerge;
};

const twMerge2 = () => {
  function e(){return(e=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var o in r)Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o])}return e}).apply(this,arguments)}function t(e){const t=function(e){const t={nextPart:{},validators:[]};return Object.entries(e.classGroups).forEach(([e,r])=>{o(r,t,e)}),t}(e);return{getClassGroupId:function(e){const o=e.split("-");return""===o[0]&&1!==o.length&&o.shift(),r(o,t)},getConflictingClassGroupIds:function(t){return e.conflictingClassGroups[t]||[]}}}function r(e,t){var o;if(0===e.length)return t.classGroupId;const n=t.nextPart[e[0]],i=n?r(e.slice(1),n):void 0;if(i)return i;if(0===t.validators.length)return;const l=e.join("-");return null==(o=t.validators.find(({validator:e})=>e(l)))?void 0:o.classGroupId}function o(e,t,r){e.forEach(e=>{"string"==typeof e?(""===e?t:n(t,e)).classGroupId=r:"function"==typeof e?t.validators.push({validator:e,classGroupId:r}):Object.entries(e).forEach(([e,i])=>{o(i,n(t,e),r)})})}function n(e,t){let r=e;return t.split("-").forEach(e=>{void 0===r.nextPart[e]&&(r.nextPart[e]={nextPart:{},validators:[]}),r=r.nextPart[e]}),r}function i(e){const t=Object.fromEntries(e.prefixes.flatMap(e=>l("",e)).map((e,t)=>[e,t]));return{isPrefixValid:function(e){return void 0!==t[e]},comparePrefixes:function(e,r){return t[e]-t[r]}}}function l(e,t){return"string"==typeof t?e.concat(t):Object.entries(t).flatMap(([t,r])=>r.flatMap(r=>l(`${e}${t}-`,r)))}const a=/^\[(.+)\]$/,s=/^\d+\/\d+$/,d=new Set(["px","full","screen"]),c=/\d+(%|px|em|rem|vh|vw|pt|pc|in|cm|mm|cap|ch|ex|lh|rlh|vi|vb|vmin|vmax)/;function u(e){return f(e)||!Number.isNaN(Number(e))||d.has(e)||s.test(e)}function f(e){var t;const r=null==(t=a.exec(e))?void 0:t[1];return!!r&&(r.startsWith("length:")||c.test(r))}function p(e){var t;const r=null==(t=a.exec(e))?void 0:t[1];return Number.isInteger(Number(r||e))}function b(e){return a.test(e)}const g=["sm","md","lg","xl","2xl"],m=["3xl","4xl","5xl","6xl","7xl"],v=["auto","contain","none"],x=["auto","hidden","visible","scroll"],h=[u],y=["auto",u],w=["",u],k=[p],j=["auto",p],z=[function(){return!0}],G=["bottom","center","left","left-bottom","left-top","right","right-bottom","right-top","top"],O=["none","",...g,"3xl","full",f],P=["solid","dashed","dotted","double","none"],I=[{blend:["normal","multiply","screen","overlay","darken","lighten","color-dodge","color-burn","hard-light","soft-light","difference","exclusion","hue","saturation","color","luminosity"]}],C=["start","end","center","between","around","evenly"],$=["","0"],E=["first","last","only","odd","even","first-of-type","last-of-type","only-of-type","visited","target","default","checked","indeterminate","placeholder-shown","autofill","required","valid","invalid","in-range","out-of-range","read-only","empty","focus-within","hover","focus","focus-visible","active","disabled"];function N(){return{cacheSize:500,prefixes:[...g,"dark","motion-safe","motion-reduce","before","after","first-letter","first-line","selection","marker",...E,{group:E,peer:E}],classGroups:{container:["container"],decoration:[{decoration:["slice","clone"]}],box:[{box:["border","content"]}],display:["block","inline-block","inline","flex","inline-flex","table","inline-table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row-group","table-row","flow-root","grid","inline-grid","contents","list-item","hidden"],float:[{float:["right","left","none"]}],clear:[{clear:["left","right","both","none"]}],isolation:["isolate","isolation-auto"],"object-fit":[{object:["contain","cover","fill","none","scale-down"]}],"object-position":[{object:G}],overflow:[{overflow:x}],"overflow-x":[{"overflow-x":x}],"overflow-y":[{"overflow-y":x}],overscroll:[{overscroll:v}],"overscroll-x":[{"overscroll-x":v}],"overscroll-y":[{"overscroll-y":v}],position:["static","fixed","absolute","relative","sticky"],inset:[{inset:y}],"inset-x":[{"inset-x":y}],"inset-y":[{"inset-y":y}],top:[{top:y}],right:[{right:y}],bottom:[{bottom:y}],left:[{left:y}],visibility:["visible","invisible"],z:[{z:h}],"flex-direction":[{flex:["row","row-reverse","col","col-reverse"]}],"flex-wrap":[{flex:["wrap","wrap-reverse","nowrap"]}],flex:[{flex:["1","auto","initial","none",b]}],"flex-grow":[{"flex-grow":["",p]}],"flex-shrink":[{"flex-shrink":["",p]}],order:[{order:["first","last","none",p]}],"grid-cols":[{"grid-cols":z}],"col-start-end":[{col:["auto",{span:k}]}],"col-start":[{"col-start":j}],"col-end":[{"col-end":j}],"grid-rows":[{"grid-rows":z}],"row-start-end":[{row:["auto",{span:k}]}],"row-start":[{"row-start":j}],"row-end":[{"row-end":j}],"grid-flow":[{"grid-flow":["row","col","row-dense","col-dense"]}],"auto-cols":[{"auto-cols":["auto","min","max","fr"]}],"auto-rows":[{"auto-rows":["auto","min","max","fr"]}],gap:[{gap:h}],"gap-x":[{"gap-x":h}],"gap-y":[{"gap-y":h}],"justify-content":[{justify:C}],"justify-items":[{"justify-items":["start","end","center","stretch"]}],"justify-self":[{"justify-self":["auto","start","end","center","stretch"]}],"align-content":[{content:C}],"align-items":[{items:["start","end","center","baseline","stretch"]}],"align-self":[{self:["auto","start","end","center","stretch","baseline"]}],"place-content":[{"place-content":[...C,"stretch"]}],"place-items":[{"place-items":["start","end","center","stretch"]}],"place-self":[{"place-self":["auto","start","end","center","stretch"]}],p:[{p:h}],px:[{px:h}],py:[{py:h}],pt:[{pt:h}],pr:[{pr:h}],pb:[{pb:h}],pl:[{pl:h}],m:[{m:y}],mx:[{mx:y}],my:[{my:y}],mt:[{mt:y}],mr:[{mr:y}],mb:[{mb:y}],ml:[{ml:y}],"space-x":[{"space-x":h}],"space-x-reverse":["space-x-reverse"],"space-y":[{"space-y":h}],"space-y-reverse":["space-y-reverse"],w:[{w:["auto","min","max",u]}],"min-w":[{"min-w":["full","min","max",u]}],"max-w":[{"max-w":["0","none",...g,...m,"full","min","max","prose",{screen:g}]}],h:[{h:y}],"min-h":[{"min-h":["full","screen",u]}],"max-h":[{"max-h":h}],"font-family":[{font:z}],"font-size":[{text:["xs",...g,"base",...m,"8xl","9xl",f]}],"font-smoothing":["antialiased","subpixel-antialiased"],"font-style":["italic","not-italic"],"font-weight":[{font:["thin","extralight","light","normal","medium","semibold","bold","extrabold","black"]}],"fvn-normal":["normal-nums"],"fvn-ordinal":["ordinal"],"fvn-slashed-zero":["slashed-zero"],"fvn-figure":["lining-nums","oldstyle-nums"],"fvn-spacing":["proportional-nums","tabular-nums"],"fvn-fraction":["diagonal-fractions","stacked-fractons"],tracking:[{tracking:["tighter","tight","normal","wide","wider","widest",f]}],leading:[{leading:["none","tight","snug","normal","relaxed","loose",u]}],"list-style-type":[{list:["none","disc","decimal"]}],"list-style-position":[{list:["inside","outside"]}],"placeholder-color":[{placeholder:z}],"placeholder-opacity":[{"placeholder-opacity":k}],"text-alignment":[{text:["left","center","right","justify"]}],"text-color":[{text:z}],"text-opacity":[{"text-opacity":k}],"text-decoration":["underline","line-through","no-underline"],"text-transform":["uppercase","lowercase","capitalize","normal-case"],"text-overflow":["truncate","overflow-ellipsis","overflow-clip"],"vertival-alignment":[{align:["baseline","top","middle","bottom","text-top","text-bottom"]}],whitespace:[{whitespace:["normal","nowrap","pre","pre-line","pre-wrap"]}],break:[{break:["normal","words","all"]}],"bg-attachment":[{bg:["fixed","local","scroll"]}],"bg-clip":[{"bg-clip":["border","padding","content","text"]}],"bg-opacity":[{"bg-opacity":k}],"bg-origin":[{"bg-origin":["border","padding","content"]}],"bg-position":[{bg:G}],"bg-repeeat":[{bg:["no-repeat",{repeat:["","x","y","round","space"]}]}],"bg-size":[{bg:["auto","cover","contain"]}],"bg-image":[{bg:["none",{"gradient-to":["t","tr","r","br","b","bl","l","tl"]}]}],"bg-blend":[{bg:I}],"bg-color":[{bg:z}],"gradient-from":[{from:z}],"gradient-via":[{via:z}],"gradient-to":[{to:z}],rounded:[{rounded:O}],"rounded-t":[{"rounded-t":O}],"rounded-r":[{"rounded-r":O}],"rounded-b":[{"rounded-b":O}],"rounded-l":[{"rounded-l":O}],"rounded-tl":[{"rounded-tl":O}],"rounded-tr":[{"rounded-tr":O}],"rounded-br":[{"rounded-br":O}],"rounded-bl":[{"rounded-bl":O}],"border-w":[{border:w}],"border-w-t":[{"border-t":w}],"border-w-r":[{"border-r":w}],"border-w-b":[{"border-b":w}],"border-w-l":[{"border-l":w}],"border-opacity":[{"border-opacity":k}],"border-style":[{border:P}],"divide-x":[{"divide-x":w}],"divide-x-reverse":["divide-x-reverse"],"divide-y":[{"divide-y":w}],"divide-y-reverse":["divide-y-reverse"],"divide-opacity":[{"divide-opacity":k}],"divide-style":[{divide:P}],"border-color":[{border:z}],"border-color-t":[{"border-t":z}],"border-color-r":[{"border-r":z}],"border-color-b":[{"border-b":z}],"border-color-l":[{"border-l":z}],"divide-color":[{divide:z}],"ring-w":[{ring:w}],"ring-w-inset":["ring-inset"],"ring-color":[{ring:z}],"ring-opacity":[{"ring-opacity":k}],"ring-offset-w":[{"ring-offset":h}],"ring-offset-color":[{"ring-offset":z}],shadow:[{shadow:["",...g,"inner","none"]}],opacity:[{opacity:k}],"mix-blend":[{"mix-blend":I}],filter:[{filter:["","none"]}],blur:[{blur:["none","",...g,"3xl",f]}],brightness:[{brightness:k}],contrast:[{contrast:k}],"drop-shadow":[{"drop-shadow":["",...g,"none"]}],grayscale:[{grayscale:$}],"hue-rotate":[{"hue-rotate":k}],invert:[{invert:$}],saturate:[{saturate:k}],sepia:[{sepia:$}],"backdrop-filter":[{"backdrop-filter":["","none"]}],"backdrop-blur":[{"backdrop-blur":["none","",...g,"3xl"]}],"backdrop-brightness":[{"backdrop-brightness":k}],"backdrop-contrast":[{"backdrop-contrast":k}],"backdrop-grayscale":[{"backdrop-grayscale":$}],"backdrop-hue-rotate":[{"backdrop-hue-rotate":k}],"backdrop-invert":[{"backdrop-invert":$}],"backdrop-opacity":[{"backdrop-opacity":k}],"backdrop-saturate":[{"backdrop-saturate":k}],"backdrop-sepia":[{"backdrop-sepia":$}],"border-collapse":[{border:["collapse","separate"]}],"table-layout":[{table:["auto","fixed"]}],transition:[{transition:["none","all","","colors","opacity","shadow","transform"]}],duration:[{duration:k}],ease:[{ease:["linear","in","out","in-out"]}],delay:[{delay:k}],animate:[{animate:["none","spin","ping","pulse","bounce"]}],transform:[{transform:["","gpu","none"]}],"transform-origin":[{origin:["center","top","top-right","right","bottom-right","bottom","bottom-left","left","top-left"]}],scale:[{scale:k}],"scale-x":[{"scale-x":k}],"scale-y":[{"scale-y":k}],rotate:[{rotate:k}],"translate-x":[{"translate-x":h}],"translate-y":[{"translate-y":h}],"skew-x":[{"skew-x":k}],"skew-y":[{"skew-y":k}],appearance:["appearance-none"],cursor:[{cursor:["auto","default","pointer","wait","text","move","help","not-allowed",b]}],outline:[{outline:["none","white","black"]}],"pointer-events":[{"pointer-events":["none","auto"]}],resize:[{resize:["none","y","x",""]}],select:[{select:["none","text","all","auto"]}],fill:["fill-current"],stroke:[{stroke:["current"]}],"stroke-w":[{stroke:h}],sr:["sr-only","not-sr-only"],content:[{content:[b]}],"caret-color":[{caret:z}]},conflictingClassGroups:{overflow:["overflow-x","overflow-y"],overscroll:["overscroll-x","overscroll-y"],inset:["inset-x","inset-y","top","right","bottom","left"],"inset-x":["right","left"],"inset-y":["top","bottom"],flex:["flex-grow","flex-shrink"],"col-start-end":["col-start","col-end"],"row-start-end":["row-start","row-end"],gap:["gap-x","gap-y"],p:["px","py","pt","pr","pb","pl"],px:["pr","pl"],py:["pt","pb"],m:["mx","my","mt","mr","mb","ml"],mx:["mr","ml"],my:["mt","mb"],"font-size":["leading"],"fvn-normal":["fvn-ordinal","fvn-slashed-zero","fvn-figure","fvn-spacing","fvn-fraction"],"fvn-ordinal":["fvn-normal"],"fvn-slashed-zero":["fvn-normal"],"fvn-figure":["fvn-normal"],"fvn-spacing":["fvn-normal"],"fvn-fraction":["fvn-normal"],rounded:["rounded-t","rounded-r","rounded-b","rounded-l","rounded-tl","rounded-tr","rounded-br","rounded-bl"],"rounded-t":["rounded-tl","rounded-tr"],"rounded-r":["rounded-tr","rounded-br"],"rounded-b":["rounded-br","rounded-bl"],"rounded-l":["rounded-tl","rounded-bl"],"border-w":["border-w-t","border-w-r","border-w-b","border-w-l"],"border-color":["border-color-t","border-color-r","border-color-b","border-color-l"]}}}const S=/\s+/,M=/:(?![^[]*\])/;function V(r){let o,n,l,a=function(d){return c=r(N),o=e({cache:(u=c.cacheSize,u>=1?function(e){if(!e)throw Error("hashlru must have a max value, of type number, greater than 0");var t=0,r=Object.create(null),o=Object.create(null);function n(n,i){r[n]=i,++t>=e&&(t=0,o=r,r=Object.create(null))}return{has:function(e){return void 0!==r[e]||void 0!==o[e]},remove:function(e){void 0!==r[e]&&(r[e]=void 0),void 0!==o[e]&&(o[e]=void 0)},get:function(e){var t=r[e];return void 0!==t?t:void 0!==(t=o[e])?(n(e,t),t):void 0},set:function(e,t){void 0!==r[e]?r[e]=t:n(e,t)},clear:function(){r=Object.create(null),o=Object.create(null)}}}(u):{get:()=>{},set:()=>{}})},i(c),t(c)),n=o.cache.get,l=o.cache.set,a=s,s(d);var c,u};function s(e){const t=n(e);if(t)return t;const r=function(e,t){const{isPrefixValid:r,getClassGroupId:o,comparePrefixes:n,getConflictingClassGroupIds:i}=t,l=new Set,a=e.trim().split(S);let s="";for(let e=a.length-1;e>=0;e-=1){const t=a[e],d=t.startsWith("!"),c=(d?t.substring(1):t).split(M),u=c.pop(),f=c.every(r)?o(u):void 0;if(!f){s=t+" "+s;continue}const p=0===c.length?"":c.sort(n).concat("").join(":"),b=d?"!"+p:p,g=`${b}:${f}`;l.has(g)||(l.add(g),i(f).forEach(e=>l.add(`${b}:${e}`)),s=t+" "+s)}return s.trim()}(e,o);return l(e,r),r}return function(...e){return a(e.join(" "))}}const W=V(e=>e());
  const createTailwindMerge = V;
	const twMerge = createTailwindMerge(c => {
		const config = c();
  	config.cacheSize = 0;
  	return config;
	});
	twMerge('');
  return twMerge;
};

Unptimized

twMerge1(
	'px-2 py-1 bg-red hover:bg-dark-red border text-white non-tailwind-class rounded-t-sm right-4',
	'p-3 bg-[#B91C1C] border-2 text-black other-non-tailwind-class',
	undefined,
	'rounded-lg inset-2',
	null
);

Optimized

twMerge2(
	'px-2 py-1 bg-red hover:bg-dark-red border text-white non-tailwind-class rounded-t-sm right-4',
	'p-3 bg-[#B91C1C] border-2 text-black other-non-tailwind-class',
	undefined,
	'rounded-lg inset-2',
	null
);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

context-v0 Related to tailwind-merge v0 feature Is new feature needs changelog edit Needs edit in changelog before release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move from array map/filter/join to for loops and string concatenation in hot path to increase performance

1 participant