diff --git a/__tests__/purgeUnusedStyles.test.js b/__tests__/purgeUnusedStyles.test.js index a3d940b317ba..b9086faf28fa 100644 --- a/__tests__/purgeUnusedStyles.test.js +++ b/__tests__/purgeUnusedStyles.test.js @@ -18,6 +18,16 @@ function suppressConsoleLogs(cb, type = 'warn') { } } +function extractRules(root) { + let rules = [] + + root.walkRules(r => { + rules = rules.concat(r.selectors) + }) + + return rules +} + const config = { ...defaultConfig, theme: { @@ -302,9 +312,8 @@ test('can purge all CSS, not just Tailwind classes', () => { expect(result.css).toContain('html') expect(result.css).toContain('body') expect(result.css).toContain('samp') - expect(result.css).not.toContain('button') - expect(result.css).not.toContain('legend') - expect(result.css).not.toContain('progress') + expect(result.css).not.toContain('.example') + expect(result.css).not.toContain('.sm\\:example') assertPurged(result) }) @@ -355,3 +364,102 @@ test('the `conservative` mode can be set explicitly', () => { expect(result.css).toContain('.text-center') }) }) + +test('element selectors are preserved by default', () => { + const OLD_NODE_ENV = process.env.NODE_ENV + process.env.NODE_ENV = 'production' + const inputPath = path.resolve(`${__dirname}/fixtures/tailwind-input.css`) + const input = fs.readFileSync(inputPath, 'utf8') + + return postcss([ + tailwind({ + ...config, + purge: { + content: [path.resolve(`${__dirname}/fixtures/**/*.html`)], + mode: 'all', + }, + }), + ]) + .process(input, { from: inputPath }) + .then(result => { + process.env.NODE_ENV = OLD_NODE_ENV + const rules = extractRules(result.root) + ;[ + 'a', + 'blockquote', + 'body', + 'code', + 'fieldset', + 'figure', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'hr', + 'html', + 'img', + 'kbd', + 'ol', + 'p', + 'pre', + 'strong', + 'sup', + 'table', + 'ul', + ].forEach(e => expect(rules).toContain(e)) + + assertPurged(result) + }) +}) + +test('preserving element selectors can be disabled', () => { + const OLD_NODE_ENV = process.env.NODE_ENV + process.env.NODE_ENV = 'production' + const inputPath = path.resolve(`${__dirname}/fixtures/tailwind-input.css`) + const input = fs.readFileSync(inputPath, 'utf8') + + return postcss([ + tailwind({ + ...config, + purge: { + content: [path.resolve(`${__dirname}/fixtures/**/*.html`)], + mode: 'all', + preserveHtmlElements: false, + }, + }), + ]) + .process(input, { from: inputPath }) + .then(result => { + process.env.NODE_ENV = OLD_NODE_ENV + + const rules = extractRules(result.root) + + ;[ + 'blockquote', + 'code', + 'em', + 'fieldset', + 'figure', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'hr', + 'img', + 'kbd', + 'li', + 'ol', + 'pre', + 'strong', + 'sup', + 'table', + 'ul', + ].forEach(e => expect(rules).not.toContain(e)) + + assertPurged(result) + }) +}) diff --git a/package.json b/package.json index 7e765255e106..91d72c784d90 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "color": "^3.1.2", "detective": "^5.2.0", "fs-extra": "^8.0.0", + "html-tags": "^3.1.0", "lodash": "^4.17.20", "node-emoji": "^1.8.1", "normalize.css": "^8.0.1", diff --git a/src/lib/purgeUnusedStyles.js b/src/lib/purgeUnusedStyles.js index 8058ce1df9ac..7ba4a2a89d3d 100644 --- a/src/lib/purgeUnusedStyles.js +++ b/src/lib/purgeUnusedStyles.js @@ -2,6 +2,7 @@ import _ from 'lodash' import postcss from 'postcss' import purgecss from '@fullhuman/postcss-purgecss' import log from '../util/log' +import htmlTags from 'html-tags' function removeTailwindMarkers(css) { css.walkAtRules('tailwind', rule => rule.remove()) @@ -75,7 +76,11 @@ export default function purgeUnusedUtilities(config, configChanged) { // Capture classes within other delimiters like .block(class="w-1/2") in Pug const innerMatches = content.match(/[^<>"'`\s.(){}[\]#=%]*[^<>"'`\s.(){}[\]#=%:]/g) || [] - return broadMatches.concat(innerMatches) + if (_.get(config, 'purge.preserveHtmlElements', true)) { + return [...htmlTags].concat(broadMatches).concat(innerMatches) + } else { + return broadMatches.concat(innerMatches) + } }, ...config.purge.options, }), diff --git a/yarn.lock b/yarn.lock index 3a0c15300211..3a6b46240878 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2761,6 +2761,11 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.0.tgz#71e87f931de3fe09e56661ab9a29aadec707b491" integrity sha512-a4u9BeERWGu/S8JiWEAQcdrg9v4QArtP9keViQjGMdff20fBdd8waotXaNmODqBe6uZ3Nafi7K/ho4gCQHV3Ig== +html-tags@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" + integrity sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg== + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1"