All files / server-renderer/src/helpers ssrRenderAttrs.ts

92.68% Statements 38/41
95.45% Branches 42/44
100% Functions 6/6
92.5% Lines 37/40

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120                                            17x               202x 202x 174x             26x   148x   148x 148x 27x 121x 10x   111x     202x                 111x 6x     105x     111x 23x 82x 82x                       8x 2x   6x       38x       18x     18x 2x   16x 16x       16x 15x 15x   29x 6x   23x     15x   1x    
import {
  escapeHtml,
  isArray,
  isObject,
  isRenderableAttrValue,
  isSVGTag,
  stringifyStyle,
} from '@vue/shared'
import {
  includeBooleanAttr,
  isBooleanAttr,
  isOn,
  isSSRSafeAttrName,
  isString,
  makeMap,
  normalizeClass,
  normalizeCssVarValue,
  normalizeStyle,
  propsToAttrMap,
} from '@vue/shared'
 
// leading comma for empty string ""
const shouldIgnoreProp = /*@__PURE__*/ makeMap(
  `,key,ref,innerHTML,textContent,ref_key,ref_for`,
)
 
export function ssrRenderAttrs(
  props: Record<string, unknown>,
  tag?: string,
): string {
  let ret = ''
  for (let key in props) {
    if (
      shouldIgnoreProp(key) ||
      isOn(key) ||
      (tag === 'textarea' && key === 'value') ||
      // force as property (not rendered in SSR)
      key.startsWith('.')
    ) {
      continue
    }
    const value = props[key]
    // force as attribute
    if (key.startsWith('^')) key = key.slice(1)
    if (key === 'class' || key === 'className') {
      ret += ` class="${ssrRenderClass(value)}"`
    } else if (key === 'style') {
      ret += ` style="${ssrRenderStyle(value)}"`
    } else {
      ret += ssrRenderDynamicAttr(key, value, tag)
    }
  }
  return ret
}
 
// render an attr with dynamic (unknown) key.
export function ssrRenderDynamicAttr(
  key: string,
  value: unknown,
  tag?: string,
): string {
  if (!isRenderableAttrValue(value)) {
    return ``
  }
  const attrKey =
    tag && (tag.indexOf('-') > 0 || isSVGTag(tag))
      ? key // preserve raw name on custom elements and svg
      : propsToAttrMap[key] || key.toLowerCase()
  if (isBooleanAttr(attrKey)) {
    return includeBooleanAttr(value) ? ` ${attrKey}` : ``
  } else if (isSSRSafeAttrName(attrKey)) {
    return value === '' ? ` ${attrKey}` : ` ${attrKey}="${escapeHtml(value)}"`
  } else E{
    console.warn(
      `[@vue/server-renderer] Skipped rendering unsafe attribute name: ${attrKey}`,
    )
    return ``
  }
}
 
// Render a v-bind attr with static key. The key is pre-processed at compile
// time and we only need to check and escape value.
export function ssrRenderAttr(key: string, value: unknown): string {
  if (!isRenderableAttrValue(value)) {
    return ``
  }
  return ` ${key}="${escapeHtml(value)}"`
}
 
export function ssrRenderClass(raw: unknown): string {
  return escapeHtml(normalizeClass(raw))
}
 
export function ssrRenderStyle(raw: unknown): string {
  Iif (!raw) {
    return ''
  }
  if (isString(raw)) {
    return escapeHtml(raw)
  }
  const styles = normalizeStyle(ssrResetCssVars(raw))
  return escapeHtml(stringifyStyle(styles))
}
 
function ssrResetCssVars(raw: unknown) {
  if (!isArray(raw) && isObject(raw)) {
    const res: Record<string, unknown> = {}
    for (const key in raw) {
      // `:` prefixed keys are coming from `ssrCssVars`
      if (key.startsWith(':--')) {
        res[key.slice(1)] = normalizeCssVarValue(raw[key])
      } else {
        res[key] = raw[key]
      }
    }
    return res
  }
  return raw
}