@@ -242,20 +242,31 @@ export class ProjectLocator {
242
242
concurrency : Math . max ( os . cpus ( ) . length , 1 ) ,
243
243
} )
244
244
245
- files = await Promise . all (
246
- files . map ( async ( file ) => {
247
- // Resolve symlinks for all found files
248
- let actualPath = await fs . realpath ( file )
249
-
250
- // Ignore network paths on Windows. Resolving relative paths on a
251
- // netshare throws in `enhanced-resolve` :/
252
- if ( actualPath . startsWith ( '\\' ) && process . platform === 'win32' ) {
253
- return normalizePath ( file )
254
- }
245
+ let realpaths = await Promise . all ( files . map ( ( file ) => fs . realpath ( file ) ) )
255
246
256
- return normalizePath ( actualPath )
257
- } ) ,
258
- )
247
+ // Remove files that are symlinked yet have an existing file in the list
248
+ files = files . filter ( ( normalPath , idx ) => {
249
+ let realPath = realpaths [ idx ]
250
+
251
+ if ( normalPath === realPath ) {
252
+ return true
253
+ }
254
+
255
+ // If the file is a symlink, aliased path, network share, etc…; AND
256
+ // the realpath is not already in the list of files, then we can add
257
+ // the file to the list of files
258
+ //
259
+ // For example, node_modules in a monorepo setup would be symlinked
260
+ // and list both unless you opened one of the directories directly
261
+ else if ( ! files . includes ( realPath ) ) {
262
+ return true
263
+ }
264
+
265
+ return false
266
+ } )
267
+
268
+ // Make sure Windows-style paths are normalized
269
+ files = files . map ( ( file ) => normalizePath ( file ) )
259
270
260
271
// Deduplicate the list of files and sort them for deterministic results
261
272
// across environments
@@ -327,6 +338,9 @@ export class ProjectLocator {
327
338
// Resolve imports in all the CSS files
328
339
await Promise . all ( imports . map ( ( file ) => file . resolveImports ( ) ) )
329
340
341
+ // Resolve real paths for all the files in the CSS import graph
342
+ await Promise . all ( imports . map ( ( file ) => file . resolveRealpaths ( ) ) )
343
+
330
344
// Create a graph of all the CSS files that might (indirectly) use Tailwind
331
345
let graph = new Graph < FileEntry > ( )
332
346
@@ -335,24 +349,21 @@ export class ProjectLocator {
335
349
let utilitiesPath : string | null = null
336
350
337
351
for ( let file of imports ) {
338
- graph . add ( file . path , file )
339
-
340
- for ( let msg of file . deps ) {
341
- let importedPath : string = normalizePath ( msg . file )
342
-
343
- // Record that `file.path` imports `msg.file`
344
- graph . add ( importedPath , new FileEntry ( 'css' , importedPath ) )
352
+ graph . add ( file . realpath , file )
345
353
346
- graph . connect ( file . path , importedPath )
354
+ // Record that `file.path` imports `msg.file`
355
+ for ( let entry of file . deps ) {
356
+ graph . add ( entry . realpath , entry )
357
+ graph . connect ( file . realpath , entry . realpath )
347
358
}
348
359
349
360
// Collect the index, theme, and utilities files for manual connection
350
- if ( file . path . includes ( 'node_modules/tailwindcss/index.css' ) ) {
351
- indexPath = file . path
352
- } else if ( file . path . includes ( 'node_modules/tailwindcss/theme.css' ) ) {
353
- themePath = file . path
354
- } else if ( file . path . includes ( 'node_modules/tailwindcss/utilities.css' ) ) {
355
- utilitiesPath = file . path
361
+ if ( file . realpath . includes ( 'node_modules/tailwindcss/index.css' ) ) {
362
+ indexPath = file . realpath
363
+ } else if ( file . realpath . includes ( 'node_modules/tailwindcss/theme.css' ) ) {
364
+ themePath = file . realpath
365
+ } else if ( file . realpath . includes ( 'node_modules/tailwindcss/utilities.css' ) ) {
366
+ utilitiesPath = file . realpath
356
367
}
357
368
}
358
369
@@ -383,7 +394,7 @@ export class ProjectLocator {
383
394
384
395
// And add the config to all their descendants as we need to track updates
385
396
// that might affect the config / project
386
- for ( let child of graph . descendants ( root . path ) ) {
397
+ for ( let child of graph . descendants ( root . realpath ) ) {
387
398
child . configs . push ( config )
388
399
}
389
400
}
@@ -540,7 +551,8 @@ type ConfigEntry = {
540
551
541
552
class FileEntry {
542
553
content : string | null
543
- deps : Message [ ] = [ ]
554
+ deps : FileEntry [ ] = [ ]
555
+ realpath : string | null
544
556
545
557
constructor (
546
558
public type : 'js' | 'css' ,
@@ -559,7 +571,10 @@ class FileEntry {
559
571
async resolveImports ( ) {
560
572
try {
561
573
let result = await resolveCssImports ( ) . process ( this . content , { from : this . path } )
562
- this . deps = result . messages . filter ( ( msg ) => msg . type === 'dependency' )
574
+ let deps = result . messages . filter ( ( msg ) => msg . type === 'dependency' )
575
+
576
+ // Record entries for each of the dependencies
577
+ this . deps = deps . map ( ( msg ) => new FileEntry ( 'css' , normalizePath ( msg . file ) ) )
563
578
564
579
// Replace the file content with the processed CSS
565
580
this . content = result . css
@@ -568,6 +583,12 @@ class FileEntry {
568
583
}
569
584
}
570
585
586
+ async resolveRealpaths ( ) {
587
+ this . realpath = normalizePath ( await fs . realpath ( this . path ) )
588
+
589
+ await Promise . all ( this . deps . map ( ( entry ) => entry . resolveRealpaths ( ) ) )
590
+ }
591
+
571
592
/**
572
593
* Look for `@config` directives in a CSS file and return the path to the config
573
594
* file that it points to. This path is (possibly) relative to the CSS file so
0 commit comments