From bf307ee3aff5729b9a9e8624edb7199b4a9b51f7 Mon Sep 17 00:00:00 2001 From: XantreDev Date: Fri, 26 Jul 2024 00:12:07 +0200 Subject: [PATCH 1/3] optimized merge-classlist --- src/lib/merge-classlist.ts | 147 +++++++++++++++++-------------------- 1 file changed, 67 insertions(+), 80 deletions(-) diff --git a/src/lib/merge-classlist.ts b/src/lib/merge-classlist.ts index a41734b..68c58ec 100644 --- a/src/lib/merge-classlist.ts +++ b/src/lib/merge-classlist.ts @@ -15,84 +15,71 @@ export const mergeClassList = (classList: string, configUtils: ConfigUtils) => { */ const classGroupsInConflict = new Set() - return ( - classList - .trim() - .split(SPLIT_CLASSES_REGEX) - .map((originalClassName) => { - const { - modifiers, - hasImportantModifier, - baseClassName, - maybePostfixModifierPosition, - } = parseClassName(originalClassName) - - let hasPostfixModifier = Boolean(maybePostfixModifierPosition) - let classGroupId = getClassGroupId( - hasPostfixModifier - ? baseClassName.substring(0, maybePostfixModifierPosition) - : baseClassName, - ) - - if (!classGroupId) { - if (!hasPostfixModifier) { - return { - isTailwindClass: false as const, - originalClassName, - } - } - - classGroupId = getClassGroupId(baseClassName) - - if (!classGroupId) { - return { - isTailwindClass: false as const, - originalClassName, - } - } - - hasPostfixModifier = false - } - - const variantModifier = sortModifiers(modifiers).join(':') - - const modifierId = hasImportantModifier - ? variantModifier + IMPORTANT_MODIFIER - : variantModifier - - return { - isTailwindClass: true as const, - modifierId, - classGroupId, - originalClassName, - hasPostfixModifier, - } - }) - .reverse() - // Last class in conflict wins, so we need to filter conflicting classes in reverse order. - .filter((parsed) => { - if (!parsed.isTailwindClass) { - return true - } - - const { modifierId, classGroupId, hasPostfixModifier } = parsed - - const classId = modifierId + classGroupId - - if (classGroupsInConflict.has(classId)) { - return false - } - - classGroupsInConflict.add(classId) - - getConflictingClassGroupIds(classGroupId, hasPostfixModifier).forEach((group) => - classGroupsInConflict.add(modifierId + group), - ) - - return true - }) - .reverse() - .map((parsed) => parsed.originalClassName) - .join(' ') - ) + let result = '' + + let currentClass = '' + for (let i = classList.length - 1; i >= 0; --i) { + const char = classList[i]! + const isSpace = SPLIT_CLASSES_REGEX.test(char) + if (!isSpace) { + currentClass = char + currentClass + if (i !== 0) continue + } else if (currentClass.length === 0) { + continue + } + + const originalClassName = currentClass + currentClass = '' + + const { modifiers, hasImportantModifier, baseClassName, maybePostfixModifierPosition } = + parseClassName(originalClassName) + + let hasPostfixModifier = Boolean(maybePostfixModifierPosition) + let classGroupId = getClassGroupId( + hasPostfixModifier + ? baseClassName.substring(0, maybePostfixModifierPosition) + : baseClassName, + ) + + if (!classGroupId) { + if (!hasPostfixModifier) { + result = originalClassName + (result.length > 0 ? ' ' + result : result) + continue + } + + classGroupId = getClassGroupId(baseClassName) + + if (!classGroupId) { + result = originalClassName + (result.length > 0 ? ' ' + result : result) + continue + } + + hasPostfixModifier = false + } + + const variantModifier = sortModifiers(modifiers).join(':') + + const modifierId = hasImportantModifier + ? variantModifier + IMPORTANT_MODIFIER + : variantModifier + + const classId = modifierId + classGroupId + + // [TODO]: consider using arrays + if (classGroupsInConflict.has(classId)) { + continue + } + + classGroupsInConflict.add(classId) + + const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier) + for (let i = 0; i < conflictGroups.length; ++i) { + const group = conflictGroups[i]! + classGroupsInConflict.add(modifierId + group) + } + + result = originalClassName + (result.length > 0 ? ' ' + result : result) + } + + return result } From b901f7fad3c548b3571c2cf0c906f2bf1aaabd30 Mon Sep 17 00:00:00 2001 From: XantreDev Date: Fri, 26 Jul 2024 01:56:51 +0200 Subject: [PATCH 2/3] optimization --- src/lib/merge-classlist.ts | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/lib/merge-classlist.ts b/src/lib/merge-classlist.ts index 68c58ec..78708b7 100644 --- a/src/lib/merge-classlist.ts +++ b/src/lib/merge-classlist.ts @@ -1,8 +1,6 @@ import { ConfigUtils } from './config-utils' import { IMPORTANT_MODIFIER, sortModifiers } from './parse-class-name' -const SPLIT_CLASSES_REGEX = /\s+/ - export const mergeClassList = (classList: string, configUtils: ConfigUtils) => { const { parseClassName, getClassGroupId, getConflictingClassGroupIds } = configUtils @@ -13,14 +11,17 @@ export const mergeClassList = (classList: string, configUtils: ConfigUtils) => { * @example 'hover:focus:bg-color' * @example 'md:!pr' */ - const classGroupsInConflict = new Set() + const classGroupsInConflict: string[] = [] + // const classGroupsInConflict = new Set() + // let result: string[] = [] let result = '' let currentClass = '' for (let i = classList.length - 1; i >= 0; --i) { const char = classList[i]! - const isSpace = SPLIT_CLASSES_REGEX.test(char) + // more performant than Regex check - suitable for our case + const isSpace = char === ' ' if (!isSpace) { currentClass = char + currentClass if (i !== 0) continue @@ -44,6 +45,7 @@ export const mergeClassList = (classList: string, configUtils: ConfigUtils) => { if (!classGroupId) { if (!hasPostfixModifier) { result = originalClassName + (result.length > 0 ? ' ' + result : result) + // result.push(originalClassName) continue } @@ -51,6 +53,7 @@ export const mergeClassList = (classList: string, configUtils: ConfigUtils) => { if (!classGroupId) { result = originalClassName + (result.length > 0 ? ' ' + result : result) + // result.push(originalClassName) continue } @@ -65,21 +68,22 @@ export const mergeClassList = (classList: string, configUtils: ConfigUtils) => { const classId = modifierId + classGroupId - // [TODO]: consider using arrays - if (classGroupsInConflict.has(classId)) { + if (classGroupsInConflict.includes(classId)) { continue } - classGroupsInConflict.add(classId) + classGroupsInConflict.push(classId) const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier) for (let i = 0; i < conflictGroups.length; ++i) { const group = conflictGroups[i]! - classGroupsInConflict.add(modifierId + group) + classGroupsInConflict.push(modifierId + group) } + // result.push(originalClassName) result = originalClassName + (result.length > 0 ? ' ' + result : result) } return result + // return result.join(' ') } From 80e6af48c5b184e489a2f50556a512a5b13e497e Mon Sep 17 00:00:00 2001 From: XantreDev Date: Sun, 28 Jul 2024 21:35:24 +0200 Subject: [PATCH 3/3] optimized mergeClassList --- src/lib/merge-classlist.ts | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/lib/merge-classlist.ts b/src/lib/merge-classlist.ts index 78708b7..099f9e9 100644 --- a/src/lib/merge-classlist.ts +++ b/src/lib/merge-classlist.ts @@ -12,25 +12,16 @@ export const mergeClassList = (classList: string, configUtils: ConfigUtils) => { * @example 'md:!pr' */ const classGroupsInConflict: string[] = [] - // const classGroupsInConflict = new Set() - // let result: string[] = [] let result = '' - let currentClass = '' - for (let i = classList.length - 1; i >= 0; --i) { - const char = classList[i]! - // more performant than Regex check - suitable for our case - const isSpace = char === ' ' - if (!isSpace) { - currentClass = char + currentClass - if (i !== 0) continue - } else if (currentClass.length === 0) { - continue + for (let i = classList.length - 1; i >= 0; ) { + while (classList[i] === ' ') { + --i } - - const originalClassName = currentClass - currentClass = '' + const nextI = classList.lastIndexOf(' ', i) + const originalClassName = classList.slice(nextI === -1 ? 0 : nextI + 1, i + 1) + i = nextI const { modifiers, hasImportantModifier, baseClassName, maybePostfixModifierPosition } = parseClassName(originalClassName) @@ -45,7 +36,6 @@ export const mergeClassList = (classList: string, configUtils: ConfigUtils) => { if (!classGroupId) { if (!hasPostfixModifier) { result = originalClassName + (result.length > 0 ? ' ' + result : result) - // result.push(originalClassName) continue } @@ -53,7 +43,6 @@ export const mergeClassList = (classList: string, configUtils: ConfigUtils) => { if (!classGroupId) { result = originalClassName + (result.length > 0 ? ' ' + result : result) - // result.push(originalClassName) continue } @@ -80,10 +69,8 @@ export const mergeClassList = (classList: string, configUtils: ConfigUtils) => { classGroupsInConflict.push(modifierId + group) } - // result.push(originalClassName) result = originalClassName + (result.length > 0 ? ' ' + result : result) } return result - // return result.join(' ') }