diff --git a/src/corePlugins.js b/src/corePlugins.js index 12228c7bd..f9a9a97cb 100644 --- a/src/corePlugins.js +++ b/src/corePlugins.js @@ -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 }) => { diff --git a/src/util/createUtilityPlugin.js b/src/util/createUtilityPlugin.js index 48985b10a..5d03df62f 100644 --- a/src/util/createUtilityPlugin.js +++ b/src/util/createUtilityPlugin.js @@ -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'], diff --git a/src/util/pluginUtils.js b/src/util/pluginUtils.js index 373cd15d1..bdbc3278b 100644 --- a/src/util/pluginUtils.js +++ b/src/util/pluginUtils.js @@ -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, diff --git a/src/util/transformThemeValue.js b/src/util/transformThemeValue.js index 5b24ee711..dbd7a3e2e 100644 --- a/src/util/transformThemeValue.js +++ b/src/util/transformThemeValue.js @@ -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) } diff --git a/tests/arbitrary-values.test.js b/tests/arbitrary-values.test.js index e75d7f988..2472d7a77 100644 --- a/tests/arbitrary-values.test.js +++ b/tests/arbitrary-values.test.js @@ -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` +
+
+
+
+
+
+
+
+
+
+
+
+ `, + }, + ], + 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`
` }], + corePlugins: { preflight: false }, + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .content-\\[\\'snake\\\\_case\\'\\] { + content: 'snake_case'; + } + `) + }) +})