@@ -5,6 +5,18 @@ import { VITE_MARKER_RE } from './markers'
55const CLASS_SELECTOR_RE = / (?: ^ | [ ^ \w - ] ) \. [ _ a - z \u00A0 - \uFFFF \\ - ] / i
66const MINI_PROGRAM_THEME_SCOPE_SELECTORS = new Set ( [ ':host' , 'page' , '.tw-root' , 'wx-root-portal-content' ] )
77const SPECIFICITY_PLACEHOLDER_RE = / : n o t \( # (?: \\ # | 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
921function normalizeCompatSelector ( selector : string ) {
1022 return selector
@@ -147,6 +159,11 @@ function isPseudoContentInitRule(rule: postcss.Rule) {
147159}
148160
149161export 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+
168218export 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