| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 | 
							- 'use strict';
 
- /**
 
-  * @typedef {import('css-tree').Rule} CsstreeRule
 
-  * @typedef {import('./types').Specificity} Specificity
 
-  * @typedef {import('./types').Stylesheet} Stylesheet
 
-  * @typedef {import('./types').StylesheetRule} StylesheetRule
 
-  * @typedef {import('./types').StylesheetDeclaration} StylesheetDeclaration
 
-  * @typedef {import('./types').ComputedStyles} ComputedStyles
 
-  * @typedef {import('./types').XastRoot} XastRoot
 
-  * @typedef {import('./types').XastElement} XastElement
 
-  * @typedef {import('./types').XastParent} XastParent
 
-  * @typedef {import('./types').XastChild} XastChild
 
-  */
 
- const stable = require('stable');
 
- const csstree = require('css-tree');
 
- // @ts-ignore not defined in @types/csso
 
- const specificity = require('csso/lib/restructure/prepare/specificity');
 
- const { visit, matches } = require('./xast.js');
 
- const {
 
-   attrsGroups,
 
-   inheritableAttrs,
 
-   presentationNonInheritableGroupAttrs,
 
- } = require('../plugins/_collections.js');
 
- // @ts-ignore not defined in @types/csstree
 
- const csstreeWalkSkip = csstree.walk.skip;
 
- /**
 
-  * @type {(ruleNode: CsstreeRule, dynamic: boolean) => StylesheetRule}
 
-  */
 
- const parseRule = (ruleNode, dynamic) => {
 
-   let selectors;
 
-   let selectorsSpecificity;
 
-   /**
 
-    * @type {Array<StylesheetDeclaration>}
 
-    */
 
-   const declarations = [];
 
-   csstree.walk(ruleNode, (cssNode) => {
 
-     if (cssNode.type === 'SelectorList') {
 
-       // compute specificity from original node to consider pseudo classes
 
-       selectorsSpecificity = specificity(cssNode);
 
-       const newSelectorsNode = csstree.clone(cssNode);
 
-       csstree.walk(newSelectorsNode, (pseudoClassNode, item, list) => {
 
-         if (pseudoClassNode.type === 'PseudoClassSelector') {
 
-           dynamic = true;
 
-           list.remove(item);
 
-         }
 
-       });
 
-       selectors = csstree.generate(newSelectorsNode);
 
-       return csstreeWalkSkip;
 
-     }
 
-     if (cssNode.type === 'Declaration') {
 
-       declarations.push({
 
-         name: cssNode.property,
 
-         value: csstree.generate(cssNode.value),
 
-         important: cssNode.important === true,
 
-       });
 
-       return csstreeWalkSkip;
 
-     }
 
-   });
 
-   if (selectors == null || selectorsSpecificity == null) {
 
-     throw Error('assert');
 
-   }
 
-   return {
 
-     dynamic,
 
-     selectors,
 
-     specificity: selectorsSpecificity,
 
-     declarations,
 
-   };
 
- };
 
- /**
 
-  * @type {(css: string, dynamic: boolean) => Array<StylesheetRule>}
 
-  */
 
- const parseStylesheet = (css, dynamic) => {
 
-   /**
 
-    * @type {Array<StylesheetRule>}
 
-    */
 
-   const rules = [];
 
-   const ast = csstree.parse(css, {
 
-     parseValue: false,
 
-     parseAtrulePrelude: false,
 
-   });
 
-   csstree.walk(ast, (cssNode) => {
 
-     if (cssNode.type === 'Rule') {
 
-       rules.push(parseRule(cssNode, dynamic || false));
 
-       return csstreeWalkSkip;
 
-     }
 
-     if (cssNode.type === 'Atrule') {
 
-       if (cssNode.name === 'keyframes') {
 
-         return csstreeWalkSkip;
 
-       }
 
-       csstree.walk(cssNode, (ruleNode) => {
 
-         if (ruleNode.type === 'Rule') {
 
-           rules.push(parseRule(ruleNode, dynamic || true));
 
-           return csstreeWalkSkip;
 
-         }
 
-       });
 
-       return csstreeWalkSkip;
 
-     }
 
-   });
 
-   return rules;
 
- };
 
- /**
 
-  * @type {(css: string) => Array<StylesheetDeclaration>}
 
-  */
 
- const parseStyleDeclarations = (css) => {
 
-   /**
 
-    * @type {Array<StylesheetDeclaration>}
 
-    */
 
-   const declarations = [];
 
-   const ast = csstree.parse(css, {
 
-     context: 'declarationList',
 
-     parseValue: false,
 
-   });
 
-   csstree.walk(ast, (cssNode) => {
 
-     if (cssNode.type === 'Declaration') {
 
-       declarations.push({
 
-         name: cssNode.property,
 
-         value: csstree.generate(cssNode.value),
 
-         important: cssNode.important === true,
 
-       });
 
-     }
 
-   });
 
-   return declarations;
 
- };
 
