• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FullHuman / purgecss / 11981163739

22 Nov 2024 10:31PM CUT coverage: 91.081%. Remained the same
11981163739

push

github

Ffloriel
docs: add breaking changes for v7 about named export for postcss plugin

351 of 420 branches covered (83.57%)

Branch coverage included in aggregate %.

517 of 533 relevant lines covered (97.0%)

5427.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

75.76
/packages/purgecss/src/bin.ts
1
import { Command } from "commander";
4✔
2
import * as fs from "fs";
4✔
3
import packageJson from "./../package.json";
4✔
4
import {
4✔
5
  defaultOptions,
6
  PurgeCSS,
7
  setOptions,
8
  standardizeSafelist,
9
} from "./index";
10
import { Options } from "./types";
11

12
async function writeCSSToFile(filePath: string, css: string) {
13
  try {
3✔
14
    await fs.promises.writeFile(filePath, css);
3✔
15
  } catch (err: unknown) {
16
    if (err instanceof Error) {
×
17
      console.error(err.message);
×
18
    }
19
  }
20
}
21

22
async function read(stream: NodeJS.ReadStream) {
23
  const chunks = [];
×
24
  for await (const chunk of stream) chunks.push(chunk);
×
25
  return Buffer.concat(chunks).toString("utf8");
×
26
}
27

28
type CommandOptions = {
29
  config?: string;
30
  css?: string[];
31
  content?: string[];
32
  output?: string;
33
  fontFace?: boolean;
34
  keyframes?: boolean;
35
  variables?: boolean;
36
  rejected?: boolean;
37
  rejectedCss?: boolean;
38
  safelist?: string[];
39
  blocklist?: string[];
40
  skippedContentGlobs: string[];
41
};
42

43
export function parseCommandOptions(program: Command): Command {
4✔
44
  program
4✔
45
    .description(packageJson.description)
46
    .version(packageJson.version)
47
    .usage("--css <css...> --content <content...> [options]");
48

49
  program
4✔
50
    .option("-con, --content <files...>", "glob of content files")
51
    .option("-css, --css <files...>", "glob of css files")
52
    .option("-c, --config <path>", "path to the configuration file")
53
    .option(
54
      "-o, --output <path>",
55
      "file path directory to write purged css files to",
56
    )
57
    .option("-font, --font-face", "option to remove unused font-faces")
58
    .option("-keyframes, --keyframes", "option to remove unused keyframes")
59
    .option("-v, --variables", "option to remove unused variables")
60
    .option("-rejected, --rejected", "option to output rejected selectors")
61
    .option("-rejected-css, --rejected-css", "option to output rejected css")
62
    .option(
63
      "-s, --safelist <list...>",
64
      "list of classes that should not be removed",
65
    )
66
    .option(
67
      "-b, --blocklist <list...>",
68
      "list of selectors that should be removed",
69
    )
70
    .option(
71
      "-k, --skippedContentGlobs <list...>",
72
      "list of glob patterns for folders/files that should not be scanned",
73
    );
74

75
  return program;
4✔
76
}
77

78
export async function getOptions(program: Command): Promise<Options> {
4✔
79
  const {
80
    config,
81
    css,
82
    content,
83
    output,
84
    fontFace,
85
    keyframes,
86
    variables,
87
    rejected,
88
    rejectedCss,
89
    safelist,
90
    blocklist,
91
    skippedContentGlobs,
92
  } = program.opts<CommandOptions>();
4✔
93
  // config file is not specified or the content and css are not,
94
  // PurgeCSS will not run
95
  if (!config && !(content && css)) {
4!
96
    program.help();
×
97
  }
98

99
  // if the config file is present, use it
100
  // other options specified will override
101
  let options = defaultOptions;
4✔
102
  if (config) {
4!
103
    options = await setOptions(config);
×
104
  }
105
  if (content) {
4✔
106
    if (content.length === 1 && content[0] === "-") {
4!
107
      options.content = [
×
108
        {
109
          raw: await read(process.stdin),
110
          extension: "",
111
        },
112
      ];
113
    } else {
114
      options.content = content;
4✔
115
    }
116
  }
117
  if (css) {
4✔
118
    if (css.length === 1 && css[0] === "-") {
4!
119
      options.css = [
×
120
        {
121
          raw: await read(process.stdin),
122
        },
123
      ];
124
    } else {
125
      options.css = css;
4✔
126
    }
127
  }
128
  if (fontFace) options.fontFace = fontFace;
4✔
129
  if (keyframes) options.keyframes = keyframes;
4✔
130
  if (rejected) options.rejected = rejected;
4✔
131
  if (rejectedCss) options.rejectedCss = rejectedCss;
4✔
132
  if (variables) options.variables = variables;
4✔
133
  if (safelist) options.safelist = standardizeSafelist(safelist);
4✔
134
  if (blocklist) options.blocklist = blocklist;
4✔
135
  if (skippedContentGlobs) options.skippedContentGlobs = skippedContentGlobs;
4✔
136
  if (output) options.output = output;
4✔
137
  return options;
4✔
138
}
139

140
export async function run(program: Command) {
4✔
141
  const options = await getOptions(program);
3✔
142
  const purged = await new PurgeCSS().purge(options);
3✔
143

144
  // output results in specified directory
145
  if (options.output) {
3✔
146
    if (purged.length === 1 && options.output.endsWith(".css")) {
2✔
147
      await writeCSSToFile(options.output, purged[0].css);
1✔
148
      return;
1✔
149
    }
150

151
    for (const purgedResult of purged) {
1✔
152
      const fileName = purgedResult?.file?.split("/").pop();
2!
153
      await writeCSSToFile(`${options.output}/${fileName}`, purgedResult.css);
2✔
154
    }
155
  } else {
156
    console.log(JSON.stringify(purged));
1✔
157
  }
158
}
159

160
export async function main() {
4✔
161
  try {
×
162
    const program = parseCommandOptions(new Command());
×
163
    program.parse(process.argv);
×
164
    run(program);
×
165
  } catch (error: unknown) {
166
    if (error instanceof Error) {
×
167
      console.error(error.message);
×
168
    }
169
    process.exit(1);
×
170
  }
171
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc