diff --git a/docs/versioning.md b/docs/versioning.md index d4a9ae89..124e523b 100644 --- a/docs/versioning.md +++ b/docs/versioning.md @@ -8,11 +8,13 @@ This package follows the [SemVer](https://semver.org) versioning rules. More spe - Major version gets incremented when breaking changes are introduced to the package API. E.g. the return type of `twMerge` changes. -- `alpha` releases might introduce breaking changes on any update. Whereas `beta` releases only introduce new features or bug fixes. +- `alpha` releases might introduce breaking changes on any update. `beta` releases intend to only introduce new features or bug fixes, but can introduce breaking changes in rare cases. + +- Any API that has `experimental` in its name can introduce breaking changes in any minor version update. - Releases with major version 0 might introduce breaking changes on a minor version update. -- A non-production-ready version of every commit pushed to the main branch is released under the `dev` tag for testing purposes. It has a format like [`1.6.1-dev.4202ccf913525617f19fbc493db478a76d64d054`](https://www.npmjs.com/package/tailwind-merge/v/1.6.1-dev.4202ccf913525617f19fbc493db478a76d64d054) in which the first numbers are the corresponding last release and the hash at the end is the git SHA of the commit. You can install the latest dev release with `yarn add tailwind-merge@dev`. +- A non-production-ready version of every commit pushed to the main branch is released under the `dev` tag for testing purposes. It has a format like [`1.6.1-dev.4202ccf913525617f19fbc493db478a76d64d054`](https://www.npmjs.com/package/tailwind-merge/v/1.6.1-dev.4202ccf913525617f19fbc493db478a76d64d054) in which the first numbers are the corresponding last release and the hash at the end is the git SHA of the commit. You can install the latest dev release with `npm install tailwind-merge@dev`. - A changelog is documented in [GitHub Releases](https://github.com/dcastil/tailwind-merge/releases). diff --git a/src/index.ts b/src/index.ts index 25d16cb2..b9873ab5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,5 +10,7 @@ export { type Config, type DefaultClassGroupIds, type DefaultThemeGroupIds, + type ExperimentalParseClassNameParam, + type ExperimentalParsedClassName, } from './lib/types' export * as validators from './lib/validators' diff --git a/src/lib/class-utils.ts b/src/lib/class-group-utils.ts similarity index 99% rename from src/lib/class-utils.ts rename to src/lib/class-group-utils.ts index 4688690a..66ec8ac2 100644 --- a/src/lib/class-utils.ts +++ b/src/lib/class-group-utils.ts @@ -22,7 +22,7 @@ interface ClassValidatorObject { const CLASS_PART_SEPARATOR = '-' -export function createClassUtils(config: GenericConfig) { +export function createClassGroupUtils(config: GenericConfig) { const classMap = createClassMap(config) const { conflictingClassGroups, conflictingClassGroupModifiers } = config diff --git a/src/lib/config-utils.ts b/src/lib/config-utils.ts index 59a7ab23..b39a2261 100644 --- a/src/lib/config-utils.ts +++ b/src/lib/config-utils.ts @@ -1,6 +1,6 @@ -import { createClassUtils } from './class-utils' +import { createClassGroupUtils } from './class-group-utils' import { createLruCache } from './lru-cache' -import { createSplitModifiers } from './modifier-utils' +import { createParseClassName } from './parse-class-name' import { GenericConfig } from './types' export type ConfigUtils = ReturnType @@ -8,7 +8,7 @@ export type ConfigUtils = ReturnType export function createConfigUtils(config: GenericConfig) { return { cache: createLruCache(config.cacheSize), - splitModifiers: createSplitModifiers(config), - ...createClassUtils(config), + parseClassName: createParseClassName(config), + ...createClassGroupUtils(config), } } diff --git a/src/lib/merge-classlist.ts b/src/lib/merge-classlist.ts index 75a8d75c..de1142ba 100644 --- a/src/lib/merge-classlist.ts +++ b/src/lib/merge-classlist.ts @@ -1,10 +1,10 @@ import { ConfigUtils } from './config-utils' -import { IMPORTANT_MODIFIER, sortModifiers } from './modifier-utils' +import { IMPORTANT_MODIFIER, sortModifiers } from './parse-class-name' const SPLIT_CLASSES_REGEX = /\s+/ export function mergeClassList(classList: string, configUtils: ConfigUtils) { - const { splitModifiers, getClassGroupId, getConflictingClassGroupIds } = configUtils + const { parseClassName, getClassGroupId, getConflictingClassGroupIds } = configUtils /** * Set of classGroupIds in following format: @@ -25,18 +25,17 @@ export function mergeClassList(classList: string, configUtils: ConfigUtils) { hasImportantModifier, baseClassName, maybePostfixModifierPosition, - } = splitModifiers(originalClassName) + } = parseClassName(originalClassName) + let hasPostfixModifier = Boolean(maybePostfixModifierPosition) let classGroupId = getClassGroupId( - maybePostfixModifierPosition + hasPostfixModifier ? baseClassName.substring(0, maybePostfixModifierPosition) : baseClassName, ) - let hasPostfixModifier = Boolean(maybePostfixModifierPosition) - if (!classGroupId) { - if (!maybePostfixModifierPosition) { + if (!hasPostfixModifier) { return { isTailwindClass: false as const, originalClassName, diff --git a/src/lib/merge-configs.ts b/src/lib/merge-configs.ts index df2d765f..94c8b39c 100644 --- a/src/lib/merge-configs.ts +++ b/src/lib/merge-configs.ts @@ -10,6 +10,7 @@ export function mergeConfigs, @@ -17,6 +18,7 @@ export function mergeConfigs { diff --git a/tests/class-map.test.ts b/tests/class-map.test.ts index 0dd5eb82..f1f1c120 100644 --- a/tests/class-map.test.ts +++ b/tests/class-map.test.ts @@ -1,5 +1,5 @@ import { getDefaultConfig } from '../src' -import { ClassPartObject, createClassMap } from '../src/lib/class-utils' +import { ClassPartObject, createClassMap } from '../src/lib/class-group-utils' test('class map has correct class groups at first part', () => { const classMap = createClassMap(getDefaultConfig()) diff --git a/tests/experimental-parse-class-name.test.ts b/tests/experimental-parse-class-name.test.ts new file mode 100644 index 00000000..889b0c43 --- /dev/null +++ b/tests/experimental-parse-class-name.test.ts @@ -0,0 +1,37 @@ +import { extendTailwindMerge } from '../src' + +test('default case', () => { + const twMerge = extendTailwindMerge({ + experimentalParseClassName({ className, parseClassName }) { + return parseClassName(className) + }, + }) + + expect(twMerge('px-2 py-1 p-3')).toBe('p-3') +}) + +test('removing first three characters from class', () => { + const twMerge = extendTailwindMerge({ + experimentalParseClassName({ className, parseClassName }) { + return parseClassName(className.slice(3)) + }, + }) + + expect(twMerge('barpx-2 foopy-1 lolp-3')).toBe('lolp-3') +}) + +test('ignoring breakpoint modifiers', () => { + const breakpoints = new Set(['sm', 'md', 'lg', 'xl', '2xl']) + const twMerge = extendTailwindMerge({ + experimentalParseClassName({ className, parseClassName }) { + const parsed = parseClassName(className) + + return { + ...parsed, + modifiers: parsed.modifiers.filter((modifier) => !breakpoints.has(modifier)), + } + }, + }) + + expect(twMerge('md:px-2 hover:py-4 py-1 lg:p-3')).toBe('hover:py-4 lg:p-3') +})