Skip to content

Commit

Permalink
Allow users to block generation of certain utilities (#9812)
Browse files Browse the repository at this point in the history
* Add blocklist tests

* Build initial implementation of blocklist

* wip

* wip

* wip

* Update changelog
  • Loading branch information
thecrypticace committed Nov 11, 2022
1 parent 4ccc0fa commit 602101d
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

- Allow users to block generation of certain utilities ([#9812](https://github.com/tailwindlabs/tailwindcss/pull/9812))

### Fixed

- Fix watching of files on Linux when renames are involved ([#9796](https://github.com/tailwindlabs/tailwindcss/pull/9796))
Expand Down
3 changes: 2 additions & 1 deletion src/lib/setupContextUtils.js
Expand Up @@ -1165,7 +1165,8 @@ export function createContext(tailwindConfig, changedContent = [], root = postcs
candidateRuleCache: new Map(),
classCache: new Map(),
applyClassCache: new Map(),
notClassCache: new Set(),
// Seed the not class cache with the blocklist (which is only strings)
notClassCache: new Set(tailwindConfig.blocklist ?? []),
postCssNodeCache: new Map(),
candidateRuleMap: new Map(),
tailwindConfig,
Expand Down
18 changes: 18 additions & 0 deletions src/util/normalizeConfig.js
Expand Up @@ -150,6 +150,24 @@ export function normalizeConfig(config) {
return []
})()

// Normalize the `blocklist`
config.blocklist = (() => {
let { blocklist } = config

if (Array.isArray(blocklist)) {
if (blocklist.every((item) => typeof item === 'string')) {
return blocklist
}

log.warn('blocklist-invalid', [
'The `blocklist` option must be an array of strings.',
'https://tailwindcss.com/docs/content-configuration#discarding-classes',
])
}

return []
})()

// Normalize prefix option
if (typeof config.prefix === 'function') {
log.warn('prefix-function', [
Expand Down
116 changes: 116 additions & 0 deletions tests/blocklist.test.js
@@ -0,0 +1,116 @@
import { run, html, css } from './util/run'

let warn

beforeEach(() => {
warn = jest.spyOn(require('../src/util/log').default, 'warn')
})

afterEach(() => warn.mockClear())

it('can block classes matched literally', () => {
let config = {
content: [
{
raw: html`<div
class="font-bold uppercase sm:hover:text-sm hover:text-sm bg-red-500/50 my-custom-class"
></div>`,
},
],
blocklist: ['font', 'uppercase', 'hover:text-sm', 'bg-red-500/50', 'my-custom-class'],
}

let input = css`
@tailwind utilities;
.my-custom-class {
color: red;
}
`

return run(input, config).then((result) => {
return expect(result.css).toMatchCss(css`
.font-bold {
font-weight: 700;
}
.my-custom-class {
color: red;
}
@media (min-width: 640px) {
.sm\:hover\:text-sm:hover {
font-size: 0.875rem;
line-height: 1.25rem;
}
}
`)
})
})

it('can block classes inside @layer', () => {
let config = {
content: [
{
raw: html`<div class="font-bold my-custom-class"></div>`,
},
],
blocklist: ['my-custom-class'],
}

let input = css`
@tailwind utilities;
@layer utilities {
.my-custom-class {
color: red;
}
}
`

return run(input, config).then((result) => {
return expect(result.css).toMatchCss(css`
.font-bold {
font-weight: 700;
}
`)
})
})

it('blocklists do NOT support regexes', async () => {
let config = {
content: [{ raw: html`<div class="font-bold bg-[#f00d1e]"></div>` }],
blocklist: [/^bg-\[[^]+\]$/],
}

let result = await run('@tailwind utilities', config)

expect(result.css).toMatchCss(css`
.bg-\[\#f00d1e\] {
--tw-bg-opacity: 1;
background-color: rgb(240 13 30 / var(--tw-bg-opacity));
}
.font-bold {
font-weight: 700;
}
`)

expect(warn).toHaveBeenCalledTimes(1)
expect(warn.mock.calls.map((x) => x[0])).toEqual(['blocklist-invalid'])
})

it('can block classes generated by the safelist', () => {
let config = {
content: [{ raw: html`<div class="font-bold"></div>` }],
safelist: [{ pattern: /^bg-red-(400|500)$/ }],
blocklist: ['bg-red-500'],
}

return run('@tailwind utilities', config).then((result) => {
return expect(result.css).toMatchCss(css`
.bg-red-400 {
--tw-bg-opacity: 1;
background-color: rgb(248 113 113 / var(--tw-bg-opacity));
}
.font-bold {
font-weight: 700;
}
`)
})
})

0 comments on commit 602101d

Please sign in to comment.