Skip to content

Commit 4d9917c

Browse files
committed
Hoist @import nodes to the top in the project locator
This ensures project initialization can proceed far enough that Tailwind CSS itself should attempt to initalize. It also means we can reliably detect this case and show an error in the console.
1 parent fc28aca commit 4d9917c

File tree

8 files changed

+90
-4
lines changed

8 files changed

+90
-4
lines changed

packages/tailwindcss-language-server/src/css/resolve-css-imports.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,39 @@ import { Resolver } from '../resolver'
66

77
export function resolveCssImports({
88
resolver,
9+
loose = false,
910
}: {
1011
resolver: Resolver
12+
loose?: boolean
1113
}) {
1214
return postcss([
15+
// Hoist imports to the top of the file
16+
{
17+
postcssPlugin: 'hoist-at-import',
18+
Once(root, { result }) {
19+
if (!loose) return
20+
21+
let hoist: postcss.AtRule[] = []
22+
let seenImportsAfterOtherNodes = false
23+
24+
for (let node of root.nodes) {
25+
if (node.type === 'atrule' && (node.name === 'import' || node.name === 'charset')) {
26+
hoist.push(node)
27+
} else if (hoist.length > 0 && (node.type === 'atrule' || node.type === 'rule')) {
28+
seenImportsAfterOtherNodes = true
29+
}
30+
}
31+
32+
root.prepend(hoist)
33+
34+
if (!seenImportsAfterOtherNodes) return
35+
36+
console.log(
37+
`hoist-at-import: The file '${result.opts.from}' contains @import rules after other at rules. This is invalid CSS and may cause problems with your build.`,
38+
)
39+
},
40+
},
41+
1342
postcssImport({
1443
async resolve(id, base) {
1544
try {

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,3 +219,11 @@ testFixture('v4/path-mappings', [
219219
],
220220
},
221221
])
222+
223+
testFixture('v4/invalid-import-order', [
224+
//
225+
{
226+
config: 'tailwind.css',
227+
content: ['{URL}/package.json'],
228+
},
229+
])

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

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -618,7 +618,9 @@ class FileEntry {
618618

619619
async resolveImports(resolver: Resolver) {
620620
try {
621-
let result = await resolveCssImports({ resolver }).process(this.content, { from: this.path })
621+
let result = await resolveCssImports({ resolver, loose: true }).process(this.content, {
622+
from: this.path,
623+
})
622624
let deps = result.messages.filter((msg) => msg.type === 'dependency')
623625

624626
deps = deps.filter((msg) => {
@@ -630,9 +632,10 @@ class FileEntry {
630632

631633
// Replace the file content with the processed CSS
632634
this.content = result.css
633-
} catch {
634-
// TODO: Errors here should be surfaced in tests and possibly the user in
635-
// `trace` logs or something like that
635+
} catch (err) {
636+
console.debug(`Unable to resolve imports for ${this.path}.`)
637+
console.debug(`This may result in failure to locate Tailwind CSS projects.`)
638+
console.error(err)
636639
}
637640
}
638641

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@layer base {
2+
:root {
3+
font-family: sans-serif;
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@layer base {
2+
:root {
3+
--foo: red;
4+
}
5+
}

packages/tailwindcss-language-server/tests/fixtures/v4/invalid-import-order/package-lock.json

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"dependencies": {
3+
"tailwindcss": "^4.0.0-beta.6"
4+
}
5+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
@import 'tailwindcss';
2+
3+
/*
4+
* This is invalid in this position because some `@import`s are not at the top of the file.
5+
* We don't want project discovery to fail so we hoist them up and then warn in the console.
6+
*/
7+
@variant dark (&:where(.dark, .dark *));
8+
9+
@import './a.css';
10+
@import './b.css';
11+
12+
@theme {
13+
--color-primary: #c0ffee;
14+
}

0 commit comments

Comments
 (0)