Skip to content

Commit 53743f2

Browse files
authored
Add support for v3-alpha (#424)
* wip * Update v3 support, color handling * Update diagnostics and completions * bump language service * update context api usage * bump language service
1 parent 11e097e commit 53743f2

17 files changed

+667
-749
lines changed

package-lock.json

+305-259
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/tailwindcss-language-server/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,15 @@
2121
"access": "public"
2222
},
2323
"devDependencies": {
24-
"@ctrl/tinycolor": "3.1.4",
2524
"@parcel/watcher": "2.0.0-alpha.10",
2625
"@types/debounce": "1.2.0",
2726
"@types/node": "14.14.34",
2827
"@types/vscode": "1.52.0",
2928
"@vercel/ncc": "0.28.4",
3029
"builtin-modules": "3.2.0",
3130
"chokidar": "3.5.1",
31+
"color-name": "1.1.4",
32+
"culori": "0.20.1",
3233
"debounce": "1.2.0",
3334
"detective": "5.2.0",
3435
"dlv": "1.1.3",

packages/tailwindcss-language-server/src/server.ts

+104-43
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,15 @@ import {
6969
} from './lsp/diagnosticsProvider'
7070
import { doCodeActions } from 'tailwindcss-language-service/src/codeActions/codeActionProvider'
7171
import { getDocumentColors } from 'tailwindcss-language-service/src/documentColorProvider'
72-
import { fromRatio, names as namedColors } from '@ctrl/tinycolor'
7372
import { debounce } from 'debounce'
7473
import { getModuleDependencies } from './util/getModuleDependencies'
7574
import assert from 'assert'
7675
// import postcssLoadConfig from 'postcss-load-config'
7776
import * as parcel from './watcher/index.js'
77+
import { generateRules } from 'tailwindcss-language-service/src/util/jit'
78+
import { getColor } from 'tailwindcss-language-service/src/util/color'
79+
import * as culori from 'culori'
80+
import namedColors from 'color-name'
7881

7982
const CONFIG_FILE_GLOB = '{tailwind,tailwind.config}.{js,cjs}'
8083
const TRIGGER_CHARACTERS = [
@@ -101,7 +104,7 @@ declare var __non_webpack_require__: typeof require
101104
const connection =
102105
process.argv.length <= 2 ? createConnection(process.stdin, process.stdout) : createConnection()
103106

104-
console.log = connection.console.log.bind(connection.console)
107+
// console.log = connection.console.log.bind(connection.console)
105108
console.error = connection.console.error.bind(connection.console)
106109

107110
process.on('unhandledRejection', (e: any) => {
@@ -150,6 +153,15 @@ function first<T>(...options: Array<() => T>): T {
150153
}
151154
}
152155

156+
function firstOptional<T>(...options: Array<() => T>): T | undefined {
157+
for (let i = 0; i < options.length; i++) {
158+
let option = options[i]
159+
try {
160+
return option()
161+
} catch (_) {}
162+
}
163+
}
164+
153165
interface ProjectService {
154166
state: State
155167
tryInit: () => Promise<void>
@@ -528,8 +540,8 @@ async function createProjectService(
528540
}
529541

530542
if (semver.gte(tailwindcssVersion, '1.99.0')) {
531-
applyComplexClasses = __non_webpack_require__(
532-
resolveFrom(tailwindDir, './lib/lib/substituteClassApplyAtRules')
543+
applyComplexClasses = firstOptional(() =>
544+
__non_webpack_require__(resolveFrom(tailwindDir, './lib/lib/substituteClassApplyAtRules'))
533545
)
534546
} else if (semver.gte(tailwindcssVersion, '1.7.0')) {
535547
applyComplexClasses = __non_webpack_require__(
@@ -551,6 +563,13 @@ async function createProjectService(
551563

552564
try {
553565
let createContext = first(
566+
() => {
567+
let createContextFn = __non_webpack_require__(
568+
resolveFrom(configDir, 'tailwindcss/lib/lib/setupContextUtils')
569+
).createContext
570+
assert.strictEqual(typeof createContextFn, 'function')
571+
return (state) => createContextFn(state.config)
572+
},
554573
() => {
555574
let createContextFn = __non_webpack_require__(
556575
resolveFrom(configDir, 'tailwindcss/lib/jit/lib/setupContextUtils')
@@ -582,17 +601,30 @@ async function createProjectService(
582601

583602
jitModules = {
584603
generateRules: {
585-
module: __non_webpack_require__(
586-
resolveFrom(configDir, 'tailwindcss/lib/jit/lib/generateRules')
587-
).generateRules,
604+
module: first(
605+
() =>
606+
__non_webpack_require__(resolveFrom(configDir, 'tailwindcss/lib/lib/generateRules'))
607+
.generateRules,
608+
() =>
609+
__non_webpack_require__(
610+
resolveFrom(configDir, 'tailwindcss/lib/jit/lib/generateRules')
611+
).generateRules
612+
),
588613
},
589614
createContext: {
590615
module: createContext,
591616
},
592617
expandApplyAtRules: {
593-
module: __non_webpack_require__(
594-
resolveFrom(configDir, 'tailwindcss/lib/jit/lib/expandApplyAtRules')
595-
).default,
618+
module: first(
619+
() =>
620+
__non_webpack_require__(
621+
resolveFrom(configDir, 'tailwindcss/lib/lib/expandApplyAtRules')
622+
).default,
623+
() =>
624+
__non_webpack_require__(
625+
resolveFrom(configDir, 'tailwindcss/lib/jit/lib/expandApplyAtRules')
626+
).default
627+
),
596628
},
597629
}
598630
} catch (_) {
@@ -728,6 +760,8 @@ async function createProjectService(
728760
let presetVariants: any[] = []
729761
let originalConfig: any
730762

763+
let isV3 = semver.gte(tailwindcss.version, '2.99.0')
764+
731765
let hook = new Hook(fs.realpathSync(state.configPath), (exports) => {
732766
originalConfig = klona(exports)
733767

@@ -736,7 +770,7 @@ async function createProjectService(
736770
separator = ':'
737771
}
738772
dset(exports, sepLocation, `__TWSEP__${separator}__TWSEP__`)
739-
exports.purge = []
773+
exports[isV3 ? 'content' : 'purge'] = []
740774

741775
let mode: any
742776
if (Array.isArray(exports.presets)) {
@@ -753,7 +787,9 @@ async function createProjectService(
753787
}
754788
delete exports.mode
755789

756-
if (state.modules.jit && mode === 'jit') {
790+
let isJit = isV3 || (state.modules.jit && mode === 'jit')
791+
792+
if (isJit) {
757793
state.jit = true
758794
exports.variants = []
759795

@@ -828,32 +864,42 @@ async function createProjectService(
828864
if (state.jit) {
829865
state.jitContext = state.modules.jit.createContext.module(state)
830866
state.jitContext.tailwindConfig.separator = state.config.separator
867+
if (state.jitContext.getClassList) {
868+
state.classList = state.jitContext.getClassList().map((className) => {
869+
return [className, { color: getColor(state, className) }]
870+
})
871+
}
831872
}
832873

833874
let postcssResult: Result
834-
try {
835-
postcssResult = await postcss
836-
.module([
837-
// ...state.postcssPlugins.before.map((x) => x()),
838-
tailwindcss.module(state.configPath),
839-
// ...state.postcssPlugins.after.map((x) => x()),
840-
])
841-
.process(
842-
[
843-
semver.gte(tailwindcss.version, '0.99.0') ? 'base' : 'preflight',
844-
'components',
845-
'utilities',
846-
]
847-
.map((x) => `/*__tw_intellisense_layer_${x}__*/\n@tailwind ${x};`)
848-
.join('\n'),
849-
{
850-
from: undefined,
851-
}
852-
)
853-
} catch (error) {
854-
throw error
855-
} finally {
875+
876+
if (state.classList) {
856877
hook.unhook()
878+
} else {
879+
try {
880+
postcssResult = await postcss
881+
.module([
882+
// ...state.postcssPlugins.before.map((x) => x()),
883+
tailwindcss.module(state.configPath),
884+
// ...state.postcssPlugins.after.map((x) => x()),
885+
])
886+
.process(
887+
[
888+
semver.gte(tailwindcss.version, '0.99.0') ? 'base' : 'preflight',
889+
'components',
890+
'utilities',
891+
]
892+
.map((x) => `/*__tw_intellisense_layer_${x}__*/\n@tailwind ${x};`)
893+
.join('\n'),
894+
{
895+
from: undefined,
896+
}
897+
)
898+
} catch (error) {
899+
throw error
900+
} finally {
901+
hook.unhook()
902+
}
857903
}
858904

859905
if (state.dependencies) {
@@ -865,7 +911,9 @@ async function createProjectService(
865911
state.configId = getConfigId(state.configPath, state.dependencies)
866912

867913
state.plugins = await getPlugins(originalConfig)
868-
state.classNames = (await extractClassNames(postcssResult.root)) as ClassNames
914+
if (postcssResult) {
915+
state.classNames = (await extractClassNames(postcssResult.root)) as ClassNames
916+
}
869917
state.variants = getVariants(state)
870918

871919
let screens = dlv(state.config, 'theme.screens', dlv(state.config, 'screens', {}))
@@ -939,16 +987,25 @@ async function createProjectService(
939987
let currentColor = match[1]
940988

941989
let isNamedColor = colorNames.includes(currentColor)
942-
let color = fromRatio({
990+
991+
let color: culori.RgbColor = {
992+
mode: 'rgb',
943993
r: params.color.red,
944994
g: params.color.green,
945995
b: params.color.blue,
946-
a: params.color.alpha,
947-
})
996+
alpha: params.color.alpha,
997+
}
998+
999+
let hexValue = culori.formatHex8(color)
1000+
1001+
if (!isNamedColor && (currentColor.length === 4 || currentColor.length === 5)) {
1002+
let [, ...chars] =
1003+
hexValue.match(/^#([a-f\d])\1([a-f\d])\2([a-f\d])\3(?:([a-f\d])\4)?$/i) ?? []
1004+
if (chars.length) {
1005+
hexValue = `#${chars.filter(Boolean).join('')}`
1006+
}
1007+
}
9481008

949-
let hexValue = color.toHex8String(
950-
!isNamedColor && (currentColor.length === 4 || currentColor.length === 5)
951-
)
9521009
if (hexValue.length === 5) {
9531010
hexValue = hexValue.replace(/f$/, '')
9541011
} else if (hexValue.length === 9) {
@@ -959,8 +1016,12 @@ async function createProjectService(
9591016

9601017
return [
9611018
hexValue,
962-
color.toRgbString().replace(/ /g, ''),
963-
color.toHslString().replace(/ /g, ''),
1019+
culori.formatRgb(color).replace(/ /g, ''),
1020+
culori
1021+
.formatHsl(color)
1022+
.replace(/ /g, '')
1023+
// round numbers
1024+
.replace(/\d+\.\d+(%?)/g, (value, suffix) => `${Math.round(parseFloat(value))}${suffix}`),
9641025
].map((value) => ({ label: `${prefix}-[${value}]` }))
9651026
},
9661027
}

packages/tailwindcss-language-server/tsconfig.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@
1414
"tailwindcss-language-service/*": ["../packages/tailwindcss-language-service/*"]
1515
}
1616
},
17-
"include": ["src", "../packages/tailwindcss-language-service"]
17+
"include": ["src", "../packages/tailwindcss-language-service", "../../types"]
1818
}

0 commit comments

Comments
 (0)