You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
98 lines
2.5 KiB
98 lines
2.5 KiB
'use strict'; |
|
|
|
/** |
|
* @typedef {import('../lib/types').XastElement} XastElement |
|
* @typedef {import('../lib/types').XastChild} XastChild |
|
*/ |
|
|
|
const { visitSkip, detachNodeFromParent } = require('../lib/xast.js'); |
|
|
|
exports.name = 'mergeStyles'; |
|
exports.description = 'merge multiple style elements into one'; |
|
|
|
/** |
|
* Merge multiple style elements into one. |
|
* |
|
* @author strarsis <strarsis@gmail.com> |
|
* |
|
* @type {import('./plugins-types').Plugin<'mergeStyles'>} |
|
*/ |
|
exports.fn = () => { |
|
/** |
|
* @type {null | XastElement} |
|
*/ |
|
let firstStyleElement = null; |
|
let collectedStyles = ''; |
|
/** |
|
* @type {'text' | 'cdata'} |
|
*/ |
|
let styleContentType = 'text'; |
|
|
|
return { |
|
element: { |
|
enter: (node, parentNode) => { |
|
// skip <foreignObject> content |
|
if (node.name === 'foreignObject') { |
|
return visitSkip; |
|
} |
|
|
|
// collect style elements |
|
if (node.name !== 'style') { |
|
return; |
|
} |
|
|
|
// skip <style> with invalid type attribute |
|
if ( |
|
node.attributes.type != null && |
|
node.attributes.type !== '' && |
|
node.attributes.type !== 'text/css' |
|
) { |
|
return; |
|
} |
|
|
|
// extract style element content |
|
let css = ''; |
|
for (const child of node.children) { |
|
if (child.type === 'text') { |
|
css += child.value; |
|
} |
|
if (child.type === 'cdata') { |
|
styleContentType = 'cdata'; |
|
css += child.value; |
|
} |
|
} |
|
|
|
// remove empty style elements |
|
if (css.trim().length === 0) { |
|
detachNodeFromParent(node, parentNode); |
|
return; |
|
} |
|
|
|
// collect css and wrap with media query if present in attribute |
|
if (node.attributes.media == null) { |
|
collectedStyles += css; |
|
} else { |
|
collectedStyles += `@media ${node.attributes.media}{${css}}`; |
|
delete node.attributes.media; |
|
} |
|
|
|
// combine collected styles in the first style element |
|
if (firstStyleElement == null) { |
|
firstStyleElement = node; |
|
} else { |
|
detachNodeFromParent(node, parentNode); |
|
/** |
|
* @type {XastChild} |
|
*/ |
|
const child = { type: styleContentType, value: collectedStyles }; |
|
// TODO remove legacy parentNode in v4 |
|
Object.defineProperty(child, 'parentNode', { |
|
writable: true, |
|
value: firstStyleElement, |
|
}); |
|
firstStyleElement.children = [child]; |
|
} |
|
}, |
|
}, |
|
}; |
|
};
|
|
|