diff --git a/src/jit/lib/setupContextUtils.js b/src/jit/lib/setupContextUtils.js index 8a41d0b3c..b4730e4c1 100644 --- a/src/jit/lib/setupContextUtils.js +++ b/src/jit/lib/setupContextUtils.js @@ -125,6 +125,64 @@ 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) { + let stack = [] + let inQuotes = false + + for (let i = 0; i < value.length; 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 + } + + // 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 +} + function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offsets }) { function getConfigValue(path, defaultValue) { return path ? dlv(tailwindConfig, path, defaultValue) : tailwindConfig @@ -273,6 +331,10 @@ function buildPluginApi(tailwindConfig, context, { variantList, variantMap, offs return [] } + if (!isValidArbitraryValue(value)) { + return [] + } + let includedRules = [] let ruleSets = [] .concat( diff --git a/tests/jit/arbitrary-values.test.css b/tests/jit/arbitrary-values.test.css index 522f3c945..2b911158b 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 e0ead092d..c7cff0497 100644 --- a/tests/jit/arbitrary-values.test.html +++ b/tests/jit/arbitrary-values.test.html @@ -116,5 +116,33 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +