@@ -538,17 +538,24 @@ async function parseCss(
538
538
node . context = { }
539
539
}
540
540
541
- // Replace `@apply` rules with the actual utility classes.
542
- features |= substituteAtApply ( ast , designSystem )
543
-
544
- features |= substituteFunctions ( ast , designSystem . resolveThemeValue )
545
-
546
- // Remove `@utility`, we couldn't replace it before yet because we had to
547
- // handle the nested `@apply` at-rules first.
541
+ // Post-process `@utility` nodes.
542
+ //
543
+ // We have to do this as a separate step because if we replace the `@utility`
544
+ // nodes when we collect it in the system, then we can't substitute the
545
+ // `@apply` at-rules anymore.
546
+ //
547
+ // We also can't substitute the `@apply` at-rules while collecting the
548
+ // `@utility` rules, because if it relies on a utility that is defined later,
549
+ // then we wouldn't be able to resolve the applied utility.
550
+ //
551
+ // Lastly, we can't rely on the single `substituteAtApply` call at the end
552
+ // because this replaces `@apply`, but won't replace `@apply` if we injected
553
+ // an `@apply`.
548
554
walk ( ast , ( node , { replaceWith } ) => {
549
555
if ( node . kind !== 'at-rule' ) return
550
556
551
557
if ( node . name === '@utility' ) {
558
+ substituteAtApply ( node . nodes , designSystem )
552
559
replaceWith ( [ ] )
553
560
}
554
561
@@ -557,6 +564,11 @@ async function parseCss(
557
564
return WalkAction . Skip
558
565
} )
559
566
567
+ // Replace `@apply` rules with the actual utility classes.
568
+ features |= substituteAtApply ( ast , designSystem )
569
+
570
+ features |= substituteFunctions ( ast , designSystem . resolveThemeValue )
571
+
560
572
return {
561
573
designSystem,
562
574
ast,
0 commit comments