@@ -95,7 +95,7 @@ function buildUtilityMap(css, lookupTree) {
95
95
let index = 0
96
96
const utilityMap = { }
97
97
98
- lookupTree . walkRules ( rule => {
98
+ function handle ( rule ) {
99
99
const utilityNames = extractUtilityNames ( rule . selector )
100
100
101
101
utilityNames . forEach ( ( utilityName , i ) => {
@@ -113,27 +113,10 @@ function buildUtilityMap(css, lookupTree) {
113
113
} )
114
114
index ++
115
115
} )
116
- } )
117
-
118
- css . walkRules ( rule => {
119
- const utilityNames = extractUtilityNames ( rule . selector )
120
-
121
- utilityNames . forEach ( ( utilityName , i ) => {
122
- if ( utilityMap [ utilityName ] === undefined ) {
123
- utilityMap [ utilityName ] = [ ]
124
- }
116
+ }
125
117
126
- utilityMap [ utilityName ] . push ( {
127
- index,
128
- utilityName,
129
- classPosition : i ,
130
- get rule ( ) {
131
- return cloneRuleWithParent ( rule )
132
- } ,
133
- } )
134
- index ++
135
- } )
136
- } )
118
+ lookupTree . walkRules ( handle )
119
+ css . walkRules ( handle )
137
120
138
121
return utilityMap
139
122
}
@@ -203,68 +186,75 @@ function makeExtractUtilityRules(css, lookupTree, config) {
203
186
}
204
187
}
205
188
189
+ function findParent ( rule , predicate ) {
190
+ let parent = rule . parent
191
+ while ( parent ) {
192
+ if ( predicate ( parent ) ) {
193
+ return parent
194
+ }
195
+
196
+ parent = parent . parent
197
+ }
198
+
199
+ throw new Error ( 'No parent could be found' )
200
+ }
201
+
206
202
function processApplyAtRules ( css , lookupTree , config ) {
207
203
const extractUtilityRules = makeExtractUtilityRules ( css , lookupTree , config )
208
204
209
205
do {
210
- css . walkRules ( rule => {
211
- const applyRules = [ ]
206
+ css . walkAtRules ( 'apply' , applyRule => {
207
+ const parent = applyRule . parent // Direct parent
208
+ const nearestParentRule = findParent ( applyRule , r => r . type === 'rule' )
209
+ const currentUtilityNames = extractUtilityNames ( nearestParentRule . selector )
210
+
211
+ const [
212
+ importantEntries ,
213
+ applyUtilityNames ,
214
+ important = importantEntries . length > 0 ,
215
+ ] = _ . partition ( applyRule . params . split ( / [ \s \t \n ] + / g) , n => n === '!important' )
216
+
217
+ if ( _ . intersection ( applyUtilityNames , currentUtilityNames ) . length > 0 ) {
218
+ const currentUtilityName = _ . intersection ( applyUtilityNames , currentUtilityNames ) [ 0 ]
219
+ throw parent . error (
220
+ `You cannot \`@apply\` the \`${ currentUtilityName } \` utility here because it creates a circular dependency.`
221
+ )
222
+ }
212
223
213
- // Only walk direct children to avoid issues with nesting plugins
214
- rule . each ( child => {
215
- if ( child . type === 'atrule' && child . name === 'apply' ) {
216
- applyRules . unshift ( child )
217
- }
218
- } )
224
+ // Extract any post-apply declarations and re-insert them after apply rules
225
+ const afterRule = parent . clone ( { raws : { } } )
226
+ afterRule . nodes = afterRule . nodes . slice ( parent . index ( applyRule ) + 1 )
227
+ parent . nodes = parent . nodes . slice ( 0 , parent . index ( applyRule ) + 1 )
219
228
220
- applyRules . forEach ( applyRule => {
221
- const [
222
- importantEntries ,
223
- applyUtilityNames ,
224
- important = importantEntries . length > 0 ,
225
- ] = _ . partition ( applyRule . params . split ( / [ \s \t \n ] + / g) , n => n === '!important' )
229
+ // Sort applys to match CSS source order
230
+ const applys = extractUtilityRules ( applyUtilityNames , applyRule )
226
231
227
- const currentUtilityNames = extractUtilityNames ( rule . selector )
232
+ // Get new rules with the utility portion of the selector replaced with the new selector
233
+ const rulesToInsert = [ ]
228
234
229
- if ( _ . intersection ( applyUtilityNames , currentUtilityNames ) . length > 0 ) {
230
- const currentUtilityName = _ . intersection ( applyUtilityNames , currentUtilityNames ) [ 0 ]
231
- throw rule . error (
232
- `You cannot \`@apply\` the \`${ currentUtilityName } \` utility here because it creates a circular dependency.`
233
- )
234
- }
235
+ applys . forEach (
236
+ nearestParentRule === parent
237
+ ? util => rulesToInsert . push ( generateRulesFromApply ( util , parent . selectors ) )
238
+ : util => util . rule . nodes . forEach ( n => afterRule . append ( n . clone ( ) ) )
239
+ )
235
240
236
- // Extract any post-apply declarations and re-insert them after apply rules
237
- const afterRule = rule . clone ( { raws : { } } )
238
- afterRule . nodes = afterRule . nodes . slice ( rule . index ( applyRule ) + 1 )
239
- rule . nodes = rule . nodes . slice ( 0 , rule . index ( applyRule ) + 1 )
240
-
241
- // Sort applys to match CSS source order
242
- const applys = extractUtilityRules ( applyUtilityNames , applyRule )
243
-
244
- // Get new rules with the utility portion of the selector replaced with the new selector
245
- const rulesToInsert = [
246
- ...applys . map ( applyUtility => {
247
- return generateRulesFromApply ( applyUtility , rule . selectors )
248
- } ) ,
249
- afterRule ,
250
- ]
251
-
252
- const { nodes } = _ . tap ( postcss . root ( { nodes : rulesToInsert } ) , root =>
253
- root . walkDecls ( d => {
254
- d . important = important
255
- } )
256
- )
241
+ rulesToInsert . push ( afterRule )
257
242
258
- const mergedRules = mergeAdjacentRules ( rule , nodes )
243
+ const { nodes } = _ . tap ( postcss . root ( { nodes : rulesToInsert } ) , root =>
244
+ root . walkDecls ( d => {
245
+ d . important = important
246
+ } )
247
+ )
259
248
260
- applyRule . remove ( )
261
- rule . after ( mergedRules )
262
- } )
249
+ const mergedRules = mergeAdjacentRules ( nearestParentRule , nodes )
250
+
251
+ applyRule . remove ( )
252
+ parent . after ( mergedRules )
263
253
264
254
// If the base rule has nothing in it (all applys were pseudo or responsive variants),
265
255
// remove the rule fuggit.
266
- if ( rule . nodes . length === 0 ) {
267
- rule . remove ( )
256
+ if ( parent . nodes . length === 0 ) {
257
+ parent . remove ( )
268
258
}
269
259
} )
270
260
0 commit comments