| [9af201e] | 1 | import negateValue from './negateValue'
|
|---|
| 2 | import corePluginList from '../corePluginList'
|
|---|
| 3 | import configurePlugins from './configurePlugins'
|
|---|
| 4 | import colors from '../public/colors'
|
|---|
| 5 | import { defaults } from './defaults'
|
|---|
| 6 | import { toPath } from './toPath'
|
|---|
| 7 | import { normalizeConfig } from './normalizeConfig'
|
|---|
| 8 | import isPlainObject from './isPlainObject'
|
|---|
| 9 | import { cloneDeep } from './cloneDeep'
|
|---|
| 10 | import { parseColorFormat } from './pluginUtils'
|
|---|
| 11 | import { withAlphaValue } from './withAlphaVariable'
|
|---|
| 12 | import toColorValue from './toColorValue'
|
|---|
| 13 |
|
|---|
| 14 | function isFunction(input) {
|
|---|
| 15 | return typeof input === 'function'
|
|---|
| 16 | }
|
|---|
| 17 |
|
|---|
| 18 | function mergeWith(target, ...sources) {
|
|---|
| 19 | let customizer = sources.pop()
|
|---|
| 20 |
|
|---|
| 21 | for (let source of sources) {
|
|---|
| 22 | for (let k in source) {
|
|---|
| 23 | let merged = customizer(target[k], source[k])
|
|---|
| 24 |
|
|---|
| 25 | if (merged === undefined) {
|
|---|
| 26 | if (isPlainObject(target[k]) && isPlainObject(source[k])) {
|
|---|
| 27 | target[k] = mergeWith({}, target[k], source[k], customizer)
|
|---|
| 28 | } else {
|
|---|
| 29 | target[k] = source[k]
|
|---|
| 30 | }
|
|---|
| 31 | } else {
|
|---|
| 32 | target[k] = merged
|
|---|
| 33 | }
|
|---|
| 34 | }
|
|---|
| 35 | }
|
|---|
| 36 |
|
|---|
| 37 | return target
|
|---|
| 38 | }
|
|---|
| 39 |
|
|---|
| 40 | const configUtils = {
|
|---|
| 41 | colors,
|
|---|
| 42 | negative(scale) {
|
|---|
| 43 | // TODO: Log that this function isn't really needed anymore?
|
|---|
| 44 | return Object.keys(scale)
|
|---|
| 45 | .filter((key) => scale[key] !== '0')
|
|---|
| 46 | .reduce((negativeScale, key) => {
|
|---|
| 47 | let negativeValue = negateValue(scale[key])
|
|---|
| 48 |
|
|---|
| 49 | if (negativeValue !== undefined) {
|
|---|
| 50 | negativeScale[`-${key}`] = negativeValue
|
|---|
| 51 | }
|
|---|
| 52 |
|
|---|
| 53 | return negativeScale
|
|---|
| 54 | }, {})
|
|---|
| 55 | },
|
|---|
| 56 | breakpoints(screens) {
|
|---|
| 57 | return Object.keys(screens)
|
|---|
| 58 | .filter((key) => typeof screens[key] === 'string')
|
|---|
| 59 | .reduce(
|
|---|
| 60 | (breakpoints, key) => ({
|
|---|
| 61 | ...breakpoints,
|
|---|
| 62 | [`screen-${key}`]: screens[key],
|
|---|
| 63 | }),
|
|---|
| 64 | {}
|
|---|
| 65 | )
|
|---|
| 66 | },
|
|---|
| 67 | }
|
|---|
| 68 |
|
|---|
| 69 | function value(valueToResolve, ...args) {
|
|---|
| 70 | return isFunction(valueToResolve) ? valueToResolve(...args) : valueToResolve
|
|---|
| 71 | }
|
|---|
| 72 |
|
|---|
| 73 | function collectExtends(items) {
|
|---|
| 74 | return items.reduce((merged, { extend }) => {
|
|---|
| 75 | return mergeWith(merged, extend, (mergedValue, extendValue) => {
|
|---|
| 76 | if (mergedValue === undefined) {
|
|---|
| 77 | return [extendValue]
|
|---|
| 78 | }
|
|---|
| 79 |
|
|---|
| 80 | if (Array.isArray(mergedValue)) {
|
|---|
| 81 | return [extendValue, ...mergedValue]
|
|---|
| 82 | }
|
|---|
| 83 |
|
|---|
| 84 | return [extendValue, mergedValue]
|
|---|
| 85 | })
|
|---|
| 86 | }, {})
|
|---|
| 87 | }
|
|---|
| 88 |
|
|---|
| 89 | function mergeThemes(themes) {
|
|---|
| 90 | return {
|
|---|
| 91 | ...themes.reduce((merged, theme) => defaults(merged, theme), {}),
|
|---|
| 92 |
|
|---|
| 93 | // In order to resolve n config objects, we combine all of their `extend` properties
|
|---|
| 94 | // into arrays instead of objects so they aren't overridden.
|
|---|
| 95 | extend: collectExtends(themes),
|
|---|
| 96 | }
|
|---|
| 97 | }
|
|---|
| 98 |
|
|---|
| 99 | function mergeExtensionCustomizer(merged, value) {
|
|---|
| 100 | // When we have an array of objects, we do want to merge it
|
|---|
| 101 | if (Array.isArray(merged) && isPlainObject(merged[0])) {
|
|---|
| 102 | return merged.concat(value)
|
|---|
| 103 | }
|
|---|
| 104 |
|
|---|
| 105 | // When the incoming value is an array, and the existing config is an object, prepend the existing object
|
|---|
| 106 | if (Array.isArray(value) && isPlainObject(value[0]) && isPlainObject(merged)) {
|
|---|
| 107 | return [merged, ...value]
|
|---|
| 108 | }
|
|---|
| 109 |
|
|---|
| 110 | // Override arrays (for example for font-families, box-shadows, ...)
|
|---|
| 111 | if (Array.isArray(value)) {
|
|---|
| 112 | return value
|
|---|
| 113 | }
|
|---|
| 114 |
|
|---|
| 115 | // Execute default behaviour
|
|---|
| 116 | return undefined
|
|---|
| 117 | }
|
|---|
| 118 |
|
|---|
| 119 | function mergeExtensions({ extend, ...theme }) {
|
|---|
| 120 | return mergeWith(theme, extend, (themeValue, extensions) => {
|
|---|
| 121 | // The `extend` property is an array, so we need to check if it contains any functions
|
|---|
| 122 | if (!isFunction(themeValue) && !extensions.some(isFunction)) {
|
|---|
| 123 | return mergeWith({}, themeValue, ...extensions, mergeExtensionCustomizer)
|
|---|
| 124 | }
|
|---|
| 125 |
|
|---|
| 126 | return (resolveThemePath, utils) =>
|
|---|
| 127 | mergeWith(
|
|---|
| 128 | {},
|
|---|
| 129 | ...[themeValue, ...extensions].map((e) => value(e, resolveThemePath, utils)),
|
|---|
| 130 | mergeExtensionCustomizer
|
|---|
| 131 | )
|
|---|
| 132 | })
|
|---|
| 133 | }
|
|---|
| 134 |
|
|---|
| 135 | /**
|
|---|
| 136 | *
|
|---|
| 137 | * @param {string} key
|
|---|
| 138 | * @return {Iterable<string[] & {alpha: string | undefined}>}
|
|---|
| 139 | */
|
|---|
| 140 | function* toPaths(key) {
|
|---|
| 141 | let path = toPath(key)
|
|---|
| 142 |
|
|---|
| 143 | if (path.length === 0) {
|
|---|
| 144 | return
|
|---|
| 145 | }
|
|---|
| 146 |
|
|---|
| 147 | yield path
|
|---|
| 148 |
|
|---|
| 149 | if (Array.isArray(key)) {
|
|---|
| 150 | return
|
|---|
| 151 | }
|
|---|
| 152 |
|
|---|
| 153 | let pattern = /^(.*?)\s*\/\s*([^/]+)$/
|
|---|
| 154 | let matches = key.match(pattern)
|
|---|
| 155 |
|
|---|
| 156 | if (matches !== null) {
|
|---|
| 157 | let [, prefix, alpha] = matches
|
|---|
| 158 |
|
|---|
| 159 | let newPath = toPath(prefix)
|
|---|
| 160 | newPath.alpha = alpha
|
|---|
| 161 |
|
|---|
| 162 | yield newPath
|
|---|
| 163 | }
|
|---|
| 164 | }
|
|---|
| 165 |
|
|---|
| 166 | function resolveFunctionKeys(object) {
|
|---|
| 167 | // theme('colors.red.500 / 0.5') -> ['colors', 'red', '500 / 0', '5]
|
|---|
| 168 |
|
|---|
| 169 | const resolvePath = (key, defaultValue) => {
|
|---|
| 170 | for (const path of toPaths(key)) {
|
|---|
| 171 | let index = 0
|
|---|
| 172 | let val = object
|
|---|
| 173 |
|
|---|
| 174 | while (val !== undefined && val !== null && index < path.length) {
|
|---|
| 175 | val = val[path[index++]]
|
|---|
| 176 |
|
|---|
| 177 | let shouldResolveAsFn =
|
|---|
| 178 | isFunction(val) && (path.alpha === undefined || index <= path.length - 1)
|
|---|
| 179 |
|
|---|
| 180 | val = shouldResolveAsFn ? val(resolvePath, configUtils) : val
|
|---|
| 181 | }
|
|---|
| 182 |
|
|---|
| 183 | if (val !== undefined) {
|
|---|
| 184 | if (path.alpha !== undefined) {
|
|---|
| 185 | let normalized = parseColorFormat(val)
|
|---|
| 186 |
|
|---|
| 187 | return withAlphaValue(normalized, path.alpha, toColorValue(normalized))
|
|---|
| 188 | }
|
|---|
| 189 |
|
|---|
| 190 | if (isPlainObject(val)) {
|
|---|
| 191 | return cloneDeep(val)
|
|---|
| 192 | }
|
|---|
| 193 |
|
|---|
| 194 | return val
|
|---|
| 195 | }
|
|---|
| 196 | }
|
|---|
| 197 |
|
|---|
| 198 | return defaultValue
|
|---|
| 199 | }
|
|---|
| 200 |
|
|---|
| 201 | Object.assign(resolvePath, {
|
|---|
| 202 | theme: resolvePath,
|
|---|
| 203 | ...configUtils,
|
|---|
| 204 | })
|
|---|
| 205 |
|
|---|
| 206 | return Object.keys(object).reduce((resolved, key) => {
|
|---|
| 207 | resolved[key] = isFunction(object[key]) ? object[key](resolvePath, configUtils) : object[key]
|
|---|
| 208 |
|
|---|
| 209 | return resolved
|
|---|
| 210 | }, {})
|
|---|
| 211 | }
|
|---|
| 212 |
|
|---|
| 213 | function extractPluginConfigs(configs) {
|
|---|
| 214 | let allConfigs = []
|
|---|
| 215 |
|
|---|
| 216 | configs.forEach((config) => {
|
|---|
| 217 | allConfigs = [...allConfigs, config]
|
|---|
| 218 |
|
|---|
| 219 | const plugins = config?.plugins ?? []
|
|---|
| 220 |
|
|---|
| 221 | if (plugins.length === 0) {
|
|---|
| 222 | return
|
|---|
| 223 | }
|
|---|
| 224 |
|
|---|
| 225 | plugins.forEach((plugin) => {
|
|---|
| 226 | if (plugin.__isOptionsFunction) {
|
|---|
| 227 | plugin = plugin()
|
|---|
| 228 | }
|
|---|
| 229 | allConfigs = [...allConfigs, ...extractPluginConfigs([plugin?.config ?? {}])]
|
|---|
| 230 | })
|
|---|
| 231 | })
|
|---|
| 232 |
|
|---|
| 233 | return allConfigs
|
|---|
| 234 | }
|
|---|
| 235 |
|
|---|
| 236 | function resolveCorePlugins(corePluginConfigs) {
|
|---|
| 237 | const result = [...corePluginConfigs].reduceRight((resolved, corePluginConfig) => {
|
|---|
| 238 | if (isFunction(corePluginConfig)) {
|
|---|
| 239 | return corePluginConfig({ corePlugins: resolved })
|
|---|
| 240 | }
|
|---|
| 241 | return configurePlugins(corePluginConfig, resolved)
|
|---|
| 242 | }, corePluginList)
|
|---|
| 243 |
|
|---|
| 244 | return result
|
|---|
| 245 | }
|
|---|
| 246 |
|
|---|
| 247 | function resolvePluginLists(pluginLists) {
|
|---|
| 248 | const result = [...pluginLists].reduceRight((resolved, pluginList) => {
|
|---|
| 249 | return [...resolved, ...pluginList]
|
|---|
| 250 | }, [])
|
|---|
| 251 |
|
|---|
| 252 | return result
|
|---|
| 253 | }
|
|---|
| 254 |
|
|---|
| 255 | export default function resolveConfig(configs) {
|
|---|
| 256 | let allConfigs = [
|
|---|
| 257 | ...extractPluginConfigs(configs),
|
|---|
| 258 | {
|
|---|
| 259 | prefix: '',
|
|---|
| 260 | important: false,
|
|---|
| 261 | separator: ':',
|
|---|
| 262 | },
|
|---|
| 263 | ]
|
|---|
| 264 |
|
|---|
| 265 | return normalizeConfig(
|
|---|
| 266 | defaults(
|
|---|
| 267 | {
|
|---|
| 268 | theme: resolveFunctionKeys(
|
|---|
| 269 | mergeExtensions(mergeThemes(allConfigs.map((t) => t?.theme ?? {})))
|
|---|
| 270 | ),
|
|---|
| 271 | corePlugins: resolveCorePlugins(allConfigs.map((c) => c.corePlugins)),
|
|---|
| 272 | plugins: resolvePluginLists(configs.map((c) => c?.plugins ?? [])),
|
|---|
| 273 | },
|
|---|
| 274 | ...allConfigs
|
|---|
| 275 | )
|
|---|
| 276 | )
|
|---|
| 277 | }
|
|---|