diff --git a/src/lib/generateRules.js b/src/lib/generateRules.js index 5092a6a..046be38 100644 --- a/src/lib/generateRules.js +++ b/src/lib/generateRules.js @@ -3,6 +3,7 @@ const parseObjectStyles = require('tailwindcss/lib/util/parseObjectStyles').defa const { isPlainObject, bigSign } = require('./utils') const selectorParser = require('postcss-selector-parser') const prefixSelector = require('tailwindcss/lib/util/prefixSelector').default +const { updateAllClasses } = require('../pluginUtils') let classNameParser = selectorParser((selectors) => { return selectors.first.filter(({ type }) => type === 'class').pop().value @@ -64,6 +65,26 @@ function applyPrefix(matches, context) { return matches } +function applyImportant(matches) { + if (matches.length === 0) { + return matches + } + let result = [] + + for (let [meta, rule] of matches) { + let container = postcss.root({ nodes: [rule] }) + container.walkRules((r) => { + r.selector = updateAllClasses(r.selector, (className) => { + return `!${className}` + }) + r.walkDecls((d) => (d.important = true)) + }) + result.push([meta, container.nodes[0]]) + } + + return result +} + // Takes a list of rule tuples and applies a variant like `hover`, sm`, // whatever to it. We used to do some extra caching here to avoid generating // a variant of the same rule more than once, but this was never hit because @@ -179,6 +200,12 @@ function sortAgainst(toSort, against) { function* resolveMatches(candidate, context) { let separator = context.tailwindConfig.separator let [classCandidate, ...variants] = candidate.split(separator).reverse() + let important = false + + if (classCandidate.startsWith('!')) { + important = true + classCandidate = classCandidate.slice(1) + } // Strip prefix // md:hover:tw-bg-black @@ -220,6 +247,10 @@ function* resolveMatches(candidate, context) { matches = applyPrefix(matches, context) + if (important) { + matches = applyImportant(matches, context) + } + for (let variant of variants) { matches = applyVariant(variant, matches, context) } diff --git a/tests/14-important-modifier.test.css b/tests/14-important-modifier.test.css new file mode 100644 index 0000000..27a8c99 --- /dev/null +++ b/tests/14-important-modifier.test.css @@ -0,0 +1,53 @@ +* { + --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% !important; +} +@media (min-width: 640px) { + .\!container { + max-width: 640px !important; + } +} +@media (min-width: 768px) { + .\!container { + max-width: 768px !important; + } +} +@media (min-width: 1024px) { + .\!container { + max-width: 1024px !important; + } +} +@media (min-width: 1280px) { + .\!container { + max-width: 1280px !important; + } +} +@media (min-width: 1536px) { + .\!container { + max-width: 1536px !important; + } +} +.\!font-bold { + font-weight: 700 !important; +} +.hover\:\!text-center:hover { + text-align: center !important; +} +@media (min-width: 1024px) { + .lg\:\!opacity-50 { + opacity: 0.5 !important; + } +} +@media (min-width: 1280px) { + .xl\:focus\:disabled\:\!float-right:focus:disabled { + float: right !important; + } +} diff --git a/tests/14-important-modifier.test.html b/tests/14-important-modifier.test.html new file mode 100644 index 0000000..02f5401 --- /dev/null +++ b/tests/14-important-modifier.test.html @@ -0,0 +1,5 @@ +
+ + + + diff --git a/tests/14-important-modifier.test.js b/tests/14-important-modifier.test.js new file mode 100644 index 0000000..dd561d3 --- /dev/null +++ b/tests/14-important-modifier.test.js @@ -0,0 +1,32 @@ +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 modifier', () => { + let config = { + important: false, + darkMode: 'class', + purge: [path.resolve(__dirname, './14-important-modifier.test.html')], + corePlugins: { preflight: false }, + theme: {}, + plugins: [], + } + + let css = ` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(css, config).then((result) => { + let expectedPath = path.resolve(__dirname, './14-important-modifier.test.css') + let expected = fs.readFileSync(expectedPath, 'utf8') + + expect(result.css).toMatchCss(expected) + }) +}) diff --git a/tests/15-important-modifier-prefix.test.css b/tests/15-important-modifier-prefix.test.css new file mode 100644 index 0000000..496153e --- /dev/null +++ b/tests/15-important-modifier-prefix.test.css @@ -0,0 +1,53 @@ +* { + --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; +} +.\!tw-container { + width: 100% !important; +} +@media (min-width: 640px) { + .\!tw-container { + max-width: 640px !important; + } +} +@media (min-width: 768px) { + .\!tw-container { + max-width: 768px !important; + } +} +@media (min-width: 1024px) { + .\!tw-container { + max-width: 1024px !important; + } +} +@media (min-width: 1280px) { + .\!tw-container { + max-width: 1280px !important; + } +} +@media (min-width: 1536px) { + .\!tw-container { + max-width: 1536px !important; + } +} +.\!tw-font-bold { + font-weight: 700 !important; +} +.hover\:\!tw-text-center:hover { + text-align: center !important; +} +@media (min-width: 1024px) { + .lg\:\!tw-opacity-50 { + opacity: 0.5 !important; + } +} +@media (min-width: 1280px) { + .xl\:focus\:disabled\:\!tw-float-right:focus:disabled { + float: right !important; + } +} diff --git a/tests/15-important-modifier-prefix.test.html b/tests/15-important-modifier-prefix.test.html new file mode 100644 index 0000000..8124d78 --- /dev/null +++ b/tests/15-important-modifier-prefix.test.html @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/15-important-modifier-prefix.test.js b/tests/15-important-modifier-prefix.test.js new file mode 100644 index 0000000..9845edb --- /dev/null +++ b/tests/15-important-modifier-prefix.test.js @@ -0,0 +1,33 @@ +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 modifier with prefix', () => { + let config = { + important: false, + prefix: 'tw-', + darkMode: 'class', + purge: [path.resolve(__dirname, './15-important-modifier-prefix.test.html')], + corePlugins: { preflight: false }, + theme: {}, + plugins: [], + } + + let css = ` + @tailwind base; + @tailwind components; + @tailwind utilities; + ` + + return run(css, config).then((result) => { + let expectedPath = path.resolve(__dirname, './15-important-modifier-prefix.test.css') + let expected = fs.readFileSync(expectedPath, 'utf8') + + expect(result.css).toMatchCss(expected) + }) +})