tailwindcss/src/util/processPlugins.js
Nick 8ceedb2dd6 fix: fix empty object error (#4903)
Co-authored-by: zhongcheng <zhongcheng@bytedance.com>
2021-09-06 15:08:22 -04:00

167 lines
5 KiB
JavaScript

import _ from 'lodash'
import postcss from 'postcss'
import Node from 'postcss/lib/node'
import isFunction from 'lodash/isFunction'
import escapeClassName from '../util/escapeClassName'
import generateVariantFunction from '../util/generateVariantFunction'
import parseObjectStyles from '../util/parseObjectStyles'
import prefixSelector from '../util/prefixSelector'
import wrapWithVariants from '../util/wrapWithVariants'
import cloneNodes from '../util/cloneNodes'
import transformThemeValue from './transformThemeValue'
import nameClass from '../util/nameClass'
import isKeyframeRule from '../util/isKeyframeRule'
function parseStyles(styles) {
if (!Array.isArray(styles)) {
return parseStyles([styles])
}
return _.flatMap(styles, (style) => (style instanceof Node ? style : parseObjectStyles(style)))
}
function wrapWithLayer(rules, layer) {
return postcss
.atRule({
name: 'layer',
params: layer,
})
.append(cloneNodes(Array.isArray(rules) ? rules : [rules]))
}
export default function (plugins, config) {
const pluginBaseStyles = []
const pluginComponents = []
const pluginUtilities = []
const pluginVariantGenerators = {}
const applyConfiguredPrefix = (selector) => {
return prefixSelector(config.prefix, selector)
}
function addUtilities(utilities, options) {
const defaultOptions = {
variants: [],
respectPrefix: true,
respectImportant: true,
}
options = Array.isArray(options)
? Object.assign({}, defaultOptions, { variants: options })
: _.defaults(options, defaultOptions)
const styles = postcss.root({ nodes: parseStyles(utilities) })
styles.walkRules((rule) => {
if (options.respectPrefix && !isKeyframeRule(rule)) {
rule.selector = applyConfiguredPrefix(rule.selector)
}
if (options.respectImportant && config.important) {
rule.__tailwind = {
...rule.__tailwind,
important: config.important,
}
}
})
pluginUtilities.push(
wrapWithLayer(wrapWithVariants(styles.nodes, options.variants), 'utilities')
)
}
const getConfigValue = (path, defaultValue) => (path ? _.get(config, path, defaultValue) : config)
plugins.forEach((plugin) => {
if (plugin.__isOptionsFunction) {
plugin = plugin()
}
const handler = isFunction(plugin) ? plugin : _.get(plugin, 'handler', () => {})
handler({
postcss,
config: getConfigValue,
theme: (path, defaultValue) => {
const [pathRoot, ...subPaths] = _.toPath(path)
const value = getConfigValue(['theme', pathRoot, ...subPaths], defaultValue)
return transformThemeValue(pathRoot)(value)
},
corePlugins: (path) => {
if (Array.isArray(config.corePlugins)) {
return config.corePlugins.includes(path)
}
return getConfigValue(`corePlugins.${path}`, true)
},
variants: (path, defaultValue) => {
if (Array.isArray(config.variants)) {
return config.variants
}
return getConfigValue(`variants.${path}`, defaultValue)
},
e: escapeClassName,
prefix: applyConfiguredPrefix,
addUtilities,
matchUtilities: (matches, { values, variants, respectPrefix, respectImportant }) => {
let modifierValues = Object.entries(values || {})
let result = Object.entries(matches).flatMap(([name, utilityFunction]) => {
return modifierValues
.map(([modifier, value]) => {
let declarations = utilityFunction(value, {
includeRules(rules, options) {
addUtilities(rules, options)
},
})
if (!declarations) {
return null
}
return {
[nameClass(name, modifier)]: declarations,
}
})
.filter(Boolean)
})
addUtilities(result, { variants, respectPrefix, respectImportant })
},
addComponents: (components, options) => {
const defaultOptions = { variants: [], respectPrefix: true }
options = Array.isArray(options)
? Object.assign({}, defaultOptions, { variants: options })
: _.defaults(options, defaultOptions)
const styles = postcss.root({ nodes: parseStyles(components) })
styles.walkRules((rule) => {
if (options.respectPrefix && !isKeyframeRule(rule)) {
rule.selector = applyConfiguredPrefix(rule.selector)
}
})
pluginComponents.push(
wrapWithLayer(wrapWithVariants(styles.nodes, options.variants), 'components')
)
},
addBase: (baseStyles) => {
pluginBaseStyles.push(wrapWithLayer(parseStyles(baseStyles), 'base'))
},
addVariant: (name, generator, options = {}) => {
pluginVariantGenerators[name] = generateVariantFunction(generator, options)
},
})
})
return {
base: pluginBaseStyles,
components: pluginComponents,
utilities: pluginUtilities,
variantGenerators: pluginVariantGenerators,
}
}