mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
* improve split logic by delimiter
The original RegEx did mostly what we want, the idea is that we wanted
to split by a `,` but one that was not within `()`. This is useful when
you define multiple background colors for example:
```html
<div class="bg-[rgb(0,0,0),rgb(255,255,255)]"></div>
```
In this case splitting by the regex would result in the proper result:
```js
let result = [
'rgb(0,0,0)',
'rgb(255,255,255)'
]
```
Visually, you can think of it like:
```
┌─[./example.html]
│
∙ 1 │ <div class="bg-[rgb(0,0,0),rgb(255,255,255)]"></div>
· ──┬── ┬ ─────┬─────
· │ │ ╰─────── Guarded by parens
· │ ╰───────────────── We will split here
· ╰───────────────────── Guarded by parens
│
└─
```
We properly split by `,` not inside a `()`. However, this RegEx fails
the moment you have deeply nested RegEx values.
Visually, this is what's happening:
```
┌─[./example.html]
│
∙ 1 │ <div class="bg-[rgba(0,0,0,var(--alpha))]"></div>
· ┬ ┬ ┬
· ╰─┴─┴── We accidentally split here
│
└─
```
This is because on the right of the `,`, the first paren is an opening
paren `(` instead of a closing one `)`.
I'm not 100% sure how we can improve the RegEx to handle that case as
well, instead I wrote a small `splitBy` function that allows you to
split the string by a character (just like you could do before) but
ignores the ones inside the given exceptions. This keeps track of a
stack to know whether we are within parens or not.
Visually, the fix looks like this:
```
┌─[./example.html]
│
∙ 1 │ <div class="bg-[rgba(0,0,0,var(--alpha)),rgb(255,255,255,var(--alpha))]"></div>
· ┬ ┬ ┬ ┬ ┬ ┬ ┬
· │ │ │ │ ╰───┴───┴── Guarded by parens
· │ │ │ ╰────────────────── We will split here
· ╰─┴─┴──────────────────────────────── Guarded by parens
│
└─
```
* use already existing `splitAtTopLevelOnly` function
* add faster implemetation for `splitAtTopLevelOnly`
However, the faster version can't handle separators with multiple
characters right now. So instead of using buggy code or only using the
"slower" code, we've added a fast path where we use the faster code
wherever we can.
* use `splitAtTopLevelOnly` directly
* make split go brrrrrrr
* update changelog
* remove unncessary array.from call
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
73 lines
1.8 KiB
JavaScript
73 lines
1.8 KiB
JavaScript
import { splitAtTopLevelOnly } from './splitAtTopLevelOnly'
|
|
|
|
let KEYWORDS = new Set(['inset', 'inherit', 'initial', 'revert', 'unset'])
|
|
let SPACE = /\ +(?![^(]*\))/g // Similar to the one above, but with spaces instead.
|
|
let LENGTH = /^-?(\d+|\.\d+)(.*?)$/g
|
|
|
|
export function parseBoxShadowValue(input) {
|
|
let shadows = splitAtTopLevelOnly(input, ',')
|
|
return shadows.map((shadow) => {
|
|
let value = shadow.trim()
|
|
let result = { raw: value }
|
|
let parts = value.split(SPACE)
|
|
let seen = new Set()
|
|
|
|
for (let part of parts) {
|
|
// Reset index, since the regex is stateful.
|
|
LENGTH.lastIndex = 0
|
|
|
|
// Keyword
|
|
if (!seen.has('KEYWORD') && KEYWORDS.has(part)) {
|
|
result.keyword = part
|
|
seen.add('KEYWORD')
|
|
}
|
|
|
|
// Length value
|
|
else if (LENGTH.test(part)) {
|
|
if (!seen.has('X')) {
|
|
result.x = part
|
|
seen.add('X')
|
|
} else if (!seen.has('Y')) {
|
|
result.y = part
|
|
seen.add('Y')
|
|
} else if (!seen.has('BLUR')) {
|
|
result.blur = part
|
|
seen.add('BLUR')
|
|
} else if (!seen.has('SPREAD')) {
|
|
result.spread = part
|
|
seen.add('SPREAD')
|
|
}
|
|
}
|
|
|
|
// Color or unknown
|
|
else {
|
|
if (!result.color) {
|
|
result.color = part
|
|
} else {
|
|
if (!result.unknown) result.unknown = []
|
|
result.unknown.push(part)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if valid
|
|
result.valid = result.x !== undefined && result.y !== undefined
|
|
|
|
return result
|
|
})
|
|
}
|
|
|
|
export function formatBoxShadowValue(shadows) {
|
|
return shadows
|
|
.map((shadow) => {
|
|
if (!shadow.valid) {
|
|
return shadow.raw
|
|
}
|
|
|
|
return [shadow.keyword, shadow.x, shadow.y, shadow.blur, shadow.spread, shadow.color]
|
|
.filter(Boolean)
|
|
.join(' ')
|
|
})
|
|
.join(', ')
|
|
}
|