UNPKG

7.66 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 buildWarning(warning, file) {
51 const builtWarning = new Error(warning.message ? warning.message : warning.toString());
52 builtWarning.name = "Warning";
53 builtWarning.hideStack = true;
54 builtWarning.file = file;
55 return builtWarning;
56 }
57
58 static buildError(error, file) {
59 let builtError;
60
61 if (typeof error === "string") {
62 // @ts-ignore
63 builtError = new Error(`${file} from Html Minimizer plugin\n${error}`);
64 builtError.file = file;
65 return builtError;
66 }
67
68 if (error.stack) {
69 // @ts-ignore
70 builtError = new Error(`${file} from Html Minimizer plugin\n${typeof error.message !== "undefined" ? error.message : ""}\n${error.stack}`);
71 builtError.file = file;
72 return builtError;
73 }
74
75 builtError = new Error(`${file} from Html Minimizer plugin\n${error.message}`);
76 builtError.file = file;
77 return builtError;
78 }
79
80 static getAvailableNumberOfCores(parallel) {
81 // In some cases cpus() returns undefined
82 // https://github.com/nodejs/node/issues/19022
83 const cpus = _os.default.cpus() || {
84 length: 1
85 };
86 return parallel === true ? cpus.length - 1 : Math.min(Number(parallel) || 0, cpus.length - 1);
87 }
88
89 async optimize(compiler, compilation, assets, optimizeOptions) {
90 const cache = compilation.getCache("HtmlMinimizerWebpackPlugin");
91 let numberOfAssetsForMinify = 0;
92 const assetsForMinify = await Promise.all(Object.keys(assets).filter(name => {
93 const {
94 info
95 } = compilation.getAsset(name); // Skip double minimize assets from child compilation
96
97 if (info.minimized) {
98 return false;
99 }
100
101 if (!compiler.webpack.ModuleFilenameHelpers.matchObject.bind( // eslint-disable-next-line no-undefined
102 undefined, this.options)(name)) {
103 return false;
104 }
105
106 return true;
107 }).map(async name => {
108 const {
109 info,
110 source
111 } = compilation.getAsset(name);
112 const eTag = cache.getLazyHashedEtag(source);
113 const cacheItem = cache.getItemCache(name, eTag);
114 const output = await cacheItem.getPromise();
115
116 if (!output) {
117 numberOfAssetsForMinify += 1;
118 }
119
120 return {
121 name,
122 info,
123 inputSource: source,
124 output,
125 cacheItem
126 };
127 }));
128 let getWorker;
129 let initializedWorker;
130 let numberOfWorkers;
131
132 if (optimizeOptions.availableNumberOfCores > 0) {
133 // Do not create unnecessary workers when the number of files is less than the available cores, it saves memory
134 numberOfWorkers = Math.min(numberOfAssetsForMinify, optimizeOptions.availableNumberOfCores); // eslint-disable-next-line consistent-return
135
136 getWorker = () => {
137 if (initializedWorker) {
138 return initializedWorker;
139 }
140
141 initializedWorker = new _jestWorker.Worker(require.resolve("./minify"), {
142 numWorkers: numberOfWorkers,
143 enableWorkerThreads: true
144 }); // https://github.com/facebook/jest/issues/8872#issuecomment-524822081
145
146 const workerStdout = initializedWorker.getStdout();
147
148 if (workerStdout) {
149 workerStdout.on("data", chunk => process.stdout.write(chunk));
150 }
151
152 const workerStderr = initializedWorker.getStderr();
153
154 if (workerStderr) {
155 workerStderr.on("data", chunk => process.stderr.write(chunk));
156 }
157
158 return initializedWorker;
159 };
160 }
161
162 const limit = (0, _pLimit.default)(getWorker && numberOfAssetsForMinify > 0 ? numberOfWorkers : Infinity);
163 const {
164 RawSource
165 } = compiler.webpack.sources;
166 const scheduledTasks = [];
167
168 for (const asset of assetsForMinify) {
169 scheduledTasks.push(limit(async () => {
170 const {
171 name,
172 inputSource,
173 cacheItem
174 } = asset;
175 let {
176 output
177 } = asset;
178 let input;
179 const sourceFromInputSource = inputSource.source();
180
181 if (!output) {
182 input = sourceFromInputSource;
183
184 if (Buffer.isBuffer(input)) {
185 input = input.toString();
186 }
187
188 const options = {
189 name,
190 input,
191 minimizerOptions: this.options.minimizerOptions,
192 minify: this.options.minify
193 };
194
195 try {
196 output = await (getWorker ? getWorker().transform((0, _serializeJavascript.default)(options)) : (0, _minify.minify)(options));
197 } catch (error) {
198 compilation.errors.push(HtmlMinimizerPlugin.buildError(error, name));
199 return;
200 }
201
202 output.source = new RawSource(output.code);
203 await cacheItem.storePromise({
204 source: output.source,
205 errors: output.errors,
206 warnings: output.warnings
207 });
208 }
209
210 const newInfo = {
211 minimized: true
212 };
213
214 if (output.warnings && output.warnings.length > 0) {
215 for (const warning of output.warnings) {
216 compilation.warnings.push(HtmlMinimizerPlugin.buildWarning(warning, name));
217 }
218 }
219
220 if (output.errors && output.errors.length > 0) {
221 for (const error of output.errors) {
222 compilation.errors.push(HtmlMinimizerPlugin.buildError(error, name));
223 }
224 }
225
226 compilation.updateAsset(name, output.source, newInfo);
227 }));
228 }
229
230 await Promise.all(scheduledTasks);
231
232 if (initializedWorker) {
233 await initializedWorker.end();
234 }
235 }
236
237 apply(compiler) {
238 const pluginName = this.constructor.name;
239 const availableNumberOfCores = HtmlMinimizerPlugin.getAvailableNumberOfCores(this.options.parallel);
240 compiler.hooks.compilation.tap(pluginName, compilation => {
241 compilation.hooks.processAssets.tapPromise({
242 name: pluginName,
243 stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_OPTIMIZE_SIZE,
244 additionalAssets: true
245 }, assets => this.optimize(compiler, compilation, assets, {
246 availableNumberOfCores
247 }));
248 compilation.hooks.statsPrinter.tap(pluginName, stats => {
249 stats.hooks.print.for("asset.info.minimized").tap("html-minimizer-webpack-plugin", (minimized, {
250 green,
251 formatFlag
252 }) => // eslint-disable-next-line no-undefined
253 minimized ? green(formatFlag("minimized")) : undefined);
254 });
255 });
256 }
257
258}
259
260HtmlMinimizerPlugin.htmlMinifierTerser = _utils.htmlMinifierTerser;
261var _default = HtmlMinimizerPlugin;
262exports.default = _default;
\No newline at end of file