test('basic', () => {
expect(
compile(`<div><span>hello</span></div>`, {
- scopeId
+ scopeId,
+ mode: 'module'
}).code
).toMatchInlineSnapshot(`
- "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"@vue/server-renderer\\")
+ "import { withScopeId as _withScopeId } from \\"vue\\"
+ import { ssrRenderAttrs as _ssrRenderAttrs } from \\"@vue/server-renderer\\"
+ const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
- return function ssrRender(_ctx, _push, _parent, _attrs) {
+ export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
_push(\`<div\${_ssrRenderAttrs(_attrs)} data-v-xxxxxxx><span data-v-xxxxxxx>hello</span></div>\`)
- }"
+ })"
`)
})
// should have no branching inside slot
expect(
compile(`<foo>foo</foo>`, {
- scopeId
+ scopeId,
+ mode: 'module'
}).code
).toMatchInlineSnapshot(`
- "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode } = require(\\"vue\\")
- const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
+ "import { resolveComponent as _resolveComponent, withCtx as _withCtx, createTextVNode as _createTextVNode, withScopeId as _withScopeId } from \\"vue\\"
+ import { ssrRenderComponent as _ssrRenderComponent } from \\"@vue/server-renderer\\"
+ const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
- return function ssrRender(_ctx, _push, _parent, _attrs) {
+ export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
_push(_ssrRenderComponent(_component_foo, _attrs, {
- default: _withCtx((_, _push, _parent, _scopeId) => {
+ default: _withId((_, _push, _parent, _scopeId) => {
if (_push) {
_push(\`foo\`)
} else {
}),
_: 1
}, _parent))
- }"
+ })"
`)
})
test('inside slots (with elements)', () => {
expect(
compile(`<foo><span>hello</span></foo>`, {
- scopeId
+ scopeId,
+ mode: 'module'
}).code
).toMatchInlineSnapshot(`
- "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode } = require(\\"vue\\")
- const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
+ "import { resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode, withScopeId as _withScopeId } from \\"vue\\"
+ import { ssrRenderComponent as _ssrRenderComponent } from \\"@vue/server-renderer\\"
+ const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
- return function ssrRender(_ctx, _push, _parent, _attrs) {
+ export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
_push(_ssrRenderComponent(_component_foo, _attrs, {
- default: _withCtx((_, _push, _parent, _scopeId) => {
+ default: _withId((_, _push, _parent, _scopeId) => {
if (_push) {
_push(\`<span data-v-xxxxxxx\${_scopeId}>hello</span>\`)
} else {
}),
_: 1
}, _parent))
- }"
+ })"
`)
})
test('nested slots', () => {
expect(
compile(`<foo><span>hello</span><bar><span/></bar></foo>`, {
- scopeId
+ scopeId,
+ mode: 'module'
}).code
).toMatchInlineSnapshot(`
- "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode } = require(\\"vue\\")
- const { ssrRenderComponent: _ssrRenderComponent } = require(\\"@vue/server-renderer\\")
+ "import { resolveComponent as _resolveComponent, withCtx as _withCtx, createVNode as _createVNode, withScopeId as _withScopeId } from \\"vue\\"
+ import { ssrRenderComponent as _ssrRenderComponent } from \\"@vue/server-renderer\\"
+ const _withId = /*#__PURE__*/_withScopeId(\\"data-v-xxxxxxx\\")
- return function ssrRender(_ctx, _push, _parent, _attrs) {
+ export const ssrRender = /*#__PURE__*/_withId(function ssrRender(_ctx, _push, _parent, _attrs) {
const _component_foo = _resolveComponent(\\"foo\\")
const _component_bar = _resolveComponent(\\"bar\\")
_push(_ssrRenderComponent(_component_foo, _attrs, {
- default: _withCtx((_, _push, _parent, _scopeId) => {
+ default: _withId((_, _push, _parent, _scopeId) => {
if (_push) {
_push(\`<span data-v-xxxxxxx\${_scopeId}>hello</span>\`)
_push(_ssrRenderComponent(_component_bar, null, {
- default: _withCtx((_, _push, _parent, _scopeId) => {
+ default: _withId((_, _push, _parent, _scopeId) => {
if (_push) {
_push(\`<span data-v-xxxxxxx\${_scopeId}></span>\`)
} else {
return [
_createVNode(\\"span\\", null, \\"hello\\"),
_createVNode(_component_bar, null, {
- default: _withCtx(() => [
+ default: _withId(() => [
_createVNode(\\"span\\")
]),
_: 1
}),
_: 1
}, _parent))
- }"
+ })"
`)
})
})
--- /dev/null
+import { createApp, withScopeId } from 'vue'
+import { renderToString } from '../src/renderToString'
+import { ssrRenderComponent, ssrRenderAttrs, ssrRenderSlot } from '../src'
+
+describe('ssr: scoped id on component root', () => {
+ test('basic', async () => {
+ const withParentId = withScopeId('parent')
+
+ const Child = {
+ ssrRender: (ctx: any, push: any, parent: any, attrs: any) => {
+ push(`<div${ssrRenderAttrs(attrs)}></div>`)
+ }
+ }
+
+ const Comp = {
+ ssrRender: withParentId((ctx: any, push: any, parent: any) => {
+ push(ssrRenderComponent(Child), null, null, parent)
+ })
+ }
+
+ const result = await renderToString(createApp(Comp))
+ expect(result).toBe(`<div parent></div>`)
+ })
+
+ test('inside slot', async () => {
+ const withParentId = withScopeId('parent')
+
+ const Child = {
+ ssrRender: (_: any, push: any, _parent: any, attrs: any) => {
+ push(`<div${ssrRenderAttrs(attrs)} child></div>`)
+ }
+ }
+
+ const Wrapper = {
+ __scopeId: 'wrapper',
+ ssrRender: (ctx: any, push: any, parent: any) => {
+ ssrRenderSlot(ctx.$slots, 'default', {}, null, push, parent)
+ }
+ }
+
+ const Comp = {
+ ssrRender: withParentId((_: any, push: any, parent: any) => {
+ push(
+ ssrRenderComponent(
+ Wrapper,
+ null,
+ {
+ default: withParentId((_: any, push: any, parent: any) => {
+ push(ssrRenderComponent(Child, null, null, parent))
+ }),
+ _: 1
+ } as any,
+ parent
+ )
+ )
+ })
+ }
+
+ const result = await renderToString(createApp(Comp))
+ expect(result).toBe(`<!--[--><div parent wrapper-s child></div><!--]-->`)
+ })
+})
-import { ComponentInternalInstance, Slot, Slots } from 'vue'
+import { ComponentInternalInstance, Slots } from 'vue'
import { Props, PushFn, renderVNodeChildren } from '../render'
export type SSRSlots = Record<string, SSRSlot>
push(`<!--[-->`)
const slotFn = slots[slotName]
if (slotFn) {
- if (slotFn.length > 1) {
- // only ssr-optimized slot fns accept more than 1 arguments
- const scopeId = parentComponent && parentComponent.type.__scopeId
- slotFn(slotProps, push, parentComponent, scopeId ? ` ${scopeId}-s` : ``)
- } else {
+ const scopeId = parentComponent && parentComponent.type.__scopeId
+ const ret = slotFn(
+ slotProps,
+ push,
+ parentComponent,
+ scopeId ? ` ${scopeId}-s` : ``
+ )
+ if (Array.isArray(ret)) {
// normal slot
- renderVNodeChildren(push, (slotFn as Slot)(slotProps), parentComponent)
+ renderVNodeChildren(push, ret, parentComponent)
}
} else if (fallbackRenderFn) {
fallbackRenderFn()