@@ -95,7 +95,7 @@ function buildUtilityMap(css, lookupTree) {
9595 let index = 0
9696 const utilityMap = { }
9797
98- lookupTree . walkRules ( rule => {
98+ function handle ( rule ) {
9999 const utilityNames = extractUtilityNames ( rule . selector )
100100
101101 utilityNames . forEach ( ( utilityName , i ) => {
@@ -113,27 +113,10 @@ function buildUtilityMap(css, lookupTree) {
113113 } )
114114 index ++
115115 } )
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+ }
125117
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 )
137120
138121 return utilityMap
139122}
@@ -203,68 +186,75 @@ function makeExtractUtilityRules(css, lookupTree, config) {
203186 }
204187}
205188
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+
206202function processApplyAtRules ( css , lookupTree , config ) {
207203 const extractUtilityRules = makeExtractUtilityRules ( css , lookupTree , config )
208204
209205 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+ }
212223
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 )
219228
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 )
226231
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 = [ ]
228234
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+ )
235240
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 )
257242
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+ )
259248
260- applyRule . remove ( )
261- rule . after ( mergedRules )
262- } )
249+ const mergedRules = mergeAdjacentRules ( nearestParentRule , nodes )
250+
251+ applyRule . remove ( )
252+ parent . after ( mergedRules )
263253
264254 // If the base rule has nothing in it (all applys were pseudo or responsive variants),
265255 // remove the rule fuggit.
266- if ( rule . nodes . length === 0 ) {
267- rule . remove ( )
256+ if ( parent . nodes . length === 0 ) {
257+ parent . remove ( )
268258 }
269259 } )
270260
0 commit comments