Skip to content

Commit e7b831f

Browse files
committed
Allow resolving an arbitrary number of stacked config files
1 parent 5ba0cc1 commit e7b831f

File tree

2 files changed

+166
-4
lines changed

2 files changed

+166
-4
lines changed

__tests__/resolveConfig.test.js

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,3 +1234,133 @@ test('custom properties are multiplied by -1 for negative values', () => {
12341234
variants: {},
12351235
})
12361236
})
1237+
1238+
test('more than two config objects can be resolved', () => {
1239+
const firstConfig = {
1240+
theme: {
1241+
extend: {
1242+
fontFamily: () => ({
1243+
code: ['Menlo', 'monospace'],
1244+
}),
1245+
colors: {
1246+
red: 'red',
1247+
},
1248+
backgroundColor: {
1249+
customBackgroundOne: '#bada55',
1250+
},
1251+
textDecorationColor: {
1252+
orange: 'orange'
1253+
}
1254+
},
1255+
},
1256+
}
1257+
1258+
const secondConfig = {
1259+
prefix: '-',
1260+
important: false,
1261+
separator: ':',
1262+
theme: {
1263+
extend: {
1264+
fontFamily: {
1265+
quote: ['Helvetica', 'serif'],
1266+
},
1267+
colors: {
1268+
green: 'green',
1269+
},
1270+
backgroundColor: {
1271+
customBackgroundTwo: '#facade',
1272+
},
1273+
textDecorationColor: theme => theme('colors')
1274+
},
1275+
},
1276+
}
1277+
1278+
const thirdConfig = {
1279+
prefix: '-',
1280+
important: false,
1281+
separator: ':',
1282+
theme: {
1283+
extend: {
1284+
fontFamily: {
1285+
hero: ['Futura', 'sans-serif'],
1286+
},
1287+
colors: {
1288+
pink: 'pink',
1289+
},
1290+
backgroundColor: () => ({
1291+
customBackgroundThree: '#c0ffee',
1292+
}),
1293+
textDecorationColor: {
1294+
lime: 'lime',
1295+
}
1296+
},
1297+
},
1298+
}
1299+
1300+
const defaultConfig = {
1301+
prefix: '-',
1302+
important: false,
1303+
separator: ':',
1304+
theme: {
1305+
fontFamily: {
1306+
body: ['Arial', 'sans-serif'],
1307+
display: ['Georgia', 'serif'],
1308+
},
1309+
colors: {
1310+
blue: 'blue',
1311+
},
1312+
backgroundColor: theme => theme('colors'),
1313+
},
1314+
variants: {
1315+
backgroundColor: ['responsive', 'hover', 'focus'],
1316+
},
1317+
}
1318+
1319+
const result = resolveConfig([
1320+
firstConfig,
1321+
secondConfig,
1322+
thirdConfig,
1323+
defaultConfig
1324+
])
1325+
1326+
expect(result).toEqual({
1327+
prefix: '-',
1328+
important: false,
1329+
separator: ':',
1330+
theme: {
1331+
fontFamily: {
1332+
body: ['Arial', 'sans-serif'],
1333+
display: ['Georgia', 'serif'],
1334+
code: ['Menlo', 'monospace'],
1335+
quote: ['Helvetica', 'serif'],
1336+
hero: ['Futura', 'sans-serif'],
1337+
},
1338+
colors: {
1339+
red: 'red',
1340+
green: 'green',
1341+
blue: 'blue',
1342+
pink: 'pink',
1343+
},
1344+
backgroundColor: {
1345+
red: 'red',
1346+
green: 'green',
1347+
blue: 'blue',
1348+
pink: 'pink',
1349+
customBackgroundOne: '#bada55',
1350+
customBackgroundTwo: '#facade',
1351+
customBackgroundThree: '#c0ffee',
1352+
},
1353+
textDecorationColor: {
1354+
red: 'red',
1355+
green: 'green',
1356+
blue: 'blue',
1357+
pink: 'pink',
1358+
orange: 'orange',
1359+
lime: 'lime',
1360+
},
1361+
},
1362+
variants: {
1363+
backgroundColor: ['responsive', 'hover', 'focus'],
1364+
},
1365+
})
1366+
})

src/util/resolveConfig.js

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
import some from 'lodash/some'
12
import mergeWith from 'lodash/mergeWith'
3+
import assignWith from 'lodash/assignWith'
24
import isFunction from 'lodash/isFunction'
5+
import isUndefined from 'lodash/isUndefined'
36
import defaults from 'lodash/defaults'
47
import map from 'lodash/map'
8+
import reduce from 'lodash/reduce'
59
import toPath from 'lodash/toPath'
610
import negateValue from './negateValue'
711

@@ -23,18 +27,46 @@ function value(valueToResolve, ...args) {
2327
return isFunction(valueToResolve) ? valueToResolve(...args) : valueToResolve
2428
}
2529

30+
function mergeThemes(themes) {
31+
const theme = (({ extend, ...t }) => t)(themes.reduce((merged, t) => {
32+
return defaults(merged, t)
33+
}, {}))
34+
35+
// In order to resolve n config objects, we combine all of their `extend` properties
36+
// into arrays instead of objects so they aren't overridden.
37+
const extend = themes.reduce((merged, { extend }) => {
38+
return mergeWith(merged, extend, (mergedValue, extendValue) => {
39+
if (isUndefined(mergedValue)) {
40+
return [extendValue]
41+
}
42+
43+
if (Array.isArray(mergedValue)) {
44+
return [...mergedValue, extendValue]
45+
}
46+
47+
return [mergedValue, extendValue]
48+
})
49+
}, {})
50+
51+
return {
52+
...theme,
53+
extend,
54+
}
55+
}
56+
2657
function mergeExtensions({ extend, ...theme }) {
2758
return mergeWith(theme, extend, (themeValue, extensions) => {
28-
if (!isFunction(themeValue) && !isFunction(extensions)) {
59+
// The `extend` property is an array, so we need to check if it contains any functions
60+
if (!isFunction(themeValue) && !some(extensions, isFunction)) {
2961
return {
3062
...themeValue,
31-
...extensions,
63+
...Object.assign({}, ...extensions),
3264
}
3365
}
3466

3567
return (resolveThemePath, utils) => ({
3668
...value(themeValue, resolveThemePath, utils),
37-
...value(extensions, resolveThemePath, utils),
69+
...Object.assign({}, ...extensions.map(e => value(e, resolveThemePath, utils))),
3870
})
3971
})
4072
}
@@ -65,7 +97,7 @@ function resolveFunctionKeys(object) {
6597
export default function resolveConfig(configs) {
6698
return defaults(
6799
{
68-
theme: resolveFunctionKeys(mergeExtensions(defaults({}, ...map(configs, 'theme')))),
100+
theme: resolveFunctionKeys(mergeExtensions(mergeThemes(map(configs, 'theme')))),
69101
variants: (firstVariants => {
70102
return Array.isArray(firstVariants)
71103
? firstVariants

0 commit comments

Comments
 (0)