export const SSR_RENDER_SLOT = Symbol(`renderSlot`)
export const SSR_RENDER_CLASS = Symbol(`renderClass`)
export const SSR_RENDER_STYLE = Symbol(`renderStyle`)
-export const SSR_RENDER_PROPS = Symbol(`renderProps`)
+export const SSR_RENDER_ATTRS = Symbol(`renderAttrs`)
+export const SSR_RENDER_ATTR = Symbol(`renderAttr`)
export const SSR_RENDER_LIST = Symbol(`renderList`)
// Note: these are helpers imported from @vue/server-renderer
[SSR_RENDER_SLOT]: `_renderSlot`,
[SSR_RENDER_CLASS]: `_renderClass`,
[SSR_RENDER_STYLE]: `_renderStyle`,
- [SSR_RENDER_PROPS]: `_renderProps`,
+ [SSR_RENDER_ATTRS]: `renderAttrs`,
+ [SSR_RENDER_ATTR]: `renderAttr`,
[SSR_RENDER_LIST]: `_renderList`
})
-import { _interpolate } from '../src'
+import { interpolate } from '../src/helpers/interpolate'
import { escapeHtml } from '@vue/shared'
test('ssr: interpolate', () => {
- expect(_interpolate(0)).toBe(`0`)
- expect(_interpolate(`foo`)).toBe(`foo`)
- expect(_interpolate(`<div>`)).toBe(`<div>`)
+ expect(interpolate(0)).toBe(`0`)
+ expect(interpolate(`foo`)).toBe(`foo`)
+ expect(interpolate(`<div>`)).toBe(`<div>`)
// should escape interpolated values
- expect(_interpolate([1, 2, 3])).toBe(
+ expect(interpolate([1, 2, 3])).toBe(
escapeHtml(JSON.stringify([1, 2, 3], null, 2))
)
expect(
- _interpolate({
+ interpolate({
foo: 1,
bar: `<div>`
})
--- /dev/null
+test('ssr: renderList', () => {
+ // TODO
+})
-import { renderProps, renderClass, renderStyle } from '../src/renderProps'
+import {
+ renderAttrs,
+ renderClass,
+ renderStyle
+} from '../src/helpers/renderAttrs'
describe('ssr: renderProps', () => {
test('ignore reserved props', () => {
expect(
- renderProps({
+ renderAttrs({
key: 1,
ref: () => {},
onClick: () => {}
test('normal attrs', () => {
expect(
- renderProps({
+ renderAttrs({
id: 'foo',
title: 'bar'
})
test('escape attrs', () => {
expect(
- renderProps({
+ renderAttrs({
id: '"><script'
})
).toBe(` id=""><script"`)
test('boolean attrs', () => {
expect(
- renderProps({
+ renderAttrs({
checked: true,
multiple: false
})
test('ignore falsy values', () => {
expect(
- renderProps({
+ renderAttrs({
foo: false,
title: null,
baz: undefined
test('props to attrs', () => {
expect(
- renderProps({
+ renderAttrs({
readOnly: true, // simple lower case conversion
htmlFor: 'foobar' // special cases
})
test('preserve name on custom element', () => {
expect(
- renderProps(
+ renderAttrs(
{
fooBar: 'ok'
},
describe('ssr: renderClass', () => {
test('via renderProps', () => {
expect(
- renderProps({
+ renderAttrs({
class: ['foo', 'bar']
})
).toBe(` class="foo bar"`)
describe('ssr: renderStyle', () => {
test('via renderProps', () => {
expect(
- renderProps({
+ renderAttrs({
style: {
color: 'red'
}
--- /dev/null
+import { escapeHtml, toDisplayString } from '@vue/shared'
+
+export function interpolate(value: unknown): string {
+ return escapeHtml(toDisplayString(value))
+}
const shouldIgnoreProp = makeMap(`key,ref,innerHTML,textContent`)
-export function renderProps(
+export function renderAttrs(
props: Record<string, unknown>,
tag?: string
): string {
ret += ` class="${renderClass(value)}"`
} else if (key === 'style') {
ret += ` style="${renderStyle(value)}"`
- } else if (value != null) {
- const attrKey =
- tag && tag.indexOf('-') > 0
- ? key // preserve raw name on custom elements
- : propsToAttrMap[key] || key.toLowerCase()
- if (isBooleanAttr(attrKey)) {
- if (value !== false) {
- ret += ` ${attrKey}`
- }
- } else if (isSSRSafeAttrName(attrKey)) {
- ret += ` ${attrKey}="${escapeHtml(value)}"`
- }
+ } else {
+ ret += renderAttr(key, value, tag)
}
}
return ret
}
+export function renderAttr(key: string, value: unknown, tag?: string): string {
+ if (value == null) {
+ 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 {
+ return ``
+ }
+}
+
export function renderClass(raw: unknown): string {
return escapeHtml(normalizeClass(raw))
}
--- /dev/null
+import { isArray, isString, isObject } from '@vue/shared'
+
+export function renderList(
+ source: unknown,
+ renderItem: (value: unknown, key: string | number, index?: number) => void
+) {
+ if (isArray(source) || isString(source)) {
+ for (let i = 0, l = source.length; i < l; i++) {
+ renderItem(source[i], i)
+ }
+ } else if (typeof source === 'number') {
+ for (let i = 0; i < source; i++) {
+ renderItem(i + 1, i)
+ }
+ } else if (isObject(source)) {
+ if (source[Symbol.iterator as any]) {
+ const arr = Array.from(source as Iterable<any>)
+ for (let i = 0, l = arr.length; i < l; i++) {
+ renderItem(arr[i], i)
+ }
+ } else {
+ const keys = Object.keys(source)
+ for (let i = 0, l = keys.length; i < l; i++) {
+ const key = keys[i]
+ renderItem(source[key], key, i)
+ }
+ }
+ }
+}
// public
export { renderToString } from './renderToString'
-// internal
+// internal runtime helpers
export {
renderComponent as _renderComponent,
renderSlot as _renderSlot
export {
renderClass as _renderClass,
renderStyle as _renderStyle,
- renderProps as _renderProps
-} from './renderProps'
-
-// utils
-import { escapeHtml, toDisplayString } from '@vue/shared'
-
-export function _interpolate(value: unknown): string {
- return escapeHtml(toDisplayString(value))
-}
+ renderAttrs as _renderAttrs
+} from './helpers/renderAttrs'
+export { interpolate as _interpolate } from './helpers/interpolate'
+export { renderList as _renderList } from './helpers/renderList'
isPromise,
isArray,
isFunction,
- isVoidTag
+ isVoidTag,
+ escapeHtml
} from '@vue/shared'
-import { renderProps } from './renderProps'
-import { escapeHtml } from '@vue/shared'
+import { renderAttrs } from './helpers/renderAttrs'
const {
isVNode,
// TODO directives
if (props !== null) {
- openTag += renderProps(props, tag)
+ openTag += renderAttrs(props, tag)
}
if (scopeId !== null) {