refactor(ssr): prefix ssr helpers

This commit is contained in:
Evan You 2020-02-06 12:07:25 -05:00
parent f3e70b3733
commit bc8f91d181
16 changed files with 256 additions and 136 deletions

View File

@ -292,7 +292,7 @@ export interface ArrayExpression extends Node {
export interface FunctionExpression extends Node { export interface FunctionExpression extends Node {
type: NodeTypes.JS_FUNCTION_EXPRESSION type: NodeTypes.JS_FUNCTION_EXPRESSION
params: ExpressionNode | ExpressionNode[] | undefined params: ExpressionNode | string | (ExpressionNode | string)[] | undefined
returns?: TemplateChildNode | TemplateChildNode[] | JSChildNode returns?: TemplateChildNode | TemplateChildNode[] | JSChildNode
body?: BlockStatement body?: BlockStatement
newline: boolean newline: boolean

View File

@ -41,7 +41,12 @@ export {
transformExpression, transformExpression,
processExpression processExpression
} from './transforms/transformExpression' } from './transforms/transformExpression'
export { trackVForSlotScopes, trackSlotScopes } from './transforms/vSlot' export {
buildSlots,
SlotFnBuilder,
trackVForSlotScopes,
trackSlotScopes
} from './transforms/vSlot'
export { resolveComponentType, buildProps } from './transforms/transformElement' export { resolveComponentType, buildProps } from './transforms/transformElement'
export { processSlotOutlet } from './transforms/transformSlotOutlet' export { processSlotOutlet } from './transforms/transformSlotOutlet'

View File

@ -93,11 +93,27 @@ export const trackVForSlotScopes: NodeTransform = (node, context) => {
} }
} }
export type SlotFnBuilder = (
slotProps: ExpressionNode | undefined,
slotChildren: TemplateChildNode[],
loc: SourceLocation
) => FunctionExpression
const buildClientSlotFn: SlotFnBuilder = (props, children, loc) =>
createFunctionExpression(
props,
children,
false /* newline */,
true /* isSlot */,
children.length ? children[0].loc : loc
)
// Instead of being a DirectiveTransform, v-slot processing is called during // Instead of being a DirectiveTransform, v-slot processing is called during
// transformElement to build the slots object for a component. // transformElement to build the slots object for a component.
export function buildSlots( export function buildSlots(
node: ElementNode, node: ElementNode,
context: TransformContext context: TransformContext,
buildSlotFn: SlotFnBuilder = buildClientSlotFn
): { ): {
slots: ObjectExpression | CallExpression slots: ObjectExpression | CallExpression
hasDynamicSlots: boolean hasDynamicSlots: boolean
@ -106,6 +122,11 @@ export function buildSlots(
const slotsProperties: Property[] = [] const slotsProperties: Property[] = []
const dynamicSlots: (ConditionalExpression | CallExpression)[] = [] const dynamicSlots: (ConditionalExpression | CallExpression)[] = []
const buildDefaultSlotProperty = (
props: ExpressionNode | undefined,
children: TemplateChildNode[]
) => createObjectProperty(`default`, buildSlotFn(props, children, loc))
// If the slot is inside a v-for or another v-slot, force it to be dynamic // If the slot is inside a v-for or another v-slot, force it to be dynamic
// since it likely uses a scope variable. // since it likely uses a scope variable.
let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0 let hasDynamicSlots = context.scopes.vSlot > 0 || context.scopes.vFor > 0
@ -125,7 +146,7 @@ export function buildSlots(
createCompilerError(ErrorCodes.X_V_SLOT_NAMED_SLOT_ON_COMPONENT, loc) createCompilerError(ErrorCodes.X_V_SLOT_NAMED_SLOT_ON_COMPONENT, loc)
) )
} }
slotsProperties.push(buildDefaultSlot(exp, children, loc)) slotsProperties.push(buildDefaultSlotProperty(exp, children))
} }
// 2. Iterate through children and check for template slots // 2. Iterate through children and check for template slots
@ -174,14 +195,7 @@ export function buildSlots(
hasDynamicSlots = true hasDynamicSlots = true
} }
const slotFunction = createFunctionExpression( const slotFunction = buildSlotFn(slotProps, slotChildren, slotLoc)
slotProps,
slotChildren,
false /* newline */,
true /* isSlot */,
slotChildren.length ? slotChildren[0].loc : slotLoc
)
// check if this slot is conditional (v-if/v-for) // check if this slot is conditional (v-if/v-for)
let vIf: DirectiveNode | undefined let vIf: DirectiveNode | undefined
let vElse: DirectiveNode | undefined let vElse: DirectiveNode | undefined
@ -280,7 +294,7 @@ export function buildSlots(
if (!onComponentDefaultSlot) { if (!onComponentDefaultSlot) {
if (!hasTemplateSlots) { if (!hasTemplateSlots) {
// implicit default slot (on component) // implicit default slot (on component)
slotsProperties.push(buildDefaultSlot(undefined, children, loc)) slotsProperties.push(buildDefaultSlotProperty(undefined, children))
} else if (implicitDefaultChildren.length) { } else if (implicitDefaultChildren.length) {
// implicit default slot (mixed with named slots) // implicit default slot (mixed with named slots)
if (hasNamedDefaultSlot) { if (hasNamedDefaultSlot) {
@ -292,7 +306,7 @@ export function buildSlots(
) )
} else { } else {
slotsProperties.push( slotsProperties.push(
buildDefaultSlot(undefined, implicitDefaultChildren, loc) buildDefaultSlotProperty(undefined, implicitDefaultChildren)
) )
} }
} }
@ -317,23 +331,6 @@ export function buildSlots(
} }
} }
function buildDefaultSlot(
slotProps: ExpressionNode | undefined,
children: TemplateChildNode[],
loc: SourceLocation
): Property {
return createObjectProperty(
`default`,
createFunctionExpression(
slotProps,
children,
false /* newline */,
true /* isSlot */,
children.length ? children[0].loc : loc
)
)
}
function buildDynamicSlot( function buildDynamicSlot(
name: ExpressionNode, name: ExpressionNode,
fn: FunctionExpression fn: FunctionExpression

View File

@ -1,33 +1,33 @@
import { registerRuntimeHelpers } from '@vue/compiler-dom' import { registerRuntimeHelpers } from '@vue/compiler-dom'
export const SSR_INTERPOLATE = Symbol(`interpolate`) export const SSR_INTERPOLATE = Symbol(`ssrInterpolate`)
export const SSR_RENDER_COMPONENT = Symbol(`renderComponent`) export const SSR_RENDER_COMPONENT = Symbol(`ssrRenderComponent`)
export const SSR_RENDER_SLOT = Symbol(`renderSlot`) export const SSR_RENDER_SLOT = Symbol(`ssrRenderSlot`)
export const SSR_RENDER_CLASS = Symbol(`renderClass`) export const SSR_RENDER_CLASS = Symbol(`ssrRenderClass`)
export const SSR_RENDER_STYLE = Symbol(`renderStyle`) export const SSR_RENDER_STYLE = Symbol(`ssrRenderStyle`)
export const SSR_RENDER_ATTRS = Symbol(`renderAttrs`) export const SSR_RENDER_ATTRS = Symbol(`ssrRenderAttrs`)
export const SSR_RENDER_ATTR = Symbol(`renderAttr`) export const SSR_RENDER_ATTR = Symbol(`ssrRenderAttr`)
export const SSR_RENDER_DYNAMIC_ATTR = Symbol(`renderDynamicAttr`) export const SSR_RENDER_DYNAMIC_ATTR = Symbol(`ssrRenderDynamicAttr`)
export const SSR_RENDER_LIST = Symbol(`renderList`) export const SSR_RENDER_LIST = Symbol(`ssrRenderList`)
export const SSR_LOOSE_EQUAL = Symbol(`looseEqual`) export const SSR_LOOSE_EQUAL = Symbol(`ssrLooseEqual`)
export const SSR_LOOSE_CONTAIN = Symbol(`looseContain`) export const SSR_LOOSE_CONTAIN = Symbol(`ssrLooseContain`)
export const SSR_RENDER_DYNAMIC_MODEL = Symbol(`renderDynamicModel`) export const SSR_RENDER_DYNAMIC_MODEL = Symbol(`ssrRenderDynamicModel`)
export const SSR_GET_DYNAMIC_MODEL_PROPS = Symbol(`getDynamicModelProps`) export const SSR_GET_DYNAMIC_MODEL_PROPS = Symbol(`ssrGetDynamicModelProps`)
export const ssrHelpers = { export const ssrHelpers = {
[SSR_INTERPOLATE]: `_interpolate`, [SSR_INTERPOLATE]: `_ssrInterpolate`,
[SSR_RENDER_COMPONENT]: `_renderComponent`, [SSR_RENDER_COMPONENT]: `_ssrRenderComponent`,
[SSR_RENDER_SLOT]: `_renderSlot`, [SSR_RENDER_SLOT]: `_ssrRenderSlot`,
[SSR_RENDER_CLASS]: `_renderClass`, [SSR_RENDER_CLASS]: `_ssrRenderClass`,
[SSR_RENDER_STYLE]: `_renderStyle`, [SSR_RENDER_STYLE]: `_ssrRenderStyle`,
[SSR_RENDER_ATTRS]: `_renderAttrs`, [SSR_RENDER_ATTRS]: `_ssrRenderAttrs`,
[SSR_RENDER_ATTR]: `_renderAttr`, [SSR_RENDER_ATTR]: `_ssrRenderAttr`,
[SSR_RENDER_DYNAMIC_ATTR]: `_renderDynamicAttr`, [SSR_RENDER_DYNAMIC_ATTR]: `_ssrRenderDynamicAttr`,
[SSR_RENDER_LIST]: `_renderList`, [SSR_RENDER_LIST]: `_ssrRenderList`,
[SSR_LOOSE_EQUAL]: `_looseEqual`, [SSR_LOOSE_EQUAL]: `_ssrLooseEqual`,
[SSR_LOOSE_CONTAIN]: `_looseContain`, [SSR_LOOSE_CONTAIN]: `_ssrLooseContain`,
[SSR_RENDER_DYNAMIC_MODEL]: `_renderDynamicModel`, [SSR_RENDER_DYNAMIC_MODEL]: `_ssrRenderDynamicModel`,
[SSR_GET_DYNAMIC_MODEL_PROPS]: `_getDynamicModelProps` [SSR_GET_DYNAMIC_MODEL_PROPS]: `_ssrGetDynamicModelProps`
} }
// Note: these are helpers imported from @vue/server-renderer // Note: these are helpers imported from @vue/server-renderer

View File

@ -1,16 +1,16 @@
import { interpolate } from '../src/helpers/interpolate' import { ssrInterpolate } from '../src/helpers/ssrInterpolate'
import { escapeHtml } from '@vue/shared' import { escapeHtml } from '@vue/shared'
test('ssr: interpolate', () => { test('ssr: interpolate', () => {
expect(interpolate(0)).toBe(`0`) expect(ssrInterpolate(0)).toBe(`0`)
expect(interpolate(`foo`)).toBe(`foo`) expect(ssrInterpolate(`foo`)).toBe(`foo`)
expect(interpolate(`<div>`)).toBe(`&lt;div&gt;`) expect(ssrInterpolate(`<div>`)).toBe(`&lt;div&gt;`)
// should escape interpolated values // should escape interpolated values
expect(interpolate([1, 2, 3])).toBe( expect(ssrInterpolate([1, 2, 3])).toBe(
escapeHtml(JSON.stringify([1, 2, 3], null, 2)) escapeHtml(JSON.stringify([1, 2, 3], null, 2))
) )
expect( expect(
interpolate({ ssrInterpolate({
foo: 1, foo: 1,
bar: `<div>` bar: `<div>`
}) })

View File

@ -1,15 +1,15 @@
import { import {
renderAttrs, ssrRenderAttrs,
renderClass, ssrRenderClass,
renderStyle, ssrRenderStyle,
renderAttr ssrRenderAttr
} from '../src/helpers/renderAttrs' } from '../src/helpers/ssrRenderAttrs'
import { escapeHtml } from '@vue/shared' import { escapeHtml } from '@vue/shared'
describe('ssr: renderAttrs', () => { describe('ssr: renderAttrs', () => {
test('ignore reserved props', () => { test('ignore reserved props', () => {
expect( expect(
renderAttrs({ ssrRenderAttrs({
key: 1, key: 1,
ref: () => {}, ref: () => {},
onClick: () => {} onClick: () => {}
@ -19,7 +19,7 @@ describe('ssr: renderAttrs', () => {
test('normal attrs', () => { test('normal attrs', () => {
expect( expect(
renderAttrs({ ssrRenderAttrs({
id: 'foo', id: 'foo',
title: 'bar' title: 'bar'
}) })
@ -28,7 +28,7 @@ describe('ssr: renderAttrs', () => {
test('escape attrs', () => { test('escape attrs', () => {
expect( expect(
renderAttrs({ ssrRenderAttrs({
id: '"><script' id: '"><script'
}) })
).toBe(` id="&quot;&gt;&lt;script"`) ).toBe(` id="&quot;&gt;&lt;script"`)
@ -36,7 +36,7 @@ describe('ssr: renderAttrs', () => {
test('boolean attrs', () => { test('boolean attrs', () => {
expect( expect(
renderAttrs({ ssrRenderAttrs({
checked: true, checked: true,
multiple: false multiple: false
}) })
@ -45,7 +45,7 @@ describe('ssr: renderAttrs', () => {
test('ignore falsy values', () => { test('ignore falsy values', () => {
expect( expect(
renderAttrs({ ssrRenderAttrs({
foo: false, foo: false,
title: null, title: null,
baz: undefined baz: undefined
@ -55,7 +55,7 @@ describe('ssr: renderAttrs', () => {
test('ingore non-renderable values', () => { test('ingore non-renderable values', () => {
expect( expect(
renderAttrs({ ssrRenderAttrs({
foo: {}, foo: {},
bar: [], bar: [],
baz: () => {} baz: () => {}
@ -65,7 +65,7 @@ describe('ssr: renderAttrs', () => {
test('props to attrs', () => { test('props to attrs', () => {
expect( expect(
renderAttrs({ ssrRenderAttrs({
readOnly: true, // simple lower case conversion readOnly: true, // simple lower case conversion
htmlFor: 'foobar' // special cases htmlFor: 'foobar' // special cases
}) })
@ -74,7 +74,7 @@ describe('ssr: renderAttrs', () => {
test('preserve name on custom element', () => { test('preserve name on custom element', () => {
expect( expect(
renderAttrs( ssrRenderAttrs(
{ {
fooBar: 'ok' fooBar: 'ok'
}, },
@ -86,16 +86,16 @@ describe('ssr: renderAttrs', () => {
describe('ssr: renderAttr', () => { describe('ssr: renderAttr', () => {
test('basic', () => { test('basic', () => {
expect(renderAttr('foo', 'bar')).toBe(` foo="bar"`) expect(ssrRenderAttr('foo', 'bar')).toBe(` foo="bar"`)
}) })
test('null and undefined', () => { test('null and undefined', () => {
expect(renderAttr('foo', null)).toBe(``) expect(ssrRenderAttr('foo', null)).toBe(``)
expect(renderAttr('foo', undefined)).toBe(``) expect(ssrRenderAttr('foo', undefined)).toBe(``)
}) })
test('escape', () => { test('escape', () => {
expect(renderAttr('foo', '<script>')).toBe( expect(ssrRenderAttr('foo', '<script>')).toBe(
` foo="${escapeHtml(`<script>`)}"` ` foo="${escapeHtml(`<script>`)}"`
) )
}) })
@ -104,28 +104,28 @@ describe('ssr: renderAttr', () => {
describe('ssr: renderClass', () => { describe('ssr: renderClass', () => {
test('via renderProps', () => { test('via renderProps', () => {
expect( expect(
renderAttrs({ ssrRenderAttrs({
class: ['foo', 'bar'] class: ['foo', 'bar']
}) })
).toBe(` class="foo bar"`) ).toBe(` class="foo bar"`)
}) })
test('standalone', () => { test('standalone', () => {
expect(renderClass(`foo`)).toBe(`foo`) expect(ssrRenderClass(`foo`)).toBe(`foo`)
expect(renderClass([`foo`, `bar`])).toBe(`foo bar`) expect(ssrRenderClass([`foo`, `bar`])).toBe(`foo bar`)
expect(renderClass({ foo: true, bar: false })).toBe(`foo`) expect(ssrRenderClass({ foo: true, bar: false })).toBe(`foo`)
expect(renderClass([{ foo: true, bar: false }, `baz`])).toBe(`foo baz`) expect(ssrRenderClass([{ foo: true, bar: false }, `baz`])).toBe(`foo baz`)
}) })
test('escape class values', () => { test('escape class values', () => {
expect(renderClass(`"><script`)).toBe(`&quot;&gt;&lt;script`) expect(ssrRenderClass(`"><script`)).toBe(`&quot;&gt;&lt;script`)
}) })
}) })
describe('ssr: renderStyle', () => { describe('ssr: renderStyle', () => {
test('via renderProps', () => { test('via renderProps', () => {
expect( expect(
renderAttrs({ ssrRenderAttrs({
style: { style: {
color: 'red' color: 'red'
} }
@ -134,14 +134,14 @@ describe('ssr: renderStyle', () => {
}) })
test('standalone', () => { test('standalone', () => {
expect(renderStyle(`color:red`)).toBe(`color:red`) expect(ssrRenderStyle(`color:red`)).toBe(`color:red`)
expect( expect(
renderStyle({ ssrRenderStyle({
color: `red` color: `red`
}) })
).toBe(`color:red;`) ).toBe(`color:red;`)
expect( expect(
renderStyle([ ssrRenderStyle([
{ color: `red` }, { color: `red` },
{ fontSize: `15px` } // case conversion { fontSize: `15px` } // case conversion
]) ])
@ -150,7 +150,7 @@ describe('ssr: renderStyle', () => {
test('number handling', () => { test('number handling', () => {
expect( expect(
renderStyle({ ssrRenderStyle({
fontSize: 15, // should be ignored since font-size requires unit fontSize: 15, // should be ignored since font-size requires unit
opacity: 0.5 opacity: 0.5
}) })
@ -158,9 +158,9 @@ describe('ssr: renderStyle', () => {
}) })
test('escape inline CSS', () => { test('escape inline CSS', () => {
expect(renderStyle(`"><script`)).toBe(`&quot;&gt;&lt;script`) expect(ssrRenderStyle(`"><script`)).toBe(`&quot;&gt;&lt;script`)
expect( expect(
renderStyle({ ssrRenderStyle({
color: `"><script` color: `"><script`
}) })
).toBe(`color:&quot;&gt;&lt;script;`) ).toBe(`color:&quot;&gt;&lt;script;`)

View File

@ -8,7 +8,7 @@ import {
} from 'vue' } from 'vue'
import { escapeHtml } from '@vue/shared' import { escapeHtml } from '@vue/shared'
import { renderToString, renderComponent } from '../src/renderToString' import { renderToString, renderComponent } from '../src/renderToString'
import { renderSlot } from '../src/helpers/renderSlot' import { ssrRenderSlot } from '../src/helpers/ssrRenderSlot'
describe('ssr: renderToString', () => { describe('ssr: renderToString', () => {
test('should apply app context', async () => { test('should apply app context', async () => {
@ -132,7 +132,7 @@ describe('ssr: renderToString', () => {
props: ['msg'], props: ['msg'],
ssrRender(ctx: any, push: any, parent: any) { ssrRender(ctx: any, push: any, parent: any) {
push(`<div class="child">`) push(`<div class="child">`)
renderSlot( ssrRenderSlot(
ctx.$slots, ctx.$slots,
'default', 'default',
{ msg: 'from slot' }, { msg: 'from slot' },
@ -195,7 +195,7 @@ describe('ssr: renderToString', () => {
props: ['msg'], props: ['msg'],
ssrRender(ctx: any, push: any, parent: any) { ssrRender(ctx: any, push: any, parent: any) {
push(`<div class="child">`) push(`<div class="child">`)
renderSlot( ssrRenderSlot(
ctx.$slots, ctx.$slots,
'default', 'default',
{ msg: 'from slot' }, { msg: 'from slot' },

View File

@ -0,0 +1,115 @@
import {
ssrRenderDynamicModel,
ssrGetDynamicModelProps
// ssrGetDynamicModelProps
} from '../src/helpers/ssrVModelHelpers'
describe('ssr: v-model helpers', () => {
test('ssrRenderDynamicModel', () => {
expect(ssrRenderDynamicModel(null, 'foo', null)).toBe(` value="foo"`)
expect(ssrRenderDynamicModel('text', 'foo', null)).toBe(` value="foo"`)
expect(ssrRenderDynamicModel('email', 'foo', null)).toBe(` value="foo"`)
expect(ssrRenderDynamicModel('checkbox', true, null)).toBe(` checked`)
expect(ssrRenderDynamicModel('checkbox', false, null)).toBe(``)
expect(ssrRenderDynamicModel('checkbox', [1], '1')).toBe(` checked`)
expect(ssrRenderDynamicModel('checkbox', [1], 1)).toBe(` checked`)
expect(ssrRenderDynamicModel('checkbox', [1], 0)).toBe(``)
expect(ssrRenderDynamicModel('radio', 'foo', 'foo')).toBe(` checked`)
expect(ssrRenderDynamicModel('radio', 1, '1')).toBe(` checked`)
expect(ssrRenderDynamicModel('radio', 1, 0)).toBe(``)
})
test('ssrGetDynamicModelProps', () => {
expect(ssrGetDynamicModelProps({}, 'foo')).toMatchObject({ value: 'foo' })
expect(
ssrGetDynamicModelProps(
{
type: 'text'
},
'foo'
)
).toMatchObject({ value: 'foo' })
expect(
ssrGetDynamicModelProps(
{
type: 'email'
},
'foo'
)
).toMatchObject({ value: 'foo' })
expect(
ssrGetDynamicModelProps(
{
type: 'checkbox'
},
true
)
).toMatchObject({ checked: true })
expect(
ssrGetDynamicModelProps(
{
type: 'checkbox'
},
false
)
).toBe(null)
expect(
ssrGetDynamicModelProps(
{
type: 'checkbox',
value: '1'
},
[1]
)
).toMatchObject({ checked: true })
expect(
ssrGetDynamicModelProps(
{
type: 'checkbox',
value: 1
},
[1]
)
).toMatchObject({ checked: true })
expect(
ssrGetDynamicModelProps(
{
type: 'checkbox',
value: 0
},
[1]
)
).toBe(null)
expect(
ssrGetDynamicModelProps(
{
type: 'radio',
value: 'foo'
},
'foo'
)
).toMatchObject({ checked: true })
expect(
ssrGetDynamicModelProps(
{
type: 'radio',
value: '1'
},
1
)
).toMatchObject({ checked: true })
expect(
ssrGetDynamicModelProps(
{
type: 'radio',
value: 0
},
1
)
).toBe(null)
})
})

View File

@ -1,5 +1,5 @@
import { escapeHtml, toDisplayString } from '@vue/shared' import { escapeHtml, toDisplayString } from '@vue/shared'
export function interpolate(value: unknown): string { export function ssrInterpolate(value: unknown): string {
return escapeHtml(toDisplayString(value)) return escapeHtml(toDisplayString(value))
} }

View File

@ -14,7 +14,7 @@ import {
const shouldIgnoreProp = makeMap(`key,ref,innerHTML,textContent`) const shouldIgnoreProp = makeMap(`key,ref,innerHTML,textContent`)
export function renderAttrs( export function ssrRenderAttrs(
props: Record<string, unknown>, props: Record<string, unknown>,
tag?: string tag?: string
): string { ): string {
@ -29,18 +29,18 @@ export function renderAttrs(
} }
const value = props[key] const value = props[key]
if (key === 'class') { if (key === 'class') {
ret += ` class="${renderClass(value)}"` ret += ` class="${ssrRenderClass(value)}"`
} else if (key === 'style') { } else if (key === 'style') {
ret += ` style="${renderStyle(value)}"` ret += ` style="${ssrRenderStyle(value)}"`
} else { } else {
ret += renderDynamicAttr(key, value, tag) ret += ssrRenderDynamicAttr(key, value, tag)
} }
} }
return ret return ret
} }
// render an attr with dynamic (unknown) key. // render an attr with dynamic (unknown) key.
export function renderDynamicAttr( export function ssrRenderDynamicAttr(
key: string, key: string,
value: unknown, value: unknown,
tag?: string tag?: string
@ -63,7 +63,7 @@ export function renderDynamicAttr(
// Render a v-bind attr with static key. The key is pre-processed at compile // 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. // time and we only need to check and escape value.
export function renderAttr(key: string, value: unknown): string { export function ssrRenderAttr(key: string, value: unknown): string {
if (!isRenderableValue(value)) { if (!isRenderableValue(value)) {
return `` return ``
} }
@ -78,11 +78,11 @@ function isRenderableValue(value: unknown): boolean {
return type === 'string' || type === 'number' || type === 'boolean' return type === 'string' || type === 'number' || type === 'boolean'
} }
export function renderClass(raw: unknown): string { export function ssrRenderClass(raw: unknown): string {
return escapeHtml(normalizeClass(raw)) return escapeHtml(normalizeClass(raw))
} }
export function renderStyle(raw: unknown): string { export function ssrRenderStyle(raw: unknown): string {
if (!raw) { if (!raw) {
return '' return ''
} }

View File

@ -1,6 +1,6 @@
import { isArray, isString, isObject } from '@vue/shared' import { isArray, isString, isObject } from '@vue/shared'
export function renderList( export function ssrRenderList(
source: unknown, source: unknown,
renderItem: (value: unknown, key: string | number, index?: number) => void renderItem: (value: unknown, key: string | number, index?: number) => void
) { ) {

View File

@ -9,7 +9,7 @@ export type SSRSlot = (
parentComponent: ComponentInternalInstance | null parentComponent: ComponentInternalInstance | null
) => void ) => void
export function renderSlot( export function ssrRenderSlot(
slots: Slots | SSRSlots, slots: Slots | SSRSlots,
slotName: string, slotName: string,
slotProps: Props, slotProps: Props,

View File

@ -1,42 +1,45 @@
import { looseEqual as _looseEqual, looseIndexOf } from '@vue/shared' import { looseEqual, looseIndexOf } from '@vue/shared'
import { renderAttr } from './renderAttrs' import { ssrRenderAttr } from './ssrRenderAttrs'
export const looseEqual = _looseEqual as (a: unknown, b: unknown) => boolean export const ssrLooseEqual = looseEqual as (a: unknown, b: unknown) => boolean
export function looseContain(arr: unknown[], value: unknown): boolean { export function ssrLooseContain(arr: unknown[], value: unknown): boolean {
return looseIndexOf(arr, value) > -1 return looseIndexOf(arr, value) > -1
} }
// for <input :type="type" v-model="model" value="value"> // for <input :type="type" v-model="model" value="value">
export function renderDynamicModel( export function ssrRenderDynamicModel(
type: unknown, type: unknown,
model: unknown, model: unknown,
value: unknown value: unknown
) { ) {
switch (type) { switch (type) {
case 'radio': case 'radio':
return _looseEqual(model, value) ? ' checked' : '' return looseEqual(model, value) ? ' checked' : ''
case 'checkbox': case 'checkbox':
return (Array.isArray(model) return (Array.isArray(model)
? looseContain(model, value) ? ssrLooseContain(model, value)
: model) : model)
? ' checked' ? ' checked'
: '' : ''
default: default:
// text types // text types
return renderAttr('value', model) return ssrRenderAttr('value', model)
} }
} }
// for <input v-bind="obj" v-model="model"> // for <input v-bind="obj" v-model="model">
export function getDynamicModelProps(existingProps: any = {}, model: unknown) { export function ssrGetDynamicModelProps(
existingProps: any = {},
model: unknown
) {
const { type, value } = existingProps const { type, value } = existingProps
switch (type) { switch (type) {
case 'radio': case 'radio':
return _looseEqual(model, value) ? { checked: true } : null return looseEqual(model, value) ? { checked: true } : null
case 'checkbox': case 'checkbox':
return (Array.isArray(model) return (Array.isArray(model)
? looseContain(model, value) ? ssrLooseContain(model, value)
: model) : model)
? { checked: true } ? { checked: true }
: null : null

View File

@ -2,22 +2,22 @@
export { renderToString } from './renderToString' export { renderToString } from './renderToString'
// internal runtime helpers // internal runtime helpers
export { renderComponent as _renderComponent } from './renderToString' export { renderComponent as _ssrRenderComponent } from './renderToString'
export { renderSlot as _renderSlot } from './helpers/renderSlot' export { ssrRenderSlot as _ssrRenderSlot } from './helpers/ssrRenderSlot'
export { export {
renderClass as _renderClass, ssrRenderClass as _ssrRenderClass,
renderStyle as _renderStyle, ssrRenderStyle as _ssrRenderStyle,
renderAttrs as _renderAttrs, ssrRenderAttrs as _ssrRenderAttrs,
renderAttr as _renderAttr, ssrRenderAttr as _ssrRenderAttr,
renderDynamicAttr as _renderDynamicAttr ssrRenderDynamicAttr as _ssrRenderDynamicAttr
} from './helpers/renderAttrs' } from './helpers/ssrRenderAttrs'
export { interpolate as _interpolate } from './helpers/interpolate' export { ssrInterpolate as _ssrInterpolate } from './helpers/ssrInterpolate'
export { renderList as _renderList } from './helpers/renderList' export { ssrRenderList as _ssrRenderList } from './helpers/ssrRenderList'
// v-model helpers // v-model helpers
export { export {
looseEqual as _looseEqual, ssrLooseEqual as _ssrLooseEqual,
looseContain as _looseContain, ssrLooseContain as _ssrLooseContain,
renderDynamicModel as _renderDynamicModel, ssrRenderDynamicModel as _ssrRenderDynamicModel,
getDynamicModelProps as _getDynamicModelProps ssrGetDynamicModelProps as _ssrGetDynamicModelProps
} from './helpers/vModelHelpers' } from './helpers/ssrVModelHelpers'

View File

@ -21,8 +21,8 @@ import {
isVoidTag, isVoidTag,
escapeHtml escapeHtml
} from '@vue/shared' } from '@vue/shared'
import { renderAttrs } from './helpers/renderAttrs' import { ssrRenderAttrs } from './helpers/ssrRenderAttrs'
import { SSRSlots } from './helpers/renderSlot' import { SSRSlots } from './helpers/ssrRenderSlot'
const { const {
isVNode, isVNode,
@ -217,7 +217,7 @@ function renderElement(
// TODO directives // TODO directives
if (props !== null) { if (props !== null) {
openTag += renderAttrs(props, tag) openTag += ssrRenderAttrs(props, tag)
} }
if (scopeId !== null) { if (scopeId !== null) {