string/template.ts

/** @module String */

import { isFunc } from '@method/isFunc'
import { isColl } from '@collection/isColl'
import { get } from '@collection/get'
import { isStr } from './isStr'

type TTempFun = (<T extends string = string>(
  tempStr: string,
  data: Record<any, any> | any[],
  fallback: any
) => T) & {
  regex?: RegExp
}

/**
 * Helper to wrap the template method, and allow passing a custom regex argument
 * Custom regex is used instead the default regex of the template method
 * @function
 * @example
 * template('${{ who }} in ${{ where }}!', { who: 'goats', where: 'boats' })
 * // Returns "goats in boats"
 * @param {String} template - String with ES6 syntax items to be replaced
 * @param {Object|Array} data - Data used to replace the ES6 placeholders
 * @param {any} fallback - Used it data does not contain key to be replaced
 * @param {Object} [RegEx?] - Regular Express to replace the default
 *
 * @returns {String} - template with placeholder values filled
 */
export const templateRx = <T extends string = string>(
  tempStr: string,
  data: Record<any, any> | any[],
  fallback: any = ``,
  rx?: RegExp
): T => {
  const orgRx = template.regex
  template.regex = rx || /{{([^}]*)}}/g
  const resp = template(tempStr, data, fallback)
  template.regex = orgRx

  return resp as T
}

/**
 * Simple template replace for ES6 template strings
 * @function
 * @example
 * template('${ who } in ${ where }!', { who: 'goats', where: 'boats' })
 * // Returns "goats in boats"
 * @param {String} template - String with ES6 syntax items to be replaced
 * @param {Object|Array} data - Data used to replace the ES6 placeholders
 * @param {any} fallback - Used it data does not contain key to be replaced
 *
 * @returns {String} - template with placeholder values filled
 */
export const template: TTempFun = <T extends string = string>(
  tempStr: string,
  data: Record<any, any> | any[],
  fallback: any = ``
): T => {
  data = (isColl(data) && data) || {}
  const regex = template.regex || /\${(.*?)\}/g

  return isStr(tempStr)
    ? (tempStr.replace(regex, (match, exact) => {
        const path = (exact || match.substring(2, match.length - 3)).trim()
        const replaceWith = get(data, path, fallback)
        return isFunc(replaceWith)
          ? replaceWith(data, path, fallback)
          : replaceWith
      }) as T)
    : (() => {
        console.error(`template requires a string as the first argument`)
        return tempStr as T
      })()
}