From 2de4816e3c148389935fe7c9c4c9037af4f866c3 Mon Sep 17 00:00:00 2001 From: Brad Cornes Date: Sat, 25 Apr 2020 22:41:17 +0100 Subject: [PATCH] add utility config map --- .../tailwindcss-class-names/src/getPlugins.js | 33 ++++++++--- .../src/getUtilityConfigMap.js | 57 +++++++++++++++++++ .../src/getVariants.js | 32 ++++------- packages/tailwindcss-class-names/src/glob.js | 22 +++++++ packages/tailwindcss-class-names/src/index.js | 32 ++++------- .../tailwindcss-class-names/src/isObject.js | 3 + .../tailwindcss-class-names/src/runPlugin.js | 19 +++++++ 7 files changed, 145 insertions(+), 53 deletions(-) create mode 100644 packages/tailwindcss-class-names/src/getUtilityConfigMap.js create mode 100644 packages/tailwindcss-class-names/src/glob.js create mode 100644 packages/tailwindcss-class-names/src/isObject.js create mode 100644 packages/tailwindcss-class-names/src/runPlugin.js diff --git a/packages/tailwindcss-class-names/src/getPlugins.js b/packages/tailwindcss-class-names/src/getPlugins.js index d831a2d..25a27b3 100644 --- a/packages/tailwindcss-class-names/src/getPlugins.js +++ b/packages/tailwindcss-class-names/src/getPlugins.js @@ -1,9 +1,24 @@ import * as path from 'path' import stackTrace from 'stack-trace' import pkgUp from 'pkg-up' +import { glob } from './glob' +import { isObject } from './isObject' -function isObject(variable) { - return Object.prototype.toString.call(variable) === '[object Object]' +export async function getBuiltInPlugins(cwd) { + try { + return ( + await glob(path.resolve(cwd, 'node_modules/tailwindcss/lib/plugins/*.js')) + ) + .map((x) => { + try { + const mod = __non_webpack_require__(x) + return mod.default ? mod.default() : mod() + } catch (_) {} + }) + .filter(Boolean) + } catch (_) { + return [] + } } export default function getPlugins(config) { @@ -13,7 +28,7 @@ export default function getPlugins(config) { return [] } - return plugins.map(plugin => { + return plugins.map((plugin) => { let pluginConfig = plugin.config if (!isObject(pluginConfig)) { pluginConfig = {} @@ -25,7 +40,7 @@ export default function getPlugins(config) { : [], variants: isObject(pluginConfig.variants) ? Object.keys(pluginConfig.variants) - : [] + : [], } const fn = plugin.handler || plugin @@ -40,32 +55,32 @@ export default function getPlugins(config) { const trace = stackTrace.parse(e) if (trace.length === 0) return { - name: fnName + name: fnName, } const file = trace[0].fileName const dir = path.dirname(file) let pkg = pkgUp.sync({ cwd: dir }) if (!pkg) return { - name: fnName + name: fnName, } try { pkg = __non_webpack_require__(pkg) } catch (_) { return { - name: fnName + name: fnName, } } if (pkg.name && path.resolve(dir, pkg.main || 'index.js') === file) { return { name: pkg.name, homepage: pkg.homepage, - contributes + contributes, } } } return { - name: fnName + name: fnName, } }) } diff --git a/packages/tailwindcss-class-names/src/getUtilityConfigMap.js b/packages/tailwindcss-class-names/src/getUtilityConfigMap.js new file mode 100644 index 0000000..9362fea --- /dev/null +++ b/packages/tailwindcss-class-names/src/getUtilityConfigMap.js @@ -0,0 +1,57 @@ +import { runPlugin } from './runPlugin' +import { getBuiltInPlugins } from './getPlugins' +import { isObject } from './isObject' + +const proxyHandler = (base = []) => ({ + get(target, key) { + if (isObject(target[key])) { + return new Proxy(target[key], proxyHandler([...base, key])) + } else { + if ( + [...base, key].every((x) => typeof x === 'string') && + target.hasOwnProperty(key) + ) { + return '$dep$' + [...base, key].join('.') + } + return target[key] + } + }, +}) + +export async function getUtilityConfigMap({ cwd, resolvedConfig, postcss }) { + const builtInPlugins = await getBuiltInPlugins(cwd) + const userPlugins = Array.isArray(resolvedConfig.plugins) + ? resolvedConfig.plugins + : [] + + try { + const classNameConfigMap = {} + const proxiedConfig = new Proxy(resolvedConfig, proxyHandler()) + + ;[...builtInPlugins, ...userPlugins].forEach((plugin) => { + runPlugin(plugin, { + postcss, + config: proxiedConfig, + addUtilities: (utilities) => { + Object.keys(utilities).forEach((util) => { + let props = Object.keys(utilities[util]) + if ( + props.length === 1 && + /^\.[^\s]+$/.test(util) && + typeof utilities[util][props[0]] === 'string' && + utilities[util][props[0]].substr(0, 5) === '$dep$' + ) { + classNameConfigMap[util.substr(1)] = utilities[util][ + props[0] + ].substr(5) + } + }) + }, + }) + }) + + return classNameConfigMap + } catch (_) { + return {} + } +} diff --git a/packages/tailwindcss-class-names/src/getVariants.js b/packages/tailwindcss-class-names/src/getVariants.js index c7df964..9688ef4 100644 --- a/packages/tailwindcss-class-names/src/getVariants.js +++ b/packages/tailwindcss-class-names/src/getVariants.js @@ -1,5 +1,5 @@ import semver from 'semver' -import dlv from 'dlv' +import { runPlugin } from './runPlugin' export default function getVariants({ config, version, postcss }) { let variants = ['responsive', 'hover'] @@ -11,28 +11,16 @@ export default function getVariants({ config, version, postcss }) { variants.push('first', 'last', 'odd', 'even', 'disabled', 'visited') semver.gte(version, '1.3.0') && variants.push('group-focus') - let plugins = config.plugins - if (!Array.isArray(plugins)) { - plugins = [] - } + let plugins = Array.isArray(config.plugins) ? config.plugins : [] + plugins.forEach((plugin) => { - try { - ;(plugin.handler || plugin)({ - addUtilities: () => {}, - addComponents: () => {}, - addBase: () => {}, - addVariant: (name) => { - variants.push(name) - }, - e: (x) => x, - prefix: (x) => x, - theme: (path, defaultValue) => - dlv(config, `theme.${path}`, defaultValue), - variants: () => [], - config: (path, defaultValue) => dlv(config, path, defaultValue), - postcss, - }) - } catch (_) {} + runPlugin(plugin, { + postcss, + config, + addVariant: (name) => { + variants.push(name) + }, + }) }) return variants diff --git a/packages/tailwindcss-class-names/src/glob.js b/packages/tailwindcss-class-names/src/glob.js new file mode 100644 index 0000000..22e3eb8 --- /dev/null +++ b/packages/tailwindcss-class-names/src/glob.js @@ -0,0 +1,22 @@ +import nodeGlob from 'glob' +import dlv from 'dlv' +import * as path from 'path' + +export function glob(pattern, options = {}) { + return new Promise((resolve, reject) => { + let g = new nodeGlob.Glob(pattern, options) + let matches = [] + let max = dlv(options, 'max', Infinity) + g.on('match', (match) => { + matches.push(path.resolve(options.cwd || process.cwd(), match)) + if (matches.length === max) { + g.abort() + resolve(matches) + } + }) + g.on('end', () => { + resolve(matches) + }) + g.on('error', reject) + }) +} diff --git a/packages/tailwindcss-class-names/src/index.js b/packages/tailwindcss-class-names/src/index.js index 4b55f76..e7a78ef 100644 --- a/packages/tailwindcss-class-names/src/index.js +++ b/packages/tailwindcss-class-names/src/index.js @@ -3,8 +3,6 @@ import Hook from './hook.mjs' import dlv from 'dlv' import dset from 'dset' import importFrom from 'import-from' -import nodeGlob from 'glob' -import * as path from 'path' import chokidar from 'chokidar' import semver from 'semver' import invariant from 'tiny-invariant' @@ -12,6 +10,8 @@ import getPlugins from './getPlugins' import getVariants from './getVariants' import resolveConfig from './resolveConfig' import * as util from 'util' +import { glob } from './glob' +import { getUtilityConfigMap } from './getUtilityConfigMap' function TailwindConfigError(error) { Error.call(this) @@ -24,25 +24,6 @@ function TailwindConfigError(error) { util.inherits(TailwindConfigError, Error) -function glob(pattern, options = {}) { - return new Promise((resolve, reject) => { - let g = new nodeGlob.Glob(pattern, options) - let matches = [] - let max = dlv(options, 'max', Infinity) - g.on('match', (match) => { - matches.push(path.resolve(options.cwd || process.cwd(), match)) - if (matches.length === max) { - g.abort() - resolve(matches) - } - }) - g.on('end', () => { - resolve(matches) - }) - g.on('error', reject) - }) -} - function arraysEqual(arr1, arr2) { return ( JSON.stringify(arr1.concat([]).sort()) === @@ -109,14 +90,21 @@ export default async function getClassNames( delete config[sepLocation] } + const resolvedConfig = resolveConfig({ cwd, config }) + return { configPath, - config: resolveConfig({ cwd, config }), + config: resolvedConfig, separator: typeof userSeperator === 'undefined' ? ':' : userSeperator, classNames: await extractClassNames(ast), dependencies: hook.deps, plugins: getPlugins(config), variants: getVariants({ config, version, postcss }), + utilityConfigMap: await getUtilityConfigMap({ + cwd, + resolvedConfig, + postcss, + }), } } diff --git a/packages/tailwindcss-class-names/src/isObject.js b/packages/tailwindcss-class-names/src/isObject.js new file mode 100644 index 0000000..502dc97 --- /dev/null +++ b/packages/tailwindcss-class-names/src/isObject.js @@ -0,0 +1,3 @@ +export function isObject(thing) { + return Object.prototype.toString.call(thing) === '[object Object]' +} diff --git a/packages/tailwindcss-class-names/src/runPlugin.js b/packages/tailwindcss-class-names/src/runPlugin.js new file mode 100644 index 0000000..059b9a9 --- /dev/null +++ b/packages/tailwindcss-class-names/src/runPlugin.js @@ -0,0 +1,19 @@ +import dlv from 'dlv' + +export function runPlugin(plugin, params = {}) { + const { config, ...rest } = params + try { + ;(plugin.handler || plugin)({ + addUtilities: () => {}, + addComponents: () => {}, + addBase: () => {}, + addVariant: () => {}, + e: (x) => x, + prefix: (x) => x, + theme: (path, defaultValue) => dlv(config, `theme.${path}`, defaultValue), + variants: () => [], + config: (path, defaultValue) => dlv(config, path, defaultValue), + ...rest, + }) + } catch (_) {} +}