import { isFn , isObj , isArr } from './utils' ; import { Scope , GetOptions , getRef } from './get' ; import { parsePath , Ref } from './parse' ; import { Tokens } from './tokenize' ; /**
 * The options for the Renderer's constructor
 */ export interface RendererOptions extends GetOptions { /**
     * When set to a truthy value, rendering literally puts a 'null' or
     * 'undefined' for values that are `null` or `undefined`.
     * By default it swallows those values to be compatible with Mustache.
     */ readonly explicit ? : boolean ; /** when set to a truthy value, validates the paths */ readonly validatePath ? : boolean ; } param scope param path example returns export type ResolveFn = ( path : string , scope ? : Scope ) => any ; /**
 * Same as `ResolveFn` but for asynchronous functions
 */ export type ResolveFnAsync = ( path : string , scope ? : Scope ) => Promise < any > ; /**
 * This class does the heavy lifting of interpolation (putting the actual values
 * in the template).
 * This is created by the `.compile()` method and is used under the hood by
 * `.render()`, `renderFn()` and `renderFnAsync()` functions.
 */ export class Renderer { /**
     * Another cache that holds the parsed values for `parsePath()` one per path
     */ private _refsCache : Ref [ ] ; param tokens param options throws constructor ( private readonly tokens , private readonly options = { } ) { if ( ! isObj ( tokens ) || ! isArr ( tokens . strings ) || ! isArr ( tokens . paths ) || tokens . strings . length !== tokens . paths . length + 1 ) { throw new TypeError ( `Invalid tokens object` ) ; } if ( ! isObj ( options ) ) { throw new TypeError ( `Options should be an object. Got a ${ typeof options }` ) ; } if ( options . validatePath ) { this . refs ; } } /**
     * This function is called internally for filling in the `refs` cache.
     * If the `validatePath` option for the constructor is set to a truthy
     * value, this function is called immediately which leads to a validation as
     * well because it throws an error if it cannot parse paths.
     */ private get refs ( ) : Ref [ ] { const { paths } = this . tokens ; if ( this . _refsCache === undefined ) { this . _refsCache = new Array < Ref > ( paths . length ) ; for ( let i = 0 ; i < paths . length ; i ++ ) { this . _refsCache [ i ] = parsePath . cached ( paths [ i ] ) ; } } return this . _refsCache ; } param template param scope public render = ( scope = { } ) : string => { const { paths } = this . tokens ; const { length } = paths ; const { refs } = this ; const values = new Array < any > ( length ) ; for ( let i = 0 ; i < length ; i ++ ) { values [ i ] = getRef ( scope , refs [ i ] , this . options ) ; } return this . stringify ( values ) ; } ; /**
     * Same as [[render]] but accepts a resolver function which returns a value for every path.
     */ public renderFn = ( resolveFn , scope = { } ) : string => { const values = this . resolveRefs ( resolveFn , scope ) ; return this . stringify ( values ) ; } ; /**
     * Same as [[render]] but accepts a resolver function which returns a promise that resolves to a
     * value for every path.
     */ public renderFnAsync = ( resolveFnAsync , scope = { } ) : Promise < string > => { return Promise . all ( this . resolveRefs ( resolveFnAsync , scope ) ) . then ( ( values ) => this . stringify ( values ) ) ; } ; private resolveRefs ( resolveFn , scope = { } ) { const { paths } = this . tokens ; if ( ! isFn < ResolveFnAsync > ( resolveFn ) ) { throw new TypeError ( `Expected a resolver function. Got ${ String ( resolveFn ) }` ) ; } const { length } = paths ; const values = new Array < any > ( length ) ; for ( let i = 0 ; i < length ; i ++ ) { values [ i ] = resolveFn . call ( null , paths [ i ] , scope ) ; } return values ; } /**
     * Puts the resolved `values` into the rest of the template (`strings`) and
     * returns the final result that'll be returned from `render()`, `renderFn()`
     * and `renderFnAsync()` functions.
     */ private stringify ( values ) { const { strings } = this . tokens ; const { explicit } = this . options ; const { length } = values ; let ret = '' ; for ( let i = 0 ; i < length ; i ++ ) { ret += strings [ i ] ; const value = values [ i ] ; if ( explicit || ( value !== null && value !== undefined ) ) { ret += value ; } } ret += strings [ length ] ; return ret ; } }