@@ -80,21 +80,27 @@ export default function tailwindcss(): Plugin[] {
80
80
81
81
constructor ( private id : string ) { }
82
82
83
- public async generate ( content : string , addWatchFile : ( file : string ) => void ) {
83
+ // Generate the CSS for the root file. This can return false if the file is
84
+ // not considered a Tailwind root. When this happened, the root can be GCed.
85
+ public async generate (
86
+ content : string ,
87
+ addWatchFile : ( file : string ) => void ,
88
+ ) : Promise < string | false > {
84
89
await import ( '@tailwindcss/node/esm-cache-hook' )
85
90
86
91
this . lastContent = content
87
92
88
93
let inputPath = idToPath ( this . id )
89
94
let inputBase = path . dirname ( path . resolve ( inputPath ) )
90
95
91
- if ( this . compiler === null || this . scanner === null || this . rebuildStrategy === 'full' ) {
96
+ let compiler = this . compiler
97
+ let scanner = this . scanner
98
+
99
+ if ( compiler === null || scanner === null || this . rebuildStrategy === 'full' ) {
92
100
this . rebuildStrategy = 'incremental'
93
101
clearRequireCache ( Array . from ( this . dependencies ) )
94
102
this . dependencies = new Set ( [ idToPath ( inputPath ) ] )
95
103
96
- // TODO: Move this out of here, we need to check wether something is a
97
- // valid root on the flattened file
98
104
let postcssCompiled = await postcss ( [
99
105
postcssImport ( {
100
106
load : ( path ) => {
@@ -110,7 +116,15 @@ export default function tailwindcss(): Plugin[] {
110
116
} )
111
117
let css = postcssCompiled . css
112
118
113
- this . compiler = await compile ( css , {
119
+ // This is done inside the Root#generate() method so that we can later
120
+ // use information from the Tailwind compiler to determine if the file
121
+ // is a CSS root (necessary because we will probably inline the
122
+ // `@import` resolution at some point).
123
+ if ( ! isCssRootFile ( css ) ) {
124
+ return false
125
+ }
126
+
127
+ compiler = await compile ( css , {
114
128
loadPlugin : async ( pluginPath ) => {
115
129
if ( pluginPath [ 0 ] !== '.' ) {
116
130
return import ( pluginPath ) . then ( ( m ) => m . default ?? m )
@@ -150,17 +164,20 @@ export default function tailwindcss(): Plugin[] {
150
164
return module . default ?? module
151
165
} ,
152
166
} )
167
+ this . compiler = compiler
153
168
154
- this . scanner = new Scanner ( {
169
+ scanner = new Scanner ( {
155
170
sources : this . compiler . globs . map ( ( pattern ) => ( {
156
171
base : inputBase , // Globs are relative to the input.css file
157
172
pattern,
158
173
} ) ) ,
159
174
} )
175
+ this . scanner
160
176
}
161
177
162
178
if ( ! this . scanner || ! this . compiler ) {
163
- // TODO: This is only here for TypeScript
179
+ // TypeScript does not properly refine the scanner and compiler
180
+ // properties (even when extracted into a variable)
164
181
throw new Error ( 'Tailwind CSS compiler is not initialized.' )
165
182
}
166
183
@@ -253,7 +270,11 @@ export default function tailwindcss(): Plugin[] {
253
270
254
271
async function regenerateOptimizedCss ( root : Root , addWatchFile : ( file : string ) => void ) {
255
272
let content = root . lastContent
256
- return optimizeCss ( await root . generate ( content , addWatchFile ) , { minify } )
273
+ let generated = await root . generate ( content , addWatchFile )
274
+ if ( generated === false ) {
275
+ return
276
+ }
277
+ return optimizeCss ( generated , { minify } )
257
278
}
258
279
259
280
// Manually run the transform functions of non-Tailwind plugins on the given CSS
@@ -337,7 +358,7 @@ export default function tailwindcss(): Plugin[] {
337
358
enforce : 'pre' ,
338
359
339
360
async transform ( src , id , options ) {
340
- if ( ! isTailwindCssFile ( id , src ) ) return
361
+ if ( ! isPotentialCssRootFile ( id ) ) return
341
362
342
363
let root = roots . get ( id )
343
364
if ( ! root ) {
@@ -358,9 +379,13 @@ export default function tailwindcss(): Plugin[] {
358
379
}
359
380
360
381
// TODO: This had a call to `transformWithPlugins`. Since we're now in
361
- // pre, we might not need it
362
- let code = await root . generate ( src , ( file ) => this . addWatchFile ( file ) )
363
- return { code }
382
+ // pre, we might not need it. Validate this.
383
+ let generated = await root . generate ( src , ( file ) => this . addWatchFile ( file ) )
384
+ if ( ! generated ) {
385
+ roots . delete ( id )
386
+ return src
387
+ }
388
+ return { code : generated }
364
389
} ,
365
390
} ,
366
391
@@ -371,7 +396,7 @@ export default function tailwindcss(): Plugin[] {
371
396
enforce : 'pre' ,
372
397
373
398
async transform ( src , id ) {
374
- if ( ! isTailwindCssFile ( id , src ) ) return
399
+ if ( ! isPotentialCssRootFile ( id ) ) return
375
400
376
401
let root = roots . get ( id )
377
402
if ( ! root ) {
@@ -387,21 +412,29 @@ export default function tailwindcss(): Plugin[] {
387
412
// TODO: This had a call to `transformWithPlugins`. Since we always do
388
413
// a transform step in the renderStart for all roots anyways, this might
389
414
// not be necessary
390
- let code = await root . generate ( src , ( file ) => this . addWatchFile ( file ) )
391
- return { code }
415
+ let generated = await root . generate ( src , ( file ) => this . addWatchFile ( file ) )
416
+ if ( ! generated ) {
417
+ roots . delete ( id )
418
+ return src
419
+ }
420
+ return { code : generated }
392
421
} ,
393
422
394
423
// `renderStart` runs in the bundle generation stage after all transforms.
395
424
// We must run before `enforce: post` so the updated chunks are picked up
396
425
// by vite:css-post.
397
426
async renderStart ( ) {
398
427
for ( let [ id , root ] of roots . entries ( ) ) {
399
- let css = await regenerateOptimizedCss ( root , ( file ) => this . addWatchFile ( file ) )
428
+ let generated = await regenerateOptimizedCss ( root , ( file ) => this . addWatchFile ( file ) )
429
+ if ( ! generated ) {
430
+ roots . delete ( id )
431
+ continue
432
+ }
400
433
401
434
// These plugins have side effects which, during build, results in CSS
402
435
// being written to the output dir. We need to run them here to ensure
403
436
// the CSS is written before the bundle is generated.
404
- await transformWithPlugins ( this , id , css )
437
+ await transformWithPlugins ( this , id , generated )
405
438
}
406
439
} ,
407
440
} ,
@@ -413,13 +446,21 @@ function getExtension(id: string) {
413
446
return path . extname ( filename ) . slice ( 1 )
414
447
}
415
448
416
- // TODO: This needs to run on the `@import`-flattened file
417
- function isTailwindCssFile ( id : string , src : string ) {
449
+ function isPotentialCssRootFile ( id : string ) {
418
450
let extension = getExtension ( id )
419
451
let isCssFile = extension === 'css' || ( extension === 'vue' && id . includes ( '&lang.css' ) )
420
452
return isCssFile
421
453
}
422
454
455
+ function isCssRootFile ( content : string ) {
456
+ return (
457
+ content . includes ( '@tailwind' ) ||
458
+ content . includes ( '@config' ) ||
459
+ content . includes ( '@plugin' ) ||
460
+ content . includes ( '@apply' )
461
+ )
462
+ }
463
+
423
464
function optimizeCss (
424
465
input : string ,
425
466
{ file = 'input.css' , minify = false } : { file ?: string ; minify ?: boolean } = { } ,
0 commit comments