import { CSSAbbreviation , CSSProperty , Value , CSSValue , NumberValue } from '@emmetio/css-abbreviation' ; import createOutputStream , { OutputStream , push , pushString , pushField , pushNewline } from '../output-stream' ; import { Config } from '../config' ; import color , { frac } from './color' ; import { CSSAbbreviationScope } from './' ; export default function css ( abbr , config ) { const out = createOutputStream ( config . options ) ; const format = config . options [ 'output.format' ] ; if ( config . context ?. name === CSSAbbreviationScope . Section ) { abbr = abbr . filter ( node => node . snippet ) ; } for ( let i = 0 ; i < abbr . length ; i ++ ) { if ( format && i !== 0 ) { pushNewline ( out , true ) ; } property ( abbr [ i ] , out , config ) ; } return out . value ; } /**
 * Outputs given abbreviation node into output stream
 */ function property ( node , out , config ) { const isJSON = config . options [ 'stylesheet.json' ] ; if ( node . name ) { const name = isJSON ? toCamelCase ( node . name ) : node . name ; pushString ( out , name + config . options [ 'stylesheet.between' ] ) ; if ( node . value . length ) { propertyValue ( node , out , config ) ; } else { pushField ( out , 0 , '' ) ; } if ( isJSON ) { push ( out , ',' ) ; } else { outputImportant ( node , out , true ) ; push ( out , config . options [ 'stylesheet.after' ] ) ; } } else { for ( const cssVal of node . value ) { for ( const v of cssVal . value ) { outputToken ( v , out , config ) ; } } outputImportant ( node , out , node . value . length > 0 ) ; } } function propertyValue ( node , out , config ) { const isJSON = config . options [ 'stylesheet.json' ] ; const num = isJSON ? getSingleNumeric ( node ) : null ; if ( num && ( ! num . unit || num . unit === 'px' ) ) { push ( out , String ( num . value ) ) ; } else { const quote = getQuote ( config ) ; isJSON && push ( out , quote ) ; for ( let i = 0 ; i < node . value . length ; i ++ ) { if ( i !== 0 ) { push ( out , ', ' ) ; } outputValue ( node . value [ i ] , out , config ) ; } isJSON && push ( out , quote ) ; } } function outputImportant ( node , out , separator ? ) { if ( node . important ) { if ( separator ) { push ( out , ' ' ) ; } push ( out , '!important' ) ; } } function outputValue ( value , out , config ) { for ( let i = 0 , prevEnd = - 1 ; i < value . value . length ; i ++ ) { const token = value . value [ i ] ; if ( i !== 0 && ( token . type !== 'Field' || token . start !== prevEnd ) ) { push ( out , ' ' ) ; } outputToken ( token , out , config ) ; prevEnd = token [ 'end' ] ; } } function outputToken ( token , out , config ) { if ( token . type === 'ColorValue' ) { push ( out , color ( token , config . options [ 'stylesheet.shortHex' ] ) ) ; } else if ( token . type === 'Literal' ) { pushString ( out , token . value ) ; } else if ( token . type === 'NumberValue' ) { pushString ( out , frac ( token . value , 4 ) + token . unit ) ; } else if ( token . type === 'StringValue' ) { const quote = token . quote === 'double' ? '"' : '\'' ; pushString ( out , quote + token . value + quote ) ; } else if ( token . type === 'Field' ) { pushField ( out , token . index ! , token . name ) ; } else if ( token . type === 'FunctionCall' ) { push ( out , token . name + '(' ) ; for ( let i = 0 ; i < token . arguments . length ; i ++ ) { if ( i ) { push ( out , ', ' ) ; } outputValue ( token . arguments [ i ] , out , config ) ; } push ( out , ')' ) ; } } /**
 * If value of given property is a single numeric value, returns this token
 */ function getSingleNumeric ( node ) { if ( node . value . length === 1 ) { const cssVal = node . value [ 0 ] ! ; if ( cssVal . value . length === 1 && cssVal . value [ 0 ] ! . type === 'NumberValue' ) { return cssVal . value [ 0 ] as NumberValue ; } } } /**
 * Converts kebab-case string to camelCase
 */ function toCamelCase ( str ) { return str . replace ( /\-(\w)/g , ( _ , letter ) => letter . toUpperCase ( ) ) ; } function getQuote ( config ) { return config . options [ 'stylesheet.jsonDoubleQuotes' ] ? '"' : '\'' ; }