import { Rule , StyleRules } from 'css' ; import { has } from 'ramda' ; import { ITagNode , NodeType } from 'svg-vdom' ; import { IDom } from '../../typings/node' ; import { createShortenID } from '../algorithm/create-shorten-id' ; import { hasProp } from '../utils/has-prop' ; import { mixWhiteSpace } from '../utils/mix-white-space' ; import { traversalObj } from '../utils/traversal-obj' ; const classSelectorReg = /\.([^,*#>+~:{\s[.]+)/gi ; interface IClassList { [ propName ] : [ string , boolean ] ; } export const shortenClass = async ( dom ) : Promise < void > => new Promise ( resolve => { const parsedCss = dom . stylesheet ; if ( parsedCss ) { let si = 0 ; const classList = { } ; const shorten = ( key ) => { if ( hasProp ( classList , key ) ) { return classList [ key ] [ 0 ] ; } const sid = createShortenID ( si ++ ) ; classList [ key ] = [ sid , false ] ; return sid ; } ; const cssRules = parsedCss . stylesheet as StyleRules ; traversalObj ( has ( 'selectors' ) , ( ruleItem ) => { const selectors = ruleItem . selectors ; if ( selectors ) { selectors . forEach ( ( selector , selectorIndex ) => { selectors [ selectorIndex ] = selector . replace ( classSelectorReg , ( m , p ) => `.${ shorten ( p ) }` ) ; } ) ; } } , cssRules . rules ) ; ( dom . querySelectorAll ( NodeType . Tag ) as ITagNode [ ] ) . forEach ( node => { const classAttr = node . getAttribute ( 'class' ) ; if ( classAttr !== null ) { const className = mixWhiteSpace ( classAttr . trim ( ) ) . split ( /\s+/ ) ; for ( let ci = className . length ; ci -- ; ) { if ( hasProp ( classList , className [ ci ] ) ) { const cName = classList [ className [ ci ] ] [ 0 ] ; classList [ className [ ci ] ] [ 1 ] = true ; className [ ci ] = cName ; } else { className . splice ( ci , 1 ) ; } } if ( className . length ) { node . setAttribute ( 'class' , className . join ( ' ' ) ) ; } else { node . removeAttribute ( 'class' ) ; } } } ) ; Object . values ( classList ) . forEach ( item => { if ( item [ 1 ] ) { return ; } const reg = new RegExp ( `\\.${ item [ 0 ] }(?=[,\\*#>+~:{\\s\\[\\.]|$)` ) ; traversalObj ( has ( 'selectors' ) , ( ruleItem , path ) => { const selectors = ruleItem . selectors ; if ( selectors ) { for ( let i = selectors . length ; i -- ; ) { if ( reg . test ( selectors [ i ] ) ) { selectors . splice ( i , 1 ) ; } } if ( ! selectors . length ) { const parent = path [ path . length - 1 ] as Rule [ ] ; parent . splice ( parent . indexOf ( ruleItem ) , 1 ) ; } } } , cssRules . rules ) ; } ) ; } else { ( dom . querySelectorAll ( NodeType . Tag ) as ITagNode [ ] ) . forEach ( node => { node . removeAttribute ( 'class' ) ; } ) ; } resolve ( ) ; } ) ;