Skip to content

Commit 411e993

Browse files
committed
perf(weapp-tailwindcss): 复用类名判定与 js 后处理结果
1 parent c9acce7 commit 411e993

4 files changed

Lines changed: 80 additions & 5 deletions

File tree

packages/weapp-tailwindcss/src/js/babel.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,16 @@ export function jsHandler(rawSource: string, options: IJsHandlerOptions): JsHand
162162

163163
const result: JsHandlerResult = {
164164
code: ms.toString(),
165-
get map() {
166-
return ms.generateMap()
167-
},
165+
}
166+
167+
if (options.generateMap) {
168+
Object.defineProperty(result, 'map', {
169+
configurable: true,
170+
enumerable: true,
171+
get() {
172+
return ms.generateMap()
173+
},
174+
})
168175
}
169176

170177
if (options.moduleGraph && options.filename) {

packages/weapp-tailwindcss/src/shared/classname-transform.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import type { IJsHandlerOptions } from '../types'
22
import { replaceWxml } from '../wxml/shared'
33

44
export type ClassNameTransformDecision = 'direct' | 'escaped' | 'fallback' | 'skip'
5+
type EscapeMap = NonNullable<IJsHandlerOptions['escapeMap']>
6+
7+
const escapedCandidateCacheByEscapeMap = new WeakMap<EscapeMap, Map<string, string>>()
8+
const defaultEscapedCandidateCache = new Map<string, string>()
9+
const URL_LIKE_CANDIDATE_REGEXP = /^(?:https?:)?\/\//
510

611
/**
712
* 决策结果,附带已计算的 escaped 值以避免下游重复计算。
@@ -31,7 +36,7 @@ function isArbitraryValueCandidate(candidate: string) {
3136
}
3237

3338
// URL 片段中的 [] 不应作为任意值候选处理。
34-
if (/^(?:https?:)?\/\//.test(normalized)) {
39+
if (URL_LIKE_CANDIDATE_REGEXP.test(normalized)) {
3540
return false
3641
}
3742

@@ -61,6 +66,30 @@ const SKIP_RESULT: ClassNameTransformResult = { decision: 'skip' }
6166
const DIRECT_RESULT: ClassNameTransformResult = { decision: 'direct' }
6267
const FALLBACK_RESULT: ClassNameTransformResult = { decision: 'fallback' }
6368

69+
function getEscapedCandidate(candidate: string, escapeMap?: EscapeMap) {
70+
if (!escapeMap) {
71+
let cached = defaultEscapedCandidateCache.get(candidate)
72+
if (cached === undefined) {
73+
cached = replaceWxml(candidate, { escapeMap })
74+
defaultEscapedCandidateCache.set(candidate, cached)
75+
}
76+
return cached
77+
}
78+
79+
let store = escapedCandidateCacheByEscapeMap.get(escapeMap)
80+
if (!store) {
81+
store = new Map<string, string>()
82+
escapedCandidateCacheByEscapeMap.set(escapeMap, store)
83+
}
84+
85+
let cached = store.get(candidate)
86+
if (cached === undefined) {
87+
cached = replaceWxml(candidate, { escapeMap })
88+
store.set(candidate, cached)
89+
}
90+
return cached
91+
}
92+
6493
/**
6594
* JS 转译严格遵循 runtime class set:
6695
* 1. 直接命中 classNameSet 原始值;
@@ -94,7 +123,7 @@ export function resolveClassNameTransformWithResult(
94123
}
95124

96125
if (classNameSet && classNameSet.size > 0) {
97-
const escapedCandidate = replaceWxml(candidate, { escapeMap })
126+
const escapedCandidate = getEscapedCandidate(candidate, escapeMap)
98127
if (escapedCandidate !== candidate && classNameSet.has(escapedCandidate)) {
99128
return { decision: 'escaped', escapedValue: escapedCandidate }
100129
}

packages/weapp-tailwindcss/test/js/babel-utils.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,16 @@ describe('babel helpers additional coverage', () => {
103103
expect(handled.error).toBeUndefined()
104104
expect(handled.map).toBeDefined()
105105
})
106+
107+
it('does not define a source map getter unless generateMap is enabled', () => {
108+
const handled = babel.jsHandler('const cls = "w-[100px]"', {
109+
classNameSet: new Set(['w-[100px]']),
110+
escapeMap: MappingChars2String,
111+
alwaysEscape: true,
112+
})
113+
114+
expect(handled.code).toContain('w-_b100px_B')
115+
expect(Object.hasOwn(handled, 'map')).toBe(false)
116+
expect(handled.map).toBeUndefined()
117+
})
106118
})
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { MappingChars2String } from '@weapp-core/escape'
2+
import { describe, expect, it, vi } from 'vitest'
3+
import * as classNameTransform from '@/shared/classname-transform'
4+
import * as wxmlShared from '@/wxml/shared'
5+
6+
describe('classname transform caching', () => {
7+
it('reuses escaped candidate results for repeated lookups', () => {
8+
const spy = vi.spyOn(wxmlShared, 'replaceWxml')
9+
const classNameSet = new Set(['bg-_b_h123456_B'])
10+
11+
const first = classNameTransform.resolveClassNameTransformWithResult('bg-[#123456]', {
12+
classNameSet,
13+
escapeMap: MappingChars2String,
14+
})
15+
const second = classNameTransform.resolveClassNameTransformWithResult('bg-[#123456]', {
16+
classNameSet,
17+
escapeMap: MappingChars2String,
18+
})
19+
20+
expect(first).toEqual({
21+
decision: 'escaped',
22+
escapedValue: 'bg-_b_h123456_B',
23+
})
24+
expect(second).toEqual(first)
25+
expect(spy).toHaveBeenCalledTimes(1)
26+
})
27+
})

0 commit comments

Comments
 (0)