if (!ssr) {
push(`function render(_ctx, _cache) {`)
} else {
- push(`function ssrRender(_ctx, _push, _parent) {`)
+ push(`function ssrRender(_ctx, _push, _parent, _attrs) {`)
}
indent()
describe('ssr: components', () => {
test('basic', () => {
expect(compile(`<foo id="a" :prop="b" />`).code).toMatchInlineSnapshot(`
- "const { resolveComponent: _resolveComponent } = require(\\"vue\\")
+ "const { resolveComponent: _resolveComponent, mergeProps: _mergeProps } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
- _push(_ssrRenderComponent(_component_foo, {
+ _push(_ssrRenderComponent(_component_foo, _mergeProps({
id: \\"a\\",
prop: _ctx.b
- }, null, _parent))
+ }, _attrs), null, _parent))
}"
`)
})
test('dynamic component', () => {
expect(compile(`<component is="foo" prop="b" />`).code)
.toMatchInlineSnapshot(`
- "const { resolveDynamicComponent: _resolveDynamicComponent } = require(\\"vue\\")
+ "const { resolveDynamicComponent: _resolveDynamicComponent, mergeProps: _mergeProps } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(_ssrRenderComponent(_resolveDynamicComponent(\\"foo\\"), { prop: \\"b\\" }, null, _parent))
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(_ssrRenderComponent(_resolveDynamicComponent(\\"foo\\"), _mergeProps({ prop: \\"b\\" }, _attrs), null, _parent))
}"
`)
expect(compile(`<component :is="foo" prop="b" />`).code)
.toMatchInlineSnapshot(`
- "const { resolveDynamicComponent: _resolveDynamicComponent } = require(\\"vue\\")
+ "const { resolveDynamicComponent: _resolveDynamicComponent, mergeProps: _mergeProps } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(_ssrRenderComponent(_resolveDynamicComponent(_ctx.foo), { prop: \\"b\\" }, null, _parent))
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(_ssrRenderComponent(_resolveDynamicComponent(_ctx.foo), _mergeProps({ prop: \\"b\\" }, _attrs), null, _parent))
}"
`)
})
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, createTextVNode: _createTextVNode } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
- _push(_ssrRenderComponent(_component_foo, null, {
+ _push(_ssrRenderComponent(_component_foo, _attrs, {
default: _withCtx((_, _push, _parent, _scopeId) => {
if (_push) {
_push(\`hello<div\${_scopeId}></div>\`)
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
- _push(_ssrRenderComponent(_component_foo, null, {
+ _push(_ssrRenderComponent(_component_foo, _attrs, {
default: _withCtx(({ msg }, _push, _parent, _scopeId) => {
if (_push) {
_push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`)
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
- _push(_ssrRenderComponent(_component_foo, null, {
+ _push(_ssrRenderComponent(_component_foo, _attrs, {
default: _withCtx((_, _push, _parent, _scopeId) => {
if (_push) {
_push(\`foo\`)
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode, createSlots: _createSlots } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
- _push(_ssrRenderComponent(_component_foo, null, _createSlots({ _: 1 }, [
+ _push(_ssrRenderComponent(_component_foo, _attrs, _createSlots({ _: 1 }, [
(_ctx.ok)
? {
name: \\"named\\",
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
- _push(_ssrRenderComponent(_component_foo, null, _createSlots({ _: 1 }, [
+ _push(_ssrRenderComponent(_component_foo, _attrs, _createSlots({ _: 1 }, [
_renderList(_ctx.names, (key) => {
return {
name: key,
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createVNode: _createVNode, createCommentVNode: _createCommentVNode } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
- _push(_ssrRenderComponent(_component_foo, null, {
+ _push(_ssrRenderComponent(_component_foo, _attrs, {
foo: _withCtx(({ list }, _push, _parent, _scopeId) => {
if (_push) {
if (_ctx.ok) {
expect(compile(`<transition><div/></transition>`).code)
.toMatchInlineSnapshot(`
"
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<div></div>\`)
}"
`)
expect(compile(`<transition-group><div/></transition-group>`).code)
.toMatchInlineSnapshot(`
"
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<!--[--><div></div><!--]-->\`)
}"
`)
"const { resolveComponent: _resolveComponent } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
_push(_ssrRenderComponent(_component_foo, null, null, _parent))
describe('children override', () => {
test('v-html', () => {
- expect(getCompiledString(`<div v-html="foo"/>`)).toMatchInlineSnapshot(
- `"\`<div>\${_ctx.foo}</div>\`"`
- )
+ expect(getCompiledString(`<div v-html="foo"/>`)).toMatchInlineSnapshot(`
+ "\`<div>\${
+ _ctx.foo
+ }</div>\`"
+ `)
})
test('v-text', () => {
- expect(getCompiledString(`<div v-text="foo"/>`)).toMatchInlineSnapshot(
- `"\`<div>\${_ssrInterpolate(_ctx.foo)}</div>\`"`
- )
+ expect(getCompiledString(`<div v-text="foo"/>`)).toMatchInlineSnapshot(`
+ "\`<div>\${
+ _ssrInterpolate(_ctx.foo)
+ }</div>\`"
+ `)
})
test('<textarea> with dynamic value', () => {
- expect(
- getCompiledString(`<textarea :value="foo"/>`)
- ).toMatchInlineSnapshot(
- `"\`<textarea>\${_ssrInterpolate(_ctx.foo)}</textarea>\`"`
- )
+ expect(getCompiledString(`<textarea :value="foo"/>`))
+ .toMatchInlineSnapshot(`
+ "\`<textarea>\${
+ _ssrInterpolate(_ctx.foo)
+ }</textarea>\`"
+ `)
})
test('<textarea> with static value', () => {
test('<textarea> with dynamic v-bind', () => {
expect(compile(`<textarea v-bind="obj">fallback</textarea>`).code)
.toMatchInlineSnapshot(`
- "const { ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
+ "const { mergeProps: _mergeProps } = require(\\"vue\\")
+ const { ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
let _temp0
_push(\`<textarea\${
- _ssrRenderAttrs(_temp0 = _ctx.obj, \\"textarea\\")
+ _ssrRenderAttrs(_temp0 = _mergeProps(_ctx.obj, _attrs), \\"textarea\\")
}>\${
_ssrInterpolate((\\"value\\" in _temp0) ? _temp0.value : \\"fallback\\")
}</textarea>\`)
isCustomElement: () => true
}).code
).toMatchInlineSnapshot(`
- "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+ "const { mergeProps: _mergeProps } = require(\\"vue\\")
+ const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<my-foo\${_ssrRenderAttrs(_ctx.obj, \\"my-foo\\")}></my-foo>\`)
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<my-foo\${_ssrRenderAttrs(_mergeProps(_ctx.obj, _attrs), \\"my-foo\\")}></my-foo>\`)
}"
`)
})
})
test('v-bind:class', () => {
- expect(
- getCompiledString(`<div id="foo" :class="bar"></div>`)
- ).toMatchInlineSnapshot(
- `"\`<div id=\\"foo\\" class=\\"\${_ssrRenderClass(_ctx.bar)}\\"></div>\`"`
- )
+ expect(getCompiledString(`<div id="foo" :class="bar"></div>`))
+ .toMatchInlineSnapshot(`
+ "\`<div id=\\"foo\\" class=\\"\${
+ _ssrRenderClass(_ctx.bar)
+ }\\"></div>\`"
+ `)
})
test('static class + v-bind:class', () => {
- expect(
- getCompiledString(`<div class="foo" :class="bar"></div>`)
- ).toMatchInlineSnapshot(
- `"\`<div class=\\"\${_ssrRenderClass([_ctx.bar, \\"foo\\"])}\\"></div>\`"`
- )
+ expect(getCompiledString(`<div class="foo" :class="bar"></div>`))
+ .toMatchInlineSnapshot(`
+ "\`<div class=\\"\${
+ _ssrRenderClass([_ctx.bar, \\"foo\\"])
+ }\\"></div>\`"
+ `)
})
test('v-bind:style', () => {
- expect(
- getCompiledString(`<div id="foo" :style="bar"></div>`)
- ).toMatchInlineSnapshot(
- `"\`<div id=\\"foo\\" style=\\"\${_ssrRenderStyle(_ctx.bar)}\\"></div>\`"`
- )
+ expect(getCompiledString(`<div id="foo" :style="bar"></div>`))
+ .toMatchInlineSnapshot(`
+ "\`<div id=\\"foo\\" style=\\"\${
+ _ssrRenderStyle(_ctx.bar)
+ }\\"></div>\`"
+ `)
})
test('static style + v-bind:style', () => {
- expect(
- getCompiledString(`<div style="color:red;" :style="bar"></div>`)
- ).toMatchInlineSnapshot(
- `"\`<div style=\\"\${_ssrRenderStyle([{\\"color\\":\\"red\\"}, _ctx.bar])}\\"></div>\`"`
- )
+ expect(getCompiledString(`<div style="color:red;" :style="bar"></div>`))
+ .toMatchInlineSnapshot(`
+ "\`<div style=\\"\${
+ _ssrRenderStyle([{\\"color\\":\\"red\\"}, _ctx.bar])
+ }\\"></div>\`"
+ `)
})
test('v-bind:key (boolean)', () => {
- expect(
- getCompiledString(`<input type="checkbox" :checked="checked">`)
- ).toMatchInlineSnapshot(
- `"\`<input type=\\"checkbox\\"\${(_ctx.checked) ? \\" checked\\" : \\"\\"}>\`"`
- )
+ expect(getCompiledString(`<input type="checkbox" :checked="checked">`))
+ .toMatchInlineSnapshot(`
+ "\`<input type=\\"checkbox\\"\${
+ (_ctx.checked) ? \\" checked\\" : \\"\\"
+ }>\`"
+ `)
})
test('v-bind:key (non-boolean)', () => {
- expect(
- getCompiledString(`<div :id="id" class="bar"></div>`)
- ).toMatchInlineSnapshot(
- `"\`<div\${_ssrRenderAttr(\\"id\\", _ctx.id)} class=\\"bar\\"></div>\`"`
- )
+ expect(getCompiledString(`<div :id="id" class="bar"></div>`))
+ .toMatchInlineSnapshot(`
+ "\`<div\${
+ _ssrRenderAttr(\\"id\\", _ctx.id)
+ } class=\\"bar\\"></div>\`"
+ `)
})
test('v-bind:[key]', () => {
- expect(
- getCompiledString(`<div v-bind:[key]="value"></div>`)
- ).toMatchInlineSnapshot(
- `"\`<div\${_ssrRenderAttrs({ [_ctx.key]: _ctx.value })}></div>\`"`
- )
+ expect(getCompiledString(`<div v-bind:[key]="value"></div>`))
+ .toMatchInlineSnapshot(`
+ "\`<div\${
+ _ssrRenderAttrs({ [_ctx.key]: _ctx.value })
+ }></div>\`"
+ `)
expect(getCompiledString(`<div class="foo" v-bind:[key]="value"></div>`))
.toMatchInlineSnapshot(`
- "\`<div\${_ssrRenderAttrs({
- class: \\"foo\\",
- [_ctx.key]: _ctx.value
- })}></div>\`"
+ "\`<div\${
+ _ssrRenderAttrs({
+ class: \\"foo\\",
+ [_ctx.key]: _ctx.value
+ })
+ }></div>\`"
`)
expect(getCompiledString(`<div :id="id" v-bind:[key]="value"></div>`))
.toMatchInlineSnapshot(`
- "\`<div\${_ssrRenderAttrs({
- id: _ctx.id,
- [_ctx.key]: _ctx.value
- })}></div>\`"
+ "\`<div\${
+ _ssrRenderAttrs({
+ id: _ctx.id,
+ [_ctx.key]: _ctx.value
+ })
+ }></div>\`"
`)
})
test('v-bind="obj"', () => {
- expect(
- getCompiledString(`<div v-bind="obj"></div>`)
- ).toMatchInlineSnapshot(`"\`<div\${_ssrRenderAttrs(_ctx.obj)}></div>\`"`)
+ expect(getCompiledString(`<div v-bind="obj"></div>`))
+ .toMatchInlineSnapshot(`
+ "\`<div\${
+ _ssrRenderAttrs(_ctx.obj)
+ }></div>\`"
+ `)
- expect(
- getCompiledString(`<div class="foo" v-bind="obj"></div>`)
- ).toMatchInlineSnapshot(
- `"\`<div\${_ssrRenderAttrs(_mergeProps({ class: \\"foo\\" }, _ctx.obj))}></div>\`"`
- )
+ expect(getCompiledString(`<div class="foo" v-bind="obj"></div>`))
+ .toMatchInlineSnapshot(`
+ "\`<div\${
+ _ssrRenderAttrs(_mergeProps({ class: \\"foo\\" }, _ctx.obj))
+ }></div>\`"
+ `)
- expect(
- getCompiledString(`<div :id="id" v-bind="obj"></div>`)
- ).toMatchInlineSnapshot(
- `"\`<div\${_ssrRenderAttrs(_mergeProps({ id: _ctx.id }, _ctx.obj))}></div>\`"`
- )
+ expect(getCompiledString(`<div :id="id" v-bind="obj"></div>`))
+ .toMatchInlineSnapshot(`
+ "\`<div\${
+ _ssrRenderAttrs(_mergeProps({ id: _ctx.id }, _ctx.obj))
+ }></div>\`"
+ `)
// dynamic key + v-bind="object"
- expect(
- getCompiledString(`<div :[key]="id" v-bind="obj"></div>`)
- ).toMatchInlineSnapshot(
- `"\`<div\${_ssrRenderAttrs(_mergeProps({ [_ctx.key]: _ctx.id }, _ctx.obj))}></div>\`"`
- )
+ expect(getCompiledString(`<div :[key]="id" v-bind="obj"></div>`))
+ .toMatchInlineSnapshot(`
+ "\`<div\${
+ _ssrRenderAttrs(_mergeProps({ [_ctx.key]: _ctx.id }, _ctx.obj))
+ }></div>\`"
+ `)
// should merge class and :class
expect(getCompiledString(`<div class="a" :class="b" v-bind="obj"></div>`))
.toMatchInlineSnapshot(`
- "\`<div\${_ssrRenderAttrs(_mergeProps({
- class: [\\"a\\", _ctx.b]
- }, _ctx.obj))}></div>\`"
+ "\`<div\${
+ _ssrRenderAttrs(_mergeProps({
+ class: [\\"a\\", _ctx.b]
+ }, _ctx.obj))
+ }></div>\`"
`)
// should merge style and :style
`<div style="color:red;" :style="b" v-bind="obj"></div>`
)
).toMatchInlineSnapshot(`
- "\`<div\${_ssrRenderAttrs(_mergeProps({
- style: [{\\"color\\":\\"red\\"}, _ctx.b]
- }, _ctx.obj))}></div>\`"
+ "\`<div\${
+ _ssrRenderAttrs(_mergeProps({
+ style: [{\\"color\\":\\"red\\"}, _ctx.b]
+ }, _ctx.obj))
+ }></div>\`"
`)
})
expect(
getCompiledString(`<div id="foo" v-on="bar"/>`)
).toMatchInlineSnapshot(`"\`<div id=\\"foo\\"></div>\`"`)
- expect(
- getCompiledString(`<div v-bind="foo" v-on="bar"/>`)
- ).toMatchInlineSnapshot(`"\`<div\${_ssrRenderAttrs(_ctx.foo)}></div>\`"`)
+ expect(getCompiledString(`<div v-bind="foo" v-on="bar"/>`))
+ .toMatchInlineSnapshot(`
+ "\`<div\${
+ _ssrRenderAttrs(_ctx.foo)
+ }></div>\`"
+ `)
})
})
})
.toMatchInlineSnapshot(`
"const { ssrRenderTeleport: _ssrRenderTeleport } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_ssrRenderTeleport(_push, (_push) => {
_push(\`<div></div>\`)
}, _ctx.target, false, _parent)
expect(
compile(`<teleport :target="target" disabled><div/></teleport>`).code
).toMatchInlineSnapshot(`
- "const { ssrRenderTeleport: _ssrRenderTeleport } = require(\\"@vue/server-renderer\\")
+ "const { ssrRenderTeleport: _ssrRenderTeleport } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _ssrRenderTeleport(_push, (_push) => {
- _push(\`<div></div>\`)
- }, _ctx.target, true, _parent)
- }"
- `)
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _ssrRenderTeleport(_push, (_push) => {
+ _push(\`<div></div>\`)
+ }, _ctx.target, true, _parent)
+ }"
+ `)
expect(
compile(`<teleport :target="target" :disabled="foo"><div/></teleport>`)
.code
).toMatchInlineSnapshot(`
- "const { ssrRenderTeleport: _ssrRenderTeleport } = require(\\"@vue/server-renderer\\")
+ "const { ssrRenderTeleport: _ssrRenderTeleport } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _ssrRenderTeleport(_push, (_push) => {
- _push(\`<div></div>\`)
- }, _ctx.target, _ctx.foo, _parent)
- }"
- `)
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _ssrRenderTeleport(_push, (_push) => {
+ _push(\`<div></div>\`)
+ }, _ctx.target, _ctx.foo, _parent)
+ }"
+ `)
})
})
scopeId
}).code
).toMatchInlineSnapshot(`
- "
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<div data-v-xxxxxxx><span data-v-xxxxxxx>hello</span></div>\`)
+ "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${_ssrRenderAttrs(_attrs)} data-v-xxxxxxx><span data-v-xxxxxxx>hello</span></div>\`)
}"
`)
})
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
- _push(_ssrRenderComponent(_component_foo, null, {
+ _push(_ssrRenderComponent(_component_foo, _attrs, {
default: _withCtx((_, _push, _parent, _scopeId) => {
if (_push) {
_push(\`foo\`)
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
- _push(_ssrRenderComponent(_component_foo, null, {
+ _push(_ssrRenderComponent(_component_foo, _attrs, {
default: _withCtx((_, _push, _parent, _scopeId) => {
if (_push) {
_push(\`<span data-v-xxxxxxx\${_scopeId}>hello</span>\`)
"const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
const _component_bar = _resolveComponent(\\"bar\\")
- _push(_ssrRenderComponent(_component_foo, null, {
+ _push(_ssrRenderComponent(_component_foo, _attrs, {
default: _withCtx((_, _push, _parent, _scopeId) => {
if (_push) {
_push(\`<span data-v-xxxxxxx\${_scopeId}>hello</span>\`)
expect(compile(`<slot/>`).code).toMatchInlineSnapshot(`
"const { ssrRenderSlot: _ssrRenderSlot } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_ssrRenderSlot(_ctx.$slots, \\"default\\", {}, null, _push, _parent)
}"
`)
expect(compile(`<slot name="foo" />`).code).toMatchInlineSnapshot(`
"const { ssrRenderSlot: _ssrRenderSlot } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_ssrRenderSlot(_ctx.$slots, \\"foo\\", {}, null, _push, _parent)
}"
`)
expect(compile(`<slot :name="bar.baz" />`).code).toMatchInlineSnapshot(`
"const { ssrRenderSlot: _ssrRenderSlot } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_ssrRenderSlot(_ctx.$slots, _ctx.bar.baz, {}, null, _push, _parent)
}"
`)
.toMatchInlineSnapshot(`
"const { ssrRenderSlot: _ssrRenderSlot } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_ssrRenderSlot(_ctx.$slots, \\"foo\\", {
p: 1,
bar: \\"2\\"
.toMatchInlineSnapshot(`
"const { ssrRenderSlot: _ssrRenderSlot, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_ssrRenderSlot(_ctx.$slots, \\"default\\", {}, () => {
_push(\`some \${_ssrInterpolate(_ctx.fallback)} content\`)
}, _push, _parent)
"const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSuspense: _ssrRenderSuspense } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
_ssrRenderSuspense(_push, {
"const { resolveComponent: _resolveComponent, withCtx: _withCtx } = require(\\"vue\\")
const { ssrRenderComponent: _ssrRenderComponent, ssrRenderSuspense: _ssrRenderSuspense } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
_ssrRenderSuspense(_push, {
expect(compile(`foo {{ bar }} baz`).code).toMatchInlineSnapshot(`
"const { ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`foo \${_ssrInterpolate(_ctx.bar)} baz\`)
}"
`)
compile(`<div><span>{{ foo }} bar</span><span>baz {{ qux }}</span></div>`)
.code
).toMatchInlineSnapshot(`
- "const { ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
+ "const { ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<div><span>\${
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><span>\${
_ssrInterpolate(_ctx.foo)
} bar</span><span>baz \${
_ssrInterpolate(_ctx.qux)
expect(compile(`<div v-for="i in list" />`).code).toMatchInlineSnapshot(`
"const { ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<!--[-->\`)
_ssrRenderList(_ctx.list, (i) => {
_push(\`<div></div>\`)
.toMatchInlineSnapshot(`
"const { ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<!--[-->\`)
_ssrRenderList(_ctx.list, (i) => {
_push(\`<div>foo<span>bar</span></div>\`)
).toMatchInlineSnapshot(`
"const { ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<!--[-->\`)
_ssrRenderList(_ctx.list, (row, i) => {
_push(\`<div><!--[-->\`)
.toMatchInlineSnapshot(`
"const { ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<!--[-->\`)
_ssrRenderList(_ctx.list, (i) => {
_push(\`<!--[-->\${_ssrInterpolate(i)}<!--]-->\`)
).toMatchInlineSnapshot(`
"const { ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<!--[-->\`)
_ssrRenderList(_ctx.list, (i) => {
_push(\`<span>\${_ssrInterpolate(i)}</span>\`)
).toMatchInlineSnapshot(`
"const { ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<!--[-->\`)
_ssrRenderList(_ctx.list, (i) => {
_push(\`<!--[--><span>\${
expect(code).toMatchInlineSnapshot(`
"const { ssrInterpolate: _ssrInterpolate, ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<!--[-->\`)
_ssrRenderList(_ctx.list, ({ foo }, index) => {
_push(\`<div>\${_ssrInterpolate(foo + _ctx.bar + index)}</div>\`)
describe('ssr: v-if', () => {
test('basic', () => {
expect(compile(`<div v-if="foo"></div>`).code).toMatchInlineSnapshot(`
- "
- return function ssrRender(_ctx, _push, _parent) {
+ "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
if (_ctx.foo) {
- _push(\`<div></div>\`)
+ _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
} else {
_push(\`<!---->\`)
}
test('with nested content', () => {
expect(compile(`<div v-if="foo">hello<span>ok</span></div>`).code)
.toMatchInlineSnapshot(`
- "
- return function ssrRender(_ctx, _push, _parent) {
+ "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
if (_ctx.foo) {
- _push(\`<div>hello<span>ok</span></div>\`)
+ _push(\`<div\${_ssrRenderAttrs(_attrs)}>hello<span>ok</span></div>\`)
} else {
_push(\`<!---->\`)
}
test('v-if + v-else', () => {
expect(compile(`<div v-if="foo"/><span v-else/>`).code)
.toMatchInlineSnapshot(`
- "
- return function ssrRender(_ctx, _push, _parent) {
+ "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
if (_ctx.foo) {
- _push(\`<div></div>\`)
+ _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
} else {
- _push(\`<span></span>\`)
+ _push(\`<span\${_ssrRenderAttrs(_attrs)}></span>\`)
}
}"
`)
test('v-if + v-else-if', () => {
expect(compile(`<div v-if="foo"/><span v-else-if="bar"/>`).code)
.toMatchInlineSnapshot(`
- "
- return function ssrRender(_ctx, _push, _parent) {
+ "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
if (_ctx.foo) {
- _push(\`<div></div>\`)
+ _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
} else if (_ctx.bar) {
- _push(\`<span></span>\`)
+ _push(\`<span\${_ssrRenderAttrs(_attrs)}></span>\`)
} else {
_push(\`<!---->\`)
}
test('v-if + v-else-if + v-else', () => {
expect(compile(`<div v-if="foo"/><span v-else-if="bar"/><p v-else/>`).code)
.toMatchInlineSnapshot(`
- "
- return function ssrRender(_ctx, _push, _parent) {
+ "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
if (_ctx.foo) {
- _push(\`<div></div>\`)
+ _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
} else if (_ctx.bar) {
- _push(\`<span></span>\`)
+ _push(\`<span\${_ssrRenderAttrs(_attrs)}></span>\`)
} else {
- _push(\`<p></p>\`)
+ _push(\`<p\${_ssrRenderAttrs(_attrs)}></p>\`)
}
}"
`)
expect(compile(`<template v-if="foo">hello</template>`).code)
.toMatchInlineSnapshot(`
"
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
if (_ctx.foo) {
_push(\`<!--[-->hello<!--]-->\`)
} else {
// single element should not wrap with fragment
expect(compile(`<template v-if="foo"><div>hi</div></template>`).code)
.toMatchInlineSnapshot(`
- "
- return function ssrRender(_ctx, _push, _parent) {
+ "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
if (_ctx.foo) {
- _push(\`<div>hi</div>\`)
+ _push(\`<div\${_ssrRenderAttrs(_attrs)}>hi</div>\`)
} else {
_push(\`<!---->\`)
}
compile(`<template v-if="foo"><div>hi</div><div>ho</div></template>`).code
).toMatchInlineSnapshot(`
"
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
if (_ctx.foo) {
_push(\`<!--[--><div>hi</div><div>ho</div><!--]-->\`)
} else {
).toMatchInlineSnapshot(`
"const { ssrRenderList: _ssrRenderList } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
if (_ctx.foo) {
_push(\`<!--[-->\`)
_ssrRenderList(_ctx.list, (i) => {
`<template v-if="foo"><div>hi</div><div>ho</div></template><div v-else/>`
).code
).toMatchInlineSnapshot(`
- "
- return function ssrRender(_ctx, _push, _parent) {
+ "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
if (_ctx.foo) {
_push(\`<!--[--><div>hi</div><div>ho</div><!--]-->\`)
} else {
- _push(\`<div></div>\`)
+ _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
}
}"
`)
import { compile } from '../src'
+function compileWithWrapper(src: string) {
+ return compile(`<div>${src}</div>`)
+}
+
describe('ssr: v-model', () => {
test('<input> (text types)', () => {
- expect(compile(`<input v-model="bar">`).code).toMatchInlineSnapshot(`
- "const { ssrRenderAttr: _ssrRenderAttr } = require(\\"@vue/server-renderer\\")
+ expect(compileWithWrapper(`<input v-model="bar">`).code)
+ .toMatchInlineSnapshot(`
+ "const { ssrRenderAttr: _ssrRenderAttr, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<input\${_ssrRenderAttr(\\"value\\", _ctx.bar)}>\`)
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><input\${
+ _ssrRenderAttr(\\"value\\", _ctx.bar)
+ }></div>\`)
}"
`)
- expect(compile(`<input type="email" v-model="bar">`).code)
+ expect(compileWithWrapper(`<input type="email" v-model="bar">`).code)
.toMatchInlineSnapshot(`
- "const { ssrRenderAttr: _ssrRenderAttr } = require(\\"@vue/server-renderer\\")
+ "const { ssrRenderAttr: _ssrRenderAttr, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<input type=\\"email\\"\${_ssrRenderAttr(\\"value\\", _ctx.bar)}>\`)
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><input type=\\"email\\"\${
+ _ssrRenderAttr(\\"value\\", _ctx.bar)
+ }></div>\`)
}"
`)
})
test('<input type="radio">', () => {
- expect(compile(`<input type="radio" value="foo" v-model="bar">`).code)
- .toMatchInlineSnapshot(`
- "const { ssrLooseEqual: _ssrLooseEqual } = require(\\"@vue/server-renderer\\")
-
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<input type=\\"radio\\" value=\\"foo\\"\${(_ssrLooseEqual(_ctx.bar, \\"foo\\")) ? \\" checked\\" : \\"\\"}>\`)
+ expect(
+ compileWithWrapper(`<input type="radio" value="foo" v-model="bar">`).code
+ ).toMatchInlineSnapshot(`
+ "const { ssrLooseEqual: _ssrLooseEqual, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><input type=\\"radio\\" value=\\"foo\\"\${
+ (_ssrLooseEqual(_ctx.bar, \\"foo\\")) ? \\" checked\\" : \\"\\"
+ }></div>\`)
}"
`)
})
test('<input type="checkbox"', () => {
- expect(compile(`<input type="checkbox" v-model="bar">`).code)
+ expect(compileWithWrapper(`<input type="checkbox" v-model="bar">`).code)
.toMatchInlineSnapshot(`
- "const { ssrLooseContain: _ssrLooseContain } = require(\\"@vue/server-renderer\\")
-
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<input type=\\"checkbox\\"\${((Array.isArray(_ctx.bar))
- ? _ssrLooseContain(_ctx.bar, null)
- : _ctx.bar) ? \\" checked\\" : \\"\\"}>\`)
+ "const { ssrLooseContain: _ssrLooseContain, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><input type=\\"checkbox\\"\${
+ ((Array.isArray(_ctx.bar))
+ ? _ssrLooseContain(_ctx.bar, null)
+ : _ctx.bar) ? \\" checked\\" : \\"\\"
+ }></div>\`)
}"
`)
- expect(compile(`<input type="checkbox" value="foo" v-model="bar">`).code)
- .toMatchInlineSnapshot(`
- "const { ssrLooseContain: _ssrLooseContain } = require(\\"@vue/server-renderer\\")
-
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<input type=\\"checkbox\\" value=\\"foo\\"\${((Array.isArray(_ctx.bar))
- ? _ssrLooseContain(_ctx.bar, \\"foo\\")
- : _ctx.bar) ? \\" checked\\" : \\"\\"}>\`)
+ expect(
+ compileWithWrapper(`<input type="checkbox" value="foo" v-model="bar">`)
+ .code
+ ).toMatchInlineSnapshot(`
+ "const { ssrLooseContain: _ssrLooseContain, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><input type=\\"checkbox\\" value=\\"foo\\"\${
+ ((Array.isArray(_ctx.bar))
+ ? _ssrLooseContain(_ctx.bar, \\"foo\\")
+ : _ctx.bar) ? \\" checked\\" : \\"\\"
+ }></div>\`)
}"
`)
})
test('<textarea>', () => {
- expect(compile(`<textarea v-model="foo">bar</textarea>`).code)
+ expect(compileWithWrapper(`<textarea v-model="foo">bar</textarea>`).code)
.toMatchInlineSnapshot(`
- "const { ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
-
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<textarea>\${_ssrInterpolate(_ctx.foo)}</textarea>\`)
+ "const { ssrRenderAttrs: _ssrRenderAttrs, ssrInterpolate: _ssrInterpolate } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><textarea>\${
+ _ssrInterpolate(_ctx.foo)
+ }</textarea></div>\`)
}"
`)
})
test('<input :type="x">', () => {
- expect(compile(`<input :type="x" v-model="foo">`).code)
+ expect(compileWithWrapper(`<input :type="x" v-model="foo">`).code)
.toMatchInlineSnapshot(`
- "const { ssrRenderAttr: _ssrRenderAttr, ssrRenderDynamicModel: _ssrRenderDynamicModel } = require(\\"@vue/server-renderer\\")
+ "const { ssrRenderAttr: _ssrRenderAttr, ssrRenderDynamicModel: _ssrRenderDynamicModel, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<input\${
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><input\${
_ssrRenderAttr(\\"type\\", _ctx.x)
}\${
_ssrRenderDynamicModel(_ctx.x, _ctx.foo, null)
- }>\`)
+ }></div>\`)
}"
`)
- expect(compile(`<input :type="x" v-model="foo" value="bar">`).code)
- .toMatchInlineSnapshot(`
- "const { ssrRenderAttr: _ssrRenderAttr, ssrRenderDynamicModel: _ssrRenderDynamicModel } = require(\\"@vue/server-renderer\\")
+ expect(
+ compileWithWrapper(`<input :type="x" v-model="foo" value="bar">`).code
+ ).toMatchInlineSnapshot(`
+ "const { ssrRenderAttr: _ssrRenderAttr, ssrRenderDynamicModel: _ssrRenderDynamicModel, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<input\${
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><input\${
_ssrRenderAttr(\\"type\\", _ctx.x)
}\${
_ssrRenderDynamicModel(_ctx.x, _ctx.foo, \\"bar\\")
- } value=\\"bar\\">\`)
+ } value=\\"bar\\"></div>\`)
}"
`)
- expect(compile(`<input :type="x" v-model="foo" :value="bar">`).code)
- .toMatchInlineSnapshot(`
- "const { ssrRenderAttr: _ssrRenderAttr, ssrRenderDynamicModel: _ssrRenderDynamicModel } = require(\\"@vue/server-renderer\\")
+ expect(
+ compileWithWrapper(`<input :type="x" v-model="foo" :value="bar">`).code
+ ).toMatchInlineSnapshot(`
+ "const { ssrRenderAttr: _ssrRenderAttr, ssrRenderDynamicModel: _ssrRenderDynamicModel, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<input\${
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><input\${
_ssrRenderAttr(\\"type\\", _ctx.x)
}\${
_ssrRenderDynamicModel(_ctx.x, _ctx.foo, _ctx.bar)
}\${
_ssrRenderAttr(\\"value\\", _ctx.bar)
- }>\`)
+ }></div>\`)
}"
`)
})
test('<input v-bind="obj">', () => {
- expect(compile(`<input v-bind="obj" v-model="foo">`).code)
+ expect(compileWithWrapper(`<input v-bind="obj" v-model="foo">`).code)
.toMatchInlineSnapshot(`
"const { mergeProps: _mergeProps } = require(\\"vue\\")
const { ssrRenderAttrs: _ssrRenderAttrs, ssrGetDynamicModelProps: _ssrGetDynamicModelProps } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
let _temp0
- _push(\`<input\${_ssrRenderAttrs((_temp0 = _ctx.obj, _mergeProps(_temp0, _ssrGetDynamicModelProps(_temp0, _ctx.foo))))}>\`)
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><input\${
+ _ssrRenderAttrs((_temp0 = _ctx.obj, _mergeProps(_temp0, _ssrGetDynamicModelProps(_temp0, _ctx.foo))))
+ }></div>\`)
}"
`)
- expect(compile(`<input id="x" v-bind="obj" v-model="foo" class="y">`).code)
- .toMatchInlineSnapshot(`
+ expect(
+ compileWithWrapper(`<input id="x" v-bind="obj" v-model="foo" class="y">`)
+ .code
+ ).toMatchInlineSnapshot(`
"const { mergeProps: _mergeProps } = require(\\"vue\\")
const { ssrRenderAttrs: _ssrRenderAttrs, ssrGetDynamicModelProps: _ssrGetDynamicModelProps } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
let _temp0
- _push(\`<input\${_ssrRenderAttrs((_temp0 = _mergeProps({ id: \\"x\\" }, _ctx.obj, { class: \\"y\\" }), _mergeProps(_temp0, _ssrGetDynamicModelProps(_temp0, _ctx.foo))))}>\`)
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><input\${
+ _ssrRenderAttrs((_temp0 = _mergeProps({ id: \\"x\\" }, _ctx.obj, { class: \\"y\\" }), _mergeProps(_temp0, _ssrGetDynamicModelProps(_temp0, _ctx.foo))))
+ }></div>\`)
}"
`)
})
import { compile } from '../src'
+function compileWithWrapper(src: string) {
+ return compile(`<div>${src}</div>`)
+}
+
describe('ssr: v-show', () => {
- test('basic', () => {
+ test('basic as root', () => {
expect(compile(`<div v-show="foo"/>`).code).toMatchInlineSnapshot(`
- "const { ssrRenderStyle: _ssrRenderStyle } = require(\\"@vue/server-renderer\\")
+ "const { mergeProps: _mergeProps } = require(\\"vue\\")
+ const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${_ssrRenderAttrs(_mergeProps({
+ style: (_ctx.foo) ? null : { display: \\"none\\" }
+ }, _attrs))}></div>\`)
+ }"
+ `)
+ })
+
+ test('basic', () => {
+ expect(compileWithWrapper(`<div v-show="foo"/>`).code)
+ .toMatchInlineSnapshot(`
+ "const { ssrRenderStyle: _ssrRenderStyle, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<div style=\\"\${_ssrRenderStyle((_ctx.foo) ? null : { display: \\"none\\" })}\\"></div>\`)
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><div style=\\"\${
+ _ssrRenderStyle((_ctx.foo) ? null : { display: \\"none\\" })
+ }\\"></div></div>\`)
}"
`)
})
test('with static style', () => {
- expect(compile(`<div style="color:red" v-show="foo"/>`).code)
+ expect(compileWithWrapper(`<div style="color:red" v-show="foo"/>`).code)
.toMatchInlineSnapshot(`
- "const { ssrRenderStyle: _ssrRenderStyle } = require(\\"@vue/server-renderer\\")
+ "const { ssrRenderStyle: _ssrRenderStyle, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<div style=\\"\${_ssrRenderStyle([
- {\\"color\\":\\"red\\"},
- (_ctx.foo) ? null : { display: \\"none\\" }
- ])}\\"></div>\`)
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><div style=\\"\${
+ _ssrRenderStyle([
+ {\\"color\\":\\"red\\"},
+ (_ctx.foo) ? null : { display: \\"none\\" }
+ ])
+ }\\"></div></div>\`)
}"
`)
})
test('with dynamic style', () => {
- expect(compile(`<div :style="{ color: 'red' }" v-show="foo"/>`).code)
- .toMatchInlineSnapshot(`
- "const { ssrRenderStyle: _ssrRenderStyle } = require(\\"@vue/server-renderer\\")
+ expect(
+ compileWithWrapper(`<div :style="{ color: 'red' }" v-show="foo"/>`).code
+ ).toMatchInlineSnapshot(`
+ "const { ssrRenderStyle: _ssrRenderStyle, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<div style=\\"\${_ssrRenderStyle([
- { color: 'red' },
- (_ctx.foo) ? null : { display: \\"none\\" }
- ])}\\"></div>\`)
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><div style=\\"\${
+ _ssrRenderStyle([
+ { color: 'red' },
+ (_ctx.foo) ? null : { display: \\"none\\" }
+ ])
+ }\\"></div></div>\`)
}"
`)
})
test('with static + dynamic style', () => {
expect(
- compile(`<div style="color:red" :style="{ fontSize: 14 }" v-show="foo"/>`)
- .code
+ compileWithWrapper(
+ `<div style="color:red" :style="{ fontSize: 14 }" v-show="foo"/>`
+ ).code
).toMatchInlineSnapshot(`
- "const { ssrRenderStyle: _ssrRenderStyle } = require(\\"@vue/server-renderer\\")
+ "const { ssrRenderStyle: _ssrRenderStyle, ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<div style=\\"\${_ssrRenderStyle([
- {\\"color\\":\\"red\\"},
- { fontSize: 14 },
- (_ctx.foo) ? null : { display: \\"none\\" }
- ])}\\"></div>\`)
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><div style=\\"\${
+ _ssrRenderStyle([
+ {\\"color\\":\\"red\\"},
+ { fontSize: 14 },
+ (_ctx.foo) ? null : { display: \\"none\\" }
+ ])
+ }\\"></div></div>\`)
}"
`)
})
test('with v-bind', () => {
expect(
- compile(
+ compileWithWrapper(
`<div v-bind="baz" style="color:red" :style="{ fontSize: 14 }" v-show="foo"/>`
).code
).toMatchInlineSnapshot(`
"const { mergeProps: _mergeProps } = require(\\"vue\\")
const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
- return function ssrRender(_ctx, _push, _parent) {
- _push(\`<div\${_ssrRenderAttrs(_mergeProps(_ctx.baz, {
- style: [
- {\\"color\\":\\"red\\"},
- { fontSize: 14 },
- (_ctx.foo) ? null : { display: \\"none\\" }
- ]
- }))}></div>\`)
+ return function ssrRender(_ctx, _push, _parent, _attrs) {
+ _push(\`<div\${
+ _ssrRenderAttrs(_attrs)
+ }><div\${
+ _ssrRenderAttrs(_mergeProps(_ctx.baz, {
+ style: [
+ {\\"color\\":\\"red\\"},
+ { fontSize: 14 },
+ (_ctx.foo) ? null : { display: \\"none\\" }
+ ]
+ }))
+ }></div></div>\`)
}"
`)
})
import { compile } from '../src'
export function getCompiledString(src: string): string {
- return compile(src).code.match(/_push\(([^]*)\)/)![1]
+ // Wrap src template in a root div so that it doesn't get injected
+ // fallthrough attr. This results in less noise in generated snapshots
+ // but also means this util can only be used for non-root cases.
+ const { code } = compile(`<div>${src}</div>`)
+ const match = code.match(
+ /_push\(\`<div\${\s*_ssrRenderAttrs\(_attrs\)\s*}>([^]*)<\/div>\`\)/
+ )
+
+ if (!match) {
+ throw new Error(`Unexpected compile result:\n${code}`)
+ }
+
+ return `\`${match[1]}\``
}
import { ssrTransformFor } from './transforms/ssrVFor'
import { ssrTransformModel } from './transforms/ssrVModel'
import { ssrTransformShow } from './transforms/ssrVShow'
+import { ssrInjectFallthroughAttrs } from './transforms/ssrInjectFallthroughAttrs'
export function compile(
template: string,
trackVForSlotScopes,
transformExpression,
ssrTransformSlotOutlet,
+ ssrInjectFallthroughAttrs,
ssrTransformElement,
ssrTransformComponent,
trackSlotScopes,
--- /dev/null
+import {
+ NodeTransform,
+ NodeTypes,
+ ElementTypes,
+ locStub,
+ createSimpleExpression,
+ RootNode,
+ TemplateChildNode,
+ ParentNode,
+ findDir
+} from '@vue/compiler-dom'
+
+const hasSingleChild = (node: ParentNode): boolean =>
+ node.children.filter(n => n.type !== NodeTypes.COMMENT).length === 1
+
+export const ssrInjectFallthroughAttrs: NodeTransform = (node, context) => {
+ // _attrs is provided as a function argument.
+ // mark it as a known identifier so that it doesn't get prefixed by
+ // transformExpression.
+ if (node.type === NodeTypes.ROOT) {
+ context.identifiers._attrs = 1
+ }
+
+ const parent = context.parent
+ if (!parent || parent.type !== NodeTypes.ROOT) {
+ return
+ }
+
+ if (node.type === NodeTypes.IF_BRANCH && hasSingleChild(node)) {
+ injectFallthroughAttrs(node.children[0])
+ } else if (hasSingleChild(parent)) {
+ injectFallthroughAttrs(node)
+ }
+}
+
+function injectFallthroughAttrs(node: RootNode | TemplateChildNode) {
+ if (
+ node.type === NodeTypes.ELEMENT &&
+ (node.tagType === ElementTypes.ELEMENT ||
+ node.tagType === ElementTypes.COMPONENT) &&
+ !findDir(node, 'for')
+ ) {
+ node.props.push({
+ type: NodeTypes.DIRECTIVE,
+ name: 'bind',
+ arg: undefined,
+ exp: createSimpleExpression(`_attrs`, false),
+ modifiers: [],
+ loc: locStub
+ })
+ }
+}
hasDynamicKeyVBind,
MERGE_PROPS,
isBindKey,
- createSequenceExpression
+ createSequenceExpression,
+ InterpolationNode
} from '@vue/compiler-dom'
import {
escapeHtml,
export const ssrTransformElement: NodeTransform = (node, context) => {
if (
- node.type === NodeTypes.ELEMENT &&
- node.tagType === ElementTypes.ELEMENT
+ node.type !== NodeTypes.ELEMENT ||
+ node.tagType !== ElementTypes.ELEMENT
) {
- return function ssrPostTransformElement() {
- // element
- // generate the template literal representing the open tag.
- const openTag: TemplateLiteral['elements'] = [`<${node.tag}`]
- // some tags need to be pasesd to runtime for special checks
- const needTagForRuntime =
- node.tag === 'textarea' || node.tag.indexOf('-') > 0
+ return
+ }
- // v-bind="obj" or v-bind:[key] can potentially overwrite other static
- // attrs and can affect final rendering result, so when they are present
- // we need to bail out to full `renderAttrs`
- const hasDynamicVBind = hasDynamicKeyVBind(node)
- if (hasDynamicVBind) {
- const { props } = buildProps(node, context, node.props, true /* ssr */)
- if (props) {
- const propsExp = createCallExpression(
- context.helper(SSR_RENDER_ATTRS),
- [props]
- )
+ return function ssrPostTransformElement() {
+ // element
+ // generate the template literal representing the open tag.
+ const openTag: TemplateLiteral['elements'] = [`<${node.tag}`]
+ // some tags need to be pasesd to runtime for special checks
+ const needTagForRuntime =
+ node.tag === 'textarea' || node.tag.indexOf('-') > 0
- if (node.tag === 'textarea') {
+ // v-bind="obj" or v-bind:[key] can potentially overwrite other static
+ // attrs and can affect final rendering result, so when they are present
+ // we need to bail out to full `renderAttrs`
+ const hasDynamicVBind = hasDynamicKeyVBind(node)
+ if (hasDynamicVBind) {
+ const { props } = buildProps(node, context, node.props, true /* ssr */)
+ if (props) {
+ const propsExp = createCallExpression(
+ context.helper(SSR_RENDER_ATTRS),
+ [props]
+ )
+
+ if (node.tag === 'textarea') {
+ const existingText = node.children[0] as
+ | TextNode
+ | InterpolationNode
+ | undefined
+ // If interpolation, this is dynamic <textarea> content, potentially
+ // injected by v-model and takes higher priority than v-bind value
+ if (!existingText || existingText.type !== NodeTypes.INTERPOLATION) {
// <textarea> with dynamic v-bind. We don't know if the final props
// will contain .value, so we will have to do something special:
// assign the merged props to a temp variable, and check whether
props
)
]
- const existingText = node.children[0] as TextNode | undefined
rawChildrenMap.set(
node,
createCallExpression(context.helper(SSR_INTERPOLATE), [
)
])
)
- } else if (node.tag === 'input') {
- // <input v-bind="obj" v-model>
- // we need to determine the props to render for the dynamic v-model
- // and merge it with the v-bind expression.
- const vModel = findVModel(node)
- if (vModel) {
- // 1. save the props (san v-model) in a temp variable
- const tempId = `_temp${context.temps++}`
- const tempExp = createSimpleExpression(tempId, false)
- propsExp.arguments = [
- createSequenceExpression([
- createAssignmentExpression(tempExp, props),
- createCallExpression(context.helper(MERGE_PROPS), [
- tempExp,
- createCallExpression(
- context.helper(SSR_GET_DYNAMIC_MODEL_PROPS),
- [
- tempExp, // existing props
- vModel.exp! // model
- ]
- )
- ])
- ])
- ]
- }
}
-
- if (needTagForRuntime) {
- propsExp.arguments.push(`"${node.tag}"`)
+ } else if (node.tag === 'input') {
+ // <input v-bind="obj" v-model>
+ // we need to determine the props to render for the dynamic v-model
+ // and merge it with the v-bind expression.
+ const vModel = findVModel(node)
+ if (vModel) {
+ // 1. save the props (san v-model) in a temp variable
+ const tempId = `_temp${context.temps++}`
+ const tempExp = createSimpleExpression(tempId, false)
+ propsExp.arguments = [
+ createSequenceExpression([
+ createAssignmentExpression(tempExp, props),
+ createCallExpression(context.helper(MERGE_PROPS), [
+ tempExp,
+ createCallExpression(
+ context.helper(SSR_GET_DYNAMIC_MODEL_PROPS),
+ [
+ tempExp, // existing props
+ vModel.exp! // model
+ ]
+ )
+ ])
+ ])
+ ]
}
+ }
- openTag.push(propsExp)
+ if (needTagForRuntime) {
+ propsExp.arguments.push(`"${node.tag}"`)
}
+
+ openTag.push(propsExp)
}
+ }
- // book keeping static/dynamic class merging.
- let dynamicClassBinding: CallExpression | undefined = undefined
- let staticClassBinding: string | undefined = undefined
- // all style bindings are converted to dynamic by transformStyle.
- // but we need to make sure to merge them.
- let dynamicStyleBinding: CallExpression | undefined = undefined
+ // book keeping static/dynamic class merging.
+ let dynamicClassBinding: CallExpression | undefined = undefined
+ let staticClassBinding: string | undefined = undefined
+ // all style bindings are converted to dynamic by transformStyle.
+ // but we need to make sure to merge them.
+ let dynamicStyleBinding: CallExpression | undefined = undefined
- for (let i = 0; i < node.props.length; i++) {
- const prop = node.props[i]
- // special cases with children override
- if (prop.type === NodeTypes.DIRECTIVE) {
- if (prop.name === 'html' && prop.exp) {
- rawChildrenMap.set(node, prop.exp)
- } else if (prop.name === 'text' && prop.exp) {
+ for (let i = 0; i < node.props.length; i++) {
+ const prop = node.props[i]
+ // special cases with children override
+ if (prop.type === NodeTypes.DIRECTIVE) {
+ if (prop.name === 'html' && prop.exp) {
+ rawChildrenMap.set(node, prop.exp)
+ } else if (prop.name === 'text' && prop.exp) {
+ node.children = [createInterpolation(prop.exp, prop.loc)]
+ } else if (prop.name === 'slot') {
+ context.onError(
+ createCompilerError(ErrorCodes.X_V_SLOT_MISPLACED, prop.loc)
+ )
+ } else if (isTextareaWithValue(node, prop) && prop.exp) {
+ if (!hasDynamicVBind) {
node.children = [createInterpolation(prop.exp, prop.loc)]
- } else if (prop.name === 'slot') {
+ }
+ } else {
+ // Directive transforms.
+ const directiveTransform = context.directiveTransforms[prop.name]
+ if (!directiveTransform) {
+ // no corresponding ssr directive transform found.
context.onError(
- createCompilerError(ErrorCodes.X_V_SLOT_MISPLACED, prop.loc)
+ createSSRCompilerError(
+ SSRErrorCodes.X_SSR_CUSTOM_DIRECTIVE_NO_TRANSFORM,
+ prop.loc
+ )
+ )
+ } else if (!hasDynamicVBind) {
+ const { props, ssrTagParts } = directiveTransform(
+ prop,
+ node,
+ context
)
- } else if (isTextareaWithValue(node, prop) && prop.exp) {
- if (!hasDynamicVBind) {
- node.children = [createInterpolation(prop.exp, prop.loc)]
+ if (ssrTagParts) {
+ openTag.push(...ssrTagParts)
}
- } else {
- // Directive transforms.
- const directiveTransform = context.directiveTransforms[prop.name]
- if (!directiveTransform) {
- // no corresponding ssr directive transform found.
- context.onError(
- createSSRCompilerError(
- SSRErrorCodes.X_SSR_CUSTOM_DIRECTIVE_NO_TRANSFORM,
- prop.loc
- )
- )
- } else if (!hasDynamicVBind) {
- const { props, ssrTagParts } = directiveTransform(
- prop,
- node,
- context
- )
- if (ssrTagParts) {
- openTag.push(...ssrTagParts)
- }
- for (let j = 0; j < props.length; j++) {
- const { key, value } = props[j]
- if (key.type === NodeTypes.SIMPLE_EXPRESSION && key.isStatic) {
- let attrName = key.content
- // static key attr
- if (attrName === 'class') {
+ for (let j = 0; j < props.length; j++) {
+ const { key, value } = props[j]
+ if (key.type === NodeTypes.SIMPLE_EXPRESSION && key.isStatic) {
+ let attrName = key.content
+ // static key attr
+ if (attrName === 'class') {
+ openTag.push(
+ ` class="`,
+ (dynamicClassBinding = createCallExpression(
+ context.helper(SSR_RENDER_CLASS),
+ [value]
+ )),
+ `"`
+ )
+ } else if (attrName === 'style') {
+ if (dynamicStyleBinding) {
+ // already has style binding, merge into it.
+ mergeCall(dynamicStyleBinding, value)
+ } else {
openTag.push(
- ` class="`,
- (dynamicClassBinding = createCallExpression(
- context.helper(SSR_RENDER_CLASS),
+ ` style="`,
+ (dynamicStyleBinding = createCallExpression(
+ context.helper(SSR_RENDER_STYLE),
[value]
)),
`"`
)
- } else if (attrName === 'style') {
- if (dynamicStyleBinding) {
- // already has style binding, merge into it.
- mergeCall(dynamicStyleBinding, value)
- } else {
- openTag.push(
- ` style="`,
- (dynamicStyleBinding = createCallExpression(
- context.helper(SSR_RENDER_STYLE),
- [value]
- )),
- `"`
+ }
+ } else {
+ attrName =
+ node.tag.indexOf('-') > 0
+ ? attrName // preserve raw name on custom elements
+ : propsToAttrMap[attrName] || attrName.toLowerCase()
+ if (isBooleanAttr(attrName)) {
+ openTag.push(
+ createConditionalExpression(
+ value,
+ createSimpleExpression(' ' + attrName, true),
+ createSimpleExpression('', true),
+ false /* no newline */
)
- }
+ )
+ } else if (isSSRSafeAttrName(attrName)) {
+ openTag.push(
+ createCallExpression(context.helper(SSR_RENDER_ATTR), [
+ key,
+ value
+ ])
+ )
} else {
- attrName =
- node.tag.indexOf('-') > 0
- ? attrName // preserve raw name on custom elements
- : propsToAttrMap[attrName] || attrName.toLowerCase()
- if (isBooleanAttr(attrName)) {
- openTag.push(
- createConditionalExpression(
- value,
- createSimpleExpression(' ' + attrName, true),
- createSimpleExpression('', true),
- false /* no newline */
- )
- )
- } else if (isSSRSafeAttrName(attrName)) {
- openTag.push(
- createCallExpression(context.helper(SSR_RENDER_ATTR), [
- key,
- value
- ])
- )
- } else {
- context.onError(
- createSSRCompilerError(
- SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME,
- key.loc
- )
+ context.onError(
+ createSSRCompilerError(
+ SSRErrorCodes.X_SSR_UNSAFE_ATTR_NAME,
+ key.loc
)
- }
- }
- } else {
- // dynamic key attr
- // this branch is only encountered for custom directive
- // transforms that returns properties with dynamic keys
- const args: CallExpression['arguments'] = [key, value]
- if (needTagForRuntime) {
- args.push(`"${node.tag}"`)
- }
- openTag.push(
- createCallExpression(
- context.helper(SSR_RENDER_DYNAMIC_ATTR),
- args
)
- )
+ }
+ }
+ } else {
+ // dynamic key attr
+ // this branch is only encountered for custom directive
+ // transforms that returns properties with dynamic keys
+ const args: CallExpression['arguments'] = [key, value]
+ if (needTagForRuntime) {
+ args.push(`"${node.tag}"`)
}
+ openTag.push(
+ createCallExpression(
+ context.helper(SSR_RENDER_DYNAMIC_ATTR),
+ args
+ )
+ )
}
}
}
- } else {
- // special case: value on <textarea>
- if (node.tag === 'textarea' && prop.name === 'value' && prop.value) {
- rawChildrenMap.set(node, escapeHtml(prop.value.content))
- } else if (!hasDynamicVBind) {
- // static prop
- if (prop.name === 'class' && prop.value) {
- staticClassBinding = JSON.stringify(prop.value.content)
- }
- openTag.push(
- ` ${prop.name}` +
- (prop.value ? `="${escapeHtml(prop.value.content)}"` : ``)
- )
+ }
+ } else {
+ // special case: value on <textarea>
+ if (node.tag === 'textarea' && prop.name === 'value' && prop.value) {
+ rawChildrenMap.set(node, escapeHtml(prop.value.content))
+ } else if (!hasDynamicVBind) {
+ // static prop
+ if (prop.name === 'class' && prop.value) {
+ staticClassBinding = JSON.stringify(prop.value.content)
}
+ openTag.push(
+ ` ${prop.name}` +
+ (prop.value ? `="${escapeHtml(prop.value.content)}"` : ``)
+ )
}
}
+ }
- // handle co-existence of dynamic + static class bindings
- if (dynamicClassBinding && staticClassBinding) {
- mergeCall(dynamicClassBinding, staticClassBinding)
- removeStaticBinding(openTag, 'class')
- }
-
- if (context.scopeId) {
- openTag.push(` ${context.scopeId}`)
- }
+ // handle co-existence of dynamic + static class bindings
+ if (dynamicClassBinding && staticClassBinding) {
+ mergeCall(dynamicClassBinding, staticClassBinding)
+ removeStaticBinding(openTag, 'class')
+ }
- node.ssrCodegenNode = createTemplateLiteral(openTag)
+ if (context.scopeId) {
+ openTag.push(` ${context.scopeId}`)
}
+
+ node.ssrCodegenNode = createTemplateLiteral(openTag)
}
}
ssrRender?: (
ctx: any,
push: (item: any) => void,
- parentInstance: ComponentInternalInstance
+ parentInstance: ComponentInternalInstance,
+ attrs?: Data
) => void
/**
const handlersRE = /^on|^vnode/
export function mergeProps(...args: (Data & VNodeProps)[]) {
- const ret: Data = {}
- extend(ret, args[0])
+ const ret = extend({}, args[0])
for (let i = 1; i < args.length; i++) {
const toMerge = args[i]
for (const key in toMerge) {
expect(
await renderToString(
createApp(
- defineComponent((props: {}) => {
+ defineComponent(() => {
const msg = ref('hello')
return () => h('div', msg.value)
})
).toBe(`<div>hello</div>`)
})
- describe('template components', () => {
- test('render', async () => {
- expect(
- await renderToString(
- createApp({
- data() {
- return { msg: 'hello' }
- },
- template: `<div>{{ msg }}</div>`
- })
- )
- ).toBe(`<div>hello</div>`)
- })
-
- test('handle compiler errors', async () => {
- await renderToString(createApp({ template: `<` }))
-
- expect(
- 'Template compilation error: Unexpected EOF in tag.\n' +
- '1 | <\n' +
- ' | ^'
- ).toHaveBeenWarned()
- })
- })
-
test('nested vnode components', async () => {
const Child = {
props: ['msg'],
{ msg: 'hello' },
{
// optimized slot using string push
- default: ({ msg }: any, push: any, p: any) => {
+ default: ({ msg }: any, push: any, _p: any) => {
push(`<span>${msg}</span>`)
},
// important to avoid slots being normalized
)
})
})
+
+ describe('integration w/ compiled template', () => {
+ test('render', async () => {
+ expect(
+ await renderToString(
+ createApp({
+ data() {
+ return { msg: 'hello' }
+ },
+ template: `<div>{{ msg }}</div>`
+ })
+ )
+ ).toBe(`<div>hello</div>`)
+ })
+
+ test('handle compiler errors', async () => {
+ await renderToString(createApp({ template: `<` }))
+
+ expect(
+ 'Template compilation error: Unexpected EOF in tag.\n' +
+ '1 | <\n' +
+ ' | ^'
+ ).toHaveBeenWarned()
+ })
+ })
})
--- /dev/null
+import { createApp } from 'vue'
+import { renderToString } from '../src/renderToString'
+
+describe('ssr: attr fallthrough', () => {
+ test('basic', async () => {
+ const Child = {
+ template: `<div class="foo" />`
+ }
+ const Parent = {
+ components: { Child },
+ template: `<child class="bar"/>`
+ }
+ const app = createApp(Parent)
+ expect(await renderToString(app)).toBe(`<div class="foo bar"></div>`)
+ })
+
+ test('with v-if', async () => {
+ const Child = {
+ props: ['ok'],
+ template: `<div v-if="ok" class="foo" /><span v-else />`
+ }
+ const Parent = {
+ props: ['ok'],
+ components: { Child },
+ template: `<child :ok="ok" class="bar"/>`
+ }
+ expect(await renderToString(createApp(Parent, { ok: true }))).toBe(
+ `<div class="foo bar"></div>`
+ )
+ expect(await renderToString(createApp(Parent, { ok: false }))).toBe(
+ `<span class="bar"></span>`
+ )
+ })
+
+ test('with v-model', async () => {
+ const Child = {
+ props: ['text'],
+ template: `<input v-model="text">`
+ }
+ const Parent = {
+ components: { Child },
+ template: `<child text="hello" class="bar"/>`
+ }
+ expect(await renderToString(createApp(Parent))).toBe(
+ `<input class="bar" value="hello">`
+ )
+ })
+
+ test('with v-bind', async () => {
+ const Child = {
+ props: ['obj'],
+ template: `<div v-bind="obj" />`
+ }
+ const Parent = {
+ components: { Child },
+ template: `<child :obj="{ class: 'foo' }" class="bar"/>`
+ }
+ expect(await renderToString(createApp(Parent))).toBe(
+ `<div class="foo bar"></div>`
+ )
+ })
+
+ test('nested fallthrough', async () => {
+ const Child = {
+ props: ['id'],
+ template: `<div :id="id"></div>`
+ }
+ const Parent = {
+ components: { Child },
+ template: `<child id="foo" class="bar"/>`
+ }
+ // pass to parent, fallthrough to child and merge
+ const app = createApp(Parent, { class: 'baz' })
+ expect(await renderToString(app)).toBe(
+ `<div id="foo" class="bar baz"></div>`
+ )
+ })
+})
// optimized
// set current rendering instance for asset resolution
setCurrentRenderingInstance(instance)
- comp.ssrRender(instance.proxy, push, instance)
+ // fallthrough attrs
+ const attrs =
+ instance.type.inheritAttrs !== false ? instance.attrs : undefined
+ comp.ssrRender(instance.proxy, push, instance, attrs)
setCurrentRenderingInstance(null)
} else if (instance.render) {
renderVNode(push, renderComponentRoot(instance), instance)