@@ -76,16 +76,17 @@ async function parseCss(
76
76
77
77
await substituteAtImports ( ast , base , loadStylesheet )
78
78
79
- // Find all `@theme` declarations
80
79
let important : boolean | null = null
81
80
let theme = new Theme ( )
82
81
let customVariants : ( ( designSystem : DesignSystem ) => void ) [ ] = [ ]
83
82
let customUtilities : ( ( designSystem : DesignSystem ) => void ) [ ] = [ ]
84
83
let firstThemeRule : Rule | null = null
85
84
let globs : { base : string ; pattern : string } [ ] = [ ]
86
85
86
+ // Handle at-rules
87
87
walk ( ast , ( node , { parent, replaceWith, context } ) => {
88
88
if ( node . kind !== 'rule' ) return
89
+ if ( node . selector [ 0 ] !== '@' ) return
89
90
90
91
// Collect custom `@utility` at-rules
91
92
if ( node . selector . startsWith ( '@utility ' ) ) {
@@ -196,124 +197,117 @@ async function parseCss(
196
197
}
197
198
}
198
199
199
- // Drop instances of `@media theme(…)`
200
- //
201
- // We support `@import "tailwindcss/theme" theme(reference)` as a way to
202
- // import an external theme file as a reference, which becomes `@media
203
- // theme(reference) { … }` when the `@import` is processed.
204
- if ( node . selector . startsWith ( '@media theme(' ) ) {
205
- let themeParams = node . selector . slice ( 13 , - 1 )
200
+ if ( node . selector . startsWith ( '@media ' ) ) {
201
+ let params = segment ( node . selector . slice ( 7 ) , ' ' )
202
+ let unknownParams : string [ ] = [ ]
206
203
207
- walk ( node . nodes , ( child ) => {
208
- if ( child . kind !== 'rule' ) {
209
- throw new Error (
210
- 'Files imported with `@import "…" theme(…)` must only contain `@theme` blocks.' ,
211
- )
212
- }
213
- if ( child . selector === '@theme' || child . selector . startsWith ( '@theme ' ) ) {
214
- child . selector += ' ' + themeParams
215
- return WalkAction . Skip
204
+ for ( let param of params ) {
205
+ // Handle `@media theme(…)`
206
+ //
207
+ // We support `@import "tailwindcss/theme" theme(reference)` as a way to
208
+ // import an external theme file as a reference, which becomes `@media
209
+ // theme(reference) { … }` when the `@import` is processed.
210
+ if ( param . startsWith ( 'theme(' ) ) {
211
+ let themeParams = param . slice ( 6 , - 1 )
212
+
213
+ walk ( node . nodes , ( child ) => {
214
+ if ( child . kind !== 'rule' ) {
215
+ throw new Error (
216
+ 'Files imported with `@import "…" theme(…)` must only contain `@theme` blocks.' ,
217
+ )
218
+ }
219
+ if ( child . selector === '@theme' || child . selector . startsWith ( '@theme ' ) ) {
220
+ child . selector += ' ' + themeParams
221
+ return WalkAction . Skip
222
+ }
223
+ } )
216
224
}
217
- } )
218
- replaceWith ( node . nodes )
219
- return WalkAction . Skip
220
- }
221
225
222
- // Drop instances of `@media prefix(…)`
223
- //
224
- // We support `@import "tailwindcss" prefix(ident)` as a way to
225
- // configure a theme prefix for variables and utilities.
226
- if ( node . selector . startsWith ( '@media prefix(' ) ) {
227
- let themeParams = node . selector . slice ( 7 )
228
-
229
- walk ( node . nodes , ( child ) => {
230
- if ( child . kind !== 'rule' ) return
231
- if ( child . selector === '@theme' || child . selector . startsWith ( '@theme ' ) ) {
232
- child . selector += ' ' + themeParams
233
- return WalkAction . Skip
226
+ // Handle `@media prefix(…)`
227
+ //
228
+ // We support `@import "tailwindcss" prefix(ident)` as a way to
229
+ // configure a theme prefix for variables and utilities.
230
+ else if ( param . startsWith ( 'prefix(' ) ) {
231
+ let prefix = param . slice ( 7 , - 1 )
232
+
233
+ walk ( node . nodes , ( child ) => {
234
+ if ( child . kind !== 'rule' ) return
235
+ if ( child . selector === '@theme' || child . selector . startsWith ( '@theme ' ) ) {
236
+ child . selector += ` prefix(${ prefix } )`
237
+ return WalkAction . Skip
238
+ }
239
+ } )
234
240
}
235
- } )
236
- replaceWith ( node . nodes )
237
- return WalkAction . Skip
238
- }
239
-
240
- if ( node . selector . startsWith ( '@media' ) ) {
241
- let features = segment ( node . selector . slice ( 6 ) , ' ' )
242
- let shouldReplace = true
243
241
244
- for ( let i = 0 ; i < features . length ; i ++ ) {
245
- let part = features [ i ]
242
+ // Handle important
243
+ else if ( param === 'important' ) {
244
+ important = true
245
+ }
246
246
247
- // Drop instances of `@media important`
248
247
//
249
- // We support `@import "tailwindcss" important` to mark all declarations
250
- // in generated utilities as `!important`.
251
- if ( part === 'important' ) {
252
- important = true
253
- shouldReplace = true
254
- features [ i ] = ''
248
+ else {
249
+ unknownParams . push ( param )
255
250
}
256
251
}
257
252
258
- let remaining = features . filter ( Boolean ) . join ( ' ' )
259
-
260
- node . selector = `@media ${ remaining } `
261
-
262
- if ( remaining . trim ( ) === '' && shouldReplace ) {
253
+ if ( unknownParams . length > 0 ) {
254
+ node . selector = `@media ${ unknownParams . join ( ' ' ) } `
255
+ } else if ( params . length > 0 ) {
263
256
replaceWith ( node . nodes )
264
257
}
265
258
266
259
return WalkAction . Skip
267
260
}
268
261
269
- if ( node . selector !== '@theme' && ! node . selector . startsWith ( '@theme ' ) ) return
262
+ // Handle `@theme`
263
+ if ( node . selector === '@theme' || node . selector . startsWith ( '@theme ' ) ) {
264
+ let [ themeOptions , themePrefix ] = parseThemeOptions ( node . selector )
270
265
271
- let [ themeOptions , themePrefix ] = parseThemeOptions ( node . selector )
266
+ if ( themePrefix ) {
267
+ if ( ! IS_VALID_PREFIX . test ( themePrefix ) ) {
268
+ throw new Error (
269
+ `The prefix "${ themePrefix } " is invalid. Prefixes must be lowercase ASCII letters (a-z) only.` ,
270
+ )
271
+ }
272
272
273
- if ( themePrefix ) {
274
- if ( ! IS_VALID_PREFIX . test ( themePrefix ) ) {
275
- throw new Error (
276
- `The prefix "${ themePrefix } " is invalid. Prefixes must be lowercase ASCII letters (a-z) only.` ,
277
- )
273
+ theme . prefix = themePrefix
278
274
}
279
275
280
- theme . prefix = themePrefix
281
- }
282
-
283
- // Record all custom properties in the `@theme` declaration
284
- walk ( node . nodes , ( child , { replaceWith } ) => {
285
- // Collect `@keyframes` rules to re-insert with theme variables later,
286
- // since the `@theme` rule itself will be removed.
287
- if ( child . kind === 'rule' && child . selector . startsWith ( '@keyframes ' ) ) {
288
- theme . addKeyframes ( child )
289
- replaceWith ( [ ] )
290
- return WalkAction . Skip
291
- }
276
+ // Record all custom properties in the `@theme` declaration
277
+ walk ( node . nodes , ( child , { replaceWith } ) => {
278
+ // Collect `@keyframes` rules to re-insert with theme variables later,
279
+ // since the `@theme` rule itself will be removed.
280
+ if ( child . kind === 'rule' && child . selector . startsWith ( '@keyframes ' ) ) {
281
+ theme . addKeyframes ( child )
282
+ replaceWith ( [ ] )
283
+ return WalkAction . Skip
284
+ }
292
285
293
- if ( child . kind === 'comment' ) return
294
- if ( child . kind === 'declaration' && child . property . startsWith ( '--' ) ) {
295
- theme . add ( child . property , child . value ?? '' , themeOptions )
296
- return
297
- }
286
+ if ( child . kind === 'comment' ) return
287
+ if ( child . kind === 'declaration' && child . property . startsWith ( '--' ) ) {
288
+ theme . add ( child . property , child . value ?? '' , themeOptions )
289
+ return
290
+ }
298
291
299
- let snippet = toCss ( [ rule ( node . selector , [ child ] ) ] )
300
- . split ( '\n' )
301
- . map ( ( line , idx , all ) => `${ idx === 0 || idx >= all . length - 2 ? ' ' : '>' } ${ line } ` )
302
- . join ( '\n' )
292
+ let snippet = toCss ( [ rule ( node . selector , [ child ] ) ] )
293
+ . split ( '\n' )
294
+ . map ( ( line , idx , all ) => `${ idx === 0 || idx >= all . length - 2 ? ' ' : '>' } ${ line } ` )
295
+ . join ( '\n' )
303
296
304
- throw new Error (
305
- `\`@theme\` blocks must only contain custom properties or \`@keyframes\`.\n\n${ snippet } ` ,
306
- )
307
- } )
297
+ throw new Error (
298
+ `\`@theme\` blocks must only contain custom properties or \`@keyframes\`.\n\n${ snippet } ` ,
299
+ )
300
+ } )
308
301
309
- // Keep a reference to the first `@theme` rule to update with the full theme
310
- // later, and delete any other `@theme` rules.
311
- if ( ! firstThemeRule && ! ( themeOptions & ThemeOptions . REFERENCE ) ) {
312
- firstThemeRule = node
313
- } else {
314
- replaceWith ( [ ] )
302
+ // Keep a reference to the first `@theme` rule to update with the full
303
+ // theme later, and delete any other `@theme` rules.
304
+ if ( ! firstThemeRule && ! ( themeOptions & ThemeOptions . REFERENCE ) ) {
305
+ firstThemeRule = node
306
+ } else {
307
+ replaceWith ( [ ] )
308
+ }
309
+ return WalkAction . Skip
315
310
}
316
- return WalkAction . Skip
317
311
} )
318
312
319
313
let designSystem = buildDesignSystem ( theme )
0 commit comments