purgecss 4.0.1 → 4.1.1
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 +6 -6
- package/bin/purgecss.d.ts +1 -0
- package/bin/purgecss.js +1 -87
- package/lib/purgecss.d.ts +262 -266
- package/lib/purgecss.esm.d.ts +262 -266
- package/lib/purgecss.esm.js +1 -1
- package/lib/purgecss.js +1 -1
- package/package.json +10 -6
package/lib/purgecss.esm.d.ts
CHANGED
|
@@ -1,94 +1,109 @@
|
|
|
1
1
|
import * as postcss from "postcss";
|
|
2
|
+
type PostCSSRoot = postcss.Root;
|
|
3
|
+
interface AtRules {
|
|
4
|
+
fontFace: Array<{
|
|
5
|
+
name: string;
|
|
6
|
+
node: postcss.AtRule;
|
|
7
|
+
}>;
|
|
8
|
+
keyframes: postcss.AtRule[];
|
|
9
|
+
}
|
|
2
10
|
interface RawContent<T = string> {
|
|
3
|
-
|
|
4
|
-
|
|
11
|
+
extension: string;
|
|
12
|
+
raw: T;
|
|
5
13
|
}
|
|
6
14
|
interface RawCSS {
|
|
7
|
-
|
|
15
|
+
raw: string;
|
|
16
|
+
name?: string;
|
|
8
17
|
}
|
|
9
18
|
interface ExtractorResultDetailed {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
attributes: {
|
|
20
|
+
names: string[];
|
|
21
|
+
values: string[];
|
|
22
|
+
};
|
|
23
|
+
classes: string[];
|
|
24
|
+
ids: string[];
|
|
25
|
+
tags: string[];
|
|
26
|
+
undetermined: string[];
|
|
18
27
|
}
|
|
19
28
|
type ExtractorResult = ExtractorResultDetailed | string[];
|
|
20
29
|
type ExtractorFunction<T = string> = (content: T) => ExtractorResult;
|
|
21
30
|
interface Extractors {
|
|
22
|
-
|
|
23
|
-
|
|
31
|
+
extensions: string[];
|
|
32
|
+
extractor: ExtractorFunction;
|
|
24
33
|
}
|
|
34
|
+
type IgnoreType = "end" | "start" | "next";
|
|
25
35
|
type StringRegExpArray = Array<RegExp | string>;
|
|
26
36
|
type ComplexSafelist = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
37
|
+
standard?: StringRegExpArray;
|
|
38
|
+
deep?: RegExp[];
|
|
39
|
+
greedy?: RegExp[];
|
|
40
|
+
variables?: StringRegExpArray;
|
|
41
|
+
keyframes?: StringRegExpArray;
|
|
32
42
|
};
|
|
33
43
|
type UserDefinedSafelist = StringRegExpArray | ComplexSafelist;
|
|
34
44
|
interface UserDefinedOptions {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
content: Array<string | RawContent>;
|
|
46
|
+
css: Array<string | RawCSS>;
|
|
47
|
+
defaultExtractor?: ExtractorFunction;
|
|
48
|
+
extractors?: Array<Extractors>;
|
|
49
|
+
fontFace?: boolean;
|
|
50
|
+
keyframes?: boolean;
|
|
51
|
+
output?: string;
|
|
52
|
+
rejected?: boolean;
|
|
53
|
+
rejectedCss?: boolean;
|
|
54
|
+
stdin?: boolean;
|
|
55
|
+
stdout?: boolean;
|
|
56
|
+
variables?: boolean;
|
|
57
|
+
safelist?: UserDefinedSafelist;
|
|
58
|
+
blocklist?: StringRegExpArray;
|
|
59
|
+
skippedContentGlobs?: Array<string>;
|
|
60
|
+
dynamicAttributes?: string[];
|
|
48
61
|
}
|
|
49
62
|
interface Options {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
content: Array<string | RawContent>;
|
|
64
|
+
css: Array<string | RawCSS>;
|
|
65
|
+
defaultExtractor: ExtractorFunction;
|
|
66
|
+
extractors: Array<Extractors>;
|
|
67
|
+
fontFace: boolean;
|
|
68
|
+
keyframes: boolean;
|
|
69
|
+
output?: string;
|
|
70
|
+
rejected: boolean;
|
|
71
|
+
rejectedCss: boolean;
|
|
72
|
+
stdin: boolean;
|
|
73
|
+
stdout: boolean;
|
|
74
|
+
variables: boolean;
|
|
75
|
+
safelist: Required<ComplexSafelist>;
|
|
76
|
+
blocklist: StringRegExpArray;
|
|
77
|
+
skippedContentGlobs: Array<string>;
|
|
78
|
+
dynamicAttributes: string[];
|
|
63
79
|
}
|
|
64
80
|
interface ResultPurge {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
css: string;
|
|
82
|
+
rejectedCss?: string;
|
|
83
|
+
file?: string;
|
|
84
|
+
rejected?: string[];
|
|
68
85
|
}
|
|
69
|
-
declare const defaultOptions: Options;
|
|
70
86
|
declare class ExtractorResultSets {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
private undetermined;
|
|
88
|
+
private attrNames;
|
|
89
|
+
private attrValues;
|
|
90
|
+
private classes;
|
|
91
|
+
private ids;
|
|
92
|
+
private tags;
|
|
93
|
+
constructor(er: ExtractorResult);
|
|
94
|
+
merge(that: ExtractorResult | ExtractorResultSets): this;
|
|
95
|
+
hasAttrName(name: string): boolean;
|
|
96
|
+
private someAttrValue;
|
|
97
|
+
hasAttrPrefix(prefix: string): boolean;
|
|
98
|
+
hasAttrSuffix(suffix: string): boolean;
|
|
99
|
+
hasAttrSubstr(substr: string): boolean;
|
|
100
|
+
hasAttrValue(value: string): boolean;
|
|
101
|
+
hasClass(name: string): boolean;
|
|
102
|
+
hasId(id: string): boolean;
|
|
103
|
+
hasTag(tag: string): boolean;
|
|
88
104
|
}
|
|
89
|
-
declare
|
|
90
|
-
|
|
91
|
-
): Required<ComplexSafelist>;
|
|
105
|
+
declare const defaultOptions: Options;
|
|
106
|
+
declare function standardizeSafelist(userDefinedSafelist?: UserDefinedSafelist): Required<ComplexSafelist>;
|
|
92
107
|
/**
|
|
93
108
|
* Load the configuration file from the path
|
|
94
109
|
* @param configFile Path of the config file
|
|
@@ -99,203 +114,184 @@ declare function setOptions(configFile?: string): Promise<Options>;
|
|
|
99
114
|
* @param extractorSelectorsA extractor selectors A
|
|
100
115
|
* @param extractorSelectorsB extractor selectors B
|
|
101
116
|
*/
|
|
102
|
-
declare function mergeExtractorSelectors(
|
|
103
|
-
...extractors: (ExtractorResultDetailed | ExtractorResultSets)[]
|
|
104
|
-
): ExtractorResultSets;
|
|
117
|
+
declare function mergeExtractorSelectors(...extractors: (ExtractorResultDetailed | ExtractorResultSets)[]): ExtractorResultSets;
|
|
105
118
|
declare class PurgeCSS {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
files: string[],
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
* Walk through the CSS AST and remove unused CSS
|
|
284
|
-
* @param root root node of the postcss AST
|
|
285
|
-
* @param selectors selectors used in content files
|
|
286
|
-
*/
|
|
287
|
-
/**
|
|
288
|
-
* Walk through the CSS AST and remove unused CSS
|
|
289
|
-
* @param root root node of the postcss AST
|
|
290
|
-
* @param selectors selectors used in content files
|
|
291
|
-
*/
|
|
292
|
-
walkThroughCSS(root: postcss.Root, selectors: ExtractorResultSets): void;
|
|
119
|
+
private ignore;
|
|
120
|
+
private atRules;
|
|
121
|
+
private usedAnimations;
|
|
122
|
+
private usedFontFaces;
|
|
123
|
+
selectorsRemoved: Set<string>;
|
|
124
|
+
removedNodes: postcss.Node[];
|
|
125
|
+
private variablesStructure;
|
|
126
|
+
options: Options;
|
|
127
|
+
private collectDeclarationsData;
|
|
128
|
+
/**
|
|
129
|
+
* Get the extractor corresponding to the extension file
|
|
130
|
+
* @param filename Name of the file
|
|
131
|
+
* @param extractors Array of extractors definition
|
|
132
|
+
*/
|
|
133
|
+
/**
|
|
134
|
+
* Get the extractor corresponding to the extension file
|
|
135
|
+
* @param filename Name of the file
|
|
136
|
+
* @param extractors Array of extractors definition
|
|
137
|
+
*/
|
|
138
|
+
private getFileExtractor;
|
|
139
|
+
/**
|
|
140
|
+
* Extract the selectors present in the files using a purgecss extractor
|
|
141
|
+
* @param files Array of files path or glob pattern
|
|
142
|
+
* @param extractors Array of extractors
|
|
143
|
+
*/
|
|
144
|
+
/**
|
|
145
|
+
* Extract the selectors present in the files using a purgecss extractor
|
|
146
|
+
* @param files Array of files path or glob pattern
|
|
147
|
+
* @param extractors Array of extractors
|
|
148
|
+
*/
|
|
149
|
+
extractSelectorsFromFiles(files: string[], extractors: Extractors[]): Promise<ExtractorResultSets>;
|
|
150
|
+
/**
|
|
151
|
+
* Extract the selectors present in the passed string using a PurgeCSS extractor
|
|
152
|
+
* @param content Array of content
|
|
153
|
+
* @param extractors Array of extractors
|
|
154
|
+
*/
|
|
155
|
+
/**
|
|
156
|
+
* Extract the selectors present in the passed string using a PurgeCSS extractor
|
|
157
|
+
* @param content Array of content
|
|
158
|
+
* @param extractors Array of extractors
|
|
159
|
+
*/
|
|
160
|
+
extractSelectorsFromString(content: RawContent[], extractors: Extractors[]): Promise<ExtractorResultSets>;
|
|
161
|
+
/**
|
|
162
|
+
* Evaluate at-rule and register it for future reference
|
|
163
|
+
* @param node node of postcss AST
|
|
164
|
+
*/
|
|
165
|
+
/**
|
|
166
|
+
* Evaluate at-rule and register it for future reference
|
|
167
|
+
* @param node node of postcss AST
|
|
168
|
+
*/
|
|
169
|
+
private evaluateAtRule;
|
|
170
|
+
/**
|
|
171
|
+
* Evaluate css selector and decide if it should be removed or not
|
|
172
|
+
* @param node node of postcss AST
|
|
173
|
+
* @param selectors selectors used in content files
|
|
174
|
+
*/
|
|
175
|
+
/**
|
|
176
|
+
* Evaluate css selector and decide if it should be removed or not
|
|
177
|
+
* @param node node of postcss AST
|
|
178
|
+
* @param selectors selectors used in content files
|
|
179
|
+
*/
|
|
180
|
+
private evaluateRule;
|
|
181
|
+
/**
|
|
182
|
+
* Get the purged version of the css based on the files
|
|
183
|
+
* @param cssOptions css options, files or raw strings
|
|
184
|
+
* @param selectors set of extracted css selectors
|
|
185
|
+
*/
|
|
186
|
+
/**
|
|
187
|
+
* Get the purged version of the css based on the files
|
|
188
|
+
* @param cssOptions css options, files or raw strings
|
|
189
|
+
* @param selectors set of extracted css selectors
|
|
190
|
+
*/
|
|
191
|
+
getPurgedCSS(cssOptions: Array<string | RawCSS>, selectors: ExtractorResultSets): Promise<ResultPurge[]>;
|
|
192
|
+
/**
|
|
193
|
+
* Check if the keyframe is safelisted with the option safelist keyframes
|
|
194
|
+
* @param keyframesName name of the keyframe animation
|
|
195
|
+
*/
|
|
196
|
+
/**
|
|
197
|
+
* Check if the keyframe is safelisted with the option safelist keyframes
|
|
198
|
+
* @param keyframesName name of the keyframe animation
|
|
199
|
+
*/
|
|
200
|
+
private isKeyframesSafelisted;
|
|
201
|
+
/**
|
|
202
|
+
* Check if the selector is blocklisted with the option blocklist
|
|
203
|
+
* @param selector css selector
|
|
204
|
+
*/
|
|
205
|
+
/**
|
|
206
|
+
* Check if the selector is blocklisted with the option blocklist
|
|
207
|
+
* @param selector css selector
|
|
208
|
+
*/
|
|
209
|
+
private isSelectorBlocklisted;
|
|
210
|
+
/**
|
|
211
|
+
* Check if the selector is safelisted with the option safelist standard
|
|
212
|
+
* @param selector css selector
|
|
213
|
+
*/
|
|
214
|
+
/**
|
|
215
|
+
* Check if the selector is safelisted with the option safelist standard
|
|
216
|
+
* @param selector css selector
|
|
217
|
+
*/
|
|
218
|
+
private isSelectorSafelisted;
|
|
219
|
+
/**
|
|
220
|
+
* Check if the selector is safelisted with the option safelist deep
|
|
221
|
+
* @param selector selector
|
|
222
|
+
*/
|
|
223
|
+
/**
|
|
224
|
+
* Check if the selector is safelisted with the option safelist deep
|
|
225
|
+
* @param selector selector
|
|
226
|
+
*/
|
|
227
|
+
private isSelectorSafelistedDeep;
|
|
228
|
+
/**
|
|
229
|
+
* Check if the selector is safelisted with the option safelist greedy
|
|
230
|
+
* @param selector selector
|
|
231
|
+
*/
|
|
232
|
+
/**
|
|
233
|
+
* Check if the selector is safelisted with the option safelist greedy
|
|
234
|
+
* @param selector selector
|
|
235
|
+
*/
|
|
236
|
+
private isSelectorSafelistedGreedy;
|
|
237
|
+
/**
|
|
238
|
+
* Remove unused css
|
|
239
|
+
* @param userOptions PurgeCSS options
|
|
240
|
+
*/
|
|
241
|
+
/**
|
|
242
|
+
* Remove unused css
|
|
243
|
+
* @param userOptions PurgeCSS options
|
|
244
|
+
*/
|
|
245
|
+
purge(userOptions: UserDefinedOptions | string | undefined): Promise<ResultPurge[]>;
|
|
246
|
+
/**
|
|
247
|
+
* Remove unused CSS variables
|
|
248
|
+
*/
|
|
249
|
+
/**
|
|
250
|
+
* Remove unused CSS variables
|
|
251
|
+
*/
|
|
252
|
+
removeUnusedCSSVariables(): void;
|
|
253
|
+
/**
|
|
254
|
+
* Remove unused font-faces
|
|
255
|
+
*/
|
|
256
|
+
/**
|
|
257
|
+
* Remove unused font-faces
|
|
258
|
+
*/
|
|
259
|
+
removeUnusedFontFaces(): void;
|
|
260
|
+
/**
|
|
261
|
+
* Remove unused keyframes
|
|
262
|
+
*/
|
|
263
|
+
/**
|
|
264
|
+
* Remove unused keyframes
|
|
265
|
+
*/
|
|
266
|
+
removeUnusedKeyframes(): void;
|
|
267
|
+
/**
|
|
268
|
+
* Transform a selector node into a string
|
|
269
|
+
*/
|
|
270
|
+
/**
|
|
271
|
+
* Transform a selector node into a string
|
|
272
|
+
*/
|
|
273
|
+
private getSelectorValue;
|
|
274
|
+
/**
|
|
275
|
+
* Determine if the selector should be kept, based on the selectors found in the files
|
|
276
|
+
* @param selector set of css selectors found in the content files or string
|
|
277
|
+
* @param selectorsFromExtractor selectors in the css rule
|
|
278
|
+
*/
|
|
279
|
+
/**
|
|
280
|
+
* Determine if the selector should be kept, based on the selectors found in the files
|
|
281
|
+
* @param selector set of css selectors found in the content files or string
|
|
282
|
+
* @param selectorsFromExtractor selectors in the css rule
|
|
283
|
+
*/
|
|
284
|
+
private shouldKeepSelector;
|
|
285
|
+
/**
|
|
286
|
+
* Walk through the CSS AST and remove unused CSS
|
|
287
|
+
* @param root root node of the postcss AST
|
|
288
|
+
* @param selectors selectors used in content files
|
|
289
|
+
*/
|
|
290
|
+
/**
|
|
291
|
+
* Walk through the CSS AST and remove unused CSS
|
|
292
|
+
* @param root root node of the postcss AST
|
|
293
|
+
* @param selectors selectors used in content files
|
|
294
|
+
*/
|
|
295
|
+
walkThroughCSS(root: PostCSSRoot, selectors: ExtractorResultSets): void;
|
|
293
296
|
}
|
|
294
|
-
export {
|
|
295
|
-
PurgeCSS as default,
|
|
296
|
-
PurgeCSS,
|
|
297
|
-
defaultOptions,
|
|
298
|
-
standardizeSafelist,
|
|
299
|
-
setOptions,
|
|
300
|
-
mergeExtractorSelectors,
|
|
301
|
-
};
|
|
297
|
+
export { PurgeCSS as default, PurgeCSS, defaultOptions, PostCSSRoot, AtRules, RawContent, RawCSS, ExtractorResultDetailed, ExtractorResult, ExtractorFunction, Extractors, IgnoreType, StringRegExpArray, ComplexSafelist, UserDefinedSafelist, UserDefinedOptions, Options, ResultPurge, standardizeSafelist, setOptions, mergeExtractorSelectors };
|
package/lib/purgecss.esm.js
CHANGED
|
@@ -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 a}from"util";import n from"glob";import o 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,safelist:{standard:[],deep:[],greedy:[],variables:[],keyframes:[]},blocklist:[]},l=["*","::-webkit-scrollbar","::selection",":root","::before","::after"];class u{constructor(e){this.nodes=[],this.isUsed=!1,this.value=e}}class h{constructor(){this.nodes=new Map,this.usedVariables=new Set,this.safelist=[]}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,t]of this.nodes)t.isUsed||this.isVariablesSafelisted(e)||t.value.remove()}isVariablesSafelisted(e){return this.safelist.some((t=>"string"==typeof t?t===e:t.test(e)))}}function d(e,t){t&&t.forEach(e.add,e)}class f{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)?d(this.undetermined,e):e instanceof f?(d(this.undetermined,e.undetermined),d(this.attrNames,e.attrNames),d(this.attrValues,e.attrValues),d(this.classes,e.classes),d(this.ids,e.ids),d(this.tags,e.tags)):(d(this.undetermined,e.undetermined),e.attributes&&(d(this.attrNames,e.attributes.names),d(this.attrValues,e.attributes.values)),d(this.classes,e.classes),d(this.ids,e.ids),d(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 e.trim().split(" ").every((e=>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 m={access:a(s),readFile:a(r)};function p(e=[]){return Array.isArray(e)?{...c.safelist,standard:e}:{...c.safelist,...e}}async function g(e="purgecss.config.js"){let t;try{const s=o.join(process.cwd(),e);t=await import(s)}catch(e){throw new Error("Error loading the config file "+e.message)}return{...c,...t,safelist:p(t.safelist)}}async function v(e,t){return new f(await t(e))}function y(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 S(...e){const t=new f([]);return e.forEach(t.merge,t),t}function b(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 F(e,t){return t.hasId(e.value)}function A(e,t){return t.hasTag(e.value)}function V(e){return"atrule"===(null==e?void 0:e.type)}function x(e){return"rule"===(null==e?void 0:e.type)}class k{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.variablesStructure=new h,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=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=b(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 f([]);for(const r of e){let e=[];try{await m.access(r,i.F_OK),e.push(r)}catch(t){e=n.sync(r,{nodir:!0})}for(const r of e){const e=await m.readFile(r,"utf-8"),i=this.getFileExtractor(r,t),a=await v(e,i);s.merge(a)}}return s}async extractSelectorsFromString(e,t){const s=new f([]);for(const{raw:r,extension:i}of e){const e=this.getFileExtractor("."+i,t),a=await v(r,e);s.merge(a)}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:b(t.value),node:e})}async evaluateRule(e,s){if(this.ignore)return;const r=e.prev();if(function(e){return"comment"===(null==e?void 0:e.type)}(r)&&y(r,"next"))return void r.remove();if(e.parent&&V(e.parent)&&"keyframes"===e.parent.name)return;if(!x(e))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 a=e.parent;e.selector||e.remove(),function(e){return!!(x(e)&&!e.selector||(null==e?void 0:e.nodes)&&!e.nodes.length||V(e)&&(!e.nodes&&!e.params||!e.params&&e.nodes&&!e.nodes.length))}(a)&&(null==a||a.remove())}async getPurgedCSS(t,s){const r=[],i=[];for(const e of t)"string"==typeof e?i.push(...n.sync(e,{nodir:!0})):i.push(e);for(const t of i){const i="string"==typeof t?this.options.stdin?t:await m.readFile(t,"utf-8"):t.raw,a=e(i);this.walkThroughCSS(a,s),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const n={css:a.toString(),file:"string"==typeof t?t:void 0};"string"==typeof t&&(n.file=t),this.options.rejected&&(n.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),r.push(n)}return r}isKeyframesSafelisted(e){return this.options.safelist.keyframes.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorBlocklisted(e){return this.options.blocklist.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorSafelisted(e){const t=this.options.safelist.standard.some((t=>"string"==typeof t?t===e:t.test(e)));return l.includes(e)||t}isSelectorSafelistedDeep(e){return this.options.safelist.deep.some((t=>t.test(e)))}isSelectorSafelistedGreedy(e){return this.options.safelist.greedy.some((t=>t.test(e)))}async purge(e){this.options="object"!=typeof e?await g(e):{...c,...e,safelist:p(e.safelist)};const{content:t,css:s,extractors:r,safelist:i}=this.options;this.options.variables&&(this.variablesStructure.safelist=i.variables||[]);const a=t.filter((e=>"string"==typeof e)),n=t.filter((e=>"object"==typeof e)),o=await this.extractSelectorsFromFiles(a,r),l=await this.extractSelectorsFromString(n,r);return this.getPurgedCSS(s,S(o,l))}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)||this.isKeyframesSafelisted(e.params)||e.remove()}getSelectorValue(e){return"attribute"===e.type&&e.attribute||e.value}shouldKeepSelector(e,t){if(function(e){return e.parent&&"pseudo"===e.parent.type&&e.parent.value.startsWith(":")||!1}(e))return!0;if(this.options.safelist.greedy.length>0){if(e.nodes.map(this.getSelectorValue).some((e=>e&&this.isSelectorSafelistedGreedy(e))))return!0}let s=!1;for(const i of e.nodes){const e=this.getSelectorValue(i);if(e&&this.isSelectorSafelistedDeep(e))return!0;if(e&&(l.includes(e)||this.isSelectorSafelisted(e)))s=!0;else{if(e&&this.isSelectorBlocklisted(e))return!1;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=F(i,t);break;case"tag":s=A(i,t);break;default:continue}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&&(y(e,"start")?(this.ignore=!0,e.remove()):y(e,"end")&&(this.ignore=!1,e.remove())))))}}export default k;export{k as PurgeCSS,c as defaultOptions,S as mergeExtractorSelectors,g as setOptions,p as standardizeSafelist};
|
|
1
|
+
import*as e from"fs";import t from"glob";import s from"path";import*as r from"postcss";import i from"postcss-selector-parser";import{promisify as o}from"util";function a(e,t){t&&t.forEach(e.add,e)}class n{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)?a(this.undetermined,e):e instanceof n?(a(this.undetermined,e.undetermined),a(this.attrNames,e.attrNames),a(this.attrValues,e.attrValues),a(this.classes,e.classes),a(this.ids,e.ids),a(this.tags,e.tags)):(a(this.undetermined,e.undetermined),e.attributes&&(a(this.attrNames,e.attributes.names),a(this.attrValues,e.attributes.values)),a(this.classes,e.classes),a(this.ids,e.ids),a(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 e.trim().split(" ").every((e=>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 c=["*","::-webkit-scrollbar","::selection",":root","::before","::after"],l={css:[],content:[],defaultExtractor:e=>e.match(/[A-Za-z0-9_-]+/g)||[],extractors:[],fontFace:!1,keyframes:!1,rejected:!1,rejectedCss:!1,stdin:!1,stdout:!1,variables:!1,safelist:{standard:[],deep:[],greedy:[],variables:[],keyframes:[]},blocklist:[],skippedContentGlobs:[],dynamicAttributes:[]};function u(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}class d{constructor(e){this.nodes=[],this.isUsed=!1,this.value=e}}class h{constructor(){this.nodes=new Map,this.usedVariables=new Set,this.safelist=[]}addVariable(e){const{prop:t}=e;if(!this.nodes.has(t)){const s=new d(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){const t=this.nodes.get(e);if(t){u(t.value.value,/var\((.+?)[,)]/g).forEach((e=>{this.usedVariables.has(e[1])||this.usedVariables.add(e[1])}))}}for(const e of this.usedVariables)this.setAsUsed(e);for(const[e,t]of this.nodes)t.isUsed||this.isVariablesSafelisted(e)||t.value.remove()}isVariablesSafelisted(e){return this.safelist.some((t=>"string"==typeof t?t===e:t.test(e)))}}const f={access:o(e.access),readFile:o(e.readFile)};function p(e=[]){return Array.isArray(e)?{...l.safelist,standard:e}:{...l.safelist,...e}}async function m(e="purgecss.config.js"){let t;try{const r=s.resolve(process.cwd(),e);t=await import(r)}catch(e){throw new Error(`Error loading the config file ${e.message}`)}return{...l,...t,safelist:p(t.safelist)}}async function g(e,t){return new n(await t(e))}function v(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 y(...e){const t=new n([]);return e.forEach(t.merge,t),t}function S(e){return e.replace(/(^["'])|(["']$)/g,"")}function b(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 w(e,t){return t.hasId(e.value)}function A(e,t){return t.hasTag(e.value)}function F(e){return"atrule"===(null==e?void 0:e.type)}function V(e){return"rule"===(null==e?void 0:e.type)}class k{constructor(){this.ignore=!1,this.atRules={fontFace:[],keyframes:[]},this.usedAnimations=new Set,this.usedFontFaces=new Set,this.selectorsRemoved=new Set,this.removedNodes=[],this.variablesStructure=new h,this.options=l}collectDeclarationsData(e){const{prop:t,value:s}=e;if(this.options.variables){const r=u(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=S(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(s,r){const i=new n([]);for(const o of s){let s=[];try{await f.access(o,e.constants.F_OK),s.push(o)}catch(e){s=t.sync(o,{nodir:!0,ignore:this.options.skippedContentGlobs})}for(const e of s){const t=await f.readFile(e,"utf-8"),s=this.getFileExtractor(e,r),o=await g(t,s);i.merge(o)}}return i}async extractSelectorsFromString(e,t){const s=new n([]);for(const{raw:r,extension:i}of e){const e=this.getFileExtractor(`.${i}`,t),o=await g(r,e);s.merge(o)}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:S(t.value),node:e})}evaluateRule(e,t){if(this.ignore)return;const s=e.prev();if(function(e){return"comment"===(null==e?void 0:e.type)}(s)&&v(s,"next"))return void s.remove();if(e.parent&&F(e.parent)&&e.parent.name.endsWith("keyframes"))return;if(!V(e))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 r=!0;const o=[];if(e.selector=i((e=>{e.walk((e=>{"selector"===e.type&&(r=this.shouldKeepSelector(e,t),r||(this.options.rejected&&this.selectorsRemoved.add(e.toString()),this.options.rejectedCss&&o.push(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 a=e.parent;if(e.selector||e.remove(),function(e){return!!(V(e)&&!e.selector||(null==e?void 0:e.nodes)&&!e.nodes.length||F(e)&&(!e.nodes&&!e.params||!e.params&&e.nodes&&!e.nodes.length))}(a)&&(null==a||a.remove()),this.options.rejectedCss&&o.length>0){const t=e.clone(),s=null==a?void 0:a.clone().removeAll().append(t);t.selectors=o;const r=s||t;this.removedNodes.push(r)}}async getPurgedCSS(e,s){const i=[],o=[];for(const s of e)"string"==typeof s?o.push(...t.sync(s,{nodir:!0,ignore:this.options.skippedContentGlobs})):o.push(s);for(const e of o){const t="string"==typeof e?this.options.stdin?e:await f.readFile(e,"utf-8"):e.raw,o=r.parse(t);this.walkThroughCSS(o,s),this.options.fontFace&&this.removeUnusedFontFaces(),this.options.keyframes&&this.removeUnusedKeyframes(),this.options.variables&&this.removeUnusedCSSVariables();const a={css:o.toString(),file:"string"==typeof e?e:e.name};this.options.rejected&&(a.rejected=Array.from(this.selectorsRemoved),this.selectorsRemoved.clear()),this.options.rejectedCss&&(a.rejectedCss=r.root({nodes:this.removedNodes}).toString()),i.push(a)}return i}isKeyframesSafelisted(e){return this.options.safelist.keyframes.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorBlocklisted(e){return this.options.blocklist.some((t=>"string"==typeof t?t===e:t.test(e)))}isSelectorSafelisted(e){const t=this.options.safelist.standard.some((t=>"string"==typeof t?t===e:t.test(e)));return c.includes(e)||t}isSelectorSafelistedDeep(e){return this.options.safelist.deep.some((t=>t.test(e)))}isSelectorSafelistedGreedy(e){return this.options.safelist.greedy.some((t=>t.test(e)))}async purge(e){this.options="object"!=typeof e?await m(e):{...l,...e,safelist:p(e.safelist)};const{content:t,css:s,extractors:r,safelist:i}=this.options;this.options.variables&&(this.variablesStructure.safelist=i.variables||[]);const o=t.filter((e=>"string"==typeof e)),a=t.filter((e=>"object"==typeof e)),n=await this.extractSelectorsFromFiles(o,r),c=await this.extractSelectorsFromString(a,r);return this.getPurgedCSS(s,y(n,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)||this.isKeyframesSafelisted(e.params)||e.remove()}getSelectorValue(e){return"attribute"===e.type&&e.attribute||e.value}shouldKeepSelector(e,t){if(function(e){return e.parent&&"pseudo"===e.parent.type&&e.parent.value.startsWith(":")||!1}(e))return!0;if(this.options.safelist.greedy.length>0){if(e.nodes.map(this.getSelectorValue).some((e=>e&&this.isSelectorSafelistedGreedy(e))))return!0}let s=!1;for(const i of e.nodes){const e=this.getSelectorValue(i);if(e&&this.isSelectorSafelistedDeep(e))return!0;if(e&&(c.includes(e)||this.isSelectorSafelisted(e)))s=!0;else{if(e&&this.isSelectorBlocklisted(e))return!1;switch(i.type){case"attribute":s=!![...this.options.dynamicAttributes,"value","checked","selected","open"].includes(i.attribute)||b(i,t);break;case"class":r=i,s=t.hasClass(r.value);break;case"id":s=w(i,t);break;case"tag":s=A(i,t);break;default:continue}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&&(v(e,"start")?(this.ignore=!0,e.remove()):v(e,"end")&&(this.ignore=!1,e.remove())))))}}export{k as PurgeCSS,k as default,l as defaultOptions,y as mergeExtractorSelectors,m as setOptions,p as standardizeSafelist};
|