Skip to content

Commit e372011

Browse files
committed
Support square bracket notation in paths
1 parent a7263a8 commit e372011

File tree

4 files changed

+135
-7
lines changed

4 files changed

+135
-7
lines changed

src/lib/evaluateTailwindFunctions.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function validatePath(config, path, defaultValue) {
4242
? pathToString(path)
4343
: path.replace(/^['"]+/g, '').replace(/['"]+$/g, '')
4444
const pathSegments = Array.isArray(path) ? path : toPath(pathString)
45-
const value = dlv(config.theme, pathString, defaultValue)
45+
const value = dlv(config.theme, pathSegments, defaultValue)
4646

4747
if (value === undefined) {
4848
let error = `'${pathString}' does not exist in your theme config.`

src/util/toPath.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,26 @@
1+
/**
2+
* Parse a path string into an array of path segments.
3+
*
4+
* Square bracket notation `a[b]` may be used to "escape" dots that would otherwise be interpreted as path separators.
5+
*
6+
* Example:
7+
* a -> ['a]
8+
* a.b.c -> ['a', 'b', 'c']
9+
* a[b].c -> ['a', 'b', 'c']
10+
* a[b.c].e.f -> ['a', 'b.c', 'e', 'f']
11+
* a[b][c][d] -> ['a', 'b', 'c', 'd']
12+
*
13+
* @param {string|string[]} path
14+
**/
115
export function toPath(path) {
216
if (Array.isArray(path)) return path
3-
return path.split(/[\.\]\[]+/g)
17+
18+
let openBrackets = path.split('[').length - 1
19+
let closedBrackets = path.split(']').length - 1
20+
21+
if (openBrackets !== closedBrackets) {
22+
throw new Error(`Path is invalid. Has unbalanced brackets: ${path}`)
23+
}
24+
25+
return path.split(/\.(?![^\[]*\])|[\[\]]/g).filter(Boolean)
426
}

tests/evaluateTailwindFunctions.test.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,111 @@ test('it looks up values in the theme using dot notation', () => {
3131
})
3232
})
3333

34+
test('it looks up values in the theme using bracket notation', () => {
35+
let input = css`
36+
.banana {
37+
color: theme('colors[yellow]');
38+
}
39+
`
40+
41+
let output = css`
42+
.banana {
43+
color: #f7cc50;
44+
}
45+
`
46+
47+
return run(input, {
48+
theme: {
49+
colors: {
50+
yellow: '#f7cc50',
51+
},
52+
},
53+
}).then((result) => {
54+
expect(result.css).toEqual(output)
55+
expect(result.warnings().length).toBe(0)
56+
})
57+
})
58+
59+
test('it looks up values in the theme using consecutive bracket notation', () => {
60+
let input = css`
61+
.banana {
62+
color: theme('colors[yellow][100]');
63+
}
64+
`
65+
66+
let output = css`
67+
.banana {
68+
color: #f7cc50;
69+
}
70+
`
71+
72+
return run(input, {
73+
theme: {
74+
colors: {
75+
yellow: {
76+
100: '#f7cc50',
77+
},
78+
},
79+
},
80+
}).then((result) => {
81+
expect(result.css).toEqual(output)
82+
expect(result.warnings().length).toBe(0)
83+
})
84+
})
85+
86+
test('it looks up values in the theme using bracket notation that have dots in them', () => {
87+
let input = css`
88+
.banana {
89+
padding-top: theme('spacing[1.5]');
90+
}
91+
`
92+
93+
let output = css`
94+
.banana {
95+
padding-top: 0.375rem;
96+
}
97+
`
98+
99+
return run(input, {
100+
theme: {
101+
spacing: {
102+
'1.5': '0.375rem',
103+
},
104+
},
105+
}).then((result) => {
106+
expect(result.css).toEqual(output)
107+
expect(result.warnings().length).toBe(0)
108+
})
109+
})
110+
111+
test('theme with mismatched brackets throws an error ', async () => {
112+
let config = {
113+
theme: {
114+
spacing: {
115+
'1.5': '0.375rem',
116+
},
117+
},
118+
}
119+
120+
let input = (path) => css`
121+
.banana {
122+
padding-top: theme('${path}');
123+
}
124+
`
125+
126+
await expect(run(input('spacing[1.5]]'), config)).rejects.toThrowError(
127+
`Path is invalid. Has unbalanced brackets: spacing[1.5]]`
128+
)
129+
130+
await expect(run(input('spacing[[1.5]'), config)).rejects.toThrowError(
131+
`Path is invalid. Has unbalanced brackets: spacing[[1.5]`
132+
)
133+
134+
await expect(run(input('spacing[a['), config)).rejects.toThrowError(
135+
`Path is invalid. Has unbalanced brackets: spacing[a[`
136+
)
137+
})
138+
34139
test('color can be a function', () => {
35140
let input = css`
36141
.backgroundColor {

tests/to-path.test.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ it('should keep an array as an array', () => {
77
})
88

99
it.each`
10-
input | output
11-
${'a.b.c'} | ${['a', 'b', 'c']}
12-
${'a[0].b.c'} | ${['a', '0', 'b', 'c']}
13-
${'.a'} | ${['', 'a']}
14-
${'[].a'} | ${['', 'a']}
10+
input | output
11+
${'a.b.c'} | ${['a', 'b', 'c']}
12+
${'a[0].b.c'} | ${['a', '0', 'b', 'c']}
13+
${'.a'} | ${['a']}
14+
${'[].a'} | ${['a']}
15+
${'a[1.5][b][c]'} | ${['a', '1.5', 'b', 'c']}
1516
`('should convert "$input" to "$output"', ({ input, output }) => {
1617
expect(toPath(input)).toEqual(output)
1718
})

0 commit comments

Comments
 (0)