2020-05-03 14:57:15 +00:00
|
|
|
import extractClassNames from './extractClassNames'
|
|
|
|
import Hook from './hook'
|
2020-04-11 21:20:45 +00:00
|
|
|
import dlv from 'dlv'
|
|
|
|
import dset from 'dset'
|
|
|
|
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-29 18:20:37 +00:00
|
|
|
import * as path from 'path'
|
2020-06-24 12:47:55 +00:00
|
|
|
import * as fs from 'fs'
|
2020-04-25 21:41:17 +00:00
|
|
|
import { getUtilityConfigMap } from './getUtilityConfigMap'
|
2020-06-24 10:55:17 +00:00
|
|
|
import glob from 'fast-glob'
|
|
|
|
import normalizePath from 'normalize-path'
|
2020-08-23 15:02:58 +00:00
|
|
|
import { withUserEnvironment } from './environment'
|
2020-12-02 13:57:17 +00:00
|
|
|
import execa from 'execa'
|
2020-04-23 18:54:01 +00:00
|
|
|
|
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 =
|
2020-12-08 13:26:02 +00:00
|
|
|
'**/{tailwind,tailwind.config,tailwind-config,.tailwindrc}.{js,cjs}'
|
2020-04-22 19:29:36 +00:00
|
|
|
|
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() {
|
2020-06-24 10:55:17 +00:00
|
|
|
const configPaths = (
|
|
|
|
await glob(CONFIG_GLOB, {
|
|
|
|
cwd,
|
|
|
|
ignore: ['**/node_modules'],
|
|
|
|
onlyFiles: true,
|
|
|
|
absolute: true,
|
2020-08-27 11:16:42 +00:00
|
|
|
suppressErrors: true,
|
2020-06-24 10:55:17 +00:00
|
|
|
})
|
|
|
|
)
|
|
|
|
.map(normalizePath)
|
|
|
|
.sort((a, b) => a.split('/').length - b.split('/').length)
|
|
|
|
.map(path.normalize)
|
|
|
|
|
|
|
|
invariant(configPaths.length > 0, 'No Tailwind CSS config found.')
|
|
|
|
const configPath = configPaths[0]
|
2020-12-01 19:03:31 +00:00
|
|
|
console.log(`Found Tailwind config file: ${configPath}`)
|
2020-04-29 18:20:37 +00:00
|
|
|
const configDir = path.dirname(configPath)
|
2020-11-24 13:27:19 +00:00
|
|
|
const {
|
|
|
|
version,
|
|
|
|
featureFlags = { future: [], experimental: [] },
|
2020-12-03 17:23:50 +00:00
|
|
|
tailwindBase,
|
2020-12-10 13:47:05 +00:00
|
|
|
} = loadMeta(configDir, cwd)
|
2020-04-11 21:20:45 +00:00
|
|
|
|
2020-12-03 17:23:50 +00:00
|
|
|
console.log(`Found tailwindcss v${version}: ${tailwindBase}`)
|
2020-08-21 14:30:29 +00:00
|
|
|
|
2020-04-11 21:20:45 +00:00
|
|
|
const sepLocation = semver.gte(version, '0.99.0')
|
|
|
|
? ['separator']
|
|
|
|
: ['options', 'separator']
|
|
|
|
let userSeperator
|
2020-06-20 18:05:36 +00:00
|
|
|
let userPurge
|
2020-06-24 12:47:55 +00:00
|
|
|
let hook = Hook(fs.realpathSync(configPath), (exports) => {
|
2020-04-11 21:20:45 +00:00
|
|
|
userSeperator = dlv(exports, sepLocation)
|
2020-06-20 18:05:36 +00:00
|
|
|
userPurge = exports.purge
|
2020-04-11 21:20:45 +00:00
|
|
|
dset(exports, sepLocation, '__TAILWIND_SEPARATOR__')
|
2020-06-20 18:05:36 +00:00
|
|
|
exports.purge = {}
|
2020-04-11 21:20:45 +00:00
|
|
|
return exports
|
|
|
|
})
|
|
|
|
|
|
|
|
hook.watch()
|
2020-04-23 18:54:01 +00:00
|
|
|
let config
|
|
|
|
try {
|
|
|
|
config = __non_webpack_require__(configPath)
|
|
|
|
} catch (error) {
|
2020-08-25 15:59:13 +00:00
|
|
|
hook.unwatch()
|
|
|
|
hook.unhook()
|
|
|
|
throw error
|
2020-04-23 18:54:01 +00:00
|
|
|
}
|
2020-08-25 15:59:13 +00:00
|
|
|
|
2020-04-11 21:20:45 +00:00
|
|
|
hook.unwatch()
|
|
|
|
|
2020-11-24 13:27:19 +00:00
|
|
|
const {
|
|
|
|
base,
|
|
|
|
components,
|
|
|
|
utilities,
|
|
|
|
resolvedConfig,
|
|
|
|
browserslist,
|
|
|
|
postcss,
|
|
|
|
} = await withPackages(
|
2020-08-23 15:02:58 +00:00
|
|
|
configDir,
|
2020-12-10 13:47:05 +00:00
|
|
|
cwd,
|
2020-12-03 17:23:50 +00:00
|
|
|
async ({
|
|
|
|
postcss,
|
|
|
|
tailwindcss,
|
|
|
|
browserslistCommand,
|
|
|
|
browserslistArgs,
|
|
|
|
}) => {
|
2020-08-23 15:02:58 +00:00
|
|
|
let postcssResult
|
|
|
|
try {
|
|
|
|
postcssResult = await Promise.all(
|
2020-11-24 13:27:19 +00:00
|
|
|
[
|
|
|
|
semver.gte(version, '0.99.0') ? 'base' : 'preflight',
|
|
|
|
'components',
|
|
|
|
'utilities',
|
|
|
|
].map((group) =>
|
|
|
|
postcss([tailwindcss(configPath)]).process(
|
|
|
|
`@tailwind ${group};`,
|
|
|
|
{
|
|
|
|
from: undefined,
|
|
|
|
}
|
|
|
|
)
|
2020-08-23 15:02:58 +00:00
|
|
|
)
|
|
|
|
)
|
2020-08-27 12:31:41 +00:00
|
|
|
} catch (error) {
|
|
|
|
throw error
|
2020-08-23 15:02:58 +00:00
|
|
|
} finally {
|
|
|
|
hook.unhook()
|
|
|
|
}
|
2020-08-25 15:59:13 +00:00
|
|
|
|
2020-08-23 15:02:58 +00:00
|
|
|
const [base, components, utilities] = postcssResult
|
2020-04-11 21:20:45 +00:00
|
|
|
|
2020-08-23 15:02:58 +00:00
|
|
|
if (typeof userSeperator !== 'undefined') {
|
|
|
|
dset(config, sepLocation, userSeperator)
|
|
|
|
} else {
|
|
|
|
delete config[sepLocation]
|
|
|
|
}
|
|
|
|
if (typeof userPurge !== 'undefined') {
|
|
|
|
config.purge = userPurge
|
|
|
|
} else {
|
|
|
|
delete config.purge
|
|
|
|
}
|
2020-04-11 21:20:45 +00:00
|
|
|
|
2020-12-10 13:47:05 +00:00
|
|
|
const resolvedConfig = resolveConfig({
|
|
|
|
base: configDir,
|
|
|
|
root: cwd,
|
|
|
|
config,
|
|
|
|
})
|
2020-04-11 21:20:45 +00:00
|
|
|
|
2020-12-03 17:23:50 +00:00
|
|
|
let browserslist = []
|
|
|
|
if (
|
|
|
|
browserslistCommand &&
|
|
|
|
semver.gte(version, '1.4.0') &&
|
|
|
|
semver.lte(version, '1.99.0')
|
|
|
|
) {
|
|
|
|
try {
|
|
|
|
const { stdout } = await execa(
|
|
|
|
browserslistCommand,
|
|
|
|
browserslistArgs,
|
|
|
|
{
|
|
|
|
preferLocal: true,
|
|
|
|
localDir: configDir,
|
|
|
|
cwd: configDir,
|
|
|
|
}
|
|
|
|
)
|
|
|
|
browserslist = stdout.split('\n')
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Failed to load browserslist:', error)
|
|
|
|
}
|
|
|
|
}
|
2020-12-02 13:57:17 +00:00
|
|
|
|
2020-08-23 15:02:58 +00:00
|
|
|
return {
|
|
|
|
base,
|
|
|
|
components,
|
|
|
|
utilities,
|
|
|
|
resolvedConfig,
|
|
|
|
postcss,
|
|
|
|
browserslist,
|
|
|
|
}
|
2020-12-02 13:57:17 +00:00
|
|
|
}
|
2020-08-23 15:02:58 +00:00
|
|
|
)
|
2020-04-25 21:41:17 +00:00
|
|
|
|
2020-04-11 21:20:45 +00:00
|
|
|
return {
|
2020-04-27 23:06:47 +00:00
|
|
|
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,
|
2020-05-10 12:06:22 +00:00
|
|
|
classNames: await extractClassNames([
|
2020-05-10 12:19:17 +00:00
|
|
|
{ root: base.root, source: 'base' },
|
2020-05-10 12:06:22 +00:00
|
|
|
{ root: components.root, source: 'components' },
|
|
|
|
{ root: utilities.root, source: 'utilities' },
|
|
|
|
]),
|
2020-04-22 19:29:36 +00:00
|
|
|
dependencies: hook.deps,
|
2020-04-11 21:20:45 +00:00
|
|
|
plugins: getPlugins(config),
|
2020-05-03 18:37:37 +00:00
|
|
|
variants: getVariants({ config, version, postcss, browserslist }),
|
2020-04-25 21:41:17 +00:00
|
|
|
utilityConfigMap: await getUtilityConfigMap({
|
2020-12-10 13:47:05 +00:00
|
|
|
base: configDir,
|
|
|
|
root: cwd,
|
2020-04-25 21:41:17 +00:00
|
|
|
resolvedConfig,
|
|
|
|
postcss,
|
2020-05-03 18:37:37 +00:00
|
|
|
browserslist,
|
2020-04-25 21:41:17 +00:00
|
|
|
}),
|
2020-06-17 17:34:53 +00:00
|
|
|
modules: {
|
|
|
|
postcss,
|
|
|
|
},
|
2020-08-21 14:30:29 +00:00
|
|
|
featureFlags,
|
2020-04-11 21:20:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let watcher
|
2020-04-22 19:29:36 +00:00
|
|
|
function watch(files = []) {
|
2020-05-03 13:45:36 +00:00
|
|
|
unwatch()
|
2020-04-11 21:20:45 +00:00
|
|
|
watcher = chokidar
|
2020-05-03 13:45:36 +00:00
|
|
|
.watch(files, { cwd })
|
2020-04-11 21:20:45 +00:00
|
|
|
.on('change', handleChange)
|
|
|
|
.on('unlink', handleChange)
|
|
|
|
}
|
2020-05-03 13:45:36 +00:00
|
|
|
function unwatch() {
|
|
|
|
if (watcher) {
|
|
|
|
watcher.close()
|
|
|
|
}
|
|
|
|
}
|
2020-04-11 21:20:45 +00:00
|
|
|
|
|
|
|
async function handleChange() {
|
2020-05-03 13:45:36 +00:00
|
|
|
const prevDeps = result ? [result.configPath, ...result.dependencies] : []
|
2020-04-22 19:29:36 +00:00
|
|
|
try {
|
|
|
|
result = await run()
|
2020-04-23 18:54:01 +00:00
|
|
|
} catch (error) {
|
2020-08-25 15:59:13 +00:00
|
|
|
onChange({ error })
|
2020-04-22 19:29:36 +00:00
|
|
|
return
|
|
|
|
}
|
2020-05-03 13:45:36 +00:00
|
|
|
const newDeps = [result.configPath, ...result.dependencies]
|
|
|
|
if (!arraysEqual(prevDeps, newDeps)) {
|
|
|
|
watch(newDeps)
|
2020-04-11 21:20:45 +00:00
|
|
|
}
|
|
|
|
onChange(result)
|
|
|
|
}
|
|
|
|
|
2020-04-22 19:29:36 +00:00
|
|
|
let result
|
|
|
|
try {
|
|
|
|
result = await run()
|
2020-12-01 19:03:31 +00:00
|
|
|
console.log('Initialised successfully.')
|
|
|
|
} catch (error) {
|
|
|
|
console.error('Failed to initialise:', error)
|
2020-04-22 19:29:36 +00:00
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
2020-05-03 13:45:36 +00:00
|
|
|
watch([result.configPath, ...result.dependencies])
|
2020-04-22 19:29:36 +00:00
|
|
|
|
2020-04-11 21:20:45 +00:00
|
|
|
return result
|
|
|
|
}
|
2020-08-23 15:02:58 +00:00
|
|
|
|
2020-12-10 13:47:05 +00:00
|
|
|
function loadMeta(configDir, root) {
|
|
|
|
return withUserEnvironment(configDir, root, ({ require, resolve }) => {
|
2020-12-03 17:23:50 +00:00
|
|
|
const tailwindBase = path.dirname(resolve('tailwindcss/package.json'))
|
2020-08-23 15:02:58 +00:00
|
|
|
const version = require('tailwindcss/package.json').version
|
|
|
|
let featureFlags
|
|
|
|
|
|
|
|
try {
|
2020-08-27 12:39:00 +00:00
|
|
|
featureFlags = require('./lib/featureFlags.js', tailwindBase).default
|
2020-08-23 15:02:58 +00:00
|
|
|
} catch (_) {}
|
|
|
|
|
2020-12-03 17:23:50 +00:00
|
|
|
return { version, featureFlags, tailwindBase }
|
2020-08-23 15:02:58 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-12-10 13:47:05 +00:00
|
|
|
function withPackages(configDir, root, cb) {
|
|
|
|
return withUserEnvironment(
|
|
|
|
configDir,
|
|
|
|
root,
|
|
|
|
async ({ isPnP, require, resolve }) => {
|
|
|
|
const tailwindBase = path.dirname(resolve('tailwindcss/package.json'))
|
|
|
|
const postcss = require('postcss', tailwindBase)
|
|
|
|
const tailwindcss = require('tailwindcss')
|
2020-12-03 17:23:50 +00:00
|
|
|
|
2020-12-10 13:47:05 +00:00
|
|
|
let browserslistCommand
|
|
|
|
let browserslistArgs = []
|
|
|
|
try {
|
|
|
|
const browserslistBin = resolve(
|
|
|
|
path.join(
|
|
|
|
'browserslist',
|
|
|
|
require('browserslist/package.json', tailwindBase).bin.browserslist
|
|
|
|
),
|
|
|
|
tailwindBase
|
|
|
|
)
|
|
|
|
if (isPnP) {
|
|
|
|
browserslistCommand = 'yarn'
|
|
|
|
browserslistArgs = ['node', browserslistBin]
|
|
|
|
} else {
|
|
|
|
browserslistCommand = process.execPath
|
|
|
|
browserslistArgs = [browserslistBin]
|
|
|
|
}
|
|
|
|
} catch (_) {}
|
2020-08-23 15:02:58 +00:00
|
|
|
|
2020-12-10 13:47:05 +00:00
|
|
|
return cb({ postcss, tailwindcss, browserslistCommand, browserslistArgs })
|
|
|
|
}
|
|
|
|
)
|
2020-08-23 15:02:58 +00:00
|
|
|
}
|