UNPKG

3.34 kBJavaScriptView Raw
1"use strict";
2
3/** @typedef {import("./index.js").MinimizedResult} MinimizedResult */
4
5/** @typedef {import("./index.js").Input} Input */
6
7/** @typedef {import("html-minifier-terser").Options} HtmlMinifierTerserOptions */
8const notSettled = Symbol(`not-settled`);
9/**
10 * @template T
11 * @typedef {() => Promise<T>} Task
12 */
13
14/**
15 * Run tasks with limited concurency.
16 * @template T
17 * @param {number} limit - Limit of tasks that run at once.
18 * @param {Task<T>[]} tasks - List of tasks to run.
19 * @returns {Promise<T[]>} A promise that fulfills to an array of the results
20 */
21
22function throttleAll(limit, tasks) {
23 if (!Number.isInteger(limit) || limit < 1) {
24 throw new TypeError(`Expected \`limit\` to be a finite number > 0, got \`${limit}\` (${typeof limit})`);
25 }
26
27 if (!Array.isArray(tasks) || !tasks.every(task => typeof task === `function`)) {
28 throw new TypeError(`Expected \`tasks\` to be a list of functions returning a promise`);
29 }
30
31 return new Promise((resolve, reject) => {
32 const result = Array(tasks.length).fill(notSettled);
33 const entries = tasks.entries();
34
35 const next = () => {
36 const {
37 done,
38 value
39 } = entries.next();
40
41 if (done) {
42 const isLast = !result.includes(notSettled);
43 if (isLast) resolve(
44 /** @type{T[]} **/
45 result);
46 return;
47 }
48
49 const [index, task] = value;
50 /**
51 * @param {T} x
52 */
53
54 const onFulfilled = x => {
55 result[index] = x;
56 next();
57 };
58
59 task().then(onFulfilled, reject);
60 };
61
62 Array(limit).fill(0).forEach(next);
63 });
64}
65/**
66 * @param {Input} input
67 * @param {HtmlMinifierTerserOptions | undefined} [minimizerOptions]
68 * @returns {Promise<MinimizedResult>}
69 */
70
71/* istanbul ignore next */
72
73
74async function htmlMinifierTerser(input, minimizerOptions = {}) {
75 // eslint-disable-next-line global-require, import/no-extraneous-dependencies
76 const htmlMinifier = require("html-minifier-terser");
77
78 const [[, code]] = Object.entries(input);
79 /** @type {HtmlMinifierTerserOptions} */
80
81 const defaultMinimizerOptions = {
82 caseSensitive: true,
83 // `collapseBooleanAttributes` is not always safe, since this can break CSS attribute selectors and not safe for XHTML
84 collapseWhitespace: true,
85 conservativeCollapse: true,
86 keepClosingSlash: true,
87 // We need ability to use cssnano, or setup own function without extra dependencies
88 minifyCSS: true,
89 minifyJS: true,
90 // `minifyURLs` is unsafe, because we can't guarantee what the base URL is
91 // `removeAttributeQuotes` is not safe in some rare cases, also HTML spec recommends against doing this
92 removeComments: true,
93 // `removeEmptyAttributes` is not safe, can affect certain style or script behavior, look at https://github.com/webpack-contrib/html-loader/issues/323
94 // `removeRedundantAttributes` is not safe, can affect certain style or script behavior, look at https://github.com/webpack-contrib/html-loader/issues/323
95 removeScriptTypeAttributes: true,
96 removeStyleLinkTypeAttributes: true // `useShortDoctype` is not safe for XHTML
97
98 };
99 const result = await htmlMinifier.minify(code, { ...defaultMinimizerOptions,
100 ...minimizerOptions
101 });
102 return {
103 code: result
104 };
105}
106
107module.exports = {
108 throttleAll,
109 htmlMinifierTerser
110};
\No newline at end of file