Skip to content

Commit aceef73

Browse files
committed
fix: speed up tailwind v3 generator reuse
1 parent 1278ba9 commit aceef73

5 files changed

Lines changed: 88 additions & 9 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"weapp-tailwindcss": patch
3+
---
4+
5+
修复 Tailwind CSS v3 生成器在 uni-app Vite 热更新中重复清理 Tailwind require cache 导致 wxss 生成缓存失效、增量编译明显变慢的问题。现在 v3 生成器会复用运行时 patch 初始化结果,并在每次生成前主动重置 Tailwind v3 plugin 上下文,避免旧 class 泄漏。

packages/weapp-tailwindcss/src/tailwindcss/v3-engine/generator.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ interface TailwindcssPlugin {
2020
}
2121
}
2222

23+
const runtimeReadyPromiseCache = new Map<string, Promise<void>>()
24+
2325
interface LegacyContentObject {
2426
files?: unknown
2527
relative?: boolean
@@ -109,6 +111,22 @@ function loadTailwindcssPlugin(source: TailwindV3ResolvedSource): TailwindcssPlu
109111
return typeof plugin === 'function' ? plugin : plugin.default as TailwindcssPlugin
110112
}
111113

114+
function createRuntimeReadyCacheKey(source: TailwindV3ResolvedSource, rootPath: string | undefined) {
115+
return [
116+
source.packageName,
117+
source.postcssPlugin,
118+
rootPath ?? 'missing',
119+
source.config ?? 'config:missing',
120+
source.cwd,
121+
].join('\0')
122+
}
123+
124+
function resetTailwindcssPluginContext(plugin: TailwindcssPlugin) {
125+
if (plugin.contextRef) {
126+
plugin.contextRef.value = []
127+
}
128+
}
129+
112130
function collectClassSet(plugin: TailwindcssPlugin) {
113131
const classSet = new Set<string>()
114132
for (const context of plugin.contextRef?.value ?? []) {
@@ -144,9 +162,20 @@ function createRuntimeReadyPromise(source: TailwindV3ResolvedSource) {
144162
version: 3,
145163
},
146164
})
147-
return ensureTailwindcssRuntimePatch(patcher, {
165+
const cacheKey = createRuntimeReadyCacheKey(source, patcher.packageInfo?.rootPath)
166+
const cached = runtimeReadyPromiseCache.get(cacheKey)
167+
if (cached) {
168+
return cached
169+
}
170+
171+
const task = ensureTailwindcssRuntimePatch(patcher, {
148172
clearRequireCache: true,
173+
}).catch((error) => {
174+
runtimeReadyPromiseCache.delete(cacheKey)
175+
throw error
149176
})
177+
runtimeReadyPromiseCache.set(cacheKey, task)
178+
return task
150179
}
151180

152181
export function createTailwindV3Engine(source: TailwindV3ResolvedSource): TailwindV3Engine {
@@ -160,6 +189,7 @@ export function createTailwindV3Engine(source: TailwindV3ResolvedSource): Tailwi
160189
target = 'weapp',
161190
} = options
162191
const tailwindcss = loadTailwindcssPlugin(source)
192+
resetTailwindcssPluginContext(tailwindcss)
163193
const tailwindConfig = createTailwindConfig(source, options)
164194
const result = await postcss([
165195
tailwindcss(tailwindConfig),

packages/weapp-tailwindcss/test/tailwindcss/v3-engine.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
1+
import path from 'node:path'
12
import { createTailwindV3Engine, resolveTailwindV3Source, transformTailwindV3CssToWeapp } from '@/tailwindcss/v3-engine'
3+
import { TailwindcssPatcher } from 'tailwindcss-patch'
24
import plugin from 'tailwindcss/plugin'
35

46
function compactCss(css: string) {
57
return css.replace(/\s+/g, '')
68
}
79

810
describe('tailwindcss v3 engine', () => {
11+
it('reuses runtime patch setup for repeated engines with the same source', async () => {
12+
const source = await resolveTailwindV3Source({
13+
css: '@tailwind utilities;',
14+
base: path.resolve(process.cwd(), 'demo/uni-app-vite-tailwindcss-v3'),
15+
config: undefined,
16+
})
17+
const patchSpy = vi.spyOn(TailwindcssPatcher.prototype, 'patch')
18+
19+
const first = createTailwindV3Engine(source)
20+
const second = createTailwindV3Engine(source)
21+
22+
try {
23+
await first.generate({ candidates: ['bg-blue-500'] })
24+
await second.generate({ candidates: ['bg-[#123455]'] })
25+
26+
expect(patchSpy).toHaveBeenCalledTimes(1)
27+
}
28+
finally {
29+
patchSpy.mockRestore()
30+
}
31+
})
32+
933
it('removes browser preflight while keeping utility variables for mini-program output', async () => {
1034
const source = await resolveTailwindV3Source({
1135
css: '@tailwind base; @tailwind utilities;',
@@ -162,6 +186,28 @@ describe('tailwindcss v3 engine', () => {
162186
expect(result.css).toContain('.w-4')
163187
})
164188

189+
it('does not leak class cache between repeated generations', async () => {
190+
const source = await resolveTailwindV3Source({
191+
css: '@tailwind utilities;',
192+
base: process.cwd(),
193+
config: undefined,
194+
})
195+
const engine = createTailwindV3Engine(source)
196+
197+
const first = await engine.generate({
198+
candidates: ['bg-blue-500'],
199+
})
200+
const second = await engine.generate({
201+
candidates: ['bg-[#123455]'],
202+
})
203+
204+
expect(first.css).toContain('.bg-blue-500')
205+
expect(first.classSet).toEqual(new Set(['bg-blue-500']))
206+
expect(second.css).toContain('.bg-_b_h123455_B')
207+
expect(second.css).not.toContain('.bg-blue-500')
208+
expect(second.classSet).toEqual(new Set(['bg-[#123455]']))
209+
})
210+
165211
it('normalizes default export configs before generating plugin components', async () => {
166212
const source = await resolveTailwindV3Source({
167213
css: '@tailwind components;',

packages/weapp-tailwindcss/test/watch-hmr-regression.unit.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -935,7 +935,7 @@ describe('watch-hmr regression cases', () => {
935935
expect(contentMutation?.mutate(
936936
[
937937
'const bgObj = ref({',
938-
' \'bg-[#999999]\':true',
938+
' \'bg-[#232322]\':true',
939939
'})',
940940
].join('\n'),
941941
payload,
@@ -1063,6 +1063,7 @@ describe('watch-hmr regression cases', () => {
10631063
path.resolve('/repo', 'demo/uni-app-vite-tailwindcss-v3/dist/build/mp-weixin/pages/index/index.wxss'),
10641064
path.resolve('/repo', 'demo/uni-app-vite-tailwindcss-v3/dist/build/mp-weixin/app.wxss'),
10651065
])
1066+
expect(uniViteCase?.maxPluginProcessMs).toBe(5000)
10661067

10671068
expect(mpxV4Case?.outputWxml).toBe(
10681069
path.resolve('/repo', 'demo/mpx-tailwindcss-v4/dist/wx/custom-tab-bar/index.wxml'),

tools/weapp-tailwindcss-scripts/src/watch-hmr-regression/cases/demo/extended.ts

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,10 @@ function mutateUniAppViteV3BgObjKey(
6161
source: string,
6262
payload: Parameters<NonNullable<WatchCase['contentMutation']>['mutate']>[1],
6363
) {
64-
const anchors = [
65-
'\'bg-[#999999]\':true',
66-
'\'bg-[#4268EA]\':true',
67-
'\'bg-[red]\':true',
68-
]
69-
const anchor = anchors.find(candidate => source.includes(candidate))
64+
const bgObjMatch = /const\s+bgObj\s*=\s*ref\s*\(\s*\{[\s\S]*?\}\s*\)/.exec(source)
65+
const anchor = /(['"]bg-\[[^\r\n'"]+\]['"]\s*:\s*true)/.exec(bgObjMatch?.[0] ?? '')?.[1]
7066
if (!anchor) {
71-
throw new Error(`uni-app vite v3 bgObj key anchor not found: ${anchors.join(' | ')}`)
67+
throw new Error('uni-app vite v3 bgObj arbitrary bg key anchor not found')
7268
}
7369
return mutateVueScriptSetupObjectKeyByAnchor(source, anchor, payload)
7470
}
@@ -144,6 +140,7 @@ export function buildDemoExtendedCases(baseCwd: string): WatchCase[] {
144140
project: 'demo/uni-app-vite-tailwindcss-v3',
145141
group: 'demo',
146142
requireStableGlobalStyleOnSameClassLiteral: false,
143+
maxPluginProcessMs: 5000,
147144
cwd: path.resolve(baseCwd, 'demo/uni-app-vite-tailwindcss-v3'),
148145
devScript: 'dev:e2e-watch',
149146
outputWxml: path.resolve(baseCwd, 'demo/uni-app-vite-tailwindcss-v3/dist/build/mp-weixin/pages/index/index.wxml'),

0 commit comments

Comments
 (0)