import { kashe } from 'kashe' ; import * as postcss from 'postcss' ; import { SelectionFilter } from '../types' ; import { SingleStyleAst , StyleBody , StyleRule , StyleSelector } from './ast' ; const separator = process . env . NODE_ENV === 'production' ? '' : '\n' ; export const escapeValue = ( value , name ) => { if ( name === 'content' ) { return value . split ( '\\' ) . join ( '\\\\' ) ; } return value ; } ; const createDecl = ( decl ) => postcss . decl ( decl ) + ';' ; const declsToString = ( rules ) => rules . map ( ( decl ) => createDecl ( decl ) ) . join ( separator ) ; const getMedia = ( { media } ) => { const prefix = [ ] ; const postfix = [ ] ; media . forEach ( ( currentMedia ) => { prefix . push ( `@media ${ currentMedia } {` ) ; postfix . push ( '}' ) ; } ) ; return [ prefix . join ( separator ) , postfix . join ( separator ) ] ; } ; type SelectorLookUp = Set < string > ; const renderRule = kashe ( ( rule , style ) => `${ rule . selector } { ${ declsToString ( style . rules ) } }` ) ; const isMatching = ( rule , rules ) => rule . pieces . length > 0 && rule . pieces . every ( ( piece ) => rules . has ( piece ) ) ; const findMatchingSelectors = ( rules , selectors ) : StyleSelector [ ] => selectors . filter ( ( rule ) => isMatching ( rule , rules ) ) ; const findUnmatchableSelectors = ( selectors ) : StyleSelector [ ] => selectors . filter ( ( rule ) => rule . pieces . length === 0 ) ; export const fromAst = ( rules , def , filter ? ) => { const blocks = [ ] ; const lookup = new Set ( rules ) ; blocks . push ( ... findMatchingSelectors ( lookup , def . selectors ) . filter ( ( block ) => ! filter || filter ( block . selector , block ) ) ) ; return convertToString ( blocks , def ) ; } ; export const getUnmatchableRules = ( def , filter ? ) : StyleSelector [ ] => findUnmatchableSelectors ( def . selectors ) . filter ( ( block ) => ! filter || filter ( block . selector , block ) ) ; export const extractUnmatchable = ( def , filter ? ) => convertToString ( getUnmatchableRules ( def , filter ) , def ) + getAtRules ( def ) ; const getAtRules = ( def ) => def . atRules . reduce ( ( acc , rule ) => acc + rule . css , '' ) ; export const convertToString = ( blocks , { bodies } ) => { blocks . sort ( ( ruleA , ruleB ) => bodies [ ruleA . declaration ] . id - bodies [ ruleB . declaration ] . id ) ; const result = [ ] ; let lastMedia = [ '' , '' ] ; blocks . forEach ( ( block , index ) => { const media = getMedia ( block ) ; if ( media [ 0 ] !== lastMedia [ 0 ] ) { result . push ( lastMedia [ 1 ] ) ; lastMedia = media ; result . push ( lastMedia [ 0 ] ) ; } if ( index < blocks . length - 1 && block . declaration === blocks [ index + 1 ] . declaration ) { result . push ( `${ block . selector },` ) ; } else { result . push ( renderRule ( block , bodies [ block . declaration ] ) ) ; } } ) ; result . push ( lastMedia [ 1 ] ) ; return result . join ( separator ) ; } ;