prevent crash on config error
parent
850ad5c0a5
commit
568e078522
|
@ -17,7 +17,7 @@ function glob(pattern, options = {}) {
|
||||||
let g = new nodeGlob.Glob(pattern, options)
|
let g = new nodeGlob.Glob(pattern, options)
|
||||||
let matches = []
|
let matches = []
|
||||||
let max = dlv(options, 'max', Infinity)
|
let max = dlv(options, 'max', Infinity)
|
||||||
g.on('match', match => {
|
g.on('match', (match) => {
|
||||||
matches.push(path.resolve(options.cwd || process.cwd(), match))
|
matches.push(path.resolve(options.cwd || process.cwd(), match))
|
||||||
if (matches.length === max) {
|
if (matches.length === max) {
|
||||||
g.abort()
|
g.abort()
|
||||||
|
@ -38,39 +38,35 @@ function arraysEqual(arr1, arr2) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CONFIG_GLOB =
|
||||||
|
'**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js'
|
||||||
|
|
||||||
export default async function getClassNames(
|
export default async function getClassNames(
|
||||||
cwd = process.cwd(),
|
cwd = process.cwd(),
|
||||||
{ onChange = () => {} } = {}
|
{ onChange = () => {} } = {}
|
||||||
) {
|
) {
|
||||||
let configPath
|
async function run() {
|
||||||
let postcss
|
let configPath
|
||||||
let tailwindcss
|
let postcss
|
||||||
let version
|
let tailwindcss
|
||||||
|
let version
|
||||||
|
|
||||||
try {
|
configPath = await glob(CONFIG_GLOB, {
|
||||||
configPath = await glob(
|
cwd,
|
||||||
'**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js',
|
ignore: '**/node_modules/**',
|
||||||
{
|
max: 1,
|
||||||
cwd,
|
})
|
||||||
ignore: '**/node_modules/**',
|
|
||||||
max: 1
|
|
||||||
}
|
|
||||||
)
|
|
||||||
invariant(configPath.length === 1, 'No Tailwind CSS config found.')
|
invariant(configPath.length === 1, 'No Tailwind CSS config found.')
|
||||||
configPath = configPath[0]
|
configPath = configPath[0]
|
||||||
postcss = importFrom(cwd, 'postcss')
|
postcss = importFrom(cwd, 'postcss')
|
||||||
tailwindcss = importFrom(cwd, 'tailwindcss')
|
tailwindcss = importFrom(cwd, 'tailwindcss')
|
||||||
version = importFrom(cwd, 'tailwindcss/package.json').version
|
version = importFrom(cwd, 'tailwindcss/package.json').version
|
||||||
} catch (_) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
async function run() {
|
|
||||||
const sepLocation = semver.gte(version, '0.99.0')
|
const sepLocation = semver.gte(version, '0.99.0')
|
||||||
? ['separator']
|
? ['separator']
|
||||||
: ['options', 'separator']
|
: ['options', 'separator']
|
||||||
let userSeperator
|
let userSeperator
|
||||||
let hook = Hook(configPath, exports => {
|
let hook = Hook(configPath, (exports) => {
|
||||||
userSeperator = dlv(exports, sepLocation)
|
userSeperator = dlv(exports, sepLocation)
|
||||||
dset(exports, sepLocation, '__TAILWIND_SEPARATOR__')
|
dset(exports, sepLocation, '__TAILWIND_SEPARATOR__')
|
||||||
return exports
|
return exports
|
||||||
|
@ -97,35 +93,48 @@ export default async function getClassNames(
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
configPath,
|
||||||
config: resolveConfig({ cwd, config }),
|
config: resolveConfig({ cwd, config }),
|
||||||
separator: typeof userSeperator === 'undefined' ? ':' : userSeperator,
|
separator: typeof userSeperator === 'undefined' ? ':' : userSeperator,
|
||||||
classNames: await extractClassNames(ast),
|
classNames: await extractClassNames(ast),
|
||||||
dependencies: [configPath, ...hook.deps],
|
dependencies: hook.deps,
|
||||||
plugins: getPlugins(config),
|
plugins: getPlugins(config),
|
||||||
variants: getVariants({ config, version, postcss })
|
variants: getVariants({ config, version, postcss }),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let watcher
|
let watcher
|
||||||
function watch(files) {
|
function watch(files = []) {
|
||||||
if (watcher) watcher.close()
|
if (watcher) watcher.close()
|
||||||
watcher = chokidar
|
watcher = chokidar
|
||||||
.watch(files)
|
.watch([CONFIG_GLOB, ...files])
|
||||||
.on('change', handleChange)
|
.on('change', handleChange)
|
||||||
.on('unlink', handleChange)
|
.on('unlink', handleChange)
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = await run()
|
|
||||||
watch(result.dependencies)
|
|
||||||
|
|
||||||
async function handleChange() {
|
async function handleChange() {
|
||||||
const prevDeps = result.dependencies
|
const prevDeps = result ? result.dependencies : []
|
||||||
result = await run()
|
try {
|
||||||
|
result = await run()
|
||||||
|
} catch (_) {
|
||||||
|
onChange(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
if (!arraysEqual(prevDeps, result.dependencies)) {
|
if (!arraysEqual(prevDeps, result.dependencies)) {
|
||||||
watch(result.dependencies)
|
watch(result.dependencies)
|
||||||
}
|
}
|
||||||
onChange(result)
|
onChange(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let result
|
||||||
|
try {
|
||||||
|
result = await run()
|
||||||
|
} catch (_) {
|
||||||
|
watch()
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(result.dependencies)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ import {
|
||||||
DidChangeConfigurationNotification,
|
DidChangeConfigurationNotification,
|
||||||
} from 'vscode-languageserver'
|
} from 'vscode-languageserver'
|
||||||
import getTailwindState from 'tailwindcss-class-names'
|
import getTailwindState from 'tailwindcss-class-names'
|
||||||
import { State, Settings } from './util/state'
|
import { State, Settings, EditorState } from './util/state'
|
||||||
import {
|
import {
|
||||||
provideCompletions,
|
provideCompletions,
|
||||||
resolveCompletionItem,
|
resolveCompletionItem,
|
||||||
|
@ -27,7 +27,7 @@ import { provideHover } from './providers/hoverProvider'
|
||||||
import { URI } from 'vscode-uri'
|
import { URI } from 'vscode-uri'
|
||||||
import { getDocumentSettings } from './util/getDocumentSettings'
|
import { getDocumentSettings } from './util/getDocumentSettings'
|
||||||
|
|
||||||
let state: State = null
|
let state: State = { enabled: false }
|
||||||
let connection = createConnection(ProposedFeatures.all)
|
let connection = createConnection(ProposedFeatures.all)
|
||||||
let documents = new TextDocuments()
|
let documents = new TextDocuments()
|
||||||
let workspaceFolder: string | null
|
let workspaceFolder: string | null
|
||||||
|
@ -46,28 +46,43 @@ documents.listen(connection)
|
||||||
|
|
||||||
connection.onInitialize(
|
connection.onInitialize(
|
||||||
async (params: InitializeParams): Promise<InitializeResult> => {
|
async (params: InitializeParams): Promise<InitializeResult> => {
|
||||||
state = await getTailwindState(
|
|
||||||
params.rootPath || URI.parse(params.rootUri).path,
|
|
||||||
{
|
|
||||||
onChange: (newState: State): void => {
|
|
||||||
state = { ...newState, editor: state.editor }
|
|
||||||
connection.sendNotification('tailwindcss/configUpdated', [
|
|
||||||
state.dependencies[0],
|
|
||||||
state.config,
|
|
||||||
state.plugins,
|
|
||||||
])
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const capabilities = params.capabilities
|
const capabilities = params.capabilities
|
||||||
|
|
||||||
state.editor = {
|
const editorState: EditorState = {
|
||||||
connection,
|
connection,
|
||||||
documents,
|
documents,
|
||||||
documentSettings,
|
documentSettings,
|
||||||
globalSettings,
|
globalSettings,
|
||||||
capabilities: { configuration: capabilities.workspace && !!capabilities.workspace.configuration },
|
capabilities: {
|
||||||
|
configuration:
|
||||||
|
capabilities.workspace && !!capabilities.workspace.configuration,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const tailwindState = await getTailwindState(
|
||||||
|
params.rootPath || URI.parse(params.rootUri).path,
|
||||||
|
{
|
||||||
|
onChange: (newState: State): void => {
|
||||||
|
if (newState) {
|
||||||
|
state = { ...newState, enabled: true, editor: editorState }
|
||||||
|
connection.sendNotification('tailwindcss/configUpdated', [
|
||||||
|
state.configPath,
|
||||||
|
state.config,
|
||||||
|
state.plugins,
|
||||||
|
])
|
||||||
|
} else {
|
||||||
|
state = { enabled: false, editor: editorState }
|
||||||
|
// TODO
|
||||||
|
// connection.sendNotification('tailwindcss/configUpdated', [null])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if (tailwindState) {
|
||||||
|
state = { enabled: true, editor: editorState, ...tailwindState }
|
||||||
|
} else {
|
||||||
|
state = { enabled: false, editor: editorState }
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -79,7 +94,20 @@ connection.onInitialize(
|
||||||
textDocumentSync: documents.syncKind,
|
textDocumentSync: documents.syncKind,
|
||||||
completionProvider: {
|
completionProvider: {
|
||||||
resolveProvider: true,
|
resolveProvider: true,
|
||||||
triggerCharacters: ['"', "'", '`', ' ', '.', '[', state.separator],
|
triggerCharacters: [
|
||||||
|
// class attributes
|
||||||
|
'"',
|
||||||
|
"'",
|
||||||
|
'`',
|
||||||
|
// between class names
|
||||||
|
' ',
|
||||||
|
// @apply and emmet-style
|
||||||
|
'.',
|
||||||
|
// config/theme helper
|
||||||
|
'[',
|
||||||
|
// TODO: restart server if separater changes?
|
||||||
|
typeof state.separator === 'undefined' ? ':' : state.separator,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
hoverProvider: true,
|
hoverProvider: true,
|
||||||
},
|
},
|
||||||
|
@ -97,7 +125,7 @@ connection.onInitialized &&
|
||||||
}
|
}
|
||||||
|
|
||||||
connection.sendNotification('tailwindcss/configUpdated', [
|
connection.sendNotification('tailwindcss/configUpdated', [
|
||||||
state.dependencies[0],
|
state.configPath,
|
||||||
state.config,
|
state.config,
|
||||||
state.plugins,
|
state.plugins,
|
||||||
])
|
])
|
||||||
|
@ -120,18 +148,21 @@ connection.onDidChangeConfiguration((change) => {
|
||||||
|
|
||||||
connection.onCompletion(
|
connection.onCompletion(
|
||||||
(params: CompletionParams): Promise<CompletionList> => {
|
(params: CompletionParams): Promise<CompletionList> => {
|
||||||
|
if (!state.enabled) return null
|
||||||
return provideCompletions(state, params)
|
return provideCompletions(state, params)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
connection.onCompletionResolve(
|
connection.onCompletionResolve(
|
||||||
(item: CompletionItem): CompletionItem => {
|
(item: CompletionItem): CompletionItem => {
|
||||||
|
if (!state.enabled) return null
|
||||||
return resolveCompletionItem(state, item)
|
return resolveCompletionItem(state, item)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
connection.onHover(
|
connection.onHover(
|
||||||
(params: TextDocumentPositionParams): Hover => {
|
(params: TextDocumentPositionParams): Hover => {
|
||||||
|
if (!state.enabled) return null
|
||||||
return provideHover(state, params)
|
return provideHover(state, params)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -28,13 +28,15 @@ export type Settings = {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type State = null | {
|
export type State = null | {
|
||||||
config: any
|
enabled: boolean
|
||||||
separator: string
|
configPath?: string
|
||||||
plugins: any[]
|
config?: any
|
||||||
variants: string[]
|
separator?: string
|
||||||
classNames: ClassNames
|
plugins?: any[]
|
||||||
dependencies: string[]
|
variants?: string[]
|
||||||
editor: EditorState
|
classNames?: ClassNames
|
||||||
|
dependencies?: string[]
|
||||||
|
editor?: EditorState
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DocumentClassList = {
|
export type DocumentClassList = {
|
||||||
|
|
Loading…
Reference in New Issue