- /**
 
-  * @type {(stylesheet: Stylesheet, node: XastElement) => ComputedStyles}
 
-  */
 
- const computeOwnStyle = (stylesheet, node) => {
 
-   /**
 
-    * @type {ComputedStyles}
 
-    */
 
-   const computedStyle = {};
 
-   const importantStyles = new Map();
 
-   // collect attributes
 
-   for (const [name, value] of Object.entries(node.attributes)) {
 
-     if (attrsGroups.presentation.includes(name)) {
 
-       computedStyle[name] = { type: 'static', inherited: false, value };
 
-       importantStyles.set(name, false);
 
-     }
 
-   }
 
-   // collect matching rules
 
-   for (const { selectors, declarations, dynamic } of stylesheet.rules) {
 
-     if (matches(node, selectors)) {
 
-       for (const { name, value, important } of declarations) {
 
-         const computed = computedStyle[name];
 
-         if (computed && computed.type === 'dynamic') {
 
-           continue;
 
-         }
 
-         if (dynamic) {
 
-           computedStyle[name] = { type: 'dynamic', inherited: false };
 
-           continue;
 
-         }
 
-         if (
 
-           computed == null ||
 
-           important === true ||
 
-           importantStyles.get(name) === false
 
-         ) {
 
-           computedStyle[name] = { type: 'static', inherited: false, value };
 
-           importantStyles.set(name, important);
 
-         }
 
-       }
 
-     }
 
-   }
 
-   // collect inline styles
 
-   const styleDeclarations =
 
-     node.attributes.style == null
 
-       ? []
 
-       : parseStyleDeclarations(node.attributes.style);
 
-   for (const { name, value, important } of styleDeclarations) {
 
-     const computed = computedStyle[name];
 
-     if (computed && computed.type === 'dynamic') {
 
-       continue;
 
-     }
 
-     if (
 
-       computed == null ||
 
-       important === true ||
 
-       importantStyles.get(name) === false
 
-     ) {
 
-       computedStyle[name] = { type: 'static', inherited: false, value };
 
-       importantStyles.set(name, important);
 
-     }
 
-   }
 
-   return computedStyle;
 
- };
 
- /**
 
-  * Compares two selector specificities.
 
-  * extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211
 
-  *
 
-  * @type {(a: Specificity, b: Specificity) => number}
 
-  */
 
- 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;
 
- };
 
- /**
 
-  * @type {(root: XastRoot) => Stylesheet}
 
-  */
 
- const collectStylesheet = (root) => {
 
-   /**
 
-    * @type {Array<StylesheetRule>}
 
-    */
 
-   const rules = [];
 
-   /**
 
-    * @type {Map<XastElement, XastParent>}
 
-    */
 
-   const parents = new Map();
 
-   visit(root, {
 
-     element: {
 
-       enter: (node, parentNode) => {
 
-         // store parents
 
-         parents.set(node, parentNode);
 
-         // find and parse all styles
 
-         if (node.name === 'style') {
 
-           const dynamic =
 
-             node.attributes.media != null && node.attributes.media !== 'all';
 
-           if (
 
-             node.attributes.type == null ||
 
-             node.attributes.type === '' ||
 
-             node.attributes.type === 'text/css'
 
-           ) {
 
-             const children = node.children;
 
-             for (const child of children) {
 
-               if (child.type === 'text' || child.type === 'cdata') {
 
-                 rules.push(...parseStylesheet(child.value, dynamic));
 
-               }
 
-             }
 
-           }
 
-         }
 
-       },
 
-     },
 
-   });
 
-   // sort by selectors specificity
 
-   stable.inplace(rules, (a, b) =>
 
-     compareSpecificity(a.specificity, b.specificity)
 
-   );
 
-   return { rules, parents };
 
- };
 
- exports.collectStylesheet = collectStylesheet;
 
- /**
 
-  * @type {(stylesheet: Stylesheet, node: XastElement) => ComputedStyles}
 
-  */
 
- const computeStyle = (stylesheet, node) => {
 
-   const { parents } = stylesheet;
 
-   // collect inherited styles
 
-   const computedStyles = computeOwnStyle(stylesheet, node);
 
-   let parent = parents.get(node);
 
-   while (parent != null && parent.type !== 'root') {
 
-     const inheritedStyles = computeOwnStyle(stylesheet, parent);
 
-     for (const [name, computed] of Object.entries(inheritedStyles)) {
 
-       if (
 
-         computedStyles[name] == null &&
 
-         // ignore not inheritable styles
 
-         inheritableAttrs.includes(name) === true &&
 
-         presentationNonInheritableGroupAttrs.includes(name) === false
 
-       ) {
 
-         computedStyles[name] = { ...computed, inherited: true };
 
-       }
 
-     }
 
-     parent = parents.get(parent);
 
-   }
 
-   return computedStyles;
 
- };
 
- exports.computeStyle = computeStyle;
 
 
  |