flow like the river

This commit is contained in:
root 2025-11-07 00:06:12 +01:00
commit 013fe673f3
42435 changed files with 5764238 additions and 0 deletions

41
BACK_BACK/node_modules/htmlnano/lib/helpers.js generated vendored Executable file
View file

@ -0,0 +1,41 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isAmpBoilerplate = isAmpBoilerplate;
exports.isComment = isComment;
exports.isConditionalComment = isConditionalComment;
exports.isStyleNode = isStyleNode;
exports.extractCssFromStyleNode = extractCssFromStyleNode;
const ampBoilerplateAttributes = ['amp-boilerplate', 'amp4ads-boilerplate', 'amp4email-boilerplate'];
function isAmpBoilerplate(node) {
if (!node.attrs) {
return false;
}
for (const attr of ampBoilerplateAttributes) {
if (attr in node.attrs) {
return true;
}
}
return false;
}
function isComment(content) {
return (content || '').trim().startsWith('<!--');
}
function isConditionalComment(content) {
return (content || '').trim().startsWith('<!--[if');
}
function isStyleNode(node) {
return node.tag === 'style' && !isAmpBoilerplate(node) && 'content' in node && node.content.length > 0;
}
function extractCssFromStyleNode(node) {
return Array.isArray(node.content) ? node.content.join(' ') : node.content;
}

54
BACK_BACK/node_modules/htmlnano/lib/htmlnano.js generated vendored Executable file
View file

