Skip to content

Commit 7e7e234

Browse files
committed
Sort files that import Tailwind CSS before ones that don’t
1 parent 7dcf79e commit 7e7e234

File tree

3 files changed

+77
-4
lines changed

3 files changed

+77
-4
lines changed

packages/tailwindcss-language-server/src/project-locator.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,38 @@ testLocator({
499499
],
500500
})
501501

502+
testLocator({
503+
name: 'Stylesheets that import Tailwind CSS are picked over ones that dont',
504+
fs: {
505+
'a/foo.css': css`
506+
@import './bar.css';
507+
.a {
508+
color: red;
509+
}
510+
`,
511+
'a/bar.css': css`
512+
.b {
513+
color: red;
514+
}
515+
`,
516+
'src/app.css': css`
517+
@import 'tailwindcss';
518+
`,
519+
},
520+
expected: [
521+
{
522+
version: '4.1.1 (bundled)',
523+
config: '/src/app.css',
524+
content: [],
525+
},
526+
{
527+
version: '4.1.1 (bundled)',
528+
config: '/a/foo.css',
529+
content: [],
530+
},
531+
],
532+
})
533+
502534
// ---
503535

504536
function testLocator({

packages/tailwindcss-language-server/src/project-locator.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ export class ProjectLocator {
350350
for (let file of imports) byRealPath[file.realpath] = file
351351
for (let file of imports) file.deps = file.deps.map((dep) => byRealPath[dep.realpath] ?? dep)
352352

353+
// Check if each file has a direct or indirect tailwind import
354+
await Promise.all(imports.map((file) => file.resolveImportsTailwind()))
355+
353356
// Create a graph of all the CSS files that might (indirectly) use Tailwind
354357
let graph = new Graph<FileEntry>()
355358

@@ -391,11 +394,13 @@ export class ProjectLocator {
391394

392395
roots.sort((a, b) => {
393396
return (
394-
// Sort the graph so potential "roots" appear first
395-
// The entire concept of roots needs to be rethought because it's not always
396-
// clear what the root of a project is. Even when imports are present a file
397-
// may import a file that is the actual "root" of the project.
397+
// Sort the graph so potential "roots" appear first
398+
// The entire concept of roots needs to be rethought because it's not always
399+
// clear what the root of a project is. Even when imports are present a file
400+
// may import a file that is the actual "root" of the project.
398401
Number(b.meta.root) - Number(a.meta.root) ||
402+
// Move stylesheets with an explicit tailwindcss import before others
403+
Number(b.importsTailwind) - Number(a.importsTailwind) ||
399404
// Otherwise stylesheets are kept in discovery order
400405
0
401406
)
@@ -737,6 +742,28 @@ class FileEntry {
737742
this.meta ??= this.content ? analyzeStylesheet(this.content) : null
738743
}
739744

745+
/**
746+
* Determine if this entry or any of its dependencies import a Tailwind CSS
747+
* stylesheet
748+
*/
749+
importsTailwind: boolean | null = null
750+
751+
resolveImportsTailwind() {
752+
// Already calculated so nothing to do
753+
if (this.importsTailwind !== null) return
754+
755+
// We import it directly
756+
if (this.meta?.explicitImport) {
757+
this.importsTailwind = true
758+
return
759+
}
760+
761+
// Maybe one of our deps does
762+
for (let dep of this.deps) dep.resolveImportsTailwind()
763+
764+
this.importsTailwind = this.deps.some((dep) => dep.importsTailwind)
765+
}
766+
740767
/**
741768
* Look for `@config` directives in a CSS file and return the path to the config
742769
* file that it points to. This path is (possibly) relative to the CSS file so

packages/tailwindcss-language-server/src/version-guesser.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ export interface TailwindStylesheet {
1010
* The likely Tailwind version used by the given file
1111
*/
1212
versions: TailwindVersion[]
13+
14+
/**
15+
* Whether or not this stylesheet explicitly imports Tailwind CSS
16+
*/
17+
explicitImport: boolean
1318
}
1419

1520
// It's likely this is a v4 file if it has a v4 import:
@@ -60,6 +65,7 @@ export function analyzeStylesheet(content: string): TailwindStylesheet {
6065
return {
6166
root: true,
6267
versions: ['4'],
68+
explicitImport: true,
6369
}
6470
}
6571

@@ -71,13 +77,15 @@ export function analyzeStylesheet(content: string): TailwindStylesheet {
7177
return {
7278
root: true,
7379
versions: ['4'],
80+
explicitImport: false,
7481
}
7582
}
7683

7784
return {
7885
// This file MUST be imported by another file to be a valid root
7986
root: false,
8087
versions: ['4'],
88+
explicitImport: false,
8189
}
8290
}
8391

@@ -87,6 +95,7 @@ export function analyzeStylesheet(content: string): TailwindStylesheet {
8795
// This file MUST be imported by another file to be a valid root
8896
root: false,
8997
versions: ['4'],
98+
explicitImport: false,
9099
}
91100
}
92101

@@ -96,6 +105,7 @@ export function analyzeStylesheet(content: string): TailwindStylesheet {
96105
// Roots are only a valid concept in v4
97106
root: false,
98107
versions: ['3'],
108+
explicitImport: false,
99109
}
100110
}
101111

@@ -104,6 +114,7 @@ export function analyzeStylesheet(content: string): TailwindStylesheet {
104114
return {
105115
root: true,
106116
versions: ['4', '3'],
117+
explicitImport: false,
107118
}
108119
}
109120

@@ -112,6 +123,7 @@ export function analyzeStylesheet(content: string): TailwindStylesheet {
112123
return {
113124
root: false,
114125
versions: ['4', '3'],
126+
explicitImport: false,
115127
}
116128
}
117129

@@ -120,12 +132,14 @@ export function analyzeStylesheet(content: string): TailwindStylesheet {
120132
return {
121133
root: true,
122134
versions: ['4', '3'],
135+
explicitImport: false,
123136
}
124137
}
125138

126139
// Pretty sure it's not related to Tailwind at all
127140
return {
128141
root: false,
129142
versions: [],
143+
explicitImport: false,
130144
}
131145
}

0 commit comments

Comments
 (0)