import type { Token , Theme , ThemeValueResolver , ThemeSectionValueType , Plugin , PluginTokenResult , InjectKeyframes , PluginResult , SelectorDecorator , Falsy , } from './types' ; import type { Context } from './context' ; import * as is from './is' ; import { tail , join } from './util' ; let negate ; let currentContext ; const groupings = [ ] ; const classNames = [ ] ; const theme = < Section extends keyof Theme > ( section , key , defaultValue ? ) => { const value = currentContext . t ( section , key , defaultValue as Falsy ) ; return negate && value && is . string ( value ) ? ( `calc(${ value } * -1)` as ThemeSectionValueType < Theme [ Section ] > ) : value ; } ; const keyframes = ( name , waypoints ) => currentContext . k ( name , waypoints || currentContext . t ( 'keyframes' , name ) || { } ) ; const processTokenResult = ( token , variants , result ) : void => { const lastClassNameLength = classNames . length ; startGrouping ( ) ; if ( is . string ( result ) ) { parseString ( result ) ; } else { result ( parse ) ; } endGrouping ( ) ; currentContext . s ( token , variants , join ( tail ( classNames , lastClassNameLength ) , ' ' ) ) ; } ; const handlePluginResult = ( token , variants , result ) : boolean | number | void => { if ( is . function ( result ) || is . string ( result ) ) { return ! ( processTokenResult ( token , variants , result ) as unknown ) ; } let suffix ; if ( is . array ( result ) ) { suffix = result [ 0 ] ; result = result [ 1 ] ; } if ( is . object ( result ) ) { return classNames . push ( currentContext . i ( token , variants , result , is . string ( suffix ) ? ( selector ) => selector + ( suffix as string ) : suffix ) ) ; } } ; const handleNegation = ( value ) : string => value [ 0 ] === '-' ? ( ( negate = '-' ) , tail ( value ) ) : ( ( negate = '' ) , value ) ; const translate = ( token , variants ) : unknown => { const className = ( token === 'group' && currentContext . a ( token ) ) || currentContext . g ( token , variants ) ; if ( className != null ) { return className && classNames . push ( className ) ; } let directive = token ; directive = handleNegation ( directive ) ; let parts = directive . split ( '-' ) ; let plugin ; for ( let index = parts . length ; index ; index -- ) { const id = join ( parts . slice ( 0 , index ) ) ; plugin = currentContext . p ( id ) ; if ( plugin ) { parts = tail ( parts , index ) ; parts . unshift ( id ) ; break ; } } if ( ! handlePluginResult ( token , variants , is . function ( plugin ) ? plugin ( parts , theme , { keyframes , tag : currentContext . a } ) : plugin ) ) { currentContext . w ( token , plugin ? `Plugin "${ parts [ 0 ] }" had no result` : `No plugin for "${ directive }" found` ) ; } } ; const reset = ( array ) : void => { array . length = 0 ; } ; const startGrouping = ( value = '' ) : '' => { groupings . push ( value ) ; return '' ; } ; const endGrouping = ( isWhitespace ? ) : void => { const index = groupings . lastIndexOf ( '' ) ; if ( ~ index ) { groupings . splice ( index + ~ ~ ( isWhitespace as boolean ) , groupings . length - index + ~ ~ ( isWhitespace as boolean ) ) ; } } ; const onlyPrefixes = ( s ) : '' | boolean => s && s [ 0 ] !== ':' ; const onlyVariants = ( s ) : '' | boolean => s [ 0 ] === ':' ; const translateBuffer = ( buffer ) : '' => { if ( buffer ) { buffer = handleNegation ( buffer ) ; const p = join ( groupings . filter ( onlyPrefixes ) ) ; translate ( buffer === '&' ? p : negate + ( p && p + '-' ) + buffer , groupings . filter ( onlyVariants ) ) ; } return '' ; } ; const parseString = ( token , isVariant ? ) : void => { let char ; let buffer = '' ; for ( let position = 0 ; position < token . length ; ) { switch ( ( char = token [ position ++ ] ) ) { case ':' : if ( buffer ) { buffer = startGrouping ( ':' + buffer ) ; } break ; case '(' : if ( buffer ) { buffer = startGrouping ( buffer ) ; } startGrouping ( ) ; break ; case ')' : case ' ' : case '\t' : case '\n' : case '\r' : buffer = translateBuffer ( buffer ) ; endGrouping ( char !== ')' ) ; break ; default : buffer += char ; } } if ( isVariant ) { if ( buffer ) { startGrouping ( ':' + buffer ) ; } } else { translateBuffer ( buffer ) ; } } ; const parseGroupedToken = ( token ) : void => { if ( token ) { startGrouping ( ) ; parse ( token ) ; endGrouping ( ) ; } } ; const parseGroup = ( key , token ) : void => { if ( token ) { startGrouping ( ) ; const isVariant = is . string ( token ) || is . array ( token ) || is . object ( token ) || is . function ( token ) ; parseString ( key , isVariant ) ; if ( isVariant ) { parseGroupedToken ( token ) ; } endGrouping ( ) ; } } ; let nextTokenId = 0 ; const parse = ( token ) : void => { if ( is . string ( token ) ) { parseString ( token ) ; } else if ( is . array ( token ) ) { token . forEach ( parseGroupedToken ) ; } else if ( is . function ( token ) ) { handlePluginResult ( `__${ token . name }_${ ( ++ nextTokenId ) . toString ( 36 ) }` , groupings . filter ( onlyVariants ) , token ( theme , { keyframes , tag : currentContext . a } ) ) ; } else if ( is . object ( token ) ) { Object . keys ( token ) . forEach ( ( key ) => { parseGroup ( key , token [ key ] ) ; } ) ; } } ; export const process = ( token , context ) : string => { if ( currentContext ) { throw new Error ( 'There is already an active context' ) ; } currentContext = context ; reset ( classNames ) ; reset ( groupings ) ; try { token . forEach ( parseGroupedToken ) ; } finally { ; ( ( currentContext as unknown ) as undefined ) = undefined ; } return join ( classNames , ' ' ) ; } ;