import { kashe } from 'kashe' ; import { StyleAst } from './parser/ast' ; import { extractUnmatchable , fromAst , getUnmatchableRules } from './parser/fromAst' ; import { FlagType , SelectionFilter , StyleChunk , StyleDefinition , UsedTypes , UsedTypesRef } from './types' ; import { assertIsReady } from './utils/async' ; import { createUsedFilter } from './utils/cache' ; import { unique } from './utils/order' ; import { getStylesInText } from './utils/string' ; export const getUnusableStyles = kashe ( ( def ) : UsedTypesRef => Object . keys ( def . ast || { } ) . filter ( ( key ) => getUnmatchableRules ( def . ast [ key ] ) . length > 0 ) . reduce ( ( acc , file ) => { acc [ file ] = true ; return acc ; } , { } as UsedTypesRef ) ) ; export const astToUsedStyles = kashe ( ( styles , def ) => { const { lookup , ast } = def ; const fetches = { } ; const visitedStyles = new Set < string > ( ) ; styles . forEach ( ( className ) => { if ( visitedStyles . has ( className ) ) { return ; } visitedStyles . add ( className ) ; const classes = className . split ( ' ' ) ; classes . forEach ( ( singleClass ) => { const files = lookup [ singleClass ] ; if ( files ) { files . forEach ( ( file ) => { if ( ! fetches [ file ] ) { fetches [ file ] = { } ; } fetches [ file ] [ singleClass ] = true ; } ) ; } } ) ; } ) ; return { fetches , usage : Object . keys ( ast ) . filter ( ( file ) => ! ! fetches [ file ] ) , } ; } ) ; const getUsedStylesIn = kashe ( ( styles , def ) : UsedTypes => { assertIsReady ( def ) ; const { usage } = astToUsedStyles ( styles , def ) ; const flags = { ... getUnusableStyles ( def ) , ... usage . reduce ( ( acc , file ) => { acc [ file ] = true ; return acc ; } , { } as FlagType ) , } ; return Object . keys ( Object . keys ( def . ast ) . reduce ( ( acc , file ) => { if ( flags [ file ] ) { acc [ file ] = true ; } return acc ; } , { } as FlagType ) ) ; } ) ; /**
 * returns names of the style files for a given HTML and style definitions
 */ export const getUsedStyles = ( htmlCode , def ) : UsedTypes => { assertIsReady ( def ) ; return getUsedStylesIn ( getStylesInText ( htmlCode ) , def ) ; } ; const astToStyles = kashe ( ( styles , def , filter ? ) : StyleChunk [ ] => { const { ast } = def ; const { fetches , usage } = astToUsedStyles ( styles , def ) ; return usage . map ( ( file ) => ( { file , css : fromAst ( Object . keys ( fetches [ file ] ) , ast [ file ] , filter ) , } ) ) ; } ) ; export const wrapInStyle = ( styles , usedStyles = [ ] ) => styles ? `<style type="text/css" data-used-styles="${ usedStyles . length === 0 ? 'true' : usedStyles . join ( ',' ) }">${ styles }</style>` : '' ; export const extractUnmatchableFromAst = kashe ( ( ast , filter ? ) : StyleChunk [ ] => Object . keys ( ast || { } ) . map ( ( file ) => { const css = extractUnmatchable ( ast [ file ] , filter ) ; if ( css ) { return { file , css , } as StyleChunk ; } return undefined ; } ) . filter ( ( x ) => ! ! x ) . map ( ( x ) => x as StyleChunk ) ) ; export const extractAllUnmatchable = ( def , filter ? ) : StyleChunk [ ] => extractUnmatchableFromAst ( def . ast , filter ) ; export const extractAllUnmatchableAsString = kashe ( ( def ) => wrapInStyle ( extractAllUnmatchable ( def ) . reduce ( ( acc , { css } ) => acc + css , '' ) , [ '_unmatched' ] ) ) ; /**
 * just wraps with <style
 */ const criticalRulesToStyle = ( styles , urlPrefix = '' ) : string => wrapInStyle ( styles . map ( ( { css } ) => css ) . join ( '' ) , unique ( styles . map ( ( { file } ) => `${ urlPrefix }${ file }` ) ) ) ; export const criticalStylesToString = ( html , def , filter ? ) : string => { assertIsReady ( def ) ; return criticalRulesToStyle ( astToStyles ( getStylesInText ( html ) , def , filter ) , def . urlPrefix ) ; } ; const getRawCriticalRules = ( html , def , filter ? ) => { assertIsReady ( def ) ; return astToStyles ( getStylesInText ( html ) , def , filter ) ; } ; see extractCriticalRules for chunk-based operations
  export const getCriticalRules = ( html , def , filter = createUsedFilter ( ) ) : string => { assertIsReady ( def ) ; return [ ... extractAllUnmatchable ( def , filter ) , ... getRawCriticalRules ( html , def , filter ) ] . map ( ( { css , file } ) => `\n/* ${ file } */\n${ css }` ) . join ( '' ) ; } ; see getCriticalRules for more complete solution, including unmatchable rules as well
  export const extractCriticalRules = ( html , def , filter = createUsedFilter ( ) ) : string => { assertIsReady ( def ) ; return getRawCriticalRules ( html , def , filter ) . map ( ( { css , file } ) => `\n/* ${ file } */\n${ css }` ) . join ( '' ) ; } ; see getCriticalRules for lower level API
 *  param html param def param filter export const getCriticalStyles = ( html , def , filter = createUsedFilter ( ) ) : string => { assertIsReady ( def ) ; return criticalRulesToStyle ( [ ... extractAllUnmatchable ( def , filter ) , ... getRawCriticalRules ( html , def , filter ) ] , def . urlPrefix ) ; } ;