diff --git a/CHANGELOG.md b/CHANGELOG.md index c3dccdede409..40ae653bfb5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fix CLI `--content` option ([#5775](https://github.com/tailwindlabs/tailwindcss/pull/5775)) - Fix before/after utilities overriding custom content values at larger breakpoints ([#5820](https://github.com/tailwindlabs/tailwindcss/pull/5820)) - Cleanup duplicate properties ([#5830](https://github.com/tailwindlabs/tailwindcss/pull/5830)) +- Allow `_` inside `url()` when using arbitrary values ([#5853](https://github.com/tailwindlabs/tailwindcss/pull/5853)) ## [3.0.0-alpha.1] - 2021-10-01 diff --git a/src/lib/expandTailwindAtRules.js b/src/lib/expandTailwindAtRules.js index d25a39f38cd2..14a1c8af44e3 100644 --- a/src/lib/expandTailwindAtRules.js +++ b/src/lib/expandTailwindAtRules.js @@ -11,6 +11,8 @@ const PATTERNS = [ /([^<>"'`\s]*\[\w*"[^"`\s]*"?\])/.source, // font-["some_font",sans-serif] /([^<>"'`\s]*\[\w*\('[^"'`\s]*'\)\])/.source, // bg-[url('...')] /([^<>"'`\s]*\[\w*\("[^"'`\s]*"\)\])/.source, // bg-[url("...")] + /([^<>"'`\s]*\[\w*\('[^"`\s]*'\)\])/.source, // bg-[url('...'),url('...')] + /([^<>"'`\s]*\[\w*\("[^'`\s]*"\)\])/.source, // bg-[url("..."),url("...")] /([^<>"'`\s]*\['[^"'`\s]*'\])/.source, // `content-['hello']` but not `content-['hello']']` /([^<>"'`\s]*\["[^"'`\s]*"\])/.source, // `content-["hello"]` but not `content-["hello"]"]` /([^<>"'`\s]*\[[^"'`\s]+\][^<>"'`\s]*)/.source, // `fill-[#bada55]`, `fill-[#bada55]/50` diff --git a/src/util/dataTypes.js b/src/util/dataTypes.js index e1b616a14867..b22d57274470 100644 --- a/src/util/dataTypes.js +++ b/src/util/dataTypes.js @@ -7,7 +7,22 @@ let UNDERSCORE = /_(?![^(]*\))/g // Underscore separator that is not located bet // This is not a data type, but rather a function that can normalize the // correct values. -export function normalize(value) { +export function normalize(value, isRoot = true) { + // Keep raw strings if it starts with `url(` + if (value.includes('url(')) { + return value + .split(/(url\(.*?\))/g) + .filter(Boolean) + .map((part) => { + if (/^url\(.*?\)$/.test(part)) { + return part + } + + return normalize(part, false) + }) + .join('') + } + // Convert `_` to ` `, except for escaped underscores `\_` value = value .replace( @@ -18,10 +33,9 @@ export function normalize(value) { .replace(/\\_/g, '_') // Remove leftover whitespace - value = value.trim() - - // Keep raw strings if it starts with `url(` - if (value.startsWith('url(')) return value + if (isRoot) { + value = value.trim() + } // Add spaces around operators inside calc() that do not follow an operator // or '('. diff --git a/src/util/pluginUtils.js b/src/util/pluginUtils.js index 6d8db4a21e14..467a651d08e4 100644 --- a/src/util/pluginUtils.js +++ b/src/util/pluginUtils.js @@ -160,9 +160,18 @@ function splitAtFirst(input, delim) { export function coerceValue(types, modifier, options, tailwindConfig) { if (isArbitraryValue(modifier)) { - let [explicitType, value] = splitAtFirst(modifier.slice(1, -1), ':') + let arbitraryValue = modifier.slice(1, -1) + let [explicitType, value] = splitAtFirst(arbitraryValue, ':') + + // It could be that this resolves to `url(https` which is not a valid + // identifier. We currently only support "simple" words with dashes or + // underscores. E.g.: family-name + if (!/^[\w-_]+$/g.test(explicitType)) { + value = arbitraryValue + } - if (explicitType !== undefined && !supportedTypes.includes(explicitType)) { + // + else if (explicitType !== undefined && !supportedTypes.includes(explicitType)) { return [] } diff --git a/tests/arbitrary-values.test.html b/tests/arbitrary-values.test.html index d27f2c70d273..910de55262d4 100644 --- a/tests/arbitrary-values.test.html +++ b/tests/arbitrary-values.test.html @@ -126,6 +126,7 @@
+
diff --git a/tests/arbitrary-values.test.js b/tests/arbitrary-values.test.js index 66d4b9a8fc2e..d86179415215 100644 --- a/tests/arbitrary-values.test.js +++ b/tests/arbitrary-values.test.js @@ -220,3 +220,35 @@ it('should warn and not generate if arbitrary values are ambiguous', () => { return expect(result.css).toMatchFormattedCss(css``) }) }) + +it('should support colons in URLs', () => { + let config = { + content: [ + { raw: html`
` }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(css` + .bg-\[url\(\'https\:\/\/www\.spacejam\.com\/1996\/img\/bg_stars\.gif\'\)\] { + background-image: url('https://www.spacejam.com/1996/img/bg_stars.gif'); + } + `) + }) +}) + +it('should support unescaped underscores in URLs', () => { + let config = { + content: [ + { raw: html`
` }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchFormattedCss(` + .bg-\\[url\\(\\'brown_potato\\.jpg\\'\\)\\2c _url\\(\\'red_tomato\\.png\\'\\)\\] { + background-image: url('brown_potato.jpg'), url('red_tomato.png'); + } + `) + }) +})