Skip to content

Enable isolated declarations for @tailwindcss/language-service #1348

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 2 commits into from
May 8, 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
12 changes: 6 additions & 6 deletions packages/tailwindcss-language-server/src/testing/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import * as os from 'node:os'
import * as fs from 'node:fs/promises'
import * as path from 'node:path'
import * as proc from 'node:child_process'
import dedent from 'dedent'
import dedent, { type Dedent } from 'dedent'

export interface TestUtils<TestInput extends Record<string, any>> {
/** The "cwd" for this test */
Expand Down Expand Up @@ -160,8 +160,8 @@ 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
export const css: Dedent = dedent
export const scss: Dedent = dedent
export const html: Dedent = dedent
export const js: Dedent = dedent
export const json: Dedent = dedent
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ function classNameToAst(
obj = rule
}

return cssObjToAst(obj, state.modules.postcss)
return cssObjToAst(obj, state.modules.postcss.module)
}

function appendPseudosToSelector(selector: string, pseudos: string[]): string | null {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2230,7 +2230,7 @@ export async function doComplete(
document: TextDocument,
position: Position,
context?: CompletionContext,
) {
): Promise<CompletionList | null> {
if (state === null) return { items: [], isIncomplete: false }

const result =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export function visit(
nodes: postcss.AnyNode[],
cb: (node: postcss.AnyNode, path: postcss.AnyNode[]) => void,
path: postcss.AnyNode[] = [],
) {
): void {
for (let child of nodes) {
path = [...path, child]
cb(child, path)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,5 +72,5 @@ let templateExtensions = [
'rs',
]

export const IS_SCRIPT_SOURCE = new RegExp(`\\.(${scriptExtensions.join('|')})$`)
export const IS_TEMPLATE_SOURCE = new RegExp(`\\.(${templateExtensions.join('|')})$`)
export const IS_SCRIPT_SOURCE: RegExp = new RegExp(`\\.(${scriptExtensions.join('|')})$`)
export const IS_TEMPLATE_SOURCE: RegExp = new RegExp(`\\.(${templateExtensions.join('|')})$`)
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Range } from 'vscode-languageserver'

export function absoluteRange(range: Range, reference?: Range) {
export function absoluteRange(range: Range, reference?: Range): Range {
return {
start: {
line: (reference?.start.line || 0) + range.start.line,
Expand Down
4 changes: 2 additions & 2 deletions packages/tailwindcss-language-service/src/util/braceLevel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function braceLevel(text: string) {
export function braceLevel(text: string): number {
let count = 0

for (let i = text.length - 1; i >= 0; i--) {
Expand All @@ -10,7 +10,7 @@ export function braceLevel(text: string) {
return count
}

export function parenLevel(text: string) {
export function parenLevel(text: string): number {
let count = 0

for (let i = text.length - 1; i >= 0; i--) {
Expand Down
84 changes: 44 additions & 40 deletions packages/tailwindcss-language-service/src/util/colorEquivalents.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { Plugin } from 'postcss'
import type { Plugin, PluginCreator } from 'postcss'
import parseValue from 'postcss-value-parser'
import { inGamut } from 'culori'
import { formatColor, getColorFromValue } from './color'
Expand All @@ -16,51 +16,55 @@ export function getEquivalentColor(value: string): string {
return formatColor(color)
}

export function equivalentColorValues({ comments }: { comments: Comment[] }): Plugin {
return {
postcssPlugin: 'plugin',
Declaration(decl) {
if (!allowedFunctions.some((fn) => decl.value.includes(fn))) {
return
}

parseValue(decl.value).walk((node) => {
if (node.type !== 'function') {
return true
export const equivalentColorValues: PluginCreator<any> = Object.assign(
({ comments }: { comments: Comment[] }): Plugin => {
return {
postcssPlugin: 'plugin',
Declaration(decl) {
if (!allowedFunctions.some((fn) => decl.value.includes(fn))) {
return
}

if (node.value === 'var') {
return true
}
parseValue(decl.value).walk((node) => {
if (node.type !== 'function') {
return true
}

if (!allowedFunctions.includes(node.value)) {
return false
}
if (node.value === 'var') {
return true
}

const values = node.nodes.filter((n) => n.type === 'word').map((n) => n.value)
if (values.length < 3) {
return false
}
if (!allowedFunctions.includes(node.value)) {
return false
}

let color = `${node.value}(${values.join(' ')})`
const values = node.nodes.filter((n) => n.type === 'word').map((n) => n.value)
if (values.length < 3) {
return false
}

let equivalent = getEquivalentColor(color)
let color = `${node.value}(${values.join(' ')})`

if (equivalent === color) {
return false
}
let equivalent = getEquivalentColor(color)

comments.push({
index:
decl.source.start.offset +
`${decl.prop}${decl.raws.between}`.length +
node.sourceEndIndex,
value: equivalent,
})
if (equivalent === color) {
return false
}

return false
})
},
}
}
equivalentColorValues.postcss = true
comments.push({
index:
decl.source.start.offset +
`${decl.prop}${decl.raws.between}`.length +
node.sourceEndIndex,
value: equivalent,
})

return false
})
},
}
},
{
postcss: true as const,
},
)
4 changes: 2 additions & 2 deletions packages/tailwindcss-language-service/src/util/css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import type { State } from './state'
import { cssLanguages } from './languages'
import { getLanguageBoundaries } from './getLanguageBoundaries'

function getCssLanguages(state: State) {
function getCssLanguages(state: State): string[] {
const userCssLanguages = Object.keys(state.editor.userLanguages).filter((lang) =>
cssLanguages.includes(state.editor.userLanguages[lang]),
)

return [...cssLanguages, ...userCssLanguages]
}

export function isCssLanguage(state: State, lang: string) {
export function isCssLanguage(state: State, lang: string): boolean {
return getCssLanguages(state).indexOf(lang) !== -1
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,9 @@ function parse(obj, parent, postcss) {
}
}

export function cssObjToAst(obj, postcss) {
import type { Postcss, Root } from 'postcss'

export function cssObjToAst(obj: any, postcss: Postcss): Root {
var root = postcss.root()
parse(obj, root, postcss)
return root
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { segment } from './segment'
* This is meant to be a lower bound, as the actual size of a class can vary
* depending on the actual CSS properties and values, configured theme, etc…
*/
export function estimatedClassSize(className: string) {
export function estimatedClassSize(className: string): number {
let size = 0

// We estimate the size using the following structure which gives a reasonable
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { State } from './state'
import dlv from 'dlv'

export function flagEnabled(state: State, flag: string) {
export function flagEnabled(state: State, flag: string): boolean {
if (state.featureFlags.future.includes(flag)) {
return state.config.future === 'all' || dlv(state.config, ['future', flag], false)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const UNITS = ['byte', 'kilobyte', 'megabyte', 'gigabyte', 'terabyte', 'petabyte']

export function formatBytes(n: number) {
export function formatBytes(n: number): string {
let i = n == 0 ? 0 : Math.floor(Math.log(n) / Math.log(1000))
return new Intl.NumberFormat('en', {
notation: 'compact',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ let vueLexer = moo.states(vueStates)

let cache = new Cache<string, LanguageBoundary[] | null>({ max: 25, maxAge: 1000 })

export function clearLanguageBoundariesCache() {
export function clearLanguageBoundariesCache(): void {
cache.clear()
}

Expand Down
2 changes: 1 addition & 1 deletion packages/tailwindcss-language-service/src/util/jit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { addPixelEquivalentsToValue } from './pixelEquivalents'
import { addEquivalents } from './equivalents'
import { addThemeValues, inlineThemeValues } from './rewriting'

export function bigSign(bigIntValue) {
export function bigSign(bigIntValue: number | bigint): number {
// @ts-ignore
return (bigIntValue > 0n) - (bigIntValue < 0n)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { State } from '../util/state'
import { type Range } from 'vscode-languageserver'
import type { Range } from 'vscode-languageserver'
import type { TextDocument } from 'vscode-languageserver-textdocument'
import { getLanguageBoundaries } from '../util/getLanguageBoundaries'
import { isCssDoc } from '../util/css'
Expand Down
17 changes: 11 additions & 6 deletions packages/tailwindcss-language-service/src/util/languages.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { EditorState } from './state'

export const htmlLanguages = [
export const htmlLanguages: string[] = [
'aspnetcorerazor',
'astro',
'astro-markdown',
Expand Down Expand Up @@ -36,7 +36,7 @@ export const htmlLanguages = [
'twig',
]

export const cssLanguages = [
export const cssLanguages: string[] = [
'css',
'less',
'postcss',
Expand All @@ -47,7 +47,7 @@ export const cssLanguages = [
'tailwindcss',
]

export const jsLanguages = [
export const jsLanguages: string[] = [
'javascript',
'javascriptreact',
'reason',
Expand All @@ -58,16 +58,21 @@ export const jsLanguages = [
'glimmer-ts',
]

export const specialLanguages = ['vue', 'svelte']
export const specialLanguages: string[] = ['vue', 'svelte']

export const languages = [...cssLanguages, ...htmlLanguages, ...jsLanguages, ...specialLanguages]
export const languages: string[] = [
...cssLanguages,
...htmlLanguages,
...jsLanguages,
...specialLanguages,
]

const semicolonlessLanguages = ['sass', 'sugarss', 'stylus']

export function isSemicolonlessCssLanguage(
languageId: string,
userLanguages: EditorState['userLanguages'] = {},
) {
): boolean {
return (
semicolonlessLanguages.includes(languageId) ||
semicolonlessLanguages.includes(userLanguages[languageId])
Expand Down
6 changes: 3 additions & 3 deletions packages/tailwindcss-language-service/src/util/lexers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import moo from 'moo'
import { lazy } from './lazy'
import { Lazy, lazy } from './lazy'

const classAttributeStates: () => { [x: string]: moo.Rules } = () => ({
doubleClassList: {
Expand Down Expand Up @@ -66,7 +66,7 @@ const simpleClassAttributeStates: { [x: string]: moo.Rules } = {
},
}

export const getClassAttributeLexer = lazy(() => {
export const getClassAttributeLexer: Lazy<moo.Lexer> = lazy(() => {
let supportsNegativeLookbehind = true
try {
new RegExp('(?<!)')
Expand All @@ -90,7 +90,7 @@ export const getClassAttributeLexer = lazy(() => {
return moo.states(simpleClassAttributeStates)
})

export const getComputedClassAttributeLexer = lazy(() => {
export const getComputedClassAttributeLexer: Lazy<moo.Lexer> = lazy(() => {
let supportsNegativeLookbehind = true
try {
new RegExp('(?<!)')
Expand Down
Loading