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
This commit is contained in:
Robin Malfait 2021-09-09 18:10:04 +02:00 • committed by GitHub
parent 4919cbfbb8
commit 9daebe43b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 128 additions and 54 deletions

View file

@ -9,7 +9,6 @@ import withAlphaVariable, { withAlphaValue } from './util/withAlphaVariable'
import toColorValue from './util/toColorValue'
import isPlainObject from './util/isPlainObject'
import transformThemeValue from './util/transformThemeValue'
import nameClass from './util/nameClass'
import {
applyPseudoToMarker,
updateLastClasses,
@ -17,7 +16,6 @@ import {
transformAllSelectors,
transformAllClasses,
transformLastClasses,
asList,
asLength,
asLookupValue,
} from './util/pluginUtils'
@ -829,16 +827,12 @@ export let gridAutoFlow = ({ addUtilities }) => {
}
export let gridAutoRows = createUtilityPlugin('gridAutoRows', [['auto-rows', ['gridAutoRows']]])
export let gridTemplateColumns = createUtilityPlugin(
'gridTemplateColumns',
[['grid-cols', ['gridTemplateColumns']]],
{ resolveArbitraryValue: asList }
)
export let gridTemplateRows = createUtilityPlugin(
'gridTemplateRows',
[['grid-rows', ['gridTemplateRows']]],
{ resolveArbitraryValue: asList }
)
export let gridTemplateColumns = createUtilityPlugin('gridTemplateColumns', [
['grid-cols', ['gridTemplateColumns']],
])
export let gridTemplateRows = createUtilityPlugin('gridTemplateRows', [
['grid-rows', ['gridTemplateRows']],
])
export let flexDirection = ({ addUtilities }) => {
addUtilities({
@ -1431,12 +1425,8 @@ export let objectFit = ({ addUtilities }) => {
'.object-scale-down': { 'object-fit': 'scale-down' },
})
}
export let objectPosition = createUtilityPlugin('objectPosition', [['object', ['object-position']]])
export let objectPosition = createUtilityPlugin(
'objectPosition',
[['object', ['object-position']]],
{ resolveArbitraryValue: asList }
)
export let padding = createUtilityPlugin('padding', [
['p', ['padding']],
[
@ -1715,7 +1705,7 @@ export let boxShadow = (() => {
}
},
},
{ values: theme('boxShadow'), type: 'lookup' }
{ values: theme('boxShadow') }
)
}
})()
@ -1867,23 +1857,21 @@ export let contrast = ({ matchUtilities, theme }) => {
)
}
export let dropShadow = ({ addUtilities, theme }) => {
let utilities = Object.fromEntries(
Object.entries(theme('dropShadow') ?? {}).map(([modifier, value]) => {
return [
nameClass('drop-shadow', modifier),
{
export let dropShadow = ({ matchUtilities, theme }) => {
matchUtilities(
{
'drop-shadow': (value) => {
return {
'--tw-drop-shadow': Array.isArray(value)
? value.map((v) => `drop-shadow(${v})`).join(' ')
: `drop-shadow(${value})`,
'@defaults filter': {},
filter: 'var(--tw-filter)',
},
]
})
}
},
},
{ values: theme('dropShadow') }
)
addUtilities(utilities)
}
export let grayscale = ({ matchUtilities, theme }) => {

View file

@ -1,9 +1,8 @@
import transformThemeValue from './transformThemeValue'
import { asValue, asList, asColor, asAngle, asLength, asLookupValue } from '../util/pluginUtils'
import { asValue, asColor, asAngle, asLength, asLookupValue } from '../util/pluginUtils'
let asMap = new Map([
[asValue, 'any'],
[asList, 'list'],
[asColor, 'color'],
[asAngle, 'angle'],
[asLength, 'length'],

View file

@ -1,5 +1,4 @@
import selectorParser from 'postcss-selector-parser'
import postcss from 'postcss'
import escapeCommas from './escapeCommas'
import { withAlphaValue } from './withAlphaVariable'
import isKeyframeRule from './isKeyframeRule'
@ -153,11 +152,7 @@ export function transformLastClasses(transformClass, { wrap, withRule } = {}) {
}
}
export function asValue(
modifier,
lookup = {},
{ validate = () => true, transform = (v) => v } = {}
) {
export function asValue(modifier, lookup = {}, { validate = () => true } = {}) {
let value = lookup[modifier]
if (value !== undefined) {
@ -174,8 +169,14 @@ export function asValue(
return undefined
}
// convert `_` to ` `, escept for escaped underscores `\_`
value = value
.replace(/([^\\])_/g, '$1 ')
.replace(/^_/g, ' ')
.replace(/\\_/g, '_')
// add spaces around operators inside calc() that do not follow an operator or (
return transform(value).replace(
return value.replace(
/(-?\d*\.?\d(?!\b-.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g,
'$1 $2 '
)
@ -190,20 +191,6 @@ export function asUnit(modifier, units, lookup = {}) {
new RegExp(`^calc\\(.+?${unitsPattern}`).test(value)
)
},
transform: (value) => {
return value
},
})
}
export function asList(modifier, lookup = {}) {
return asValue(modifier, lookup, {
transform: (value) => {
return postcss.list
.comma(value)
.map((v) => v.replace(/,/g, ', '))
.join(' ')
},
})
}
@ -281,7 +268,6 @@ export function asLookupValue(modifier, lookup = {}) {
let typeMap = {
any: asValue,
list: asList,
color: asColor,
angle: asAngle,
length: asLength,

View file

@ -1,3 +1,5 @@
import postcss from 'postcss'
export default function transformThemeValue(themeSection) {
if (['fontSize', 'outline'].includes(themeSection)) {
return (value) => (Array.isArray(value) ? value[0] : value)
@ -21,6 +23,12 @@ export default function transformThemeValue(themeSection) {
return (value) => (Array.isArray(value) ? value.join(', ') : value)
}
// For backwards compatibility reasons, before we switched to underscores
// instead of commas for arbitrary values.
if (['gridTemplateColumns', 'gridTemplateRows', 'objectPosition'].includes(themeSection)) {
return (value) => (typeof value === 'string' ? postcss.list.comma(value).join(' ') : value)
}
if (themeSection === 'colors') {
return (value) => (typeof value === 'function' ? value({}) : value)
}

View file

@ -1,7 +1,7 @@
import fs from 'fs'
import path from 'path'
import { run } from './util/run'
import { run, html, css } from './util/run'
test('arbitrary values', () => {
let config = {
@ -15,3 +15,96 @@ test('arbitrary values', () => {
expect(result.css).toMatchFormattedCss(expected)
})
})
it('should convert _ to spaces', () => {
let config = {
content: [
{
raw: html`
<div class="grid-cols-[200px_repeat(auto-fill,minmax(15%,100px))_300px]"></div>
<div class="grid-rows-[200px_repeat(auto-fill,minmax(15%,100px))_300px]"></div>
<div class="shadow-[0px_0px_4px_black]"></div>
<div class="rounded-[0px_4px_4px_0px]"></div>
<div class="m-[8px_4px]"></div>
<div class="p-[8px_4px]"></div>
<div class="flex-[1_1_100%]"></div>
<div class="col-[span_3_/_span_8]"></div>
<div class="row-[span_3_/_span_8]"></div>
<div class="auto-cols-[minmax(0,_1fr)]"></div>
<div class="drop-shadow-[0px_1px_3px_black]"></div>
<div class="content-[_hello_world_]"></div>
`,
},
],
corePlugins: { preflight: false },
}
return run('@tailwind utilities', config).then((result) => {
return expect(result.css).toMatchFormattedCss(css`
.col-\\[span_3_\\/_span_8\\] {
grid-column: span 3 / span 8;
}
.row-\\[span_3_\\/_span_8\\] {
grid-row: span 3 / span 8;
}
.m-\\[8px_4px\\] {
margin: 8px 4px;
}
.flex-\\[1_1_100\\%\\] {
flex: 1 1 100%;
}
.auto-cols-\\[minmax\\(0\\2c _1fr\\)\\] {
grid-auto-columns: minmax(0, 1fr);
}
.grid-cols-\\[200px_repeat\\(auto-fill\\2c minmax\\(15\\%\\2c 100px\\)\\)_300px\\] {
grid-template-columns: 200px repeat(auto-fill, minmax(15%, 100px)) 300px;
}
.grid-rows-\\[200px_repeat\\(auto-fill\\2c minmax\\(15\\%\\2c 100px\\)\\)_300px\\] {
grid-template-rows: 200px repeat(auto-fill, minmax(15%, 100px)) 300px;
}
.rounded-\\[0px_4px_4px_0px\\] {
border-radius: 0px 4px 4px 0px;
}
.p-\\[8px_4px\\] {
padding: 8px 4px;
}
.shadow-\\[0px_0px_4px_black\\] {
--tw-shadow: 0px 0px 4px black;
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000),
var(--tw-shadow);
}
.drop-shadow-\\[0px_1px_3px_black\\] {
--tw-drop-shadow: drop-shadow(0px 1px 3px black);
filter: var(--tw-filter);
}
.content-\\[_hello_world_\\] {
content: hello world;
}
`)
})
})
it('should not convert escaped underscores with spaces', () => {
let config = {
content: [{ raw: html` <div class="content-['snake\\_case']"></div> ` }],
corePlugins: { preflight: false },
}
return run('@tailwind utilities', config).then((result) => {
return expect(result.css).toMatchFormattedCss(css`
.content-\\[\\'snake\\\\_case\\'\\] {
content: 'snake_case';
}
`)
})
})