mirror of
https://github.com/SamEyeBam/animate.git
synced 2026-06-10 02:16:44 +00:00
larry babby and threejs for glsl
This commit is contained in:
86
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseAttributeWhitespace.cjs
generated
vendored
Normal file
86
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseAttributeWhitespace.cjs
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.attributesWithLists = void 0;
|
||||
exports.onAttrs = onAttrs;
|
||||
var _helpers = require("../helpers.cjs");
|
||||
const attributesWithLists = exports.attributesWithLists = new Set(['class', 'dropzone', 'rel',
|
||||
// a, area, link
|
||||
'ping',
|
||||
// a, area
|
||||
'sandbox',
|
||||
// iframe
|
||||
/**
|
||||
* https://github.com/posthtml/htmlnano/issues/180
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-sizes
|
||||
*
|
||||
* "sizes" of <img> should not be modified, while "sizes" of <link> will only have one entry in most cases.
|
||||
*/
|
||||
// 'sizes', // link
|
||||
'headers' // td, th
|
||||
]);
|
||||
|
||||
/** @type Record<string, string[] | null> */
|
||||
const attributesWithSingleValue = {
|
||||
accept: ['input'],
|
||||
action: ['form'],
|
||||
accesskey: null,
|
||||
'accept-charset': ['form'],
|
||||
cite: ['blockquote', 'del', 'ins', 'q'],
|
||||
cols: ['textarea'],
|
||||
colspan: ['td', 'th'],
|
||||
data: ['object'],
|
||||
dropzone: null,
|
||||
formaction: ['button', 'input'],
|
||||
height: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
|
||||
high: ['meter'],
|
||||
href: ['a', 'area', 'base', 'link'],
|
||||
itemid: null,
|
||||
low: ['meter'],
|
||||
manifest: ['html'],
|
||||
max: ['meter', 'progress'],
|
||||
maxlength: ['input', 'textarea'],
|
||||
media: ['source'],
|
||||
min: ['meter'],
|
||||
minlength: ['input', 'textarea'],
|
||||
optimum: ['meter'],
|
||||
ping: ['a', 'area'],
|
||||
poster: ['video'],
|
||||
profile: ['head'],
|
||||
rows: ['textarea'],
|
||||
rowspan: ['td', 'th'],
|
||||
size: ['input', 'select'],
|
||||
span: ['col', 'colgroup'],
|
||||
src: ['audio', 'embed', 'iframe', 'img', 'input', 'script', 'source', 'track', 'video'],
|
||||
start: ['ol'],
|
||||
step: ['input'],
|
||||
style: null,
|
||||
tabindex: null,
|
||||
usemap: ['img', 'object'],
|
||||
value: ['li', 'meter', 'progress'],
|
||||
width: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video']
|
||||
};
|
||||
|
||||
/** Collapse whitespaces inside list-like attributes (e.g. class, rel) */
|
||||
function onAttrs() {
|
||||
return (attrs, node) => {
|
||||
const newAttrs = attrs;
|
||||
Object.entries(attrs).forEach(([attrName, attrValue]) => {
|
||||
if (typeof attrValue !== 'string') return;
|
||||
if (attributesWithLists.has(attrName)) {
|
||||
const newAttrValue = attrValue.replace(/\s+/g, ' ').trim();
|
||||
newAttrs[attrName] = newAttrValue;
|
||||
return;
|
||||
}
|
||||
if ((0, _helpers.isEventHandler)(attrName) || Object.prototype.hasOwnProperty.call(attributesWithSingleValue, attrName) && (attributesWithSingleValue[attrName] === null || attributesWithSingleValue[attrName].includes(node.tag))) {
|
||||
newAttrs[attrName] = minifySingleAttributeValue(attrValue);
|
||||
}
|
||||
});
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
function minifySingleAttributeValue(value) {
|
||||
return typeof value === 'string' ? value.trim() : value;
|
||||
}
|
||||
104
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseAttributeWhitespace.mjs
generated
vendored
Normal file
104
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseAttributeWhitespace.mjs
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
import { isEventHandler } from '../helpers.mjs';
|
||||
|
||||
export const attributesWithLists = new Set([
|
||||
'class',
|
||||
'dropzone',
|
||||
'rel', // a, area, link
|
||||
'ping', // a, area
|
||||
'sandbox', // iframe
|
||||
/**
|
||||
* https://github.com/posthtml/htmlnano/issues/180
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link#attr-sizes
|
||||
*
|
||||
* "sizes" of <img> should not be modified, while "sizes" of <link> will only have one entry in most cases.
|
||||
*/
|
||||
// 'sizes', // link
|
||||
'headers' // td, th
|
||||
]);
|
||||
|
||||
/** @type Record<string, string[] | null> */
|
||||
const attributesWithSingleValue = {
|
||||
accept: ['input'],
|
||||
action: ['form'],
|
||||
accesskey: null,
|
||||
'accept-charset': ['form'],
|
||||
cite: ['blockquote', 'del', 'ins', 'q'],
|
||||
cols: ['textarea'],
|
||||
colspan: ['td', 'th'],
|
||||
data: ['object'],
|
||||
dropzone: null,
|
||||
formaction: ['button', 'input'],
|
||||
height: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
|
||||
high: ['meter'],
|
||||
href: ['a', 'area', 'base', 'link'],
|
||||
itemid: null,
|
||||
low: ['meter'],
|
||||
manifest: ['html'],
|
||||
max: ['meter', 'progress'],
|
||||
maxlength: ['input', 'textarea'],
|
||||
media: ['source'],
|
||||
min: ['meter'],
|
||||
minlength: ['input', 'textarea'],
|
||||
optimum: ['meter'],
|
||||
ping: ['a', 'area'],
|
||||
poster: ['video'],
|
||||
profile: ['head'],
|
||||
rows: ['textarea'],
|
||||
rowspan: ['td', 'th'],
|
||||
size: ['input', 'select'],
|
||||
span: ['col', 'colgroup'],
|
||||
src: [
|
||||
'audio',
|
||||
'embed',
|
||||
'iframe',
|
||||
'img',
|
||||
'input',
|
||||
'script',
|
||||
'source',
|
||||
'track',
|
||||
'video'
|
||||
],
|
||||
start: ['ol'],
|
||||
step: ['input'],
|
||||
style: null,
|
||||
tabindex: null,
|
||||
usemap: ['img', 'object'],
|
||||
value: ['li', 'meter', 'progress'],
|
||||
width: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video']
|
||||
};
|
||||
|
||||
/** Collapse whitespaces inside list-like attributes (e.g. class, rel) */
|
||||
export function onAttrs() {
|
||||
return (attrs, node) => {
|
||||
const newAttrs = attrs;
|
||||
|
||||
Object.entries(attrs).forEach(([attrName, attrValue]) => {
|
||||
if (typeof attrValue !== 'string') return;
|
||||
|
||||
if (attributesWithLists.has(attrName)) {
|
||||
const newAttrValue = attrValue.replace(/\s+/g, ' ').trim();
|
||||
newAttrs[attrName] = newAttrValue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
isEventHandler(attrName)
|
||||
|| (
|
||||
Object.prototype.hasOwnProperty.call(attributesWithSingleValue, attrName)
|
||||
&& (
|
||||
attributesWithSingleValue[attrName] === null
|
||||
|| attributesWithSingleValue[attrName].includes(node.tag)
|
||||
)
|
||||
)
|
||||
) {
|
||||
newAttrs[attrName] = minifySingleAttributeValue(attrValue);
|
||||
}
|
||||
});
|
||||
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
|
||||
function minifySingleAttributeValue(value) {
|
||||
return typeof value === 'string' ? value.trim() : value;
|
||||
}
|
||||
62
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseBooleanAttributes.cjs
generated
vendored
Normal file
62
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseBooleanAttributes.cjs
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.onAttrs = onAttrs;
|
||||
// Source: https://github.com/kangax/html-minifier/issues/63
|
||||
// https://html.spec.whatwg.org/#boolean-attribute
|
||||
// https://html.spec.whatwg.org/#attributes-1
|
||||
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', 'nomodule', 'noresize', 'noshade', 'novalidate', 'nowrap', 'open', 'pauseonexit', 'playsinline', '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']);
|
||||
const missingValueDefaultEmptyStringAttributes = {
|
||||
// https://html.spec.whatwg.org/#attr-media-preload
|
||||
audio: {
|
||||
preload: 'auto'
|
||||
},
|
||||
video: {
|
||||
preload: 'auto'
|
||||
}
|
||||
};
|
||||
const tagsHasMissingValueDefaultEmptyStringAttributes = new Set(Object.keys(missingValueDefaultEmptyStringAttributes));
|
||||
function onAttrs(options, moduleOptions) {
|
||||
return (attrs, node) => {
|
||||
if (!node.tag) return attrs;
|
||||
const newAttrs = attrs;
|
||||
if (tagsHasMissingValueDefaultEmptyStringAttributes.has(node.tag)) {
|
||||
const tagAttributesCanBeReplacedWithEmptyString = missingValueDefaultEmptyStringAttributes[node.tag];
|
||||
for (const attributesCanBeReplacedWithEmptyString of Object.keys(tagAttributesCanBeReplacedWithEmptyString)) {
|
||||
if (Object.prototype.hasOwnProperty.call(attrs, attributesCanBeReplacedWithEmptyString) && attrs[attributesCanBeReplacedWithEmptyString] === tagAttributesCanBeReplacedWithEmptyString[attributesCanBeReplacedWithEmptyString]) {
|
||||
attrs[attributesCanBeReplacedWithEmptyString] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const attrName of Object.keys(attrs)) {
|
||||
if (attrName === 'visible' && node.tag.startsWith('a-')) {
|
||||
continue;
|
||||
}
|
||||
if (htmlBooleanAttributes.has(attrName)) {
|
||||
newAttrs[attrName] = true;
|
||||
}
|
||||
|
||||
// Fast path optimization.
|
||||
// The rest of tranformations are only for string type attrValue.
|
||||
if (typeof newAttrs[attrName] !== 'string') continue;
|
||||
if (moduleOptions.amphtml && amphtmlBooleanAttributes.has(attrName) && attrs[attrName] === '') {
|
||||
newAttrs[attrName] = true;
|
||||
}
|
||||
// https://html.spec.whatwg.org/#a-quick-introduction-to-html
|
||||
// The value, along with the "=" character, can be omitted altogether if the value is the empty string.
|
||||
if (attrs[attrName] === '') {
|
||||
newAttrs[attrName] = true;
|
||||
}
|
||||
|
||||
// collapse crossorigin attributes
|
||||
// Specification: https://html.spec.whatwg.org/multipage/urls-and-fetching.html#cors-settings-attributes
|
||||
if (attrName.toLowerCase() === 'crossorigin' && attrs[attrName] === 'anonymous') {
|
||||
newAttrs[attrName] = true;
|
||||
}
|
||||
}
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
175
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseBooleanAttributes.mjs
generated
vendored
Normal file
175
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseBooleanAttributes.mjs
generated
vendored
Normal file
@@ -0,0 +1,175 @@
|
||||
// Source: https://github.com/kangax/html-minifier/issues/63
|
||||
// https://html.spec.whatwg.org/#boolean-attribute
|
||||
// https://html.spec.whatwg.org/#attributes-1
|
||||
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',
|
||||
'nomodule',
|
||||
'noresize',
|
||||
'noshade',
|
||||
'novalidate',
|
||||
'nowrap',
|
||||
'open',
|
||||
'pauseonexit',
|
||||
'playsinline',
|
||||
'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'
|
||||
]);
|
||||
|
||||
const missingValueDefaultEmptyStringAttributes = {
|
||||
// https://html.spec.whatwg.org/#attr-media-preload
|
||||
audio: {
|
||||
preload: 'auto'
|
||||
},
|
||||
video: {
|
||||
preload: 'auto'
|
||||
}
|
||||
};
|
||||
|
||||
const tagsHasMissingValueDefaultEmptyStringAttributes = new Set(Object.keys(missingValueDefaultEmptyStringAttributes));
|
||||
|
||||
export function onAttrs(options, moduleOptions) {
|
||||
return (attrs, node) => {
|
||||
if (!node.tag) return attrs;
|
||||
|
||||
const newAttrs = attrs;
|
||||
|
||||
if (tagsHasMissingValueDefaultEmptyStringAttributes.has(node.tag)) {
|
||||
const tagAttributesCanBeReplacedWithEmptyString = missingValueDefaultEmptyStringAttributes[node.tag];
|
||||
|
||||
for (const attributesCanBeReplacedWithEmptyString of Object.keys(tagAttributesCanBeReplacedWithEmptyString)) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(attrs, attributesCanBeReplacedWithEmptyString)
|
||||
&& attrs[attributesCanBeReplacedWithEmptyString] === tagAttributesCanBeReplacedWithEmptyString[attributesCanBeReplacedWithEmptyString]
|
||||
) {
|
||||
attrs[attributesCanBeReplacedWithEmptyString] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const attrName of Object.keys(attrs)) {
|
||||
if (attrName === 'visible' && node.tag.startsWith('a-')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (htmlBooleanAttributes.has(attrName)) {
|
||||
newAttrs[attrName] = true;
|
||||
}
|
||||
|
||||
// Fast path optimization.
|
||||
// The rest of tranformations are only for string type attrValue.
|
||||
if (typeof newAttrs[attrName] !== 'string') continue;
|
||||
|
||||
if (moduleOptions.amphtml && amphtmlBooleanAttributes.has(attrName) && attrs[attrName] === '') {
|
||||
newAttrs[attrName] = true;
|
||||
}
|
||||
// https://html.spec.whatwg.org/#a-quick-introduction-to-html
|
||||
// The value, along with the "=" character, can be omitted altogether if the value is the empty string.
|
||||
if (attrs[attrName] === '') {
|
||||
newAttrs[attrName] = true;
|
||||
}
|
||||
|
||||
// collapse crossorigin attributes
|
||||
// Specification: https://html.spec.whatwg.org/multipage/urls-and-fetching.html#cors-settings-attributes
|
||||
if (
|
||||
attrName.toLowerCase() === 'crossorigin' && (
|
||||
attrs[attrName] === 'anonymous'
|
||||
)
|
||||
) {
|
||||
newAttrs[attrName] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
100
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseWhitespace.cjs
generated
vendored
Normal file
100
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseWhitespace.cjs
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = collapseWhitespace;
|
||||
var _helpers = require("../helpers.cjs");
|
||||
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 startsWithWhitespacePattern = /^\s/;
|
||||
const endsWithWhitespacePattern = /\s$/;
|
||||
// See https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace and https://infra.spec.whatwg.org/#ascii-whitespace
|
||||
const multipleWhitespacePattern = /[\t\n\f\r ]+/g;
|
||||
const NONE = '';
|
||||
const SINGLE_SPACE = ' ';
|
||||
const validOptions = ['all', 'aggressive', 'conservative'];
|
||||
|
||||
/** Collapses redundant whitespaces */
|
||||
function collapseWhitespace(tree, options, collapseType, parent) {
|
||||
collapseType = validOptions.includes(collapseType) ? collapseType : 'conservative';
|
||||
tree.forEach((node, index) => {
|
||||
const prevNode = tree[index - 1];
|
||||
const nextNode = tree[index + 1];
|
||||
if (typeof node === 'string') {
|
||||
const parentNodeTag = parent && parent.node && parent.node.tag;
|
||||
const isTopLevel = !parentNodeTag || parentNodeTag === 'html' || parentNodeTag === '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';
|
||||
node = collapseRedundantWhitespaces(node, collapseType, shouldTrim, parent, prevNode, nextNode);
|
||||
}
|
||||
const isAllowCollapseWhitespace = !noWhitespaceCollapseElements.has(node.tag);
|
||||
if (node.content && node.content.length && isAllowCollapseWhitespace) {
|
||||
node.content = collapseWhitespace(node.content, options, collapseType, {
|
||||
node,
|
||||
prevNode,
|
||||
nextNode
|
||||
});
|
||||
}
|
||||
tree[index] = node;
|
||||
});
|
||||
return tree;
|
||||
}
|
||||
function collapseRedundantWhitespaces(text, collapseType, shouldTrim = false, parent, prevNode, nextNode) {
|
||||
if (!text || text.length === 0) {
|
||||
return NONE;
|
||||
}
|
||||
if (!(0, _helpers.isComment)(text)) {
|
||||
text = text.replace(multipleWhitespacePattern, SINGLE_SPACE);
|
||||
}
|
||||
if (shouldTrim) {
|
||||
if (collapseType === 'aggressive') {
|
||||
if (!noTrimWhitespacesInsideElements.has(parent && parent.node && parent.node.tag)) {
|
||||
if (
|
||||
// It is the first child node of the parent
|
||||
!prevNode
|
||||
// It is not the first child node, and prevNode not a text node, and prevNode is safe to trim around
|
||||
|| prevNode && prevNode.tag && !noTrimWhitespacesArroundElements.has(prevNode.tag)) {
|
||||
text = text.trimStart();
|
||||
} else {
|
||||
// previous node is a "no trim whitespaces arround element"
|
||||
if (
|
||||
// but previous node ends with a whitespace
|
||||
prevNode && prevNode.content && prevNode.content.length && endsWithWhitespacePattern.test(prevNode.content[prevNode.content.length - 1]) && (!nextNode // either the current node is the last child of the parent
|
||||
||
|
||||
// or the next node starts with a white space
|
||||
nextNode && nextNode.content && nextNode.content.length && !startsWithWhitespacePattern.test(nextNode.content[0]))) {
|
||||
text = text.trimStart();
|
||||
}
|
||||
}
|
||||
if (!nextNode || nextNode && nextNode.tag && !noTrimWhitespacesArroundElements.has(nextNode.tag)) {
|
||||
text = text.trimEnd();
|
||||
}
|
||||
} else {
|
||||
// now it is a textNode inside a "no trim whitespaces inside elements" node
|
||||
if (!prevNode // it the textnode is the first child of the node
|
||||
&& startsWithWhitespacePattern.test(text[0]) // it starts with white space
|
||||
&& typeof parent.prevNode === 'string' // the prev of the node is a textNode as well
|
||||
&& endsWithWhitespacePattern.test(parent.prevNode[parent.prevNode.length - 1]) // that prev is ends with a white
|
||||
) {
|
||||
text = text.trimStart();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// collapseType is 'all', trim spaces
|
||||
text = text.trim();
|
||||
}
|
||||
}
|
||||
return text;
|
||||
}
|
||||
132
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseWhitespace.mjs
generated
vendored
Normal file
132
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/collapseWhitespace.mjs
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
import { isComment } from '../helpers.mjs';
|
||||
|
||||
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 startsWithWhitespacePattern = /^\s/;
|
||||
const endsWithWhitespacePattern = /\s$/;
|
||||
// See https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace and https://infra.spec.whatwg.org/#ascii-whitespace
|
||||
const multipleWhitespacePattern = /[\t\n\f\r ]+/g;
|
||||
const NONE = '';
|
||||
const SINGLE_SPACE = ' ';
|
||||
const validOptions = ['all', 'aggressive', 'conservative'];
|
||||
|
||||
/** Collapses redundant whitespaces */
|
||||
export default function collapseWhitespace(tree, options, collapseType, parent) {
|
||||
collapseType = validOptions.includes(collapseType) ? collapseType : 'conservative';
|
||||
|
||||
tree.forEach((node, index) => {
|
||||
const prevNode = tree[index - 1];
|
||||
const nextNode = tree[index + 1];
|
||||
|
||||
if (typeof node === 'string') {
|
||||
const parentNodeTag = parent && parent.node && parent.node.tag;
|
||||
const isTopLevel = !parentNodeTag || parentNodeTag === 'html' || parentNodeTag === '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'
|
||||
);
|
||||
|
||||
node = collapseRedundantWhitespaces(node, collapseType, shouldTrim, parent, prevNode, nextNode);
|
||||
}
|
||||
|
||||
const isAllowCollapseWhitespace = !noWhitespaceCollapseElements.has(node.tag);
|
||||
if (node.content && node.content.length && isAllowCollapseWhitespace) {
|
||||
node.content = collapseWhitespace(node.content, options, collapseType, {
|
||||
node,
|
||||
prevNode,
|
||||
nextNode
|
||||
});
|
||||
}
|
||||
|
||||
tree[index] = node;
|
||||
});
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
function collapseRedundantWhitespaces(text, collapseType, shouldTrim = false, parent, prevNode, nextNode) {
|
||||
if (!text || text.length === 0) {
|
||||
return NONE;
|
||||
}
|
||||
|
||||
if (!isComment(text)) {
|
||||
text = text.replace(multipleWhitespacePattern, SINGLE_SPACE);
|
||||
}
|
||||
|
||||
if (shouldTrim) {
|
||||
if (collapseType === 'aggressive') {
|
||||
if (!noTrimWhitespacesInsideElements.has(parent && parent.node && parent.node.tag)) {
|
||||
if (
|
||||
// It is the first child node of the parent
|
||||
!prevNode
|
||||
// It is not the first child node, and prevNode not a text node, and prevNode is safe to trim around
|
||||
|| prevNode && prevNode.tag && !noTrimWhitespacesArroundElements.has(prevNode.tag)
|
||||
) {
|
||||
text = text.trimStart();
|
||||
} else {
|
||||
// previous node is a "no trim whitespaces arround element"
|
||||
if (
|
||||
// but previous node ends with a whitespace
|
||||
prevNode && prevNode.content && prevNode.content.length
|
||||
&& endsWithWhitespacePattern.test(prevNode.content[prevNode.content.length - 1])
|
||||
&& (
|
||||
!nextNode // either the current node is the last child of the parent
|
||||
|| (
|
||||
// or the next node starts with a white space
|
||||
nextNode && nextNode.content && nextNode.content.length
|
||||
&& !startsWithWhitespacePattern.test(nextNode.content[0])
|
||||
)
|
||||
)
|
||||
) {
|
||||
text = text.trimStart();
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!nextNode
|
||||
|| nextNode && nextNode.tag && !noTrimWhitespacesArroundElements.has(nextNode.tag)
|
||||
) {
|
||||
text = text.trimEnd();
|
||||
}
|
||||
} else {
|
||||
// now it is a textNode inside a "no trim whitespaces inside elements" node
|
||||
if (
|
||||
!prevNode // it the textnode is the first child of the node
|
||||
&& startsWithWhitespacePattern.test(text[0]) // it starts with white space
|
||||
&& typeof parent.prevNode === 'string' // the prev of the node is a textNode as well
|
||||
&& endsWithWhitespacePattern.test(parent.prevNode[parent.prevNode.length - 1]) // that prev is ends with a white
|
||||
) {
|
||||
text = text.trimStart();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// collapseType is 'all', trim spaces
|
||||
text = text.trim();
|
||||
}
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
19
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/custom.cjs
generated
vendored
Normal file
19
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/custom.cjs
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
"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;
|
||||
}
|
||||
16
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/custom.mjs
generated
vendored
Normal file
16
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/custom.mjs
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
/** Meta-module that runs custom modules */
|
||||
export default function custom(tree, options, customModules) {
|
||||
if (! customModules) {
|
||||
return tree;
|
||||
}
|
||||
|
||||
if (! Array.isArray(customModules)) {
|
||||
customModules = [customModules];
|
||||
}
|
||||
|
||||
customModules.forEach(customModule => {
|
||||
tree = customModule(tree, options);
|
||||
});
|
||||
|
||||
return tree;
|
||||
}
|
||||
38
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/deduplicateAttributeValues.cjs
generated
vendored
Normal file
38
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/deduplicateAttributeValues.cjs
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.onAttrs = onAttrs;
|
||||
var _collapseAttributeWhitespace = require("./collapseAttributeWhitespace.cjs");
|
||||
/** Deduplicate values inside list-like attributes (e.g. class, rel) */
|
||||
function onAttrs() {
|
||||
return attrs => {
|
||||
const newAttrs = attrs;
|
||||
Object.keys(attrs).forEach(attrName => {
|
||||
if (!_collapseAttributeWhitespace.attributesWithLists.has(attrName)) {
|
||||
return;
|
||||
}
|
||||
if (typeof attrs[attrName] !== 'string') {
|
||||
return;
|
||||
}
|
||||
const attrValues = 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);
|
||||
});
|
||||
newAttrs[attrName] = deduplicatedAttrValues.join(' ');
|
||||
});
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
40
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/deduplicateAttributeValues.mjs
generated
vendored
Normal file
40
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/deduplicateAttributeValues.mjs
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
import { attributesWithLists } from './collapseAttributeWhitespace.mjs';
|
||||
|
||||
/** Deduplicate values inside list-like attributes (e.g. class, rel) */
|
||||
export function onAttrs() {
|
||||
return (attrs) => {
|
||||
const newAttrs = attrs;
|
||||
Object.keys(attrs).forEach(attrName => {
|
||||
if (! attributesWithLists.has(attrName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof attrs[attrName] !== 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
const attrValues = 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);
|
||||
});
|
||||
|
||||
newAttrs[attrName] = deduplicatedAttrValues.join(' ');
|
||||
});
|
||||
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
85
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/example.cjs
generated
vendored
Normal file
85
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/example.cjs
generated
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = example;
|
||||
exports.onAttrs = onAttrs;
|
||||
exports.onContent = onContent;
|
||||
exports.onNode = onNode;
|
||||
/**
|
||||
* It is an example htmlnano module.
|
||||
*
|
||||
* A htmlnano module can be modify the attributes of every node (through a "onAttrs" named export),
|
||||
* modify the content of every node (through an optional "onContent" named export), modify the node
|
||||
* itself (through an optional "onNode" named export), or modify the entire tree (through an optional
|
||||
* default export).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Modify attributes of node. Optional.
|
||||
*
|
||||
* @param {object} options - Options that were passed to htmlnano
|
||||
* @param moduleOptions — Module options. For most modules this is just "true" (indication that the module was enabled)
|
||||
* @return {Function} - Return a function that takes attribute object and the node (for the context), and returns the modified attribute object
|
||||
*/
|
||||
function onAttrs(options, moduleOptions) {
|
||||
return (attrs, node) => {
|
||||
// You can modify "attrs" based on "node"
|
||||
const newAttrs = {
|
||||
...attrs
|
||||
};
|
||||
return newAttrs; // ... then return the modified attrs
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify content of node. Optional.
|
||||
*
|
||||
* @param {object} options - Options that were passed to htmlnano
|
||||
* @param moduleOptions — Module options. For most modules this is just "true" (indication that the module was enabled)
|
||||
* @return {Function} - Return a function that takes contents (an array of node and string) and the node (for the context), and returns the modified content array.
|
||||
*/
|
||||
function onContent(options, moduleOptions) {
|
||||
return (content, node) => {
|
||||
// Same goes the "content"
|
||||
|
||||
return content; // ... return modified content here
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* It is possible to modify entire ndde as well. Optional.
|
||||
* @param {object} options - Options that were passed to htmlnano
|
||||
* @param moduleOptions — Module options. For most modules this is just "true" (indication that the module was enabled)
|
||||
* @return {Function} - Return a function that takes the node, and returns the new, modified node.
|
||||
*/
|
||||
function onNode(options, moduleOptions) {
|
||||
return node => {
|
||||
return node; // ... return new node here
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the entire tree. Optional.
|
||||
*
|
||||
* @param {object} tree - PostHTML tree (https://github.com/posthtml/posthtml/blob/master/README.md)
|
||||
* @param {object} options - Options that were passed to htmlnano
|
||||
* @param moduleOptions — Module options. For most modules this is just "true" (indication that the module was enabled)
|
||||
* @return {object | Proimse} - Return the modified tree.
|
||||
*/
|
||||
function example(tree, options, moduleOptions) {
|
||||
// Module filename (example.es6), exported default function name (example),
|
||||
// and test filename (example.js) must be the same.
|
||||
|
||||
// You can traverse the tree...
|
||||
tree.walk(node => {
|
||||
// ...and make some minification
|
||||
});
|
||||
|
||||
// At the end you must return the tree
|
||||
return tree;
|
||||
|
||||
// Or a promise with the tree
|
||||
return somePromise.then(() => tree);
|
||||
}
|
||||
75
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/example.mjs
generated
vendored
Normal file
75
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/example.mjs
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* It is an example htmlnano module.
|
||||
*
|
||||
* A htmlnano module can be modify the attributes of every node (through a "onAttrs" named export),
|
||||
* modify the content of every node (through an optional "onContent" named export), modify the node
|
||||
* itself (through an optional "onNode" named export), or modify the entire tree (through an optional
|
||||
* default export).
|
||||
*/
|
||||
|
||||
/**
|
||||
* Modify attributes of node. Optional.
|
||||
*
|
||||
* @param {object} options - Options that were passed to htmlnano
|
||||
* @param moduleOptions — Module options. For most modules this is just "true" (indication that the module was enabled)
|
||||
* @return {Function} - Return a function that takes attribute object and the node (for the context), and returns the modified attribute object
|
||||
*/
|
||||
export function onAttrs(options, moduleOptions) {
|
||||
return (attrs, node) => {
|
||||
// You can modify "attrs" based on "node"
|
||||
const newAttrs = { ...attrs };
|
||||
|
||||
return newAttrs; // ... then return the modified attrs
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify content of node. Optional.
|
||||
*
|
||||
* @param {object} options - Options that were passed to htmlnano
|
||||
* @param moduleOptions — Module options. For most modules this is just "true" (indication that the module was enabled)
|
||||
* @return {Function} - Return a function that takes contents (an array of node and string) and the node (for the context), and returns the modified content array.
|
||||
*/
|
||||
export function onContent(options, moduleOptions) {
|
||||
return (content, node) => {
|
||||
// Same goes the "content"
|
||||
|
||||
return content; // ... return modified content here
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* It is possible to modify entire ndde as well. Optional.
|
||||
* @param {object} options - Options that were passed to htmlnano
|
||||
* @param moduleOptions — Module options. For most modules this is just "true" (indication that the module was enabled)
|
||||
* @return {Function} - Return a function that takes the node, and returns the new, modified node.
|
||||
*/
|
||||
export function onNode(options, moduleOptions) {
|
||||
return (node) => {
|
||||
return node; // ... return new node here
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the entire tree. Optional.
|
||||
*
|
||||
* @param {object} tree - PostHTML tree (https://github.com/posthtml/posthtml/blob/master/README.md)
|
||||
* @param {object} options - Options that were passed to htmlnano
|
||||
* @param moduleOptions — Module options. For most modules this is just "true" (indication that the module was enabled)
|
||||
* @return {object | Proimse} - Return the modified tree.
|
||||
*/
|
||||
export default function example(tree, options, moduleOptions) {
|
||||
// Module filename (example.es6), exported default function name (example),
|
||||
// and test filename (example.js) must be the same.
|
||||
|
||||
// You can traverse the tree...
|
||||
tree.walk(node => {
|
||||
// ...and make some minification
|
||||
});
|
||||
|
||||
// At the end you must return the tree
|
||||
return tree;
|
||||
|
||||
// Or a promise with the tree
|
||||
return somePromise.then(() => tree);
|
||||
}
|
||||
54
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/mergeScripts.cjs
generated
vendored
Normal file
54
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/mergeScripts.cjs
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
"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 ('src' in nodeAttrs
|
||||
// Skip SRI, reasons are documented in "minifyJs" module
|
||||
|| 'integrity' in nodeAttrs) {
|
||||
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;
|
||||
}
|
||||
56
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/mergeScripts.mjs
generated
vendored
Normal file
56
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/mergeScripts.mjs
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
/* Merge multiple <script> into one */
|
||||
export default function mergeScripts (tree) {
|
||||
let scriptNodesIndex = {};
|
||||
let scriptSrcIndex = 1;
|
||||
|
||||
tree.match({ tag: 'script' }, node => {
|
||||
const nodeAttrs = node.attrs || {};
|
||||
if (
|
||||
'src' in nodeAttrs
|
||||
// Skip SRI, reasons are documented in "minifyJs" module
|
||||
|| 'integrity' in nodeAttrs
|
||||
) {
|
||||
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;
|
||||
}
|
||||
38
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/mergeStyles.cjs
generated
vendored
Normal file
38
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/mergeStyles.cjs
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = mergeStyles;
|
||||
var _helpers = require("../helpers.cjs");
|
||||
/* 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
|
||||
//
|
||||
// Also skip SRI, reasons are documented in "minifyJs" module
|
||||
if ('scoped' in nodeAttrs || 'integrity' in nodeAttrs) {
|
||||
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;
|
||||
}
|
||||
36
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/mergeStyles.mjs
generated
vendored
Normal file
36
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/mergeStyles.mjs
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import { isAmpBoilerplate } from '../helpers.mjs';
|
||||
|
||||
/* Merge multiple <style> into one */
|
||||
export default 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
|
||||
//
|
||||
// Also skip SRI, reasons are documented in "minifyJs" module
|
||||
if ('scoped' in nodeAttrs || 'integrity' in nodeAttrs) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (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;
|
||||
}
|
||||
47
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyConditionalComments.cjs
generated
vendored
Normal file
47
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyConditionalComments.cjs
generated
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = minifyConditionalComments;
|
||||
var _htmlnano = _interopRequireDefault(require("../htmlnano.cjs"));
|
||||
var _helpers = require("../helpers.cjs");
|
||||
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];
|
||||
}));
|
||||
}
|
||||
49
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyConditionalComments.mjs
generated
vendored
Normal file
49
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyConditionalComments.mjs
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
import htmlnano from '../htmlnano.mjs';
|
||||
import { isConditionalComment } from '../helpers.mjs';
|
||||
|
||||
// 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 */
|
||||
export default 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' && 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.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];
|
||||
}));
|
||||
}
|
||||
73
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyCss.cjs
generated
vendored
Normal file
73
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyCss.cjs
generated
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = minifyCss;
|
||||
var _helpers = require("../helpers.cjs");
|
||||
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 */
|
||||
async function minifyCss(tree, options, cssnanoOptions) {
|
||||
const cssnano = await (0, _helpers.optionalImport)('cssnano');
|
||||
const postcss = await (0, _helpers.optionalImport)('postcss');
|
||||
if (!cssnano || !postcss) {
|
||||
return tree;
|
||||
}
|
||||
let promises = [];
|
||||
tree.walk(node => {
|
||||
// Skip SRI, reasons are documented in "minifyJs" module
|
||||
if (node.attrs && 'integrity' in node.attrs) {
|
||||
return node;
|
||||
}
|
||||
if ((0, _helpers.isStyleNode)(node)) {
|
||||
promises.push(processStyleNode(node, cssnanoOptions, cssnano, postcss));
|
||||
} else if (node.attrs && node.attrs.style) {
|
||||
promises.push(processStyleAttr(node, cssnanoOptions, cssnano, postcss));
|
||||
}
|
||||
return node;
|
||||
});
|
||||
return Promise.all(promises).then(() => tree);
|
||||
}
|
||||
function processStyleNode(styleNode, cssnanoOptions, cssnano, postcss) {
|
||||
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 postcss([cssnano(cssnanoOptions)]).process(css, postcssOptions).then(result => {
|
||||
if (isCdataWrapped) {
|
||||
return styleNode.content = ['<![CDATA[' + result + ']]>'];
|
||||
}
|
||||
return styleNode.content = [result.css];
|
||||
});
|
||||
}
|
||||
function processStyleAttr(node, cssnanoOptions, cssnano, postcss) {
|
||||
// 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 postcss([cssnano(cssnanoOptions)]).process(wrappedStyle, postcssOptions).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;
|
||||
}
|
||||
88
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyCss.mjs
generated
vendored
Normal file
88
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyCss.mjs
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
import { isStyleNode, extractCssFromStyleNode, optionalImport } from '../helpers.mjs';
|
||||
|
||||
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 */
|
||||
export default async function minifyCss(tree, options, cssnanoOptions) {
|
||||
const cssnano = await optionalImport('cssnano');
|
||||
const postcss = await optionalImport('postcss');
|
||||
|
||||
if (!cssnano || !postcss) {
|
||||
return tree;
|
||||
}
|
||||
|
||||
let promises = [];
|
||||
tree.walk(node => {
|
||||
// Skip SRI, reasons are documented in "minifyJs" module
|
||||
if (node.attrs && 'integrity' in node.attrs) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (isStyleNode(node)) {
|
||||
promises.push(processStyleNode(node, cssnanoOptions, cssnano, postcss));
|
||||
} else if (node.attrs && node.attrs.style) {
|
||||
promises.push(processStyleAttr(node, cssnanoOptions, cssnano, postcss));
|
||||
}
|
||||
|
||||
return node;
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(() => tree);
|
||||
}
|
||||
|
||||
|
||||
function processStyleNode(styleNode, cssnanoOptions, cssnano, postcss) {
|
||||
let css = 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 postcss([cssnano(cssnanoOptions)])
|
||||
.process(css, postcssOptions)
|
||||
.then(result => {
|
||||
if (isCdataWrapped) {
|
||||
return styleNode.content = ['<![CDATA[' + result + ']]>'];
|
||||
}
|
||||
return styleNode.content = [result.css];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function processStyleAttr(node, cssnanoOptions, cssnano, postcss) {
|
||||
// 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 postcss([cssnano(cssnanoOptions)])
|
||||
.process(wrappedStyle, postcssOptions)
|
||||
.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;
|
||||
}
|
||||
103
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyJs.cjs
generated
vendored
Normal file
103
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyJs.cjs
generated
vendored
Normal file
@@ -0,0 +1,103 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = minifyJs;
|
||||
var _helpers = require("../helpers.cjs");
|
||||
var _removeRedundantAttributes = require("./removeRedundantAttributes.cjs");
|
||||
/** Minify JS with Terser */
|
||||
async function minifyJs(tree, options, terserOptions) {
|
||||
const terser = await (0, _helpers.optionalImport)('terser');
|
||||
if (!terser) return tree;
|
||||
let promises = [];
|
||||
tree.walk(node => {
|
||||
const nodeAttrs = node.attrs || {};
|
||||
|
||||
/**
|
||||
* Skip SRI
|
||||
*
|
||||
* If the input <script /> has an SRI attribute, it means that the original <script /> could be trusted,
|
||||
* and should not be altered anymore.
|
||||
*
|
||||
* htmlnano is exactly an MITM that SRI is designed to protect from. If htmlnano or its dependencies get
|
||||
* compromised and introduces malicious code, then it is up to the original SRI to protect the end user.
|
||||
*
|
||||
* So htmlnano will simply skip <script /> that has SRI.
|
||||
* If developers do trust htmlnano, they should generate SRI after htmlnano modify the <script />.
|
||||
*/
|
||||
if ('integrity' in nodeAttrs) {
|
||||
return node;
|
||||
}
|
||||
if (node.tag && node.tag === 'script') {
|
||||
const mimeType = nodeAttrs.type || 'text/javascript';
|
||||
if (_removeRedundantAttributes.redundantScriptTypes.has(mimeType) || mimeType === 'module') {
|
||||
promises.push(processScriptNode(node, terserOptions, terser));
|
||||
}
|
||||
}
|
||||
if (node.attrs) {
|
||||
promises = promises.concat(processNodeWithOnAttrs(node, terserOptions, terser));
|
||||
}
|
||||
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, terser) {
|
||||
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.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, terser) {
|
||||
const jsWrapperStart = 'a=function(){';
|
||||
const jsWrapperEnd = '};a();';
|
||||
const promises = [];
|
||||
for (const attrName of Object.keys(node.attrs || {})) {
|
||||
if (!(0, _helpers.isEventHandler)(attrName)) {
|
||||
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.minify(wrappedJs, terserOptions).then(({
|
||||
code
|
||||
}) => {
|
||||
let minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
|
||||
node.attrs[attrName] = minifiedJs;
|
||||
});
|
||||
promises.push(promise);
|
||||
}
|
||||
return promises;
|
||||
}
|
||||
121
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyJs.mjs
generated
vendored
Normal file
121
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyJs.mjs
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
import { isEventHandler, optionalImport } from '../helpers.mjs';
|
||||
import { redundantScriptTypes } from './removeRedundantAttributes.mjs';
|
||||
|
||||
/** Minify JS with Terser */
|
||||
export default async function minifyJs (tree, options, terserOptions) {
|
||||
const terser = await optionalImport('terser');
|
||||
|
||||
if (!terser) return tree;
|
||||
|
||||
let promises = [];
|
||||
tree.walk(node => {
|
||||
const nodeAttrs = node.attrs || {};
|
||||
|
||||
/**
|
||||
* Skip SRI
|
||||
*
|
||||
* If the input <script /> has an SRI attribute, it means that the original <script /> could be trusted,
|
||||
* and should not be altered anymore.
|
||||
*
|
||||
* htmlnano is exactly an MITM that SRI is designed to protect from. If htmlnano or its dependencies get
|
||||
* compromised and introduces malicious code, then it is up to the original SRI to protect the end user.
|
||||
*
|
||||
* So htmlnano will simply skip <script /> that has SRI.
|
||||
* If developers do trust htmlnano, they should generate SRI after htmlnano modify the <script />.
|
||||
*/
|
||||
if ('integrity' in nodeAttrs) {
|
||||
return node;
|
||||
}
|
||||
|
||||
if (node.tag && node.tag === 'script') {
|
||||
const mimeType = nodeAttrs.type || 'text/javascript';
|
||||
if (redundantScriptTypes.has(mimeType) || mimeType === 'module') {
|
||||
promises.push(processScriptNode(node, terserOptions, terser));
|
||||
}
|
||||
}
|
||||
|
||||
if (node.attrs) {
|
||||
promises = promises.concat(processNodeWithOnAttrs(node, terserOptions, terser));
|
||||
}
|
||||
|
||||
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, terser) {
|
||||
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
|
||||
.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, terser) {
|
||||
const jsWrapperStart = 'a=function(){';
|
||||
const jsWrapperEnd = '};a();';
|
||||
|
||||
const promises = [];
|
||||
for (const attrName of Object.keys(node.attrs || {})) {
|
||||
if (!isEventHandler(attrName)) {
|
||||
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
|
||||
.minify(wrappedJs, terserOptions)
|
||||
.then(({ code }) => {
|
||||
let minifiedJs = code.substring(
|
||||
jsWrapperStart.length,
|
||||
code.length - jsWrapperEnd.length
|
||||
);
|
||||
node.attrs[attrName] = minifiedJs;
|
||||
});
|
||||
promises.push(promise);
|
||||
}
|
||||
|
||||
return promises;
|
||||
}
|
||||
24
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyJson.cjs
generated
vendored
Normal file
24
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyJson.cjs
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.onContent = onContent;
|
||||
const rNodeAttrsTypeJson = /(\/|\+)json/;
|
||||
function onContent() {
|
||||
return (content, node) => {
|
||||
// Skip SRI, reasons are documented in "minifyJs" module
|
||||
if (node.attrs && 'integrity' in node.attrs) {
|
||||
return content;
|
||||
}
|
||||
if (node.attrs && node.attrs.type && rNodeAttrsTypeJson.test(node.attrs.type)) {
|
||||
try {
|
||||
// cast minified JSON to an array
|
||||
return [JSON.stringify(JSON.parse((content || []).join('')))];
|
||||
} catch (error) {
|
||||
// Invalid JSON
|
||||
}
|
||||
}
|
||||
return content;
|
||||
};
|
||||
}
|
||||
21
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyJson.mjs
generated
vendored
Normal file
21
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyJson.mjs
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
const rNodeAttrsTypeJson = /(\/|\+)json/;
|
||||
|
||||
export function onContent() {
|
||||
return (content, node) => {
|
||||
// Skip SRI, reasons are documented in "minifyJs" module
|
||||
if (node.attrs && 'integrity' in node.attrs) {
|
||||
return content;
|
||||
}
|
||||
|
||||
if (node.attrs && node.attrs.type && rNodeAttrsTypeJson.test(node.attrs.type)) {
|
||||
try {
|
||||
// cast minified JSON to an array
|
||||
return [JSON.stringify(JSON.parse((content || []).join('')))];
|
||||
} catch (error) {
|
||||
// Invalid JSON
|
||||
}
|
||||
}
|
||||
|
||||
return content;
|
||||
};
|
||||
}
|
||||
37
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifySvg.cjs
generated
vendored
Normal file
37
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifySvg.cjs
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = minifySvg;
|
||||
var _helpers = require("../helpers.cjs");
|
||||
/** Minify SVG with SVGO */
|
||||
async function minifySvg(tree, options, svgoOptions = {}) {
|
||||
const svgo = await (0, _helpers.optionalImport)('svgo');
|
||||
if (!svgo) return tree;
|
||||
tree.match({
|
||||
tag: 'svg'
|
||||
}, node => {
|
||||
let svgStr = tree.render(node, {
|
||||
closingSingleTag: 'slash',
|
||||
quoteAllAttributes: true
|
||||
});
|
||||
try {
|
||||
const result = svgo.optimize(svgStr, svgoOptions);
|
||||
node.tag = false;
|
||||
node.attrs = {};
|
||||
// result.data is a string, we need to cast it to an array
|
||||
node.content = [result.data];
|
||||
return node;
|
||||
} catch (error) {
|
||||
console.error('htmlnano fails to minify the svg:');
|
||||
console.error(error);
|
||||
if (error.name === 'SvgoParserError') {
|
||||
console.error(error.toString());
|
||||
}
|
||||
// We return the node as-is
|
||||
return node;
|
||||
}
|
||||
});
|
||||
return tree;
|
||||
}
|
||||
30
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifySvg.mjs
generated
vendored
Normal file
30
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifySvg.mjs
generated
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
import { optionalImport } from '../helpers.mjs';
|
||||
|
||||
/** Minify SVG with SVGO */
|
||||
export default async function minifySvg(tree, options, svgoOptions = {}) {
|
||||
const svgo = await optionalImport('svgo');
|
||||
|
||||
if (!svgo) return tree;
|
||||
|
||||
tree.match({tag: 'svg'}, node => {
|
||||
let svgStr = tree.render(node, { closingSingleTag: 'slash', quoteAllAttributes: true });
|
||||
try {
|
||||
const result = svgo.optimize(svgStr, svgoOptions);
|
||||
node.tag = false;
|
||||
node.attrs = {};
|
||||
// result.data is a string, we need to cast it to an array
|
||||
node.content = [result.data];
|
||||
return node;
|
||||
} catch (error) {
|
||||
console.error('htmlnano fails to minify the svg:');
|
||||
console.error(error);
|
||||
if (error.name === 'SvgoParserError') {
|
||||
console.error(error.toString());
|
||||
}
|
||||
// We return the node as-is
|
||||
return node;
|
||||
}
|
||||
});
|
||||
|
||||
return tree;
|
||||
}
|
||||
141
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyUrls.cjs
generated
vendored
Normal file
141
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyUrls.cjs
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = minifyUrls;
|
||||
var _helpers = require("../helpers.cjs");
|
||||
// Adopts from https://github.com/kangax/html-minifier/blob/51ce10f4daedb1de483ffbcccecc41be1c873da2/src/htmlminifier.js#L209-L221
|
||||
const tagsHaveUriValuesForAttributes = new Set(['a', 'area', 'link', 'base', 'object', 'blockquote', 'q', 'del', 'ins', 'form', 'input', 'head', 'audio', 'embed', 'iframe', 'img', 'script', 'track', 'video']);
|
||||
const tagsHasHrefAttributes = new Set(['a', 'area', 'link', 'base']);
|
||||
const attributesOfImgTagHasUriValues = new Set(['src', 'longdesc', 'usemap']);
|
||||
const attributesOfObjectTagHasUriValues = new Set(['classid', 'codebase', 'data', 'usemap']);
|
||||
const tagsHasCiteAttributes = new Set(['blockquote', 'q', 'ins', 'del']);
|
||||
const tagsHasSrcAttributes = new Set(['audio', 'embed', 'iframe', 'img', 'input', 'script', 'track', 'video',
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
'source']);
|
||||
const isUriTypeAttribute = (tag, attr) => {
|
||||
return tagsHasHrefAttributes.has(tag) && attr === 'href' || tag === 'img' && attributesOfImgTagHasUriValues.has(attr) || tag === 'object' && attributesOfObjectTagHasUriValues.has(attr) || tagsHasCiteAttributes.has(tag) && attr === 'cite' || tag === 'form' && attr === 'action' || tag === 'input' && attr === 'usemap' || tag === 'head' && attr === 'profile' || tag === 'script' && attr === 'for' || tagsHasSrcAttributes.has(tag) && 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;
|
||||
};
|
||||
const JAVASCRIPT_URL_PROTOCOL = 'javascript:';
|
||||
let relateUrlInstance;
|
||||
let STORED_URL_BASE;
|
||||
|
||||
/** Convert absolute url into relative url */
|
||||
async function minifyUrls(tree, options, moduleOptions) {
|
||||
const RelateUrl = await (0, _helpers.optionalImport)('relateurl');
|
||||
const srcset = await (0, _helpers.optionalImport)('srcset');
|
||||
const terser = await (0, _helpers.optionalImport)('terser');
|
||||
let promises = [];
|
||||
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) {
|
||||
if (RelateUrl) {
|
||||
relateUrlInstance = new RelateUrl(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)) {
|
||||
if (isJavaScriptUrl(attrValue)) {
|
||||
promises.push(minifyJavaScriptUrl(node, attrName, terser));
|
||||
} else {
|
||||
if (relateUrlInstance) {
|
||||
// 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)) {
|
||||
if (srcset) {
|
||||
try {
|
||||
const parsedSrcset = srcset.parse(attrValue, {
|
||||
strict: true
|
||||
});
|
||||
node.attrs[attrName] = srcset.stringify(parsedSrcset.map(srcset => {
|
||||
if (relateUrlInstance) {
|
||||
srcset.url = relateUrlInstance.relate(srcset.url);
|
||||
}
|
||||
return srcset;
|
||||
}));
|
||||
} catch (e) {
|
||||
// srcset will throw an Error for invalid srcset.
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
});
|
||||
if (promises.length > 0) return Promise.all(promises).then(() => tree);
|
||||
return Promise.resolve(tree);
|
||||
}
|
||||
function isJavaScriptUrl(url) {
|
||||
return typeof url === 'string' && url.toLowerCase().startsWith(JAVASCRIPT_URL_PROTOCOL);
|
||||
}
|
||||
const jsWrapperStart = 'function a(){';
|
||||
const jsWrapperEnd = '}a();';
|
||||
function minifyJavaScriptUrl(node, attrName, terser) {
|
||||
if (!terser) return Promise.resolve();
|
||||
let result = node.attrs[attrName];
|
||||
if (result) {
|
||||
result = jsWrapperStart + result.slice(JAVASCRIPT_URL_PROTOCOL.length) + jsWrapperEnd;
|
||||
return terser.minify(result, {}) // Default Option is good enough
|
||||
.then(({
|
||||
code
|
||||
}) => {
|
||||
const minifiedJs = code.substring(jsWrapperStart.length, code.length - jsWrapperEnd.length);
|
||||
node.attrs[attrName] = JAVASCRIPT_URL_PROTOCOL + minifiedJs;
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
229
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyUrls.mjs
generated
vendored
Normal file
229
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/minifyUrls.mjs
generated
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
import { optionalImport } from '../helpers.mjs';
|
||||
|
||||
// Adopts from https://github.com/kangax/html-minifier/blob/51ce10f4daedb1de483ffbcccecc41be1c873da2/src/htmlminifier.js#L209-L221
|
||||
const tagsHaveUriValuesForAttributes = new Set([
|
||||
'a',
|
||||
'area',
|
||||
'link',
|
||||
'base',
|
||||
'object',
|
||||
'blockquote',
|
||||
'q',
|
||||
'del',
|
||||
'ins',
|
||||
'form',
|
||||
'input',
|
||||
'head',
|
||||
'audio',
|
||||
'embed',
|
||||
'iframe',
|
||||
'img',
|
||||
'script',
|
||||
'track',
|
||||
'video',
|
||||
]);
|
||||
|
||||
const tagsHasHrefAttributes = new Set([
|
||||
'a',
|
||||
'area',
|
||||
'link',
|
||||
'base'
|
||||
]);
|
||||
|
||||
const attributesOfImgTagHasUriValues = new Set([
|
||||
'src',
|
||||
'longdesc',
|
||||
'usemap'
|
||||
]);
|
||||
|
||||
const attributesOfObjectTagHasUriValues = new Set([
|
||||
'classid',
|
||||
'codebase',
|
||||
'data',
|
||||
'usemap'
|
||||
]);
|
||||
|
||||
const tagsHasCiteAttributes = new Set([
|
||||
'blockquote',
|
||||
'q',
|
||||
'ins',
|
||||
'del'
|
||||
]);
|
||||
|
||||
const tagsHasSrcAttributes = new Set([
|
||||
'audio',
|
||||
'embed',
|
||||
'iframe',
|
||||
'img',
|
||||
'input',
|
||||
'script',
|
||||
'track',
|
||||
'video',
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
'source'
|
||||
]);
|
||||
|
||||
const isUriTypeAttribute = (tag, attr) => {
|
||||
return (
|
||||
tagsHasHrefAttributes.has(tag) && attr === 'href' ||
|
||||
tag === 'img' && attributesOfImgTagHasUriValues.has(attr) ||
|
||||
tag === 'object' && attributesOfObjectTagHasUriValues.has(attr) ||
|
||||
tagsHasCiteAttributes.has(tag) && attr === 'cite' ||
|
||||
tag === 'form' && attr === 'action' ||
|
||||
tag === 'input' && attr === 'usemap' ||
|
||||
tag === 'head' && attr === 'profile' ||
|
||||
tag === 'script' && attr === 'for' ||
|
||||
tagsHasSrcAttributes.has(tag) && 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;
|
||||
};
|
||||
|
||||
const JAVASCRIPT_URL_PROTOCOL = 'javascript:';
|
||||
|
||||
let relateUrlInstance;
|
||||
let STORED_URL_BASE;
|
||||
|
||||
/** Convert absolute url into relative url */
|
||||
export default async function minifyUrls(tree, options, moduleOptions) {
|
||||
const RelateUrl = await optionalImport('relateurl');
|
||||
const srcset = await optionalImport('srcset');
|
||||
const terser = await optionalImport('terser');
|
||||
|
||||
let promises = [];
|
||||
|
||||
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) {
|
||||
if (RelateUrl) {
|
||||
relateUrlInstance = new RelateUrl(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)) {
|
||||
if (isJavaScriptUrl(attrValue)) {
|
||||
promises.push(minifyJavaScriptUrl(node, attrName, terser));
|
||||
} else {
|
||||
if (relateUrlInstance) {
|
||||
// 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)) {
|
||||
if (srcset) {
|
||||
try {
|
||||
const parsedSrcset = srcset.parse(attrValue, { strict: true });
|
||||
|
||||
node.attrs[attrName] = srcset.stringify(parsedSrcset.map(srcset => {
|
||||
if (relateUrlInstance) {
|
||||
srcset.url = relateUrlInstance.relate(srcset.url);
|
||||
}
|
||||
|
||||
return srcset;
|
||||
}));
|
||||
} catch (e) {
|
||||
// srcset will throw an Error for invalid srcset.
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
});
|
||||
|
||||
if (promises.length > 0) return Promise.all(promises).then(() => tree);
|
||||
return Promise.resolve(tree);
|
||||
}
|
||||
|
||||
function isJavaScriptUrl(url) {
|
||||
return typeof url === 'string' && url.toLowerCase().startsWith(JAVASCRIPT_URL_PROTOCOL);
|
||||
}
|
||||
|
||||
const jsWrapperStart = 'function a(){';
|
||||
const jsWrapperEnd = '}a();';
|
||||
|
||||
function minifyJavaScriptUrl(node, attrName, terser) {
|
||||
if (!terser) return Promise.resolve();
|
||||
|
||||
let result = node.attrs[attrName];
|
||||
if (result) {
|
||||
result = jsWrapperStart + result.slice(JAVASCRIPT_URL_PROTOCOL.length) + jsWrapperEnd;
|
||||
|
||||
return terser
|
||||
.minify(result, {}) // Default Option is good enough
|
||||
.then(({ code }) => {
|
||||
const minifiedJs = code.substring(
|
||||
jsWrapperStart.length,
|
||||
code.length - jsWrapperEnd.length
|
||||
);
|
||||
node.attrs[attrName] = JAVASCRIPT_URL_PROTOCOL + minifiedJs;
|
||||
});
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
120
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/normalizeAttributeValues.cjs
generated
vendored
Normal file
120
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/normalizeAttributeValues.cjs
generated
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.onAttrs = onAttrs;
|
||||
const caseInsensitiveAttributes = {
|
||||
autocomplete: ['form'],
|
||||
charset: ['meta', 'script'],
|
||||
contenteditable: null,
|
||||
crossorigin: ['audio', 'img', 'link', 'script', 'video'],
|
||||
dir: null,
|
||||
draggable: null,
|
||||
dropzone: null,
|
||||
formmethod: ['button', 'input'],
|
||||
inputmode: ['input', 'textarea'],
|
||||
kind: ['track'],
|
||||
method: ['form'],
|
||||
preload: ['audio', 'video'],
|
||||
referrerpolicy: null,
|
||||
sandbox: ['iframe'],
|
||||
spellcheck: null,
|
||||
scope: ['th'],
|
||||
shape: ['area'],
|
||||
sizes: ['link'],
|
||||
step: ['input'],
|
||||
translate: null,
|
||||
type: ['a', 'link', 'button', 'embed', 'object', 'script', 'source', 'style', 'input', 'menu', 'menuitem'],
|
||||
wrap: ['textarea']
|
||||
};
|
||||
|
||||
// https://html.spec.whatwg.org/#invalid-value-default
|
||||
/** @typedef { [key: string]: { tag: null | string[], default: string, valid: string[] } } */
|
||||
const invalidValueDefault = {
|
||||
crossorigin: {
|
||||
tag: null,
|
||||
default: 'anonymous',
|
||||
valid: ['', 'anonymous', 'use-credentials']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#referrer-policy-attributes
|
||||
// The attribute's invalid value default and missing value default are both the empty string state.
|
||||
referrerpolicy: {
|
||||
tag: null,
|
||||
default: '',
|
||||
valid: ['', 'url', 'origin', 'no-referrer', 'no-referrer-when-downgrade', 'same-origin', 'origin-when-cross-origin', 'strict-origin-when-cross-origin', 'unsafe-url']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#lazy-loading-attributes
|
||||
loading: {
|
||||
tag: ['img', 'iframe'],
|
||||
default: 'eager',
|
||||
valid: ['lazy', 'eager']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#the-img-element
|
||||
// https://html.spec.whatwg.org/#image-decoding-hint
|
||||
decoding: {
|
||||
tag: ['img'],
|
||||
default: 'auto',
|
||||
valid: ['auto', 'sync', 'async']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#the-track-element
|
||||
kind: {
|
||||
tag: ['track'],
|
||||
default: 'metadata',
|
||||
valid: ['subtitles', 'captions', 'descriptions', 'chapters', 'metadata']
|
||||
},
|
||||
type: {
|
||||
tag: ['button'],
|
||||
default: 'submit',
|
||||
valid: ['submit', 'reset', 'button']
|
||||
},
|
||||
wrap: {
|
||||
tag: ['textarea'],
|
||||
default: 'soft',
|
||||
valid: ['soft', 'hard']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#the-hidden-attribute
|
||||
hidden: {
|
||||
tag: null,
|
||||
default: 'hidden',
|
||||
valid: ['hidden', 'until-found']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#autocapitalization
|
||||
autocapitalize: {
|
||||
tag: null,
|
||||
default: 'sentences',
|
||||
valid: ['none', 'off', 'on', 'sentences', 'words', 'characters']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#the-marquee-element
|
||||
behavior: {
|
||||
tag: ['marquee'],
|
||||
default: 'scroll',
|
||||
valid: ['scroll', 'slide', 'alternate']
|
||||
},
|
||||
direction: {
|
||||
tag: ['marquee'],
|
||||
default: 'left',
|
||||
valid: ['left', 'right', 'up', 'down']
|
||||
}
|
||||
};
|
||||
function onAttrs() {
|
||||
return (attrs, node) => {
|
||||
const newAttrs = attrs;
|
||||
Object.entries(attrs).forEach(([attrName, attrValue]) => {
|
||||
let newAttrValue = attrValue;
|
||||
if (Object.hasOwnProperty.call(caseInsensitiveAttributes, attrName) && (caseInsensitiveAttributes[attrName] === null || caseInsensitiveAttributes[attrName].includes(node.tag))) {
|
||||
newAttrValue = typeof attrValue.toLowerCase === 'function' ? attrValue.toLowerCase() : attrValue;
|
||||
}
|
||||
if (Object.hasOwnProperty.call(invalidValueDefault, attrName)) {
|
||||
const meta = invalidValueDefault[attrName];
|
||||
if (meta.tag === null || node && node.tag && meta.tag.includes(node.tag)) {
|
||||
if (!meta.valid.includes(newAttrValue)) {
|
||||
newAttrValue = meta.default;
|
||||
}
|
||||
}
|
||||
}
|
||||
newAttrs[attrName] = newAttrValue;
|
||||
});
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
140
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/normalizeAttributeValues.mjs
generated
vendored
Normal file
140
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/normalizeAttributeValues.mjs
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
const caseInsensitiveAttributes = {
|
||||
autocomplete: ['form'],
|
||||
charset: ['meta', 'script'],
|
||||
contenteditable: null,
|
||||
crossorigin: ['audio', 'img', 'link', 'script', 'video'],
|
||||
dir: null,
|
||||
draggable: null,
|
||||
dropzone: null,
|
||||
formmethod: ['button', 'input'],
|
||||
inputmode: ['input', 'textarea'],
|
||||
kind: ['track'],
|
||||
method: ['form'],
|
||||
preload: ['audio', 'video'],
|
||||
referrerpolicy: null,
|
||||
sandbox: ['iframe'],
|
||||
spellcheck: null,
|
||||
scope: ['th'],
|
||||
shape: ['area'],
|
||||
sizes: ['link'],
|
||||
step: ['input'],
|
||||
translate: null,
|
||||
type: [
|
||||
'a',
|
||||
'link',
|
||||
'button',
|
||||
'embed',
|
||||
'object',
|
||||
'script',
|
||||
'source',
|
||||
'style',
|
||||
'input',
|
||||
'menu',
|
||||
'menuitem'
|
||||
],
|
||||
wrap: ['textarea']
|
||||
};
|
||||
|
||||
// https://html.spec.whatwg.org/#invalid-value-default
|
||||
/** @typedef { [key: string]: { tag: null | string[], default: string, valid: string[] } } */
|
||||
const invalidValueDefault = {
|
||||
crossorigin: {
|
||||
tag: null,
|
||||
default: 'anonymous',
|
||||
valid: ['', 'anonymous', 'use-credentials']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#referrer-policy-attributes
|
||||
// The attribute's invalid value default and missing value default are both the empty string state.
|
||||
referrerpolicy: {
|
||||
tag: null,
|
||||
default: '',
|
||||
valid: ['', 'url', 'origin', 'no-referrer', 'no-referrer-when-downgrade', 'same-origin', 'origin-when-cross-origin', 'strict-origin-when-cross-origin', 'unsafe-url']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#lazy-loading-attributes
|
||||
loading: {
|
||||
tag: ['img', 'iframe'],
|
||||
default: 'eager',
|
||||
valid: ['lazy', 'eager']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#the-img-element
|
||||
// https://html.spec.whatwg.org/#image-decoding-hint
|
||||
decoding: {
|
||||
tag: ['img'],
|
||||
default: 'auto',
|
||||
valid: ['auto', 'sync', 'async']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#the-track-element
|
||||
kind: {
|
||||
tag: ['track'],
|
||||
default: 'metadata',
|
||||
valid: ['subtitles', 'captions', 'descriptions', 'chapters', 'metadata']
|
||||
},
|
||||
type: {
|
||||
tag: ['button'],
|
||||
default: 'submit',
|
||||
valid: ['submit', 'reset', 'button']
|
||||
},
|
||||
wrap: {
|
||||
tag: ['textarea'],
|
||||
default: 'soft',
|
||||
valid: ['soft', 'hard']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#the-hidden-attribute
|
||||
hidden: {
|
||||
tag: null,
|
||||
default: 'hidden',
|
||||
valid: ['hidden', 'until-found']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#autocapitalization
|
||||
autocapitalize: {
|
||||
tag: null,
|
||||
default: 'sentences',
|
||||
valid: ['none', 'off', 'on', 'sentences', 'words', 'characters']
|
||||
},
|
||||
// https://html.spec.whatwg.org/#the-marquee-element
|
||||
behavior: {
|
||||
tag: ['marquee'],
|
||||
default: 'scroll',
|
||||
valid: ['scroll', 'slide', 'alternate']
|
||||
},
|
||||
direction: {
|
||||
tag: ['marquee'],
|
||||
default: 'left',
|
||||
valid: ['left', 'right', 'up', 'down']
|
||||
}
|
||||
};
|
||||
|
||||
export function onAttrs() {
|
||||
return (attrs, node) => {
|
||||
const newAttrs = attrs;
|
||||
|
||||
Object.entries(attrs).forEach(([attrName, attrValue]) => {
|
||||
let newAttrValue = attrValue;
|
||||
|
||||
if (
|
||||
Object.hasOwnProperty.call(caseInsensitiveAttributes, attrName)
|
||||
&& (
|
||||
caseInsensitiveAttributes[attrName] === null
|
||||
|| caseInsensitiveAttributes[attrName].includes(node.tag)
|
||||
)
|
||||
) {
|
||||
newAttrValue = typeof attrValue.toLowerCase === 'function' ? attrValue.toLowerCase() : attrValue;
|
||||
}
|
||||
|
||||
if (
|
||||
Object.hasOwnProperty.call(invalidValueDefault, attrName)
|
||||
) {
|
||||
const meta = invalidValueDefault[attrName];
|
||||
if (meta.tag === null || (node && node.tag && meta.tag.includes(node.tag))) {
|
||||
if (!meta.valid.includes(newAttrValue)) {
|
||||
newAttrValue = meta.default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newAttrs[attrName] = newAttrValue;
|
||||
});
|
||||
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
17
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeAttributeQuotes.cjs
generated
vendored
Normal file
17
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeAttributeQuotes.cjs
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
"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;
|
||||
}
|
||||
12
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeAttributeQuotes.mjs
generated
vendored
Normal file
12
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeAttributeQuotes.mjs
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
// 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 */
|
||||
export default function removeAttributeQuotes(tree) {
|
||||
if (tree.options && typeof tree.options.quoteAllAttributes === 'undefined') {
|
||||
tree.options.quoteAllAttributes = false;
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
86
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeComments.cjs
generated
vendored
Normal file
86
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeComments.cjs
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.onContent = onContent;
|
||||
exports.onNode = onNode;
|
||||
var _helpers = require("../helpers.cjs");
|
||||
const MATCH_EXCERPT_REGEXP = /<!-- ?more ?-->/i;
|
||||
|
||||
/** Removes HTML comments */
|
||||
function onNode(options, removeType) {
|
||||
if (removeType !== 'all' && removeType !== 'safe' && !isMatcher(removeType)) {
|
||||
removeType = 'safe';
|
||||
}
|
||||
return node => {
|
||||
if (isCommentToRemove(node, removeType)) {
|
||||
return '';
|
||||
}
|
||||
return node;
|
||||
};
|
||||
}
|
||||
function onContent(options, removeType) {
|
||||
if (removeType !== 'all' && removeType !== 'safe' && !isMatcher(removeType)) {
|
||||
removeType = 'safe';
|
||||
}
|
||||
return contents => {
|
||||
return contents.filter(content => !isCommentToRemove(content, removeType));
|
||||
};
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (isMatcher(removeType)) {
|
||||
return isMatch(text, removeType);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
function isMatch(input, matcher) {
|
||||
if (matcher instanceof RegExp) {
|
||||
return matcher.test(input);
|
||||
}
|
||||
if (typeof matcher === 'function') {
|
||||
return Boolean(matcher(input));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function isMatcher(matcher) {
|
||||
if (matcher instanceof RegExp || typeof matcher === 'function') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
92
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeComments.mjs
generated
vendored
Normal file
92
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeComments.mjs
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
import { isComment, isConditionalComment } from '../helpers.mjs';
|
||||
|
||||
const MATCH_EXCERPT_REGEXP = /<!-- ?more ?-->/i;
|
||||
|
||||
/** Removes HTML comments */
|
||||
export function onNode(options, removeType) {
|
||||
if (removeType !== 'all' && removeType !== 'safe' && !isMatcher(removeType)) {
|
||||
removeType = 'safe';
|
||||
}
|
||||
return (node) => {
|
||||
if (isCommentToRemove(node, removeType)) {
|
||||
return '';
|
||||
}
|
||||
return node;
|
||||
};
|
||||
}
|
||||
|
||||
export function onContent(options, removeType) {
|
||||
if (removeType !== 'all' && removeType !== 'safe' && !isMatcher(removeType)) {
|
||||
removeType = 'safe';
|
||||
}
|
||||
return (contents) => {
|
||||
return contents.filter(content => ! isCommentToRemove(content, removeType));
|
||||
};
|
||||
}
|
||||
|
||||
function isCommentToRemove(text, removeType) {
|
||||
if (typeof text !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! 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 (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;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMatcher(removeType)) {
|
||||
return isMatch(text, removeType);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function isMatch(input, matcher) {
|
||||
if (matcher instanceof RegExp) {
|
||||
return matcher.test(input);
|
||||
}
|
||||
|
||||
if (typeof matcher === 'function') {
|
||||
return Boolean(matcher(input));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isMatcher(matcher) {
|
||||
if (matcher instanceof RegExp || typeof matcher === 'function') {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
72
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeEmptyAttributes.cjs
generated
vendored
Normal file
72
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeEmptyAttributes.cjs
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.onAttrs = onAttrs;
|
||||
var _helpers = require("../helpers.cjs");
|
||||
const safeToRemoveAttrs = {
|
||||
id: null,
|
||||
class: null,
|
||||
style: null,
|
||||
title: null,
|
||||
lang: null,
|
||||
dir: null,
|
||||
abbr: ['th'],
|
||||
accept: ['input'],
|
||||
'accept-charset': ['form'],
|
||||
charset: ['meta', 'script'],
|
||||
action: ['form'],
|
||||
cols: ['textarea'],
|
||||
colspan: ['td', 'th'],
|
||||
coords: ['area'],
|
||||
dirname: ['input', 'textarea'],
|
||||
dropzone: null,
|
||||
headers: ['td', 'th'],
|
||||
form: ['button', 'fieldset', 'input', 'keygen', 'object', 'output', 'select', 'textarea'],
|
||||
formaction: ['button', 'input'],
|
||||
height: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
|
||||
high: 'meter',
|
||||
href: 'link',
|
||||
list: 'input',
|
||||
low: 'meter',
|
||||
manifest: 'html',
|
||||
max: ['meter', 'progress'],
|
||||
maxLength: ['input', 'textarea'],
|
||||
menu: 'button',
|
||||
min: 'meter',
|
||||
minLength: ['input', 'textarea'],
|
||||
name: ['button', 'fieldset', 'input', 'keygen', 'output', 'select', 'textarea', 'form', 'map', 'meta', 'param', 'slot'],
|
||||
pattern: ['input'],
|
||||
ping: ['a', 'area'],
|
||||
placeholder: ['input', 'textarea'],
|
||||
poster: ['video'],
|
||||
rel: ['a', 'area', 'link'],
|
||||
rows: 'textarea',
|
||||
rowspan: ['td', 'th'],
|
||||
size: ['input', 'select'],
|
||||
span: ['col', 'colgroup'],
|
||||
src: ['audio', 'embed', 'iframe', 'img', 'input', 'script', 'source', 'track', 'video'],
|
||||
start: 'ol',
|
||||
tabindex: null,
|
||||
type: ['a', 'link', 'button', 'embed', 'object', 'script', 'source', 'style', 'input', 'menu', 'menuitem', 'ol'],
|
||||
value: ['button', 'input', 'li'],
|
||||
width: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video']
|
||||
};
|
||||
function onAttrs() {
|
||||
return (attrs, node) => {
|
||||
const newAttrs = {
|
||||
...attrs
|
||||
};
|
||||
Object.entries(attrs).forEach(([attrName, attrValue]) => {
|
||||
if ((0, _helpers.isEventHandler)(attrName) || Object.hasOwnProperty.call(safeToRemoveAttrs, attrName) && (safeToRemoveAttrs[attrName] === null || safeToRemoveAttrs[attrName].includes(node.tag))) {
|
||||
if (typeof attrValue === 'string') {
|
||||
if (attrValue === '' || attrValue.trim() === '') {
|
||||
delete newAttrs[attrName];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
121
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeEmptyAttributes.mjs
generated
vendored
Normal file
121
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeEmptyAttributes.mjs
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
import { isEventHandler } from '../helpers.mjs';
|
||||
|
||||
const safeToRemoveAttrs = {
|
||||
id: null,
|
||||
class: null,
|
||||
style: null,
|
||||
title: null,
|
||||
lang: null,
|
||||
dir: null,
|
||||
abbr: ['th'],
|
||||
accept: ['input'],
|
||||
'accept-charset': ['form'],
|
||||
charset: ['meta', 'script'],
|
||||
action: ['form'],
|
||||
cols: ['textarea'],
|
||||
colspan: ['td', 'th'],
|
||||
coords: ['area'],
|
||||
dirname: ['input', 'textarea'],
|
||||
dropzone: null,
|
||||
headers: ['td', 'th'],
|
||||
form: [
|
||||
'button',
|
||||
'fieldset',
|
||||
'input',
|
||||
'keygen',
|
||||
'object',
|
||||
'output',
|
||||
'select',
|
||||
'textarea'
|
||||
],
|
||||
formaction: ['button', 'input'],
|
||||
height: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video'],
|
||||
high: 'meter',
|
||||
href: 'link',
|
||||
list: 'input',
|
||||
low: 'meter',
|
||||
manifest: 'html',
|
||||
max: ['meter', 'progress'],
|
||||
maxLength: ['input', 'textarea'],
|
||||
menu: 'button',
|
||||
min: 'meter',
|
||||
minLength: ['input', 'textarea'],
|
||||
name: [
|
||||
'button',
|
||||
'fieldset',
|
||||
'input',
|
||||
'keygen',
|
||||
'output',
|
||||
'select',
|
||||
'textarea',
|
||||
'form',
|
||||
'map',
|
||||
'meta',
|
||||
'param',
|
||||
'slot'
|
||||
],
|
||||
pattern: ['input'],
|
||||
ping: ['a', 'area'],
|
||||
placeholder: ['input', 'textarea'],
|
||||
poster: ['video'],
|
||||
rel: ['a', 'area', 'link'],
|
||||
rows: 'textarea',
|
||||
rowspan: ['td', 'th'],
|
||||
size: ['input', 'select'],
|
||||
span: ['col', 'colgroup'],
|
||||
src: [
|
||||
'audio',
|
||||
'embed',
|
||||
'iframe',
|
||||
'img',
|
||||
'input',
|
||||
'script',
|
||||
'source',
|
||||
'track',
|
||||
'video'
|
||||
],
|
||||
start: 'ol',
|
||||
tabindex: null,
|
||||
type: [
|
||||
'a',
|
||||
'link',
|
||||
'button',
|
||||
'embed',
|
||||
'object',
|
||||
'script',
|
||||
'source',
|
||||
'style',
|
||||
'input',
|
||||
'menu',
|
||||
'menuitem',
|
||||
'ol'
|
||||
],
|
||||
value: ['button', 'input', 'li'],
|
||||
width: ['canvas', 'embed', 'iframe', 'img', 'input', 'object', 'video']
|
||||
};
|
||||
|
||||
export function onAttrs() {
|
||||
return (attrs, node) => {
|
||||
const newAttrs = { ...attrs };
|
||||
Object.entries(attrs).forEach(([attrName, attrValue]) => {
|
||||
if (
|
||||
isEventHandler(attrName)
|
||||
|| (
|
||||
Object.hasOwnProperty.call(safeToRemoveAttrs, attrName)
|
||||
&& (
|
||||
safeToRemoveAttrs[attrName] === null
|
||||
|| safeToRemoveAttrs[attrName].includes(node.tag)
|
||||
)
|
||||
)
|
||||
) {
|
||||
if (typeof attrValue === 'string') {
|
||||
if (attrValue === '' || attrValue.trim() === '') {
|
||||
delete newAttrs[attrName];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
183
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeOptionalTags.cjs
generated
vendored
Normal file
183
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeOptionalTags.cjs
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = removeOptionalTags;
|
||||
var _helpers = require("../helpers.cjs");
|
||||
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;
|
||||
}
|
||||
225
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeOptionalTags.mjs
generated
vendored
Normal file
225
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeOptionalTags.mjs
generated
vendored
Normal file
@@ -0,0 +1,225 @@
|
||||
import { isComment } from '../helpers.mjs';
|
||||
|
||||
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 */
|
||||
export default 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' && isComment(firstNonEmptyChildNode)) {
|
||||
isHtmlStartTagCanBeOmitted = false;
|
||||
}
|
||||
|
||||
if (typeof nextNonEmptyNode === 'string' && 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' && 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' && isComment(firstNonEmptyChildNode))
|
||||
) {
|
||||
isBodyStartTagCanBeOmitted = false;
|
||||
}
|
||||
|
||||
if (firstNonEmptyChildNode && firstNonEmptyChildNode.tag && bodyStartTagCantBeOmittedWithFirstChildTags.has(firstNonEmptyChildNode.tag)) {
|
||||
isBodyStartTagCanBeOmitted = false;
|
||||
}
|
||||
|
||||
if (nextNode && typeof nextNode === 'string' && 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' && 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;
|
||||
}
|
||||
112
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeRedundantAttributes.cjs
generated
vendored
Normal file
112
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeRedundantAttributes.cjs
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.onAttrs = onAttrs;
|
||||
exports.redundantScriptTypes = void 0;
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#JavaScript_types
|
||||
const redundantScriptTypes = exports.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']);
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#missing-value-default
|
||||
const missingValueDefaultAttributes = {
|
||||
'form': {
|
||||
'method': 'get'
|
||||
},
|
||||
input: {
|
||||
type: 'text'
|
||||
},
|
||||
button: {
|
||||
// https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type
|
||||
type: 'submit'
|
||||
},
|
||||
'script': {
|
||||
'language': 'javascript',
|
||||
'type': attrs => {
|
||||
for (const [attrName, attrValue] of Object.entries(attrs)) {
|
||||
if (attrName.toLowerCase() !== 'type') {
|
||||
continue;
|
||||
}
|
||||
return redundantScriptTypes.has(attrValue);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
// Remove attribute if the function returns false
|
||||
'charset': attrs => {
|
||||
// The charset attribute only really makes sense on “external” SCRIPT elements:
|
||||
// http://perfectionkills.com/optimizing-html/#8_script_charset
|
||||
return !attrs.src;
|
||||
}
|
||||
},
|
||||
'style': {
|
||||
'media': 'all',
|
||||
'type': 'text/css'
|
||||
},
|
||||
'link': {
|
||||
media: 'all',
|
||||
'type': attrs => {
|
||||
// https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet
|
||||
let isRelStyleSheet = false;
|
||||
let isTypeTextCSS = false;
|
||||
if (attrs) {
|
||||
for (const [attrName, attrValue] of Object.entries(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',
|
||||
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decoding
|
||||
decoding: 'auto'
|
||||
},
|
||||
iframe: {
|
||||
'loading': 'eager'
|
||||
},
|
||||
// https://html.spec.whatwg.org/multipage/media.html#htmltrackelement
|
||||
track: {
|
||||
kind: 'subtitles'
|
||||
},
|
||||
textarea: {
|
||||
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-textarea-wrap
|
||||
wrap: 'soft'
|
||||
},
|
||||
area: {
|
||||
// https://html.spec.whatwg.org/multipage/image-maps.html#attr-area-shape
|
||||
shape: 'rect'
|
||||
}
|
||||
};
|
||||
const tagsHaveMissingValueDefaultAttributes = new Set(Object.keys(missingValueDefaultAttributes));
|
||||
|
||||
/** Removes redundant attributes */
|
||||
function onAttrs() {
|
||||
return (attrs, node) => {
|
||||
if (!node.tag) return attrs;
|
||||
const newAttrs = attrs;
|
||||
if (tagsHaveMissingValueDefaultAttributes.has(node.tag)) {
|
||||
const tagRedundantAttributes = missingValueDefaultAttributes[node.tag];
|
||||
for (const redundantAttributeName of Object.keys(tagRedundantAttributes)) {
|
||||
let tagRedundantAttributeValue = tagRedundantAttributes[redundantAttributeName];
|
||||
let isRemove = false;
|
||||
if (typeof tagRedundantAttributeValue === 'function') {
|
||||
isRemove = tagRedundantAttributeValue(attrs);
|
||||
} else if (attrs[redundantAttributeName] === tagRedundantAttributeValue) {
|
||||
isRemove = true;
|
||||
}
|
||||
if (isRemove) {
|
||||
delete newAttrs[redundantAttributeName];
|
||||
}
|
||||
}
|
||||
}
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
141
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeRedundantAttributes.mjs
generated
vendored
Normal file
141
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeRedundantAttributes.mjs
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#JavaScript_types
|
||||
export 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'
|
||||
]);
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#missing-value-default
|
||||
const missingValueDefaultAttributes = {
|
||||
'form': {
|
||||
'method': 'get'
|
||||
},
|
||||
|
||||
input: {
|
||||
type: 'text'
|
||||
},
|
||||
|
||||
button: {
|
||||
// https://html.spec.whatwg.org/multipage/form-elements.html#attr-button-type
|
||||
type: 'submit'
|
||||
},
|
||||
|
||||
'script': {
|
||||
'language': 'javascript',
|
||||
'type': attrs => {
|
||||
for (const [attrName, attrValue] of Object.entries(attrs)) {
|
||||
if (attrName.toLowerCase() !== 'type') {
|
||||
continue;
|
||||
}
|
||||
|
||||
return redundantScriptTypes.has(attrValue);
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
// Remove attribute if the function returns false
|
||||
'charset': attrs => {
|
||||
// The charset attribute only really makes sense on “external” SCRIPT elements:
|
||||
// http://perfectionkills.com/optimizing-html/#8_script_charset
|
||||
return !attrs.src;
|
||||
}
|
||||
},
|
||||
|
||||
'style': {
|
||||
'media': 'all',
|
||||
'type': 'text/css'
|
||||
},
|
||||
|
||||
'link': {
|
||||
media: 'all',
|
||||
'type': attrs => {
|
||||
// https://html.spec.whatwg.org/multipage/links.html#link-type-stylesheet
|
||||
let isRelStyleSheet = false;
|
||||
let isTypeTextCSS = false;
|
||||
|
||||
if (attrs) {
|
||||
for (const [attrName, attrValue] of Object.entries(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',
|
||||
// https://html.spec.whatwg.org/multipage/embedded-content.html#dom-img-decoding
|
||||
decoding: 'auto'
|
||||
},
|
||||
iframe: {
|
||||
'loading': 'eager'
|
||||
},
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/media.html#htmltrackelement
|
||||
track: {
|
||||
kind: 'subtitles'
|
||||
},
|
||||
|
||||
textarea: {
|
||||
// https://html.spec.whatwg.org/multipage/form-elements.html#dom-textarea-wrap
|
||||
wrap: 'soft'
|
||||
},
|
||||
|
||||
area: {
|
||||
// https://html.spec.whatwg.org/multipage/image-maps.html#attr-area-shape
|
||||
shape: 'rect'
|
||||
}
|
||||
};
|
||||
|
||||
const tagsHaveMissingValueDefaultAttributes = new Set(Object.keys(missingValueDefaultAttributes));
|
||||
|
||||
/** Removes redundant attributes */
|
||||
export function onAttrs() {
|
||||
return (attrs, node) => {
|
||||
if (!node.tag) return attrs;
|
||||
|
||||
const newAttrs = attrs;
|
||||
|
||||
if (tagsHaveMissingValueDefaultAttributes.has(node.tag)) {
|
||||
const tagRedundantAttributes = missingValueDefaultAttributes[node.tag];
|
||||
|
||||
for (const redundantAttributeName of Object.keys(tagRedundantAttributes)) {
|
||||
let tagRedundantAttributeValue = tagRedundantAttributes[redundantAttributeName];
|
||||
let isRemove = false;
|
||||
|
||||
if (typeof tagRedundantAttributeValue === 'function') {
|
||||
isRemove = tagRedundantAttributeValue(attrs);
|
||||
} else if (attrs[redundantAttributeName] === tagRedundantAttributeValue) {
|
||||
isRemove = true;
|
||||
}
|
||||
|
||||
if (isRemove) {
|
||||
delete newAttrs[redundantAttributeName];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return newAttrs;
|
||||
};
|
||||
}
|
||||
113
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeUnusedCss.cjs
generated
vendored
Normal file
113
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeUnusedCss.cjs
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = removeUnusedCss;
|
||||
var _helpers = require("../helpers.cjs");
|
||||
// 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, uncss) {
|
||||
const css = (0, _helpers.extractCssFromStyleNode)(styleNode);
|
||||
return runUncss(html, css, uncssOptions, uncss).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, uncss) {
|
||||
if (typeof userOptions !== 'object') {
|
||||
userOptions = {};
|
||||
}
|
||||
const options = {
|
||||
...userOptions,
|
||||
...uncssOptions
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
options.raw = css;
|
||||
uncss(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, purgecss) {
|
||||
const css = (0, _helpers.extractCssFromStyleNode)(styleNode);
|
||||
return runPurgecss(tree, css, purgecssOptions, purgecss).then(css => {
|
||||
if (css.trim().length === 0) {
|
||||
styleNode.tag = false;
|
||||
styleNode.content = [];
|
||||
return;
|
||||
}
|
||||
styleNode.content = [css];
|
||||
});
|
||||
}
|
||||
function runPurgecss(tree, css, userOptions, purgecss) {
|
||||
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.PurgeCSS().purge(options).then(result => {
|
||||
return result[0].css;
|
||||
});
|
||||
}
|
||||
|
||||
/** Remove unused CSS */
|
||||
async function removeUnusedCss(tree, options, userOptions) {
|
||||
const promises = [];
|
||||
const html = userOptions.tool !== 'purgeCSS' && tree.render(tree);
|
||||
const purgecss = await (0, _helpers.optionalImport)('purgecss');
|
||||
const uncss = await (0, _helpers.optionalImport)('uncss');
|
||||
tree.walk(node => {
|
||||
if ((0, _helpers.isStyleNode)(node)) {
|
||||
if (userOptions.tool === 'purgeCSS') {
|
||||
if (purgecss) {
|
||||
promises.push(processStyleNodePurgeCSS(tree, node, userOptions, purgecss));
|
||||
}
|
||||
} else {
|
||||
if (uncss) {
|
||||
promises.push(processStyleNodeUnCSS(html, node, userOptions, uncss));
|
||||
}
|
||||
}
|
||||
}
|
||||
return node;
|
||||
});
|
||||
return Promise.all(promises).then(() => tree);
|
||||
}
|
||||
122
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeUnusedCss.mjs
generated
vendored
Normal file
122
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/removeUnusedCss.mjs
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
import { isStyleNode, extractCssFromStyleNode, optionalImport } from '../helpers.mjs';
|
||||
|
||||
// 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, uncss) {
|
||||
const css = extractCssFromStyleNode(styleNode);
|
||||
|
||||
return runUncss(html, css, uncssOptions, uncss).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, uncss) {
|
||||
if (typeof userOptions !== 'object') {
|
||||
userOptions = {};
|
||||
}
|
||||
|
||||
const options = { ...userOptions, ...uncssOptions };
|
||||
return new Promise((resolve, reject) => {
|
||||
options.raw = css;
|
||||
uncss(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, purgecss) {
|
||||
const css = extractCssFromStyleNode(styleNode);
|
||||
return runPurgecss(tree, css, purgecssOptions, purgecss)
|
||||
.then(css => {
|
||||
if (css.trim().length === 0) {
|
||||
styleNode.tag = false;
|
||||
styleNode.content = [];
|
||||
return;
|
||||
}
|
||||
styleNode.content = [css];
|
||||
});
|
||||
}
|
||||
|
||||
function runPurgecss(tree, css, userOptions, purgecss) {
|
||||
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.PurgeCSS()
|
||||
.purge(options)
|
||||
.then((result) => {
|
||||
return result[0].css;
|
||||
});
|
||||
}
|
||||
|
||||
/** Remove unused CSS */
|
||||
export default async function removeUnusedCss(tree, options, userOptions) {
|
||||
const promises = [];
|
||||
const html = userOptions.tool !== 'purgeCSS' && tree.render(tree);
|
||||
|
||||
const purgecss = await optionalImport('purgecss');
|
||||
const uncss = await optionalImport('uncss');
|
||||
|
||||
tree.walk(node => {
|
||||
if (isStyleNode(node)) {
|
||||
if (userOptions.tool === 'purgeCSS') {
|
||||
if (purgecss) {
|
||||
promises.push(processStyleNodePurgeCSS(tree, node, userOptions, purgecss));
|
||||
}
|
||||
} else {
|
||||
if (uncss) {
|
||||
promises.push(processStyleNodeUnCSS(html, node, userOptions, uncss));
|
||||
}
|
||||
}
|
||||
}
|
||||
return node;
|
||||
});
|
||||
|
||||
return Promise.all(promises).then(() => tree);
|
||||
}
|
||||
100
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/sortAttributes.cjs
generated
vendored
Normal file
100
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/sortAttributes.cjs
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
"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] != null) {
|
||||
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;
|
||||
}
|
||||
121
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/sortAttributes.mjs
generated
vendored
Normal file
121
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/sortAttributes.mjs
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
import { sort as timSort } from '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()];
|
||||
timSort(_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] != null) {
|
||||
newAttrs[attrNameLower] = loweredNodeAttrs[attrNameLower];
|
||||
}
|
||||
});
|
||||
|
||||
return newAttrs;
|
||||
}
|
||||
}
|
||||
|
||||
/** Sort attibutes */
|
||||
export default 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;
|
||||
}
|
||||
116
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/sortAttributesWithLists.cjs
generated
vendored
Normal file
116
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/sortAttributesWithLists.cjs
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = collapseAttributeWhitespace;
|
||||
var _timsort = require("timsort");
|
||||
var _collapseAttributeWhitespace = require("./collapseAttributeWhitespace.cjs");
|
||||
// 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;
|
||||
});
|
||||
}
|
||||
135
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/sortAttributesWithLists.mjs
generated
vendored
Normal file
135
webGl/my-threejs-test/node_modules/htmlnano/lib/modules/sortAttributesWithLists.mjs
generated
vendored
Normal file
@@ -0,0 +1,135 @@
|
||||
// class, rel, ping
|
||||
import { sort as timSort } from 'timsort';
|
||||
import { attributesWithLists } from './collapseAttributeWhitespace.mjs';
|
||||
|
||||
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()];
|
||||
timSort(_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) */
|
||||
export default 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 (!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 (!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 (!attributesWithLists.has(attrNameLower)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tokenChainObj[attrNameLower]) {
|
||||
node.attrs[attrName] = tokenChainObj[attrNameLower].sortFromNodeAttrsArray(attrValues.split(/\s/)).join(' ');
|
||||
}
|
||||
});
|
||||
|
||||
return node;
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user