function cartesian(array) { function* cartesian(head, ...tail) { let remainder = tail.length ? cartesian(...tail) : [[]] for (let r of remainder) for (let h of head) yield [h, ...r] } return [...cartesian(...array)] } function deepKeys(object, separator = '.', prefix = '') { return Object.keys(object).reduce((result, key) => { if (Array.isArray(object[key])) { return [...result, prefix + key] } else if (typeof object[key] === 'object' && object[key] !== null) { return [...result, ...deepKeys(object[key], separator, prefix + key + separator)] } return [...result, prefix + key] }, []) } const extractTokens = pattern => pattern.split(/(?={[^}]+})|(?<={[^}]+})/) const expandTokens = theme => tokens => { return tokens.map(token => { if (token.startsWith('{')) { const cleanToken = token.replace(/{|}/g, '') if (cleanToken.includes('.')) { const color = cleanToken.split('.')[1] const colorToken = { [color]: theme(cleanToken, {}), } return deepKeys(colorToken, '-') } return deepKeys(theme(cleanToken, {}), '-') } else { return [token] } }) } const mapToClasses = expanded => expanded.map(values => values.join('').replace('-DEFAULT', '')) module.exports = theme => patterns => { return patterns // ["text-{gray}", …] .map(extractTokens) // [["text", "{gray}"], …] .map(expandTokens(theme)) // [[["text"], ["gray-100", "gray-200",…]], …] .map(cartesian) // [[["text", "gray-100"], ["text", "gray-200"], …], …] .flatMap(mapToClasses) // ["text-gray-100", "text-gray-200",…] }