vue3-core/packages/server-renderer/src/helpers/ssrRenderAttrs.ts

110 lines
2.6 KiB
TypeScript
Raw Normal View History

import { escapeHtml } from '@vue/shared'
2020-01-29 07:48:27 +08:00
import {
normalizeClass,
normalizeStyle,
propsToAttrMap,
hyphenate,
isString,
isNoUnitNumericStyleProp,
isOn,
isSSRSafeAttrName,
isBooleanAttr,
makeMap
} from '@vue/shared'
2020-01-28 07:06:37 +08:00
const shouldIgnoreProp = makeMap(`key,ref,innerHTML,textContent`)
2020-02-07 01:07:25 +08:00
export function ssrRenderAttrs(
2020-01-29 07:48:27 +08:00
props: Record<string, unknown>,
tag?: string
2020-01-29 07:48:27 +08:00
): string {
let ret = ''
for (const key in props) {
if (
shouldIgnoreProp(key) ||
isOn(key) ||
(tag === 'textarea' && key === 'value')
) {
2020-01-29 07:48:27 +08:00
continue
}
const value = props[key]
if (key === 'class') {
2020-02-07 01:07:25 +08:00
ret += ` class="${ssrRenderClass(value)}"`
2020-01-29 07:48:27 +08:00
} else if (key === 'style') {
2020-02-07 01:07:25 +08:00
ret += ` style="${ssrRenderStyle(value)}"`
} else {
2020-02-07 01:07:25 +08:00
ret += ssrRenderDynamicAttr(key, value, tag)
2020-01-29 07:48:27 +08:00
}
}
return ret
}
2020-01-28 07:06:37 +08:00
// render an attr with dynamic (unknown) key.
2020-02-07 01:07:25 +08:00
export function ssrRenderDynamicAttr(
key: string,
value: unknown,
tag?: string
): string {
if (!isRenderableValue(value)) {
return ``
}
const attrKey =
tag && tag.indexOf('-') > 0
? key // preserve raw name on custom elements
: propsToAttrMap[key] || key.toLowerCase()
if (isBooleanAttr(attrKey)) {
return value === false ? `` : ` ${attrKey}`
} else if (isSSRSafeAttrName(attrKey)) {
return ` ${attrKey}="${escapeHtml(value)}"`
} else {
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.
2020-02-07 01:07:25 +08:00
export function ssrRenderAttr(key: string, value: unknown): string {
if (!isRenderableValue(value)) {
return ``
}
return ` ${key}="${escapeHtml(value)}"`
}
function isRenderableValue(value: unknown): boolean {
if (value == null) {
return false
}
const type = typeof value
return type === 'string' || type === 'number' || type === 'boolean'
}
2020-02-07 01:07:25 +08:00
export function ssrRenderClass(raw: unknown): string {
return escapeHtml(normalizeClass(raw))
2020-01-29 07:48:27 +08:00
}
2020-02-07 01:07:25 +08:00
export function ssrRenderStyle(raw: unknown): string {
2020-01-29 07:48:27 +08:00
if (!raw) {
return ''
}
if (isString(raw)) {
return escapeHtml(raw)
}
2020-01-29 07:48:27 +08:00
const styles = normalizeStyle(raw)
let ret = ''
for (const key in styles) {
const value = styles[key]
const normalizedKey = key.indexOf(`--`) === 0 ? key : hyphenate(key)
if (
isString(value) ||
(typeof value === 'number' && isNoUnitNumericStyleProp(normalizedKey))
) {
// only render valid values
ret += `${normalizedKey}:${value};`
}
}
return escapeHtml(ret)
2020-01-29 07:48:27 +08:00
}