diff --git a/packages/tailwindcss-language-server/src/css/extract-source-directives.ts b/packages/tailwindcss-language-server/src/css/extract-source-directives.ts index 9de33a9b3..a97e35594 100644 --- a/packages/tailwindcss-language-server/src/css/extract-source-directives.ts +++ b/packages/tailwindcss-language-server/src/css/extract-source-directives.ts @@ -1,12 +1,21 @@ import type { Plugin } from 'postcss' +import type { SourcePattern } from '../project-locator' -export function extractSourceDirectives(sources: string[]): Plugin { +export function extractSourceDirectives(sources: SourcePattern[]): Plugin { return { postcssPlugin: 'extract-at-rules', AtRule: { source: ({ params }) => { + let negated = /^not\s+/.test(params) + + if (negated) params = params.slice(4).trimStart() + if (params[0] !== '"' && params[0] !== "'") return - sources.push(params.slice(1, -1)) + + sources.push({ + pattern: params.slice(1, -1), + negated, + }) }, }, } diff --git a/packages/tailwindcss-language-server/src/project-locator.test.ts b/packages/tailwindcss-language-server/src/project-locator.test.ts index ae3598e5b..429f0b27b 100644 --- a/packages/tailwindcss-language-server/src/project-locator.test.ts +++ b/packages/tailwindcss-language-server/src/project-locator.test.ts @@ -265,6 +265,40 @@ testLocator({ ], }) +testLocator({ + // TODO: Enable once v4.1 is released + options: { skip: true }, + name: 'automatic content detection with negative custom sources', + fs: { + 'package.json': json` + { + "dependencies": { + "tailwindcss": "0.0.0-insiders.3e53e25", + "@tailwindcss/oxide": "0.0.0-insiders.3e53e25" + } + } + `, + 'src/app.css': css` + @import 'tailwindcss'; + @source './**/*.html'; + @source not './ignored.html'; + `, + 'src/index.html': html`
`, + 'src/ignored.html': html`
`, + }, + expected: [ + { + config: '/src/app.css', + content: [ + '/*', + '/package.json', + '/src/index.html', + '/src/{**/*.html,**/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,json,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}}', + ], + }, + ], +}) + testFixture('v4/missing-files', [ // { diff --git a/packages/tailwindcss-language-server/src/project-locator.ts b/packages/tailwindcss-language-server/src/project-locator.ts index c75ae1dee..c04738bf8 100644 --- a/packages/tailwindcss-language-server/src/project-locator.ts +++ b/packages/tailwindcss-language-server/src/project-locator.ts @@ -623,7 +623,7 @@ async function* contentSelectorsFromCssConfig( async function* detectContentFiles( base: string, inputFile: string, - sources: string[], + sources: SourcePattern[], resolver: Resolver, ): AsyncIterable { try { @@ -636,9 +636,10 @@ async function* detectContentFiles( oxidePath, oxideVersion: oxidePackageJson.version, basePath: base, - sources: sources.map((pattern) => ({ + sources: sources.map((source) => ({ base: path.dirname(inputFile), - pattern, + pattern: source.pattern, + negated: source.negated, })), }) @@ -672,11 +673,16 @@ type ConfigEntry = { content: ContentItem[] } +export interface SourcePattern { + pattern: string + negated: boolean +} + class FileEntry { content: string | null deps: FileEntry[] = [] realpath: string | null - sources: string[] = [] + sources: SourcePattern[] = [] meta: TailwindStylesheet | null = null constructor(