UNPKG

6.84 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7
8var _os = _interopRequireDefault(require("os"));
9
10var _schemaUtils = require("schema-utils");
11
12var _serializeJavascript = _interopRequireDefault(require("serialize-javascript"));
13
14var _pLimit = _interopRequireDefault(require("p-limit"));
15
16var _jestWorker = require("jest-worker");
17
18var _options = _interopRequireDefault(require("./options.json"));
19
20var _utils = require("./utils");
21
22var _minify = require("./minify");
23
24function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
26class HtmlMinimizerPlugin {
27 constructor(options = {}) {
28 (0, _schemaUtils.validate)(_options.default, options, {
29 name: "Html Minimizer Plugin",
30 baseDataPath: "options"
31 });
32 const {
33 minify = _utils.htmlMinifierTerser,
34 minimizerOptions,
35 test = /\.html(\?.*)?$/i,
36 parallel = true,
37 include,
38 exclude
39 } = options;
40 this.options = {
41 test,
42 parallel,
43 include,
44 exclude,
45 minify,
46 minimizerOptions
47 };
48 }
49
50 static buildError(error, file, context) {
51 return new Error(`${file} in "${context}" from Html Minimizer\n${error.stack}`);
52 }
53
54 static getAvailableNumberOfCores(parallel) {
55 // In some cases cpus() returns undefined
56 // https://github.com/nodejs/node/issues/19022
57 const cpus = _os.default.cpus() || {
58 length: 1
59 };
60 return parallel === true ? cpus.length - 1 : Math.min(Number(parallel) || 0, cpus.length - 1);
61 }
62
63 async optimize(compiler, compilation, assets, optimizeOptions) {
64 const cache = compilation.getCache("HtmlMinimizerWebpackPlugin");
65 let numberOfAssetsForMinify = 0;
66 const assetsForMinify = await Promise.all(Object.keys(assets).filter(name => {
67 const {
68 info
69 } = compilation.getAsset(name); // Skip double minimize assets from child compilation
70
71 if (info.minimized) {
72 return false;
73 }
74
75 if (!compiler.webpack.ModuleFilenameHelpers.matchObject.bind( // eslint-disable-next-line no-undefined
76 undefined, this.options)(name)) {
77 return false;
78 }
79
80 return true;
81 }).map(async name => {
82 const {
83 info,
84 source
85 } = compilation.getAsset(name);
86 const eTag = cache.getLazyHashedEtag(source);
87 const cacheItem = cache.getItemCache(name, eTag);
88 const output = await cacheItem.getPromise();
89
90 if (!output) {
91 numberOfAssetsForMinify += 1;
92 }
93
94 return {
95 name,
96 info,
97 inputSource: source,
98 output,
99 cacheItem
100 };
101 }));
102 let getWorker;
103 let initializedWorker;
104 let numberOfWorkers;
105
106 if (optimizeOptions.availableNumberOfCores > 0) {
107 // Do not create unnecessary workers when the number of files is less than the available cores, it saves memory
108 numberOfWorkers = Math.min(numberOfAssetsForMinify, optimizeOptions.availableNumberOfCores); // eslint-disable-next-line consistent-return
109
110 getWorker = () => {
111 if (initializedWorker) {
112 return initializedWorker;
113 }
114
115 initializedWorker = new _jestWorker.Worker(require.resolve("./minify"), {
116 numWorkers: numberOfWorkers,
117 enableWorkerThreads: true
118 }); // https://github.com/facebook/jest/issues/8872#issuecomment-524822081
119
120 const workerStdout = initializedWorker.getStdout();
121
122 if (workerStdout) {
123 workerStdout.on("data", chunk => process.stdout.write(chunk));
124 }
125
126 const workerStderr = initializedWorker.getStderr();
127
128 if (workerStderr) {
129 workerStderr.on("data", chunk => process.stderr.write(chunk));
130 }
131
132 return initializedWorker;
133 };
134 }
135
136 const limit = (0, _pLimit.default)(getWorker && numberOfAssetsForMinify > 0 ? numberOfWorkers : Infinity);
137 const {
138 RawSource
139 } = compiler.webpack.sources;
140 const scheduledTasks = [];
141
142 for (const asset of assetsForMinify) {
143 scheduledTasks.push(limit(async () => {
144 const {
145 name,
146 inputSource,
147 cacheItem
148 } = asset;
149 let {
150 output
151 } = asset;
152 let input;
153 const sourceFromInputSource = inputSource.source();
154
155 if (!output) {
156 input = sourceFromInputSource;
157
158 if (Buffer.isBuffer(input)) {
159 input = input.toString();
160 }
161
162 const options = {
163 name,
164 input,
165 minimizerOptions: this.options.minimizerOptions,
166 minify: this.options.minify
167 };
168
169 try {
170 output = await (getWorker ? getWorker().transform((0, _serializeJavascript.default)(options)) : (0, _minify.minify)(options));
171 } catch (error) {
172 compilation.errors.push(HtmlMinimizerPlugin.buildError(error, name, compiler.context));
173 return;
174 }
175
176 if (output.errors.length > 0) {
177 output.errors.forEach(error => {
178 compilation.errors.push(error);
179 });
180 return;
181 }
182
183 output.source = new RawSource(output.code);
184 await cacheItem.storePromise({
185 source: output.source,
186 warnings: output.warnings
187 });
188 }
189
190 const newInfo = {
191 minimized: true
192 };
193 const {
194 source,
195 warnings
196 } = output;
197
198 if (warnings && warnings.length > 0) {
199 warnings.forEach(warning => {
200 compilation.warnings.push(warning);
201 });
202 }
203
204 compilation.updateAsset(name, source, newInfo);
205 }));
206 }
207
208 await Promise.all(scheduledTasks);
209
210 if (initializedWorker) {
211 await initializedWorker.end();
212 }
213 }
214
215 apply(compiler) {
216 const pluginName = this.constructor.name;
217 const availableNumberOfCores = HtmlMinimizerPlugin.getAvailableNumberOfCores(this.options.parallel);
218 compiler.hooks.compilation.tap(pluginName, compilation => {
219 compilation.hooks.processAssets.tapPromise({
220 name: pluginName,
221 stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
222 additionalAssets: true
223 }, assets => this.optimize(compiler, compilation, assets, {
224 availableNumberOfCores
225 }));
226 compilation.hooks.statsPrinter.tap(pluginName, stats => {
227 stats.hooks.print.for("asset.info.minimized").tap("html-minimizer-webpack-plugin", (minimized, {
228 green,
229 formatFlag
230 }) => // eslint-disable-next-line no-undefined
231 minimized ? green(formatFlag("minimized")) : undefined);
232 });
233 });
234 }
235
236}
237
238HtmlMinimizerPlugin.htmlMinifierTerser = _utils.htmlMinifierTerser;
239var _default = HtmlMinimizerPlugin;
240exports.default = _default;
\No newline at end of file