@@ -2,6 +2,7 @@ import { compile, env, normalizePath } from '@tailwindcss/node'
2
2
import { clearRequireCache } from '@tailwindcss/node/require-cache'
3
3
import { Scanner } from '@tailwindcss/oxide'
4
4
import { Features , transform } from 'lightningcss'
5
+ import fs from 'node:fs/promises'
5
6
import path from 'path'
6
7
import type { Plugin , ResolvedConfig , Rollup , Update , ViteDevServer } from 'vite'
7
8
@@ -35,7 +36,7 @@ export default function tailwindcss(): Plugin[] {
35
36
// Note: To improve performance, we do not remove candidates from this set.
36
37
// This means a longer-ongoing dev mode session might contain candidates that
37
38
// are no longer referenced in code.
38
- let moduleGraphCandidates = new Set < string > ( )
39
+ let moduleGraphCandidates = new DefaultMap < string , Set < string > > ( ( ) => new Set < string > ( ) )
39
40
let moduleGraphScanner = new Scanner ( { } )
40
41
41
42
let roots : DefaultMap < string , Root > = new DefaultMap (
@@ -46,7 +47,7 @@ export default function tailwindcss(): Plugin[] {
46
47
let updated = false
47
48
for ( let candidate of moduleGraphScanner . scanFiles ( [ { content, extension } ] ) ) {
48
49
updated = true
49
- moduleGraphCandidates . add ( candidate )
50
+ moduleGraphCandidates . get ( id ) . add ( candidate )
50
51
}
51
52
52
53
if ( updated ) {
@@ -343,14 +344,16 @@ class Root {
343
344
// the lifetime of the root.
344
345
private candidates : Set < string > = new Set < string > ( )
345
346
346
- // List of all file dependencies that were captured while generating the root.
347
- // These are retained so we can clear the require cache when we rebuild the
348
- // root.
347
+ // List of all dependencies captured while generating the root. These are
348
+ // retained so we can clear the require cache when we rebuild the root.
349
349
private dependencies = new Set < string > ( )
350
350
351
+ // The resolved path given to `source(…)`. When not given this is `null`.
352
+ private basePath : string | null = null
353
+
351
354
constructor (
352
355
private id : string ,
353
- private getSharedCandidates : ( ) => Set < string > ,
356
+ private getSharedCandidates : ( ) => Map < string , Set < string > > ,
354
357
private base : string ,
355
358
) { }
356
359
@@ -379,9 +382,22 @@ class Root {
379
382
} )
380
383
env . DEBUG && console . timeEnd ( '[@tailwindcss/vite] Setup compiler' )
381
384
382
- this . scanner = new Scanner ( {
383
- sources : this . compiler . globs ,
384
- } )
385
+ let sources = ( ( ) => {
386
+ // Disable auto source detection
387
+ if ( this . compiler . root === 'none' ) {
388
+ return [ ]
389
+ }
390
+
391
+ // No root specified, use the module graph
392
+ if ( this . compiler . root === null ) {
393
+ return [ ]
394
+ }
395
+
396
+ // Use the specified root
397
+ return [ this . compiler . root ]
398
+ } ) ( ) . concat ( this . compiler . globs )
399
+
400
+ this . scanner = new Scanner ( { sources } )
385
401
}
386
402
387
403
// This should not be here, but right now the Vite plugin is setup where we
@@ -411,14 +427,62 @@ class Root {
411
427
relative = normalizePath ( relative )
412
428
413
429
addWatchFile ( path . posix . join ( relative , glob . pattern ) )
430
+
431
+ let root = this . compiler . root
432
+
433
+ if ( root !== 'none' && root !== null ) {
434
+ let basePath = path . posix . resolve ( root . base , root . pattern )
435
+
436
+ let isDir = await fs . stat ( basePath ) . then (
437
+ ( stats ) => stats . isDirectory ( ) ,
438
+ ( ) => false ,
439
+ )
440
+
441
+ if ( ! isDir ) {
442
+ throw new Error (
443
+ `The path given to \`source(…)\` must be a directory but got \`source(${ basePath } )\` instead.` ,
444
+ )
445
+ }
446
+
447
+ this . basePath = basePath
448
+ } else if ( root === null ) {
449
+ this . basePath = null
450
+ }
414
451
}
415
452
416
453
this . requiresRebuild = true
417
454
418
455
env . DEBUG && console . time ( '[@tailwindcss/vite] Build CSS' )
419
- let result = this . compiler . build ( [ ...this . getSharedCandidates ( ) , ...this . candidates ] )
456
+ let result = this . compiler . build ( [ ...this . sharedCandidates ( ) , ...this . candidates ] )
420
457
env . DEBUG && console . timeEnd ( '[@tailwindcss/vite] Build CSS' )
421
458
422
459
return result
423
460
}
461
+
462
+ private sharedCandidates ( ) : Set < string > {
463
+ if ( ! this . compiler ) return new Set ( )
464
+ if ( this . compiler . root === 'none' ) return new Set ( )
465
+
466
+ let shouldIncludeCandidatesFrom = ( id : string ) => {
467
+ if ( this . basePath === null ) return true
468
+
469
+ // This a virtual module that's not on the file system
470
+ // TODO: What should we do here?
471
+ if ( ! id . startsWith ( '/' ) ) return true
472
+
473
+ return id . startsWith ( this . basePath )
474
+ }
475
+
476
+ let shared = new Set < string > ( )
477
+
478
+ for ( let [ id , candidates ] of this . getSharedCandidates ( ) ) {
479
+ if ( ! shouldIncludeCandidatesFrom ( id ) ) continue
480
+
481
+ for ( let candidate of candidates ) {
482
+ shared . add ( candidate )
483
+ }
484
+ }
485
+
486
+ return shared
487
+ }
424
488
}
0 commit comments