Skip to content

Commit 8a2f9ed

Browse files
akbngthecrypticace
andauthored
Fix !important selectors not being classified as valid class inside safelist config (#9791)
* fix !imp selector not safelisted as valid class * add tests for !imp selectors in safelist config * add test to check for invalid variants * Only check important utilities for patterns that include a `!` * Update changelog Co-authored-by: Jordan Pittman <[email protected]>
1 parent 6bd9912 commit 8a2f9ed

File tree

3 files changed

+234
-0
lines changed

3 files changed

+234
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- Fixed use of `raw` content in the CLI ([#9773](https://github.com/tailwindlabs/tailwindcss/pull/9773))
1313
- Pick up changes from files that are both context and content deps ([#9787](https://github.com/tailwindlabs/tailwindcss/pull/9787))
1414
- Sort pseudo-elements ONLY after classes when using variants and `@apply` ([#9765](https://github.com/tailwindlabs/tailwindcss/pull/9765))
15+
- Support important utilities in the safelist (pattern must include a `!`) ([#9791](https://github.com/tailwindlabs/tailwindcss/pull/9791))
1516

1617
## [3.2.2] - 2022-11-04
1718

src/lib/setupContextUtils.js

+5
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,7 @@ function registerPlugins(plugins, context) {
825825
if (checks.length > 0) {
826826
let patternMatchingCount = new Map()
827827
let prefixLength = context.tailwindConfig.prefix.length
828+
let checkImportantUtils = checks.some((check) => check.pattern.source.includes('!'))
828829

829830
for (let util of classList) {
830831
let utils = Array.isArray(util)
@@ -861,6 +862,10 @@ function registerPlugins(plugins, context) {
861862
]
862863
}
863864

865+
if (checkImportantUtils && options?.respectImportant) {
866+
classes = [...classes, ...classes.map((cls) => '!' + cls)]
867+
}
868+
864869
return classes
865870
})()
866871
: [util]

tests/safelist.test.js

+228
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,35 @@ it('should not safelist when an sparse/holey list is provided', () => {
195195
})
196196
})
197197

198+
it('should not safelist any invalid variants if provided', () => {
199+
let config = {
200+
content: [{ raw: html`<div class="uppercase"></div>` }],
201+
safelist: [
202+
{
203+
pattern: /^bg-(red)-(100|200)$/,
204+
variants: ['foo', 'bar'],
205+
},
206+
],
207+
}
208+
209+
return run('@tailwind utilities', config).then((result) => {
210+
return expect(result.css).toMatchCss(css`
211+
.bg-red-100 {
212+
--tw-bg-opacity: 1;
213+
background-color: rgb(254 226 226 / var(--tw-bg-opacity));
214+
}
215+
.bg-red-200 {
216+
--tw-bg-opacity: 1;
217+
background-color: rgb(254 202 202 / var(--tw-bg-opacity));
218+
}
219+
220+
.uppercase {
221+
text-transform: uppercase;
222+
}
223+
`)
224+
})
225+
})
226+
198227
it('should safelist negatives based on a pattern regex', () => {
199228
let config = {
200229
content: [{ raw: html`<div class="uppercase"></div>` }],
@@ -304,3 +333,202 @@ it('should safelist negatives based on a pattern regex', () => {
304333
`)
305334
})
306335
})
336+
337+
it('should safelist pattern regex with !important selector', () => {
338+
let config = {
339+
content: [{ raw: html`<div class="uppercase"></div>` }],
340+
safelist: [{ pattern: /^!grid-cols-(4|5|6)$/ }],
341+
}
342+
343+
return run('@tailwind utilities', config).then((result) => {
344+
return expect(result.css).toMatchCss(css`
345+
.\!grid-cols-4 {
346+
grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
347+
}
348+
349+
.\!grid-cols-5 {
350+
grid-template-columns: repeat(5, minmax(0, 1fr)) !important;
351+
}
352+
353+
.\!grid-cols-6 {
354+
grid-template-columns: repeat(6, minmax(0, 1fr)) !important;
355+
}
356+
357+
.uppercase {
358+
text-transform: uppercase;
359+
}
360+
`)
361+
})
362+
})
363+
364+
it('should safelist pattern regex with custom prefix along with !important selector', () => {
365+
let config = {
366+
prefix: 'tw-',
367+
content: [{ raw: html`<div class="tw-uppercase"></div>` }],
368+
safelist: [{ pattern: /^!tw-grid-cols-(4|5|6)$/ }],
369+
}
370+
371+
return run('@tailwind utilities', config).then((result) => {
372+
return expect(result.css).toMatchCss(css`
373+
.\!tw-grid-cols-4 {
374+
grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
375+
}
376+
377+
.\!tw-grid-cols-5 {
378+
grid-template-columns: repeat(5, minmax(0, 1fr)) !important;
379+
}
380+
381+
.\!tw-grid-cols-6 {
382+
grid-template-columns: repeat(6, minmax(0, 1fr)) !important;
383+
}
384+
385+
.tw-uppercase {
386+
text-transform: uppercase;
387+
}
388+
`)
389+
})
390+
})
391+
392+
it('should safelist pattern regex having !important selector with variants', () => {
393+
let config = {
394+
content: [{ raw: html`<div class="uppercase"></div>` }],
395+
safelist: [
396+
{
397+
pattern: /^!bg-gray-(500|600|700|800)$/,
398+
variants: ['hover'],
399+
},
400+
],
401+
}
402+
403+
return run('@tailwind utilities', config).then((result) => {
404+
return expect(result.css).toMatchCss(css`
405+
.\!bg-gray-500 {
406+
--tw-bg-opacity: 1 !important;
407+
background-color: rgb(107 114 128 / var(--tw-bg-opacity)) !important;
408+
}
409+
410+
.\!bg-gray-600 {
411+
--tw-bg-opacity: 1 !important;
412+
background-color: rgb(75 85 99 / var(--tw-bg-opacity)) !important;
413+
}
414+
415+
.\!bg-gray-700 {
416+
--tw-bg-opacity: 1 !important;
417+
background-color: rgb(55 65 81 / var(--tw-bg-opacity)) !important;
418+
}
419+
420+
.\!bg-gray-800 {
421+
--tw-bg-opacity: 1 !important;
422+
background-color: rgb(31 41 55 / var(--tw-bg-opacity)) !important;
423+
}
424+
425+
.uppercase {
426+
text-transform: uppercase;
427+
}
428+
429+
.hover\:\!bg-gray-500:hover {
430+
--tw-bg-opacity: 1 !important;
431+
background-color: rgb(107 114 128 / var(--tw-bg-opacity)) !important;
432+
}
433+
434+
.hover\:\!bg-gray-600:hover {
435+
--tw-bg-opacity: 1 !important;
436+
background-color: rgb(75 85 99 / var(--tw-bg-opacity)) !important;
437+
}
438+
439+
.hover\:\!bg-gray-700:hover {
440+
--tw-bg-opacity: 1 !important;
441+
background-color: rgb(55 65 81 / var(--tw-bg-opacity)) !important;
442+
}
443+
444+
.hover\:\!bg-gray-800:hover {
445+
--tw-bg-opacity: 1 !important;
446+
background-color: rgb(31 41 55 / var(--tw-bg-opacity)) !important;
447+
}
448+
`)
449+
})
450+
})
451+
452+
it('should safelist multiple patterns with !important selector', () => {
453+
let config = {
454+
content: [{ raw: html`<div class="uppercase"></div>` }],
455+
safelist: [
456+
{
457+
pattern: /^!text-gray-(700|800|900)$/,
458+
variants: ['hover'],
459+
},
460+
{
461+
pattern: /^!bg-gray-(200|300|400)$/,
462+
variants: ['hover'],
463+
},
464+
],
465+
}
466+
467+
return run('@tailwind utilities', config).then((result) => {
468+
return expect(result.css).toMatchCss(css`
469+
.\!bg-gray-200 {
470+
--tw-bg-opacity: 1 !important;
471+
background-color: rgb(229 231 235 / var(--tw-bg-opacity)) !important;
472+
}
473+
474+
.\!bg-gray-300 {
475+
--tw-bg-opacity: 1 !important;
476+
background-color: rgb(209 213 219 / var(--tw-bg-opacity)) !important;
477+
}
478+
479+
.\!bg-gray-400 {
480+
--tw-bg-opacity: 1 !important;
481+
background-color: rgb(156 163 175 / var(--tw-bg-opacity)) !important;
482+
}
483+
484+
.uppercase {
485+
text-transform: uppercase;
486+
}
487+
488+
.\!text-gray-700 {
489+
--tw-text-opacity: 1 !important;
490+
color: rgb(55 65 81 / var(--tw-text-opacity)) !important;
491+
}
492+
493+
.\!text-gray-800 {
494+
--tw-text-opacity: 1 !important;
495+
color: rgb(31 41 55 / var(--tw-text-opacity)) !important;
496+
}
497+
498+
.\!text-gray-900 {
499+
--tw-text-opacity: 1 !important;
500+
color: rgb(17 24 39 / var(--tw-text-opacity)) !important;
501+
}
502+
503+
.hover\:\!bg-gray-200:hover {
504+
--tw-bg-opacity: 1 !important;
505+
background-color: rgb(229 231 235 / var(--tw-bg-opacity)) !important;
506+
}
507+
508+
.hover\:\!bg-gray-300:hover {
509+
--tw-bg-opacity: 1 !important;
510+
background-color: rgb(209 213 219 / var(--tw-bg-opacity)) !important;
511+
}
512+
513+
.hover\:\!bg-gray-400:hover {
514+
--tw-bg-opacity: 1 !important;
515+
background-color: rgb(156 163 175 / var(--tw-bg-opacity)) !important;
516+
}
517+
518+
.hover\:\!text-gray-700:hover {
519+
--tw-text-opacity: 1 !important;
520+
color: rgb(55 65 81 / var(--tw-text-opacity)) !important;
521+
}
522+
523+
.hover\:\!text-gray-800:hover {
524+
--tw-text-opacity: 1 !important;
525+
color: rgb(31 41 55 / var(--tw-text-opacity)) !important;
526+
}
527+
528+
.hover\:\!text-gray-900:hover {
529+
--tw-text-opacity: 1 !important;
530+
color: rgb(17 24 39 / var(--tw-text-opacity)) !important;
531+
}
532+
`)
533+
})
534+
})

0 commit comments

Comments
 (0)