@ -0,0 +1,54 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _posthtml = _interopRequireDefault(require("posthtml"));
var _safe = _interopRequireDefault(require("./presets/safe"));
var _ampSafe = _interopRequireDefault(require("./presets/ampSafe"));
var _max = _interopRequireDefault(require("./presets/max"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function htmlnano(options = {}, preset = _safe.default) {
return function minifier(tree) {
options = { ...preset,
...options
};
let promise = Promise.resolve(tree);
for (const [moduleName, moduleOptions] of Object.entries(options)) {
if (!moduleOptions) {
// The module is disabled
continue;
}
if (_safe.default[moduleName] === undefined) {
throw new Error('Module "' + moduleName + '" is not defined');
}
let module = require('./modules/' + moduleName);
promise = promise.then(tree => module.default(tree, options, moduleOptions));
}
return promise;
};
}
htmlnano.process = function (html, options, preset, postHtmlOptions) {
return (0, _posthtml.default)([htmlnano(options, preset)]).process(html, postHtmlOptions);
};
htmlnano.presets = {
safe: _safe.default,
ampSafe: _ampSafe.default,
max: _max.default
};
var _default = htmlnano;
exports.default = _default;

View file

@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = collapseAttributeWhitespace;
exports.attributesWithLists = void 0;
const attributesWithLists = new Set(['class', 'rel', 'ping']);
/** Collapse whitespaces inside list-like attributes (e.g. class, rel) */
exports.attributesWithLists = attributesWithLists;
function collapseAttributeWhitespace(tree) {
tree.walk(node => {
if (!node.attrs) {
return node;
}
Object.entries(node.attrs).forEach(([attrName, attrValue]) => {
const attrNameLower = attrName.toLowerCase();
if (!attributesWithLists.has(attrNameLower)) {
return;
}
const newAttrValue = attrValue.replace(/\s+/g, ' ').trim();
node.attrs[attrName] = newAttrValue;
});
return node;
});
return tree;
}

View file

@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = collapseBooleanAttributes;
// Source: https://github.com/kangax/html-minifier/issues/63
const htmlBooleanAttributes = new Set(['allowfullscreen', 'allowpaymentrequest', 'allowtransparency', 'async', 'autofocus', 'autoplay', 'checked', 'compact', 'controls', 'declare', 'default', 'defaultchecked', 'defaultmuted', 'defaultselected', 'defer', 'disabled', 'enabled', 'formnovalidate', 'hidden', 'indeterminate', 'inert', 'ismap', 'itemscope', 'loop', 'multiple', 'muted', 'nohref', 'noresize', 'noshade', 'novalidate', 'nowrap', 'open', 'pauseonexit', 'readonly', 'required', 'reversed', 'scoped', 'seamless', 'selected', 'sortable', 'truespeed', 'typemustmatch', 'visible']);
const amphtmlBooleanAttributes = new Set(['⚡', 'amp', '⚡4ads', 'amp4ads', '⚡4email', 'amp4email', 'amp-custom', 'amp-boilerplate', 'amp4ads-boilerplate', 'amp4email-boilerplate', 'allow-blocked-ranges', 'amp-access-hide', 'amp-access-template', 'amp-keyframes', 'animate', 'arrows', 'data-block-on-consent', 'data-enable-refresh', 'data-multi-size', 'date-template', 'disable-double-tap', 'disable-session-states', 'disableremoteplayback', 'dots', 'expand-single-section', 'expanded', 'fallback', 'first', 'fullscreen', 'inline', 'lightbox', 'noaudio', 'noautoplay', 'noloading', 'once', 'open-after-clear', 'open-after-select', 'open-button', 'placeholder', 'preload', 'reset-on-refresh', 'reset-on-resize', 'resizable', 'rotate-to-fullscreen', 'second', 'standalone', 'stereo', 'submit-error', 'submit-success', 'submitting', 'subscriptions-actions', 'subscriptions-dialog']);
function collapseBooleanAttributes(tree, options, moduleOptions) {
tree.walk(node => {
if (!node.attrs) {
return node;
}
if (!node.tag) {
return node;
}
for (const attrName of Object.keys(node.attrs)) {
if (attrName === 'visible' && node.tag.startsWith('a-')) {
continue;
}
if (htmlBooleanAttributes.has(attrName)) {
node.attrs[attrName] = true;
}
if (moduleOptions.amphtml && amphtmlBooleanAttributes.has(attrName) && node.attrs[attrName] === '') {
node.attrs[attrName] = true;
} // collapse crossorigin attributes
// Specification: https://html.spec.whatwg.org/multipage/urls-and-fetching.html#cors-settings-attributes
if (attrName.toLowerCase() === 'crossorigin' && (node.attrs[attrName] === 'anonymous' || node.attrs[attrName] === '')) {
node.attrs[attrName] = true;
}
}
return node;
});
return tree;
}

View file

@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = collapseWhitespace;
var _helpers = require("../helpers");
const noWhitespaceCollapseElements = new Set(['script', 'style', 'pre', 'textarea']);
const noTrimWhitespacesArroundElements = new Set([// non-empty tags that will maintain whitespace around them
'a', 'abbr', 'acronym', 'b', 'bdi', 'bdo', 'big', 'button', 'cite', 'code', 'del', 'dfn', 'em', 'font', 'i', 'ins', 'kbd', 'label', 'mark', 'math', 'nobr', 'object', 'q', 'rp', 'rt', 'rtc', 'ruby', 's', 'samp', 'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'svg', 'textarea', 'time', 'tt', 'u', 'var', // self-closing tags that will maintain whitespace around them
'comment', 'img', 'input', 'wbr']);
const noTrimWhitespacesInsideElements = new Set([// non-empty tags that will maintain whitespace within them
'a', 'abbr', 'acronym', 'b', 'big', 'del', 'em', 'font', 'i', 'ins', 'kbd', 'mark', 'nobr', 'rp', 's', 'samp', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'time', 'tt', 'u', 'var']);
const whitespacePattern = /\s+/g;
const NONE = '';
const SINGLE_SPACE = ' ';
const validOptions = ['all', 'aggressive', 'conservative'];
/** Collapses redundant whitespaces */
function collapseWhitespace(tree, options, collapseType, tag) {
collapseType = validOptions.includes(collapseType) ? collapseType : 'conservative';
tree.forEach((node, index) => {
if (typeof node === 'string') {
const prevNode = tree[index - 1];
const nextNode = tree[index + 1];
const prevNodeTag = prevNode && prevNode.tag;
const nextNodeTag = nextNode && nextNode.tag;
const isTopLevel = !tag || tag === 'html' || tag === 'head';
const shouldTrim = collapseType === 'all' || isTopLevel ||
/*
* When collapseType is set to 'aggressive', and the tag is not inside 'noTrimWhitespacesInsideElements'.
* the first & last space inside the tag will be trimmed
*/
collapseType === 'aggressive' && !noTrimWhitespacesInsideElements.has(tag);
node = collapseRedundantWhitespaces(node, collapseType, shouldTrim, tag, prevNodeTag, nextNodeTag);
}
const isAllowCollapseWhitespace = !noWhitespaceCollapseElements.has(node.tag);
if (node.content && node.content.length && isAllowCollapseWhitespace) {
node.content = collapseWhitespace(node.content, options, collapseType, node.tag);
}
tree[index] = node;
});
return tree;
}
function collapseRedundantWhitespaces(text, collapseType, shouldTrim = false, currentTag, prevNodeTag, nextNodeTag) {
if (!text || text.length === 0) {
return NONE;
}
if (!(0, _helpers.isComment)(text)) {
text = text.replace(whitespacePattern, SINGLE_SPACE);
}
if (shouldTrim) {
if (collapseType === 'aggressive') {
if (!noTrimWhitespacesArroundElements.has(prevNodeTag)) {
text = text.trimStart();
}
if (!noTrimWhitespacesArroundElements.has(nextNodeTag)) {
text = text.trimEnd();
}
} else {
// collapseType is 'all', trim spaces
text = text.trim();
}
}
return text;
}

22
BACK_BACK/node_modules/htmlnano/lib/modules/custom.js generated vendored Executable file
View file

@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = custom;
/** Meta-module that runs custom modules */
function custom(tree, options, customModules) {
if (!customModules) {
return tree;
}
if (!Array.isArray(customModules)) {
customModules = [customModules];
}
customModules.forEach(customModule => {
tree = customModule(tree, options);
});
return tree;
}

View file

@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = collapseAttributeWhitespace;
var _collapseAttributeWhitespace = require("./collapseAttributeWhitespace");
/** Deduplicate values inside list-like attributes (e.g. class, rel) */
function collapseAttributeWhitespace(tree) {
tree.walk(node => {
if (!node.attrs) {
return node;
}
Object.keys(node.attrs).forEach(attrName => {
const attrNameLower = attrName.toLowerCase();
if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
return;
}
const attrValues = node.attrs[attrName].split(/\s/);
const uniqeAttrValues = new Set();
const deduplicatedAttrValues = [];
attrValues.forEach(attrValue => {
if (!attrValue) {
// Keep whitespaces
deduplicatedAttrValues.push('');
return;
}
if (uniqeAttrValues.has(attrValue)) {
return;
}
deduplicatedAttrValues.push(attrValue);
uniqeAttrValues.add(attrValue);
});
node.attrs[attrName] = deduplicatedAttrValues.join(' ');
});
return node;
});
return tree;
}

63
BACK_BACK/node_modules/htmlnano/lib/modules/mergeScripts.js generated vendored Executable file
View file

@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = mergeScripts;
/* Merge multiple <script> into one */
function mergeScripts(tree) {
let scriptNodesIndex = {};
let scriptSrcIndex = 1;
tree.match({
tag: 'script'
}, node => {
const nodeAttrs = node.attrs || {};
if (nodeAttrs.src) {
scriptSrcIndex++;
return node;
}
const scriptType = nodeAttrs.type || 'text/javascript';
if (scriptType !== 'text/javascript' && scriptType !== 'application/javascript') {
return node;
}
const scriptKey = JSON.stringify({
id: nodeAttrs.id,
class: nodeAttrs.class,
type: scriptType,
defer: nodeAttrs.defer !== undefined,
async: nodeAttrs.async !== undefined,
index: scriptSrcIndex
});
if (!scriptNodesIndex[scriptKey]) {
scriptNodesIndex[scriptKey] = [];
}
scriptNodesIndex[scriptKey].push(node);
return node;
});
for (const scriptNodes of Object.values(scriptNodesIndex)) {
let lastScriptNode = scriptNodes.pop();
scriptNodes.reverse().forEach(scriptNode => {
let scriptContent = (scriptNode.content || []).join(' ');
scriptContent = scriptContent.trim();
if (scriptContent.slice(-1) !== ';') {
scriptContent += ';';
}
lastScriptNode.content = lastScriptNode.content || [];
lastScriptNode.content.unshift(scriptContent);
scriptNode.tag = false;
scriptNode.content = [];
});
}
return tree;
}

42
BACK_BACK/node_modules/htmlnano/lib/modules/mergeStyles.js generated vendored Executable file
View file

@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = mergeStyles;
var _helpers = require("../helpers");
/* Merge multiple <style> into one */
function mergeStyles(tree) {
const styleNodes = {};
tree.match({
tag: 'style'
}, node => {
const nodeAttrs = node.attrs || {}; // Skip <style scoped></style>
// https://developer.mozilla.org/en/docs/Web/HTML/Element/style
if (nodeAttrs.scoped !== undefined) {
return node;
}
if ((0, _helpers.isAmpBoilerplate)(node)) {
return node;
}
const styleType = nodeAttrs.type || 'text/css';
const styleMedia = nodeAttrs.media || 'all';
const styleKey = styleType + '_' + styleMedia;
if (styleNodes[styleKey]) {
const styleContent = (node.content || []).join(' ');
styleNodes[styleKey].content.push(' ' + styleContent);
return '';
}
node.content = node.content || [];
styleNodes[styleKey] = node;
return node;
});
return tree;
}

View file

@ -0,0 +1,58 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = minifyConditionalComments;
var _htmlnano = _interopRequireDefault(require("../htmlnano"));
var _helpers = require("../helpers");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// Spec: https://docs.microsoft.com/en-us/previous-versions/windows/internet-explorer/ie-developer/compatibility/ms537512(v=vs.85)
const CONDITIONAL_COMMENT_REGEXP = /(<!--\[if\s+?[^<>[\]]+?]>)([\s\S]+?)(<!\[endif\]-->)/gm;
/** Minify content inside conditional comments */
async function minifyConditionalComments(tree, htmlnanoOptions) {
// forEach, tree.walk, tree.match just don't support Promise.
for (let i = 0, len = tree.length; i < len; i++) {
const node = tree[i];
if (typeof node === 'string' && (0, _helpers.isConditionalComment)(node)) {
tree[i] = await minifycontentInsideConditionalComments(node, htmlnanoOptions);
}
if (node.content && node.content.length) {
tree[i].content = await minifyConditionalComments(node.content, htmlnanoOptions);
}
}
return tree;
}
async function minifycontentInsideConditionalComments(text, htmlnanoOptions) {
let match;
const matches = []; // FIXME!
// String#matchAll is supported since Node.js 12
while ((match = CONDITIONAL_COMMENT_REGEXP.exec(text)) !== null) {
matches.push([match[1], match[2], match[3]]);
}
if (!matches.length) {
return Promise.resolve(text);
}
return Promise.all(matches.map(async match => {
const result = await _htmlnano.default.process(match[1], htmlnanoOptions, {}, {});
let minified = result.html;
if (match[1].includes('<html') && minified.includes('</html>')) {
minified = minified.replace('</html>', '');
}
return match[0] + minified + match[2];
}));
}

78
BACK_BACK/node_modules/htmlnano/lib/modules/minifyCss.js generated vendored Executable file
View file

@ -0,0 +1,78 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = minifyCss;
var _helpers = require("../helpers");
var _cssnano = _interopRequireDefault(require("cssnano"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const postcssOptions = {
// Prevent the following warning from being shown:
// > Without `from` option PostCSS could generate wrong source map and will not find Browserslist config.
// > Set it to CSS file path or to `undefined` to prevent this warning.
from: undefined
};
/** Minify CSS with cssnano */
function minifyCss(tree, options, cssnanoOptions) {
let promises = [];
tree.walk(node => {
if ((0, _helpers.isStyleNode)(node)) {
promises.push(processStyleNode(node, cssnanoOptions));
} else if (node.attrs && node.attrs.style) {
promises.push(processStyleAttr(node, cssnanoOptions));
}
return node;
});
return Promise.all(promises).then(() => tree);
}
function processStyleNode(styleNode, cssnanoOptions) {
let css = (0, _helpers.extractCssFromStyleNode)(styleNode); // Improve performance by avoiding calling stripCdata again and again
let isCdataWrapped = false;
if (css.includes('CDATA')) {
const strippedCss = stripCdata(css);
isCdataWrapped = css !== strippedCss;
css = strippedCss;
}
return _cssnano.default.process(css, postcssOptions, cssnanoOptions).then(result => {
if (isCdataWrapped) {
return styleNode.content = ['<![CDATA[' + result + ']]>'];
}
return styleNode.content = [result.css];
});
}
function processStyleAttr(node, cssnanoOptions) {
// CSS "color: red;" is invalid. Therefore it should be wrapped inside some selector:
// a{color: red;}
const wrapperStart = 'a{';
const wrapperEnd = '}';
const wrappedStyle = wrapperStart + (node.attrs.style || '') + wrapperEnd;
return _cssnano.default.process(wrappedStyle, postcssOptions, cssnanoOptions).then(result => {
const minifiedCss = result.css; // Remove wrapperStart at the start and wrapperEnd at the end of minifiedCss
node.attrs.style = minifiedCss.substring(wrapperStart.length, minifiedCss.length - wrapperEnd.length);
});
}
function stripCdata(css) {
const leftStrippedCss = css.replace('<![CDATA[', '');
if (leftStrippedCss === css) {
return css;
}
const strippedCss = leftStrippedCss.replace(']]>', '');
return leftStrippedCss === strippedCss ? css : strippedCss;
}

109
BACK_BACK/node_modules/htmlnano/lib/modules/minifyJs.js generated vendored Executable file
View file

@ -0,0 +1,109 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = minifyJs;
var _terser = _interopRequireDefault(require("terser"));
var _removeRedundantAttributes = require("./removeRedundantAttributes");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/** Minify JS with Terser */
function minifyJs(tree, options, terserOptions) {
let promises = [];
tree.walk(node => {
if (node.tag && node.tag === 'script') {
const nodeAttrs = node.attrs || {};
const mimeType = nodeAttrs.type || 'text/javascript';
if (_removeRedundantAttributes.redundantScriptTypes.has(mimeType) || mimeType === 'module') {
promises.push(processScriptNode(node, terserOptions));
}
}
if (node.attrs) {
promises = promises.concat(processNodeWithOnAttrs(node, terserOptions));
}
return node;
});
return Promise.all(promises).then(() => tree);
}
function stripCdata(js) {
const leftStrippedJs = js.replace(/\/\/\s*<!\[CDATA\[/, '').replace(/\/\*\s*<!\[CDATA\[\s*\*\//, '');
if (leftStrippedJs === js) {
return js;
}
const strippedJs = leftStrippedJs.replace(/\/\/\s*\]\]>/, '').replace(/\/\*\s*\]\]>\s*\*\//, '');
return leftStrippedJs === strippedJs ? js : strippedJs;
}
function processScriptNode(scriptNode, terserOptions) {
let js = (scriptNode.content || []).join('').trim();
if (!js) {
return scriptNode;
} // Improve performance by avoiding calling stripCdata again and again
let isCdataWrapped = false;
if (js.includes('CDATA')) {
const strippedJs = stripCdata(js);
isCdataWrapped = js !== strippedJs;
js = strippedJs;
}
return _terser.default.minify(js, terserOptions).then(result => {
if (result.error) {
throw new Error(result.error);
}
if (result.code === undefined) {
return;
}
let content = result.code;
if (isCdataWrapped) {
content = '/*<![CDATA[*/' + content + '/*]]>*/';
}
scriptNode.content = [content];
});
}
function processNodeWithOnAttrs(node, terserOptions) {
const jsWrapperStart = 'function _(){';
const jsWrapperEnd = '}';
const promises = [];
for (const attrName of Object.keys(node.attrs || {})) {
if (!attrName.startsWith('on')) {
continue;
} // For example onclick="return false" is valid,
// but "return false;" is invalid (error: 'return' outside of function)
// Therefore the attribute's code should be wrapped inside function:
// "function _(){return false;}"
let wrappedJs = jsWrapperStart + node.attrs[attrName] + jsWrapperEnd;
let promise = _terser.default.minify(wrappedJs, terserOptions).then(({
code
}) => {
let minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
node.attrs[attrName] = minifiedJs;
});
promises.push(promise);
}
return promises;
}

33
BACK_BACK/node_modules/htmlnano/lib/modules/minifyJson.js generated vendored Executable file
View file

@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = minifyJson;
/* Minify JSON inside <script> tags */
function minifyJson(tree) {
// Match all <script> tags which have JSON mime type
tree.match({
tag: 'script',
attrs: {
type: /(\/|\+)json/
}
}, node => {
let content = (node.content || []).join('');
if (!content) {
return node;
}
try {
content = JSON.stringify(JSON.parse(content));
} catch (error) {
return node;
}
node.content = [content];
return node;
});
return tree;
}

32
BACK_BACK/node_modules/htmlnano/lib/modules/minifySvg.js generated vendored Executable file
View file

@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = minifySvg;
var _svgo = _interopRequireDefault(require("svgo"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/** Minify SVG with SVGO */
function minifySvg(tree, options, svgoOptions = {}) {
let promises = [];
const svgo = new _svgo.default(svgoOptions);
tree.match({
tag: 'svg'
}, node => {
let svgStr = tree.render(node, {
closingSingleTag: 'slash',
quoteAllAttributes: true
});
let promise = svgo.optimize(svgStr).then(result => {
node.tag = false;
node.attrs = {};
node.content = result.data;
});
promises.push(promise);
return node;
});
return Promise.all(promises).then(() => tree);
}

116
BACK_BACK/node_modules/htmlnano/lib/modules/minifyUrls.js generated vendored Executable file
View file

@ -0,0 +1,116 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = minifyUrls;
var _relateurl = _interopRequireDefault(require("relateurl"));
var _srcset = _interopRequireDefault(require("srcset"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// Adopts from https://github.com/kangax/html-minifier/blob/51ce10f4daedb1de483ffbcccecc41be1c873da2/src/htmlminifier.js#L209-L221
const tagsHaveUriValuesForAttributes = new Set(['a', 'area', 'link', 'base', 'img', 'object', 'q', 'blockquote', 'ins', 'form', 'input', 'head', 'script']);
const tagsHasHrefAttributes = new Set(['a', 'area', 'link', 'base']);
const attributesOfImgTagHasUriValues = new Set(['src', 'longdesc', 'usemap']);
const attributesOfObjectTagHasUriValues = new Set(['classid', 'codebase', 'data', 'usemap']);
const isUriTypeAttribute = (tag, attr) => {
return tagsHasHrefAttributes.has(tag) && attr === 'href' || tag === 'img' && attributesOfImgTagHasUriValues.has(attr) || tag === 'object' && attributesOfObjectTagHasUriValues.has(attr) || tag === 'q' && attr === 'cite' || tag === 'blockquote' && attr === 'cite' || (tag === 'ins' || tag === 'del') && attr === 'cite' || tag === 'form' && attr === 'action' || tag === 'input' && (attr === 'src' || attr === 'usemap') || tag === 'head' && attr === 'profile' || tag === 'script' && (attr === 'src' || attr === 'for') ||
/**
* https://html.spec.whatwg.org/#attr-source-src
*
* Although most of browsers recommend not to use "src" in <source>,
* but technically it does comply with HTML Standard.
*/
tag === 'source' && attr === 'src';
};
const isSrcsetAttribute = (tag, attr) => {
return tag === 'source' && attr === 'srcset' || tag === 'img' && attr === 'srcset' || tag === 'link' && attr === 'imagesrcset';
};
const processModuleOptions = options => {
// FIXME!
// relateurl@1.0.0-alpha only supports URL while stable version (0.2.7) only supports string
// should convert input into URL instance after relateurl@1 is stable
if (typeof options === 'string') return options;
if (options instanceof URL) return options.toString();
return false;
};
const isLinkRelCanonical = ({
tag,
attrs
}) => {
// Return false early for non-"link" tag
if (tag !== 'link') return false;
for (const [attrName, attrValue] of Object.entries(attrs)) {
if (attrName.toLowerCase() === 'rel' && attrValue === 'canonical') return true;
}
return false;
};
let relateUrlInstance;
let STORED_URL_BASE;
/** Convert absolute url into relative url */
function minifyUrls(tree, options, moduleOptions) {
const urlBase = processModuleOptions(moduleOptions); // Invalid configuration, return tree directly
if (!urlBase) return tree;
/** Bring up a reusable RelateUrl instances (only once)
*
* STORED_URL_BASE is used to invalidate RelateUrl instances,
* avoiding require.cache acrossing multiple htmlnano instance with different configuration,
* e.g. unit tests cases.
*/
if (!relateUrlInstance || STORED_URL_BASE !== urlBase) {
relateUrlInstance = new _relateurl.default(urlBase);
STORED_URL_BASE = urlBase;
}
tree.walk(node => {
if (!node.attrs) return node;
if (!node.tag) return node;
if (!tagsHaveUriValuesForAttributes.has(node.tag)) return node; // Prevent link[rel=canonical] being processed
// Can't be excluded by isUriTypeAttribute()
if (isLinkRelCanonical(node)) return node;
for (const [attrName, attrValue] of Object.entries(node.attrs)) {
const attrNameLower = attrName.toLowerCase();
if (isUriTypeAttribute(node.tag, attrNameLower)) {
// FIXME!
// relateurl@1.0.0-alpha only supports URL while stable version (0.2.7) only supports string
// the WHATWG URL API is very strict while attrValue might not be a valid URL
// new URL should be used, and relateUrl#relate should be wrapped in try...catch after relateurl@1 is stable
node.attrs[attrName] = relateUrlInstance.relate(attrValue);
continue;
}
if (isSrcsetAttribute(node.tag, attrNameLower)) {
try {
const parsedSrcset = _srcset.default.parse(attrValue);
node.attrs[attrName] = _srcset.default.stringify(parsedSrcset.map(srcset => {
srcset.url = relateUrlInstance.relate(srcset.url);
return srcset;
}));
} catch (e) {// srcset will throw an Error for invalid srcset.
}
continue;
}
}
return node;
});
return tree;
}

View file

@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = removeAttributeQuotes;
// Specification: https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
// See also: https://github.com/posthtml/posthtml-render/pull/30
// See also: https://github.com/posthtml/htmlnano/issues/6#issuecomment-707105334
/** Disable quoteAllAttributes while not overriding the configuration */
function removeAttributeQuotes(tree) {
if (tree.options && typeof tree.options.quoteAllAttributes === 'undefined') {
tree.options.quoteAllAttributes = false;
}
return tree;
}

View file

@ -0,0 +1,72 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = removeComments;
var _helpers = require("../helpers");
const MATCH_EXCERPT_REGEXP = /<!-- ?more ?-->/i;
/** Removes HTML comments */
function removeComments(tree, options, removeType) {
if (removeType !== 'all' && removeType !== 'safe') {
removeType = 'safe';
}
tree.walk(node => {
if (node.contents && node.contents.length) {
node.contents = node.contents.filter(content => !isCommentToRemove(content, removeType));
} else if (isCommentToRemove(node, removeType)) {
node = '';
}
return node;
});
return tree;
}
function isCommentToRemove(text, removeType) {
if (typeof text !== 'string') {
return false;
}
if (!(0, _helpers.isComment)(text)) {
// Not HTML comment
return false;
}
if (removeType === 'safe') {
const isNoindex = text === '<!--noindex-->' || text === '<!--/noindex-->'; // Don't remove noindex comments.
// See: https://yandex.com/support/webmaster/controlling-robot/html.xml
if (isNoindex) {
return false;
}
const isServerSideExclude = text === '<!--sse-->' || text === '<!--/sse-->'; // Don't remove sse comments.
// See: https://support.cloudflare.com/hc/en-us/articles/200170036-What-does-Server-Side-Excludes-SSE-do-
if (isServerSideExclude) {
return false;
} // https://en.wikipedia.org/wiki/Conditional_comment
if ((0, _helpers.isConditionalComment)(text)) {
return false;
} // Hexo: https://hexo.io/docs/tag-plugins#Post-Excerpt
// Hugo: https://gohugo.io/content-management/summaries/#manual-summary-splitting
// WordPress: https://wordpress.com/support/wordpress-editor/blocks/more-block/2/
// Jekyll: https://jekyllrb.com/docs/posts/#post-excerpts
const isCMSExcerptComment = MATCH_EXCERPT_REGEXP.test(text);
if (isCMSExcerptComment) {
return false;
}
}
return true;
}

View file

@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = removeEmptyAttributes;
// Source: https://www.w3.org/TR/html4/sgml/dtd.html#events (Generic Attributes)
const safeToRemoveAttrs = new Set(['id', 'class', 'style', 'title', 'lang', 'dir', 'onclick', 'ondblclick', 'onmousedown', 'onmouseup', 'onmouseover', 'onmousemove', 'onmouseout', 'onkeypress', 'onkeydown', 'onkeyup']);
/** Removes empty attributes */
function removeEmptyAttributes(tree) {
tree.walk(node => {
if (!node.attrs) {
return node;
}
Object.entries(node.attrs).forEach(([attrName, attrValue]) => {
const attrNameLower = attrName.toLowerCase();
if (!safeToRemoveAttrs.has(attrNameLower)) {
return;
}
if (attrValue === '' || (attrValue || '').match(/^\s+$/)) {
delete node.attrs[attrName];
}
});
return node;
});
return tree;
}

View file

@ -0,0 +1,220 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = removeOptionalTags;
var _helpers = require("../helpers");
const startWithWhitespacePattern = /^\s+/;
const bodyStartTagCantBeOmittedWithFirstChildTags = new Set(['meta', 'link', 'script', 'style']);
const tbodyStartTagCantBeOmittedWithPrecededTags = new Set(['tbody', 'thead', 'tfoot']);
const tbodyEndTagCantBeOmittedWithFollowedTags = new Set(['tbody', 'tfoot']);
function isEmptyTextNode(node) {
if (typeof node === 'string' && node.trim() === '') {
return true;
}
return false;
}
function isEmptyNode(node) {
if (!node.content) {
return true;
}
if (node.content.length) {
return !node.content.filter(n => typeof n === 'string' && isEmptyTextNode(n) ? false : true).length;
}
return true;
}
function getFirstChildTag(node, nonEmpty = true) {
if (node.content && node.content.length) {
if (nonEmpty) {
for (const childNode of node.content) {
if (childNode.tag) return childNode;
if (typeof childNode === 'string' && !isEmptyTextNode(childNode)) return childNode;
}
} else {
return node.content[0] || null;
}
}
return null;
}
function getPrevNode(tree, currentNodeIndex, nonEmpty = false) {
if (nonEmpty) {
for (let i = currentNodeIndex - 1; i >= 0; i--) {
const node = tree[i];
if (node.tag) return node;
if (typeof node === 'string' && !isEmptyTextNode(node)) return node;
}
} else {
return tree[currentNodeIndex - 1] || null;
}
return null;
}
function getNextNode(tree, currentNodeIndex, nonEmpty = false) {
if (nonEmpty) {
for (let i = currentNodeIndex + 1; i < tree.length; i++) {
const node = tree[i];
if (node.tag) return node;
if (typeof node === 'string' && !isEmptyTextNode(node)) return node;
}
} else {
return tree[currentNodeIndex + 1] || null;
}
return null;
} // Specification https://html.spec.whatwg.org/multipage/syntax.html#optional-tags
/** Remove optional tag in the DOM */
function removeOptionalTags(tree) {
tree.forEach((node, index) => {
if (!node.tag) return node;
if (node.attrs && Object.keys(node.attrs).length) return node; // const prevNode = getPrevNode(tree, index);
const prevNonEmptyNode = getPrevNode(tree, index, true);
const nextNode = getNextNode(tree, index);
const nextNonEmptyNode = getNextNode(tree, index, true);
const firstChildNode = getFirstChildTag(node, false);
const firstNonEmptyChildNode = getFirstChildTag(node);
/**
* An "html" element's start tag may be omitted if the first thing inside the "html" element is not a comment.
* An "html" element's end tag may be omitted if the "html" element is not IMMEDIATELY followed by a comment.
*/
if (node.tag === 'html') {
let isHtmlStartTagCanBeOmitted = true;
let isHtmlEndTagCanBeOmitted = true;
if (typeof firstNonEmptyChildNode === 'string' && (0, _helpers.isComment)(firstNonEmptyChildNode)) {
isHtmlStartTagCanBeOmitted = false;
}
if (typeof nextNonEmptyNode === 'string' && (0, _helpers.isComment)(nextNonEmptyNode)) {
isHtmlEndTagCanBeOmitted = false;
}
if (isHtmlStartTagCanBeOmitted && isHtmlEndTagCanBeOmitted) {
node.tag = false;
}
}
/**
* A "head" element's start tag may be omitted if the element is empty, or if the first thing inside the "head" element is an element.
* A "head" element's end tag may be omitted if the "head" element is not IMMEDIATELY followed by ASCII whitespace or a comment.
*/
if (node.tag === 'head') {
let isHeadStartTagCanBeOmitted = false;
let isHeadEndTagCanBeOmitted = true;
if (isEmptyNode(node) || firstNonEmptyChildNode && firstNonEmptyChildNode.tag) {
isHeadStartTagCanBeOmitted = true;
}
if (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode) || nextNonEmptyNode && typeof nextNonEmptyNode === 'string' && (0, _helpers.isComment)(nextNode)) {
isHeadEndTagCanBeOmitted = false;
}
if (isHeadStartTagCanBeOmitted && isHeadEndTagCanBeOmitted) {
node.tag = false;
}
}
/**
* A "body" element's start tag may be omitted if the element is empty, or if the first thing inside the "body" element is not ASCII whitespace or a comment, except if the first thing inside the "body" element is a "meta", "link", "script", "style", or "template" element.
* A "body" element's end tag may be omitted if the "body" element is not IMMEDIATELY followed by a comment.
*/
if (node.tag === 'body') {
let isBodyStartTagCanBeOmitted = true;
let isBodyEndTagCanBeOmitted = true;
if (typeof firstChildNode === 'string' && startWithWhitespacePattern.test(firstChildNode) || typeof firstNonEmptyChildNode === 'string' && (0, _helpers.isComment)(firstNonEmptyChildNode)) {
isBodyStartTagCanBeOmitted = false;
}
if (firstNonEmptyChildNode && firstNonEmptyChildNode.tag && bodyStartTagCantBeOmittedWithFirstChildTags.has(firstNonEmptyChildNode.tag)) {
isBodyStartTagCanBeOmitted = false;
}
if (nextNode && typeof nextNode === 'string' && (0, _helpers.isComment)(nextNode)) {
isBodyEndTagCanBeOmitted = false;
}
if (isBodyStartTagCanBeOmitted && isBodyEndTagCanBeOmitted) {
node.tag = false;
}
}
/**
* A "colgroup" element's start tag may be omitted if the first thing inside the "colgroup" element is a "col" element, and if the element is not IMMEDIATELY preceded by another "colgroup" element. It can't be omitted if the element is empty.
* A "colgroup" element's end tag may be omitted if the "colgroup" element is not IMMEDIATELY followed by ASCII whitespace or a comment.
*/
if (node.tag === 'colgroup') {
let isColgroupStartTagCanBeOmitted = false;
let isColgroupEndTagCanBeOmitted = true;
if (firstNonEmptyChildNode && firstNonEmptyChildNode.tag && firstNonEmptyChildNode.tag === 'col') {
isColgroupStartTagCanBeOmitted = true;
}
if (prevNonEmptyNode && prevNonEmptyNode.tag && prevNonEmptyNode.tag === 'colgroup') {
isColgroupStartTagCanBeOmitted = false;
}
if (nextNode && typeof nextNode === 'string' && startWithWhitespacePattern.test(nextNode) || nextNonEmptyNode && typeof nextNonEmptyNode === 'string' && (0, _helpers.isComment)(nextNonEmptyNode)) {
isColgroupEndTagCanBeOmitted = false;
}
if (isColgroupStartTagCanBeOmitted && isColgroupEndTagCanBeOmitted) {
node.tag = false;
}
}
/**
* A "tbody" element's start tag may be omitted if the first thing inside the "tbody" element is a "tr" element, and if the element is not immediately preceded by another "tbody", "thead" or "tfoot" element. It can't be omitted if the element is empty.
* A "tbody" element's end tag may be omitted if the "tbody" element is not IMMEDIATELY followed by a "tbody" or "tfoot" element.
*/
if (node.tag === 'tbody') {
let isTbodyStartTagCanBeOmitted = false;
let isTbodyEndTagCanBeOmitted = true;
if (firstNonEmptyChildNode && firstNonEmptyChildNode.tag && firstNonEmptyChildNode.tag === 'tr') {
isTbodyStartTagCanBeOmitted = true;
}
if (prevNonEmptyNode && prevNonEmptyNode.tag && tbodyStartTagCantBeOmittedWithPrecededTags.has(prevNonEmptyNode.tag)) {
isTbodyStartTagCanBeOmitted = false;
}
if (nextNonEmptyNode && nextNonEmptyNode.tag && tbodyEndTagCantBeOmittedWithFollowedTags.has(nextNonEmptyNode.tag)) {
isTbodyEndTagCanBeOmitted = false;
}
if (isTbodyStartTagCanBeOmitted && isTbodyEndTagCanBeOmitted) {
node.tag = false;
}
}
if (node.content && node.content.length) {
removeOptionalTags(node.content);
}
return node;
});
return tree;
}

View file

@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = removeRedundantAttributes;
exports.redundantScriptTypes = void 0;
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#JavaScript_types
const redundantScriptTypes = new Set(['application/javascript', 'application/ecmascript', 'application/x-ecmascript', 'application/x-javascript', 'text/javascript', 'text/ecmascript', 'text/javascript1.0', 'text/javascript1.1', 'text/javascript1.2', 'text/javascript1.3', 'text/javascript1.4', 'text/javascript1.5', 'text/jscript', 'text/livescript', 'text/x-ecmascript', 'text/x-javascript']);
exports.redundantScriptTypes = redundantScriptTypes;
const redundantAttributes = {
'form': {
'method': 'get'
},
'input': {
'type': 'text'
},
'button': {
'type': 'submit'
},
'script': {
'language': 'javascript',
'type': node => {
for (const [attrName, attrValue] of Object.entries(node.attrs)) {
if (attrName.toLowerCase() !== 'type') {
continue;
}
return redundantScriptTypes.has(attrValue);
}
return false;
},
// Remove attribute if the function returns false
'charset': node => {
// The charset attribute only really makes sense on “external” SCRIPT elements:
// http://perfectionkills.com/optimizing-html/#8_script_charset
return node.attrs && !node.attrs.src;
}
},
'style': {
'media': 'all',
'type': 'text/css'
},
'link': {
'media': 'all',
'type': node => {
// https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet
let isRelStyleSheet = false;
let isTypeTextCSS = false;
if (node.attrs) {
for (const [attrName, attrValue] of Object.entries(node.attrs)) {
if (attrName.toLowerCase() === 'rel' && attrValue === 'stylesheet') {
isRelStyleSheet = true;
}
if (attrName.toLowerCase() === 'type' && attrValue === 'text/css') {
isTypeTextCSS = true;
}
}
} // Only "text/css" is redudant for link[rel=stylesheet]. Otherwise "type" shouldn't be removed
return isRelStyleSheet && isTypeTextCSS;
}
},
// See: https://html.spec.whatwg.org/#lazy-loading-attributes
'img': {
'loading': 'eager'
},
'iframe': {
'loading': 'eager'
}
};
const tagsHaveRedundantAttributes = new Set(Object.keys(redundantAttributes));
/** Removes redundant attributes */
function removeRedundantAttributes(tree) {
tree.walk(node => {
if (!node.tag) {
return node;
}
if (!tagsHaveRedundantAttributes.has(node.tag)) {
return node;
}
const tagRedundantAttributes = redundantAttributes[node.tag];
node.attrs = node.attrs || {};
for (const redundantAttributeName of Object.keys(tagRedundantAttributes)) {
let tagRedundantAttributeValue = tagRedundantAttributes[redundantAttributeName];
let isRemove = false;
if (typeof tagRedundantAttributeValue === 'function') {
isRemove = tagRedundantAttributeValue(node);
} else if (node.attrs[redundantAttributeName] === tagRedundantAttributeValue) {
isRemove = true;
}
if (isRemove) {
delete node.attrs[redundantAttributeName];
}
}
return node;
});
return tree;
}

View file

@ -0,0 +1,125 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = removeUnusedCss;
var _helpers = require("../helpers");
var _uncss = _interopRequireDefault(require("uncss"));
var _purgecss = _interopRequireDefault(require("purgecss"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// These options must be set and shouldn't be overriden to ensure uncss doesn't look at linked stylesheets.
const uncssOptions = {
ignoreSheets: [/\s*/],
stylesheets: []
};
function processStyleNodeUnCSS(html, styleNode, uncssOptions) {
const css = (0, _helpers.extractCssFromStyleNode)(styleNode);
return runUncss(html, css, uncssOptions).then(css => {
// uncss may have left some style tags empty
if (css.trim().length === 0) {
styleNode.tag = false;
styleNode.content = [];
return;
}
styleNode.content = [css];
});
}
function runUncss(html, css, userOptions) {
if (typeof userOptions !== 'object') {
userOptions = {};
}
const options = { ...userOptions,
...uncssOptions
};
return new Promise((resolve, reject) => {
options.raw = css;
(0, _uncss.default)(html, options, (error, output) => {
if (error) {
reject(error);
return;
}
resolve(output);
});
});
}
const purgeFromHtml = function (tree) {
// content is not used as we can directly used the parsed HTML,
// making the process faster
const selectors = [];
tree.walk(node => {
const classes = node.attrs && node.attrs.class && node.attrs.class.split(' ') || [];
const ids = node.attrs && node.attrs.id && node.attrs.id.split(' ') || [];
selectors.push(...classes, ...ids);
node.tag && selectors.push(node.tag);
return node;
});
return () => selectors;
};
function processStyleNodePurgeCSS(tree, styleNode, purgecssOptions) {
const css = (0, _helpers.extractCssFromStyleNode)(styleNode);
return runPurgecss(tree, css, purgecssOptions).then(css => {
if (css.trim().length === 0) {
styleNode.tag = false;
styleNode.content = [];
return;
}
styleNode.content = [css];
});
}
function runPurgecss(tree, css, userOptions) {
if (typeof userOptions !== 'object') {
userOptions = {};
}
const options = { ...userOptions,
content: [{
raw: tree,
extension: 'html'
}],
css: [{
raw: css,
extension: 'css'
}],
extractors: [{
extractor: purgeFromHtml(tree),
extensions: ['html']
}]
};
return new _purgecss.default().purge(options).then(result => {
return result[0].css;
});
}
/** Remove unused CSS */
function removeUnusedCss(tree, options, userOptions) {
const promises = [];
const html = userOptions.tool !== 'purgeCSS' && tree.render(tree);
tree.walk(node => {
if ((0, _helpers.isStyleNode)(node)) {
if (userOptions.tool === 'purgeCSS') {
promises.push(processStyleNodePurgeCSS(tree, node, userOptions));
} else {
promises.push(processStyleNodeUnCSS(html, node, userOptions));
}
}
return node;
});
return Promise.all(promises).then(() => tree);
}

115
BACK_BACK/node_modules/htmlnano/lib/modules/sortAttributes.js generated vendored Executable file
View file

@ -0,0 +1,115 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = sortAttributes;
var _timsort = require("timsort");
const validOptions = new Set(['frequency', 'alphabetical']);
const processModuleOptions = options => {
if (options === true) return 'alphabetical';
return validOptions.has(options) ? options : false;
};
class AttributeTokenChain {
constructor() {
this.freqData = new Map(); // <attr, frequency>[]
}
addFromNodeAttrs(nodeAttrs) {
Object.keys(nodeAttrs).forEach(attrName => {
const attrNameLower = attrName.toLowerCase();
if (this.freqData.has(attrNameLower)) {
this.freqData.set(attrNameLower, this.freqData.get(attrNameLower) + 1);
} else {
this.freqData.set(attrNameLower, 1);
}
});
}
createSortOrder() {
let _sortOrder = [...this.freqData.entries()];
(0, _timsort.sort)(_sortOrder, (a, b) => b[1] - a[1]);
this.sortOrder = _sortOrder.map(i => i[0]);
}
sortFromNodeAttrs(nodeAttrs) {
const newAttrs = {}; // Convert node.attrs attrName into lower case.
const loweredNodeAttrs = {};
Object.entries(nodeAttrs).forEach(([attrName, attrValue]) => {
loweredNodeAttrs[attrName.toLowerCase()] = attrValue;
});
if (!this.sortOrder) {
this.createSortOrder();
}
this.sortOrder.forEach(attrNameLower => {
// The attrName inside "sortOrder" has been lowered
if (loweredNodeAttrs[attrNameLower]) {
newAttrs[attrNameLower] = loweredNodeAttrs[attrNameLower];
}
});
return newAttrs;
}
}
/** Sort attibutes */
function sortAttributes(tree, options, moduleOptions) {
const sortType = processModuleOptions(moduleOptions);
if (sortType === 'alphabetical') {
return sortAttributesInAlphabeticalOrder(tree);
}
if (sortType === 'frequency') {
return sortAttributesByFrequency(tree);
} // Invalid configuration
return tree;
}
function sortAttributesInAlphabeticalOrder(tree) {
tree.walk(node => {
if (!node.attrs) {
return node;
}
const newAttrs = {};
Object.keys(node.attrs).sort((a, b) => typeof a.localeCompare === 'function' ? a.localeCompare(b) : a - b).forEach(attr => newAttrs[attr] = node.attrs[attr]);
node.attrs = newAttrs;
return node;
});
return tree;
}
function sortAttributesByFrequency(tree) {
const tokenchain = new AttributeTokenChain(); // Traverse through tree to get frequency
tree.walk(node => {
if (!node.attrs) {
return node;
}
tokenchain.addFromNodeAttrs(node.attrs);
return node;
}); // Traverse through tree again, this time sort the attributes
tree.walk(node => {
if (!node.attrs) {
return node;
}
node.attrs = tokenchain.sortFromNodeAttrs(node.attrs);
return node;
});
return tree;
}

View file

@ -0,0 +1,138 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = collapseAttributeWhitespace;
var _timsort = require("timsort");
var _collapseAttributeWhitespace = require("./collapseAttributeWhitespace");
// class, rel, ping
const validOptions = new Set(['frequency', 'alphabetical']);
const processModuleOptions = options => {
if (options === true) return 'alphabetical';
return validOptions.has(options) ? options : false;
};
class AttributeTokenChain {
constructor() {
this.freqData = new Map(); // <attrValue, frequency>[]
}
addFromNodeAttrsArray(attrValuesArray) {
attrValuesArray.forEach(attrValue => {
if (this.freqData.has(attrValue)) {
this.freqData.set(attrValue, this.freqData.get(attrValue) + 1);
} else {
this.freqData.set(attrValue, 1);
}
});
}
createSortOrder() {
let _sortOrder = [...this.freqData.entries()];
(0, _timsort.sort)(_sortOrder, (a, b) => b[1] - a[1]);
this.sortOrder = _sortOrder.map(i => i[0]);
}
sortFromNodeAttrsArray(attrValuesArray) {
const resultArray = [];
if (!this.sortOrder) {
this.createSortOrder();
}
this.sortOrder.forEach(k => {
if (attrValuesArray.includes(k)) {
resultArray.push(k);
}
});
return resultArray;
}
}
/** Sort values inside list-like attributes (e.g. class, rel) */
function collapseAttributeWhitespace(tree, options, moduleOptions) {
const sortType = processModuleOptions(moduleOptions);
if (sortType === 'alphabetical') {
return sortAttributesWithListsInAlphabeticalOrder(tree);
}
if (sortType === 'frequency') {
return sortAttributesWithListsByFrequency(tree);
} // Invalid configuration
return tree;
}
function sortAttributesWithListsInAlphabeticalOrder(tree) {
tree.walk(node => {
if (!node.attrs) {
return node;
}
Object.keys(node.attrs).forEach(attrName => {
const attrNameLower = attrName.toLowerCase();
if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
return;
}
const attrValues = node.attrs[attrName].split(/\s/);
node.attrs[attrName] = attrValues.sort((a, b) => {
return typeof a.localeCompare === 'function' ? a.localeCompare(b) : a - b;
}).join(' ');
});
return node;
});
return tree;
}
function sortAttributesWithListsByFrequency(tree) {
const tokenChainObj = {}; // <attrNameLower: AttributeTokenChain>[]
// Traverse through tree to get frequency
tree.walk(node => {
if (!node.attrs) {
return node;
}
Object.entries(node.attrs).forEach(([attrName, attrValues]) => {
const attrNameLower = attrName.toLowerCase();
if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
return;
}
tokenChainObj[attrNameLower] = tokenChainObj[attrNameLower] || new AttributeTokenChain();
tokenChainObj[attrNameLower].addFromNodeAttrsArray(attrValues.split(/\s/));
});
return node;
}); // Traverse through tree again, this time sort the attribute values
tree.walk(node => {
if (!node.attrs) {
return node;
}
Object.entries(node.attrs).forEach(([attrName, attrValues]) => {
const attrNameLower = attrName.toLowerCase();
if (!_collapseAttributeWhitespace.attributesWithLists.has(attrNameLower)) {
return;
}
if (tokenChainObj[attrNameLower]) {
node.attrs[attrName] = tokenChainObj[attrNameLower].sortFromNodeAttrsArray(attrValues.split(/\s/)).join(' ');
}
});
return node;
});
}

26
BACK_BACK/node_modules/htmlnano/lib/presets/ampSafe.js generated vendored Executable file
View file

@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _safe = _interopRequireDefault(require("./safe"));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
/**
* A safe preset for AMP pages (https://www.ampproject.org)
*/
var _default = { ..._safe.default,
collapseBooleanAttributes: {
amphtml: true
},
minifyJs: false
};
exports.default = _default;

33
BACK_BACK/node_modules/htmlnano/lib/presets/max.js generated vendored Executable file
View file

@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _safe = _interopRequireDefault(require("./safe"));
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
/**
* Maximal minification (might break some pages)
*/
var _default = { ..._safe.default,
collapseWhitespace: 'all',
removeComments: 'all',
removeAttributeQuotes: true,
removeRedundantAttributes: true,
removeUnusedCss: {},
minifyCss: {
preset: 'default'
},
minifySvg: {},
minifyConditionalComments: true,
removeOptionalTags: true
};
exports.default = _default;

44
BACK_BACK/node_modules/htmlnano/lib/presets/safe.js generated vendored Executable file
View file

@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
/**
* Minify HTML in a safe way without breaking anything.
*/
var _default = {
sortAttributes: false,
collapseAttributeWhitespace: true,
collapseBooleanAttributes: {
amphtml: false
},
collapseWhitespace: 'conservative',
custom: [],
deduplicateAttributeValues: true,
mergeScripts: true,
mergeStyles: true,
removeUnusedCss: false,
minifyCss: {
preset: 'default'
},
minifyJs: {},
minifyJson: {},
minifySvg: {
plugins: [{
collapseGroups: false
}, {
convertShapeToPath: false
}]
},
minifyConditionalComments: false,
removeEmptyAttributes: true,
removeRedundantAttributes: false,
removeComments: 'safe',
removeAttributeQuotes: false,
sortAttributesWithLists: 'alphabetical',
minifyUrls: false,
removeOptionalTags: false
};
exports.default = _default;