source: frontend/node_modules/tailwindcss/src/util/resolveConfig.js

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 6.8 KB
RevLine 
[9af201e]1import negateValue from './negateValue'
2import corePluginList from '../corePluginList'
3import configurePlugins from './configurePlugins'
4import colors from '../public/colors'
5import { defaults } from './defaults'
6import { toPath } from './toPath'
7import { normalizeConfig } from './normalizeConfig'
8import isPlainObject from './isPlainObject'
9import { cloneDeep } from './cloneDeep'
10import { parseColorFormat } from './pluginUtils'
11import { withAlphaValue } from './withAlphaVariable'
12import toColorValue from './toColorValue'
13
14function isFunction(input) {
15 return typeof input === 'function'
16}
17
18function 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
40const 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
69function value(valueToResolve, ...args) {
70 return isFunction(valueToResolve) ? valueToResolve(...args) : valueToResolve
71}
72
73function 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
89function 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
99function 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
119function 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 */
140function* 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
166function 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
213function 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
236function 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
247function resolvePluginLists(pluginLists) {
248 const result = [...pluginLists].reduceRight((resolved, pluginList) => {
249 return [...resolved, ...pluginList]
250 }, [])
251
252 return result
253}
254
255export 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}
Note: See TracBrowser for help on using the repository browser.