tailwind-ctp-intellisense/packages/tailwindcss-class-names/src/index.js

153 lines
3.7 KiB
JavaScript
Raw Normal View History

2020-04-11 21:20:45 +00:00
import extractClassNames from './extractClassNames.mjs'
import Hook from './hook.mjs'
import dlv from 'dlv'
import dset from 'dset'
import importFrom from 'import-from'
import chokidar from 'chokidar'
import semver from 'semver'
import invariant from 'tiny-invariant'
import getPlugins from './getPlugins'
import getVariants from './getVariants'
import resolveConfig from './resolveConfig'
2020-04-23 18:54:01 +00:00
import * as util from 'util'
import * as path from 'path'
2020-04-25 21:41:17 +00:00
import { glob } from './glob'
import { getUtilityConfigMap } from './getUtilityConfigMap'
2020-04-23 18:54:01 +00:00
function TailwindConfigError(error) {
Error.call(this)
Error.captureStackTrace(this, this.constructor)
this.name = this.constructor.name
this.message = error.message
this.stack = error.stack
}
util.inherits(TailwindConfigError, Error)
2020-04-11 21:20:45 +00:00
function arraysEqual(arr1, arr2) {
2020-04-19 16:30:25 +00:00
return (
JSON.stringify(arr1.concat([]).sort()) ===
JSON.stringify(arr2.concat([]).sort())
)
2020-04-11 21:20:45 +00:00
}
2020-04-22 19:29:36 +00:00
const CONFIG_GLOB =
'**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.js'
2020-04-11 21:20:45 +00:00
export default async function getClassNames(
cwd = process.cwd(),
{ onChange = () => {} } = {}
) {
2020-04-22 19:29:36 +00:00
async function run() {
let configPath
let postcss
let tailwindcss
let version
2020-04-11 21:20:45 +00:00
2020-04-22 19:29:36 +00:00
configPath = await glob(CONFIG_GLOB, {
cwd,
ignore: '**/node_modules/**',
max: 1,
})
2020-04-11 21:20:45 +00:00
invariant(configPath.length === 1, 'No Tailwind CSS config found.')
configPath = configPath[0]
const configDir = path.dirname(configPath)
postcss = importFrom(configDir, 'postcss')
tailwindcss = importFrom(configDir, 'tailwindcss')
version = importFrom(configDir, 'tailwindcss/package.json').version
2020-04-11 21:20:45 +00:00
const sepLocation = semver.gte(version, '0.99.0')
? ['separator']
: ['options', 'separator']
let userSeperator
2020-04-22 19:29:36 +00:00
let hook = Hook(configPath, (exports) => {
2020-04-11 21:20:45 +00:00
userSeperator = dlv(exports, sepLocation)
dset(exports, sepLocation, '__TAILWIND_SEPARATOR__')
return exports
})
hook.watch()
2020-04-23 18:54:01 +00:00
let config
try {
config = __non_webpack_require__(configPath)
} catch (error) {
throw new TailwindConfigError(error)
}
2020-04-11 21:20:45 +00:00
hook.unwatch()
const ast = await postcss([tailwindcss(configPath)]).process(
`
@tailwind components;
@tailwind utilities;
`,
{ from: undefined }
)
hook.unhook()
if (typeof userSeperator !== 'undefined') {
dset(config, sepLocation, userSeperator)
} else {
delete config[sepLocation]
}
const resolvedConfig = resolveConfig({ cwd: configDir, config })
2020-04-25 21:41:17 +00:00
2020-04-11 21:20:45 +00:00
return {
version,
2020-04-22 19:29:36 +00:00
configPath,
2020-04-25 21:41:17 +00:00
config: resolvedConfig,
2020-04-11 21:20:45 +00:00
separator: typeof userSeperator === 'undefined' ? ':' : userSeperator,
classNames: await extractClassNames(ast),
2020-04-22 19:29:36 +00:00
dependencies: hook.deps,
2020-04-11 21:20:45 +00:00
plugins: getPlugins(config),
2020-04-22 19:29:36 +00:00
variants: getVariants({ config, version, postcss }),
2020-04-25 21:41:17 +00:00
utilityConfigMap: await getUtilityConfigMap({
cwd: configDir,
2020-04-25 21:41:17 +00:00
resolvedConfig,
postcss,
}),
2020-04-11 21:20:45 +00:00
}
}
let watcher
2020-04-22 19:29:36 +00:00
function watch(files = []) {
2020-04-11 21:20:45 +00:00
if (watcher) watcher.close()
watcher = chokidar
2020-04-29 18:13:48 +00:00
.watch([CONFIG_GLOB, ...files], { cwd })
2020-04-11 21:20:45 +00:00
.on('change', handleChange)
.on('unlink', handleChange)
}
async function handleChange() {
2020-04-22 19:29:36 +00:00
const prevDeps = result ? result.dependencies : []
try {
result = await run()
2020-04-23 18:54:01 +00:00
} catch (error) {
if (error instanceof TailwindConfigError) {
onChange({ error })
} else {
onChange(null)
}
2020-04-22 19:29:36 +00:00
return
}
2020-04-11 21:20:45 +00:00
if (!arraysEqual(prevDeps, result.dependencies)) {
watch(result.dependencies)
}
onChange(result)
}
2020-04-22 19:29:36 +00:00
let result
try {
result = await run()
} catch (_) {
watch()
return null
}
watch(result.dependencies)
2020-04-11 21:20:45 +00:00
return result
}