139 lines
3.6 KiB
JavaScript
139 lines
3.6 KiB
JavaScript
const plugin = require('tailwindcss/plugin')
|
|
const merge = require('lodash.merge')
|
|
const castArray = require('lodash.castarray')
|
|
const styles = require('./styles')
|
|
const { commonTrailingPseudos } = require('./utils')
|
|
|
|
const computed = {
|
|
// Reserved for future "magic properties", for example:
|
|
// bulletColor: (color) => ({ 'ul > li::before': { backgroundColor: color } }),
|
|
}
|
|
|
|
function inWhere(selector, { className, modifier, prefix }) {
|
|
let prefixedNot = prefix(`.not-${className}`).slice(1)
|
|
let selectorPrefix = selector.startsWith('>')
|
|
? `${modifier === 'DEFAULT' ? `.${className}` : `.${className}-${modifier}`} `
|
|
: ''
|
|
|
|
// Parse the selector, if every component ends in the same pseudo element(s) then move it to the end
|
|
let [trailingPseudo, rebuiltSelector] = commonTrailingPseudos(selector)
|
|
|
|
if (trailingPseudo) {
|
|
return `:where(${selectorPrefix}${rebuiltSelector}):not(:where([class~="${prefixedNot}"],[class~="${prefixedNot}"] *))${trailingPseudo}`
|
|
}
|
|
|
|
return `:where(${selectorPrefix}${selector}):not(:where([class~="${prefixedNot}"],[class~="${prefixedNot}"] *))`
|
|
}
|
|
|
|
function isObject(value) {
|
|
return typeof value === 'object' && value !== null
|
|
}
|
|
|
|
function configToCss(config = {}, { target, className, modifier, prefix }) {
|
|
function updateSelector(k, v) {
|
|
if (target === 'legacy') {
|
|
return [k, v]
|
|
}
|
|
|
|
if (Array.isArray(v)) {
|
|
return [k, v]
|
|
}
|
|
|
|
if (isObject(v)) {
|
|
let nested = Object.values(v).some(isObject)
|
|
if (nested) {
|
|
return [
|
|
inWhere(k, { className, modifier, prefix }),
|
|
v,
|
|
Object.fromEntries(Object.entries(v).map(([k, v]) => updateSelector(k, v))),
|
|
]
|
|
}
|
|
|
|
return [inWhere(k, { className, modifier, prefix }), v]
|
|
}
|
|
|
|
return [k, v]
|
|
}
|
|
|
|
return Object.fromEntries(
|
|
Object.entries(
|
|
merge(
|
|
{},
|
|
...Object.keys(config)
|
|
.filter((key) => computed[key])
|
|
.map((key) => computed[key](config[key])),
|
|
...castArray(config.css || {})
|
|
)
|
|
).map(([k, v]) => updateSelector(k, v))
|
|
)
|
|
}
|
|
|
|
module.exports = plugin.withOptions(
|
|
({ className = 'prose', target = 'modern' } = {}) => {
|
|
return function ({ addVariant, addComponents, theme, prefix }) {
|
|
let modifiers = theme('typography')
|
|
|
|
let options = { className, prefix }
|
|
|
|
for (let [name, ...selectors] of [
|
|
['headings', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'th'],
|
|
['h1'],
|
|
['h2'],
|
|
['h3'],
|
|
['h4'],
|
|
['h5'],
|
|
['h6'],
|
|
['p'],
|
|
['a'],
|
|
['blockquote'],
|
|
['figure'],
|
|
['figcaption'],
|
|
['strong'],
|
|
['em'],
|
|
['code'],
|
|
['pre'],
|
|
['ol'],
|
|
['ul'],
|
|
['li'],
|
|
['table'],
|
|
['thead'],
|
|
['tr'],
|
|
['th'],
|
|
['td'],
|
|
['img'],
|
|
['video'],
|
|
['hr'],
|
|
['lead', '[class~="lead"]'],
|
|
]) {
|
|
selectors = selectors.length === 0 ? [name] : selectors
|
|
|
|
let selector =
|
|
target === 'legacy' ? selectors.map((selector) => `& ${selector}`) : selectors.join(', ')
|
|
|
|
addVariant(
|
|
`${className}-${name}`,
|
|
target === 'legacy' ? selector : `& :is(${inWhere(selector, options)})`
|
|
)
|
|
}
|
|
|
|
addComponents(
|
|
Object.keys(modifiers).map((modifier) => ({
|
|
[modifier === 'DEFAULT' ? `.${className}` : `.${className}-${modifier}`]: configToCss(
|
|
modifiers[modifier],
|
|
{
|
|
target,
|
|
className,
|
|
modifier,
|
|
prefix,
|
|
}
|
|
),
|
|
}))
|
|
)
|
|
}
|
|
},
|
|
() => {
|
|
return {
|
|
theme: { typography: styles },
|
|
}
|
|
}
|
|
)
|