diff --git a/src/jit/lib/setupContextUtils.js b/src/jit/lib/setupContextUtils.js index 4f60067b5478..652f9597bf99 100644 --- a/src/jit/lib/setupContextUtils.js +++ b/src/jit/lib/setupContextUtils.js @@ -125,31 +125,61 @@ function withIdentifiers(styles) { }) } +let matchingBrackets = new Map([ + ['{', '}'], + ['[', ']'], + ['(', ')'], +]) +let inverseMatchingBrackets = new Map( + Array.from(matchingBrackets.entries()).map(([k, v]) => [v, k]) +) + +let quotes = new Set(['"', "'", '`']) + +// Arbitrary values must contain balanced brackets (), [] and {}. Escaped +// values don't count, and brackets inside quotes also don't count. +// +// E.g.: w-[this-is]w-[weird-and-invalid] +// E.g.: w-[this-is\\]w-\\[weird-but-valid] +// E.g.: content-['this-is-also-valid]-weirdly-enough'] function isValidArbitraryValue(value) { - if (value === undefined) { - return true - } + let stack = [] - // When an arbitrary value contans `[]` then it is probably invalid, unless - // it is properly escaped. Or it is content that exists between quotes. - // E.g.: w-[this-is]w-[weird-and-invalid] - // E.g.: w-[this-is\\]w-\\[weird-but-valid] - // E.g.: content-['this-is-also-valid]-weirdly-enough'] let inQuotes = false for (let i = 0; i < value.length; i++) { - if (['"', "'", '`'].includes(value[i])) { + let char = value[i] + + // Non-escaped quotes allow us to "allow" anything in between + if (quotes.has(char) && value[i - 1] !== '\\') { inQuotes = !inQuotes } if (inQuotes) continue + if (value[i - 1] === '\\') continue // Escaped + + if (matchingBrackets.has(char)) { + stack.push(char) + } else if (inverseMatchingBrackets.has(char)) { + let inverse = inverseMatchingBrackets.get(char) + + // Nothing to pop from, therefore it is unbalanced + if (stack.length <= 0) { + return false + } - if (value[i] === '[' || value[i] === ']') { - if (value[i - 1] !== '\\') { + // Popped value must match the inverse value, otherwise it is unbalanced + if (stack.pop() !== inverse) { return false } } } + // If there is still something on the stack, it is also unbalanced + if (stack.length > 0) { + return false + } + + // All good, totally balanced! return true } @@ -297,11 +327,11 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs type = [].concat(type) let [value, coercedType] = coerceValue(type, modifier, options.values, tailwindConfig) - if (!isValidArbitraryValue(value)) { + if (!type.includes(coercedType) || value === undefined) { return [] } - if (!type.includes(coercedType) || value === undefined) { + if (!isValidArbitraryValue(value)) { return [] } diff --git a/tests/jit/arbitrary-values.test.css b/tests/jit/arbitrary-values.test.css index 522f3c94597f..2b911158b1c3 100644 --- a/tests/jit/arbitrary-values.test.css +++ b/tests/jit/arbitrary-values.test.css @@ -93,6 +93,28 @@ .w-\[calc\(100\%\/3-1rem\*2\)\] { width: calc(100% / 3 - 1rem * 2); } +.w-\[\{\}\] { + width: { + } +} +.w-\[\{\{\}\}\] { + width: { + { + } + } +} +.w-\[\(\)\] { + width: (); +} +.w-\[\(\(\)\)\] { + width: (()); +} +.w-\[\'\)\(\)\'\] { + width: ')()'; +} +.w-\[\'\}\{\}\'\] { + width: '}{}'; +} .min-w-\[3\.23rem\] { min-width: 3.23rem; } diff --git a/tests/jit/arbitrary-values.test.html b/tests/jit/arbitrary-values.test.html index fbca24e8a532..c7cff0497b11 100644 --- a/tests/jit/arbitrary-values.test.html +++ b/tests/jit/arbitrary-values.test.html @@ -21,7 +21,6 @@
- @@ -117,5 +116,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +