Skip to content

Commit c638864

Browse files
committed
chore: bump version
1 parent c5a834c commit c638864

19 files changed

+1554
-411
lines changed

.changeset/merge-v5-rewrite.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
"@weapp-tailwindcss/merge": major
3+
---
4+
5+
- 使用 `@weapp-core/escape``escape`/`unescape` 重写运行时,默认归一化并重转义类名,废弃旧的 `disableEscape``escapeFn`
6+
- 新增 `escape`/`unescape` 配置及共享的 transformers,让 `twMerge`/`twJoin`/`createTailwindMerge``cva` 与新的 `variants` 入口保持一致逻辑。
7+
- 发布 `@weapp-tailwindcss/merge/variants`,包装 `tailwind-variants` 以适配小程序场景并自动调用 weapp 转义。
8+
- 完成 core、cva、v3/v4 运行时与 variants 的全覆盖单测,锁定破坏性行为。

packages/merge/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@
3838
"types": "./dist/v4.d.ts",
3939
"import": "./dist/v4.js",
4040
"require": "./dist/v4.cjs"
41+
},
42+
"./variants": {
43+
"types": "./dist/variants.d.ts",
44+
"import": "./dist/variants.js",
45+
"require": "./dist/variants.cjs"
4146
}
4247
},
4348
"main": "./dist/index.cjs",
@@ -78,13 +83,19 @@
7883
"types": "./dist/v4.d.ts",
7984
"import": "./dist/v4.js",
8085
"require": "./dist/v4.cjs"
86+
},
87+
"./variants": {
88+
"types": "./dist/variants.d.ts",
89+
"import": "./dist/variants.js",
90+
"require": "./dist/variants.cjs"
8191
}
8292
},
8393
"main": "./dist/index.cjs",
8494
"module": "./dist/index.js",
8595
"types": "./dist/index.d.ts"
8696
},
8797
"dependencies": {
98+
"@weapp-core/escape": "^5.0.1",
8899
"class-variance-authority": "^0.7.1",
89100
"clsx": "^2.1.1",
90101
"local-pkg": "^1.1.2",

packages/merge/src/core/create-runtime.ts

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import type { ClassValue } from 'clsx'
22
import type {
33
CreateOptions,
4-
EscapeFn,
54
TailwindMergeFactory,
65
TailwindMergeLibraryFn,
76
TailwindMergeRuntime,
87
TailwindMergeVersion,
8+
Transformers,
99
} from '../types'
1010
import { clsx } from 'clsx'
11-
import { resolveEscape } from './escape'
11+
import { resolveTransformers } from './transformers'
1212

1313
type TailwindMergeFactoryFn = (...args: any[]) => TailwindMergeLibraryFn
1414

@@ -25,19 +25,20 @@ interface CreateRuntimeFactoryOptions<
2525
createTailwindMerge: TCreateFactory
2626
}
2727

28-
function wrapClassAggregator(fn: TailwindMergeLibraryFn, escapeFn: EscapeFn): TailwindMergeRuntime {
28+
function wrapClassAggregator(fn: TailwindMergeLibraryFn, transformers: Transformers): TailwindMergeRuntime {
2929
return (...inputs: ClassValue[]) => {
30-
return escapeFn(fn(clsx(...inputs)))
30+
const normalized = transformers.unescape(clsx(...inputs))
31+
return transformers.escape(fn(normalized))
3132
}
3233
}
3334

3435
function wrapFactory<TFactory extends TailwindMergeFactoryFn>(
3536
factory: TFactory,
36-
escapeFn: EscapeFn,
37+
transformers: Transformers,
3738
): TailwindMergeFactory<TFactory> {
3839
return (...args: Parameters<TFactory>) => {
3940
const runtime = factory(...args)
40-
return wrapClassAggregator(runtime, escapeFn)
41+
return wrapClassAggregator(runtime, transformers)
4142
}
4243
}
4344

@@ -56,12 +57,12 @@ export function createRuntimeFactory<
5657
} = options
5758

