diff --git a/packages/purgecss/__tests__/safelist.test.ts b/packages/purgecss/__tests__/safelist.test.ts index 71a4a8d4..6b669cac 100644 --- a/packages/purgecss/__tests__/safelist.test.ts +++ b/packages/purgecss/__tests__/safelist.test.ts @@ -176,3 +176,27 @@ describe("safelist option: variables", () => { notFindInCSS(expect, ["--tertiary-color:"], purgedCSS); }); }); + +describe("blocklist option", () => { + let purgedCSS: string; + beforeAll(async () => { + const resultsPurge = await new PurgeCSS().purge({ + content: [`${ROOT_TEST_EXAMPLES}safelist/blocklist.html`], + css: [`${ROOT_TEST_EXAMPLES}safelist/blocklist.css`], + blocklist: ["h1", "yep", "button", /nav-/], + }); + purgedCSS = resultsPurge[0].css; + }); + + it("excludes blocklisted selectors", () => { + notFindInCSS( + expect, + ["h1", "yep", "button", "nav-blue", "nav-red"], + purgedCSS + ); + }); + + it("includes non-blocklisted selectors", () => { + findInCSS(expect, ["data-v-test", ".random"], purgedCSS); + }); +}); diff --git a/packages/purgecss/__tests__/test_examples/safelist/blocklist.css b/packages/purgecss/__tests__/test_examples/safelist/blocklist.css new file mode 100644 index 00000000..f28ae760 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/blocklist.css @@ -0,0 +1,28 @@ +h1 { + color: blue; + } + + .random { + color: green; + } + + #yep { + color: red; + } + + button { + color: rebeccapurple; + } + + .nav-blue { + background-color: blue; + } + + .nav-red { + background-color: red; + } + + [data-v-test] { + color: green; + } + \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/safelist/blocklist.html b/packages/purgecss/__tests__/test_examples/safelist/blocklist.html new file mode 100644 index 00000000..985eb619 --- /dev/null +++ b/packages/purgecss/__tests__/test_examples/safelist/blocklist.html @@ -0,0 +1,11 @@ + + + + + +

Title

+
random
+ + + + \ No newline at end of file diff --git a/packages/purgecss/src/index.ts b/packages/purgecss/src/index.ts index 7b76fe02..f922f69c 100644 --- a/packages/purgecss/src/index.ts +++ b/packages/purgecss/src/index.ts @@ -554,6 +554,18 @@ class PurgeCSS { }); } + /** + * Check if the selector is blocklisted with the option blocklist + * @param selector css selector + */ + private isSelectorBlocklisted(selector: string): boolean { + return this.options.blocklist.some((blocklistItem) => { + return typeof blocklistItem === "string" + ? blocklistItem === selector + : blocklistItem.test(selector); + }); + } + /** * Check if the selector is safelisted with the option safelist standard * @param selector css selector @@ -718,6 +730,12 @@ class PurgeCSS { continue; } + // The selector is present in the blocklist + if (selectorValue && this.isSelectorBlocklisted(selectorValue)) { + isPresent = false; + continue; + } + switch (selectorNode.type) { case "attribute": // `value` is a dynamic attribute, highly used in input element diff --git a/packages/purgecss/src/options.ts b/packages/purgecss/src/options.ts index e96afa20..1d416bd8 100644 --- a/packages/purgecss/src/options.ts +++ b/packages/purgecss/src/options.ts @@ -19,4 +19,5 @@ export const defaultOptions: Options = { variables: [], keyframes: [], }, + blocklist: [], }; diff --git a/packages/purgecss/src/types/index.ts b/packages/purgecss/src/types/index.ts index 42adc1cb..5b52ae1e 100644 --- a/packages/purgecss/src/types/index.ts +++ b/packages/purgecss/src/types/index.ts @@ -65,6 +65,7 @@ export interface UserDefinedOptions { stdout?: boolean; variables?: boolean; safelist?: UserDefinedSafelist; + blocklist?: StringRegExpArray; } export interface Options { @@ -80,6 +81,7 @@ export interface Options { stdout: boolean; variables: boolean; safelist: Required; + blocklist: StringRegExpArray; } export interface ResultPurge {