diff --git a/packages/purgecss/__tests__/css-variables.test.ts b/packages/purgecss/__tests__/css-variables.test.ts index 7c5b44ef..57b5776b 100644 --- a/packages/purgecss/__tests__/css-variables.test.ts +++ b/packages/purgecss/__tests__/css-variables.test.ts @@ -1,5 +1,4 @@ import PurgeCSS from "./../src/index"; - import { ROOT_TEST_EXAMPLES } from "./utils"; describe("purge unused css variables", () => { @@ -25,4 +24,8 @@ describe("purge unused css variables", () => { expect(purgedCSS.includes("--unused-color")).toBe(false); expect(purgedCSS.includes("--button-color")).toBe(false); }); + it("keeps '--color-first:', '--wrong-order'", () => { + expect(purgedCSS.includes("--color-first:")).toBe(true); + expect(purgedCSS.includes("--wrong-order:")).toBe(true); + }); }); diff --git a/packages/purgecss/__tests__/test_examples/css-variables/variables.css b/packages/purgecss/__tests__/test_examples/css-variables/variables.css index c8df7826..696ae7d0 100644 --- a/packages/purgecss/__tests__/test_examples/css-variables/variables.css +++ b/packages/purgecss/__tests__/test_examples/css-variables/variables.css @@ -1,10 +1,13 @@ :root { + --color-first: var(--wrong-order); --primary-color: blue; --secondary-color: indigo; --tertiary-color: aqua; --unused-color: violet; --used-color: rebeccapurple; --accent-color: orange; + --wrong-order: yellow; + --random: var(--not-existing); } .button { @@ -19,4 +22,5 @@ .button:focus { background-color: var(--accent-color); color: var(--primary-color); + border-color: var(--color-first); } \ No newline at end of file diff --git a/packages/purgecss/src/VariablesStructure.ts b/packages/purgecss/src/VariablesStructure.ts index b26a5a6e..e20fb6af 100644 --- a/packages/purgecss/src/VariablesStructure.ts +++ b/packages/purgecss/src/VariablesStructure.ts @@ -1,4 +1,5 @@ import * as postcss from "postcss"; +import { matchAll } from "./index"; import { StringRegExpArray } from "./types"; class VariableNode { @@ -62,9 +63,26 @@ class VariablesStructure { } removeUnused(): void { + // check unordered usage + for (const used of this.usedVariables) { + const usedNode = this.nodes.get(used); + if (usedNode) { + const usedVariablesMatchesInDeclaration = matchAll( + usedNode.value.value, + /var\((.+?)[,)]/g + ); + usedVariablesMatchesInDeclaration.forEach((usage) => { + if (!this.usedVariables.has(usage[1])) { + this.usedVariables.add(usage[1]); + } + }); + } + } + for (const used of this.usedVariables) { this.setAsUsed(used); } + for (const [name, declaration] of this.nodes) { if (!declaration.isUsed && !this.isVariablesSafelisted(name)) { declaration.value.remove(); diff --git a/packages/purgecss/src/index.ts b/packages/purgecss/src/index.ts index 78c84e36..4c1965fb 100644 --- a/packages/purgecss/src/index.ts +++ b/packages/purgecss/src/index.ts @@ -243,7 +243,7 @@ function isInPseudoClass(selector: selectorParser.Node): boolean { ); } -function matchAll(str: string, regexp: RegExp): RegExpMatchArray[] { +export function matchAll(str: string, regexp: RegExp): RegExpMatchArray[] { const matches: RegExpMatchArray[] = []; str.replace(regexp, function () { // eslint-disable-next-line prefer-rest-params