import { Declaration , Rule , StyleRules } from 'css' ; import { parseSelector } from 'svg-vdom' ; import { IRuleOption } from '../../typings' ; import { IDom , ITag } from '../../typings/node' ; import { importantReg } from '../const/regs' ; import { regularAttr } from '../const/regular-attr' ; import { checkApply } from '../style/check-apply' ; import { hasProp } from '../utils/has-prop' ; import { mixWhiteSpace } from '../utils/mix-white-space' ; import { knownCSS } from '../validate/known-css' ; interface ICSSUnique { [ propName ] : Rule ; } export const shortenStyleTag = async ( dom , { params : { ignoreKnownCSS , } , } ) : Promise < void > => new Promise ( resolve => { if ( dom . stylesheet ) { const cssRules = dom . stylesheet . stylesheet as StyleRules ; const selectorUnique = { } ; const declareUnique = { } ; for ( let i = 0 , l = cssRules . rules . length ; i < l ; i ++ ) { const styleRule = cssRules . rules [ i ] as Rule ; if ( styleRule . type === 'rule' ) { const theSelectors = styleRule . selectors as string [ ] ; const declarations = styleRule . declarations as Declaration [ ] ; const usedRule = new Set < string > ( ) ; for ( let si = theSelectors . length ; si -- ; ) { const matchNodes = dom . querySelectorAll ( parseSelector ( theSelectors [ si ] ) ) as ITag [ ] ; if ( ! matchNodes . length ) { theSelectors . splice ( si , 1 ) ; } else { let anyMatch = false ; for ( let mi = declarations . length ; mi -- ; ) { const ruleItem = declarations [ mi ] ; const property = ruleItem . property as string ; const isImportant = importantReg . test ( ruleItem . value as string ) ; if ( ( regularAttr [ property ] . isUndef && knownCSS ( property ) && ! ignoreKnownCSS ) || matchNodes . some ( matchNode => checkApply ( regularAttr [ property ] , matchNode , dom , true , isImportant ) ) ) { anyMatch = true ; usedRule . add ( property ) ; } } if ( ! anyMatch ) { theSelectors . splice ( si , 1 ) ; } } } for ( let ci = declarations . length ; ci -- ; ) { if ( ! usedRule . has ( declarations [ ci ] . property as string ) ) { declarations . splice ( ci , 1 ) ; } } if ( ! theSelectors . length ) { cssRules . rules . splice ( i , 1 ) ; i -- ; l -- ; continue ; } theSelectors . sort ( ( a , b ) => a < b ? - 1 : 1 ) ; styleRule . selectors = theSelectors . map ( s => mixWhiteSpace ( s . trim ( ) ) ) ; const selectorKey = styleRule . selectors . join ( ',' ) ; if ( hasProp ( selectorUnique , selectorKey ) ) { const uDeclarations = ( selectorUnique [ selectorKey ] . declarations as Declaration [ ] ) . concat ( styleRule . declarations as Declaration [ ] ) ; const declared = new Set < string > ( ) ; for ( let j = uDeclarations . length ; j -- ; ) { const prop = uDeclarations [ j ] . property as string ; if ( declared . has ( prop ) ) { uDeclarations . splice ( j , 1 ) ; } else { declared . add ( prop ) ; } } selectorUnique [ selectorKey ] . declarations = uDeclarations ; cssRules . rules . splice ( i , 1 ) ; i -- ; l -- ; continue ; } else { selectorUnique [ selectorKey ] = styleRule ; } ( styleRule . declarations as Declaration [ ] ) . sort ( ( a , b ) => ( a . property as string ) < ( b . property as string ) ? - 1 : 1 ) ; const declareKey = ( styleRule . declarations as Declaration [ ] ) . map ( ( d ) => `${ d . property }:${ d . value }` ) . join ( ';' ) ; if ( hasProp ( declareUnique , declareKey ) ) { const selectors = ( declareUnique [ declareKey ] . selectors as string [ ] ) . concat ( styleRule . selectors ) ; const selected = new Set < string > ( ) ; for ( let j = selectors . length ; j -- ; ) { if ( selected . has ( selectors [ j ] ) ) { selectors . splice ( j , 1 ) ; } else { selected . add ( selectors [ j ] ) ; } } declareUnique [ declareKey ] . selectors = selectors ; cssRules . rules . splice ( i , 1 ) ; i -- ; l -- ; continue ; } else { declareUnique [ declareKey ] = styleRule ; } } } } resolve ( ) ; } ) ;