Skip to content

Commit

Permalink
Fix errors thrown by detecting content files with oxide (#945)
Browse files Browse the repository at this point in the history
* Fix errors thrown by detecting content files with oxide (#942)

* Add test

* Cleanup code

* Update version

* Manually connect index.css to theme.css and utilities.css

* Move comment

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
  • Loading branch information
hampustagerud and thecrypticace committed Apr 4, 2024
1 parent d25652b commit 48056f2
Show file tree
Hide file tree
Showing 17 changed files with 470 additions and 37 deletions.
182 changes: 182 additions & 0 deletions package-lock.json

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

1 change: 1 addition & 0 deletions packages/tailwindcss-language-server/package.json
Expand Up @@ -39,6 +39,7 @@
"@tailwindcss/forms": "0.5.3",
"@tailwindcss/language-service": "*",
"@tailwindcss/line-clamp": "0.4.2",
"@tailwindcss/oxide": "^4.0.0-alpha.11",
"@tailwindcss/typography": "0.5.7",
"@types/color-name": "^1.1.3",
"@types/culori": "^2.1.0",
Expand Down
23 changes: 23 additions & 0 deletions packages/tailwindcss-language-server/src/project-locator.test.ts
Expand Up @@ -29,6 +29,16 @@ function testFixture(fixture: string, details: any[]) {
let configPath = path.relative(fixturePath, project.config.path)

expect(configPath).toEqual(detail?.config)

if (detail?.content) {
let expected = detail?.content.map((path) => path.replace('{URL}', fixturePath))

let actual = project.documentSelector
.filter((selector) => selector.priority === 1 /** content */)
.map((selector) => selector.pattern)

expect(actual).toEqual(expected)
}
}

expect(projects).toHaveLength(details.length)
Expand Down Expand Up @@ -84,3 +94,16 @@ testFixture('v4/workspaces', [
// { config: 'packages/style-export/lib.css' }, // Should this be included?
{ config: 'packages/web/app.css' },
])

testFixture('v4/auto-content', [
//
{
config: 'src/app.css',
content: [
'{URL}/package.json',
'{URL}/src/index.html',
'{URL}/src/components/example.html',
'{URL}/src/**/*.{py,tpl,js,vue,php,mjs,cts,jsx,tsx,rhtml,slim,handlebars,twig,rs,njk,svelte,liquid,pug,md,ts,heex,mts,astro,nunjucks,rb,eex,haml,cjs,html,hbs,jade,aspx,razor,erb,mustache,mdx}',
],
},
])
47 changes: 38 additions & 9 deletions packages/tailwindcss-language-server/src/project-locator.ts
Expand Up @@ -303,6 +303,10 @@ export class ProjectLocator {
// Create a graph of all the CSS files that might (indirectly) use Tailwind
let graph = new Graph<FileEntry>()

let indexPath: string | null = null
let themePath: string | null = null
let utilitiesPath: string | null = null

for (let file of imports) {
graph.add(file.path, file)

Expand All @@ -314,8 +318,28 @@ export class ProjectLocator {

graph.connect(file.path, importedPath)
}

// Collect the index, theme, and utilities files for manual connection
if (file.path.includes('node_modules/tailwindcss/index.css')) {
indexPath = file.path
} else if (file.path.includes('node_modules/tailwindcss/theme.css')) {
themePath = file.path
} else if (file.path.includes('node_modules/tailwindcss/utilities.css')) {
utilitiesPath = file.path
}
}

// We flatten the index file on publish so there are no imports that
// need to be resolved. But this messes with our graph traversal, so
// we need to manually connect the index file to the theme and utilities
// files so we do not get extra roots in the graph.
// - node_modules/tailwindcss/index.css
// -> node_modules/tailwindcss/theme.css
// -> node_modules/tailwindcss/utilities.css

if (indexPath && themePath) graph.connect(indexPath, themePath)
if (indexPath && utilitiesPath) graph.connect(indexPath, utilitiesPath)

for (let root of graph.roots()) {
let config: ConfigEntry = configs.remember(root.path, () => ({
source: 'css',
Expand Down Expand Up @@ -438,9 +462,9 @@ async function* contentSelectorsFromCssConfig(entry: ConfigEntry): AsyncIterable
}
} else if (item.kind === 'auto' && !auto) {
auto = true
for await (let file of detectContentFiles(entry.packageRoot)) {
for await (let pattern of detectContentFiles(entry.packageRoot)) {
yield {
pattern: normalizePath(file),
pattern,
priority: DocumentSelectorPriority.CONTENT_FILE,
}
}
Expand All @@ -453,16 +477,21 @@ async function* detectContentFiles(base: string): AsyncIterable<string> {
let oxidePath = resolveFrom(path.dirname(base), '@tailwindcss/oxide')
oxidePath = pathToFileURL(oxidePath).href

const oxide: typeof import('@tailwindcss/oxide') = await import(oxidePath)

// This isn't a v4 project
const oxide = await import(oxidePath)
if (!oxide.scanDir) {
return
}
if (!oxide.scanDir) return

let { files, globs } = await oxide.scanDir({ base, globs: true })
let { files, globs } = oxide.scanDir({ base, globs: true })

yield* files
yield* globs
for (let file of files) {
yield normalizePath(file)
}

for (let { base, glob } of globs) {
// Do not normalize the glob itself as it may contain escape sequences
yield normalizePath(base) + '/' + glob
}
} catch {
//
}
Expand Down
Expand Up @@ -310,8 +310,8 @@ withFixture('v4/basic', (c) => {
let result = await completion({ lang, text, position, settings })
let textEdit = expect.objectContaining({ range: { start: position, end: position } })

expect(result.items.length).toBe(12106)
expect(result.items.filter((item) => item.label.endsWith(':')).length).toBe(215)
expect(result.items.length).toBe(12312)
expect(result.items.filter((item) => item.label.endsWith(':')).length).toBe(216)
expect(result).toEqual({
isIncomplete: false,
items: expect.arrayContaining([
Expand Down Expand Up @@ -522,11 +522,12 @@ withFixture('v4/basic', (c) => {

expect(resolved).toEqual({
...item,
detail: 'font-size: 0.875rem /* 8.75px */; line-height: 1.25rem /* 12.5px */;',
detail:
'font-size: var(--font-size-sm, 0.875rem /* 8.75px */); line-height: var(--font-size-sm--line-height, 1.25rem /* 12.5px */);',
documentation: {
kind: 'markdown',
value:
'```css\n.text-sm {\n font-size: 0.875rem /* 8.75px */;\n line-height: 1.25rem /* 12.5px */;\n}\n```',
'```css\n.text-sm {\n font-size: var(--font-size-sm, 0.875rem /* 8.75px */);\n line-height: var(--font-size-sm--line-height, 1.25rem /* 12.5px */);\n}\n```',
},
})
})
Expand All @@ -548,7 +549,7 @@ withFixture('v4/basic', (c) => {

expect(resolved).toEqual({
...item,
detail: 'background-color: #ef4444;',
detail: 'background-color: var(--color-red-500, #ef4444);',
documentation: '#ef4444',
})
})
Expand Down Expand Up @@ -577,19 +578,19 @@ withFixture('v4/workspaces', (c) => {

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

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

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

0 comments on commit 48056f2

Please sign in to comment.