diff --git a/packages/purgecss/__tests__/rejectedCss.test.ts b/packages/purgecss/__tests__/rejectedCss.test.ts index 8ab5bcad..b5a5cfdb 100644 --- a/packages/purgecss/__tests__/rejectedCss.test.ts +++ b/packages/purgecss/__tests__/rejectedCss.test.ts @@ -9,10 +9,7 @@ describe("rejectedCss", () => { css: [`${ROOT_TEST_EXAMPLES}rejectedCss/simple.css`], rejectedCss: true, }); - const expected = ` -.rejected { - color: blue; -}`; + const expected = `.rejected {\n color: blue;\n}`; expect(resultsPurge[0].rejectedCss?.trim()).toBe(expected.trim()); }); it("contains the rejected selectors as part of the rejected css", async () => { @@ -28,14 +25,16 @@ describe("rejectedCss", () => { /** * https://github.com/FullHuman/purgecss/pull/763#discussion_r754618902 */ - it("preserves the node correctly when having an empty parent node", async () => { - expect.assertions(1); + it("preserves the node correctly", async () => { + expect.assertions(2); const resultsPurge = await new PurgeCSS().purge({ content: [`${ROOT_TEST_EXAMPLES}rejectedCss/empty-parent-node.js`], css: [`${ROOT_TEST_EXAMPLES}rejectedCss/empty-parent-node.css`], rejectedCss: true, }); - const expected = `@media (max-width: 66666px) {\n .unused-class, .unused-class2 {\n color: black;\n }\n}`; - expect(resultsPurge[0].rejectedCss?.trim()).toEqual(expected); + const expectedRejectedCss = `@media (max-width: 66666px) {\n .unused-class {\n color: black;\n }\n}`; + const expectedPurgedCss = `@media (max-width: 66666px) {\n .used-class {\n color: black;\n }\n}`; + expect(resultsPurge[0].rejectedCss?.trim()).toEqual(expectedRejectedCss); + expect(resultsPurge[0].css.trim()).toEqual(expectedPurgedCss); }); }); diff --git a/packages/purgecss/__tests__/test_examples/rejectedCss/empty-parent-node.css b/packages/purgecss/__tests__/test_examples/rejectedCss/empty-parent-node.css index ff2ae0ed..c3f16f70 100644 --- a/packages/purgecss/__tests__/test_examples/rejectedCss/empty-parent-node.css +++ b/packages/purgecss/__tests__/test_examples/rejectedCss/empty-parent-node.css @@ -1,5 +1,5 @@ @media (max-width: 66666px) { - .unused-class, .unused-class2 { + .used-class, .unused-class { color: black; } } diff --git a/packages/purgecss/__tests__/test_examples/rejectedCss/empty-parent-node.js b/packages/purgecss/__tests__/test_examples/rejectedCss/empty-parent-node.js index e69de29b..1e7cd0c2 100644 --- a/packages/purgecss/__tests__/test_examples/rejectedCss/empty-parent-node.js +++ b/packages/purgecss/__tests__/test_examples/rejectedCss/empty-parent-node.js @@ -0,0 +1 @@ +"used-class" \ No newline at end of file diff --git a/packages/purgecss/__tests__/test_examples/rejectedCss/simple.css b/packages/purgecss/__tests__/test_examples/rejectedCss/simple.css index f3ab29dd..d8d68cfc 100644 --- a/packages/purgecss/__tests__/test_examples/rejectedCss/simple.css +++ b/packages/purgecss/__tests__/test_examples/rejectedCss/simple.css @@ -1,7 +1,7 @@ .critical { - color: red; + color: red; } .rejected { - color: blue; + color: blue; } diff --git a/packages/purgecss/src/index.ts b/packages/purgecss/src/index.ts index e28b2755..a70061c7 100644 --- a/packages/purgecss/src/index.ts +++ b/packages/purgecss/src/index.ts @@ -457,7 +457,7 @@ class PurgeCSS { } let keepSelector = true; - const originalSelector = node.selector; + const selectorsRemovedFromRule: string[] = []; node.selector = selectorParser((selectorsParsed) => { selectorsParsed.walk((selector) => { if (selector.type !== "selector") { @@ -470,6 +470,9 @@ class PurgeCSS { if (this.options.rejected) { this.selectorsRemoved.add(selector.toString()); } + if (this.options.rejectedCss) { + selectorsRemovedFromRule.push(selector.toString()); + } selector.remove(); } }); @@ -487,18 +490,19 @@ class PurgeCSS { const parent = node.parent; if (!node.selector) { node.remove(); - if (this.options.rejectedCss) { - node.selector = originalSelector; - if (parent && isRuleEmpty(parent)) { - const clone = parent.clone(); - clone.append(node); - this.removedNodes.push(clone); - } else { - this.removedNodes.push(node); - } - } } if (isRuleEmpty(parent)) parent?.remove(); + + // rebuild the rule with the removed selectors and optionally its parent + if (this.options.rejectedCss) { + if (selectorsRemovedFromRule.length > 0) { + const clone = node.clone(); + const parentClone = parent?.clone().removeAll().append(clone); + clone.selectors = selectorsRemovedFromRule; + const nodeToPreserve = parentClone ? parentClone : clone; + this.removedNodes.push(nodeToPreserve); + } + } } /**