5859
return function createRuntime(createOptions?: CreateOptions) {
59-
const escapeFn = resolveEscape(createOptions)
60+
const transformers = resolveTransformers(createOptions)
6061

61-
const twMerge: TailwindMergeRuntime = wrapClassAggregator(_twMerge, escapeFn)
62-
const twJoin: TailwindMergeRuntime = wrapClassAggregator(_twJoin, escapeFn)
63-
const extendTailwindMerge = wrapFactory(_extendTailwindMerge, escapeFn)
64-
const createTailwindMerge = wrapFactory(_createTailwindMerge, escapeFn)
62+
const twMerge: TailwindMergeRuntime = wrapClassAggregator(_twMerge, transformers)
63+
const twJoin: TailwindMergeRuntime = wrapClassAggregator(_twJoin, transformers)
64+
const extendTailwindMerge = wrapFactory(_extendTailwindMerge, transformers)
65+
const createTailwindMerge = wrapFactory(_createTailwindMerge, transformers)
6566

6667
return {
6768
version,

packages/merge/src/core/escape.ts

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type { EscapeOptions, UnescapeOptions } from '@weapp-core/escape'
2+
import type { CreateOptions, Transformers } from '../types'
3+
import {
4+
escape as escapeSelectors,
5+
MappingChars2String,
6+
unescape as unescapeSelectors,
7+
} from '@weapp-core/escape'
8+
9+
const identity = (value: string) => value
10+
11+
function isConfig<T extends object>(value: false | T | undefined): value is T {
12+
return typeof value === 'object' && value !== null
13+
}
14+
15+
function mergeWithDefaultMap(map?: Record<string, string>) {
16+
if (!map) {
17+
return undefined
18+
}
19+
20+
if (map === MappingChars2String) {
21+
return MappingChars2String
22+
}
23+
24+
return {
25+
...MappingChars2String,
26+
...map,
27+
}
28+
}
29+
30+
function resolveSharedMap(options?: CreateOptions) {
31+
const { escape, unescape } = options ?? {}
32+
33+
if (isConfig<EscapeOptions>(escape) && escape.map) {
34+
return mergeWithDefaultMap(escape.map)!
35+
}
36+
37+
if (isConfig<UnescapeOptions>(unescape) && unescape.map) {
38+
return mergeWithDefaultMap(unescape.map)!
39+
}
40+
41+
return MappingChars2String
42+
}
43+
44+
export function resolveTransformers(options?: CreateOptions): Transformers {
45+
const sharedMap = resolveSharedMap(options)
46+
const escapeConfig = options?.escape
47+
const unescapeConfig = options?.unescape
48+
49+
const escapeEnabled = escapeConfig !== false
50+
const unescapeEnabled = unescapeConfig !== false
51+
52+
let escapeOptions: EscapeOptions | undefined
53+
if (escapeEnabled) {
54+
if (isConfig<EscapeOptions>(escapeConfig)) {
55+
escapeOptions = {
56+
...escapeConfig,
57+
map: mergeWithDefaultMap(escapeConfig.map) ?? sharedMap,
58+
}
59+
}
60+
else {
61+
escapeOptions = { map: sharedMap }
62+
}
63+
}
64+
65+
let unescapeOptions: UnescapeOptions | undefined
66+
if (unescapeEnabled) {
67+
if (isConfig<UnescapeOptions>(unescapeConfig)) {
68+
unescapeOptions = {
69+
...unescapeConfig,
70+
map: mergeWithDefaultMap(unescapeConfig.map) ?? sharedMap,
71+
}
72+
}
73+
else {
74+
unescapeOptions = { map: sharedMap }
75+
}
76+
}
77+
78+
const escapeFn = escapeEnabled
79+
? (value: string) => escapeSelectors(value, escapeOptions)
80+
: identity
81+
82+
const unescapeFn = unescapeEnabled
83+
? (value: string) => unescapeSelectors(value, unescapeOptions)
84+
: identity
85+
86+
return {
87+
escape: escapeFn,
88+
unescape: unescapeFn,
89+
}
90+
}
91+
92+
export {
93+
identity,
94+
}

packages/merge/src/cva.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import type { ClassValue, Config, Props } from './cva/index'
22
import type { CreateOptions } from './types'
33
import { cva as _cva } from 'class-variance-authority'
4-
import { resolveEscape } from './core/escape'
4+
import { resolveTransformers } from './core/transformers'
55

66
function create(options?: CreateOptions) {
7-
const escapeFn = resolveEscape(options)
7+
const transformers = resolveTransformers(options)
88
function cva<T>(base?: ClassValue, config?: Config<T>): (props?: Props<T> | undefined) => string {
99
const fn = _cva(base, config) as (...args: any[]) => string
1010
return (...props) => {
11-
return escapeFn(fn(...props))
11+
const value = fn(...props)
12+
const normalized = transformers.unescape(value)
13+
return transformers.escape(normalized)
1214
}
1315
}
1416
return {

packages/merge/src/types.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1+
import type { EscapeOptions, UnescapeOptions } from '@weapp-core/escape'
12
import type { ClassValue } from 'clsx'
23

3-
export type EscapeFn = (value: string) => string
4+
export type EscapeConfig = false | EscapeOptions
5+
6+
export type UnescapeConfig = false | UnescapeOptions
47

58
export interface CreateOptions {
6-
disableEscape?: boolean
7-
escapeFn?: EscapeFn
9+
escape?: EscapeConfig
10+
unescape?: UnescapeConfig
11+
}
12+
13+
export interface Transformers {
14+
escape: (value: string) => string
15+
unescape: (value: string) => string
816
}
917

1018
export type TailwindMergeLibraryFn = (...inputs: any[]) => string

packages/merge/src/v3.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,9 @@ export {
4141
export type {
4242
ClassValue,
4343
}
44+
45+
export type {
46+
CreateOptions,
47+
EscapeConfig,
48+
UnescapeConfig,
49+
} from './types'

packages/merge/src/v4.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,9 @@ export {
4141
export type {
4242
ClassValue,
4343
}
44+
45+
export type {
46+
CreateOptions,
47+
EscapeConfig,
48+
UnescapeConfig,
49+
} from './types'

packages/merge/src/variants.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import type {
2+
CnOptions,
3+
CnReturn,
4+
createTV as TailwindVariantsCreateTV,
5+
tv as TailwindVariantsTv,
6+
TV,
7+
TVConfig,
8+
TWMConfig,
9+
TWMergeConfig,
10+
} from 'tailwind-variants'
11+
import type { CreateOptions } from './types'
12+
import { defaultConfig, cnBase as tailwindVariantsCnBase } from 'tailwind-variants'
13+
// @ts-expect-error - upstream package does not ship type definitions for this path.
14+
import { c as createTailwindVariantsFactory } from 'tailwind-variants/dist/chunk-IFWU2MEM.js'
15+
import { resolveTransformers } from './core/transformers'
16+
import { create as createTailwindMergeRuntime } from './v4'
17+
18+
type TailwindVariantsCn = <T extends CnOptions>(...classes: T) => (config?: TWMConfig) => CnReturn
19+
20+
type TailwindVariantsFactory = (
21+
cn: TailwindVariantsCn,
22+
) => {
23+
tv: typeof TailwindVariantsTv
24+
createTV: typeof TailwindVariantsCreateTV
25+
}
26+
27+
function createVariantsRuntime(options?: CreateOptions) {
28+
const transformers = resolveTransformers(options)
29+
const tailwindMergeRuntime = createTailwindMergeRuntime(options)
30+
31+
let cachedConfig: TWMConfig['twMergeConfig']
32+
let cachedMergeFn = tailwindMergeRuntime.twMerge
33+
34+
const getMergeFn = (config?: TWMConfig['twMergeConfig']) => {
35+
if (!config) {
36+
return tailwindMergeRuntime.twMerge
37+
}
38+
39+
if (config === cachedConfig) {
40+
return cachedMergeFn
41+
}
42+
43+
cachedConfig = config
44+
cachedMergeFn = tailwindMergeRuntime.extendTailwindMerge(config)
45+
return cachedMergeFn
46+
}
47+
48+
const cn: TailwindVariantsCn = (...classes) => {
49+
const aggregated = tailwindVariantsCnBase(...classes)
50+
51+
return (config?: TWMConfig) => {
52+
if (aggregated == null) {
53+
return aggregated
54+
}
55+
56+
const shouldMerge = config?.twMerge ?? true
57+
if (!shouldMerge) {
58+
const normalized = transformers.unescape(aggregated)
59+
return transformers.escape(normalized)
60+
}
61+
62+
const mergeFn = getMergeFn(config?.twMergeConfig)
63+
return mergeFn(aggregated)
64+
}
65+
}
66+
67+
const factory = (createTailwindVariantsFactory as TailwindVariantsFactory)(cn)
68+
69+
const cnBase = (...classes: Parameters<typeof tailwindVariantsCnBase>) => {
70+
const aggregated = tailwindVariantsCnBase(...classes)
71+
if (aggregated == null) {
72+
return aggregated
73+
}
74+
75+
const normalized = transformers.unescape(aggregated)
76+
return transformers.escape(normalized)
77+
}
78+
79+
return {
80+
cnBase,
81+
cn,
82+
tv: factory.tv as TV,
83+
createTV: factory.createTV,
84+
}
85+
}
86+
87+
function create(options?: CreateOptions) {
88+
return createVariantsRuntime(options)
89+
}
90+
91+
const variantsRuntime = create()
92+
93+
const {
94+
cnBase,
95+
cn,
96+
tv,
97+
createTV,
98+
} = variantsRuntime
99+
100+
export {
101+
cn,
102+
cnBase,
103+
create,
104+
createTV,
105+
defaultConfig,
106+
tv,
107+
}
108+
109+
export type {
110+
CnOptions,
111+
CnReturn,
112+
TV,
113+
TVConfig,
114+
TWMConfig,
115+
TWMergeConfig,
116+
}

0 commit comments

Comments
 (0)