Spaces:
Sleeping
Sleeping
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'], | |
['kbd'], | |
['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 }, | |
} | |
} | |
) | |