@@ -11,9 +11,83 @@ import serialize from "serialize-javascript";
11
11
12
12
import schema from "./options.json" ;
13
13
14
+ /** @typedef {import("schema-utils/declarations/validate").Schema } Schema */
15
+ /** @typedef {import("webpack").Compiler } Compiler */
16
+ /** @typedef {import("webpack").Compilation } Compilation */
17
+ /** @typedef {import("webpack").sources.Source } Source */
18
+ /** @typedef {import("webpack").Asset } Asset */
19
+ /** @typedef {import("webpack").WebpackError } WebpackError */
20
+
21
+ /** @typedef {RegExp | string } Rule */
22
+
23
+ /** @typedef {Rule[] | Rule } Rules */
24
+
25
+ /**
26
+ * @typedef {{ [key: string]: any } } CustomOptions
27
+ */
28
+
29
+ /**
30
+ * @template T
31
+ * @typedef {T extends infer U ? U : CustomOptions } InferDefaultType
32
+ */
33
+
34
+ /**
35
+ * @template T
36
+ * @typedef {InferDefaultType<T> } CompressionOptions
37
+ */
38
+
39
+ /**
40
+ * @template T
41
+ * @callback AlgorithmFunction
42
+ * @param {Buffer } input
43
+ * @param {CompressionOptions<T> } options
44
+ * @param {(error: Error, result: string | Buffer) => void } callback
45
+ */
46
+
47
+ /**
48
+ * @typedef {{[key: string]: any} } PathData
49
+ */
50
+
51
+ /**
52
+ * @typedef {string | ((fileData: PathData) => string) } Filename
53
+ */
54
+
55
+ /**
56
+ * @typedef {boolean | "keep-source-map" } DeleteOriginalAssets
57
+ */
58
+
59
+ /**
60
+ * @template T
61
+ * @typedef {Object } BasePluginOptions
62
+ * @property {Rules } [test]
63
+ * @property {Rules } [include]
64
+ * @property {Rules } [exclude]
65
+ * @property {string | AlgorithmFunction<T> } [algorithm]
66
+ * @property {CompressionOptions<T> } [compressionOptions]
67
+ * @property {number } [threshold]
68
+ * @property {number } [minRatio]
69
+ * @property {DeleteOriginalAssets } [deleteOriginalAssets]
70
+ * @property {Filename } [filename]
71
+ */
72
+
73
+ /**
74
+ * @template T
75
+ * @typedef {BasePluginOptions<T> & { compressionOptions: CompressionOptions<T>, threshold: number, minRatio: number, deleteOriginalAssets: DeleteOriginalAssets, filename: Filename } } InternalPluginOptions
76
+ */
77
+
78
+ /**
79
+ * @typedef {import("zlib").ZlibOptions } ZlibOptions
80
+ */
81
+
82
+ /**
83
+ * @template [T=ZlibOptions]
84
+ */
14
85
class CompressionPlugin {
86
+ /**
87
+ * @param {BasePluginOptions<T> } [options]
88
+ */
15
89
constructor ( options = { } ) {
16
- validate ( schema , options , {
90
+ validate ( /** @type { Schema } */ ( schema ) , options , {
17
91
name : "Compression Plugin" ,
18
92
baseDataPath : "options" ,
19
93
} ) ;
@@ -23,13 +97,17 @@ class CompressionPlugin {
23
97
include,
24
98
exclude,
25
99
algorithm = "gzip" ,
26
- compressionOptions = { } ,
100
+ compressionOptions = /** @type { CompressionOptions<T> } */ ( { } ) ,
27
101
filename = "[path][base].gz" ,
28
102
threshold = 0 ,
29
103
minRatio = 0.8 ,
30
104
deleteOriginalAssets = false ,
31
105
} = options ;
32
106
107
+ /**
108
+ * @private
109
+ * @type {InternalPluginOptions<T> }
110
+ */
33
111
this . options = {
34
112
test,
35
113
include,
@@ -42,12 +120,25 @@ class CompressionPlugin {
42
120
deleteOriginalAssets,
43
121
} ;
44
122
45
- this . algorithm = this . options . algorithm ;
123
+ /**
124
+ * @private
125
+ * @type {AlgorithmFunction<T> }
126
+ */
127
+ this . algorithm =
128
+ /** @type {AlgorithmFunction<T> } */
129
+ ( this . options . algorithm ) ;
46
130
47
131
if ( typeof this . algorithm === "string" ) {
132
+ /**
133
+ * @type {typeof import("zlib") }
134
+ */
48
135
// eslint-disable-next-line global-require
49
136
const zlib = require ( "zlib" ) ;
50
137
138
+ /**
139
+ * @private
140
+ * @type {AlgorithmFunction<T> }
141
+ */
51
142
this . algorithm = zlib [ this . algorithm ] ;
52
143
53
144
if ( ! this . algorithm ) {
@@ -73,42 +164,62 @@ class CompressionPlugin {
73
164
zlib . constants . BROTLI_MAX_QUALITY ,
74
165
} ,
75
166
} ,
76
- } [ algorithm ] || { } ;
77
-
78
- this . options . compressionOptions = {
79
- ...defaultCompressionOptions ,
80
- ...this . options . compressionOptions ,
81
- } ;
167
+ } [ /** @type {string } */ ( algorithm ) ] || { } ;
168
+
169
+ this . options . compressionOptions =
170
+ /**
171
+ * @type {CompressionOptions<T> }
172
+ */
173
+ ( {
174
+ .../** @type {object } */ ( defaultCompressionOptions ) ,
175
+ .../** @type {object } */ ( this . options . compressionOptions ) ,
176
+ } ) ;
82
177
}
83
178
}
84
179
180
+ /**
181
+ * @private
182
+ * @param {Buffer } input
183
+ * @returns {Promise<Buffer> }
184
+ */
85
185
runCompressionAlgorithm ( input ) {
86
186
return new Promise ( ( resolve , reject ) => {
87
187
this . algorithm (
88
188
input ,
89
189
this . options . compressionOptions ,
90
190
( error , result ) => {
91
191
if ( error ) {
92
- return reject ( error ) ;
192
+ reject ( error ) ;
193
+
194
+ return ;
93
195
}
94
196
95
197
if ( ! Buffer . isBuffer ( result ) ) {
96
198
// eslint-disable-next-line no-param-reassign
97
199
result = Buffer . from ( result ) ;
98
200
}
99
201
100
- return resolve ( result ) ;
202
+ resolve ( result ) ;
101
203
}
102
204
) ;
103
205
} ) ;
104
206
}
105
207
208
+ /**
209
+ * @private
210
+ * @param {Compiler } compiler
211
+ * @param {Compilation } compilation
212
+ * @param {Record<string, Source> } assets
213
+ * @returns {Promise<void> }
214
+ */
106
215
async compress ( compiler , compilation , assets ) {
107
216
const cache = compilation . getCache ( "CompressionWebpackPlugin" ) ;
108
217
const assetsForMinify = (
109
218
await Promise . all (
110
219
Object . keys ( assets ) . map ( async ( name ) => {
111
- const { info, source } = compilation . getAsset ( name ) ;
220
+ const { info, source } = /** @type {Asset } */ (
221
+ compilation . getAsset ( name )
222
+ ) ;
112
223
113
224
if ( info . compressed ) {
114
225
return false ;
@@ -124,6 +235,9 @@ class CompressionPlugin {
124
235
return false ;
125
236
}
126
237
238
+ /**
239
+ * @type {string | undefined }
240
+ */
127
241
let relatedName ;
128
242
129
243
if ( typeof this . options . algorithm === "function" ) {
@@ -133,6 +247,9 @@ class CompressionPlugin {
133
247
. update ( serialize ( this . options . filename ) )
134
248
. digest ( "hex" ) } `;
135
249
} else {
250
+ /**
251
+ * @type {string }
252
+ */
136
253
let filenameForRelatedName = this . options . filename ;
137
254
138
255
const index = filenameForRelatedName . indexOf ( "?" ) ;
@@ -202,6 +319,7 @@ class CompressionPlugin {
202
319
for ( const asset of assetsForMinify ) {
203
320
scheduledTasks . push (
204
321
( async ( ) => {
322
+ // @ts -ignore
205
323
const { name, source, buffer, output, cacheItem, info, relatedName } =
206
324
asset ;
207
325
@@ -210,7 +328,7 @@ class CompressionPlugin {
210
328
try {
211
329
output . compressed = await this . runCompressionAlgorithm ( buffer ) ;
212
330
} catch ( error ) {
213
- compilation . errors . push ( error ) ;
331
+ compilation . errors . push ( /** @type { WebpackError } */ ( error ) ) ;
214
332
215
333
return ;
216
334
}
@@ -235,16 +353,21 @@ class CompressionPlugin {
235
353
} ) ;
236
354
const newInfo = { compressed : true } ;
237
355
356
+ // TODO: possible problem when developer uses custom function, ideally we need to get parts of filname (i.e. name/base/ext/etc) in info
357
+ // otherwise we can't detect an asset as immutable
238
358
if (
239
359
info . immutable &&
360
+ typeof this . options . filename === "string" &&
240
361
/ ( \[ n a m e ] | \[ b a s e ] | \[ f i l e ] ) / . test ( this . options . filename )
241
362
) {
363
+ // @ts -ignore
242
364
newInfo . immutable = true ;
243
365
}
244
366
245
367
if ( this . options . deleteOriginalAssets ) {
246
368
if ( this . options . deleteOriginalAssets === "keep-source-map" ) {
247
369
compilation . updateAsset ( name , source , {
370
+ // @ts -ignore
248
371
related : { sourceMap : null } ,
249
372
} ) ;
250
373
}
@@ -261,9 +384,13 @@ class CompressionPlugin {
261
384
) ;
262
385
}
263
386
264
- return Promise . all ( scheduledTasks ) ;
387
+ await Promise . all ( scheduledTasks ) ;
265
388
}
266
389
390
+ /**
391
+ * @param {Compiler } compiler
392
+ * @returns {void }
393
+ */
267
394
apply ( compiler ) {
268
395
const pluginName = this . constructor . name ;
269
396
@@ -284,8 +411,11 @@ class CompressionPlugin {
284
411
. tap (
285
412
"compression-webpack-plugin" ,
286
413
( compressed , { green, formatFlag } ) =>
287
- // eslint-disable-next-line no-undefined
288
- compressed ? green ( formatFlag ( "compressed" ) ) : undefined
414
+ compressed
415
+ ? /** @type {Function } */ ( green ) (
416
+ /** @type {Function } */ ( formatFlag ) ( "compressed" )
417
+ )
418
+ : ""
289
419
) ;
290
420
} ) ;
291
421
} ) ;
0 commit comments