From bd7db038cd38458b2f6aa172358a5b3c1736e3ca Mon Sep 17 00:00:00 2001 From: Petr Novak Date: Sat, 26 Feb 2022 08:35:31 +0100 Subject: [PATCH 1/2] Add additional safelist regex variations --- src/lib/setupContextUtils.js | 52 ++++++-- tests/safelist.test.js | 228 +++++++++++++++++++++++++++++++++++ 2 files changed, 270 insertions(+), 10 deletions(-) diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index e35edcc14145..83a4c4e0f632 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -638,15 +638,6 @@ function registerPlugins(plugins, context) { continue } - if (value instanceof RegExp) { - log.warn('root-regex', [ - 'Regular expressions in `safelist` work differently in Tailwind CSS v3.0.', - 'Update your `safelist` configuration to eliminate this warning.', - 'https://tailwindcss.com/docs/content-configuration#safelisting-classes', - ]) - continue - } - checks.push(value) } @@ -683,7 +674,34 @@ function registerPlugins(plugins, context) { : [util] for (let util of utils) { - for (let { pattern, variants = [] } of checks) { + for (let check of checks) { + let [pattern = /(?:)/, variants = [], opacities = []] = (() => { + if (check instanceof RegExp) { + let pattern = check + return [pattern] + } else if (Array.isArray(check) || Array.isArray(check.pattern)) { + if (Array.isArray(check.pattern)) check = check.pattern + if (check.length == 1) { + let [pattern] = check + return [pattern] + } else if (check.length == 2) { + if (check[1] instanceof RegExp) { + let [variants, pattern] = check + return [pattern, variants] + } else { + let [pattern, opacities] = check + return [pattern, [], opacities] + } + } else if (check.length == 3) { + let [variants, pattern, opacities] = check + return [pattern, variants, opacities] + } else return + } else if (check instanceof Object) { + let { pattern, variants = [] } = check + return [pattern, variants] + } else return + })() + // RegExp with the /g flag are stateful, so let's reset the last // index pointer to reset the state. pattern.lastIndex = 0 @@ -702,6 +720,20 @@ function registerPlugins(plugins, context) { content: variant + context.tailwindConfig.separator + util, extension: 'html', }) + + for (let opacity of opacities) { + context.changedContent.push({ + content: variant + context.tailwindConfig.separator + util + '/' + opacity, + extension: 'html', + }) + } + } + + for (let opacity of opacities) { + context.changedContent.push({ + content: util + '/' + opacity, + extension: 'html', + }) } } } diff --git a/tests/safelist.test.js b/tests/safelist.test.js index d81a6b796ae4..1d611dbfe66e 100644 --- a/tests/safelist.test.js +++ b/tests/safelist.test.js @@ -86,6 +86,234 @@ it('should safelist based on a pattern regex', () => { }) }) +it('should safelist based on a pattern tupple with regex', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + { + pattern: [['hover'], /bg-(red)-(100|200)/], + }, + { + pattern: [/bg-(green)-(100|200)/, ['50']], + }, + { + pattern: [['hover'], /bg-(blue)-(100|200)/, ['50']], + }, + ], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); + } + + .bg-red-200 { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); + } + + .bg-green-100 { + --tw-bg-opacity: 1; + background-color: rgb(220 252 231 / var(--tw-bg-opacity)); + } + + .bg-green-100\/50 { + background-color: rgb(220 252 231 / 0.5); + } + + .bg-green-200 { + --tw-bg-opacity: 1; + background-color: rgb(187 247 208 / var(--tw-bg-opacity)); + } + + .bg-green-200\/50 { + background-color: rgb(187 247 208 / 0.5); + } + + .bg-blue-100 { + --tw-bg-opacity: 1; + background-color: rgb(219 234 254 / var(--tw-bg-opacity)); + } + + .bg-blue-100\/50 { + background-color: rgb(219 234 254 / 0.5); + } + + .bg-blue-200 { + --tw-bg-opacity: 1; + background-color: rgb(191 219 254 / var(--tw-bg-opacity)); + } + + .bg-blue-200\/50 { + background-color: rgb(191 219 254 / 0.5); + } + + .uppercase { + text-transform: uppercase; + } + + .hover\:bg-red-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); + } + + .hover\:bg-red-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); + } + + .hover\:bg-blue-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(219 234 254 / var(--tw-bg-opacity)); + } + + .hover\:bg-blue-100\/50:hover { + background-color: rgb(219 234 254 / 0.5); + } + + .hover\:bg-blue-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(191 219 254 / var(--tw-bg-opacity)); + } + + .hover\:bg-blue-200\/50:hover { + background-color: rgb(191 219 254 / 0.5); + } + `) + }) +}) + +it('should safelist based on a regex', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [/bg-(red)-(100|200)/], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); + } + + .bg-red-200 { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); + } + + .uppercase { + text-transform: uppercase; + } + `) + }) +}) + +it('should safelist based on a tupple with regex', () => { + let config = { + content: [{ raw: html`
` }], + safelist: [ + [/bg-(red)-(100|200)/], + [['hover'], /bg-(blue)-(100|200)/], + [/bg-(green)-(100|200)/, ['50']], + [['hover'], /bg-(yellow)-(100|200)/, ['50']], + ], + } + + return run('@tailwind utilities', config).then((result) => { + return expect(result.css).toMatchCss(css` + .bg-red-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 226 226 / var(--tw-bg-opacity)); + } + + .bg-red-200 { + --tw-bg-opacity: 1; + background-color: rgb(254 202 202 / var(--tw-bg-opacity)); + } + + .bg-yellow-100 { + --tw-bg-opacity: 1; + background-color: rgb(254 249 195 / var(--tw-bg-opacity)); + } + + .bg-yellow-100\/50 { + background-color: rgb(254 249 195 / 0.5); + } + + .bg-yellow-200 { + --tw-bg-opacity: 1; + background-color: rgb(254 240 138 / var(--tw-bg-opacity)); + } + + .bg-yellow-200\/50 { + background-color: rgb(254 240 138 / 0.5); + } + + .bg-green-100 { + --tw-bg-opacity: 1; + background-color: rgb(220 252 231 / var(--tw-bg-opacity)); + } + + .bg-green-100\/50 { + background-color: rgb(220 252 231 / 0.5); + } + + .bg-green-200 { + --tw-bg-opacity: 1; + background-color: rgb(187 247 208 / var(--tw-bg-opacity)); + } + + .bg-green-200\/50 { + background-color: rgb(187 247 208 / 0.5); + } + + .bg-blue-100 { + --tw-bg-opacity: 1; + background-color: rgb(219 234 254 / var(--tw-bg-opacity)); + } + + .bg-blue-200 { + --tw-bg-opacity: 1; + background-color: rgb(191 219 254 / var(--tw-bg-opacity)); + } + + .uppercase { + text-transform: uppercase; + } + + .hover\:bg-yellow-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(254 249 195 / var(--tw-bg-opacity)); + } + + .hover\:bg-yellow-100\/50:hover { + background-color: rgb(254 249 195 / 0.5); + } + + .hover\:bg-yellow-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(254 240 138 / var(--tw-bg-opacity)); + } + + .hover\:bg-yellow-200\/50:hover { + background-color: rgb(254 240 138 / 0.5); + } + + .hover\:bg-blue-100:hover { + --tw-bg-opacity: 1; + background-color: rgb(219 234 254 / var(--tw-bg-opacity)); + } + + .hover\:bg-blue-200:hover { + --tw-bg-opacity: 1; + background-color: rgb(191 219 254 / var(--tw-bg-opacity)); + } + `) + }) +}) + it('should not generate duplicates', () => { let config = { content: [{ raw: html`
` }], From 7af5f5c92b0573a785644305f910fdb530baf593 Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Sat, 26 Feb 2022 10:40:45 -0500 Subject: [PATCH 2/2] Remove support for tuple syntax in pattern object --- src/lib/setupContextUtils.js | 40 ++++++++------ tests/safelist.test.js | 101 +---------------------------------- 2 files changed, 25 insertions(+), 116 deletions(-) diff --git a/src/lib/setupContextUtils.js b/src/lib/setupContextUtils.js index 83a4c4e0f632..77a0f77c90ba 100644 --- a/src/lib/setupContextUtils.js +++ b/src/lib/setupContextUtils.js @@ -677,29 +677,37 @@ function registerPlugins(plugins, context) { for (let check of checks) { let [pattern = /(?:)/, variants = [], opacities = []] = (() => { if (check instanceof RegExp) { - let pattern = check - return [pattern] - } else if (Array.isArray(check) || Array.isArray(check.pattern)) { - if (Array.isArray(check.pattern)) check = check.pattern + return [check] + } + + if (Array.isArray(check)) { if (check.length == 1) { - let [pattern] = check - return [pattern] - } else if (check.length == 2) { + return check + } + + if (check.length == 2) { if (check[1] instanceof RegExp) { let [variants, pattern] = check return [pattern, variants] - } else { - let [pattern, opacities] = check - return [pattern, [], opacities] } - } else if (check.length == 3) { + + let [pattern, opacities] = check + return [pattern, [], opacities] + } + + if (check.length == 3) { let [variants, pattern, opacities] = check return [pattern, variants, opacities] - } else return - } else if (check instanceof Object) { - let { pattern, variants = [] } = check - return [pattern, variants] - } else return + } + + throw new Error( + `Array values in your Tailwind CSS \`safelist\` must contain 1–3 items, found invalid entry containing ${check.length}.` + ) + } + + // Check must be an object in the shape of { pattern: ..., variants: ... } + let { pattern, variants = [] } = check + return [pattern, variants] })() // RegExp with the /g flag are stateful, so let's reset the last diff --git a/tests/safelist.test.js b/tests/safelist.test.js index 1d611dbfe66e..6eb162031f52 100644 --- a/tests/safelist.test.js +++ b/tests/safelist.test.js @@ -86,105 +86,6 @@ it('should safelist based on a pattern regex', () => { }) }) -it('should safelist based on a pattern tupple with regex', () => { - let config = { - content: [{ raw: html`
` }], - safelist: [ - { - pattern: [['hover'], /bg-(red)-(100|200)/], - }, - { - pattern: [/bg-(green)-(100|200)/, ['50']], - }, - { - pattern: [['hover'], /bg-(blue)-(100|200)/, ['50']], - }, - ], - } - - return run('@tailwind utilities', config).then((result) => { - return expect(result.css).toMatchCss(css` - .bg-red-100 { - --tw-bg-opacity: 1; - background-color: rgb(254 226 226 / var(--tw-bg-opacity)); - } - - .bg-red-200 { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); - } - - .bg-green-100 { - --tw-bg-opacity: 1; - background-color: rgb(220 252 231 / var(--tw-bg-opacity)); - } - - .bg-green-100\/50 { - background-color: rgb(220 252 231 / 0.5); - } - - .bg-green-200 { - --tw-bg-opacity: 1; - background-color: rgb(187 247 208 / var(--tw-bg-opacity)); - } - - .bg-green-200\/50 { - background-color: rgb(187 247 208 / 0.5); - } - - .bg-blue-100 { - --tw-bg-opacity: 1; - background-color: rgb(219 234 254 / var(--tw-bg-opacity)); - } - - .bg-blue-100\/50 { - background-color: rgb(219 234 254 / 0.5); - } - - .bg-blue-200 { - --tw-bg-opacity: 1; - background-color: rgb(191 219 254 / var(--tw-bg-opacity)); - } - - .bg-blue-200\/50 { - background-color: rgb(191 219 254 / 0.5); - } - - .uppercase { - text-transform: uppercase; - } - - .hover\:bg-red-100:hover { - --tw-bg-opacity: 1; - background-color: rgb(254 226 226 / var(--tw-bg-opacity)); - } - - .hover\:bg-red-200:hover { - --tw-bg-opacity: 1; - background-color: rgb(254 202 202 / var(--tw-bg-opacity)); - } - - .hover\:bg-blue-100:hover { - --tw-bg-opacity: 1; - background-color: rgb(219 234 254 / var(--tw-bg-opacity)); - } - - .hover\:bg-blue-100\/50:hover { - background-color: rgb(219 234 254 / 0.5); - } - - .hover\:bg-blue-200:hover { - --tw-bg-opacity: 1; - background-color: rgb(191 219 254 / var(--tw-bg-opacity)); - } - - .hover\:bg-blue-200\/50:hover { - background-color: rgb(191 219 254 / 0.5); - } - `) - }) -}) - it('should safelist based on a regex', () => { let config = { content: [{ raw: html`
` }], @@ -210,7 +111,7 @@ it('should safelist based on a regex', () => { }) }) -it('should safelist based on a tupple with regex', () => { +it('should safelist based on a tuple with regex', () => { let config = { content: [{ raw: html`
` }], safelist: [