html-minifier-next
Version:
Super-configurable and well-tested web page minifier (enhanced successor of HTML Minifier)
110 lines (93 loc) • 2.69 kB
JavaScript
// Stringify for options signatures (sorted keys, shallow, nested objects)
function stableStringify(obj) {
if (obj == null || typeof obj !== 'object') return JSON.stringify(obj);
if (Array.isArray(obj)) return '[' + obj.map(stableStringify).join(',') + ']';
const keys = Object.keys(obj).sort();
let out = '{';
for (let i = 0; i < keys.length; i++) {
const k = keys[i];
out += JSON.stringify(k) + ':' + stableStringify(obj[k]) + (i < keys.length - 1 ? ',' : '');
}
return out + '}';
}
// LRU cache for strings and promises
class LRU {
constructor(limit = 200) {
this.limit = limit;
this.map = new Map();
}
get(key) {
if (this.map.has(key)) {
const v = this.map.get(key);
this.map.delete(key);
this.map.set(key, v);
return v;
}
return undefined;
}
set(key, value) {
if (this.map.has(key)) this.map.delete(key);
this.map.set(key, value);
if (this.map.size > this.limit) {
const first = this.map.keys().next().value;
this.map.delete(first);
}
}
delete(key) { this.map.delete(key); }
}
// Unique ID generator
function uniqueId(value) {
let id;
do {
id = 'u' + crypto.randomUUID().replace(/-/g, '');
} while (~value.indexOf(id));
return id;
}
// Identity and transform functions
function identity(value) {
return value;
}
function isThenable(value) {
return value != null && typeof value === 'object' && typeof value.then === 'function';
}
function lowercase(value) {
return value.toLowerCase();
}
// Replace async helper
/**
* Asynchronously replace matches in a string
* @param {string} str - Input string
* @param {RegExp} regex - Regular expression with global flag
* @param {Function} asyncFn - Async function to process each match
* @returns {Promise<string>} Processed string
*/
async function replaceAsync(str, regex, asyncFn) {
const promises = [];
str.replace(regex, (match, ...args) => {
const promise = asyncFn(match, ...args);
promises.push(promise);
});
const data = await Promise.all(promises);
return str.replace(regex, () => data.shift());
}
// String patterns to RegExp conversion (for JSON config support)
function parseRegExp(value) {
if (typeof value === 'string') {
if (!value) return undefined; // Empty string = not configured
const match = value.match(/^\/(.+)\/([dgimsuvy]*)$/);
if (match) {
return new RegExp(match[1], match[2]);
}
return new RegExp(value);
}
return value;
}
// Exports
export { stableStringify };
export { LRU };
export { uniqueId };
export { identity };
export { isThenable };
export { lowercase };
export { replaceAsync };
export { parseRegExp };