import { createClassNameGenerator } from './ClassNameGenerator' ; import resolveStyles , { Basic } from './resolveStyles' ; import { getRule } from './RulesRegistry' ; import { RuleRendererState , RuleRenderer , createRuleRenderer } from './RuleRenderer' ; import { Indexable , RecursiveArray , Nullable } from './utils/types' ; import adler32 from './utils/adler32' ; import warning from './utils/warning' ; export type StylesRenderer = { renderStyles ( ... styles ) : string ; attach ( styleElement ) : void ; renderToString ( ) : string ; serialize ( ) : StylesRendererState ; } ; export type StylesRendererState = { rules : { rendererState : RuleRendererState ; ruleId : string ; } [ ] ; classNameGeneratorKey : number ; checkSum : string ; } ; export function createStylesRenderer ( state ? ) { let sheet = null ; let stringValue = null ; const orderedRules = state ? state . rules . map ( ( { ruleId } ) => ruleId ) : [ ] ; const classNameGenerator = createClassNameGenerator ( state && state . classNameGeneratorKey ) ; const renderers = { } ; if ( state ) { state . rules . forEach ( ( { ruleId , rendererState : { classNames , index } } ) => { const rule = getRule ( ruleId ) ; if ( ! rule ) { throw new Error ( 'Some rules defined in the serialized state of your styles renderer have not been registered' ) ; } renderers [ ruleId ] = createRuleRenderer ( rule , index , classNameGenerator , classNames ) ; } ) ; } function renderStyles ( ... styles ) { const rules = resolveStyles ( styles ) ; const classNames = [ ] ; let overrideCount = 0 ; let maxRenderedIndex = - 1 ; for ( const rule of rules ) { let renderer = renderers [ rule . id ] ; if ( ! renderer ) { renderer = renderers [ rule . id ] = createRuleRenderer ( rule , orderedRules . length , classNameGenerator ) ; orderedRules . push ( rule . id ) ; stringValue = null ; } const index = renderer . index ( ) ; if ( index < maxRenderedIndex ) { overrideCount ++ ; } else { maxRenderedIndex = index ; } if ( renderer . setRuleOverrideCount ( overrideCount ) ) { stringValue = null ; } if ( sheet ) { renderer . attach ( sheet ) ; } classNames . push ( ... renderer . getRuleClassNames ( overrideCount ) ) ; } return classNames . join ( ' ' ) ; } function attach ( styleElement ) { sheet = styleElement . sheet as CSSStyleSheet ; if ( state ) { if ( adler32 ( styleElement . textContent || '' ) !== state . checkSum ) { warning ( 'VStyle attempted to reuse the styles inside the provided style element ' + 'but the checksum was invalid.' ) ; styleElement . textContent = '' ; sheet = styleElement . sheet as CSSStyleSheet ; } else { let styleSheetIndex = 0 ; for ( const id of orderedRules ) { styleSheetIndex = renderers [ id ] . connect ( sheet , styleSheetIndex ) ; } return ; } } if ( ! state && sheet . cssRules . length ) { warning ( 'The style element already contains styles, but no serialized state ' + 'has been provided to `createStylesRenderer`' ) ; styleElement . textContent = '' ; sheet = styleElement . sheet as CSSStyleSheet ; } for ( const id of orderedRules ) { renderers [ id ] . attach ( sheet ) ; } } function renderToString ( ) { if ( ! stringValue ) { stringValue = orderedRules . map ( id => renderers [ id ] . renderToString ( ) ) . join ( '' ) ; } return stringValue ; } function serialize ( ) { return { rules : orderedRules . map ( ruleId => ( { ruleId , rendererState : renderers [ ruleId ] . serialize ( ) , } ) ) , classNameGeneratorKey : classNameGenerator . getCurrCSSKey ( ) , checkSum : adler32 ( renderToString ( ) ) , } ; } return { renderStyles , attach , renderToString , serialize , } ; }