Skip to content

Commit 9daebe4

Browse files
authored
Replace underscore with space in arbitrary values (#5460)
* refactor dropShadow plugin, use `matchUtilities` * replace `_` with ` ` for arbitrary values * remove custom `asList` function * do not replace escaped underscores with spaces
1 parent 4919cbf commit 9daebe4

5 files changed

+128
-54
lines changed

src/corePlugins.js

+17-29
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,13 @@ import withAlphaVariable, { withAlphaValue } from './util/withAlphaVariable'
99
import toColorValue from './util/toColorValue'
1010
import isPlainObject from './util/isPlainObject'
1111
import transformThemeValue from './util/transformThemeValue'
12-
import nameClass from './util/nameClass'
1312
import {
1413
applyPseudoToMarker,
1514
updateLastClasses,
1615
updateAllClasses,
1716
transformAllSelectors,
1817
transformAllClasses,
1918
transformLastClasses,
20-
asList,
2119
asLength,
2220
asLookupValue,
2321
} from './util/pluginUtils'
@@ -829,16 +827,12 @@ export let gridAutoFlow = ({ addUtilities }) => {
829827
}
830828

831829
export let gridAutoRows = createUtilityPlugin('gridAutoRows', [['auto-rows', ['gridAutoRows']]])
832-
export let gridTemplateColumns = createUtilityPlugin(
833-
'gridTemplateColumns',
834-
[['grid-cols', ['gridTemplateColumns']]],
835-
{ resolveArbitraryValue: asList }
836-
)
837-
export let gridTemplateRows = createUtilityPlugin(
838-
'gridTemplateRows',
839-
[['grid-rows', ['gridTemplateRows']]],
840-
{ resolveArbitraryValue: asList }
841-
)
830+
export let gridTemplateColumns = createUtilityPlugin('gridTemplateColumns', [
831+
['grid-cols', ['gridTemplateColumns']],
832+
])
833+
export let gridTemplateRows = createUtilityPlugin('gridTemplateRows', [
834+
['grid-rows', ['gridTemplateRows']],
835+
])
842836

843837
export let flexDirection = ({ addUtilities }) => {
844838
addUtilities({
@@ -1431,12 +1425,8 @@ export let objectFit = ({ addUtilities }) => {
14311425
'.object-scale-down': { 'object-fit': 'scale-down' },
14321426
})
14331427
}
1428+
export let objectPosition = createUtilityPlugin('objectPosition', [['object', ['object-position']]])
14341429

1435-
export let objectPosition = createUtilityPlugin(
1436-
'objectPosition',
1437-
[['object', ['object-position']]],
1438-
{ resolveArbitraryValue: asList }
1439-
)
14401430
export let padding = createUtilityPlugin('padding', [
14411431
['p', ['padding']],
14421432
[
@@ -1715,7 +1705,7 @@ export let boxShadow = (() => {
17151705
}
17161706
},
17171707
},
1718-
{ values: theme('boxShadow'), type: 'lookup' }
1708+
{ values: theme('boxShadow') }
17191709
)
17201710
}
17211711
})()
@@ -1867,23 +1857,21 @@ export let contrast = ({ matchUtilities, theme }) => {
18671857
)
18681858
}
18691859

