refactor(ssr): prefix ssr helpers
This commit is contained in:
parent
f3e70b3733
commit
bc8f91d181
@ -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
|
||||||
|
@ -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'
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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(`<div>`)
|
expect(ssrInterpolate(`<div>`)).toBe(`<div>`)
|
||||||
// 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>`
|
||||||
})
|
})
|
@ -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=""><script"`)
|
).toBe(` id=""><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(`"><script`)
|
expect(ssrRenderClass(`"><script`)).toBe(`"><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(`"><script`)
|
expect(ssrRenderStyle(`"><script`)).toBe(`"><script`)
|
||||||
expect(
|
expect(
|
||||||
renderStyle({
|
ssrRenderStyle({
|
||||||
color: `"><script`
|
color: `"><script`
|
||||||
})
|
})
|
||||||
).toBe(`color:"><script;`)
|
).toBe(`color:"><script;`)
|
@ -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' },
|
115
packages/server-renderer/__tests__/ssrVModelHelpers.spec.ts
Normal file
115
packages/server-renderer/__tests__/ssrVModelHelpers.spec.ts
Normal 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)
|
||||||
|
})
|
||||||
|
})
|
@ -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))
|
||||||
}
|
}
|
@ -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 ''
|
||||||
}
|
}
|
@ -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
|
||||||
) {
|
) {
|
@ -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,
|
@ -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
|
@ -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'
|
||||||
|
@ -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) {
|
||||||
|
Loading…
Reference in New Issue
Block a user