tailwind-merge
Advanced tools
Comparing version 0.4.0 to 0.5.0
import HLRU from 'hashlru'; | ||
function _extends() { | ||
_extends = Object.assign || function (target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i]; | ||
for (var key in source) { | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
return target; | ||
}; | ||
return _extends.apply(this, arguments); | ||
} | ||
function getLruCache(cacheSize) { | ||
if (cacheSize >= 1) { | ||
return HLRU(cacheSize); | ||
} | ||
return { | ||
get: () => undefined, | ||
set: () => {} | ||
}; | ||
} | ||
const CLASS_PART_SEPARATOR = '-'; | ||
@@ -11,3 +40,3 @@ function createClassUtils(config) { | ||
function getGroupId(className) { | ||
function getClassGroupId(className) { | ||
const classParts = className.split(CLASS_PART_SEPARATOR); // Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and remove it from classParts. | ||
@@ -22,3 +51,3 @@ | ||
function getConflictingGroupIds(classGroupId) { | ||
function getConflictingClassGroupIds(classGroupId) { | ||
return config.conflictingClassGroups[classGroupId] || []; | ||
@@ -28,4 +57,4 @@ } | ||
return { | ||
getGroupId, | ||
getConflictingGroupIds | ||
getClassGroupId, | ||
getConflictingClassGroupIds | ||
}; | ||
@@ -101,7 +130,7 @@ } | ||
function isValid(maybePrefix) { | ||
function isPrefixValid(maybePrefix) { | ||
return prefixToIndexMap[maybePrefix] !== undefined; | ||
} | ||
function compare(firstPrefix, secondPrefix) { | ||
function comparePrefixes(firstPrefix, secondPrefix) { | ||
return prefixToIndexMap[firstPrefix] - prefixToIndexMap[secondPrefix]; | ||
@@ -111,4 +140,4 @@ } | ||
return { | ||
isValid, | ||
compare | ||
isPrefixValid, | ||
comparePrefixes | ||
}; | ||
@@ -118,6 +147,5 @@ } | ||
function createConfigUtils(config) { | ||
return { | ||
prefix: createPrefixUtils(config), | ||
class: createClassUtils(config) | ||
}; | ||
return _extends({ | ||
cache: getLruCache(config.cacheSize) | ||
}, createPrefixUtils(config), createClassUtils(config)); | ||
} | ||
@@ -1784,13 +1812,2 @@ | ||
function getLruCache(cacheSize) { | ||
if (cacheSize >= 1) { | ||
return HLRU(cacheSize); | ||
} | ||
return { | ||
get: () => undefined, | ||
set: () => {} | ||
}; | ||
} | ||
const SPLIT_CLASSES_REGEX = /\s+/; | ||
@@ -1803,2 +1820,8 @@ const IMPORTANT_MODIFIER = '!'; // Regex is needed so we don't match against colons in labels for custom values like `text-[color:var(--mystery-var)]` | ||
function mergeClassList(classList, configUtils) { | ||
const { | ||
isPrefixValid, | ||
getClassGroupId, | ||
comparePrefixes, | ||
getConflictingClassGroupIds | ||
} = configUtils; | ||
/** | ||
@@ -1811,2 +1834,3 @@ * Set of classGroupIds in following format: | ||
*/ | ||
const classGroupsInConflict = new Set(); | ||
@@ -1818,4 +1842,4 @@ return classList.trim().split(SPLIT_CLASSES_REGEX).map(originalClassName => { | ||
const className = prefixes.pop(); | ||
const arePrefixesValid = prefixes.every(configUtils.prefix.isValid); | ||
const classGroupId = arePrefixesValid ? configUtils.class.getGroupId(className) : undefined; | ||
const arePrefixesValid = prefixes.every(isPrefixValid); | ||
const classGroupId = arePrefixesValid ? getClassGroupId(className) : undefined; | ||
@@ -1829,3 +1853,3 @@ if (!classGroupId) { | ||
const variantPrefix = prefixes.length === 0 ? '' : prefixes.sort(configUtils.prefix.compare).concat('').join(PREFIX_SEPARATOR); | ||
const variantPrefix = prefixes.length === 0 ? '' : prefixes.sort(comparePrefixes).concat('').join(PREFIX_SEPARATOR); | ||
const fullPrefix = hasImportantModifier ? IMPORTANT_MODIFIER + variantPrefix : variantPrefix; | ||
@@ -1855,3 +1879,3 @@ return { | ||
classGroupsInConflict.add(classId); | ||
configUtils.class.getConflictingGroupIds(classGroupId).forEach(group => classGroupsInConflict.add(`${prefix}:${group}`)); | ||
getConflictingClassGroupIds(classGroupId).forEach(group => classGroupsInConflict.add(`${prefix}:${group}`)); | ||
return true; | ||
@@ -1862,8 +1886,18 @@ }).reverse().map(parsed => parsed.originalClassName).join(' '); | ||
function createTailwindMerge(createConfig) { | ||
const config = createConfig(getDefaultConfig); | ||
const configUtils = createConfigUtils(config); | ||
const cache = getLruCache(config.cacheSize); | ||
return function tailwindMerge(...classLists) { | ||
let configUtils; | ||
let cacheGet; | ||
let cacheSet; | ||
let functionToCall = initTailwindMerge; | ||
function initTailwindMerge(classLists) { | ||
configUtils = createConfigUtils(createConfig(getDefaultConfig)); | ||
cacheGet = configUtils.cache.get; | ||
cacheSet = configUtils.cache.set; | ||
functionToCall = tailwindMerge; | ||
return tailwindMerge(classLists); | ||
} | ||
function tailwindMerge(classLists) { | ||
const classList = classLists.filter(Boolean).join(' '); | ||
const cachedResult = cache.get(classList); | ||
const cachedResult = cacheGet(classList); | ||
@@ -1875,4 +1909,8 @@ if (cachedResult) { | ||
const result = mergeClassList(classList, configUtils); | ||
cache.set(classList, result); | ||
cacheSet(classList, result); | ||
return result; | ||
} | ||
return function callTailwindMerge(...classLists) { | ||
return functionToCall(classLists); | ||
}; | ||
@@ -1879,0 +1917,0 @@ } |
import HLRU from 'hashlru'; | ||
function _extends() { | ||
_extends = Object.assign || function (target) { | ||
for (var i = 1; i < arguments.length; i++) { | ||
var source = arguments[i]; | ||
for (var key in source) { | ||
if (Object.prototype.hasOwnProperty.call(source, key)) { | ||
target[key] = source[key]; | ||
} | ||
} | ||
} | ||
return target; | ||
}; | ||
return _extends.apply(this, arguments); | ||
} | ||
function getLruCache(cacheSize) { | ||
if (cacheSize >= 1) { | ||
return HLRU(cacheSize); | ||
} | ||
return { | ||
get: function get() { | ||
return undefined; | ||
}, | ||
set: function set() {} | ||
}; | ||
} | ||
var CLASS_PART_SEPARATOR = '-'; | ||
@@ -11,3 +42,3 @@ function createClassUtils(config) { | ||
function getGroupId(className) { | ||
function getClassGroupId(className) { | ||
var classParts = className.split(CLASS_PART_SEPARATOR); // Classes like `-inset-1` produce an empty string as first classPart. We assume that classes for negative values are used correctly and remove it from classParts. | ||
@@ -22,3 +53,3 @@ | ||
function getConflictingGroupIds(classGroupId) { | ||
function getConflictingClassGroupIds(classGroupId) { | ||
return config.conflictingClassGroups[classGroupId] || []; | ||
@@ -28,4 +59,4 @@ } | ||
return { | ||
getGroupId: getGroupId, | ||
getConflictingGroupIds: getConflictingGroupIds | ||
getClassGroupId: getClassGroupId, | ||
getConflictingClassGroupIds: getConflictingClassGroupIds | ||
}; | ||
@@ -108,7 +139,7 @@ } | ||
function isValid(maybePrefix) { | ||
function isPrefixValid(maybePrefix) { | ||
return prefixToIndexMap[maybePrefix] !== undefined; | ||
} | ||
function compare(firstPrefix, secondPrefix) { | ||
function comparePrefixes(firstPrefix, secondPrefix) { | ||
return prefixToIndexMap[firstPrefix] - prefixToIndexMap[secondPrefix]; | ||
@@ -118,4 +149,4 @@ } | ||
return { | ||
isValid: isValid, | ||
compare: compare | ||
isPrefixValid: isPrefixValid, | ||
comparePrefixes: comparePrefixes | ||
}; | ||
@@ -125,6 +156,5 @@ } | ||
function createConfigUtils(config) { | ||
return { | ||
prefix: createPrefixUtils(config), | ||
"class": createClassUtils(config) | ||
}; | ||
return _extends({ | ||
cache: getLruCache(config.cacheSize) | ||
}, createPrefixUtils(config), createClassUtils(config)); | ||
} | ||
@@ -1791,15 +1821,2 @@ | ||
function getLruCache(cacheSize) { | ||
if (cacheSize >= 1) { | ||
return HLRU(cacheSize); | ||
} | ||
return { | ||
get: function get() { | ||
return undefined; | ||
}, | ||
set: function set() {} | ||
}; | ||
} | ||
var SPLIT_CLASSES_REGEX = /\s+/; | ||
@@ -1812,2 +1829,6 @@ var IMPORTANT_MODIFIER = '!'; // Regex is needed so we don't match against colons in labels for custom values like `text-[color:var(--mystery-var)]` | ||
function mergeClassList(classList, configUtils) { | ||
var isPrefixValid = configUtils.isPrefixValid, | ||
getClassGroupId = configUtils.getClassGroupId, | ||
comparePrefixes = configUtils.comparePrefixes, | ||
getConflictingClassGroupIds = configUtils.getConflictingClassGroupIds; | ||
/** | ||
@@ -1820,2 +1841,3 @@ * Set of classGroupIds in following format: | ||
*/ | ||
var classGroupsInConflict = new Set(); | ||
@@ -1827,4 +1849,4 @@ return classList.trim().split(SPLIT_CLASSES_REGEX).map(function (originalClassName) { | ||
var className = prefixes.pop(); | ||
var arePrefixesValid = prefixes.every(configUtils.prefix.isValid); | ||
var classGroupId = arePrefixesValid ? configUtils["class"].getGroupId(className) : undefined; | ||
var arePrefixesValid = prefixes.every(isPrefixValid); | ||
var classGroupId = arePrefixesValid ? getClassGroupId(className) : undefined; | ||
@@ -1838,3 +1860,3 @@ if (!classGroupId) { | ||
var variantPrefix = prefixes.length === 0 ? '' : prefixes.sort(configUtils.prefix.compare).concat('').join(PREFIX_SEPARATOR); | ||
var variantPrefix = prefixes.length === 0 ? '' : prefixes.sort(comparePrefixes).concat('').join(PREFIX_SEPARATOR); | ||
var fullPrefix = hasImportantModifier ? IMPORTANT_MODIFIER + variantPrefix : variantPrefix; | ||
@@ -1862,3 +1884,3 @@ return { | ||
classGroupsInConflict.add(classId); | ||
configUtils["class"].getConflictingGroupIds(classGroupId).forEach(function (group) { | ||
getConflictingClassGroupIds(classGroupId).forEach(function (group) { | ||
return classGroupsInConflict.add(prefix + ":" + group); | ||
@@ -1873,9 +1895,19 @@ }); | ||
function createTailwindMerge(createConfig) { | ||
var config = createConfig(getDefaultConfig); | ||
var configUtils = createConfigUtils(config); | ||
var cache = getLruCache(config.cacheSize); | ||
return function tailwindMerge() { | ||
var classList = [].slice.call(arguments).filter(Boolean).join(' '); | ||
var cachedResult = cache.get(classList); | ||
var configUtils; | ||
var cacheGet; | ||
var cacheSet; | ||
var functionToCall = initTailwindMerge; | ||
function initTailwindMerge(classLists) { | ||
configUtils = createConfigUtils(createConfig(getDefaultConfig)); | ||
cacheGet = configUtils.cache.get; | ||
cacheSet = configUtils.cache.set; | ||
functionToCall = tailwindMerge; | ||
return tailwindMerge(classLists); | ||
} | ||
function tailwindMerge(classLists) { | ||
var classList = classLists.filter(Boolean).join(' '); | ||
var cachedResult = cacheGet(classList); | ||
if (cachedResult) { | ||
@@ -1886,4 +1918,8 @@ return cachedResult; | ||
var result = mergeClassList(classList, configUtils); | ||
cache.set(classList, result); | ||
cacheSet(classList, result); | ||
return result; | ||
} | ||
return function callTailwindMerge() { | ||
return functionToCall([].slice.call(arguments)); | ||
}; | ||
@@ -1890,0 +1926,0 @@ } |
@@ -1,2 +0,2 @@ | ||
interface LruCache<T> { | ||
export interface LruCache<T> { | ||
get(key: string): T | undefined; | ||
@@ -6,2 +6,1 @@ set(key: string, value: T): void; | ||
export declare function getLruCache<T>(cacheSize: number): LruCache<T>; | ||
export {}; |
@@ -1,2 +0,2 @@ | ||
import { ConfigUtils } from './config/config-utils'; | ||
import { ConfigUtils } from './config-utils'; | ||
export declare function mergeClassList(classList: string, configUtils: ConfigUtils): string; |
@@ -1,6 +0,7 @@ | ||
import { getDefaultConfig } from './config/default-config'; | ||
import { Config } from './config/types'; | ||
import { getDefaultConfig } from './default-config'; | ||
import { Config } from './types'; | ||
declare type CreateConfig = (getDefault: typeof getDefaultConfig) => Config; | ||
declare type TailwindMerge = (...classLists: Array<string | undefined>) => string; | ||
declare type ClassLists = Array<string | undefined>; | ||
declare type TailwindMerge = (...classLists: ClassLists) => string; | ||
export declare function createTailwindMerge(createConfig: CreateConfig): TailwindMerge; | ||
export {}; |
{ | ||
"name": "tailwind-merge", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"description": "Merge Tailwind CSS classes without style conflicts", | ||
@@ -43,3 +43,3 @@ "keywords": [ | ||
"preversion": "git checkout main && git pull", | ||
"version": "yarn build && node scripts/package-build-stats && git add README.md", | ||
"version": "zx scripts/update-readme.js", | ||
"postversion": "git push --follow-tags && open https://github.com/dcastil/tailwind-merge/releases" | ||
@@ -57,2 +57,3 @@ }, | ||
"eslint-plugin-jest": "^24.3.6", | ||
"fp-ts": "^2.10.5", | ||
"jest": "^27.0.6", | ||
@@ -63,4 +64,5 @@ "microbundle": "^0.13.3", | ||
"ts-jest": "^27.0.3", | ||
"typescript": "^4.3.5" | ||
"typescript": "^4.3.5", | ||
"zx": "^2.0.0" | ||
} | ||
} |
<div align="center"> | ||
<br /> | ||
<a href="https://github.com/dcastil/tailwind-merge"> | ||
<img src="https://github.com/dcastil/tailwind-merge/raw/v0.4.0/assets/logo.svg" alt="tailwind-merge" width="221px" /> | ||
<!-- AUTOGENERATED START logo-image --><img src="https://github.com/dcastil/tailwind-merge/raw/v0.5.0/assets/logo.svg" alt="tailwind-merge" width="221px" /><!-- AUTOGENERATED END --> | ||
</a> | ||
@@ -19,6 +19,6 @@ </div> | ||
- Supports Tailwind v2.0.0 up to v2.2.5, support for newer version will be added continuously | ||
- Supports Tailwind v2.0.0 up to v2.2.6, support for newer version will be added continuously | ||
- Works in Node >=12 and all modern browsers | ||
- Fully typed | ||
- [<!-- AUTOGENERATED START package-build-stats:gzipSize -->4.6 kB<!-- AUTOGENERATED END --> minified + gzipped](https://bundlephobia.com/package/tailwind-merge) (<!-- AUTOGENERATED START package-build-stats:composition -->95.9% self, 4.1% hashlru<!-- AUTOGENERATED END -->) | ||
- [<!-- AUTOGENERATED START package-gzip-size -->4.7 kB<!-- AUTOGENERATED END --> minified + gzipped](https://bundlephobia.com/package/tailwind-merge) (<!-- AUTOGENERATED START package-composition -->96.1% self, 3.9% hashlru<!-- AUTOGENERATED END -->) | ||
@@ -65,3 +65,4 @@ ## What is it for | ||
- Results get cached by default, so you don't need to worry about wasteful rerenders. The library uses a [LRU cache](<https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)>) which stores up to 500 different results. The cache size can be modified or opt-out of by using [`createTailwindMerge()`](#createtailwindmerge). | ||
- Expensive computations happen during startup time so that `twMerge()` calls without a cache hit stay fast. | ||
- Expensive computations happen upfront so that `twMerge()` calls without a cache hit stay fast. | ||
- These computations are called lazily on the first call to `twMerge()` to prevent it from impacting app startup performance if it isn't used initially. | ||
@@ -152,2 +153,4 @@ ### Last conflicting class wins | ||
```ts | ||
// ↓ Callback passed to `createTailwindMerge()` is called when | ||
// `customTwMerge()` gets called the first time. | ||
const customTwMerge = createTailwindMerge((getDefaultConfig) => { | ||
@@ -154,0 +157,0 @@ const defaultConfig = getDefaultConfig() |
import HLRU from 'hashlru' | ||
interface LruCache<T> { | ||
// Export is needed because TypeScript complains about an error otherwise: | ||
// Error: …/tailwind-merge/src/config-utils.ts(8,17): semantic error TS4058: Return type of exported function has or is using name 'LruCache' from external module "…/tailwind-merge/src/lru-cache" but cannot be named. | ||
export interface LruCache<T> { | ||
get(key: string): T | undefined | ||
@@ -5,0 +7,0 @@ set(key: string, value: T): void |
@@ -1,2 +0,2 @@ | ||
import { ConfigUtils } from './config/config-utils' | ||
import { ConfigUtils } from './config-utils' | ||
@@ -11,2 +11,5 @@ const SPLIT_CLASSES_REGEX = /\s+/ | ||
export function mergeClassList(classList: string, configUtils: ConfigUtils) { | ||
const { isPrefixValid, getClassGroupId, comparePrefixes, getConflictingClassGroupIds } = | ||
configUtils | ||
/** | ||
@@ -34,6 +37,4 @@ * Set of classGroupIds in following format: | ||
const arePrefixesValid = prefixes.every(configUtils.prefix.isValid) | ||
const classGroupId = arePrefixesValid | ||
? configUtils.class.getGroupId(className) | ||
: undefined | ||
const arePrefixesValid = prefixes.every(isPrefixValid) | ||
const classGroupId = arePrefixesValid ? getClassGroupId(className) : undefined | ||
@@ -50,6 +51,3 @@ if (!classGroupId) { | ||
? '' | ||
: prefixes | ||
.sort(configUtils.prefix.compare) | ||
.concat('') | ||
.join(PREFIX_SEPARATOR) | ||
: prefixes.sort(comparePrefixes).concat('').join(PREFIX_SEPARATOR) | ||
@@ -83,6 +81,7 @@ const fullPrefix = hasImportantModifier | ||
classGroupsInConflict.add(classId) | ||
configUtils.class | ||
.getConflictingGroupIds(classGroupId) | ||
.forEach((group) => classGroupsInConflict.add(`${prefix}:${group}`)) | ||
getConflictingClassGroupIds(classGroupId).forEach((group) => | ||
classGroupsInConflict.add(`${prefix}:${group}`) | ||
) | ||
return true | ||
@@ -89,0 +88,0 @@ }) |
@@ -1,18 +0,29 @@ | ||
import { createConfigUtils } from './config/config-utils' | ||
import { getDefaultConfig } from './config/default-config' | ||
import { Config } from './config/types' | ||
import { getLruCache } from './lru-cache' | ||
import { createConfigUtils } from './config-utils' | ||
import { getDefaultConfig } from './default-config' | ||
import { Config } from './types' | ||
import { mergeClassList } from './merge-classlist' | ||
type CreateConfig = (getDefault: typeof getDefaultConfig) => Config | ||
type TailwindMerge = (...classLists: Array<string | undefined>) => string | ||
type ClassLists = Array<string | undefined> | ||
type TailwindMerge = (...classLists: ClassLists) => string | ||
type ConfigUtils = ReturnType<typeof createConfigUtils> | ||
export function createTailwindMerge(createConfig: CreateConfig): TailwindMerge { | ||
const config = createConfig(getDefaultConfig) | ||
const configUtils = createConfigUtils(config) | ||
const cache = getLruCache<string>(config.cacheSize) | ||
let configUtils: ConfigUtils | ||
let cacheGet: ConfigUtils['cache']['get'] | ||
let cacheSet: ConfigUtils['cache']['set'] | ||
let functionToCall = initTailwindMerge | ||
return function tailwindMerge(...classLists) { | ||
function initTailwindMerge(classLists: ClassLists) { | ||
configUtils = createConfigUtils(createConfig(getDefaultConfig)) | ||
cacheGet = configUtils.cache.get | ||
cacheSet = configUtils.cache.set | ||
functionToCall = tailwindMerge | ||
return tailwindMerge(classLists) | ||
} | ||
function tailwindMerge(classLists: ClassLists) { | ||
const classList = classLists.filter(Boolean).join(' ') | ||
const cachedResult = cache.get(classList) | ||
const cachedResult = cacheGet(classList) | ||
@@ -24,6 +35,10 @@ if (cachedResult) { | ||
const result = mergeClassList(classList, configUtils) | ||
cache.set(classList, result) | ||
cacheSet(classList, result) | ||
return result | ||
} | ||
return function callTailwindMerge(...classLists) { | ||
return functionToCall(classLists) | ||
} | ||
} |
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 not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
517043
8039
198
14