1870-
export let dropShadow = ({ addUtilities, theme }) => {
1871-
let utilities = Object.fromEntries(
1872-
Object.entries(theme('dropShadow') ?? {}).map(([modifier, value]) => {
1873-
return [
1874-
nameClass('drop-shadow', modifier),
1875-
{
1860+
export let dropShadow = ({ matchUtilities, theme }) => {
1861+
matchUtilities(
1862+
{
1863+
'drop-shadow': (value) => {
1864+
return {
18761865
'--tw-drop-shadow': Array.isArray(value)
18771866
? value.map((v) => `drop-shadow(${v})`).join(' ')
18781867
: `drop-shadow(${value})`,
18791868
'@defaults filter': {},
18801869
filter: 'var(--tw-filter)',
1881-
},
1882-
]
1883-
})
1870+
}
1871+
},
1872+
},
1873+
{ values: theme('dropShadow') }
18841874
)
1885-
1886-
addUtilities(utilities)
18871875
}
18881876

18891877
export let grayscale = ({ matchUtilities, theme }) => {

src/util/createUtilityPlugin.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import transformThemeValue from './transformThemeValue'
2-
import { asValue, asList, asColor, asAngle, asLength, asLookupValue } from '../util/pluginUtils'
2+
import { asValue, asColor, asAngle, asLength, asLookupValue } from '../util/pluginUtils'
33

44
let asMap = new Map([
55
[asValue, 'any'],
6-
[asList, 'list'],
76
[asColor, 'color'],
87
[asAngle, 'angle'],
98
[asLength, 'length'],

src/util/pluginUtils.js

+8-22
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import selectorParser from 'postcss-selector-parser'
2-
import postcss from 'postcss'
32
import escapeCommas from './escapeCommas'
43
import { withAlphaValue } from './withAlphaVariable'
54
import isKeyframeRule from './isKeyframeRule'
@@ -153,11 +152,7 @@ export function transformLastClasses(transformClass, { wrap, withRule } = {}) {
153152
}
154153
}
155154

156-
export function asValue(
157-
modifier,
158-
lookup = {},
159-
{ validate = () => true, transform = (v) => v } = {}
160-
) {
155+
export function asValue(modifier, lookup = {}, { validate = () => true } = {}) {
161156
let value = lookup[modifier]
162157

163158
if (value !== undefined) {
@@ -174,8 +169,14 @@ export function asValue(
174169
return undefined
175170
}
176171

172+
// convert `_` to ` `, escept for escaped underscores `\_`
173+
value = value
174+
.replace(/([^\\])_/g, '$1 ')
175+
.replace(/^_/g, ' ')
176+
.replace(/\\_/g, '_')
177+
177178
// add spaces around operators inside calc() that do not follow an operator or (
178-
return transform(value).replace(
179+
return value.replace(
179180
/(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g,
180181
'$1 $2 '
181182
)
@@ -190,20 +191,6 @@ export function asUnit(modifier, units, lookup = {}) {
190191
new RegExp(`^calc\\(.+?${unitsPattern}`).test(value)
191192
)
192193
},
193-
transform: (value) => {
194-
return value
195-
},
196-
})
197-
}
198-
199-
export function asList(modifier, lookup = {}) {
200-
return asValue(modifier, lookup, {
201-
transform: (value) => {
202-
return postcss.list
203-
.comma(value)
204-
.map((v) => v.replace(/,/g, ', '))
205-
.join(' ')
206-
},
207194
})
208195
}
209196

@@ -281,7 +268,6 @@ export function asLookupValue(modifier, lookup = {}) {
281268

282269
let typeMap = {
283270
any: asValue,
284-
list: asList,
285271
color: asColor,
286272
angle: asAngle,
287273
length: asLength,

src/util/transformThemeValue.js

+8
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import postcss from 'postcss'
2+
13
export default function transformThemeValue(themeSection) {
24
if (['fontSize', 'outline'].includes(themeSection)) {
35
return (value) => (Array.isArray(value) ? value[0] : value)
@@ -21,6 +23,12 @@ export default function transformThemeValue(themeSection) {
2123
return (value) => (Array.isArray(value) ? value.join(', ') : value)
2224
}
2325

26+
// For backwards compatibility reasons, before we switched to underscores
27+
// instead of commas for arbitrary values.
28+
if (['gridTemplateColumns', 'gridTemplateRows', 'objectPosition'].includes(themeSection)) {
29+
return (value) => (typeof value === 'string' ? postcss.list.comma(value).join(' ') : value)
30+
}
31+
2432
if (themeSection === 'colors') {
2533
return (value) => (typeof value === 'function' ? value({}) : value)
2634
}

tests/arbitrary-values.test.js

+94-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import fs from 'fs'
22
import path from 'path'
33

4-
import { run } from './util/run'
4+
import { run, html, css } from './util/run'
55

66
test('arbitrary values', () => {
77
let config = {
@@ -15,3 +15,96 @@ test('arbitrary values', () => {
1515
expect(result.css).toMatchFormattedCss(expected)
1616
})
1717
})
18+
19+
it('should convert _ to spaces', () => {
20+
let config = {
21+
content: [
22+
{
23+
raw: html`
24+
<div class="grid-cols-[200px_repeat(auto-fill,minmax(15%,100px))_300px]"></div>
25+
<div class="grid-rows-[200px_repeat(auto-fill,minmax(15%,100px))_300px]"></div>
26+
<div class="shadow-[0px_0px_4px_black]"></div>
27+
<div class="rounded-[0px_4px_4px_0px]"></div>
28+
<div class="m-[8px_4px]"></div>
29+
<div class="p-[8px_4px]"></div>
30+
<div class="flex-[1_1_100%]"></div>
31+
<div class="col-[span_3_/_span_8]"></div>
32+
<div class="row-[span_3_/_span_8]"></div>
33+
<div class="auto-cols-[minmax(0,_1fr)]"></div>
34+
<div class="drop-shadow-[0px_1px_3px_black]"></div>
35+
<div class="content-[_hello_world_]"></div>
36+
`,
37+
},
38+
],
39+
corePlugins: { preflight: false },
40+
}
41+
42+
return run('@tailwind utilities', config).then((result) => {
43+
return expect(result.css).toMatchFormattedCss(css`
44+
.col-\\[span_3_\\/_span_8\\] {
45+
grid-column: span 3 / span 8;
46+
}
47+
48+
.row-\\[span_3_\\/_span_8\\] {
49+
grid-row: span 3 / span 8;
50+
}
51+
52+
.m-\\[8px_4px\\] {
53+
margin: 8px 4px;
54+
}
55+
56+
.flex-\\[1_1_100\\%\\] {
57+
flex: 1 1 100%;
58+
}
59+
60+
.auto-cols-\\[minmax\\(0\\2c _1fr\\)\\] {
61+
grid-auto-columns: minmax(0, 1fr);
62+
}
63+
64+
.grid-cols-\\[200px_repeat\\(auto-fill\\2c minmax\\(15\\%\\2c 100px\\)\\)_300px\\] {
65+
grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px;
66+
}
67+
68+
.grid-rows-\\[200px_repeat\\(auto-fill\\2c minmax\\(15\\%\\2c 100px\\)\\)_300px\\] {
69+
grid-template-rows: 200px repeat(auto-fill, minmax(15%, 100px)) 300px;
70+
}
71+
72+
.rounded-\\[0px_4px_4px_0px\\] {
73+
border-radius: 0px 4px 4px 0px;
74+
}
75+
76+
.p-\\[8px_4px\\] {
77+
padding: 8px 4px;
78+
}
79+
80+
.shadow-\\[0px_0px_4px_black\\] {
81+
--tw-shadow: 0px 0px 4px black;
82+
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
83+
var(--tw-shadow);
84+
}
85+
86+
.drop-shadow-\\[0px_1px_3px_black\\] {
87+
--tw-drop-shadow: drop-shadow(0px 1px 3px black);
88+
filter: var(--tw-filter);
89+
}
90+
.content-\\[_hello_world_\\] {
91+
content: hello world;
92+
}
93+
`)
94+
})
95+
})
96+
97+
it('should not convert escaped underscores with spaces', () => {
98+
let config = {
99+
content: [{ raw: html` <div class="content-['snake\\_case']"></div> ` }],
100+
corePlugins: { preflight: false },
101+
}
102+
103+
return run('@tailwind utilities', config).then((result) => {
104+
return expect(result.css).toMatchFormattedCss(css`
105+
.content-\\[\\'snake\\\\_case\\'\\] {
106+
content: 'snake_case';
107+
}
108+
`)
109+
})
110+
})

0 commit comments

Comments
 (0)