Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions docs/CLI.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,25 @@ npm i -g purgecss
To see the available options for the CLI: `purgecss --help`

```text
Usage: purgecss --css <css> --content <content> [options]
Usage: purgecss --css <css...> --content <content...> [options]

Remove unused css selectors

Options:
-con, --content <files> glob of content files
-css, --css <files> glob of css files
-c, --config <path> path to the configuration file
-o, --output <path> file path directory to write purged css files to
-font, --font-face option to remove unused font-faces
-keyframes, --keyframes option to remove unused keyframes
-rejected, --rejected option to output rejected selectors
-s, --safelist <list> list of classes that should not be removed
-h, --help display help for command
-V, --version output the version number
-con, --content <files...> glob of content files
-css, --css <files...> glob of css files
-c, --config <path> path to the configuration file
-o, --output <path> file path directory to write purged css files to
-font, --font-face option to remove unused font-faces
-keyframes, --keyframes option to remove unused keyframes
-v, --variables option to remove unused variables
-rejected, --rejected option to output rejected selectors
-rejected-css, --rejected-css option to output rejected css
-s, --safelist <list...> list of classes that should not be removed
-b, --blocklist <list...> list of selectors that should be removed
-k, --skippedContentGlobs <list...> list of glob patterns for folders/files that should not be scanned
-h, --help display help for command
```

The options available through the CLI are similar to the ones available with a configuration file. You can also use the CLI with a configuration file.
Expand Down
1 change: 1 addition & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,6 @@ interface ResultPurge {
css: string;
file?: string;
rejected?: string[];
rejectedCss?: string;
}
```
12 changes: 12 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ interface UserDefinedOptions {
keyframes?: boolean;
output?: string;
rejected?: boolean;
rejectedCss?: boolean;
stdin?: boolean;
stdout?: boolean;
variables?: boolean;
Expand Down Expand Up @@ -236,6 +237,17 @@ await new PurgeCSS().purge({
rejected: true
})
```
- **rejectedCss \(default: false\)**

If you would like to keep the discarded CSS you can do so by using the `rejectedCss` option.

```js
await new PurgeCSS().purge({
content: ['index.html', '**/*.js', '**/*.html', '**/*.vue'],
css: ['css/app.css'],
rejectedCss: true
})
```

- **safelist**

Expand Down
41 changes: 41 additions & 0 deletions packages/purgecss/__tests__/rejectedCss.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import PurgeCSS from "./../src/index";
import { ROOT_TEST_EXAMPLES } from "./utils";

describe("rejectedCss", () => {
it("returns the rejected css as part of the result", async () => {
expect.assertions(1);
const resultsPurge = await new PurgeCSS().purge({
content: [`${ROOT_TEST_EXAMPLES}rejectedCss/simple.js`],
css: [`${ROOT_TEST_EXAMPLES}rejectedCss/simple.css`],
rejectedCss: true,
});
const expected = `
.rejected {
color: blue;
}`;
expect(resultsPurge[0].rejectedCss?.trim()).toBe(expected.trim());
});
it("contains the rejected selectors as part of the rejected css", async () => {
expect.assertions(1);
const resultsPurge = await new PurgeCSS().purge({
content: [`${ROOT_TEST_EXAMPLES}rejectedCss/simple.js`],
css: [`${ROOT_TEST_EXAMPLES}rejectedCss/simple.css`],
rejected: true,
rejectedCss: true,
});
expect(resultsPurge[0].rejectedCss?.trim()).toContain(resultsPurge[0].rejected?.[0]);
});
/**
* https://github.com/FullHuman/purgecss/pull/763#discussion_r754618902
*/
it("preserves the node correctly when having an empty parent node", async () => {
expect.assertions(1);
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);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@media (max-width: 66666px) {
.unused-class, .unused-class2 {
color: black;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.critical {
color: red;
}

.rejected {
color: blue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@

"critical"
4 changes: 4 additions & 0 deletions packages/purgecss/src/bin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type CommandOptions = {
keyframes?: boolean;
variables?: boolean;
rejected?: boolean;
rejectedCss?: boolean;
safelist?: string[];
blocklist?: string[];
skippedContentGlobs: string[];
Expand All @@ -48,6 +49,7 @@ function parseCommandOptions() {
.option("-keyframes, --keyframes", "option to remove unused keyframes")
.option("-v, --variables", "option to remove unused variables")
.option("-rejected, --rejected", "option to output rejected selectors")
.option("-rejected-css, --rejected-css", "option to output rejected css")
.option(
"-s, --safelist <list...>",
"list of classes that should not be removed"
Expand Down Expand Up @@ -77,6 +79,7 @@ async function run() {
keyframes,
variables,
rejected,
rejectedCss,
safelist,
blocklist,
skippedContentGlobs,
Expand All @@ -99,6 +102,7 @@ async function run() {
if (fontFace) options.fontFace = fontFace;
if (keyframes) options.keyframes = keyframes;
if (rejected) options.rejected = rejected;
if (rejectedCss) options.rejectedCss = rejectedCss;
if (variables) options.variables = variables;
if (safelist) options.safelist = standardizeSafelist(safelist);
if (blocklist) options.blocklist = blocklist;
Expand Down
23 changes: 21 additions & 2 deletions packages/purgecss/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ class PurgeCSS {
private usedAnimations: Set<string> = new Set();
private usedFontFaces: Set<string> = new Set();
public selectorsRemoved: Set<string> = new Set();
public removedNodes: postcss.Node[] = [];
private variablesStructure: VariablesStructure = new VariablesStructure();

public options: Options = defaultOptions;
Expand Down Expand Up @@ -456,6 +457,7 @@ class PurgeCSS {
}

let keepSelector = true;
const originalSelector = node.selector;
node.selector = selectorParser((selectorsParsed) => {
selectorsParsed.walk((selector) => {
if (selector.type !== "selector") {
Expand All @@ -465,8 +467,9 @@ class PurgeCSS {
keepSelector = this.shouldKeepSelector(selector, selectors);

if (!keepSelector) {
if (this.options.rejected)
if (this.options.rejected) {
this.selectorsRemoved.add(selector.toString());
}
selector.remove();
}
});
Expand All @@ -482,7 +485,19 @@ class PurgeCSS {

// remove empty rules
const parent = node.parent;
if (!node.selector) node.remove();
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();
}

Expand Down Expand Up @@ -538,6 +553,10 @@ class PurgeCSS {
this.selectorsRemoved.clear();
}

if (this.options.rejectedCss) {
result.rejectedCss = postcss.root({ nodes: this.removedNodes }).toString();
}

sources.push(result);
}
return sources;
Expand Down
1 change: 1 addition & 0 deletions packages/purgecss/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const defaultOptions: Options = {
fontFace: false,
keyframes: false,
rejected: false,
rejectedCss: false,
stdin: false,
stdout: false,
variables: false,
Expand Down
3 changes: 3 additions & 0 deletions packages/purgecss/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export interface UserDefinedOptions {
keyframes?: boolean;
output?: string;
rejected?: boolean;
rejectedCss?: boolean;
stdin?: boolean;
stdout?: boolean;
variables?: boolean;
Expand All @@ -81,6 +82,7 @@ export interface Options {
keyframes: boolean;
output?: string;
rejected: boolean;
rejectedCss: boolean;
stdin: boolean;
stdout: boolean;
variables: boolean;
Expand All @@ -92,6 +94,7 @@ export interface Options {

export interface ResultPurge {
css: string;
rejectedCss?: string;
file?: string;
rejected?: string[];
}