Skip to content

Commit a135bb2

Browse files
committed
perf: cache generated css selector analysis
1 parent b4bebe1 commit a135bb2

1 file changed

Lines changed: 66 additions & 10 deletions

File tree

packages/weapp-tailwindcss/src/bundlers/shared/generator-css/legacy-selectors.ts

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ import { VITE_MARKER_RE } from './markers'
55
const CLASS_SELECTOR_RE = /(?:^|[^\w-])\.[_a-z\u00A0-\uFFFF\\-]/i
66
const MINI_PROGRAM_THEME_SCOPE_SELECTORS = new Set([':host', 'page', '.tw-root', 'wx-root-portal-content'])
77
const SPECIFICITY_PLACEHOLDER_RE = /:not\(#(?:\\#|n)\)/g
8+
const SELECTOR_CACHE_LIMIT = 64
9+
const generatedSelectorCache = new Map<string, Set<string>>()
10+
11+
function setGeneratedSelectorCache(css: string, selectors: Set<string>) {
12+
if (generatedSelectorCache.size >= SELECTOR_CACHE_LIMIT) {
13+
const firstKey = generatedSelectorCache.keys().next().value
14+
if (firstKey !== undefined) {
15+
generatedSelectorCache.delete(firstKey)
16+
}
17+
}
18+
generatedSelectorCache.set(css, selectors)
19+
}
820

921
function normalizeCompatSelector(selector: string) {
1022
return selector
@@ -147,6 +159,11 @@ function isPseudoContentInitRule(rule: postcss.Rule) {
147159
}
148160

149161
export function collectGeneratedSelectors(css: string) {
162+
const cached = generatedSelectorCache.get(css)
163+
if (cached) {
164+
return cached
165+
}
166+
150167
const selectors = new Set<string>()
151168
try {
152169
const root = postcss.parse(css)
@@ -162,9 +179,42 @@ export function collectGeneratedSelectors(css: string) {
162179
catch {
163180
return selectors
164181
}
182+
setGeneratedSelectorCache(css, selectors)
165183
return selectors
166184
}
167185

186+
function collectGeneratedDeclarationPropsBySelector(generatedCss: string, selectors: Set<string>) {
187+
const propsBySelector = new Map<string, Set<string>>()
188+
try {
189+
const generatedRoot = postcss.parse(generatedCss)
190+
generatedRoot.walkRules((rule) => {
191+
const matchedSelectors = getRuleCompatSelectorKeys(rule).filter(selector => selectors.has(selector))
192+
if (matchedSelectors.length === 0) {
193+
return
194+
}
195+
const props = new Set<string>()
196+
rule.walkDecls((decl) => {
197+
props.add(decl.prop)
198+
})
199+
for (const selector of matchedSelectors) {
200+
const existing = propsBySelector.get(selector)
201+
if (existing) {
202+
for (const prop of props) {
203+
existing.add(prop)
204+
}
205+
}
206+
else {
207+
propsBySelector.set(selector, new Set(props))
208+
}
209+
}
210+
})
211+
}
212+
catch {
213+
return propsBySelector
214+
}
215+
return propsBySelector
216+
}
217+
168218
export function removeGeneratedSelectorCompatCss(css: string, generatedCss: string) {
169219
const generatedSelectors = collectGeneratedSelectors(generatedCss)
170220
if (generatedSelectors.size === 0) {
@@ -205,27 +255,33 @@ export function collectDedupedPostTransformCompatCss(css: string, generatedCss:
205255
if (generatedSelectors.size === 0) {
206256
return css
207257
}
258+
const generatedDeclarationPropsBySelector = collectGeneratedDeclarationPropsBySelector(generatedCss, generatedSelectors)
208259

209260
const preservedNodes: postcss.Node[] = []
210261
try {
211262
const root = postcss.parse(css)
212263
root.each((node) => {
213-
if (node.type === 'rule' && getRuleCompatSelectorKeys(node).some(selector => generatedSelectors.has(selector))) {
264+
if (node.type === 'rule') {
265+
const nodeSelectors = getRuleCompatSelectorKeys(node)
266+
const duplicated = nodeSelectors.some(selector => generatedSelectors.has(selector))
267+
if (!duplicated) {
268+
preservedNodes.push(node.clone())
269+
return
270+
}
214271
if (isCustomPropertyOnlyRule(node) && !isPseudoContentInitRule(node) && !hasUtilityClassSelector(node.selector)) {
215272
const declarationProps = new Set<string>()
216273
node.walkDecls((decl) => {
217274
declarationProps.add(decl.prop)
218275
})
219-
const generatedRoot = postcss.parse(generatedCss)
220-
generatedRoot.walkRules((rule) => {
221-
const nodeSelectors = new Set(getRuleCompatSelectorKeys(node))
222-
if (!getRuleCompatSelectorKeys(rule).some(selector => nodeSelectors.has(selector))) {
223-
return
276+
for (const selector of nodeSelectors) {
277+
const generatedProps = generatedDeclarationPropsBySelector.get(selector)
278+
if (!generatedProps) {
279+
continue
224280
}
225-
rule.walkDecls((decl) => {
226-
declarationProps.delete(decl.prop)
227-
})
228-
})
281+
for (const prop of generatedProps) {
282+
declarationProps.delete(prop)
283+
}
284+
}
229285
const nextRule = node.clone()
230286
nextRule.walkDecls((decl) => {
231287
if (!declarationProps.has(decl.prop)) {

0 commit comments

Comments
 (0)