purgecss 7.0.2 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,7 +18,6 @@ This is where PurgeCSS comes into play. PurgeCSS analyzes your content and your
18
18
 
19
19
  ## Sponsors 🥰
20
20
 
21
- [<img src="https://avatars0.githubusercontent.com/u/67109815?v=4" height="85" style="margin-right: 10px">](https://tailwindcss.com)
22
21
  [<img src="https://avatars.githubusercontent.com/u/133211198?v=4" height="85" style="margin-right: 10px">](https://www.bairesdev.com/sponsoring-open-source-projects/)
23
22
 
24
23
  ## Documentation
package/bin/purgecss.js CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  var commander = require('commander');
5
5
  var fs = require('fs');
6
- var glob = require('glob');
6
+ var glob = require('fast-glob');
7
7
  var path = require('path');
8
8
  var postcss = require('postcss');
9
9
  var selectorParser = require('postcss-selector-parser');
@@ -31,79 +31,11 @@ var glob__namespace = /*#__PURE__*/_interopNamespaceDefault(glob);
31
31
  var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
32
32
  var postcss__namespace = /*#__PURE__*/_interopNamespaceDefault(postcss);
33
33
 
34
- var name = "purgecss";
35
- var version = "6.0.0";
34
+ var version = "8.0.0";
36
35
  var description = "Remove unused css selectors";
37
- var author = "Ffloriel";
38
- var homepage = "https://purgecss.com";
39
- var keywords = [
40
- "optimize",
41
- "optimization",
42
- "remove",
43
- "unused",
44
- "css",
45
- "html",
46
- "rules",
47
- "purge",
48
- "uncss",
49
- "purify"
50
- ];
51
- var license = "MIT";
52
- var main$1 = "lib/purgecss.js";
53
- var module$1 = "./lib/purgecss.esm.js";
54
- var types = "./lib/purgecss.d.ts";
55
- var bin = {
56
- purgecss: "bin/purgecss.js"
57
- };
58
- var directories = {
59
- lib: "lib",
60
- test: "__tests__"
61
- };
62
- var files = [
63
- "bin",
64
- "lib"
65
- ];
66
- var repository = {
67
- type: "git",
68
- url: "git+https://github.com/FullHuman/purgecss.git"
69
- };
70
- var scripts = {
71
- build: "ts-node build.ts",
72
- test: "jest"
73
- };
74
- var dependencies = {
75
- commander: "^12.1.0",
76
- glob: "^11.0.0",
77
- postcss: "^8.4.47",
78
- "postcss-selector-parser": "^6.1.2"
79
- };
80
- var bugs = {
81
- url: "https://github.com/FullHuman/purgecss/issues"
82
- };
83
- var publishConfig = {
84
- access: "public",
85
- registry: "https://registry.npmjs.org/"
86
- };
87
36
  var packageJson = {
88
- name: name,
89
37
  version: version,
90
- description: description,
91
- author: author,
92
- homepage: homepage,
93
- keywords: keywords,
94
- license: license,
95
- main: main$1,
96
- module: module$1,
97
- types: types,
98
- bin: bin,
99
- directories: directories,
100
- files: files,
101
- repository: repository,
102
- scripts: scripts,
103
- dependencies: dependencies,
104
- bugs: bugs,
105
- publishConfig: publishConfig
106
- };
38
+ description: description};
107
39
 
108
40
  const IGNORE_ANNOTATION_CURRENT = "purgecss ignore current";
109
41
  const IGNORE_ANNOTATION_NEXT = "purgecss ignore";
@@ -370,7 +302,12 @@ async function setOptions(configFile = CONFIG_FILENAME) {
370
302
  let options;
371
303
  try {
372
304
  const t = path__namespace.resolve(process.cwd(), configFile);
373
- options = await import(t);
305
+ const importedConfig = await import(t);
306
+ // Handle both ES modules (direct export) and CommonJS (default export)
307
+ options =
308
+ importedConfig.default && typeof importedConfig.default === "object"
309
+ ? importedConfig.default
310
+ : importedConfig;
374
311
  }
375
312
  catch (err) {
376
313
  if (err instanceof Error) {
@@ -625,6 +562,13 @@ class PurgeCSS {
625
562
  }
626
563
  return;
627
564
  }
565
+ // Also check CSS custom properties for animation names
566
+ // e.g., --my-animation: fadeIn 0.4s;
567
+ if (prop.startsWith("--")) {
568
+ for (const word of value.split(/[\s,]+/)) {
569
+ this.usedAnimations.add(word);
570
+ }
571
+ }
628
572
  }
629
573
  // collect font faces data
630
574
  if (this.options.fontFace) {
@@ -665,7 +609,7 @@ class PurgeCSS {
665
609
  }
666
610
  catch (err) {
667
611
  filesNames.push(...glob__namespace.sync(globFile, {
668
- nodir: true,
612
+ onlyFiles: true,
669
613
  ignore: this.options.skippedContentGlobs.map((glob) => glob.replace(/^\.\//, "")),
670
614
  }));
671
615
  }
@@ -831,7 +775,7 @@ class PurgeCSS {
831
775
  for (const option of cssOptions) {
832
776
  if (typeof option === "string") {
833
777
  processedOptions.push(...glob__namespace.sync(option, {
834
- nodir: true,
778
+ onlyFiles: true,
835
779
  ignore: this.options.skippedContentGlobs,
836
780
  }));
837
781
  }
@@ -859,9 +803,12 @@ class PurgeCSS {
859
803
  this.removeUnusedCSSVariables();
860
804
  const postCSSResult = root.toResult({
861
805
  map: this.options.sourceMap,
862
- to: typeof this.options.sourceMap === "object"
806
+ to: typeof this.options.sourceMap === "object" &&
807
+ this.options.sourceMap.to
863
808
  ? this.options.sourceMap.to
864
- : undefined,
809
+ : isFromFile
810
+ ? option
811
+ : undefined,
865
812
  });
866
813
  const result = {
867
814
  css: postCSSResult.toString(),
@@ -1154,11 +1101,12 @@ function parseCommandOptions(program) {
1154
1101
  .option("-rejected-css, --rejected-css", "option to output rejected css")
1155
1102
  .option("-s, --safelist <list...>", "list of classes that should not be removed")
1156
1103
  .option("-b, --blocklist <list...>", "list of selectors that should be removed")
1157
- .option("-k, --skippedContentGlobs <list...>", "list of glob patterns for folders/files that should not be scanned");
1104
+ .option("-k, --skippedContentGlobs <list...>", "list of glob patterns for folders/files that should not be scanned")
1105
+ .option("-p, --preserve-paths", "preserve folder hierarchy in the output");
1158
1106
  return program;
1159
1107
  }
1160
1108
  async function getOptions(program) {
1161
- const { config, css, content, output, fontFace, keyframes, variables, rejected, rejectedCss, safelist, blocklist, skippedContentGlobs, } = program.opts();
1109
+ const { config, css, content, output, fontFace, keyframes, variables, rejected, rejectedCss, safelist, blocklist, skippedContentGlobs, preservePaths, } = program.opts();
1162
1110
  // config file is not specified or the content and css are not,
1163
1111
  // PurgeCSS will not run
1164
1112
  if (!config && !(content && css)) {
@@ -1213,12 +1161,13 @@ async function getOptions(program) {
1213
1161
  options.skippedContentGlobs = skippedContentGlobs;
1214
1162
  if (output)
1215
1163
  options.output = output;
1216
- return options;
1164
+ return { ...options, preservePaths };
1217
1165
  }
1218
1166
  async function run(program) {
1219
1167
  var _a;
1220
1168
  const options = await getOptions(program);
1221
- const purged = await new PurgeCSS().purge(options);
1169
+ const { preservePaths, ...purgeOptions } = options;
1170
+ const purged = await new PurgeCSS().purge(purgeOptions);
1222
1171
  // output results in specified directory
1223
1172
  if (options.output) {
1224
1173
  if (purged.length === 1 && options.output.endsWith(".css")) {
@@ -1226,8 +1175,20 @@ async function run(program) {
1226
1175
  return;
1227
1176
  }
1228
1177
  for (const purgedResult of purged) {
1229
- const fileName = (_a = purgedResult === null || purgedResult === void 0 ? void 0 : purgedResult.file) === null || _a === void 0 ? void 0 : _a.split("/").pop();
1230
- await writeCSSToFile(`${options.output}/${fileName}`, purgedResult.css);
1178
+ let outputPath;
1179
+ if (preservePaths && (purgedResult === null || purgedResult === void 0 ? void 0 : purgedResult.file)) {
1180
+ // Preserve the folder hierarchy
1181
+ outputPath = `${options.output}/${purgedResult.file}`;
1182
+ }
1183
+ else {
1184
+ // Default behavior: flatten to just the filename
1185
+ const fileName = (_a = purgedResult === null || purgedResult === void 0 ? void 0 : purgedResult.file) === null || _a === void 0 ? void 0 : _a.split("/").pop();
1186
+ outputPath = `${options.output}/${fileName}`;
1187
+ }
1188
+ // Ensure the directory exists
1189
+ const dir = outputPath.substring(0, outputPath.lastIndexOf("/"));
1190
+ await fs__namespace.promises.mkdir(dir, { recursive: true });
1191
+ await writeCSSToFile(outputPath, purgedResult.css);
1231
1192
  }
1232
1193
  }
1233
1194
  else {
@@ -1,5 +1,5 @@
1
1
  import * as fs from 'fs';
2
- import * as glob from 'glob';
2
+ import * as glob from 'fast-glob';
3
3
  import * as path from 'path';
4
4
  import * as postcss from 'postcss';
5
5
  import selectorParser from 'postcss-selector-parser';
@@ -270,7 +270,12 @@ async function setOptions(configFile = CONFIG_FILENAME) {
270
270
  let options;
271
271
  try {
272
272
  const t = path.resolve(process.cwd(), configFile);
273
- options = await import(t);
273
+ const importedConfig = await import(t);
274
+ // Handle both ES modules (direct export) and CommonJS (default export)
275
+ options =
276
+ importedConfig.default && typeof importedConfig.default === "object"
277
+ ? importedConfig.default
278
+ : importedConfig;
274
279
  }
275
280
  catch (err) {
276
281
  if (err instanceof Error) {
@@ -525,6 +530,13 @@ class PurgeCSS {
525
530
  }
526
531
  return;
527
532
  }
533
+ // Also check CSS custom properties for animation names
534
+ // e.g., --my-animation: fadeIn 0.4s;
535
+ if (prop.startsWith("--")) {
536
+ for (const word of value.split(/[\s,]+/)) {
537
+ this.usedAnimations.add(word);
538
+ }
539
+ }
528
540
  }
529
541
  // collect font faces data
530
542
  if (this.options.fontFace) {
@@ -565,7 +577,7 @@ class PurgeCSS {
565
577
  }
566
578
  catch (err) {
567
579
  filesNames.push(...glob.sync(globFile, {
568
- nodir: true,
580
+ onlyFiles: true,
569
581
  ignore: this.options.skippedContentGlobs.map((glob) => glob.replace(/^\.\//, "")),
570
582
  }));
571
583
  }
@@ -731,7 +743,7 @@ class PurgeCSS {
731
743
  for (const option of cssOptions) {
732
744
  if (typeof option === "string") {
733
745
  processedOptions.push(...glob.sync(option, {
734
- nodir: true,
746
+ onlyFiles: true,
735
747
  ignore: this.options.skippedContentGlobs,
736
748
  }));
737
749
  }
@@ -759,9 +771,12 @@ class PurgeCSS {
759
771
  this.removeUnusedCSSVariables();
760
772
  const postCSSResult = root.toResult({
761
773
  map: this.options.sourceMap,
762
- to: typeof this.options.sourceMap === "object"
774
+ to: typeof this.options.sourceMap === "object" &&
775
+ this.options.sourceMap.to
763
776
  ? this.options.sourceMap.to
764
- : undefined,
777
+ : isFromFile
778
+ ? option
779
+ : undefined,
765
780
  });
766
781
  const result = {
767
782
  css: postCSSResult.toString(),
package/lib/purgecss.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var fs = require('fs');
4
- var glob = require('glob');
4
+ var glob = require('fast-glob');
5
5
  var path = require('path');
6
6
  var postcss = require('postcss');
7
7
  var selectorParser = require('postcss-selector-parser');
@@ -294,7 +294,12 @@ async function setOptions(configFile = CONFIG_FILENAME) {
294
294
  let options;
295
295
  try {
296
296
  const t = path__namespace.resolve(process.cwd(), configFile);
297
- options = await import(t);
297
+ const importedConfig = await import(t);
298
+ // Handle both ES modules (direct export) and CommonJS (default export)
299
+ options =
300
+ importedConfig.default && typeof importedConfig.default === "object"
301
+ ? importedConfig.default
302
+ : importedConfig;
298
303
  }
299
304
  catch (err) {
300
305
  if (err instanceof Error) {
@@ -549,6 +554,13 @@ class PurgeCSS {
549
554
  }
550
555
  return;
551
556
  }
557
+ // Also check CSS custom properties for animation names
558
+ // e.g., --my-animation: fadeIn 0.4s;
559
+ if (prop.startsWith("--")) {
560
+ for (const word of value.split(/[\s,]+/)) {
561
+ this.usedAnimations.add(word);
562
+ }
563
+ }
552
564
  }
553
565
  // collect font faces data
554
566
  if (this.options.fontFace) {
@@ -589,7 +601,7 @@ class PurgeCSS {
589
601
  }
590
602
  catch (err) {
591
603
  filesNames.push(...glob__namespace.sync(globFile, {
592
- nodir: true,
604
+ onlyFiles: true,
593
605
  ignore: this.options.skippedContentGlobs.map((glob) => glob.replace(/^\.\//, "")),
594
606
  }));
595
607
  }
@@ -755,7 +767,7 @@ class PurgeCSS {
755
767
  for (const option of cssOptions) {
756
768
  if (typeof option === "string") {
757
769
  processedOptions.push(...glob__namespace.sync(option, {
758
- nodir: true,
770
+ onlyFiles: true,
759
771
  ignore: this.options.skippedContentGlobs,
760
772
  }));
761
773
  }
@@ -783,9 +795,12 @@ class PurgeCSS {
783
795
  this.removeUnusedCSSVariables();
784
796
  const postCSSResult = root.toResult({
785
797
  map: this.options.sourceMap,
786
- to: typeof this.options.sourceMap === "object"
798
+ to: typeof this.options.sourceMap === "object" &&
799
+ this.options.sourceMap.to
787
800
  ? this.options.sourceMap.to
788
- : undefined,
801
+ : isFromFile
802
+ ? option
803
+ : undefined,
789
804
  });
790
805
  const result = {
791
806
  css: postCSSResult.toString(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "purgecss",
3
- "version": "7.0.2",
3
+ "version": "8.0.0",
4
4
  "description": "Remove unused css selectors",
5
5
  "author": "Ffloriel",
6
6
  "homepage": "https://purgecss.com",
@@ -41,9 +41,9 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "commander": "^12.1.0",
44
- "glob": "^11.0.0",
44
+ "fast-glob": "^3.3.2",
45
45
  "postcss": "^8.4.47",
46
- "postcss-selector-parser": "^6.1.2"
46
+ "postcss-selector-parser": "^7.0.0"
47
47
  },
48
48
  "bugs": {
49
49
  "url": "https://github.com/FullHuman/purgecss/issues"
@@ -52,5 +52,5 @@
52
52
  "access": "public",
53
53
  "registry": "https://registry.npmjs.org/"
54
54
  },
55
- "gitHead": "e95398d961e03a809b5e3da75e3dae1813b46b68"
55
+ "gitHead": "9269dbc8b589832bab90d29f5821fc4cf55077d6"
56
56
  }