176 lines
4.5 KiB
JavaScript
176 lines
4.5 KiB
JavaScript
|
// 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;
|
||
|
};
|
||
|
}
|