diff options
Diffstat (limited to 'node_modules/tailwindcss/src/util/resolveConfig.js')
| -rw-r--r-- | node_modules/tailwindcss/src/util/resolveConfig.js | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/node_modules/tailwindcss/src/util/resolveConfig.js b/node_modules/tailwindcss/src/util/resolveConfig.js new file mode 100644 index 0000000..cfeda6e --- /dev/null +++ b/node_modules/tailwindcss/src/util/resolveConfig.js @@ -0,0 +1,277 @@ +import negateValue from './negateValue' +import corePluginList from '../corePluginList' +import configurePlugins from './configurePlugins' +import colors from '../public/colors' +import { defaults } from './defaults' +import { toPath } from './toPath' +import { normalizeConfig } from './normalizeConfig' +import isPlainObject from './isPlainObject' +import { cloneDeep } from './cloneDeep' +import { parseColorFormat } from './pluginUtils' +import { withAlphaValue } from './withAlphaVariable' +import toColorValue from './toColorValue' + +function isFunction(input) { + return typeof input === 'function' +} + +function mergeWith(target, ...sources) { + let customizer = sources.pop() + + for (let source of sources) { + for (let k in source) { + let merged = customizer(target[k], source[k]) + + if (merged === undefined) { + if (isPlainObject(target[k]) && isPlainObject(source[k])) { + target[k] = mergeWith({}, target[k], source[k], customizer) + } else { + target[k] = source[k] + } + } else { + target[k] = merged + } + } + } + + return target +} + +const configUtils = { + colors, + negative(scale) { + // TODO: Log that this function isn't really needed anymore? + return Object.keys(scale) + .filter((key) => scale[key] !== '0') + .reduce((negativeScale, key) => { + let negativeValue = negateValue(scale[key]) + + if (negativeValue !== undefined) { + negativeScale[`-${key}`] = negativeValue + } + + return negativeScale + }, {}) + }, + breakpoints(screens) { + return Object.keys(screens) + .filter((key) => typeof screens[key] === 'string') + .reduce( + (breakpoints, key) => ({ + ...breakpoints, + [`screen-${key}`]: screens[key], + }), + {} + ) + }, +} + +function value(valueToResolve, ...args) { + return isFunction(valueToResolve) ? valueToResolve(...args) : valueToResolve +} + +function collectExtends(items) { + return items.reduce((merged, { extend }) => { + return mergeWith(merged, extend, (mergedValue, extendValue) => { + if (mergedValue === undefined) { + return [extendValue] + } + + if (Array.isArray(mergedValue)) { + return [extendValue, ...mergedValue] + } + + return [extendValue, mergedValue] + }) + }, {}) +} + +function mergeThemes(themes) { + return { + ...themes.reduce((merged, theme) => defaults(merged, theme), {}), + + // In order to resolve n config objects, we combine all of their `extend` properties + // into arrays instead of objects so they aren't overridden. + extend: collectExtends(themes), + } +} + +function mergeExtensionCustomizer(merged, value) { + // When we have an array of objects, we do want to merge it + if (Array.isArray(merged) && isPlainObject(merged[0])) { + return merged.concat(value) + } + + // When the incoming value is an array, and the existing config is an object, prepend the existing object + if (Array.isArray(value) && isPlainObject(value[0]) && isPlainObject(merged)) { + return [merged, ...value] + } + + // Override arrays (for example for font-families, box-shadows, ...) + if (Array.isArray(value)) { + return value + } + + // Execute default behaviour + return undefined +} + +function mergeExtensions({ extend, ...theme }) { + return mergeWith(theme, extend, (themeValue, extensions) => { + // The `extend` property is an array, so we need to check if it contains any functions + if (!isFunction(themeValue) && !extensions.some(isFunction)) { + return mergeWith({}, themeValue, ...extensions, mergeExtensionCustomizer) + } + + return (resolveThemePath, utils) => + mergeWith( + {}, + ...[themeValue, ...extensions].map((e) => value(e, resolveThemePath, utils)), + mergeExtensionCustomizer + ) + }) +} + +/** + * + * @param {string} key + * @return {Iterable<string[] & {alpha: string | undefined}>} + */ +function* toPaths(key) { + let path = toPath(key) + + if (path.length === 0) { + return + } + + yield path + + if (Array.isArray(key)) { + return + } + + let pattern = /^(.*?)\s*\/\s*([^/]+)$/ + let matches = key.match(pattern) + + if (matches !== null) { + let [, prefix, alpha] = matches + + let newPath = toPath(prefix) + newPath.alpha = alpha + + yield newPath + } +} + +function resolveFunctionKeys(object) { + // theme('colors.red.500 / 0.5') -> ['colors', 'red', '500 / 0', '5] + + const resolvePath = (key, defaultValue) => { + for (const path of toPaths(key)) { + let index = 0 + let val = object + + while (val !== undefined && val !== null && index < path.length) { + val = val[path[index++]] + + let shouldResolveAsFn = + isFunction(val) && (path.alpha === undefined || index <= path.length - 1) + + val = shouldResolveAsFn ? val(resolvePath, configUtils) : val + } + + if (val !== undefined) { + if (path.alpha !== undefined) { + let normalized = parseColorFormat(val) + + return withAlphaValue(normalized, path.alpha, toColorValue(normalized)) + } + + if (isPlainObject(val)) { + return cloneDeep(val) + } + + return val + } + } + + return defaultValue + } + + Object.assign(resolvePath, { + theme: resolvePath, + ...configUtils, + }) + + return Object.keys(object).reduce((resolved, key) => { + resolved[key] = isFunction(object[key]) ? object[key](resolvePath, configUtils) : object[key] + + return resolved + }, {}) +} + +function extractPluginConfigs(configs) { + let allConfigs = [] + + configs.forEach((config) => { + allConfigs = [...allConfigs, config] + + const plugins = config?.plugins ?? [] + + if (plugins.length === 0) { + return + } + + plugins.forEach((plugin) => { + if (plugin.__isOptionsFunction) { + plugin = plugin() + } + allConfigs = [...allConfigs, ...extractPluginConfigs([plugin?.config ?? {}])] + }) + }) + + return allConfigs +} + +function resolveCorePlugins(corePluginConfigs) { + const result = [...corePluginConfigs].reduceRight((resolved, corePluginConfig) => { + if (isFunction(corePluginConfig)) { + return corePluginConfig({ corePlugins: resolved }) + } + return configurePlugins(corePluginConfig, resolved) + }, corePluginList) + + return result +} + +function resolvePluginLists(pluginLists) { + const result = [...pluginLists].reduceRight((resolved, pluginList) => { + return [...resolved, ...pluginList] + }, []) + + return result +} + +export default function resolveConfig(configs) { + let allConfigs = [ + ...extractPluginConfigs(configs), + { + prefix: '', + important: false, + separator: ':', + }, + ] + + return normalizeConfig( + defaults( + { + theme: resolveFunctionKeys( + mergeExtensions(mergeThemes(allConfigs.map((t) => t?.theme ?? {}))) + ), + corePlugins: resolveCorePlugins(allConfigs.map((c) => c.corePlugins)), + plugins: resolvePluginLists(configs.map((c) => c?.plugins ?? [])), + }, + ...allConfigs + ) + ) +} |