purgecss 2.1.0 → 2.3.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/bin/purgecss CHANGED
@@ -7,6 +7,14 @@ function getList(list) {
7
7
  return list.split(',')
8
8
  }
9
9
 
10
+ async function writeCSSToFile(filePath, css) {
11
+ try {
12
+ await fs.promises.writeFile(filePath, css);
13
+ } catch(err) {
14
+ console.error(err.message);
15
+ }
16
+ }
17
+
10
18
  program
11
19
  .usage('--css <css> --content <content> [options]')
12
20
  .option('-con, --content <files>', 'glob of content files (comma separated)', getList)
@@ -41,16 +49,25 @@ if (!program.config && !(program.content && program.css)) {
41
49
  if (program.variables) options.variables = program.variables;
42
50
  if (program.whitelist) options.whitelist = program.whitelist;
43
51
 
52
+ // if (!options.css) {
53
+ // options.css = [{
54
+ // raw: stdInCSS
55
+ // }]
56
+ // }
57
+
44
58
  const purged = await new PurgeCSS().purge(options);
59
+
60
+ const output = options.output || program.output;
45
61
  // output results in specified directory
46
- if (program.output) {
62
+ if (output) {
63
+ if (purged.length === 1 && output.endsWith('.css')) {
64
+ await writeCSSToFile(output, purged[0].css)
65
+ return
66
+ }
67
+
47
68
  for (const purgedResult of purged) {
48
69
  const fileName = purgedResult.file.split("/").pop();
49
- try {
50
- fs.promises.writeFile(`${program.output}/${fileName}`, purgedResult.css);
51
- } catch(err) {
52
- console.error(err.message);
53
- }
70
+ await writeCSSToFile(`${output}/${fileName}`, purgedResult.css);
54
71
  }
55
72
  } else {
56
73
  console.log(JSON.stringify(purged));
package/lib/purgecss.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as postcss from "postcss";
2
- interface RawContent {
2
+ interface RawContent<T = string> {
3
3
  extension: string;
4
- raw: string;
4
+ raw: T;
5
5
  }
6
6
  interface RawCSS {
7
7
  raw: string;
@@ -17,7 +17,7 @@ interface ExtractorResultDetailed {
17
17
  undetermined: string[];
18
18
  }
19
19
  type ExtractorResult = ExtractorResultDetailed | string[];
20
- type ExtractorFunction = (content: string) => ExtractorResult;
20
+ type ExtractorFunction<T = string> = (content: T) => ExtractorResult;
21
21
  interface Extractors {
22
22
  extensions: string[];
23
23
  extractor: ExtractorFunction;
@@ -60,6 +60,25 @@ interface ResultPurge {
60
60
  rejected?: string[];
61
61
  }
62
62
  declare const defaultOptions: Options;
63
+ declare class ExtractorResultSets {
64
+ private undetermined;
65
+ private attrNames;
66
+ private attrValues;
67
+ private classes;
68
+ private ids;
69
+ private tags;
70
+ constructor(er: ExtractorResult);
71
+ merge(that: ExtractorResult | ExtractorResultSets): this;
72
+ hasAttrName(name: string): boolean;
73
+ private someAttrValue;
74
+ hasAttrPrefix(prefix: string): boolean;
75
+ hasAttrSuffix(suffix: string): boolean;
76
+ hasAttrSubstr(substr: string): boolean;
77
+ hasAttrValue(value: string): boolean;
78
+ hasClass(name: string): boolean;
79
+ hasId(id: string): boolean;
80
+ hasTag(tag: string): boolean;
81
+ }
63
82
  /**
64
83
  * Load the configuration file from the path
65
84
  * @param configFile Path of the config file
@@ -70,7 +89,7 @@ declare function setOptions(configFile?: string): Promise<Options>;
70
89
  * @param extractorSelectorsA extractor selectors A
71
90
  * @param extractorSelectorsB extractor selectors B
72
91
  */
73
- declare function mergeExtractorSelectors(extractorSelectorsA: ExtractorResultDetailed, extractorSelectorsB: ExtractorResultDetailed): ExtractorResultDetailed;
92
+ declare function mergeExtractorSelectors(...extractors: (ExtractorResultDetailed | ExtractorResultSets)[]): ExtractorResultSets;
74
93
  declare class PurgeCSS {
75
94
  private ignore;
76
95
  private atRules;
@@ -80,6 +99,11 @@ declare class PurgeCSS {
80
99
  private variablesStructure;
81
100
  options: Options;
82
101
  private collectDeclarationsData;
102
+ /**
103
+ * Get the extractor corresponding to the extension file
104
+ * @param filename Name of the file
105
+ * @param extractors Array of extractors definition
106
+ */
83
107
  /**
84
108
  * Get the extractor corresponding to the extension file
85
109
  * @param filename Name of the file
@@ -91,18 +115,37 @@ declare class PurgeCSS {
91
115
  * @param files Array of files path or glob pattern
92
116
  * @param extractors Array of extractors
93
117
  */
94
- extractSelectorsFromFiles(files: string[], extractors: Extractors[]): Promise<ExtractorResultDetailed>;
118
+ /**
119
+ * Extract the selectors present in the files using a purgecss extractor
120
+ * @param files Array of files path or glob pattern
121
+ * @param extractors Array of extractors
122
+ */
123
+ extractSelectorsFromFiles(files: string[], extractors: Extractors[]): Promise<ExtractorResultSets>;
124
+ /**
125
+ * Extract the selectors present in the passed string using a PurgeCSS extractor
126
+ * @param content Array of content
127
+ * @param extractors Array of extractors
128
+ */
95
129
  /**
96
130
  * Extract the selectors present in the passed string using a PurgeCSS extractor
97
131
  * @param content Array of content
98
132
  * @param extractors Array of extractors
99
133
  */
100
- extractSelectorsFromString(content: RawContent[], extractors: Extractors[]): Promise<ExtractorResultDetailed>;
134
+ extractSelectorsFromString(content: RawContent[], extractors: Extractors[]): Promise<ExtractorResultSets>;
135
+ /**
136
+ * Evaluate at-rule and register it for future reference
137
+ * @param node node of postcss AST
138
+ */
101
139
  /**
102
140
  * Evaluate at-rule and register it for future reference
103
141
  * @param node node of postcss AST
104
142
  */
105
143
  private evaluateAtRule;
144
+ /**
145
+ * Evaluate css selector and decide if it should be removed or not
146
+ * @param node node of postcss AST
147
+ * @param selectors selectors used in content files
148
+ */
106
149
  /**
107
150
  * Evaluate css selector and decide if it should be removed or not
108
151
  * @param node node of postcss AST
@@ -114,12 +157,25 @@ declare class PurgeCSS {
114
157
  * @param cssOptions css options, files or raw strings
115
158
  * @param selectors set of extracted css selectors
116
159
  */
117
- getPurgedCSS(cssOptions: Array<string | RawCSS>, selectors: ExtractorResultDetailed): Promise<ResultPurge[]>;
160
+ /**
161
+ * Get the purged version of the css based on the files
162
+ * @param cssOptions css options, files or raw strings
163
+ * @param selectors set of extracted css selectors
164
+ */
165
+ getPurgedCSS(cssOptions: Array<string | RawCSS>, selectors: ExtractorResultSets): Promise<ResultPurge[]>;
166
+ /**
167
+ * Check if the selector is whitelisted by the option whitelist or whitelistPatterns
168
+ * @param selector css selector
169
+ */
118
170
  /**
119
171
  * Check if the selector is whitelisted by the option whitelist or whitelistPatterns
120
172
  * @param selector css selector
121
173
  */
122
174
  private isSelectorWhitelisted;
175
+ /**
176
+ * Check if the selector is whitelisted by the whitelistPatternsChildren option
177
+ * @param selector selector
178
+ */
123
179
  /**
124
180
  * Check if the selector is whitelisted by the whitelistPatternsChildren option
125
181
  * @param selector selector
@@ -127,24 +183,42 @@ declare class PurgeCSS {
127
183
  private isSelectorWhitelistedChildren;
128
184
  /**
129
185
  * Remove unused css
130
- * @param options PurgeCSS options
186
+ * @param userOptions PurgeCSS options
187
+ */
188
+ /**
189
+ * Remove unused css
190
+ * @param userOptions PurgeCSS options
131
191
  */
132
192
  purge(userOptions: UserDefinedOptions | string | undefined): Promise<ResultPurge[]>;
193
+ /**
194
+ * Remove unused CSS variables
195
+ */
133
196
  /**
134
197
  * Remove unused CSS variables
135
198
  */
136
199
  removeUnusedCSSVariables(): void;
200
+ /**
201
+ * Remove unused font-faces
202
+ */
137
203
  /**
138
204
  * Remove unused font-faces
139
205
  */
140
206
  removeUnusedFontFaces(): void;
207
+ /**
208
+ * Remove unused keyframes
209
+ */
141
210
  /**
142
211
  * Remove unused keyframes
143
212
  */
144
213
  removeUnusedKeyframes(): void;
145
214
  /**
146
215
  * Determine if the selector should be kept, based on the selectors found in the files
147
- * @param selectorsInContent set of css selectors found in the content files or string
216
+ * @param selector set of css selectors found in the content files or string
217
+ * @param selectorsFromExtractor selectors in the css rule
218
+ */
219
+ /**
220
+ * Determine if the selector should be kept, based on the selectors found in the files
221
+ * @param selector set of css selectors found in the content files or string
148
222
  * @param selectorsFromExtractor selectors in the css rule
149
223
  */
150
224
  private shouldKeepSelector;
@@ -153,6 +227,11 @@ declare class PurgeCSS {
153
227
  * @param root root node of the postcss AST
154
228
  * @param selectors selectors used in content files
155
229
  */
156
- walkThroughCSS(root: postcss.Root, selectors: ExtractorResultDetailed): void;
230
+ /**
231
+ * Walk through the CSS AST and remove unused CSS
232
+ * @param root root node of the postcss AST
233
+ * @param selectors selectors used in content files
234
+ */
235
+ walkThroughCSS(root: postcss.Root, selectors: ExtractorResultSets): void;
157
236
  }
158
237
  export { PurgeCSS as default, PurgeCSS, defaultOptions, setOptions, mergeExtractorSelectors };
@@ -1,7 +1,7 @@
1
1
  import * as postcss from "postcss";
2
- interface RawContent {
2
+ interface RawContent<T = string> {
3
3
  extension: string;
4
- raw: string;
4
+ raw: T;
5
5
  }
6
6
  interface RawCSS {
7
7
  raw: string;
@@ -17,7 +17,7 @@ interface ExtractorResultDetailed {
17
17
  undetermined: string[];
18
18
  }
19
19
  type ExtractorResult = ExtractorResultDetailed | string[];
20
- type ExtractorFunction = (content: string) => ExtractorResult;
20
+ type ExtractorFunction<T = string> = (content: T) => ExtractorResult;
21
21
  interface Extractors {
22
22
  extensions: string[];
23
23
  extractor: ExtractorFunction;
@@ -60,6 +60,25 @@ interface ResultPurge {
60
60
  rejected?: string[];
61
61
  }
62
62
  declare const defaultOptions: Options;
63
+ declare class ExtractorResultSets {
64
+ private undetermined;
65
+ private attrNames;
66
+ private attrValues;
67
+ private classes;
68
+ private ids;
69
+ private tags;
70
+ constructor(er: ExtractorResult);
71
+ merge(that: ExtractorResult | ExtractorResultSets): this;
72
+ hasAttrName(name: string): boolean;
73
+ private someAttrValue;
74
+ hasAttrPrefix(prefix: string): boolean;
75
+ hasAttrSuffix(suffix: string): boolean;
76
+ hasAttrSubstr(substr: string): boolean;
77
+ hasAttrValue(value: string): boolean;
78
+ hasClass(name: string): boolean;
79
+ hasId(id: string): boolean;
80
+ hasTag(tag: string): boolean;
81
+ }
63
82
  /**
64
83
  * Load the configuration file from the path
65
84
  * @param configFile Path of the config file
@@ -70,7 +89,7 @@ declare function setOptions(configFile?: string): Promise<Options>;
70
89
  * @param extractorSelectorsA extractor selectors A
71
90
  * @param extractorSelectorsB extractor selectors B
72
91
  */
73
- declare function mergeExtractorSelectors(extractorSelectorsA: ExtractorResultDetailed, extractorSelectorsB: ExtractorResultDetailed): ExtractorResultDetailed;
92
+ declare function mergeExtractorSelectors(...extractors: (ExtractorResultDetailed | ExtractorResultSets)[]): ExtractorResultSets;
74
93
  declare class PurgeCSS {
75
94
  private ignore;
76
95
  private atRules;
@@ -80,6 +99,11 @@ declare class PurgeCSS {
80
99
  private variablesStructure;
81
100
  options: Options;
82
101
  private collectDeclarationsData;
102
+ /**
103
+ * Get the extractor corresponding to the extension file
104
+ * @param filename Name of the file
105
+ * @param extractors Array of extractors definition
106
+ */
83
107
  /**
84
108
  * Get the extractor corresponding to the extension file
85
109
  * @param filename Name of the file
@@ -91,18 +115,37 @@ declare class PurgeCSS {
91
115
  * @param files Array of files path or glob pattern
92
116
  * @param extractors Array of extractors
93
117
  */
94
- extractSelectorsFromFiles(files: string[], extractors: Extractors[]): Promise<ExtractorResultDetailed>;
118
+ /**
119
+ * Extract the selectors present in the files using a purgecss extractor
120
+ * @param files Array of files path or glob pattern
121
+ * @param extractors Array of extractors
122
+ */
123
+ extractSelectorsFromFiles(files: string[], extractors: Extractors[]): Promise<ExtractorResultSets>;
124
+ /**
125
+ * Extract the selectors present in the passed string using a PurgeCSS extractor
126
+ * @param content Array of content
127
+ * @param extractors Array of extractors
128
+ */
95
129
  /**
96
130
  * Extract the selectors present in the passed string using a PurgeCSS extractor
97
131
  * @param content Array of content
98
132
  * @param extractors Array of extractors
99
133
  */
100
- extractSelectorsFromString(content: RawContent[], extractors: Extractors[]): Promise<ExtractorResultDetailed>;
134
+ extractSelectorsFromString(content: RawContent[], extractors: Extractors[]): Promise<ExtractorResultSets>;
135
+ /**
136
+ * Evaluate at-rule and register it for future reference
137
+ * @param node node of postcss AST
138
+ */
101
139
  /**
102
140
  * Evaluate at-rule and register it for future reference
103
141
  * @param node node of postcss AST
104
142
  */
105
143
  private evaluateAtRule;
144
+ /**
145
+ * Evaluate css selector and decide if it should be removed or not
146
+ * @param node node of postcss AST
147
+ * @param selectors selectors used in content files
148
+ */
106
149
  /**
107
150
  * Evaluate css selector and decide if it should be removed or not
108
151
  * @param node node of postcss AST
@@ -114,12 +157,25 @@ declare class PurgeCSS {
114
157
  * @param cssOptions css options, files or raw strings
115
158
  * @param selectors set of extracted css selectors
116
159
  */
117
- getPurgedCSS(cssOptions: Array<string | RawCSS>, selectors: ExtractorResultDetailed): Promise<ResultPurge[]>;
160
+ /**
161
+ * Get the purged version of the css based on the files
162
+ * @param cssOptions css options, files or raw strings
163
+ * @param selectors set of extracted css selectors
164
+ */
165
+ getPurgedCSS(cssOptions: Array<string | RawCSS>, selectors: ExtractorResultSets): Promise<ResultPurge[]>;
166
+ /**
167
+ * Check if the selector is whitelisted by the option whitelist or whitelistPatterns
168
+ * @param selector css selector
169
+ */
118
170
  /**
119
171
  * Check if the selector is whitelisted by the option whitelist or whitelistPatterns
120
172
  * @param selector css selector
121
173
  */
122
174
  private isSelectorWhitelisted;
175
+ /**
176
+ * Check if the selector is whitelisted by the whitelistPatternsChildren option
177
+ * @param selector selector
178
+ */
123
179
  /**
124
180
  * Check if the selector is whitelisted by the whitelistPatternsChildren option
125
181
  * @param selector selector
@@ -127,24 +183,42 @@ declare class PurgeCSS {
127
183
  private isSelectorWhitelistedChildren;
128
184
  /**
129
185
  * Remove unused css
130
- * @param options PurgeCSS options
186
+ * @param userOptions PurgeCSS options
187
+ */
188
+ /**
189
+ * Remove unused css
190
+ * @param userOptions PurgeCSS options
131
191
  */
132
192
  purge(userOptions: UserDefinedOptions | string | undefined): Promise<ResultPurge[]>;
193
+ /**
194
+ * Remove unused CSS variables
195
+ */
133
196
  /**
134
197
  * Remove unused CSS variables
135
198
  */
136
199
  removeUnusedCSSVariables(): void;
200
+ /**
201
+ * Remove unused font-faces
202
+ */
137
203
  /**
138
204
  * Remove unused font-faces
139
205
  */
140
206
  removeUnusedFontFaces(): void;
207
+ /**
208
+ * Remove unused keyframes
209
+ */
141
210
  /**
142
211
  * Remove unused keyframes
143
212
  */
144
213
  removeUnusedKeyframes(): void;
145
214
  /**
146
215
  * Determine if the selector should be kept, based on the selectors found in the files
147
- * @param selectorsInContent set of css selectors found in the content files or string
216
+ * @param selector set of css selectors found in the content files or string
217
+ * @param selectorsFromExtractor selectors in the css rule
218
+ */
219
+ /**
220
+ * Determine if the selector should be kept, based on the selectors found in the files
221
+ * @param selector set of css selectors found in the content files or string
148
222
  * @param selectorsFromExtractor selectors in the css rule
149
223
  */
150
224
  private shouldKeepSelector;
@@ -153,6 +227,11 @@ declare class PurgeCSS {
153
227
  * @param root root node of the postcss AST
154
228
  * @param selectors selectors used in content files
155
229
  */
156
- walkThroughCSS(root: postcss.Root, selectors: ExtractorResultDetailed): void;
230
+ /**
231
+ * Walk through the CSS AST and remove unused CSS
232
+ * @param root root node of the postcss AST
233
+ * @param selectors selectors used in content files
234
+ */
235
+ walkThroughCSS(root: postcss.Root, selectors: ExtractorResultSets): void;
157
236
  }
158
237
  export { PurgeCSS as default, PurgeCSS, defaultOptions, setOptions, mergeExtractorSelectors };
@@ -1 +1 @@
1
- import{parse as e}from"postcss";import t from"postcss-selector-parser";import{access as s,readFile as r,constants as i}from"fs";import{promisify as n}from"util";import o from"glob";import a from"path";const c={css:[],content:[],defaultExtractor:e=>e.match(/[A-Za-z0-9_-]+/g)||[],extractors:[],fontFace:!1,keyframes:!1,rejected:!1,stdin:!1,stdout:!1,variables:!1,whitelist:[],whitelistPatterns:[],whitelistPatternsChildren:[]},u=["*","::-webkit-scrollbar","::selection",":root","::before","::after"];class l{constructor(e){this.nodes=[],this.isUsed=!1,this.value=e}}class d{constructor(){this.nodes=new Map,this.usedVariables=new Set}addVariable(e){const{prop:t}=e;if(!this.nodes.has(t)){const s=new l(e);this.nodes.set(t,s)}}addVariableUsage(e,t){const{prop:s}=e,r=this.nodes.get(s);for(const e of t){const t=e[1];if(this.nodes.has(t)){const e=this.nodes.get(t);r.nodes.push(e)}}}addVariableUsageInProperties(e){for(const t of e){const e=t[1];this.usedVariables.add(e)}}setAsUsed(e){const t=[this.nodes.get(e)];for(;0!==t.length;){const e=t.pop();e.isUsed||(e.isUsed=!0,t.push(...e.nodes))}}removeUnused(){for(const e of this.usedVariables)this.setAsUsed(e);for(const[,e]of this.nodes)e.isUsed||e.value.remove()}}const h={access:n(s),readFile:n(r)};async function f(e="purgecss.config.js"){let t;try{const s=a.join(process.cwd(),e);t=await import(s)}catch(e){throw new Error(`Error loading the config file ${e.message}`)}return{...c,...t}}async function m(e,t){const s=await t(e);return Array.isArray(s)?{attributes:{names:[],values:[]},classes:[],ids:[],tags:[],undetermined:s}:s}function p(e,t){switch(t){case"next":return e.text.includes("purgecss ignore");case"start":return e.text.includes("purgecss start ignore");case"end":return e.text.includes("purgecss end ignore")}}function v(e,t){return{attributes:{names:[...e.attributes.names,...t.attributes.names],values:[...e.attributes.values,...t.attributes.values]},classes:[...e.classes,...t.classes],ids:[...e.ids,...t.ids],tags:[...e.tags,...t.tags],undetermined:[...e.undetermined,...t.undetermined]}}function g(e){return e.replace(/(^["'])|(["']$)/g,"")}function y(e,t){if(!t.attributes.names.includes(e.attribute)&&!t.undetermined.includes(e.attribute))return!1;switch(e.operator){case"$=":return t.attributes.values.some(t=>t.endsWith(e.value))||t.undetermined.some(t=>t.endsWith(e.value));case"~=":case"*=":return t.attributes.values.some(t=>t.includes(e.value))||t.undetermined.some(t=>t.includes(e.value));case"=":return t.attributes.values.some(t=>t===e.value)||t.undetermined.some(t=>t===e.value);case"|=":case"^=":return t.attributes.values.some(t=>t.startsWith(e.value))||t.undetermined.some(t=>t.startsWith(e.value));default:return!0}}function b(e,t){return t.ids.includes(e.value)||t.undetermined.includes(e.value)}function w(e,t){return t.tags.includes(e.value)||t.undetermined.includes(e.value)}class S{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.variablesStructure=new d,this.options=c}collectDeclarationsData(e){const{prop:t,value:s}=e;if(this.options.variables){const r=function(e,t){const s=[];return e.replace(t,(function(){const t=Array.prototype.slice.call(arguments,0,-2);return t.input=arguments[arguments.length-1],t.index=arguments[arguments.length-2],s.push(t),e})),s}(s,/var\((.+?)[\,)]/g);t.startsWith("--")?(this.variablesStructure.addVariable(e),r.length>0&&this.variablesStructure.addVariableUsage(e,r)):r.length>0&&this.variablesStructure.addVariableUsageInProperties(r)}if(!this.options.keyframes||"animation"!==t&&"animation-name"!==t)if(this.options.fontFace){if("font-family"===t)for(const e of s.split(",")){const t=g(e.trim());this.usedFontFaces.add(t)}}else;else for(const e of s.split(/[\s,]+/))this.usedAnimations.add(e)}getFileExtractor(e,t){const s=t.find(t=>t.extensions.find(t=>e.endsWith(t)));return void 0===s?this.options.defaultExtractor:s.extractor}async extractSelectorsFromFiles(e,t){let s={attributes:{names:[],values:[]},classes:[],ids:[],tags:[],undetermined:[]};for(const r of e){let e=[];try{await h.access(r,i.F_OK),e.push(r)}catch(t){e=o.sync(r)}for(const r of e){const e=await h.readFile(r,"utf-8"),i=this.getFileExtractor(r,t);s=v(s,await m(e,i))}}return s}async extractSelectorsFromString(e,t){let s={attributes:{names:[],values:[]},classes:[],ids:[],tags:[],undetermined:[]};for(const{raw:r,extension:i}of e){const e=this.getFileExtractor(`.${i}`,t);s=v(s,await m(r,e))}return s}evaluateAtRule(e){if(this.options.keyframes&&e.name.endsWith("keyframes"))this.atRules.keyframes.push(e);else if(this.options.fontFace&&"font-face"===e.name&&e.nodes)for(const t of e.nodes)"decl"===t.type&&"font-family"===t.prop&&this.atRules.fontFace.push({name:g(t.value),node:e})}async evaluateRule(e,s){if(this.ignore)return;const r=e.prev();if(r&&"comment"===r.type&&p(r,"next"))return void r.remove();if(e.parent&&"atrule"===e.parent.type&&"keyframes"===e.parent.name)return;if("rule"!==e.type)return;if(function(e){let t=!1;return e.walkComments(e=>{e&&"comment"===e.type&&e.text.includes("purgecss ignore current")&&(t=!0,e.remove())}),t}(e))return;let i=!0;if(e.selector=t(e=>{e.walk(e=>{"selector"===e.type&&(i=this.shouldKeepSelector(e,s),i||(this.options.rejected&&this.selectorsRemoved.add(e.toString()),e.remove()))})}).processSync(e.selector),i&&void 0!==e.nodes)for(const t of e.nodes)"decl"===t.type&&this.collectDeclarationsData(t);const n=e.parent;e.selector||e.remove(),function(e){return!!("rule"===e.type&&!e.selector||e.nodes&&!e.nodes.length||"atrule"===e.type&&(!e.nodes&&!e.params||!e.params&&e.nodes&&!e.nodes.length))}(n)&&n.remove()}async getPurgedCSS(t,s){const r=[],i=[];for(const e of t)"string"==typeof e?i.push(...o.sync(e)):i.push(e);for(const t of i){const i="string"==typeof t?this.options.stdin?t:await h.readFile(t,"utf-8"):t.raw,n=e(i);this.walkThroughCSS(n,s),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const o={css:n.toString(),file:"string"==typeof t?t:void 0};"string"==typeof t&&(o.file=t),this.options.rejected&&(o.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),r.push(o)}return r}isSelectorWhitelisted(e){return u.includes(e)||this.options.whitelist&&this.options.whitelist.some(t=>t===e)||this.options.whitelistPatterns&&this.options.whitelistPatterns.some(t=>t.test(e))}isSelectorWhitelistedChildren(e){return this.options.whitelistPatternsChildren&&this.options.whitelistPatternsChildren.some(t=>t.test(e))}async purge(e){this.options="object"!=typeof e?await f(e):{...c,...e};const{content:t,css:s,extractors:r}=this.options,i=t.filter(e=>"string"==typeof e),n=t.filter(e=>"object"==typeof e),o=await this.extractSelectorsFromFiles(i,r),a=await this.extractSelectorsFromString(n,r);return this.getPurgedCSS(s,v(o,a))}removeUnusedCSSVariables(){this.variablesStructure.removeUnused()}removeUnusedFontFaces(){for(const{name:e,node:t}of this.atRules.fontFace)this.usedFontFaces.has(e)||t.remove()}removeUnusedKeyframes(){for(const e of this.atRules.keyframes)this.usedAnimations.has(e.params)||e.remove()}shouldKeepSelector(e,t){if(function(e){return e.parent&&"pseudo"===e.parent.type&&e.parent.value.startsWith(":")}(e))return!0;let s=!1;for(const n of e.nodes){const e="attribute"===n.type&&n.attribute||n.value;if(e&&this.isSelectorWhitelistedChildren(e))return!0;if(e&&(u.includes(e)||this.isSelectorWhitelisted(e)))s=!0;else{switch(n.type){case"attribute":s=!!["value","checked","selected","open"].includes(n.attribute)||y(n,t);break;case"class":r=n,s=(i=t).classes.includes(r.value)||i.undetermined.includes(r.value);break;case"id":s=b(n,t);break;case"tag":s=w(n,t)}if(!s)return!1}}var r,i;return s}walkThroughCSS(e,t){e.walk(e=>"rule"===e.type?this.evaluateRule(e,t):"atrule"===e.type?this.evaluateAtRule(e):void("comment"===e.type&&(p(e,"start")?(this.ignore=!0,e.remove()):p(e,"end")&&(this.ignore=!1,e.remove()))))}}export default S;export{S as PurgeCSS,c as defaultOptions,v as mergeExtractorSelectors,f as setOptions};
1
+ import{parse as t}from"postcss";import e from"postcss-selector-parser";import{access as s,readFile as r,constants as i}from"fs";import{promisify as n}from"util";import o from"glob";import a from"path";const c={css:[],content:[],defaultExtractor:t=>t.match(/[A-Za-z0-9_-]+/g)||[],extractors:[],fontFace:!1,keyframes:!1,rejected:!1,stdin:!1,stdout:!1,variables:!1,whitelist:[],whitelistPatterns:[],whitelistPatternsChildren:[]},u=["*","::-webkit-scrollbar","::selection",":root","::before","::after"];class h{constructor(t){this.nodes=[],this.isUsed=!1,this.value=t}}class l{constructor(){this.nodes=new Map,this.usedVariables=new Set}addVariable(t){const{prop:e}=t;if(!this.nodes.has(e)){const s=new h(t);this.nodes.set(e,s)}}addVariableUsage(t,e){const{prop:s}=t,r=this.nodes.get(s);for(const t of e){const e=t[1];if(this.nodes.has(e)){const t=this.nodes.get(e);null==r||r.nodes.push(t)}}}addVariableUsageInProperties(t){for(const e of t){const t=e[1];this.usedVariables.add(t)}}setAsUsed(t){const e=[this.nodes.get(t)];for(;0!==e.length;){const t=e.pop();t&&!t.isUsed&&(t.isUsed=!0,e.push(...t.nodes))}}removeUnused(){for(const t of this.usedVariables)this.setAsUsed(t);for(const[,t]of this.nodes)t.isUsed||t.value.remove()}}function d(t,e){e&&e.forEach(t.add,t)}class f{constructor(t){this.undetermined=new Set,this.attrNames=new Set,this.attrValues=new Set,this.classes=new Set,this.ids=new Set,this.tags=new Set,this.merge(t)}merge(t){return Array.isArray(t)?d(this.undetermined,t):t instanceof f?(d(this.undetermined,t.undetermined),d(this.attrNames,t.attrNames),d(this.attrValues,t.attrValues),d(this.classes,t.classes),d(this.ids,t.ids),d(this.tags,t.tags)):(d(this.undetermined,t.undetermined),t.attributes&&(d(this.attrNames,t.attributes.names),d(this.attrValues,t.attributes.values)),d(this.classes,t.classes),d(this.ids,t.ids),d(this.tags,t.tags)),this}hasAttrName(t){return this.attrNames.has(t)||this.undetermined.has(t)}someAttrValue(t){for(const e of this.attrValues)if(t(e))return!0;for(const e of this.undetermined)if(t(e))return!0;return!1}hasAttrPrefix(t){return this.someAttrValue(e=>e.startsWith(t))}hasAttrSuffix(t){return this.someAttrValue(e=>e.endsWith(t))}hasAttrSubstr(t){return this.someAttrValue(e=>e.includes(t))}hasAttrValue(t){return this.attrValues.has(t)||this.undetermined.has(t)}hasClass(t){return this.classes.has(t)||this.undetermined.has(t)}hasId(t){return this.ids.has(t)||this.undetermined.has(t)}hasTag(t){return this.tags.has(t)||this.undetermined.has(t)}}const m={access:n(s),readFile:n(r)};async function p(t="purgecss.config.js"){let e;try{const s=a.join(process.cwd(),t);e=await import(s)}catch(t){throw new Error(`Error loading the config file ${t.message}`)}return{...c,...e}}async function g(t,e){return new f(await e(t))}function v(t,e){switch(e){case"next":return t.text.includes("purgecss ignore");case"start":return t.text.includes("purgecss start ignore");case"end":return t.text.includes("purgecss end ignore")}}function y(...t){const e=new f([]);return t.forEach(e.merge,e),e}function w(t){return t.replace(/(^["'])|(["']$)/g,"")}function S(t,e){if(!e.hasAttrName(t.attribute))return!1;if(void 0===t.value)return!0;switch(t.operator){case"$=":return e.hasAttrSuffix(t.value);case"~=":case"*=":return e.hasAttrSubstr(t.value);case"=":return e.hasAttrValue(t.value);case"|=":case"^=":return e.hasAttrPrefix(t.value);default:return!0}}function b(t,e){return e.hasId(t.value)}function F(t,e){return e.hasTag(t.value)}class x{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.variablesStructure=new l,this.options=c}collectDeclarationsData(t){const{prop:e,value:s}=t;if(this.options.variables){const r=function(t,e){const s=[];return t.replace(e,(function(){const e=arguments,r=Array.prototype.slice.call(e,0,-2);return r.input=e[e.length-1],r.index=e[e.length-2],s.push(r),t})),s}(s,/var\((.+?)[,)]/g);e.startsWith("--")?(this.variablesStructure.addVariable(t),r.length>0&&this.variablesStructure.addVariableUsage(t,r)):r.length>0&&this.variablesStructure.addVariableUsageInProperties(r)}if(!this.options.keyframes||"animation"!==e&&"animation-name"!==e)if(this.options.fontFace){if("font-family"===e)for(const t of s.split(",")){const e=w(t.trim());this.usedFontFaces.add(e)}}else;else for(const t of s.split(/[\s,]+/))this.usedAnimations.add(t)}getFileExtractor(t,e){const s=e.find(e=>e.extensions.find(e=>t.endsWith(e)));return void 0===s?this.options.defaultExtractor:s.extractor}async extractSelectorsFromFiles(t,e){const s=new f([]);for(const r of t){let t=[];try{await m.access(r,i.F_OK),t.push(r)}catch(e){t=o.sync(r,{nodir:!0})}for(const r of t){const t=await m.readFile(r,"utf-8"),i=this.getFileExtractor(r,e),n=await g(t,i);s.merge(n)}}return s}async extractSelectorsFromString(t,e){const s=new f([]);for(const{raw:r,extension:i}of t){const t=this.getFileExtractor(`.${i}`,e),n=await g(r,t);s.merge(n)}return s}evaluateAtRule(t){if(this.options.keyframes&&t.name.endsWith("keyframes"))this.atRules.keyframes.push(t);else if(this.options.fontFace&&"font-face"===t.name&&t.nodes)for(const e of t.nodes)"decl"===e.type&&"font-family"===e.prop&&this.atRules.fontFace.push({name:w(e.value),node:t})}async evaluateRule(t,s){if(this.ignore)return;const r=t.prev();if(r&&"comment"===r.type&&v(r,"next"))return void r.remove();if(t.parent&&"atrule"===t.parent.type&&"keyframes"===t.parent.name)return;if("rule"!==t.type)return;if(function(t){let e=!1;return t.walkComments(t=>{t&&"comment"===t.type&&t.text.includes("purgecss ignore current")&&(e=!0,t.remove())}),e}(t))return;let i=!0;if(t.selector=e(t=>{t.walk(t=>{"selector"===t.type&&(i=this.shouldKeepSelector(t,s),i||(this.options.rejected&&this.selectorsRemoved.add(t.toString()),t.remove()))})}).processSync(t.selector),i&&void 0!==t.nodes)for(const e of t.nodes)"decl"===e.type&&this.collectDeclarationsData(e);const n=t.parent;t.selector||t.remove(),function(t){return!!("rule"===t.type&&!t.selector||t.nodes&&!t.nodes.length||"atrule"===t.type&&(!t.nodes&&!t.params||!t.params&&t.nodes&&!t.nodes.length))}(n)&&n.remove()}async getPurgedCSS(e,s){const r=[],i=[];for(const t of e)"string"==typeof t?i.push(...o.sync(t,{nodir:!0})):i.push(t);for(const e of i){const i="string"==typeof e?this.options.stdin?e:await m.readFile(e,"utf-8"):e.raw,n=t(i);this.walkThroughCSS(n,s),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const o={css:n.toString(),file:"string"==typeof e?e:void 0};"string"==typeof e&&(o.file=e),this.options.rejected&&(o.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),r.push(o)}return r}isSelectorWhitelisted(t){return u.includes(t)||this.options.whitelist&&this.options.whitelist.some(e=>e===t)||this.options.whitelistPatterns&&this.options.whitelistPatterns.some(e=>e.test(t))}isSelectorWhitelistedChildren(t){return this.options.whitelistPatternsChildren&&this.options.whitelistPatternsChildren.some(e=>e.test(t))}async purge(t){this.options="object"!=typeof t?await p(t):{...c,...t};const{content:e,css:s,extractors:r}=this.options,i=e.filter(t=>"string"==typeof t),n=e.filter(t=>"object"==typeof t),o=await this.extractSelectorsFromFiles(i,r),a=await this.extractSelectorsFromString(n,r);return this.getPurgedCSS(s,y(o,a))}removeUnusedCSSVariables(){this.variablesStructure.removeUnused()}removeUnusedFontFaces(){for(const{name:t,node:e}of this.atRules.fontFace)this.usedFontFaces.has(t)||e.remove()}removeUnusedKeyframes(){for(const t of this.atRules.keyframes)this.usedAnimations.has(t.params)||t.remove()}shouldKeepSelector(t,e){if(function(t){return t.parent&&"pseudo"===t.parent.type&&t.parent.value.startsWith(":")||!1}(t))return!0;let s=!1;for(const i of t.nodes){const t="attribute"===i.type&&i.attribute||i.value;if(t&&this.isSelectorWhitelistedChildren(t))return!0;if(t&&(u.includes(t)||this.isSelectorWhitelisted(t)))s=!0;else{switch(i.type){case"attribute":s=!!["value","checked","selected","open"].includes(i.attribute)||S(i,e);break;case"class":r=i,s=e.hasClass(r.value);break;case"id":s=b(i,e);break;case"tag":s=F(i,e)}if(!s)return!1}}var r;return s}walkThroughCSS(t,e){t.walk(t=>"rule"===t.type?this.evaluateRule(t,e):"atrule"===t.type?this.evaluateAtRule(t):void("comment"===t.type&&(v(t,"start")?(this.ignore=!0,t.remove()):v(t,"end")&&(this.ignore=!1,t.remove()))))}}export default x;export{x as PurgeCSS,c as defaultOptions,y as mergeExtractorSelectors,p as setOptions};
package/lib/purgecss.js CHANGED
@@ -1 +1 @@
1
- "use strict";function _interopDefault(e){return e&&"object"==typeof e&&"default"in e?e.default:e}function _interopNamespace(e){if(e&&e.__esModule)return e;var t={};return e&&Object.keys(e).forEach((function(s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(t,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})})),t.default=e,t}Object.defineProperty(exports,"__esModule",{value:!0});var postcss=require("postcss"),selectorParser=_interopDefault(require("postcss-selector-parser")),fs=require("fs"),util=require("util"),glob=_interopDefault(require("glob")),path=_interopDefault(require("path"));const defaultOptions={css:[],content:[],defaultExtractor:e=>e.match(/[A-Za-z0-9_-]+/g)||[],extractors:[],fontFace:!1,keyframes:!1,rejected:!1,stdin:!1,stdout:!1,variables:!1,whitelist:[],whitelistPatterns:[],whitelistPatternsChildren:[]},IGNORE_ANNOTATION_CURRENT="purgecss ignore current",IGNORE_ANNOTATION_NEXT="purgecss ignore",IGNORE_ANNOTATION_START="purgecss start ignore",IGNORE_ANNOTATION_END="purgecss end ignore",CONFIG_FILENAME="purgecss.config.js",ERROR_CONFIG_FILE_LOADING="Error loading the config file",CSS_WHITELIST=["*","::-webkit-scrollbar","::selection",":root","::before","::after"];class VariableNode{constructor(e){this.nodes=[],this.isUsed=!1,this.value=e}}class VariablesStructure{constructor(){this.nodes=new Map,this.usedVariables=new Set}addVariable(e){const{prop:t}=e;if(!this.nodes.has(t)){const s=new VariableNode(e);this.nodes.set(t,s)}}addVariableUsage(e,t){const{prop:s}=e,r=this.nodes.get(s);for(const e of t){const t=e[1];if(this.nodes.has(t)){const e=this.nodes.get(t);r.nodes.push(e)}}}addVariableUsageInProperties(e){for(const t of e){const e=t[1];this.usedVariables.add(e)}}setAsUsed(e){const t=[this.nodes.get(e)];for(;0!==t.length;){const e=t.pop();e.isUsed||(e.isUsed=!0,t.push(...e.nodes))}}removeUnused(){for(const e of this.usedVariables)this.setAsUsed(e);for(const[,e]of this.nodes)e.isUsed||e.value.remove()}}const asyncFs={access:util.promisify(fs.access),readFile:util.promisify(fs.readFile)};async function setOptions(e=CONFIG_FILENAME){let t;try{const s=path.join(process.cwd(),e);t=await new Promise((function(e){e(_interopNamespace(require(s)))}))}catch(e){throw new Error(`${ERROR_CONFIG_FILE_LOADING} ${e.message}`)}return{...defaultOptions,...t}}async function extractSelectors(e,t){const s=await t(e);return Array.isArray(s)?{attributes:{names:[],values:[]},classes:[],ids:[],tags:[],undetermined:s}:s}function isIgnoreAnnotation(e,t){switch(t){case"next":return e.text.includes("purgecss ignore");case"start":return e.text.includes("purgecss start ignore");case"end":return e.text.includes("purgecss end ignore")}}function isRuleEmpty(e){return!!("rule"===e.type&&!e.selector||e.nodes&&!e.nodes.length||"atrule"===e.type&&(!e.nodes&&!e.params||!e.params&&e.nodes&&!e.nodes.length))}function hasIgnoreAnnotation(e){let t=!1;return e.walkComments(e=>{e&&"comment"===e.type&&e.text.includes("purgecss ignore current")&&(t=!0,e.remove())}),t}function mergeExtractorSelectors(e,t){return{attributes:{names:[...e.attributes.names,...t.attributes.names],values:[...e.attributes.values,...t.attributes.values]},classes:[...e.classes,...t.classes],ids:[...e.ids,...t.ids],tags:[...e.tags,...t.tags],undetermined:[...e.undetermined,...t.undetermined]}}function stripQuotes(e){return e.replace(/(^["'])|(["']$)/g,"")}function isAttributeFound(e,t){if(!t.attributes.names.includes(e.attribute)&&!t.undetermined.includes(e.attribute))return!1;switch(e.operator){case"$=":return t.attributes.values.some(t=>t.endsWith(e.value))||t.undetermined.some(t=>t.endsWith(e.value));case"~=":case"*=":return t.attributes.values.some(t=>t.includes(e.value))||t.undetermined.some(t=>t.includes(e.value));case"=":return t.attributes.values.some(t=>t===e.value)||t.undetermined.some(t=>t===e.value);case"|=":case"^=":return t.attributes.values.some(t=>t.startsWith(e.value))||t.undetermined.some(t=>t.startsWith(e.value));default:return!0}}function isClassFound(e,t){return t.classes.includes(e.value)||t.undetermined.includes(e.value)}function isIdentifierFound(e,t){return t.ids.includes(e.value)||t.undetermined.includes(e.value)}function isTagFound(e,t){return t.tags.includes(e.value)||t.undetermined.includes(e.value)}function isInPseudoClass(e){return e.parent&&"pseudo"===e.parent.type&&e.parent.value.startsWith(":")}function matchAll(e,t){const s=[];return e.replace(t,(function(){const t=Array.prototype.slice.call(arguments,0,-2);return t.input=arguments[arguments.length-1],t.index=arguments[arguments.length-2],s.push(t),e})),s}class PurgeCSS{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.variablesStructure=new VariablesStructure,this.options=defaultOptions}collectDeclarationsData(e){const{prop:t,value:s}=e;if(this.options.variables){const r=matchAll(s,/var\((.+?)[\,)]/g);t.startsWith("--")?(this.variablesStructure.addVariable(e),r.length>0&&this.variablesStructure.addVariableUsage(e,r)):r.length>0&&this.variablesStructure.addVariableUsageInProperties(r)}if(!this.options.keyframes||"animation"!==t&&"animation-name"!==t)if(this.options.fontFace){if("font-family"===t)for(const e of s.split(",")){const t=stripQuotes(e.trim());this.usedFontFaces.add(t)}}else;else for(const e of s.split(/[\s,]+/))this.usedAnimations.add(e)}getFileExtractor(e,t){const s=t.find(t=>t.extensions.find(t=>e.endsWith(t)));return void 0===s?this.options.defaultExtractor:s.extractor}async extractSelectorsFromFiles(e,t){let s={attributes:{names:[],values:[]},classes:[],ids:[],tags:[],undetermined:[]};for(const r of e){let e=[];try{await asyncFs.access(r,fs.constants.F_OK),e.push(r)}catch(t){e=glob.sync(r)}for(const r of e){const e=await asyncFs.readFile(r,"utf-8"),n=this.getFileExtractor(r,t);s=mergeExtractorSelectors(s,await extractSelectors(e,n))}}return s}async extractSelectorsFromString(e,t){let s={attributes:{names:[],values:[]},classes:[],ids:[],tags:[],undetermined:[]};for(const{raw:r,extension:n}of e){const e=this.getFileExtractor(`.${n}`,t);s=mergeExtractorSelectors(s,await extractSelectors(r,e))}return s}evaluateAtRule(e){if(this.options.keyframes&&e.name.endsWith("keyframes"))this.atRules.keyframes.push(e);else if(this.options.fontFace&&"font-face"===e.name&&e.nodes)for(const t of e.nodes)"decl"===t.type&&"font-family"===t.prop&&this.atRules.fontFace.push({name:stripQuotes(t.value),node:e})}async evaluateRule(e,t){if(this.ignore)return;const s=e.prev();if(s&&"comment"===s.type&&isIgnoreAnnotation(s,"next"))return void s.remove();if(e.parent&&"atrule"===e.parent.type&&"keyframes"===e.parent.name)return;if("rule"!==e.type)return;if(hasIgnoreAnnotation(e))return;let r=!0;if(e.selector=selectorParser(e=>{e.walk(e=>{"selector"===e.type&&(r=this.shouldKeepSelector(e,t),r||(this.options.rejected&&this.selectorsRemoved.add(e.toString()),e.remove()))})}).processSync(e.selector),r&&void 0!==e.nodes)for(const t of e.nodes)"decl"===t.type&&this.collectDeclarationsData(t);const n=e.parent;e.selector||e.remove(),isRuleEmpty(n)&&n.remove()}async getPurgedCSS(e,t){const s=[],r=[];for(const t of e)"string"==typeof t?r.push(...glob.sync(t)):r.push(t);for(const e of r){const r="string"==typeof e?this.options.stdin?e:await asyncFs.readFile(e,"utf-8"):e.raw,n=postcss.parse(r);this.walkThroughCSS(n,t),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const i={css:n.toString(),file:"string"==typeof e?e:void 0};"string"==typeof e&&(i.file=e),this.options.rejected&&(i.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),s.push(i)}return s}isSelectorWhitelisted(e){return CSS_WHITELIST.includes(e)||this.options.whitelist&&this.options.whitelist.some(t=>t===e)||this.options.whitelistPatterns&&this.options.whitelistPatterns.some(t=>t.test(e))}isSelectorWhitelistedChildren(e){return this.options.whitelistPatternsChildren&&this.options.whitelistPatternsChildren.some(t=>t.test(e))}async purge(e){this.options="object"!=typeof e?await setOptions(e):{...defaultOptions,...e};const{content:t,css:s,extractors:r}=this.options,n=t.filter(e=>"string"==typeof e),i=t.filter(e=>"object"==typeof e),o=await this.extractSelectorsFromFiles(n,r),a=await this.extractSelectorsFromString(i,r);return this.getPurgedCSS(s,mergeExtractorSelectors(o,a))}removeUnusedCSSVariables(){this.variablesStructure.removeUnused()}removeUnusedFontFaces(){for(const{name:e,node:t}of this.atRules.fontFace)this.usedFontFaces.has(e)||t.remove()}removeUnusedKeyframes(){for(const e of this.atRules.keyframes)this.usedAnimations.has(e.params)||e.remove()}shouldKeepSelector(e,t){if(isInPseudoClass(e))return!0;let s=!1;for(const r of e.nodes){const e="attribute"===r.type&&r.attribute||r.value;if(e&&this.isSelectorWhitelistedChildren(e))return!0;if(e&&(CSS_WHITELIST.includes(e)||this.isSelectorWhitelisted(e)))s=!0;else{switch(r.type){case"attribute":s=!!["value","checked","selected","open"].includes(r.attribute)||isAttributeFound(r,t);break;case"class":s=isClassFound(r,t);break;case"id":s=isIdentifierFound(r,t);break;case"tag":s=isTagFound(r,t)}if(!s)return!1}}return s}walkThroughCSS(e,t){e.walk(e=>"rule"===e.type?this.evaluateRule(e,t):"atrule"===e.type?this.evaluateAtRule(e):void("comment"===e.type&&(isIgnoreAnnotation(e,"start")?(this.ignore=!0,e.remove()):isIgnoreAnnotation(e,"end")&&(this.ignore=!1,e.remove()))))}}exports.PurgeCSS=PurgeCSS,exports.default=PurgeCSS,exports.defaultOptions=defaultOptions,exports.mergeExtractorSelectors=mergeExtractorSelectors,exports.setOptions=setOptions;
1
+ "use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e.default:e}Object.defineProperty(exports,"__esModule",{value:!0});var t=require("postcss"),s=e(require("postcss-selector-parser")),r=require("fs"),i=require("util"),n=e(require("glob")),a=e(require("path"));const o={css:[],content:[],defaultExtractor:e=>e.match(/[A-Za-z0-9_-]+/g)||[],extractors:[],fontFace:!1,keyframes:!1,rejected:!1,stdin:!1,stdout:!1,variables:!1,whitelist:[],whitelistPatterns:[],whitelistPatternsChildren:[]},c=["*","::-webkit-scrollbar","::selection",":root","::before","::after"];class u{constructor(e){this.nodes=[],this.isUsed=!1,this.value=e}}class l{constructor(){this.nodes=new Map,this.usedVariables=new Set}addVariable(e){const{prop:t}=e;if(!this.nodes.has(t)){const s=new u(e);this.nodes.set(t,s)}}addVariableUsage(e,t){const{prop:s}=e,r=this.nodes.get(s);for(const e of t){const t=e[1];if(this.nodes.has(t)){const e=this.nodes.get(t);null==r||r.nodes.push(e)}}}addVariableUsageInProperties(e){for(const t of e){const e=t[1];this.usedVariables.add(e)}}setAsUsed(e){const t=[this.nodes.get(e)];for(;0!==t.length;){const e=t.pop();e&&!e.isUsed&&(e.isUsed=!0,t.push(...e.nodes))}}removeUnused(){for(const e of this.usedVariables)this.setAsUsed(e);for(const[,e]of this.nodes)e.isUsed||e.value.remove()}}function h(e,t){t&&t.forEach(e.add,e)}class d{constructor(e){this.undetermined=new Set,this.attrNames=new Set,this.attrValues=new Set,this.classes=new Set,this.ids=new Set,this.tags=new Set,this.merge(e)}merge(e){return Array.isArray(e)?h(this.undetermined,e):e instanceof d?(h(this.undetermined,e.undetermined),h(this.attrNames,e.attrNames),h(this.attrValues,e.attrValues),h(this.classes,e.classes),h(this.ids,e.ids),h(this.tags,e.tags)):(h(this.undetermined,e.undetermined),e.attributes&&(h(this.attrNames,e.attributes.names),h(this.attrValues,e.attributes.values)),h(this.classes,e.classes),h(this.ids,e.ids),h(this.tags,e.tags)),this}hasAttrName(e){return this.attrNames.has(e)||this.undetermined.has(e)}someAttrValue(e){for(const t of this.attrValues)if(e(t))return!0;for(const t of this.undetermined)if(e(t))return!0;return!1}hasAttrPrefix(e){return this.someAttrValue(t=>t.startsWith(e))}hasAttrSuffix(e){return this.someAttrValue(t=>t.endsWith(e))}hasAttrSubstr(e){return this.someAttrValue(t=>t.includes(e))}hasAttrValue(e){return this.attrValues.has(e)||this.undetermined.has(e)}hasClass(e){return this.classes.has(e)||this.undetermined.has(e)}hasId(e){return this.ids.has(e)||this.undetermined.has(e)}hasTag(e){return this.tags.has(e)||this.undetermined.has(e)}}const f={access:i.promisify(r.access),readFile:i.promisify(r.readFile)};async function p(e="purgecss.config.js"){let t;try{const s=a.join(process.cwd(),e);t=await new Promise((function(e){e(function(e){if(e&&e.__esModule)return e;var t={};return e&&Object.keys(e).forEach((function(s){var r=Object.getOwnPropertyDescriptor(e,s);Object.defineProperty(t,s,r.get?r:{enumerable:!0,get:function(){return e[s]}})})),t.default=e,t}(require(s)))}))}catch(e){throw new Error(`Error loading the config file ${e.message}`)}return{...o,...t}}async function m(e,t){return new d(await t(e))}function g(e,t){switch(t){case"next":return e.text.includes("purgecss ignore");case"start":return e.text.includes("purgecss start ignore");case"end":return e.text.includes("purgecss end ignore")}}function v(...e){const t=new d([]);return e.forEach(t.merge,t),t}function y(e){return e.replace(/(^["'])|(["']$)/g,"")}function w(e,t){if(!t.hasAttrName(e.attribute))return!1;if(void 0===e.value)return!0;switch(e.operator){case"$=":return t.hasAttrSuffix(e.value);case"~=":case"*=":return t.hasAttrSubstr(e.value);case"=":return t.hasAttrValue(e.value);case"|=":case"^=":return t.hasAttrPrefix(e.value);default:return!0}}function S(e,t){return t.hasId(e.value)}function b(e,t){return t.hasTag(e.value)}class x{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.variablesStructure=new l,this.options=o}collectDeclarationsData(e){const{prop:t,value:s}=e;if(this.options.variables){const r=function(e,t){const s=[];return e.replace(t,(function(){const t=arguments,r=Array.prototype.slice.call(t,0,-2);return r.input=t[t.length-1],r.index=t[t.length-2],s.push(r),e})),s}(s,/var\((.+?)[,)]/g);t.startsWith("--")?(this.variablesStructure.addVariable(e),r.length>0&&this.variablesStructure.addVariableUsage(e,r)):r.length>0&&this.variablesStructure.addVariableUsageInProperties(r)}if(!this.options.keyframes||"animation"!==t&&"animation-name"!==t)if(this.options.fontFace){if("font-family"===t)for(const e of s.split(",")){const t=y(e.trim());this.usedFontFaces.add(t)}}else;else for(const e of s.split(/[\s,]+/))this.usedAnimations.add(e)}getFileExtractor(e,t){const s=t.find(t=>t.extensions.find(t=>e.endsWith(t)));return void 0===s?this.options.defaultExtractor:s.extractor}async extractSelectorsFromFiles(e,t){const s=new d([]);for(const i of e){let e=[];try{await f.access(i,r.constants.F_OK),e.push(i)}catch(t){e=n.sync(i,{nodir:!0})}for(const r of e){const e=await f.readFile(r,"utf-8"),i=this.getFileExtractor(r,t),n=await m(e,i);s.merge(n)}}return s}async extractSelectorsFromString(e,t){const s=new d([]);for(const{raw:r,extension:i}of e){const e=this.getFileExtractor(`.${i}`,t),n=await m(r,e);s.merge(n)}return s}evaluateAtRule(e){if(this.options.keyframes&&e.name.endsWith("keyframes"))this.atRules.keyframes.push(e);else if(this.options.fontFace&&"font-face"===e.name&&e.nodes)for(const t of e.nodes)"decl"===t.type&&"font-family"===t.prop&&this.atRules.fontFace.push({name:y(t.value),node:e})}async evaluateRule(e,t){if(this.ignore)return;const r=e.prev();if(r&&"comment"===r.type&&g(r,"next"))return void r.remove();if(e.parent&&"atrule"===e.parent.type&&"keyframes"===e.parent.name)return;if("rule"!==e.type)return;if(function(e){let t=!1;return e.walkComments(e=>{e&&"comment"===e.type&&e.text.includes("purgecss ignore current")&&(t=!0,e.remove())}),t}(e))return;let i=!0;if(e.selector=s(e=>{e.walk(e=>{"selector"===e.type&&(i=this.shouldKeepSelector(e,t),i||(this.options.rejected&&this.selectorsRemoved.add(e.toString()),e.remove()))})}).processSync(e.selector),i&&void 0!==e.nodes)for(const t of e.nodes)"decl"===t.type&&this.collectDeclarationsData(t);const n=e.parent;e.selector||e.remove(),function(e){return!!("rule"===e.type&&!e.selector||e.nodes&&!e.nodes.length||"atrule"===e.type&&(!e.nodes&&!e.params||!e.params&&e.nodes&&!e.nodes.length))}(n)&&n.remove()}async getPurgedCSS(e,s){const r=[],i=[];for(const t of e)"string"==typeof t?i.push(...n.sync(t,{nodir:!0})):i.push(t);for(const e of i){const i="string"==typeof e?this.options.stdin?e:await f.readFile(e,"utf-8"):e.raw,n=t.parse(i);this.walkThroughCSS(n,s),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const a={css:n.toString(),file:"string"==typeof e?e:void 0};"string"==typeof e&&(a.file=e),this.options.rejected&&(a.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),r.push(a)}return r}isSelectorWhitelisted(e){return c.includes(e)||this.options.whitelist&&this.options.whitelist.some(t=>t===e)||this.options.whitelistPatterns&&this.options.whitelistPatterns.some(t=>t.test(e))}isSelectorWhitelistedChildren(e){return this.options.whitelistPatternsChildren&&this.options.whitelistPatternsChildren.some(t=>t.test(e))}async purge(e){this.options="object"!=typeof e?await p(e):{...o,...e};const{content:t,css:s,extractors:r}=this.options,i=t.filter(e=>"string"==typeof e),n=t.filter(e=>"object"==typeof e),a=await this.extractSelectorsFromFiles(i,r),c=await this.extractSelectorsFromString(n,r);return this.getPurgedCSS(s,v(a,c))}removeUnusedCSSVariables(){this.variablesStructure.removeUnused()}removeUnusedFontFaces(){for(const{name:e,node:t}of this.atRules.fontFace)this.usedFontFaces.has(e)||t.remove()}removeUnusedKeyframes(){for(const e of this.atRules.keyframes)this.usedAnimations.has(e.params)||e.remove()}shouldKeepSelector(e,t){if(function(e){return e.parent&&"pseudo"===e.parent.type&&e.parent.value.startsWith(":")||!1}(e))return!0;let s=!1;for(const i of e.nodes){const e="attribute"===i.type&&i.attribute||i.value;if(e&&this.isSelectorWhitelistedChildren(e))return!0;if(e&&(c.includes(e)||this.isSelectorWhitelisted(e)))s=!0;else{switch(i.type){case"attribute":s=!!["value","checked","selected","open"].includes(i.attribute)||w(i,t);break;case"class":r=i,s=t.hasClass(r.value);break;case"id":s=S(i,t);break;case"tag":s=b(i,t)}if(!s)return!1}}var r;return s}walkThroughCSS(e,t){e.walk(e=>"rule"===e.type?this.evaluateRule(e,t):"atrule"===e.type?this.evaluateAtRule(e):void("comment"===e.type&&(g(e,"start")?(this.ignore=!0,e.remove()):g(e,"end")&&(this.ignore=!1,e.remove()))))}}exports.PurgeCSS=x,exports.default=x,exports.defaultOptions=o,exports.mergeExtractorSelectors=v,exports.setOptions=p;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "purgecss",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "description": "Remove unused css selectors",
5
5
  "author": "Ffloriel",
6
6
  "homepage": "https://purgecss.com",
@@ -39,16 +39,17 @@
39
39
  "test": "echo \"Error: run tests from root\" && exit 1"
40
40
  },
41
41
  "dependencies": {
42
- "commander": "^4.0.0",
42
+ "commander": "^5.0.0",
43
43
  "glob": "^7.0.0",
44
- "postcss": "7.0.27",
44
+ "postcss": "7.0.32",
45
45
  "postcss-selector-parser": "^6.0.2"
46
46
  },
47
47
  "devDependencies": {
48
- "@types/glob": "^7.1.1"
48
+ "@types/glob": "^7.1.1",
49
+ "tailwindcss": "^1.2.0"
49
50
  },
50
51
  "bugs": {
51
52
  "url": "https://github.com/FullHuman/purgecss/issues"
52
53
  },
53
- "gitHead": "0ce7486ad5fa949e33b7162ab4aef02ff237c297"
54
+ "gitHead": "5314e41edf328e2ad2639549e1587b82a964a42e"
54
55
  }