Make purge still preserve HTML elements for user-defined extractors (#2704)

* Add failing test for purge preserving element selectors when `defaultExtractor` is overridden

* `preserveHtmlElements` works with user-defined purge extractors
This commit is contained in:
Navith 2020-10-31 10:02:40 -04:00 committed by GitHub
parent c606d2be23
commit d4bd2d0b05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 74 additions and 12 deletions

View File

@ -2,6 +2,7 @@ import fs from 'fs'
import path from 'path'
import postcss from 'postcss'
import tailwind from '../src/index'
import { tailwindExtractor } from '../src/lib/purgeUnusedStyles'
import defaultConfig from '../stubs/defaultConfig.stub.js'
function suppressConsoleLogs(cb, type = 'warn') {
@ -658,6 +659,60 @@ test('element selectors are preserved by default', () => {
)
})
test('element selectors are preserved even when defaultExtractor is overridden', () => {
return inProduction(
suppressConsoleLogs(() => {
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: true,
options: {
defaultExtractor: tailwindExtractor,
},
},
}),
])
.process(input, { from: inputPath })
.then((result) => {
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', () => {
return inProduction(
suppressConsoleLogs(() => {

View File

@ -22,6 +22,17 @@ function removeTailwindMarkers(css) {
})
}
export function tailwindExtractor(content) {
// Capture as liberally as possible, including things like `h-(screen-1.5)`
const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || []
const broadMatchesWithoutTrailingSlash = broadMatches.map((match) => _.trimEnd(match, '\\'))
// Capture classes within other delimiters like .block(class="w-1/2") in Pug
const innerMatches = content.match(/[^<>"'`\s.(){}[\]#=%]*[^<>"'`\s.(){}[\]#=%:]/g) || []
return broadMatches.concat(broadMatchesWithoutTrailingSlash).concat(innerMatches)
}
export default function purgeUnusedUtilities(config, configChanged) {
const purgeEnabled = _.get(
config,
@ -46,6 +57,8 @@ export default function purgeUnusedUtilities(config, configChanged) {
return removeTailwindMarkers
}
const { defaultExtractor, ...purgeOptions } = config.purge.options || {}
return postcss([
function (css) {
const mode = _.get(config, 'purge.mode', 'layers')
@ -93,22 +106,16 @@ export default function purgeUnusedUtilities(config, configChanged) {
purgecss({
content: Array.isArray(config.purge) ? config.purge : config.purge.content,
defaultExtractor: (content) => {
// Capture as liberally as possible, including things like `h-(screen-1.5)`
const broadMatches = content.match(/[^<>"'`\s]*[^<>"'`\s:]/g) || []
const broadMatchesWithoutTrailingSlash = broadMatches.map((match) => _.trimEnd(match, '\\'))
// Capture classes within other delimiters like .block(class="w-1/2") in Pug
const innerMatches = content.match(/[^<>"'`\s.(){}[\]#=%]*[^<>"'`\s.(){}[\]#=%:]/g) || []
const matches = broadMatches.concat(broadMatchesWithoutTrailingSlash).concat(innerMatches)
const extractor = defaultExtractor || tailwindExtractor
const preserved = [...extractor(content)]
if (_.get(config, 'purge.preserveHtmlElements', true)) {
return [...htmlTags].concat(matches)
} else {
return matches
preserved.push(...htmlTags)
}
return preserved
},
...config.purge.options,
...purgeOptions,
}),
])
}