Skip to content

Handle style exports condition when processing @imports #934

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,6 @@ testFixture('v4/multi-config', [
testFixture('v4/workspaces', [
{ config: 'packages/admin/app.css' },
// { config: 'packages/shared/ui.css' }, // Should this be included?
// { config: 'packages/style-export/lib.css' }, // Should this be included?
{ config: 'packages/web/app.css' },
])
6 changes: 2 additions & 4 deletions packages/tailwindcss-language-server/src/project-locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ import { CONFIG_GLOB, CSS_GLOB } from './lib/constants'
import { readCssFile } from './util/css'
import { Graph } from './graph'
import type { Message } from 'postcss'
import postcss from 'postcss'
import postcssImport from 'postcss-import'
import { type DocumentSelector, DocumentSelectorPriority } from './projects'
import { CacheMap } from './cache-map'
import { getPackageRoot } from './util/get-package-root'
import resolveFrom from './util/resolveFrom'
import { type Feature, supportedFeatures } from '@tailwindcss/language-service/src/features'
import { pathToFileURL } from 'node:url'
import { resolveCssImports } from './resolve-css-imports'

export interface ProjectConfig {
/** The folder that contains the project */
Expand Down Expand Up @@ -483,7 +482,6 @@ type ConfigEntry = {
content: ContentItem[]
}

let resolveImports = postcss([postcssImport()])
class FileEntry {
content: string | null
deps: Message[] = []
Expand All @@ -504,7 +502,7 @@ class FileEntry {

async resolveImports() {
try {
let result = await resolveImports.process(this.content, { from: this.path })
let result = await resolveCssImports().process(this.content, { from: this.path })
this.deps = result.messages.filter((msg) => msg.type === 'dependency')

// Replace the file content with the processed CSS
Expand Down
1 change: 1 addition & 0 deletions packages/tailwindcss-language-server/src/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ export async function createProjectService(
originalConfig = { theme: {} }
} catch {
// TODO: Fall back to built-in v4 stuff
// TODO: Log errors? It might get noisy as people are editing their CSS though
enabled = false
state.enabled = false
return
Expand Down
24 changes: 24 additions & 0 deletions packages/tailwindcss-language-server/src/resolve-css-imports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import postcss from 'postcss'
import postcssImport from 'postcss-import'
import { createResolver } from './util/resolve'

const resolver = createResolver({
extensions: ['.css'],
mainFields: ['style'],
conditionNames: ['style'],
})

const resolveImports = postcss([
postcssImport({
resolve(id, basedir) {
let paths = resolver.resolveSync({}, basedir, id)
return paths
? paths
: id
},
}),
])

export function resolveCssImports() {
return resolveImports
}
16 changes: 16 additions & 0 deletions packages/tailwindcss-language-server/src/util/resolve.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as fs from 'fs'
import {
CachedInputFileSystem,
ResolverFactory,
Resolver,
ResolveOptions,
} from 'enhanced-resolve-301'

export function createResolver(options: Partial<ResolveOptions> = {}): Resolver {
return ResolverFactory.createResolver({
fileSystem: new CachedInputFileSystem(fs, 4000),
useSyncFileSystemCalls: true,
conditionNames: ['node', 'require'],
...options,
})
}
26 changes: 6 additions & 20 deletions packages/tailwindcss-language-server/src/util/resolveFrom.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
import * as fs from 'fs'
import {
CachedInputFileSystem,
ResolverFactory,
Resolver,
ResolveOptions,
} from 'enhanced-resolve-301'
import { equal } from '@tailwindcss/language-service/src/util/array'
import { createResolver } from './resolve'

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

function createResolver(options: Partial<ResolveOptions> = {}): Resolver {
return ResolverFactory.createResolver({
fileSystem: new CachedInputFileSystem(fs, 4000),
useSyncFileSystemCalls: true,
// cachePredicate: () => false,
conditionNames: ['node', 'require'],
extensions,
pnpApi,
...options,
})
function recreateResolver() {
return createResolver({ extensions, pnpApi })
}

let resolver = createResolver()
let resolver = recreateResolver()

export function setPnpApi(newPnpApi: any): void {
pnpApi = newPnpApi
resolver = createResolver()
resolver = recreateResolver()
}

export default function resolveFrom(from?: string, id?: string): string {
Expand All @@ -35,7 +21,7 @@ export default function resolveFrom(from?: string, id?: string): string {
let newExtensions = Object.keys(require.extensions)
if (!equal(newExtensions, extensions)) {
extensions = newExtensions
resolver = createResolver()
resolver = recreateResolver()
}

let result = resolver.resolveSync({}, from, id)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type { DesignSystem } from '@tailwindcss/language-service/src/util/v4'

import postcss from 'postcss'
import postcssImport from 'postcss-import'

const resolveImports = postcss([postcssImport()])
import { resolveCssImports } from '../../resolve-css-imports'

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

// Step 3: Take the resolved CSS and pass it to v4's `loadDesignSystem`
let design = tailwindcss.__unstable__loadDesignSystem(resolved.css) as DesignSystem
Expand Down
2 changes: 2 additions & 0 deletions packages/tailwindcss-language-server/tests/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,8 @@ async function init(fixture: string): Promise<FixtureContext> {
},
} as DidOpenTextDocumentParams)

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

return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ function buildCompletion(c) {
triggerKind: 1,
},
settings,
dir = '',
}) {
let textDocument = await c.openDocument({ text, lang, settings })
let textDocument = await c.openDocument({ text, lang, settings, dir })

return c.sendRequest('textDocument/completion', {
textDocument,
Expand Down Expand Up @@ -552,3 +553,42 @@ withFixture('v4/basic', (c) => {
})
})
})

withFixture('v4/workspaces', (c) => {
let completion = buildCompletion(c)

test('@import resolution supports exports.style', async ({ expect }) => {
let result = await completion({
dir: 'packages/web',
lang: 'html',
text: '<div class=""></div>',
position: { line: 0, character: 12 },
})

let items = [
result.items.find((item) => item.label === 'bg-beet'),
result.items.find((item) => item.label === 'bg-orangepeel'),
result.items.find((item) => item.label === 'bg-style-main'),
]

let resolved = await Promise.all(items.map((item) => c.sendRequest('completionItem/resolve', item)))

expect(resolved[0]).toEqual({
...items[0],
detail: 'background-color: #8e3b46;',
documentation: '#8e3b46',
})

expect(resolved[1]).toEqual({
...items[1],
detail: 'background-color: #ff9f00;',
documentation: '#ff9f00',
})

expect(resolved[2]).toEqual({
...items[2],
detail: 'background-color: #8e3b46;',
documentation: '#8e3b46',
})
})
})

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@theme {
--color-beet: #8e3b46;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@private/style-export",
"exports": {
".": {
"style": "./lib.css"
},
"./theme": "./theme.css"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@theme {
--color-orangepeel: #ff9f00;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@theme {
--color-style-main: #8e3b46;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"name": "@private/style-main-field",
"style": "./lib.css"
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
@import 'tailwindcss';
@import '@private/shared/ui.css';
@import '@private/style-export';
@import '@private/style-export/theme';
@import '@private/style-main-field';

@theme {
--color-potato: #907a70;
}
1 change: 1 addition & 0 deletions packages/vscode-tailwindcss/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Fix hovers and CSS conflict detection in Vue `<style lang="sass">` blocks (#930)
- Add support for `<script type="text/babel">` (#932)
- Show pixel equivalents in completions and hovers of the theme() helper (#935)
- Handle `style` exports condition when processing `@import`s (#934)

## 0.10.5

Expand Down