Skip to content

Commit 2f6de10

Browse files
committed
Sort files that import Tailwind CSS before ones that don’t
1 parent 75ebf04 commit 2f6de10

File tree

3 files changed

+83
-0
lines changed

3 files changed

+83
-0
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: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,17 @@ export class ProjectLocator {
345345
// Resolve all @source directives
346346
await Promise.all(imports.map((file) => file.resolveSourceDirectives()))
347347

348+
let byRealPath: Record<string, FileEntry> = {}
349+
for (let file of imports) byRealPath[file.realpath] = file
350+
351+
// TODO: Link every entry in the import graph
352+
// This breaks things tho
353+
// for (let file of imports) file.deps = file.deps.map((dep) => byRealPath[dep.realpath] ?? dep)
354+
355+
// Check if each file has a direct or indirect tailwind import
356+
// TODO: Remove the `byRealPath` argument and use linked deps instead
357+
await Promise.all(imports.map((file) => file.resolveImportsTailwind(byRealPath)))
358+
348359
// Create a graph of all the CSS files that might (indirectly) use Tailwind
349360
let graph = new Graph<FileEntry>()
350361

@@ -391,6 +402,8 @@ export class ProjectLocator {
391402
// clear what the root of a project is. Even when imports are present a file
392403
// may import a file that is the actual "root" of the project.
393404
Number(b.meta.root) - Number(a.meta.root) ||
405+
// Move stylesheets with an explicit tailwindcss import before others
406+
Number(b.importsTailwind) - Number(a.importsTailwind) ||
394407
// Otherwise stylesheets are kept in discovery order
395408
0
396409
)
@@ -732,6 +745,30 @@ class FileEntry {
732745
this.meta ??= this.content ? analyzeStylesheet(this.content) : null
733746
}
734747

748+
/**
749+
* Determine if this entry or any of its dependencies import a Tailwind CSS
750+
* stylesheet
751+
*/
752+
importsTailwind: boolean | null = null
753+
754+
resolveImportsTailwind(byPath: Record<string, FileEntry>) {
755+
// Already calculated so nothing to do
756+
if (this.importsTailwind !== null) return
757+
758+
// We import it directly
759+
let self = byPath[this.realpath]
760+
761+
if (this.meta?.explicitImport || self?.meta?.explicitImport) {
762+
this.importsTailwind = true
763+
return
764+
}
765+
766+
// Maybe one of our deps does
767+
for (let dep of this.deps) dep.resolveImportsTailwind(byPath)
768+
769+
this.importsTailwind = this.deps.some((dep) => dep.importsTailwind)
770+
}
771+
735772
/**
736773
* Look for `@config` directives in a CSS file and return the path to the config
737774
* 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)