From 9b0fdc3219b1b24856e302e0798e4d22ab006a5a Mon Sep 17 00:00:00 2001 From: Ffloriel Date: Sun, 7 Mar 2021 21:19:53 +0000 Subject: [PATCH] feat: add dynamic attributes option #588 add option to add custom CSS attribute selectors --- .../purgecss/__tests__/attributes.test.ts | 6 +- .../attributes/attribute_selector.css | 6 +- packages/purgecss/src/index.ts | 59 ++++++++++--------- packages/purgecss/src/options.ts | 3 +- packages/purgecss/src/types/index.ts | 2 + 5 files changed, 44 insertions(+), 32 deletions(-) diff --git a/packages/purgecss/__tests__/attributes.test.ts b/packages/purgecss/__tests__/attributes.test.ts index 421605a7..bf58bcb2 100644 --- a/packages/purgecss/__tests__/attributes.test.ts +++ b/packages/purgecss/__tests__/attributes.test.ts @@ -1,5 +1,4 @@ import PurgeCSS from "./../src/index"; - import { ROOT_TEST_EXAMPLES } from "./utils"; describe("attributes", () => { @@ -9,6 +8,7 @@ describe("attributes", () => { const resultsPurge = await new PurgeCSS().purge({ content: [`${ROOT_TEST_EXAMPLES}attributes/attribute_selector.html`], css: [`${ROOT_TEST_EXAMPLES}attributes/attribute_selector.css`], + dynamicAttributes: ["aria-selected"], }); purgedCSS = resultsPurge[0].css; }); @@ -72,4 +72,8 @@ describe("attributes", () => { expect(purgedCSS.includes('[class*=" class2"]')).toBe(true); expect(purgedCSS.includes('[class*="class1 class2 "]')).toBe(true); }); + + it("keeps dynamic attributes", () => { + expect(purgedCSS.includes("[aria-selected]")).toBe(true); + }); }); diff --git a/packages/purgecss/__tests__/test_examples/attributes/attribute_selector.css b/packages/purgecss/__tests__/test_examples/attributes/attribute_selector.css index 5a3f5362..b19ee60f 100644 --- a/packages/purgecss/__tests__/test_examples/attributes/attribute_selector.css +++ b/packages/purgecss/__tests__/test_examples/attributes/attribute_selector.css @@ -80,4 +80,8 @@ a[title*="fat"] { } [class*="class1 class2 "] { color: blue; -} \ No newline at end of file +} + +[aria-selected] { + font-weight: 700; +} diff --git a/packages/purgecss/src/index.ts b/packages/purgecss/src/index.ts index 2f084851..3339d5ac 100644 --- a/packages/purgecss/src/index.ts +++ b/packages/purgecss/src/index.ts @@ -1,40 +1,38 @@ -import * as postcss from "postcss"; -import selectorParser from "postcss-selector-parser"; import * as fs from "fs"; -import { promisify } from "util"; - import glob from "glob"; import path from "path"; - +import * as postcss from "postcss"; +import selectorParser from "postcss-selector-parser"; +import { promisify } from "util"; +import { + CONFIG_FILENAME, + ERROR_CONFIG_FILE_LOADING, + IGNORE_ANNOTATION_CURRENT, + IGNORE_ANNOTATION_END, + IGNORE_ANNOTATION_NEXT, + IGNORE_ANNOTATION_START, +} from "./constants"; +import ExtractorResultSets from "./ExtractorResultSets"; +import { CSS_SAFELIST } from "./internal-safelist"; import { defaultOptions } from "./options"; -export { defaultOptions } from "./options"; - import { - Options, - ResultPurge, - RawContent, - Extractors, + AtRules, + ComplexSafelist, ExtractorFunction, + ExtractorResultDetailed, + Extractors, IgnoreType, - AtRules, + Options, + RawContent, RawCSS, + ResultPurge, UserDefinedOptions, - ExtractorResultDetailed, UserDefinedSafelist, - ComplexSafelist, } from "./types"; - -import { - CONFIG_FILENAME, - ERROR_CONFIG_FILE_LOADING, - IGNORE_ANNOTATION_NEXT, - IGNORE_ANNOTATION_START, - IGNORE_ANNOTATION_END, - IGNORE_ANNOTATION_CURRENT, -} from "./constants"; -import { CSS_SAFELIST } from "./internal-safelist"; import VariablesStructure from "./VariablesStructure"; -import ExtractorResultSets from "./ExtractorResultSets"; + +export { defaultOptions } from "./options"; +export { PurgeCSS }; const asyncFs = { access: promisify(fs.access), @@ -751,9 +749,13 @@ class PurgeCSS { // `value` is a dynamic attribute, highly used in input element // the choice is to always leave `value` as it can change based on the user // idem for `checked`, `selected`, `open` - isPresent = ["value", "checked", "selected", "open"].includes( - selectorNode.attribute - ) + isPresent = [ + ...this.options.dynamicAttributes, + "value", + "checked", + "selected", + "open", + ].includes(selectorNode.attribute) ? true : isAttributeFound(selectorNode, selectorsFromExtractor); break; @@ -811,5 +813,4 @@ class PurgeCSS { } } -export { PurgeCSS }; export default PurgeCSS; diff --git a/packages/purgecss/src/options.ts b/packages/purgecss/src/options.ts index 1d416bd8..4eaff1df 100644 --- a/packages/purgecss/src/options.ts +++ b/packages/purgecss/src/options.ts @@ -1,4 +1,4 @@ -import { Options, ExtractorResult } from "./types/"; +import { ExtractorResult, Options } from "./types/"; export const defaultOptions: Options = { css: [], @@ -20,4 +20,5 @@ export const defaultOptions: Options = { keyframes: [], }, blocklist: [], + dynamicAttributes: [], }; diff --git a/packages/purgecss/src/types/index.ts b/packages/purgecss/src/types/index.ts index cd1e97c3..df30a8a8 100644 --- a/packages/purgecss/src/types/index.ts +++ b/packages/purgecss/src/types/index.ts @@ -66,6 +66,7 @@ export interface UserDefinedOptions { variables?: boolean; safelist?: UserDefinedSafelist; blocklist?: StringRegExpArray; + dynamicAttributes?: string[]; } export interface Options { @@ -82,6 +83,7 @@ export interface Options { variables: boolean; safelist: Required; blocklist: StringRegExpArray; + dynamicAttributes: string[]; } export interface ResultPurge {