Socket
Socket
Sign inDemoInstall

tailwind-merge

Package Overview
Dependencies
Maintainers
1
Versions
276
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

tailwind-merge - npm Package Compare versions

Comparing version 2.4.0-dev.4e3c37aebdee0d2619aed25af271f7f1482ce5c2 to 2.4.0-dev.f3b5d4a2acc36bef2a58f30c9808f3874fafc76d

2

package.json
{
"name": "tailwind-merge",
"version": "2.4.0-dev.4e3c37aebdee0d2619aed25af271f7f1482ce5c2",
"version": "2.4.0-dev.f3b5d4a2acc36bef2a58f30c9808f3874fafc76d",
"description": "Merge Tailwind CSS classes without style conflicts",

@@ -5,0 +5,0 @@ "keywords": [

@@ -6,3 +6,3 @@ <!-- This file is autogenerated. If you want to change this content, please do the changes in `./docs/README.md` instead. -->

<a href="https://github.com/dcastil/tailwind-merge">
<img src="https://github.com/dcastil/tailwind-merge/raw/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/assets/logo.svg" alt="tailwind-merge" height="150px" />
<img src="https://github.com/dcastil/tailwind-merge/raw/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/assets/logo.svg" alt="tailwind-merge" height="150px" />
</a>

@@ -29,12 +29,12 @@ </div>

- [What is it for](https://github.com/dcastil/tailwind-merge/tree/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/docs/what-is-it-for.md)
- [When and how to use it](https://github.com/dcastil/tailwind-merge/tree/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/docs/when-and-how-to-use-it.md)
- [Features](https://github.com/dcastil/tailwind-merge/tree/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/docs/features.md)
- [Limitations](https://github.com/dcastil/tailwind-merge/tree/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/docs/limitations.md)
- [Configuration](https://github.com/dcastil/tailwind-merge/tree/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/docs/configuration.md)
- [Recipes](https://github.com/dcastil/tailwind-merge/tree/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/docs/recipes.md)
- [API reference](https://github.com/dcastil/tailwind-merge/tree/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/docs/api-reference.md)
- [Writing plugins](https://github.com/dcastil/tailwind-merge/tree/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/docs/writing-plugins.md)
- [Versioning](https://github.com/dcastil/tailwind-merge/tree/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/docs/versioning.md)
- [Contributing](https://github.com/dcastil/tailwind-merge/tree/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/docs/contributing.md)
- [Similar packages](https://github.com/dcastil/tailwind-merge/tree/4e3c37aebdee0d2619aed25af271f7f1482ce5c2/docs/similar-packages.md)
- [What is it for](https://github.com/dcastil/tailwind-merge/tree/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/docs/what-is-it-for.md)
- [When and how to use it](https://github.com/dcastil/tailwind-merge/tree/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/docs/when-and-how-to-use-it.md)
- [Features](https://github.com/dcastil/tailwind-merge/tree/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/docs/features.md)
- [Limitations](https://github.com/dcastil/tailwind-merge/tree/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/docs/limitations.md)
- [Configuration](https://github.com/dcastil/tailwind-merge/tree/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/docs/configuration.md)
- [Recipes](https://github.com/dcastil/tailwind-merge/tree/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/docs/recipes.md)
- [API reference](https://github.com/dcastil/tailwind-merge/tree/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/docs/api-reference.md)
- [Writing plugins](https://github.com/dcastil/tailwind-merge/tree/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/docs/writing-plugins.md)
- [Versioning](https://github.com/dcastil/tailwind-merge/tree/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/docs/versioning.md)
- [Contributing](https://github.com/dcastil/tailwind-merge/tree/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/docs/contributing.md)
- [Similar packages](https://github.com/dcastil/tailwind-merge/tree/f3b5d4a2acc36bef2a58f30c9808f3874fafc76d/docs/similar-packages.md)

@@ -25,7 +25,7 @@ import {

export function createClassGroupUtils(config: GenericConfig) {
export const createClassGroupUtils = (config: GenericConfig) => {
const classMap = createClassMap(config)
const { conflictingClassGroups, conflictingClassGroupModifiers } = config
function getClassGroupId(className: string) {
const getClassGroupId = (className: string) => {
const classParts = className.split(CLASS_PART_SEPARATOR)

@@ -41,6 +41,6 @@

function getConflictingClassGroupIds(
const getConflictingClassGroupIds = (
classGroupId: GenericClassGroupIds,
hasPostfixModifier: boolean,
) {
) => {
const conflicts = conflictingClassGroups[classGroupId] || []

@@ -61,6 +61,6 @@

function getGroupRecursive(
const getGroupRecursive = (
classParts: string[],
classPartObject: ClassPartObject,
): GenericClassGroupIds | undefined {
): GenericClassGroupIds | undefined => {
if (classParts.length === 0) {

@@ -91,3 +91,3 @@ return classPartObject.classGroupId

function getGroupIdForArbitraryProperty(className: string) {
const getGroupIdForArbitraryProperty = (className: string) => {
if (arbitraryPropertyRegex.test(className)) {

@@ -110,3 +110,3 @@ const arbitraryPropertyClassName = arbitraryPropertyRegex.exec(className)![1]

*/
export function createClassMap(config: Config<GenericClassGroupIds, GenericThemeGroupIds>) {
export const createClassMap = (config: Config<GenericClassGroupIds, GenericThemeGroupIds>) => {
const { theme, prefix } = config

@@ -130,3 +130,3 @@ const classMap: ClassPartObject = {

function processClassesRecursively(
const processClassesRecursively = (
classGroup: ClassGroup<GenericThemeGroupIds>,

@@ -136,3 +136,3 @@ classPartObject: ClassPartObject,

theme: ThemeObject<GenericThemeGroupIds>,
) {
) => {
classGroup.forEach((classDefinition) => {

@@ -176,3 +176,3 @@ if (typeof classDefinition === 'string') {

function getPart(classPartObject: ClassPartObject, path: string) {
const getPart = (classPartObject: ClassPartObject, path: string) => {
let currentClassPartObject = classPartObject

@@ -194,10 +194,9 @@

function isThemeGetter(func: ClassValidator | ThemeGetter): func is ThemeGetter {
return (func as ThemeGetter).isThemeGetter
}
const isThemeGetter = (func: ClassValidator | ThemeGetter): func is ThemeGetter =>
(func as ThemeGetter).isThemeGetter
function getPrefixedClassGroupEntries(
const getPrefixedClassGroupEntries = (
classGroupEntries: Array<[classGroupId: string, classGroup: ClassGroup<GenericThemeGroupIds>]>,
prefix: string | undefined,
): Array<[classGroupId: string, classGroup: ClassGroup<GenericThemeGroupIds>]> {
): Array<[classGroupId: string, classGroup: ClassGroup<GenericThemeGroupIds>]> => {
if (!prefix) {

@@ -204,0 +203,0 @@ return classGroupEntries

@@ -8,8 +8,6 @@ import { createClassGroupUtils } from './class-group-utils'

export function createConfigUtils(config: GenericConfig) {
return {
cache: createLruCache<string, string>(config.cacheSize),
parseClassName: createParseClassName(config),
...createClassGroupUtils(config),
}
}
export const createConfigUtils = (config: GenericConfig) => ({
cache: createLruCache<string, string>(config.cacheSize),
parseClassName: createParseClassName(config),
...createClassGroupUtils(config),
})

@@ -8,3 +8,3 @@ import { createTailwindMerge } from './create-tailwind-merge'

export function extendTailwindMerge<
export const extendTailwindMerge = <
AdditionalClassGroupIds extends string = never,

@@ -20,4 +20,4 @@ AdditionalThemeGroupIds extends string = never,

...createConfig: CreateConfigSubsequent[]
) {
return typeof configExtension === 'function'
) =>
typeof configExtension === 'function'
? createTailwindMerge(getDefaultConfig, configExtension, ...createConfig)

@@ -28,2 +28,1 @@ : createTailwindMerge(

)
}
import { DefaultThemeGroupIds, NoInfer, ThemeGetter, ThemeObject } from './types'
export function fromTheme<
export const fromTheme = <
AdditionalThemeGroupIds extends string = never,
DefaultThemeGroupIdsInner extends string = DefaultThemeGroupIds,
>(key: NoInfer<DefaultThemeGroupIdsInner | AdditionalThemeGroupIds>): ThemeGetter {
>(key: NoInfer<DefaultThemeGroupIdsInner | AdditionalThemeGroupIds>): ThemeGetter => {
const themeGetter = (theme: ThemeObject<DefaultThemeGroupIdsInner | AdditionalThemeGroupIds>) =>

@@ -8,0 +8,0 @@ theme[key] || []

@@ -9,3 +9,3 @@ // Export is needed because TypeScript complains about an error otherwise:

// LRU cache inspired from hashlru (https://github.com/dominictarr/hashlru/blob/v1.0.4/index.js) but object replaced with Map to improve performance
export function createLruCache<Key, Value>(maxCacheSize: number): LruCache<Key, Value> {
export const createLruCache = <Key, Value>(maxCacheSize: number): LruCache<Key, Value> => {
if (maxCacheSize < 1) {

@@ -22,3 +22,3 @@ return {

function update(key: Key, value: Value) {
const update = (key: Key, value: Value) => {
cache.set(key, value)

@@ -25,0 +25,0 @@ cacheSize++

import { ConfigUtils } from './config-utils'
import { IMPORTANT_MODIFIER, sortModifiers } from './parse-class-name'
const SPLIT_CLASSES_REGEX = /\s+/
export function mergeClassList(classList: string, configUtils: ConfigUtils) {
export const mergeClassList = (classList: string, configUtils: ConfigUtils) => {
const { parseClassName, getClassGroupId, getConflictingClassGroupIds } = configUtils

@@ -16,84 +14,64 @@

*/
const classGroupsInConflict = new Set<string>()
const classGroupsInConflict: string[] = []
return (
classList
.trim()
.split(SPLIT_CLASSES_REGEX)
.map((originalClassName) => {
const {
modifiers,
hasImportantModifier,
baseClassName,
maybePostfixModifierPosition,
} = parseClassName(originalClassName)
let result = ''
let hasPostfixModifier = Boolean(maybePostfixModifierPosition)
let classGroupId = getClassGroupId(
hasPostfixModifier
? baseClassName.substring(0, maybePostfixModifierPosition)
: baseClassName,
)
for (let i = classList.length - 1; i >= 0; ) {
while (classList[i] === ' ') {
--i
}
const nextI = classList.lastIndexOf(' ', i)
const originalClassName = classList.slice(nextI === -1 ? 0 : nextI + 1, i + 1)
i = nextI
if (!classGroupId) {
if (!hasPostfixModifier) {
return {
isTailwindClass: false as const,
originalClassName,
}
}
const { modifiers, hasImportantModifier, baseClassName, maybePostfixModifierPosition } =
parseClassName(originalClassName)
classGroupId = getClassGroupId(baseClassName)
let hasPostfixModifier = Boolean(maybePostfixModifierPosition)
let classGroupId = getClassGroupId(
hasPostfixModifier
? baseClassName.substring(0, maybePostfixModifierPosition)
: baseClassName,
)
if (!classGroupId) {
return {
isTailwindClass: false as const,
originalClassName,
}
}
if (!classGroupId) {
if (!hasPostfixModifier) {
result = originalClassName + (result.length > 0 ? ' ' + result : result)
continue
}
hasPostfixModifier = false
}
classGroupId = getClassGroupId(baseClassName)
const variantModifier = sortModifiers(modifiers).join(':')
if (!classGroupId) {
result = originalClassName + (result.length > 0 ? ' ' + result : result)
continue
}
const modifierId = hasImportantModifier
? variantModifier + IMPORTANT_MODIFIER
: variantModifier
hasPostfixModifier = false
}
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 variantModifier = sortModifiers(modifiers).join(':')
const { modifierId, classGroupId, hasPostfixModifier } = parsed
const modifierId = hasImportantModifier
? variantModifier + IMPORTANT_MODIFIER
: variantModifier
const classId = modifierId + classGroupId
const classId = modifierId + classGroupId
if (classGroupsInConflict.has(classId)) {
return false
}
if (classGroupsInConflict.includes(classId)) {
continue
}
classGroupsInConflict.add(classId)
classGroupsInConflict.push(classId)
getConflictingClassGroupIds(classGroupId, hasPostfixModifier).forEach((group) =>
classGroupsInConflict.add(modifierId + group),
)
const conflictGroups = getConflictingClassGroupIds(classGroupId, hasPostfixModifier)
for (let i = 0; i < conflictGroups.length; ++i) {
const group = conflictGroups[i]!
classGroupsInConflict.push(modifierId + group)
}
return true
})
.reverse()
.map((parsed) => parsed.originalClassName)
.join(' ')
)
result = originalClassName + (result.length > 0 ? ' ' + result : result)
}
return result
}

@@ -7,3 +7,3 @@ import { ConfigExtension, GenericConfig } from './types'

*/
export function mergeConfigs<ClassGroupIds extends string, ThemeGroupIds extends string = never>(
export const mergeConfigs = <ClassGroupIds extends string, ThemeGroupIds extends string = never>(
baseConfig: GenericConfig,

@@ -18,3 +18,3 @@ {

}: ConfigExtension<ClassGroupIds, ThemeGroupIds>,
) {
) => {
overrideProperty(baseConfig, 'cacheSize', cacheSize)

@@ -42,7 +42,7 @@ overrideProperty(baseConfig, 'prefix', prefix)

function overrideProperty<T extends object, K extends keyof T>(
const overrideProperty = <T extends object, K extends keyof T>(
baseObject: T,
overrideKey: K,
overrideValue: T[K] | undefined,
) {
) => {
if (overrideValue !== undefined) {

@@ -53,6 +53,6 @@ baseObject[overrideKey] = overrideValue

function overrideConfigProperties(
const overrideConfigProperties = (
baseObject: Partial<Record<string, readonly unknown[]>>,
overrideObject: Partial<Record<string, readonly unknown[]>> | undefined,
) {
) => {
if (overrideObject) {

@@ -65,6 +65,6 @@ for (const key in overrideObject) {

function mergeConfigProperties(
const mergeConfigProperties = (
baseObject: Partial<Record<string, readonly unknown[]>>,
mergeObject: Partial<Record<string, readonly unknown[]>> | undefined,
) {
) => {
if (mergeObject) {

@@ -71,0 +71,0 @@ for (const key in mergeObject) {

@@ -5,3 +5,3 @@ import { GenericConfig } from './types'

export function createParseClassName(config: GenericConfig) {
export const createParseClassName = (config: GenericConfig) => {
const { separator, experimentalParseClassName } = config

@@ -13,3 +13,3 @@ const isSeparatorSingleCharacter = separator.length === 1

// parseClassName inspired by https://github.com/tailwindlabs/tailwindcss/blob/v3.2.2/src/util/splitAtTopLevelOnly.js
function parseClassName(className: string) {
const parseClassName = (className: string) => {
const modifiers = []

@@ -70,5 +70,3 @@

if (experimentalParseClassName) {
return function parseClassNameExperimental(className: string) {
return experimentalParseClassName({ className, parseClassName })
}
return (className: string) => experimentalParseClassName({ className, parseClassName })
}

@@ -84,3 +82,3 @@

*/
export function sortModifiers(modifiers: string[]) {
export const sortModifiers = (modifiers: string[]) => {
if (modifiers.length <= 1) {

@@ -87,0 +85,0 @@ return modifiers

@@ -32,3 +32,3 @@ /**

function toValue(mix: ClassNameArray | string) {
const toValue = (mix: ClassNameArray | string) => {
if (typeof mix === 'string') {

@@ -35,0 +35,0 @@ return mix

@@ -13,63 +13,40 @@ const arbitraryValueRegex = /^\[(?:([a-z-]+):)?(.+)\]$/i

export function isLength(value: string) {
return isNumber(value) || stringLengths.has(value) || fractionRegex.test(value)
}
export const isLength = (value: string) =>
isNumber(value) || stringLengths.has(value) || fractionRegex.test(value)
export function isArbitraryLength(value: string) {
return getIsArbitraryValue(value, 'length', isLengthOnly)
}
export const isArbitraryLength = (value: string) =>
getIsArbitraryValue(value, 'length', isLengthOnly)
export function isNumber(value: string) {
return Boolean(value) && !Number.isNaN(Number(value))
}
export const isNumber = (value: string) => Boolean(value) && !Number.isNaN(Number(value))
export function isArbitraryNumber(value: string) {
return getIsArbitraryValue(value, 'number', isNumber)
}
export const isArbitraryNumber = (value: string) => getIsArbitraryValue(value, 'number', isNumber)
export function isInteger(value: string) {
return Boolean(value) && Number.isInteger(Number(value))
}
export const isInteger = (value: string) => Boolean(value) && Number.isInteger(Number(value))
export function isPercent(value: string) {
return value.endsWith('%') && isNumber(value.slice(0, -1))
}
export const isPercent = (value: string) => value.endsWith('%') && isNumber(value.slice(0, -1))
export function isArbitraryValue(value: string) {
return arbitraryValueRegex.test(value)
}
export const isArbitraryValue = (value: string) => arbitraryValueRegex.test(value)
export function isTshirtSize(value: string) {
return tshirtUnitRegex.test(value)
}
export const isTshirtSize = (value: string) => tshirtUnitRegex.test(value)
const sizeLabels = new Set(['length', 'size', 'percentage'])
export function isArbitrarySize(value: string) {
return getIsArbitraryValue(value, sizeLabels, isNever)
}
export const isArbitrarySize = (value: string) => getIsArbitraryValue(value, sizeLabels, isNever)
export function isArbitraryPosition(value: string) {
return getIsArbitraryValue(value, 'position', isNever)
}
export const isArbitraryPosition = (value: string) =>
getIsArbitraryValue(value, 'position', isNever)
const imageLabels = new Set(['image', 'url'])
export function isArbitraryImage(value: string) {
return getIsArbitraryValue(value, imageLabels, isImage)
}
export const isArbitraryImage = (value: string) => getIsArbitraryValue(value, imageLabels, isImage)
export function isArbitraryShadow(value: string) {
return getIsArbitraryValue(value, '', isShadow)
}
export const isArbitraryShadow = (value: string) => getIsArbitraryValue(value, '', isShadow)
export function isAny() {
return true
}
export const isAny = () => true
function getIsArbitraryValue(
const getIsArbitraryValue = (
value: string,
label: string | Set<string>,
testValue: (value: string) => boolean,
) {
) => {
const result = arbitraryValueRegex.exec(value)

@@ -88,19 +65,12 @@

function isLengthOnly(value: string) {
const isLengthOnly = (value: string) =>
// `colorFunctionRegex` check is necessary because color functions can have percentages in them which which would be incorrectly classified as lengths.
// For example, `hsl(0 0% 0%)` would be classified as a length without this check.
// I could also use lookbehind assertion in `lengthUnitRegex` but that isn't supported widely enough.
return lengthUnitRegex.test(value) && !colorFunctionRegex.test(value)
}
lengthUnitRegex.test(value) && !colorFunctionRegex.test(value)
function isNever() {
return false
}
const isNever = () => false
function isShadow(value: string) {
return shadowRegex.test(value)
}
const isShadow = (value: string) => shadowRegex.test(value)
function isImage(value: string) {
return imageRegex.test(value)
}
const isImage = (value: string) => imageRegex.test(value)

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc