From 8c95074b7d7f6247a38740169970185f8c70e74c Mon Sep 17 00:00:00 2001 From: Adam Wathan Date: Sun, 28 Mar 2021 09:08:21 -0400 Subject: [PATCH] Add support for selectors in option --- src/lib/generateRules.js | 36 ++++++++----- tests/important-selector.test.css | 85 ++++++++++++++++++++++++++++++ tests/important-selector.test.html | 12 +++++ tests/important-selector.test.js | 70 ++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 14 deletions(-) create mode 100644 tests/important-selector.test.css create mode 100644 tests/important-selector.test.html create mode 100644 tests/important-selector.test.js diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js index 046be38..fe48a0d 100644 --- a/src/lib/generateRules.js +++ b/src/lib/generateRules.js @@ -261,18 +261,8 @@ function* resolveMatches(candidate, context) { } } -function inKeyframes(d) { - return ( - d.parent.parent && d.parent.parent.type === 'atrule' && d.parent.parent.name === 'keyframes' - ) -} - -function makeImportant(rule) { - rule.walkDecls((d) => { - if (d.parent.type === 'rule' && !inKeyframes(d)) { - d.important = true - } - }) +function inKeyframes(rule) { + return rule.parent && rule.parent.type === 'atrule' && rule.parent.name === 'keyframes' } function generateRules(candidates, context) { @@ -300,8 +290,26 @@ function generateRules(candidates, context) { } return allRules.flat(1).map(([{ sort, layer, options }, rule]) => { - if (context.tailwindConfig.important === true && options.respectImportant) { - makeImportant(rule) + if (options.respectImportant) { + if (context.tailwindConfig.important === true) { + rule.walkDecls((d) => { + if (d.parent.type === 'rule' && !inKeyframes(d.parent)) { + d.important = true + } + }) + } else if (typeof context.tailwindConfig.important === 'string') { + let container = postcss.root({ nodes: [rule] }) + container.walkRules((r) => { + if (inKeyframes(r)) { + return + } + + r.selectors = r.selectors.map((selector) => { + return `${context.tailwindConfig.important} ${selector}` + }) + }) + rule = container.nodes[0] + } } return [sort | context.layerOrder[layer], rule] }) diff --git a/tests/important-selector.test.css b/tests/important-selector.test.css new file mode 100644 index 0000000..a631401 --- /dev/null +++ b/tests/important-selector.test.css @@ -0,0 +1,85 @@ +* { + --tw-shadow: 0 0 #0000; + --tw-ring-inset: var(--tw-empty, /*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgba(59, 130, 246, 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; +} +.container { + width: 100%; +} +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} +#app .btn { + button: yes; +} +@font-face { + font-family: Inter; +} +@page { + margin: 1cm; +} +.custom-component { + font-weight: 700; +} +.custom-important-component { + text-align: center !important; +} +@keyframes spin { + to { + transform: rotate(360deg); + } +} +#app .animate-spin { + animation: spin 1s linear infinite; +} +#app .font-bold { + font-weight: 700; +} +.custom-util { + button: no; +} +#app .group:hover .group-hover\:focus-within\:text-left:focus-within { + text-align: left; +} +#app [dir='rtl'] .rtl\:active\:text-center:active { + text-align: center; +} +@media (prefers-reduced-motion: no-preference) { + #app .motion-safe\:hover\:text-center:hover { + text-align: center; + } +} +#app .dark .dark\:focus\:text-left:focus { + text-align: left; +} +@media (min-width: 768px) { + #app .md\:hover\:text-right:hover { + text-align: right; + } +} diff --git a/tests/important-selector.test.html b/tests/important-selector.test.html new file mode 100644 index 0000000..5668043 --- /dev/null +++ b/tests/important-selector.test.html @@ -0,0 +1,12 @@ +
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/important-selector.test.js b/tests/important-selector.test.js new file mode 100644 index 0000000..c4349bf --- /dev/null +++ b/tests/important-selector.test.js @@ -0,0 +1,70 @@ +const postcss = require('postcss') +const tailwind = require('../src/index.js') +const fs = require('fs') +const path = require('path') + +function run(input, config = {}) { + return postcss([tailwind(config)]).process(input, { from: path.resolve(__filename) }) +} + +test('important selector', () => { + let config = { + important: '#app', + darkMode: 'class', + purge: [path.resolve(__dirname, './important-selector.test.html')], + corePlugins: { preflight: false }, + theme: {}, + plugins: [ + function ({ addComponents, addUtilities }) { + addComponents( + { + '.btn': { + button: 'yes', + }, + }, + { respectImportant: true } + ) + addComponents( + { + '@font-face': { + 'font-family': 'Inter', + }, + '@page': { + margin: '1cm', + }, + }, + { respectImportant: true } + ) + addUtilities( + { + '.custom-util': { + button: 'no', + }, + }, + { respectImportant: false } + ) + }, + ], + } + + let css = ` + @tailwind base; + @tailwind components; + @layer components { + .custom-component { + @apply font-bold; + } + .custom-important-component { + @apply text-center !important; + } + } + @tailwind utilities; + ` + + return run(css, config).then((result) => { + let expectedPath = path.resolve(__dirname, './important-selector.test.css') + let expected = fs.readFileSync(expectedPath, 'utf8') + + expect(result.css).toMatchCss(expected) + }) +})