Dev Build / cleanup (push) Canceled after 0s
Dev Build / typecheck (push) Canceled after 0s
Dev Build / build (linux-arm64, ubuntu-24.04-arm, linux-arm64) (push) Canceled after 0s
Dev Build / build (linux-x64, ubuntu-latest, linux-x64) (push) Canceled after 0s
Dev Build / build (win-x64, windows-latest, win32-x64) (push) Canceled after 0s
Dev Build / dispatch-dev-docker (push) Skipped
115 lines
4.0 KiB
Plaintext
115 lines
4.0 KiB
Plaintext
import { AnyConfig, ParsedClassName } from './types'
|
|
|
|
export const IMPORTANT_MODIFIER = '!'
|
|
|
|
const MODIFIER_SEPARATOR = ':'
|
|
const EMPTY_MODIFIERS: string[] = []
|
|
|
|
// Pre-allocated result object shape for consistency
|
|
const createResultObject = (
|
|
modifiers: string[],
|
|
hasImportantModifier: boolean,
|
|
baseClassName: string,
|
|
maybePostfixModifierPosition?: number,
|
|
isExternal?: boolean,
|
|
): ParsedClassName => ({
|
|
modifiers,
|
|
hasImportantModifier,
|
|
baseClassName,
|
|
maybePostfixModifierPosition,
|
|
isExternal,
|
|
})
|
|
|
|
export const createParseClassName = (config: AnyConfig) => {
|
|
const { prefix, experimentalParseClassName } = config
|
|
|
|
/**
|
|
* Parse class name into parts.
|
|
*
|
|
* Inspired by `splitAtTopLevelOnly` used in Tailwind CSS
|
|
* @see https://github.com/tailwindlabs/tailwindcss/blob/v3.2.2/src/util/splitAtTopLevelOnly.js
|
|
*/
|
|
let parseClassName = (className: string): ParsedClassName => {
|
|
// Use simple array with push for better performance
|
|
const modifiers: string[] = []
|
|
|
|
let bracketDepth = 0
|
|
let parenDepth = 0
|
|
let modifierStart = 0
|
|
let postfixModifierPosition: number | undefined
|
|
|
|
const len = className.length
|
|
for (let index = 0; index < len; index++) {
|
|
const currentCharacter = className[index]!
|
|
|
|
if (bracketDepth === 0 && parenDepth === 0) {
|
|
if (currentCharacter === MODIFIER_SEPARATOR) {
|
|
modifiers.push(className.slice(modifierStart, index))
|
|
modifierStart = index + 1
|
|
continue
|
|
}
|
|
|
|
if (currentCharacter === '/') {
|
|
postfixModifierPosition = index
|
|
continue
|
|
}
|
|
}
|
|
|
|
if (currentCharacter === '[') bracketDepth++
|
|
else if (currentCharacter === ']') bracketDepth--
|
|
else if (currentCharacter === '(') parenDepth++
|
|
else if (currentCharacter === ')') parenDepth--
|
|
}
|
|
|
|
const baseClassNameWithImportantModifier =
|
|
modifiers.length === 0 ? className : className.slice(modifierStart)
|
|
|
|
// Inline important modifier check
|
|
let baseClassName = baseClassNameWithImportantModifier
|
|
let hasImportantModifier = false
|
|
|
|
if (baseClassNameWithImportantModifier.endsWith(IMPORTANT_MODIFIER)) {
|
|
baseClassName = baseClassNameWithImportantModifier.slice(0, -1)
|
|
hasImportantModifier = true
|
|
} else if (
|
|
/**
|
|
* In Tailwind CSS v3 the important modifier was at the start of the base class name. This is still supported for legacy reasons.
|
|
* @see https://github.com/dcastil/tailwind-merge/issues/513#issuecomment-2614029864
|
|
*/
|
|
baseClassNameWithImportantModifier.startsWith(IMPORTANT_MODIFIER)
|
|
) {
|
|
baseClassName = baseClassNameWithImportantModifier.slice(1)
|
|
hasImportantModifier = true
|
|
}
|
|
|
|
const maybePostfixModifierPosition =
|
|
postfixModifierPosition && postfixModifierPosition > modifierStart
|
|
? postfixModifierPosition - modifierStart
|
|
: undefined
|
|
|
|
return createResultObject(
|
|
modifiers,
|
|
hasImportantModifier,
|
|
baseClassName,
|
|
maybePostfixModifierPosition,
|
|
)
|
|
}
|
|
|
|
if (prefix) {
|
|
const fullPrefix = prefix + MODIFIER_SEPARATOR
|
|
const parseClassNameOriginal = parseClassName
|
|
parseClassName = (className: string) =>
|
|
className.startsWith(fullPrefix)
|
|
? parseClassNameOriginal(className.slice(fullPrefix.length))
|
|
: createResultObject(EMPTY_MODIFIERS, false, className, undefined, true)
|
|
}
|
|
|
|
if (experimentalParseClassName) {
|
|
const parseClassNameOriginal = parseClassName
|
|
parseClassName = (className: string) =>
|
|
experimentalParseClassName({ className, parseClassName: parseClassNameOriginal })
|
|
}
|
|
|
|
return parseClassName
|
|
}
|