Changeset e29cc2e for trip-planner-front/node_modules/svgo/plugins
- Timestamp:
- 11/25/21 22:08:24 (3 years ago)
- Branches:
- master
- Children:
- 8d391a1
- Parents:
- 59329aa
- Location:
- trip-planner-front/node_modules/svgo/plugins
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trip-planner-front/node_modules/svgo/plugins/inlineStyles.js
r59329aa re29cc2e 1 1 'use strict'; 2 2 3 /** 4 * @typedef {import('../lib/types').Specificity} Specificity 5 * @typedef {import('../lib/types').XastElement} XastElement 6 * @typedef {import('../lib/types').XastParent} XastParent 7 */ 8 3 9 const csstree = require('css-tree'); 4 const { querySelectorAll, closestByName } = require('../lib/xast.js'); 5 const cssTools = require('../lib/css-tools'); 6 10 // @ts-ignore not defined in @types/csso 11 const specificity = require('csso/lib/restructure/prepare/specificity'); 12 const stable = require('stable'); 13 const { 14 visitSkip, 15 querySelectorAll, 16 detachNodeFromParent, 17 } = require('../lib/xast.js'); 18 19 exports.type = 'visitor'; 7 20 exports.name = 'inlineStyles'; 8 9 exports.type = 'full';10 11 21 exports.active = true; 12 13 exports.params = { 14 onlyMatchedOnce: true, 15 removeMatchedSelectors: true, 16 useMqs: ['', 'screen'], 17 usePseudos: [''], 22 exports.description = 'inline styles (additional options)'; 23 24 /** 25 * Compares two selector specificities. 26 * extracted from https://github.com/keeganstreet/specificity/blob/master/specificity.js#L211 27 * 28 * @type {(a: Specificity, b: Specificity) => number} 29 */ 30 const compareSpecificity = (a, b) => { 31 for (var i = 0; i < 4; i += 1) { 32 if (a[i] < b[i]) { 33 return -1; 34 } else if (a[i] > b[i]) { 35 return 1; 36 } 37 } 38 return 0; 18 39 }; 19 20 exports.description = 'inline styles (additional options)';21 40 22 41 /** … … 39 58 * empty string element for all non-pseudo-classes and/or -elements 40 59 * 41 * @param {Object} root document element42 * @param {Object} opts plugin params43 *44 60 * @author strarsis <strarsis@gmail.com> 61 * 62 * @type {import('../lib/types').Plugin<{ 63 * onlyMatchedOnce?: boolean, 64 * removeMatchedSelectors?: boolean, 65 * useMqs?: Array<string>, 66 * usePseudos?: Array<string> 67 * }>} 45 68 */ 46 exports.fn = function (root, opts) { 47 // collect <style/>s 48 var styleEls = querySelectorAll(root, 'style'); 49 50 //no <styles/>s, nothing to do 51 if (styleEls.length === 0) { 52 return root; 53 } 54 55 var styles = [], 56 selectors = []; 57 58 for (var styleEl of styleEls) { 59 // values other than the empty string or text/css are not used 60 if ( 61 styleEl.attributes.type != null && 62 styleEl.attributes.type !== '' && 63 styleEl.attributes.type !== 'text/css' 64 ) { 65 continue; 66 } 67 // skip empty <style/>s or <foreignObject> content. 68 if ( 69 styleEl.children.length === 0 || 70 closestByName(styleEl, 'foreignObject') 71 ) { 72 continue; 73 } 74 75 var cssStr = cssTools.getCssStr(styleEl); 76 77 // collect <style/>s and their css ast 78 var cssAst = {}; 79 try { 80 cssAst = csstree.parse(cssStr, { 81 parseValue: false, 82 parseCustomProperty: false, 83 }); 84 } catch (parseError) { 85 // console.warn('Warning: Parse error of styles of <style/> element, skipped. Error details: ' + parseError); 86 continue; 87 } 88 89 styles.push({ 90 styleEl: styleEl, 91 cssAst: cssAst, 92 }); 93 94 selectors = selectors.concat(cssTools.flattenToSelectors(cssAst)); 95 } 96 97 // filter for mediaqueries to be used or without any mediaquery 98 var selectorsMq = cssTools.filterByMqs(selectors, opts.useMqs); 99 100 // filter for pseudo elements to be used 101 var selectorsPseudo = cssTools.filterByPseudos(selectorsMq, opts.usePseudos); 102 103 // remove PseudoClass from its SimpleSelector for proper matching 104 cssTools.cleanPseudos(selectorsPseudo); 105 106 // stable sort selectors 107 var sortedSelectors = cssTools.sortSelectors(selectorsPseudo).reverse(); 108 109 var selector, selectedEl; 110 111 // match selectors 112 for (selector of sortedSelectors) { 113 var selectorStr = csstree.generate(selector.item.data), 114 selectedEls = null; 115 116 try { 117 selectedEls = querySelectorAll(root, selectorStr); 118 } catch (selectError) { 119 // console.warn('Warning: Syntax error when trying to select \n\n' + selectorStr + '\n\n, skipped. Error details: ' + selectError); 120 continue; 121 } 122 123 if (selectedEls.length === 0) { 124 // nothing selected 125 continue; 126 } 127 128 selector.selectedEls = selectedEls; 129 } 130 131 // apply <style/> styles to matched elements 132 for (selector of sortedSelectors) { 133 if (!selector.selectedEls) { 134 continue; 135 } 136 137 if ( 138 opts.onlyMatchedOnce && 139 selector.selectedEls !== null && 140 selector.selectedEls.length > 1 141 ) { 142 // skip selectors that match more than once if option onlyMatchedOnce is enabled 143 continue; 144 } 145 146 // apply <style/> to matched elements 147 for (selectedEl of selector.selectedEls) { 148 if (selector.rule === null) { 149 continue; 150 } 151 const styleDeclarationList = csstree.parse( 152 selectedEl.attributes.style == null ? '' : selectedEl.attributes.style, 153 { 154 context: 'declarationList', 155 parseValue: false, 156 } 157 ); 158 const styleDeclarationItems = new Map(); 159 csstree.walk(styleDeclarationList, { 160 visit: 'Declaration', 161 enter(node, item) { 162 styleDeclarationItems.set(node.property, item); 163 }, 164 }); 165 // merge declarations 166 csstree.walk(selector.rule, { 167 visit: 'Declaration', 168 enter(ruleDeclaration) { 169 // existing inline styles have higher priority 170 // no inline styles, external styles, external styles used 171 // inline styles, external styles same priority as inline styles, inline styles used 172 // inline styles, external styles higher priority than inline styles, external styles used 173 const matchedItem = styleDeclarationItems.get( 174 ruleDeclaration.property 175 ); 176 const ruleDeclarationItem = 177 styleDeclarationList.children.createItem(ruleDeclaration); 178 if (matchedItem == null) { 179 styleDeclarationList.children.append(ruleDeclarationItem); 180 } else if ( 181 matchedItem.data.important !== true && 182 ruleDeclaration.important === true 69 exports.fn = (root, params) => { 70 const { 71 onlyMatchedOnce = true, 72 removeMatchedSelectors = true, 73 useMqs = ['', 'screen'], 74 usePseudos = [''], 75 } = params; 76 77 /** 78 * @type {Array<{ node: XastElement, parentNode: XastParent, cssAst: csstree.StyleSheet }>} 79 */ 80 const styles = []; 81 /** 82 * @type {Array<{ 83 * node: csstree.Selector, 84 * item: csstree.ListItem<csstree.CssNode>, 85 * rule: csstree.Rule, 86 * matchedElements?: Array<XastElement> 87 * }>} 88 */ 89 let selectors = []; 90 91 return { 92 element: { 93 enter: (node, parentNode) => { 94 // skip <foreignObject /> content 95 if (node.name === 'foreignObject') { 96 return visitSkip; 97 } 98 // collect only non-empty <style /> elements 99 if (node.name !== 'style' || node.children.length === 0) { 100 return; 101 } 102 // values other than the empty string or text/css are not used 103 if ( 104 node.attributes.type != null && 105 node.attributes.type !== '' && 106 node.attributes.type !== 'text/css' 107 ) { 108 return; 109 } 110 // parse css in style element 111 let cssText = ''; 112 for (const child of node.children) { 113 if (child.type === 'text' || child.type === 'cdata') { 114 cssText += child.value; 115 } 116 } 117 /** 118 * @type {null | csstree.CssNode} 119 */ 120 let cssAst = null; 121 try { 122 cssAst = csstree.parse(cssText, { 123 parseValue: false, 124 parseCustomProperty: false, 125 }); 126 } catch { 127 return; 128 } 129 if (cssAst.type === 'StyleSheet') { 130 styles.push({ node, parentNode, cssAst }); 131 } 132 133 // collect selectors 134 csstree.walk(cssAst, { 135 visit: 'Selector', 136 enter(node, item) { 137 const atrule = this.atrule; 138 const rule = this.rule; 139 if (rule == null) { 140 return; 141 } 142 143 // skip media queries not included into useMqs param 144 let mq = ''; 145 if (atrule != null) { 146 mq = atrule.name; 147 if (atrule.prelude != null) { 148 mq += ` ${csstree.generate(atrule.prelude)}`; 149 } 150 } 151 if (useMqs.includes(mq) === false) { 152 return; 153 } 154 155 /** 156 * @type {Array<{ 157 * item: csstree.ListItem<csstree.CssNode>, 158 * list: csstree.List<csstree.CssNode> 159 * }>} 160 */ 161 const pseudos = []; 162 if (node.type === 'Selector') { 163 node.children.each((childNode, childItem, childList) => { 164 if ( 165 childNode.type === 'PseudoClassSelector' || 166 childNode.type === 'PseudoElementSelector' 167 ) { 168 pseudos.push({ item: childItem, list: childList }); 169 } 170 }); 171 } 172 173 // skip pseudo classes and pseudo elements not includes into usePseudos param 174 const pseudoSelectors = csstree.generate({ 175 type: 'Selector', 176 children: new csstree.List().fromArray( 177 pseudos.map((pseudo) => pseudo.item.data) 178 ), 179 }); 180 if (usePseudos.includes(pseudoSelectors) === false) { 181 return; 182 } 183 184 // remove pseudo classes and elements to allow querySelector match elements 185 // TODO this is not very accurate since some pseudo classes like first-child 186 // are used for selection 187 for (const pseudo of pseudos) { 188 pseudo.list.remove(pseudo.item); 189 } 190 191 selectors.push({ node, item, rule }); 192 }, 193 }); 194 }, 195 }, 196 197 root: { 198 exit: () => { 199 if (styles.length === 0) { 200 return; 201 } 202 // stable sort selectors 203 const sortedSelectors = stable(selectors, (a, b) => { 204 const aSpecificity = specificity(a.item.data); 205 const bSpecificity = specificity(b.item.data); 206 return compareSpecificity(aSpecificity, bSpecificity); 207 }).reverse(); 208 209 for (const selector of sortedSelectors) { 210 // match selectors 211 const selectorText = csstree.generate(selector.item.data); 212 /** 213 * @type {Array<XastElement>} 214 */ 215 const matchedElements = []; 216 try { 217 for (const node of querySelectorAll(root, selectorText)) { 218 if (node.type === 'element') { 219 matchedElements.push(node); 220 } 221 } 222 } catch (selectError) { 223 continue; 224 } 225 // nothing selected 226 if (matchedElements.length === 0) { 227 continue; 228 } 229 230 // apply styles to matched elements 231 // skip selectors that match more than once if option onlyMatchedOnce is enabled 232 if (onlyMatchedOnce && matchedElements.length > 1) { 233 continue; 234 } 235 236 // apply <style/> to matched elements 237 for (const selectedEl of matchedElements) { 238 const styleDeclarationList = csstree.parse( 239 selectedEl.attributes.style == null 240 ? '' 241 : selectedEl.attributes.style, 242 { 243 context: 'declarationList', 244 parseValue: false, 245 } 246 ); 247 if (styleDeclarationList.type !== 'DeclarationList') { 248 continue; 249 } 250 const styleDeclarationItems = new Map(); 251 csstree.walk(styleDeclarationList, { 252 visit: 'Declaration', 253 enter(node, item) { 254 styleDeclarationItems.set(node.property, item); 255 }, 256 }); 257 // merge declarations 258 csstree.walk(selector.rule, { 259 visit: 'Declaration', 260 enter(ruleDeclaration) { 261 // existing inline styles have higher priority 262 // no inline styles, external styles, external styles used 263 // inline styles, external styles same priority as inline styles, inline styles used 264 // inline styles, external styles higher priority than inline styles, external styles used 265 const matchedItem = styleDeclarationItems.get( 266 ruleDeclaration.property 267 ); 268 const ruleDeclarationItem = 269 styleDeclarationList.children.createItem(ruleDeclaration); 270 if (matchedItem == null) { 271 styleDeclarationList.children.append(ruleDeclarationItem); 272 } else if ( 273 matchedItem.data.important !== true && 274 ruleDeclaration.important === true 275 ) { 276 styleDeclarationList.children.replace( 277 matchedItem, 278 ruleDeclarationItem 279 ); 280 styleDeclarationItems.set( 281 ruleDeclaration.property, 282 ruleDeclarationItem 283 ); 284 } 285 }, 286 }); 287 selectedEl.attributes.style = 288 csstree.generate(styleDeclarationList); 289 } 290 291 if ( 292 removeMatchedSelectors && 293 matchedElements.length !== 0 && 294 selector.rule.prelude.type === 'SelectorList' 183 295 ) { 184 styleDeclarationList.children.replace( 185 matchedItem, 186 ruleDeclarationItem 296 // clean up matching simple selectors if option removeMatchedSelectors is enabled 297 selector.rule.prelude.children.remove(selector.item); 298 } 299 selector.matchedElements = matchedElements; 300 } 301 302 // no further processing required 303 if (removeMatchedSelectors === false) { 304 return; 305 } 306 307 // clean up matched class + ID attribute values 308 for (const selector of sortedSelectors) { 309 if (selector.matchedElements == null) { 310 continue; 311 } 312 313 if (onlyMatchedOnce && selector.matchedElements.length > 1) { 314 // skip selectors that match more than once if option onlyMatchedOnce is enabled 315 continue; 316 } 317 318 for (const selectedEl of selector.matchedElements) { 319 // class 320 const classList = new Set( 321 selectedEl.attributes.class == null 322 ? null 323 : selectedEl.attributes.class.split(' ') 187 324 ); 188 styleDeclarationItems.set( 189 ruleDeclaration.property, 190 ruleDeclarationItem 191 ); 192 } 193 }, 194 }); 195 selectedEl.attributes.style = csstree.generate(styleDeclarationList); 196 } 197 198 if ( 199 opts.removeMatchedSelectors && 200 selector.selectedEls !== null && 201 selector.selectedEls.length > 0 202 ) { 203 // clean up matching simple selectors if option removeMatchedSelectors is enabled 204 selector.rule.prelude.children.remove(selector.item); 205 } 206 } 207 208 if (!opts.removeMatchedSelectors) { 209 return root; // no further processing required 210 } 211 212 // clean up matched class + ID attribute values 213 for (selector of sortedSelectors) { 214 if (!selector.selectedEls) { 215 continue; 216 } 217 218 if ( 219 opts.onlyMatchedOnce && 220 selector.selectedEls !== null && 221 selector.selectedEls.length > 1 222 ) { 223 // skip selectors that match more than once if option onlyMatchedOnce is enabled 224 continue; 225 } 226 227 for (selectedEl of selector.selectedEls) { 228 // class 229 const classList = new Set( 230 selectedEl.attributes.class == null 231 ? null 232 : selectedEl.attributes.class.split(' ') 233 ); 234 const firstSubSelector = selector.item.data.children.first(); 235 if (firstSubSelector.type === 'ClassSelector') { 236 classList.delete(firstSubSelector.name); 237 } 238 if (classList.size === 0) { 239 delete selectedEl.attributes.class; 240 } else { 241 selectedEl.attributes.class = Array.from(classList).join(' '); 242 } 243 244 // ID 245 if (firstSubSelector.type === 'IdSelector') { 246 if (selectedEl.attributes.id === firstSubSelector.name) { 247 delete selectedEl.attributes.id; 248 } 249 } 250 } 251 } 252 253 // clean up now empty elements 254 for (var style of styles) { 255 csstree.walk(style.cssAst, { 256 visit: 'Rule', 257 enter: function (node, item, list) { 258 // clean up <style/> atrules without any rulesets left 259 if ( 260 node.type === 'Atrule' && 261 // only Atrules containing rulesets 262 node.block !== null && 263 node.block.children.isEmpty() 264 ) { 265 list.remove(item); 266 return; 267 } 268 269 // clean up <style/> rulesets without any css selectors left 270 if (node.type === 'Rule' && node.prelude.children.isEmpty()) { 271 list.remove(item); 325 const firstSubSelector = selector.node.children.first(); 326 if ( 327 firstSubSelector != null && 328 firstSubSelector.type === 'ClassSelector' 329 ) { 330 classList.delete(firstSubSelector.name); 331 } 332 if (classList.size === 0) { 333 delete selectedEl.attributes.class; 334 } else { 335 selectedEl.attributes.class = Array.from(classList).join(' '); 336 } 337 338 // ID 339 if ( 340 firstSubSelector != null && 341 firstSubSelector.type === 'IdSelector' 342 ) { 343 if (selectedEl.attributes.id === firstSubSelector.name) { 344 delete selectedEl.attributes.id; 345 } 346 } 347 } 348 } 349 350 for (const style of styles) { 351 csstree.walk(style.cssAst, { 352 visit: 'Rule', 353 enter: function (node, item, list) { 354 // clean up <style/> rulesets without any css selectors left 355 if ( 356 node.type === 'Rule' && 357 node.prelude.type === 'SelectorList' && 358 node.prelude.children.isEmpty() 359 ) { 360 list.remove(item); 361 } 362 }, 363 }); 364 365 if (style.cssAst.children.isEmpty()) { 366 // remove emtpy style element 367 detachNodeFromParent(style.node, style.parentNode); 368 } else { 369 // update style element if any styles left 370 const firstChild = style.node.children[0]; 371 if (firstChild.type === 'text' || firstChild.type === 'cdata') { 372 firstChild.value = csstree.generate(style.cssAst); 373 } 374 } 272 375 } 273 376 }, 274 }); 275 276 if (style.cssAst.children.isEmpty()) { 277 // clean up now emtpy <style/>s 278 var styleParentEl = style.styleEl.parentNode; 279 styleParentEl.spliceContent( 280 styleParentEl.children.indexOf(style.styleEl), 281 1 282 ); 283 284 if ( 285 styleParentEl.name === 'defs' && 286 styleParentEl.children.length === 0 287 ) { 288 // also clean up now empty <def/>s 289 var defsParentEl = styleParentEl.parentNode; 290 defsParentEl.spliceContent( 291 defsParentEl.children.indexOf(styleParentEl), 292 1 293 ); 294 } 295 296 continue; 297 } 298 299 // update existing, left over <style>s 300 cssTools.setCssStr(style.styleEl, csstree.generate(style.cssAst)); 301 } 302 303 return root; 377 }, 378 }; 304 379 }; -
trip-planner-front/node_modules/svgo/plugins/prefixIds.js
r59329aa re29cc2e 207 207 node.attributes[name].length !== 0 208 208 ) { 209 // extract id reference from url(...) value 210 const matches = /url\((.*?)\)/gi.exec(node.attributes[name]); 211 if (matches != null) { 212 const value = matches[1]; 213 const prefixed = prefixReference(prefix, value); 214 if (prefixed != null) { 215 node.attributes[name] = `url(${prefixed})`; 209 node.attributes[name] = node.attributes[name].replace( 210 /url\((.*?)\)/gi, 211 (match, url) => { 212 const prefixed = prefixReference(prefix, url); 213 if (prefixed == null) { 214 return match; 215 } 216 return `url(${prefixed})`; 216 217 } 217 }218 ); 218 219 } 219 220 } -
trip-planner-front/node_modules/svgo/plugins/removeEmptyAttrs.js
r59329aa re29cc2e 3 3 const { attrsGroups } = require('./_collections.js'); 4 4 5 exports.type = 'visitor'; 5 6 exports.name = 'removeEmptyAttrs'; 6 7 exports.type = 'perItem';8 9 7 exports.active = true; 10 11 8 exports.description = 'removes empty attributes'; 12 9 … … 14 11 * Remove attributes with empty values. 15 12 * 16 * @param {Object} item current iteration item 17 * @return {Boolean} if false, item will be filtered out 13 * @author Kir Belevich 18 14 * 19 * @ author Kir Belevich15 * @type {import('../lib/types').Plugin<void>} 20 16 */ 21 exports.fn = function (item) { 22 if (item.type === 'element') { 23 for (const [name, value] of Object.entries(item.attributes)) { 24 if ( 25 value === '' && 26 // empty conditional processing attributes prevents elements from rendering 27 attrsGroups.conditionalProcessing.includes(name) === false 28 ) { 29 delete item.attributes[name]; 30 } 31 } 32 } 17 exports.fn = () => { 18 return { 19 element: { 20 enter: (node) => { 21 for (const [name, value] of Object.entries(node.attributes)) { 22 if ( 23 value === '' && 24 // empty conditional processing attributes prevents elements from rendering 25 attrsGroups.conditionalProcessing.includes(name) === false 26 ) { 27 delete node.attributes[name]; 28 } 29 } 30 }, 31 }, 32 }; 33 33 }; -
trip-planner-front/node_modules/svgo/plugins/removeXMLNS.js
r59329aa re29cc2e 26 26 if (item.type === 'element' && item.name === 'svg') { 27 27 delete item.attributes.xmlns; 28 delete item.attributes['xmlns:xlink']; 28 29 } 29 30 };
Note:
See TracChangeset
for help on using the changeset viewer.