123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- 'use strict';
- const csstree = require('css-tree');
- const specificity = require('csso/lib/restructure/prepare/specificity');
- const stable = require('stable');
- const {
- visitSkip,
- querySelectorAll,
- detachNodeFromParent,
- } = require('../lib/xast.js');
- exports.type = 'visitor';
- exports.name = 'inlineStyles';
- exports.active = true;
- exports.description = 'inline styles (additional options)';
- const compareSpecificity = (a, b) => {
- for (var i = 0; i < 4; i += 1) {
- if (a[i] < b[i]) {
- return -1;
- } else if (a[i] > b[i]) {
- return 1;
- }
- }
- return 0;
- };
- exports.fn = (root, params) => {
- const {
- onlyMatchedOnce = true,
- removeMatchedSelectors = true,
- useMqs = ['', 'screen'],
- usePseudos = [''],
- } = params;
-
- const styles = [];
-
- let selectors = [];
- return {
- element: {
- enter: (node, parentNode) => {
-
- if (node.name === 'foreignObject') {
- return visitSkip;
- }
-
- if (node.name !== 'style' || node.children.length === 0) {
- return;
- }
-
- if (
- node.attributes.type != null &&
- node.attributes.type !== '' &&
- node.attributes.type !== 'text/css'
- ) {
- return;
- }
-
- let cssText = '';
- for (const child of node.children) {
- if (child.type === 'text' || child.type === 'cdata') {
- cssText += child.value;
- }
- }
-
- let cssAst = null;
- try {
- cssAst = csstree.parse(cssText, {
- parseValue: false,
- parseCustomProperty: false,
- });
- } catch {
- return;
- }
- if (cssAst.type === 'StyleSheet') {
- styles.push({ node, parentNode, cssAst });
- }
-
- csstree.walk(cssAst, {
- visit: 'Selector',
- enter(node, item) {
- const atrule = this.atrule;
- const rule = this.rule;
- if (rule == null) {
- return;
- }
-
- let mq = '';
- if (atrule != null) {
- mq = atrule.name;
- if (atrule.prelude != null) {
- mq += ` ${csstree.generate(atrule.prelude)}`;
- }
- }
- if (useMqs.includes(mq) === false) {
- return;
- }
-
- const pseudos = [];
- if (node.type === 'Selector') {
- node.children.each((childNode, childItem, childList) => {
- if (
- childNode.type === 'PseudoClassSelector' ||
- childNode.type === 'PseudoElementSelector'
- ) {
- pseudos.push({ item: childItem, list: childList });
- }
- });
- }
-
- const pseudoSelectors = csstree.generate({
- type: 'Selector',
- children: new csstree.List().fromArray(
- pseudos.map((pseudo) => pseudo.item.data)
- ),
- });
- if (usePseudos.includes(pseudoSelectors) === false) {
- return;
- }
-
-
-
- for (const pseudo of pseudos) {
- pseudo.list.remove(pseudo.item);
- }
- selectors.push({ node, item, rule });
- },
- });
- },
- },
- root: {
- exit: () => {
- if (styles.length === 0) {
- return;
- }
-
- const sortedSelectors = stable(selectors, (a, b) => {
- const aSpecificity = specificity(a.item.data);
- const bSpecificity = specificity(b.item.data);
- return compareSpecificity(aSpecificity, bSpecificity);
- }).reverse();
- for (const selector of sortedSelectors) {
-
- const selectorText = csstree.generate(selector.item.data);
-
- const matchedElements = [];
- try {
- for (const node of querySelectorAll(root, selectorText)) {
- if (node.type === 'element') {
- matchedElements.push(node);
- }
- }
- } catch (selectError) {
- continue;
- }
-
- if (matchedElements.length === 0) {
- continue;
- }
-
-
- if (onlyMatchedOnce && matchedElements.length > 1) {
- continue;
- }
-
- for (const selectedEl of matchedElements) {
- const styleDeclarationList = csstree.parse(
- selectedEl.attributes.style == null
- ? ''
- : selectedEl.attributes.style,
- {
- context: 'declarationList',
- parseValue: false,
- }
- );
- if (styleDeclarationList.type !== 'DeclarationList') {
- continue;
- }
- const styleDeclarationItems = new Map();
- csstree.walk(styleDeclarationList, {
- visit: 'Declaration',
- enter(node, item) {
- styleDeclarationItems.set(node.property, item);
- },
- });
-
- csstree.walk(selector.rule, {
- visit: 'Declaration',
- enter(ruleDeclaration) {
-
-
-
-
- const matchedItem = styleDeclarationItems.get(
- ruleDeclaration.property
- );
- const ruleDeclarationItem =
- styleDeclarationList.children.createItem(ruleDeclaration);
- if (matchedItem == null) {
- styleDeclarationList.children.append(ruleDeclarationItem);
- } else if (
- matchedItem.data.important !== true &&
- ruleDeclaration.important === true
- ) {
- styleDeclarationList.children.replace(
- matchedItem,
- ruleDeclarationItem
- );
- styleDeclarationItems.set(
- ruleDeclaration.property,
- ruleDeclarationItem
- );
- }
- },
- });
- selectedEl.attributes.style =
- csstree.generate(styleDeclarationList);
- }
- if (
- removeMatchedSelectors &&
- matchedElements.length !== 0 &&
- selector.rule.prelude.type === 'SelectorList'
- ) {
-
- selector.rule.prelude.children.remove(selector.item);
- }
- selector.matchedElements = matchedElements;
- }
-
- if (removeMatchedSelectors === false) {
- return;
- }
-
- for (const selector of sortedSelectors) {
- if (selector.matchedElements == null) {
- continue;
- }
- if (onlyMatchedOnce && selector.matchedElements.length > 1) {
-
- continue;
- }
- for (const selectedEl of selector.matchedElements) {
-
- const classList = new Set(
- selectedEl.attributes.class == null
- ? null
- : selectedEl.attributes.class.split(' ')
- );
- const firstSubSelector = selector.node.children.first();
- if (
- firstSubSelector != null &&
- firstSubSelector.type === 'ClassSelector'
- ) {
- classList.delete(firstSubSelector.name);
- }
- if (classList.size === 0) {
- delete selectedEl.attributes.class;
- } else {
- selectedEl.attributes.class = Array.from(classList).join(' ');
- }
-
- if (
- firstSubSelector != null &&
- firstSubSelector.type === 'IdSelector'
- ) {
- if (selectedEl.attributes.id === firstSubSelector.name) {
- delete selectedEl.attributes.id;
- }
- }
- }
- }
- for (const style of styles) {
- csstree.walk(style.cssAst, {
- visit: 'Rule',
- enter: function (node, item, list) {
-
- if (
- node.type === 'Rule' &&
- node.prelude.type === 'SelectorList' &&
- node.prelude.children.isEmpty()
- ) {
- list.remove(item);
- }
- },
- });
- if (style.cssAst.children.isEmpty()) {
-
- detachNodeFromParent(style.node, style.parentNode);
- } else {
-
- const firstChild = style.node.children[0];
- if (firstChild.type === 'text' || firstChild.type === 'cdata') {
- firstChild.value = csstree.generate(style.cssAst);
- }
- }
- }
- },
- },
- };
- };
|