UNPKG

flyonui

Version:

The easiest, free and open-source Tailwind CSS component library with semantic classes.

153 lines (130 loc) 4.42 kB
const defaultExcludedPrefixes = ['color-', 'size-', 'radius-', 'border', 'depth', 'noise'] const shouldExcludeVariable = (variableName, excludedPrefixes) => { if (variableName.startsWith('tw')) { return true } return excludedPrefixes.some(excludedPrefix => variableName.startsWith(excludedPrefix)) } const prefixVariable = (variableName, prefix, excludedPrefixes) => { if (shouldExcludeVariable(variableName, excludedPrefixes)) { return variableName } return `${prefix}${variableName}` } const getPrefixedSelector = (selector, prefix) => { if (!selector.startsWith('.')) return selector return `.${prefix}${selector.slice(1)}` } const getPrefixedKey = (key, prefix, excludedPrefixes) => { const prefixAmpDot = prefix ? `&.${prefix}` : '' if (!prefix) return key if (key.startsWith('--')) { const variableName = key.slice(2) return `--${prefixVariable(variableName, prefix, excludedPrefixes)}` } if (key.startsWith('@') || key.startsWith('[')) { return key } if (key.startsWith('&')) { // If it's a complex selector with :not(), :has(), etc. if (key.match(/:[a-z-]+\(/)) { return key.replace(/\.([\w-]+)/g, `.${prefix}$1`) } // For simple &. cases if (key.startsWith('&.')) { return `${prefixAmpDot}${key.slice(2)}` } // For other & cases (like &:hover or &:not(...)) return key.replace(/\.([\w-]+)/g, `.${prefix}$1`) } if (key.startsWith(':')) { return key.replace(/\.([\w-]+)/g, `.${prefix}$1`) } if (key.includes('.') && !key.includes(' ') && !key.includes('>') && !key.includes('+') && !key.includes('~')) { return key .split('.') .filter(Boolean) .map(part => prefix + part) .join('.') .replace(/^/, '.') } if (key.includes('>') || key.includes('+') || key.includes('~')) { // For comma-separated selectors if (key.includes(',')) { return key .split(/\s*,\s*/) .map(part => { // Replace class names with prefixed versions for each part return part.replace(/\.([\w-]+)/g, `.${prefix}$1`) }) .join(', ') } // For simple combinators (not comma-separated) let processedKey = key.replace(/\.([\w-]+)/g, `.${prefix}$1`) // Add a space before combinators at the beginning if (processedKey.startsWith('>') || processedKey.startsWith('+') || processedKey.startsWith('~')) { processedKey = ` ${processedKey}` } return processedKey } if (key.includes(' ')) { return key .split(/\s+/) .map(part => { if (part.startsWith('.')) { return getPrefixedSelector(part, prefix) } return part }) .join(' ') } if (key.includes(':')) { const [selector, ...pseudo] = key.split(':') if (selector.startsWith('.')) { return `${getPrefixedSelector(selector, prefix)}:${pseudo.join(':')}` } return key.replace(/\.([\w-]+)/g, `.${prefix}$1`) } if (key.startsWith('.')) { return getPrefixedSelector(key, prefix) } return key } const processArrayValue = (value, prefix, excludedPrefixes) => { return value.map(item => { if (typeof item === 'string') { if (item.startsWith('.')) { return prefix ? `.${prefix}${item.slice(1)}` : item } return processStringValue(item, prefix, excludedPrefixes) } return item }) } const processStringValue = (value, prefix, excludedPrefixes) => { if (prefix === 0) return value return value.replace(/var\(--([^)]+)\)/g, (match, variableName) => { if (shouldExcludeVariable(variableName, excludedPrefixes)) { return match } return `var(--${prefix}${variableName})` }) } const processValue = (value, prefix, excludedPrefixes) => { if (Array.isArray(value)) { return processArrayValue(value, prefix, excludedPrefixes) } else if (typeof value === 'object' && value !== null) { return addPrefix(value, prefix, excludedPrefixes) } else if (typeof value === 'string') { return processStringValue(value, prefix, excludedPrefixes) } else { return value } } export const addPrefix = (obj, prefix, excludedPrefixes = defaultExcludedPrefixes) => { return Object.entries(obj).reduce((result, [key, value]) => { const newKey = getPrefixedKey(key, prefix, excludedPrefixes) result[newKey] = processValue(value, prefix, excludedPrefixes) return result }, {}) }