Skip to content

Commit cf97d7a

Browse files
Handle style exports condition when processing @imports (#934)
* Refactor * Support customizing directory of an opened completion document * Add notes * Resolve `style` exports field in postcss import * Update changelog * Add test * Refactor * Make sure the main `style` field is still supported
1 parent 37c4b96 commit cf97d7a

File tree

17 files changed

+134
-29
lines changed

17 files changed

+134
-29
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -81,5 +81,6 @@ testFixture('v4/multi-config', [
8181
testFixture('v4/workspaces', [
8282
{ config: 'packages/admin/app.css' },
8383
// { config: 'packages/shared/ui.css' }, // Should this be included?
84+
// { config: 'packages/style-export/lib.css' }, // Should this be included?
8485
{ config: 'packages/web/app.css' },
8586
])

Diff for: packages/tailwindcss-language-server/src/project-locator.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@ import { CONFIG_GLOB, CSS_GLOB } from './lib/constants'
99
import { readCssFile } from './util/css'
1010
import { Graph } from './graph'
1111
import type { Message } from 'postcss'
12-
import postcss from 'postcss'
13-
import postcssImport from 'postcss-import'
1412
import { type DocumentSelector, DocumentSelectorPriority } from './projects'
1513
import { CacheMap } from './cache-map'
1614
import { getPackageRoot } from './util/get-package-root'
1715
import resolveFrom from './util/resolveFrom'
1816
import { type Feature, supportedFeatures } from '@tailwindcss/language-service/src/features'
1917
import { pathToFileURL } from 'node:url'
18+
import { resolveCssImports } from './resolve-css-imports'
2019

2120
export interface ProjectConfig {
2221
/** The folder that contains the project */
@@ -483,7 +482,6 @@ type ConfigEntry = {
483482
content: ContentItem[]
484483
}
485484

486-
let resolveImports = postcss([postcssImport()])
487485
class FileEntry {
488486
content: string | null
489487
deps: Message[] = []
@@ -504,7 +502,7 @@ class FileEntry {
504502

505503
async resolveImports() {
506504
try {
507-
let result = await resolveImports.process(this.content, { from: this.path })
505+
let result = await resolveCssImports().process(this.content, { from: this.path })
508506
this.deps = result.messages.filter((msg) => msg.type === 'dependency')
509507

510508
// Replace the file content with the processed CSS

Diff for: packages/tailwindcss-language-server/src/projects.ts

+1
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,7 @@ export async function createProjectService(
755755
originalConfig = { theme: {} }
756756
} catch {
757757
// TODO: Fall back to built-in v4 stuff
758+
// TODO: Log errors? It might get noisy as people are editing their CSS though
758759
enabled = false
759760
state.enabled = false
760761
return
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import postcss from 'postcss'
2+
import postcssImport from 'postcss-import'
3+
import { createResolver } from './util/resolve'
4+
5+
const resolver = createResolver({
6+
extensions: ['.css'],
7+
mainFields: ['style'],
8+
conditionNames: ['style'],
9+
})
10+
11+
const resolveImports = postcss([
12+
postcssImport({
13+
resolve(id, basedir) {
14+
let paths = resolver.resolveSync({}, basedir, id)
15+
return paths
16+
? paths
17+
: id
18+
},
19+
}),
20+
])
21+
22+
export function resolveCssImports() {
23+
return resolveImports
24+
}
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import * as fs from 'fs'
2+
import {
3+
CachedInputFileSystem,
4+
ResolverFactory,
5+
Resolver,
6+
ResolveOptions,
7+
} from 'enhanced-resolve-301'
8+
9+
export function createResolver(options: Partial<ResolveOptions> = {}): Resolver {
10+
return ResolverFactory.createResolver({
11+
fileSystem: new CachedInputFileSystem(fs, 4000),
12+
useSyncFileSystemCalls: true,
13+
conditionNames: ['node', 'require'],
14+
...options,
15+
})
16+
}

Diff for: packages/tailwindcss-language-server/src/util/resolveFrom.ts

+6-20
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,18 @@
1-
import * as fs from 'fs'
2-
import {
3-
CachedInputFileSystem,
4-
ResolverFactory,
5-
Resolver,
6-
ResolveOptions,
7-
} from 'enhanced-resolve-301'
81
import { equal } from '@tailwindcss/language-service/src/util/array'
2+
import { createResolver } from './resolve'
93

104
let pnpApi: any
115
let extensions = Object.keys(require.extensions)
126

13-
function createResolver(options: Partial<ResolveOptions> = {}): Resolver {
14-
return ResolverFactory.createResolver({
15-
fileSystem: new CachedInputFileSystem(fs, 4000),
16-
useSyncFileSystemCalls: true,
17-
// cachePredicate: () => false,
18-
conditionNames: ['node', 'require'],
19-
extensions,
20-
pnpApi,
21-
...options,
22-
})
7+
function recreateResolver() {
8+
return createResolver({ extensions, pnpApi })
239
}
2410

25-
let resolver = createResolver()
11+
let resolver = recreateResolver()
2612

2713
export function setPnpApi(newPnpApi: any): void {
2814
pnpApi = newPnpApi
29-
resolver = createResolver()
15+
resolver = recreateResolver()
3016
}
3117

3218
export default function resolveFrom(from?: string, id?: string): string {
@@ -35,7 +21,7 @@ export default function resolveFrom(from?: string, id?: string): string {
3521
let newExtensions = Object.keys(require.extensions)
3622
if (!equal(newExtensions, extensions)) {
3723
extensions = newExtensions
38-
resolver = createResolver()
24+
resolver = recreateResolver()
3925
}
4026

4127
let result = resolver.resolveSync({}, from, id)

Diff for: packages/tailwindcss-language-server/src/util/v4/design-system.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
import type { DesignSystem } from '@tailwindcss/language-service/src/util/v4'
22

33
import postcss from 'postcss'
4-
import postcssImport from 'postcss-import'
5-
6-
const resolveImports = postcss([postcssImport()])
4+
import { resolveCssImports } from '../../resolve-css-imports'
75

86
const HAS_V4_IMPORT = /@import\s*(?:'tailwindcss'|"tailwindcss")/
97
const HAS_V4_THEME = /@theme\s*\{/
@@ -36,7 +34,7 @@ export async function loadDesignSystem(
3634
// Step 2: Use postcss to resolve `@import` rules in the CSS file
3735
// TODO: What if someone is actively editing their config and introduces a syntax error?
3836
// We don't want to necessarily throw away the knowledge that we have a v4 project.
39-
let resolved = await resolveImports.process(css, { from: filepath })
37+
let resolved = await resolveCssImports().process(css, { from: filepath })
4038

4139
// Step 3: Take the resolved CSS and pass it to v4's `loadDesignSystem`
4240
let design = tailwindcss.__unstable__loadDesignSystem(resolved.css) as DesignSystem

Diff for: packages/tailwindcss-language-server/tests/common.ts

+2
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ async function init(fixture: string): Promise<FixtureContext> {
201201
},
202202
} as DidOpenTextDocumentParams)
203203

204+
// If opening a document stalls then it's probably because this promise is not being resolved
205+
// This can happen if a document is not covered by one of the selectors because of it's URI
204206
await initPromise
205207

206208
return {

Diff for: packages/tailwindcss-language-server/tests/completions/completions.test.js

+41-1
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ function buildCompletion(c) {
1010
triggerKind: 1,
1111
},
1212
settings,
13+
dir = '',
1314
}) {
14-
let textDocument = await c.openDocument({ text, lang, settings })
15+
let textDocument = await c.openDocument({ text, lang, settings, dir })
1516

1617
return c.sendRequest('textDocument/completion', {
1718
textDocument,
@@ -552,3 +553,42 @@ withFixture('v4/basic', (c) => {
552553
})
553554
})
554555
})
556+
557+
withFixture('v4/workspaces', (c) => {
558+
let completion = buildCompletion(c)
559+
560+
test('@import resolution supports exports.style', async ({ expect }) => {
561+
let result = await completion({
562+
dir: 'packages/web',
563+
lang: 'html',
564+
text: '<div class=""></div>',
565+
position: { line: 0, character: 12 },
566+
})
567+
568+
let items = [
569+
result.items.find((item) => item.label === 'bg-beet'),
570+
result.items.find((item) => item.label === 'bg-orangepeel'),
571+
result.items.find((item) => item.label === 'bg-style-main'),
572+
]
573+
574+
let resolved = await Promise.all(items.map((item) => c.sendRequest('completionItem/resolve', item)))
575+
576+
expect(resolved[0]).toEqual({
577+
...items[0],
578+
detail: 'background-color: #8e3b46;',
579+
documentation: '#8e3b46',
580+
})
581+
582+
expect(resolved[1]).toEqual({
583+
...items[1],
584+
detail: 'background-color: #ff9f00;',
585+
documentation: '#ff9f00',
586+
})
587+
588+
expect(resolved[2]).toEqual({
589+
...items[2],
590+
detail: 'background-color: #8e3b46;',
591+
documentation: '#8e3b46',
592+
})
593+
})
594+
})

Diff for: packages/tailwindcss-language-server/tests/fixtures/v4/workspaces/package-lock.json

+12
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@theme {
2+
--color-beet: #8e3b46;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"name": "@private/style-export",
3+
"exports": {
4+
".": {
5+
"style": "./lib.css"
6+
},
7+
"./theme": "./theme.css"
8+
}
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@theme {
2+
--color-orangepeel: #ff9f00;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@theme {
2+
--color-style-main: #8e3b46;
3+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "@private/style-main-field",
3+
"style": "./lib.css"
4+
}
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
@import 'tailwindcss';
22
@import '@private/shared/ui.css';
3+
@import '@private/style-export';
4+
@import '@private/style-export/theme';
5+
@import '@private/style-main-field';
6+
37
@theme {
48
--color-potato: #907a70;
59
}

Diff for: packages/vscode-tailwindcss/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- Fix hovers and CSS conflict detection in Vue `<style lang="sass">` blocks (#930)
88
- Add support for `<script type="text/babel">` (#932)
99
- Show pixel equivalents in completions and hovers of the theme() helper (#935)
10+
- Handle `style` exports condition when processing `@import`s (#934)
1011

1112
## 0.10.5
1213

0 commit comments

Comments
 (0)