index.js
function memoizedTagFunction (computeStaticHelper, computeResultHelper) {
  const memoTable = new LruCache()

  /**
   * @param {!Array.<string>} staticStrings
   * @return {T}
   */
  function staticStateFor (staticStrings) {
    let staticState = null
    const canMemoize = Object.isFrozen(staticStrings) &&
          Object.isFrozen(staticStrings.raw)
    if (canMemoize) {
      staticState = memoTable.get(staticStrings)
    }
    let failure = null
    if (!staticState) {
      try {
        staticState = { pass: computeStaticHelper(staticStrings) }
      } catch (exc) {
        failure = exc
        staticState = { fail: exc.message || 'Failure' }
      }
      if (canMemoize) {
        memoTable.set(staticStrings, staticState)
      }
    }
    if (staticState.fail) {
      throw failure || new Error(staticState.fail)
    }
    return staticState.pass
  }

  /** @param {O} options */
  function usingOptions (options) {
    return (staticStringsOrOptions, ...dynamicValues) => {
      // If we've only been passed an options object,
      // return a template tag that uses it.
      if (dynamicValues.length === 0 &&
          !Array.isArray(staticStringsOrOptions)) {
        return usingOptions(staticStringsOrOptions)
      }

      const staticStrings = staticStringsOrOptions
      requireValidTagInputs(staticStrings, dynamicValues)

      return computeResultHelper(
        options, staticStateFor(staticStrings), staticStrings, dynamicValues)
    }
  }

  // eslint-disable-next-line no-inline-comments
  return usingOptions(/** @type {O} */ ({}))
}
function memoizedTagFunction (computeStaticHelper, computeResultHelper) {
  const memoTable = new LruCache()

  /**
   * @param {!Array.<string>} staticStrings
   * @return {T}
   */
  function staticStateFor (staticStrings) {
    let staticState = null
    const canMemoize = Object.isFrozen(staticStrings) &&
          Object.isFrozen(staticStrings.raw)
    if (canMemoize) {
      staticState = memoTable.get(staticStrings)
    }
    let failure = null
    if (!staticState) {
      try {
        staticState = { pass: computeStaticHelper(staticStrings) }
      } catch (exc) {
        failure = exc
        staticState = { fail: exc.message || 'Failure' }
      }
      if (canMemoize) {
        memoTable.set(staticStrings, staticState)
      }
    }
    if (staticState.fail) {
      throw failure || new Error(staticState.fail)
    }
    return staticState.pass
  }

  /** @param {O} options */
  function usingOptions (options) {
    return (staticStringsOrOptions, ...dynamicValues) => {
      // If we've only been passed an options object,
      // return a template tag that uses it.
      if (dynamicValues.length === 0 &&
          !Array.isArray(staticStringsOrOptions)) {
        return usingOptions(staticStringsOrOptions)
      }

      const staticStrings = staticStringsOrOptions
      requireValidTagInputs(staticStrings, dynamicValues)

      return computeResultHelper(
        options, staticStateFor(staticStrings), staticStrings, dynamicValues)
    }
  }

  // eslint-disable-next-line no-inline-comments
  return usingOptions(/** @type {O} */ ({}))
}
function memoizedTagFunction ( computeStaticHelper , computeResultHelper ) { const memoTable = new LruCache ( ) /**
   * @param {!Array.<string>} staticStrings
   * @return {T}
   */ function staticStateFor ( staticStrings ) { let staticState = null const canMemoize = Object . isFrozen ( staticStrings ) && Object . isFrozen ( staticStrings . raw ) if ( canMemoize ) { staticState = memoTable . get ( staticStrings ) } let failure = null if ( ! staticState ) { try { staticState = { pass : computeStaticHelper ( staticStrings ) } } catch ( exc ) { failure = exc staticState = { fail : exc . message || 'Failure' } } if ( canMemoize ) { memoTable . set ( staticStrings , staticState ) } } if ( staticState . fail ) { throw failure || new Error ( staticState . fail ) } return staticState . pass } /** @param {O} options */ function usingOptions ( options ) { return ( staticStringsOrOptions , ... dynamicValues ) => { // If we've only been passed an options object, // return a template tag that uses it. if ( dynamicValues . length === 0 && ! Array . isArray ( staticStringsOrOptions ) ) { return usingOptions ( staticStringsOrOptions ) } const staticStrings = staticStringsOrOptions requireValidTagInputs ( staticStrings , dynamicValues ) return computeResultHelper ( options , staticStateFor ( staticStrings ) , staticStrings , dynamicValues ) } } // eslint-disable-next-line no-inline-comments return usingOptions ( /** @type {O} */ ( { } ) ) }
Memoizes operations on the static portions so the per-use cost
of a tagged template literal is related to the complexity of handling
the dynamic values.

@template O
@template R
@template T
@param {!function (!Array.<string>): T} computeStaticHelper
called when there is no entry for the
frozen static strings object, and cached weakly thereafter.
Receives a string of arrays with a {@code .raw} property that is
a string array of the same length.
@param {!function (O, T, !Array.<string>, !Array.<*>): R} computeResultHelper
a function that takes three parameters:
@return {configurableTemplateTag<O, R>}
A function that either takes an options object (O), in which case it returns
another configurableTemplateTag; or it takes a TemplateObject and returns a result of
type R.
When used as a template tag it calls computeStaticHelper as needed
on the static portion and returns the result of applying
computeResultHelper.
Memoizes operations on the static portions so the per - use cost of a tagged template literal is related to the complexity of handling the dynamic values .