Skip to content

Skip over preprocessor files when looking for v4 configs #1159

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 3 commits into from
Feb 4, 2025
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
144 changes: 143 additions & 1 deletion packages/tailwindcss-language-server/src/project-locator.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { test } from 'vitest'
import { expect, test } from 'vitest'
import * as path from 'node:path'
import { ProjectLocator } from './project-locator'
import { URL, fileURLToPath } from 'url'
import { Settings } from '@tailwindcss/language-service/src/util/state'
import { createResolver } from './resolver'
import { css, defineTest, js, json, scss, Storage, TestUtils } from './testing'

let settings: Settings = {
tailwindCSS: {
Expand Down Expand Up @@ -227,3 +228,144 @@ testFixture('v4/invalid-import-order', [
content: ['{URL}/package.json'],
},
])

// ---

testLocator({
name: 'Sass files are not detected with v4',
fs: {
'package.json': json`
{
"dependencies": {
"tailwindcss": "^4.0.2"
}
}
`,
'src/app1.scss': scss`
@import 'tailwindcss';
`,
'src/app2.scss': scss`
@use 'tailwindcss';
`,
},
expected: [],
})

testLocator({
name: 'Sass files are detected with v3',
fs: {
'package.json': json`
{
"dependencies": {
"tailwindcss": "^3.4.17"
}
}
`,
'tailwind.admin.config.js': js`
module.exports = {
content: ['./src/**/*.{html,js}'],
}
`,
'src/app.scss': scss`
@config '../tailwind.admin.config.js';
`,
},
expected: [
{
version: '3.4.17',
config: '/tailwind.admin.config.js',
content: ['/src/**/*.{html,js}'],
},
],
})

// ---

function testLocator({
name,
fs,
expected,
settings,
}: {
name: string
fs: Storage
settings?: Partial<Settings>
expected: any[]
}) {
defineTest({
name,
fs,
prepare,
async handle({ search }) {
let projects = await search(settings)

let details = projects.map((project) => ({
version: project.tailwind.isDefaultVersion
? `${project.tailwind.version} (bundled)`
: project.tailwind.version,
config: project.config.path,
content: project.documentSelector
.filter((selector) => selector.priority === 1 /** content */)
.map((selector) => selector.pattern)
.sort(),
selectors: project.documentSelector.map((selector) => selector.pattern).sort(),
}))

expect(details).toMatchObject(expected)
},
})
}

async function prepare({ root }: TestUtils) {
let defaultSettings = {
tailwindCSS: {
files: {
// We want to ignore `node_modules` folders otherwise we'll pick up
// configs from there and we don't want that.
exclude: ['**/node_modules'],
},
},
} as Settings

function adjustPath(filepath: string) {
filepath = filepath.replace(root, '{URL}')

if (filepath.startsWith('{URL}/')) {
filepath = filepath.slice(5)
}

return filepath
}

async function search(overrides?: Partial<Settings>) {
let settings = {
...defaultSettings,
...overrides,
}

let resolver = await createResolver({ root, tsconfig: true })
let locator = new ProjectLocator(root, settings, resolver)
let projects = await locator.search()

// Normalize all the paths for easier testing
for (let project of projects) {
project.folder = adjustPath(project.folder)
project.configPath = adjustPath(project.configPath)

// Config data
project.config.path = adjustPath(project.config.path)
project.config.packageRoot = adjustPath(project.config.packageRoot)
for (let entry of project.config.entries) {
entry.path = adjustPath(entry.path)
}

for (let selector of project.documentSelector ?? []) {
selector.pattern = adjustPath(selector.pattern)
}
}

return projects
}

return { search }
}
22 changes: 21 additions & 1 deletion packages/tailwindcss-language-server/src/project-locator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,18 @@ export class ProjectLocator {
if (!tailwind.features.includes('css-at-theme')) {
return null
}

// v4 does not support .sass, .scss, .less, and .styl files as configs
if (requiresPreprocessor(config.path)) {
console.warn(
`The config ${config.path} requires a preprocessor and is not supported by Tailwind CSS v4.0.`,
)

return null
}
}

// Don't boot a project for the CS config if using Tailwind v4
// Don't boot a project for the JS config if using Tailwind v4
if (config.type === 'js' && tailwind.features.includes('css-at-theme')) {
return null
}
Expand Down Expand Up @@ -649,6 +658,11 @@ class FileEntry {
}

async resolveImports(resolver: Resolver) {
// Files that require a preprocessor are not processed
if (requiresPreprocessor(this.path)) {
return
}

try {
let result = await resolveCssImports({ resolver, loose: true }).process(this.content, {
from: this.path,
Expand Down Expand Up @@ -733,3 +747,9 @@ class FileEntry {
)
}
}

function requiresPreprocessor(filepath: string) {
let ext = path.extname(filepath)

return ext === '.scss' || ext === '.sass' || ext === '.less' || ext === '.styl' || ext === '.pcss'
}
1 change: 1 addition & 0 deletions packages/tailwindcss-language-server/src/testing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ async function installDependenciesIn(dir: string) {
}

export const css = dedent
export const scss = dedent
export const html = dedent
export const js = dedent
export const json = dedent