ssrComponent.spec.ts 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. import { compile } from '../src'
  2. describe('ssr: components', () => {
  3. test('basic', () => {
  4. expect(compile(`<foo id="a" :prop="b" />`).code).toMatchInlineSnapshot(`
  5. "const { resolveComponent: _resolveComponent, mergeProps: _mergeProps } = require(\\"vue\\")
  6. const { ssrRenderComponent: _ssrRenderComponent } = require(\\"vue/server-renderer\\")
  7. return function ssrRender(_ctx, _push, _parent, _attrs) {
  8. const _component_foo = _resolveComponent(\\"foo\\")
  9. _push(_ssrRenderComponent(_component_foo, _mergeProps({
  10. id: \\"a\\",
  11. prop: _ctx.b
  12. }, _attrs), null, _parent))
  13. }"
  14. `)
  15. })
  16. test('dynamic component', () => {
  17. expect(compile(`<component is="foo" prop="b" />`).code)
  18. .toMatchInlineSnapshot(`
  19. "const { resolveDynamicComponent: _resolveDynamicComponent, mergeProps: _mergeProps, createVNode: _createVNode } = require(\\"vue\\")
  20. const { ssrRenderVNode: _ssrRenderVNode } = require(\\"vue/server-renderer\\")
  21. return function ssrRender(_ctx, _push, _parent, _attrs) {
  22. _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(\\"foo\\"), _mergeProps({ prop: \\"b\\" }, _attrs), null), _parent)
  23. }"
  24. `)
  25. expect(compile(`<component :is="foo" prop="b" />`).code)
  26. .toMatchInlineSnapshot(`
  27. "const { resolveDynamicComponent: _resolveDynamicComponent, mergeProps: _mergeProps, createVNode: _createVNode } = require(\\"vue\\")
  28. const { ssrRenderVNode: _ssrRenderVNode } = require(\\"vue/server-renderer\\")
  29. return function ssrRender(_ctx, _push, _parent, _attrs) {
  30. _ssrRenderVNode(_push, _createVNode(_resolveDynamicComponent(_ctx.foo), _mergeProps({ prop: \\"b\\" }, _attrs), null), _parent)
  31. }"
  32. `)
  33. })
  34. describe('slots', () => {
  35. test('implicit default slot', () => {
  36. expect(compile(`<foo>hello<div/></foo>`).code).toMatchInlineSnapshot(`
  37. "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createVNode: _createVNode, createTextVNode: _createTextVNode } = require(\\"vue\\")
  38. const { ssrRenderComponent: _ssrRenderComponent } = require(\\"vue/server-renderer\\")
  39. return function ssrRender(_ctx, _push, _parent, _attrs) {
  40. const _component_foo = _resolveComponent(\\"foo\\")
  41. _push(_ssrRenderComponent(_component_foo, _attrs, {
  42. default: _withCtx((_, _push, _parent, _scopeId) => {
  43. if (_push) {
  44. _push(\`hello<div\${_scopeId}></div>\`)
  45. } else {
  46. return [
  47. _createTextVNode(\\"hello\\"),
  48. _createVNode(\\"div\\")
  49. ]
  50. }
  51. }),
  52. _: 1 /* STABLE */
  53. }, _parent))
  54. }"
  55. `)
  56. })
  57. test('explicit default slot', () => {
  58. expect(compile(`<foo v-slot="{ msg }">{{ msg + outer }}</foo>`).code)
  59. .toMatchInlineSnapshot(`
  60. "const { resolveComponent: _resolveComponent, withCtx: _withCtx, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode } = require(\\"vue\\")
  61. const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"vue/server-renderer\\")
  62. return function ssrRender(_ctx, _push, _parent, _attrs) {
  63. const _component_foo = _resolveComponent(\\"foo\\")
  64. _push(_ssrRenderComponent(_component_foo, _attrs, {
  65. default: _withCtx(({ msg }, _push, _parent, _scopeId) => {
  66. if (_push) {
  67. _push(\`\${_ssrInterpolate(msg + _ctx.outer)}\`)
  68. } else {
  69. return [
  70. _createTextVNode(_toDisplayString(msg + _ctx.outer), 1 /* TEXT */)
  71. ]
  72. }
  73. }),
  74. _: 1 /* STABLE */
  75. }, _parent))
  76. }"
  77. `)
  78. })
  79. test('named slots', () => {
  80. expect(
  81. compile(`<foo>
  82. <template v-slot>foo</template>
  83. <template v-slot:named>bar</template>
  84. </foo>`).code
  85. ).toMatchInlineSnapshot(`
  86. "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode } = require(\\"vue\\")
  87. const { ssrRenderComponent: _ssrRenderComponent } = require(\\"vue/server-renderer\\")
  88. return function ssrRender(_ctx, _push, _parent, _attrs) {
  89. const _component_foo = _resolveComponent(\\"foo\\")
  90. _push(_ssrRenderComponent(_component_foo, _attrs, {
  91. default: _withCtx((_, _push, _parent, _scopeId) => {
  92. if (_push) {
  93. _push(\`foo\`)
  94. } else {
  95. return [
  96. _createTextVNode(\\"foo\\")
  97. ]
  98. }
  99. }),
  100. named: _withCtx((_, _push, _parent, _scopeId) => {
  101. if (_push) {
  102. _push(\`bar\`)
  103. } else {
  104. return [
  105. _createTextVNode(\\"bar\\")
  106. ]
  107. }
  108. }),
  109. _: 1 /* STABLE */
  110. }, _parent))
  111. }"
  112. `)
  113. })
  114. test('v-if slot', () => {
  115. expect(
  116. compile(`<foo>
  117. <template v-slot:named v-if="ok">foo</template>
  118. </foo>`).code
  119. ).toMatchInlineSnapshot(`
  120. "const { resolveComponent: _resolveComponent, withCtx: _withCtx, createTextVNode: _createTextVNode, createSlots: _createSlots } = require(\\"vue\\")
  121. const { ssrRenderComponent: _ssrRenderComponent } = require(\\"vue/server-renderer\\")
  122. return function ssrRender(_ctx, _push, _parent, _attrs) {
  123. const _component_foo = _resolveComponent(\\"foo\\")
  124. _push(_ssrRenderComponent(_component_foo, _attrs, _createSlots({ _: 2 /* DYNAMIC */ }, [
  125. (_ctx.ok)
  126. ? {
  127. name: \\"named\\",
  128. fn: _withCtx((_, _push, _parent, _scopeId) => {
  129. if (_push) {
  130. _push(\`foo\`)
  131. } else {
  132. return [
  133. _createTextVNode(\\"foo\\")
  134. ]
  135. }
  136. })
  137. }
  138. : undefined
  139. ]), _parent))
  140. }"
  141. `)
  142. })
  143. test('v-for slot', () => {
  144. expect(
  145. compile(`<foo>
  146. <template v-for="key in names" v-slot:[key]="{ msg }">{{ msg + key + bar }}</template>
  147. </foo>`).code
  148. ).toMatchInlineSnapshot(`
  149. "const { resolveComponent: _resolveComponent, withCtx: _withCtx, toDisplayString: _toDisplayString, createTextVNode: _createTextVNode, renderList: _renderList, createSlots: _createSlots } = require(\\"vue\\")
  150. const { ssrRenderComponent: _ssrRenderComponent, ssrInterpolate: _ssrInterpolate } = require(\\"vue/server-renderer\\")
  151. return function ssrRender(_ctx, _push, _parent, _attrs) {
  152. const _component_foo = _resolveComponent(\\"foo\\")
  153. _push(_ssrRenderComponent(_component_foo, _attrs, _createSlots({ _: 2 /* DYNAMIC */ }, [
  154. _renderList(_ctx.names, (key) => {
  155. return {
  156. name: key,
  157. fn: _withCtx(({ msg }, _push, _parent, _scopeId) => {
  158. if (_push) {
  159. _push(\`\${_ssrInterpolate(msg + key + _ctx.bar)}\`)
  160. } else {
  161. return [
  162. _createTextVNode(_toDisplayString(msg + _ctx.key + _ctx.bar), 1 /* TEXT */)
  163. ]
  164. }
  165. })
  166. }
  167. })
  168. ]), _parent))
  169. }"
  170. `)
  171. })
  172. test('nested transform scoping in vnode branch', () => {
  173. expect(
  174. compile(`<foo>
  175. <template v-slot:foo="{ list }">
  176. <div v-if="ok">
  177. <span v-for="i in list"></span>
  178. </div>
  179. </template>
  180. <template v-slot:bar="{ ok }">
  181. <div v-if="ok">
  182. <span v-for="i in list"></span>
  183. </div>
  184. </template>
  185. </foo>`).code
  186. ).toMatchInlineSnapshot(`
  187. "const { resolveComponent: _resolveComponent, withCtx: _withCtx, renderList: _renderList, Fragment: _Fragment, openBlock: _openBlock, createBlock: _createBlock, createCommentVNode: _createCommentVNode } = require(\\"vue\\")
  188. const { ssrRenderComponent: _ssrRenderComponent, ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
  189. return function ssrRender(_ctx, _push, _parent, _attrs) {
  190. const _component_foo = _resolveComponent(\\"foo\\")
  191. _push(_ssrRenderComponent(_component_foo, _attrs, {
  192. foo: _withCtx(({ list }, _push, _parent, _scopeId) => {
  193. if (_push) {
  194. if (_ctx.ok) {
  195. _push(\`<div\${_scopeId}><!--[-->\`)
  196. _ssrRenderList(list, (i) => {
  197. _push(\`<span\${_scopeId}></span>\`)
  198. })
  199. _push(\`<!--]--></div>\`)
  200. } else {
  201. _push(\`<!---->\`)
  202. }
  203. } else {
  204. return [
  205. (_ctx.ok)
  206. ? (_openBlock(), _createBlock(\\"div\\", { key: 0 }, [
  207. (_openBlock(true), _createBlock(_Fragment, null, _renderList(list, (i) => {
  208. return (_openBlock(), _createBlock(\\"span\\"))
  209. }), 256 /* UNKEYED_FRAGMENT */))
  210. ]))
  211. : _createCommentVNode(\\"v-if\\", true)
  212. ]
  213. }
  214. }),
  215. bar: _withCtx(({ ok }, _push, _parent, _scopeId) => {
  216. if (_push) {
  217. if (ok) {
  218. _push(\`<div\${_scopeId}><!--[-->\`)
  219. _ssrRenderList(_ctx.list, (i) => {
  220. _push(\`<span\${_scopeId}></span>\`)
  221. })
  222. _push(\`<!--]--></div>\`)
  223. } else {
  224. _push(\`<!---->\`)
  225. }
  226. } else {
  227. return [
  228. ok
  229. ? (_openBlock(), _createBlock(\\"div\\", { key: 0 }, [
  230. (_openBlock(true), _createBlock(_Fragment, null, _renderList(_ctx.list, (i) => {
  231. return (_openBlock(), _createBlock(\\"span\\"))
  232. }), 256 /* UNKEYED_FRAGMENT */))
  233. ]))
  234. : _createCommentVNode(\\"v-if\\", true)
  235. ]
  236. }
  237. }),
  238. _: 1 /* STABLE */
  239. }, _parent))
  240. }"
  241. `)
  242. })
  243. test('built-in fallthroughs', () => {
  244. expect(compile(`<transition><div/></transition>`).code)
  245. .toMatchInlineSnapshot(`
  246. "const { ssrRenderAttrs: _ssrRenderAttrs } = require(\\"vue/server-renderer\\")
  247. return function ssrRender(_ctx, _push, _parent, _attrs) {
  248. _push(\`<div\${_ssrRenderAttrs(_attrs)}></div>\`)
  249. }"
  250. `)
  251. expect(compile(`<keep-alive><foo/></keep-alive>`).code)
  252. .toMatchInlineSnapshot(`
  253. "const { resolveComponent: _resolveComponent } = require(\\"vue\\")
  254. const { ssrRenderComponent: _ssrRenderComponent } = require(\\"vue/server-renderer\\")
  255. return function ssrRender(_ctx, _push, _parent, _attrs) {
  256. const _component_foo = _resolveComponent(\\"foo\\")
  257. _push(_ssrRenderComponent(_component_foo, _attrs, null, _parent))
  258. }"
  259. `)
  260. })
  261. // transition-group should flatten and concat its children fragments into
  262. // a single one
  263. describe('transition-group', () => {
  264. test('basic', () => {
  265. expect(
  266. compile(
  267. `<transition-group><div v-for="i in list"/></transition-group>`
  268. ).code
  269. ).toMatchInlineSnapshot(`
  270. "const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
  271. return function ssrRender(_ctx, _push, _parent, _attrs) {
  272. _push(\`<!--[-->\`)
  273. _ssrRenderList(_ctx.list, (i) => {
  274. _push(\`<div></div>\`)
  275. })
  276. _push(\`<!--]-->\`)
  277. }"
  278. `)
  279. })
  280. test('with static tag', () => {
  281. expect(
  282. compile(
  283. `<transition-group tag="ul"><div v-for="i in list"/></transition-group>`
  284. ).code
  285. ).toMatchInlineSnapshot(`
  286. "const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
  287. return function ssrRender(_ctx, _push, _parent, _attrs) {
  288. _push(\`<ul>\`)
  289. _ssrRenderList(_ctx.list, (i) => {
  290. _push(\`<div></div>\`)
  291. })
  292. _push(\`</ul>\`)
  293. }"
  294. `)
  295. })
  296. test('with dynamic tag', () => {
  297. expect(
  298. compile(
  299. `<transition-group :tag="someTag"><div v-for="i in list"/></transition-group>`
  300. ).code
  301. ).toMatchInlineSnapshot(`
  302. "const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
  303. return function ssrRender(_ctx, _push, _parent, _attrs) {
  304. _push(\`<\${_ctx.someTag}>\`)
  305. _ssrRenderList(_ctx.list, (i) => {
  306. _push(\`<div></div>\`)
  307. })
  308. _push(\`</\${_ctx.someTag}>\`)
  309. }"
  310. `)
  311. })
  312. test('with multi fragments children', () => {
  313. expect(
  314. compile(
  315. `<transition-group>
  316. <div v-for="i in 10"/>
  317. <div v-for="i in 10"/>
  318. <template v-if="ok"><div>ok</div></template>
  319. </transition-group>`
  320. ).code
  321. ).toMatchInlineSnapshot(`
  322. "const { ssrRenderList: _ssrRenderList } = require(\\"vue/server-renderer\\")
  323. return function ssrRender(_ctx, _push, _parent, _attrs) {
  324. _push(\`<!--[-->\`)
  325. _ssrRenderList(10, (i) => {
  326. _push(\`<div></div>\`)
  327. })
  328. _ssrRenderList(10, (i) => {
  329. _push(\`<div></div>\`)
  330. })
  331. if (_ctx.ok) {
  332. _push(\`<div>ok</div>\`)
  333. } else {
  334. _push(\`<!---->\`)
  335. }
  336. _push(\`<!--]-->\`)
  337. }"
  338. `)
  339. })
  340. })
  341. })
  342. })