diff options
Diffstat (limited to 'node_modules/svgo/lib/style.js')
| -rw-r--r-- | node_modules/svgo/lib/style.js | 95 |
1 files changed, 72 insertions, 23 deletions
diff --git a/node_modules/svgo/lib/style.js b/node_modules/svgo/lib/style.js index eb8d93d8..29e4a538 100644 --- a/node_modules/svgo/lib/style.js +++ b/node_modules/svgo/lib/style.js @@ -14,8 +14,9 @@ */ const csstree = require('css-tree'); +const csswhat = require('css-what'); const { - // @ts-ignore not defined in @types/csso + // @ts-ignore internal api syntax: { specificity }, } = require('csso'); const { visit, matches } = require('./xast.js'); @@ -47,9 +48,7 @@ const parseRule = (ruleNode, dynamic) => { } }); - /** - * @type {StylesheetRule[]} - */ + /** @type {StylesheetRule[]} */ const rules = []; csstree.walk(ruleNode.prelude, (node) => { if (node.type === 'Selector') { @@ -78,9 +77,7 @@ const parseRule = (ruleNode, dynamic) => { * @type {(css: string, dynamic: boolean) => Array<StylesheetRule>} */ const parseStylesheet = (css, dynamic) => { - /** - * @type {Array<StylesheetRule>} - */ + /** @type {Array<StylesheetRule>} */ const rules = []; const ast = csstree.parse(css, { parseValue: false, @@ -92,7 +89,10 @@ const parseStylesheet = (css, dynamic) => { return csstreeWalkSkip; } if (cssNode.type === 'Atrule') { - if (cssNode.name === 'keyframes') { + if ( + cssNode.name === 'keyframes' || + cssNode.name === '-webkit-keyframes' + ) { return csstreeWalkSkip; } csstree.walk(cssNode, (ruleNode) => { @@ -111,9 +111,7 @@ const parseStylesheet = (css, dynamic) => { * @type {(css: string) => Array<StylesheetDeclaration>} */ const parseStyleDeclarations = (css) => { - /** - * @type {Array<StylesheetDeclaration>} - */ + /** @type {Array<StylesheetDeclaration>} */ const declarations = []; const ast = csstree.parse(css, { context: 'declarationList', @@ -135,9 +133,7 @@ const parseStyleDeclarations = (css) => { * @type {(stylesheet: Stylesheet, node: XastElement) => ComputedStyles} */ const computeOwnStyle = (stylesheet, node) => { - /** - * @type {ComputedStyles} - */ + /** @type {ComputedStyles} */ const computedStyle = {}; const importantStyles = new Map(); @@ -197,10 +193,12 @@ const computeOwnStyle = (stylesheet, node) => { }; /** - * Compares two selector specificities. - * extracted from https://github.com/keeganstreet/specificity/blob/main/specificity.js#L211 + * Compares selector specificities. + * Derived from https://github.com/keeganstreet/specificity/blob/8757133ddd2ed0163f120900047ff0f92760b536/specificity.js#L207 * - * @type {(a: Specificity, b: Specificity) => number} + * @param {Specificity} a + * @param {Specificity} b + * @returns {number} */ const compareSpecificity = (a, b) => { for (let i = 0; i < 4; i += 1) { @@ -213,18 +211,15 @@ const compareSpecificity = (a, b) => { return 0; }; +exports.compareSpecificity = compareSpecificity; /** * @type {(root: XastRoot) => Stylesheet} */ const collectStylesheet = (root) => { - /** - * @type {Array<StylesheetRule>} - */ + /** @type {Array<StylesheetRule>} */ const rules = []; - /** - * @type {Map<XastElement, XastParent>} - */ + /** @type {Map<XastElement, XastParent>} */ const parents = new Map(); visit(root, { element: { @@ -282,3 +277,57 @@ const computeStyle = (stylesheet, node) => { return computedStyles; }; exports.computeStyle = computeStyle; + +/** + * Determines if the CSS selector includes or traverses the given attribute. + * + * Classes and IDs are generated as attribute selectors, so you can check for + * if a `.class` or `#id` is included by passing `name=class` or `name=id` + * respectively. + * + * @param {csstree.ListItem<csstree.CssNode>|string} selector + * @param {string} name + * @param {?string} value + * @param {boolean} traversed + * @returns {boolean} + */ +const includesAttrSelector = ( + selector, + name, + value = null, + traversed = false +) => { + const selectors = + typeof selector === 'string' + ? csswhat.parse(selector) + : csswhat.parse(csstree.generate(selector.data)); + + for (const subselector of selectors) { + const hasAttrSelector = subselector.some((segment, index) => { + if (traversed) { + if (index === subselector.length - 1) { + return false; + } + + const isNextTraversal = csswhat.isTraversal(subselector[index + 1]); + + if (!isNextTraversal) { + return false; + } + } + + if (segment.type !== 'attribute' || segment.name !== name) { + return false; + } + + return value == null ? true : segment.value === value; + }); + + if (hasAttrSelector) { + return true; + } + } + + return false; +}; +exports.includesAttrSelector = includesAttrSelector; |
