htmlnano
Version:
Modular HTML minifier, built on top of the PostHTML
257 lines (254 loc) • 5.93 kB
JavaScript
Object.defineProperty(exports, '__esModule', { value: true });
const caseInsensitiveAttributes = {
autocomplete: [
'form'
],
charset: [
'meta',
'script'
],
contenteditable: null,
crossorigin: [
'audio',
'img',
'link',
'script',
'video'
],
dir: null,
draggable: null,
dropzone: null,
fetchpriority: [
'img',
'link',
'script'
],
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
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/#lazy-loading-attributes
// https://html.spec.whatwg.org/#fetch-priority-attributes
fetchpriority: {
tag: [
'img',
'link',
'script'
],
default: 'auto',
valid: [
'high',
'low',
'auto'
]
},
// 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'
]
}
};
const mod = {
onAttrs () {
return (attrs, node)=>{
const newAttrs = attrs;
Object.entries(attrs).forEach(([attrName, attrValue])=>{
let newAttrValue = attrValue;
const hasCaseInsensitive = Object.hasOwnProperty.call(caseInsensitiveAttributes, attrName) && (caseInsensitiveAttributes[attrName] === null || node.tag && caseInsensitiveAttributes[attrName].includes(node.tag));
const hasInvalidValueDefault = Object.hasOwnProperty.call(invalidValueDefault, attrName);
const invalidMeta = hasInvalidValueDefault ? invalidValueDefault[attrName] : undefined;
const appliesInvalidValueDefault = Boolean(invalidMeta && (invalidMeta.tag === null || node && node.tag && invalidMeta.tag.includes(node.tag)));
const shouldNormalizeCase = hasCaseInsensitive || appliesInvalidValueDefault;
if (typeof attrValue === 'string' && shouldNormalizeCase) {
newAttrValue = attrValue.trim().toLowerCase();
}
if (appliesInvalidValueDefault && invalidMeta) {
if (typeof newAttrValue !== 'string' || !invalidMeta.valid.includes(newAttrValue)) {
newAttrValue = invalidMeta.default;
}
}
newAttrs[attrName] = newAttrValue;
});
return newAttrs;
};
}
};
exports.default = mod;