diff options
Diffstat (limited to 'node_modules/svgo/lib/style.js')
| -rw-r--r-- | node_modules/svgo/lib/style.js | 336 |
1 files changed, 0 insertions, 336 deletions
diff --git a/node_modules/svgo/lib/style.js b/node_modules/svgo/lib/style.js deleted file mode 100644 index 0e62afb9..00000000 --- a/node_modules/svgo/lib/style.js +++ /dev/null @@ -1,336 +0,0 @@ -'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 csstree = require('css-tree'); -const csswhat = require('css-what'); -const { - syntax: { specificity }, -} = require('csso'); -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) => { - /** - * @type {StylesheetDeclaration[]} - */ - const declarations = []; - // collect declarations - ruleNode.block.children.forEach((cssNode) => { - if (cssNode.type === 'Declaration') { - declarations.push({ - name: cssNode.property, - value: csstree.generate(cssNode.value), - important: cssNode.important === true, - }); - } - }); - - /** @type {StylesheetRule[]} */ - const rules = []; - csstree.walk(ruleNode.prelude, (node) => { - if (node.type === 'Selector') { - const newNode = csstree.clone(node); - let hasPseudoClasses = false; - csstree.walk(newNode, (pseudoClassNode, item, list) => { - if (pseudoClassNode.type === 'PseudoClassSelector') { - hasPseudoClasses = true; - list.remove(item); - } - }); - rules.push({ - specificity: specificity(node), - dynamic: hasPseudoClasses || dynamic, - // compute specificity from original node to consider pseudo classes - selector: csstree.generate(newNode), - declarations, - }); - } - }); - - return rules; -}; - -/** - * @type {(css: string, dynamic: boolean) => StylesheetRule[]} - */ -const parseStylesheet = (css, dynamic) => { - /** @type {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' || - cssNode.name === '-webkit-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) => StylesheetDeclaration[]} - */ -const parseStyleDeclarations = (css) => { - /** @type {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; -}; - -/** - * @param {Stylesheet} stylesheet - * @param {XastElement} node - * @returns {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.has(name)) { - computedStyle[name] = { type: 'static', inherited: false, value }; - importantStyles.set(name, false); - } - } - - // collect matching rules - for (const { selector, declarations, dynamic } of stylesheet.rules) { - if (matches(node, selector)) { - 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 selector specificities. - * Derived from https://github.com/keeganstreet/specificity/blob/8757133ddd2ed0163f120900047ff0f92760b536/specificity.js#L207 - * - * @param {Specificity} a - * @param {Specificity} b - * @returns {number} - */ -const compareSpecificity = (a, b) => { - for (let i = 0; i < 4; i += 1) { - if (a[i] < b[i]) { - return -1; - } else if (a[i] > b[i]) { - return 1; - } - } - - return 0; -}; -exports.compareSpecificity = compareSpecificity; - -/** - * @type {(root: XastRoot) => Stylesheet} - */ -const collectStylesheet = (root) => { - /** @type {StylesheetRule[]} */ - const rules = []; - /** @type {Map<XastElement, XastParent>} */ - const parents = new Map(); - - visit(root, { - element: { - enter: (node, parentNode) => { - parents.set(node, parentNode); - - if (node.name !== 'style') { - return; - } - - if ( - node.attributes.type == null || - node.attributes.type === '' || - node.attributes.type === 'text/css' - ) { - const dynamic = - node.attributes.media != null && node.attributes.media !== 'all'; - - for (const child of node.children) { - if (child.type === 'text' || child.type === 'cdata') { - rules.push(...parseStylesheet(child.value, dynamic)); - } - } - } - }, - }, - }); - // sort by selectors specificity - rules.sort((a, b) => compareSpecificity(a.specificity, b.specificity)); - return { rules, parents }; -}; -exports.collectStylesheet = collectStylesheet; - -/** - * @param {Stylesheet} stylesheet - * @param {XastElement} node - * @returns {ComputedStyles} - */ -const computeStyle = (stylesheet, node) => { - const { parents } = stylesheet; - 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 && - inheritableAttrs.has(name) && - !presentationNonInheritableGroupAttrs.has(name) - ) { - computedStyles[name] = { ...computed, inherited: true }; - } - } - parent = parents.get(parent); - } - 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; |
