forked from VladislavStrelchenko/brotli-webpack-plugin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
117 lines (97 loc) · 3.34 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// @ts-check
const brotliCompress = require('./compress').compress;
const parsePath = require('path').parse;
/**
* @typedef {import('zlib').BrotliOptions & Parameters<import('brotli')['compress']>['1']} BrotliLibsOptions
*/
/**
* @typedef {Object} BrotliPluginOptions
* @property {string} outputFormat Format of the output file. Check {@link https://nodejs.org/api/path.html#pathparsepath} for available properties
* @property {number} minSizeInBytes
* @property {number} minCompressionRatio
* @property {boolean} deleteOriginalAssets
* @property {RegExp | undefined} excludeRegex
*/
class BrotliPlugin {
/** @type {BrotliPluginOptions & BrotliLibsOptions} */
static defaultOptions = {
outputFormat: '[base].br',
minSizeInBytes: 0,
minCompressionRatio: 0.8,
deleteOriginalAssets: false,
excludeRegex: new RegExp(/^.*?(?<!gz|br|woff2)$/m),
// Brotli default options
mode: 0,
quality: 11,
lgwin: 22,
};
/**
* @param {Partial<BrotliPluginOptions>} options
*/
constructor(options = {}) {
this.options = { ...BrotliPlugin.defaultOptions, ...options };
}
/**
*
* @param {import('webpack').Compiler} compiler
*/
apply(compiler) {
const pluginName = BrotliPlugin.name;
// webpack module instance can be accessed from the compiler object,
// this ensures that correct version of the module is used
// (do not require/import the webpack or any symbols from it directly).
const { webpack } = compiler;
// Compilation object gives us reference to some useful constants.
const { Compilation } = webpack;
// RawSource is one of the "sources" classes that should be used
// to represent asset sources in compilation.
const { RawSource } = webpack.sources;
// Tapping to the "thisCompilation" hook in order to further tap
// to the compilation process on an earlier stage.
compiler.hooks.thisCompilation.tap(pluginName, (compilation) => {
// Tapping to the assets processing pipeline on a specific stage.
compilation.hooks.processAssets.tapPromise(
{
name: pluginName,
// Using one of the later asset processing stages to ensure
// that all assets were already added to the compilation by other plugins.
stage: Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_TRANSFER,
},
async (assets) => {
Promise.all(
Object.keys(assets).map(async (filename) => {
if (
this.options.excludeRegex &&
!this.options.excludeRegex.test(filename)
)
return;
const asset = compilation.getAsset(filename);
if (!asset) return;
let rawContent = asset.source.buffer();
if (!Buffer.isBuffer(rawContent)) return;
if (rawContent.length < this.options.minSizeInBytes) return;
const compressedResult = await brotliCompress(rawContent, this.options);
if (
compressedResult.length / rawContent.length >
this.options.minCompressionRatio
)
return;
const parsedFilename = parsePath(filename);
const compressedFileName = this.options.outputFormat.replace(
/\[(base|name|ext)]/g,
(_, part) => parsedFilename[part]
);
if (this.options.deleteOriginalAssets)
compilation.deleteAsset(filename);
compilation.emitAsset(
compressedFileName,
new RawSource(compressedResult)
);
})
);
}
);
});
}
}
module.exports = BrotliPlugin;