Skip to content

Commit

Permalink
Improve normalisation of calc()-like functions (#11686)
Browse files Browse the repository at this point in the history
* parse the `calc()`-like expressions and format them

* update changelog

* Add test case for double negatives

wanted to be sure this worked

---------

Co-authored-by: Jordan Pittman <[email protected]>
  • Loading branch information
RobinMalfait and thecrypticace authored Jul 26, 2023
1 parent e0c52a9 commit ae4d213
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 11 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Bump `jiti`, `lightningcss`, `fast-glob` and `browserlist` dependencies and reflect `lightningcss` related improvements in tests ([#11550](https://github.com/tailwindlabs/tailwindcss/pull/11550))
- Make PostCSS plugin async to improve performance ([#11548](https://github.com/tailwindlabs/tailwindcss/pull/11548))
- Allow variant to be an at-rule without a prelude ([#11589](https://github.com/tailwindlabs/tailwindcss/pull/11589))
- Improve normalisation of `calc()`-like functions ([#11686](https://github.com/tailwindlabs/tailwindcss/pull/11686))

### Added

Expand Down
63 changes: 52 additions & 11 deletions src/util/dataTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,6 @@ function isCSSFunction(value) {
return cssFunctions.some((fn) => new RegExp(`^${fn}\\(.*\\)`).test(value))
}

const placeholder = '--tw-placeholder'
const placeholderRe = new RegExp(placeholder, 'g')

// This is not a data type, but rather a function that can normalize the
// correct values.
export function normalize(value, isRoot = true) {
Expand Down Expand Up @@ -63,15 +60,59 @@ export function normalize(value, isRoot = true) {
*/
function normalizeMathOperatorSpacing(value) {
return value.replace(/(calc|min|max|clamp)\(.+\)/g, (match) => {
let vars = []
let result = ''

return match
.replace(/var\((--.+?)[,)]/g, (match, g1) => {
vars.push(g1)
return match.replace(g1, placeholder)
})
.replace(/(-?\d*\.?\d(?!\b-\d.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g, '$1 $2 ')
.replace(placeholderRe, () => vars.shift())
function lastChar() {
let char = result.trimEnd()
return char[char.length - 1]
}

for (let i = 0; i < match.length; i++) {
function peek(word) {
return word.split('').every((char, j) => match[i + j] === char)
}

function consumeUntil(chars) {
let minIndex = Infinity
for (let char of chars) {
let index = match.indexOf(char, i)
if (index !== -1 && index < minIndex) {
minIndex = index
}
}

let result = match.slice(i, minIndex)
i += result.length - 1
return result
}

let char = match[i]

// Handle `var(--variable)`
if (peek('var')) {
// When we consume until `)`, then we are dealing with this scenario:
// `var(--example)`
//
// When we consume until `,`, then we are dealing with this scenario:
// `var(--example, 1rem)`
//
// In this case we do want to "format", the default value as well
result += consumeUntil([')', ','])
}

// Handle operators
else if (
['+', '-', '*', '/'].includes(char) &&
!['(', '+', '-', '*', '/'].includes(lastChar())
) {
result += ` ${char} `
} else {
result += char
}
}

// Simplify multiple spaces
return result.replace(/\s+/g, ' ')
})
}

Expand Down
20 changes: 20 additions & 0 deletions tests/normalize-data-types.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,26 @@ let table = [
['var(--my-var-with-more-than-3-words)', 'var(--my-var-with-more-than-3-words)'],
['var(--width, calc(100%+1rem))', 'var(--width, calc(100% + 1rem))'],

['calc(1px*(7--12/24))', 'calc(1px * (7 - -12 / 24))'],
['calc((7-32)/(1400-782))', 'calc((7 - 32) / (1400 - 782))'],
['calc((7-3)/(1400-782))', 'calc((7 - 3) / (1400 - 782))'],
['calc((7-32)/(1400-782))', 'calc((7 - 32) / (1400 - 782))'],
['calc((70-3)/(1400-782))', 'calc((70 - 3) / (1400 - 782))'],
['calc((70-32)/(1400-782))', 'calc((70 - 32) / (1400 - 782))'],
['calc((704-3)/(1400-782))', 'calc((704 - 3) / (1400 - 782))'],
['calc((704-320))', 'calc((704 - 320))'],
['calc((704-320)/1)', 'calc((704 - 320) / 1)'],
['calc((704-320)/(1400-782))', 'calc((704 - 320) / (1400 - 782))'],
['calc(24px+-1rem)', 'calc(24px + -1rem)'],
['calc(24px+(-1rem))', 'calc(24px + (-1rem))'],
['calc(24px_+_-1rem)', 'calc(24px + -1rem)'],
['calc(24px+(-1rem))', 'calc(24px + (-1rem))'],
['calc(24px_+_(-1rem))', 'calc(24px + (-1rem))'],
[
'calc(var(--10-10px,calc(-20px-(-30px--40px)-50px)',
'calc(var(--10-10px,calc(-20px - (-30px - -40px) - 50px)',
],

// Misc
['color(0_0_0/1.0)', 'color(0 0 0/1.0)'],
['color(0_0_0_/_1.0)', 'color(0 0 0 / 1.0)'],
Expand Down

0 comments on commit ae4d213

Please sign in to comment.