codegen.spec.ts 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. import {
  2. locStub,
  3. generate,
  4. NodeTypes,
  5. RootNode,
  6. createSimpleExpression,
  7. createObjectExpression,
  8. createObjectProperty,
  9. createArrayExpression,
  10. createCompoundExpression,
  11. createInterpolation,
  12. createSequenceExpression,
  13. createCallExpression,
  14. createConditionalExpression,
  15. IfCodegenNode,
  16. ForCodegenNode
  17. } from '../src'
  18. import {
  19. CREATE_VNODE,
  20. COMMENT,
  21. TO_STRING,
  22. RESOLVE_DIRECTIVE,
  23. helperNameMap,
  24. RESOLVE_COMPONENT
  25. } from '../src/runtimeHelpers'
  26. import { createElementWithCodegen } from './testUtils'
  27. import { PatchFlags } from '@vue/shared'
  28. function createRoot(options: Partial<RootNode> = {}): RootNode {
  29. return {
  30. type: NodeTypes.ROOT,
  31. children: [],
  32. helpers: [],
  33. components: [],
  34. directives: [],
  35. hoists: [],
  36. codegenNode: createSimpleExpression(`null`, false),
  37. loc: locStub,
  38. ...options
  39. }
  40. }
  41. describe('compiler: codegen', () => {
  42. test('module mode preamble', () => {
  43. const root = createRoot({
  44. helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
  45. })
  46. const { code } = generate(root, { mode: 'module' })
  47. expect(code).toMatch(
  48. `import { ${helperNameMap[CREATE_VNODE]}, ${
  49. helperNameMap[RESOLVE_DIRECTIVE]
  50. } } from "vue"`
  51. )
  52. expect(code).toMatchSnapshot()
  53. })
  54. test('function mode preamble', () => {
  55. const root = createRoot({
  56. helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
  57. })
  58. const { code } = generate(root, { mode: 'function' })
  59. expect(code).toMatch(`const _Vue = Vue`)
  60. expect(code).toMatch(
  61. `const { ${helperNameMap[CREATE_VNODE]}: _${
  62. helperNameMap[CREATE_VNODE]
  63. }, ${helperNameMap[RESOLVE_DIRECTIVE]}: _${
  64. helperNameMap[RESOLVE_DIRECTIVE]
  65. } } = _Vue`
  66. )
  67. expect(code).toMatchSnapshot()
  68. })
  69. test('function mode preamble w/ prefixIdentifiers: true', () => {
  70. const root = createRoot({
  71. helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
  72. })
  73. const { code } = generate(root, {
  74. mode: 'function',
  75. prefixIdentifiers: true
  76. })
  77. expect(code).not.toMatch(`const _Vue = Vue`)
  78. expect(code).toMatch(
  79. `const { ${helperNameMap[CREATE_VNODE]}, ${
  80. helperNameMap[RESOLVE_DIRECTIVE]
  81. } } = Vue`
  82. )
  83. expect(code).toMatchSnapshot()
  84. })
  85. test('assets', () => {
  86. const root = createRoot({
  87. components: [`Foo`, `bar-baz`, `barbaz`],
  88. directives: [`my_dir`]
  89. })
  90. const { code } = generate(root, { mode: 'function' })
  91. expect(code).toMatch(
  92. `const _component_Foo = _${helperNameMap[RESOLVE_COMPONENT]}("Foo")\n`
  93. )
  94. expect(code).toMatch(
  95. `const _component_bar_baz = _${
  96. helperNameMap[RESOLVE_COMPONENT]
  97. }("bar-baz")\n`
  98. )
  99. expect(code).toMatch(
  100. `const _component_barbaz = _${
  101. helperNameMap[RESOLVE_COMPONENT]
  102. }("barbaz")\n`
  103. )
  104. expect(code).toMatch(
  105. `const _directive_my_dir = _${
  106. helperNameMap[RESOLVE_DIRECTIVE]
  107. }("my_dir")\n`
  108. )
  109. expect(code).toMatchSnapshot()
  110. })
  111. test('hoists', () => {
  112. const root = createRoot({
  113. hoists: [
  114. createSimpleExpression(`hello`, false, locStub),
  115. createObjectExpression(
  116. [
  117. createObjectProperty(
  118. createSimpleExpression(`id`, true, locStub),
  119. createSimpleExpression(`foo`, true, locStub)
  120. )
  121. ],
  122. locStub
  123. )
  124. ]
  125. })
  126. const { code } = generate(root)
  127. expect(code).toMatch(`const _hoisted_1 = hello`)
  128. expect(code).toMatch(`const _hoisted_2 = { id: "foo" }`)
  129. expect(code).toMatchSnapshot()
  130. })
  131. test('prefixIdentifiers: true should inject _ctx statement', () => {
  132. const { code } = generate(createRoot(), { prefixIdentifiers: true })
  133. expect(code).toMatch(`const _ctx = this\n`)
  134. expect(code).toMatchSnapshot()
  135. })
  136. test('static text', () => {
  137. const { code } = generate(
  138. createRoot({
  139. codegenNode: {
  140. type: NodeTypes.TEXT,
  141. content: 'hello',
  142. isEmpty: false,
  143. loc: locStub
  144. }
  145. })
  146. )
  147. expect(code).toMatch(`return "hello"`)
  148. expect(code).toMatchSnapshot()
  149. })
  150. test('interpolation', () => {
  151. const { code } = generate(
  152. createRoot({
  153. codegenNode: createInterpolation(`hello`, locStub)
  154. })
  155. )
  156. expect(code).toMatch(`return _${helperNameMap[TO_STRING]}(hello)`)
  157. expect(code).toMatchSnapshot()
  158. })
  159. test('comment', () => {
  160. const { code } = generate(
  161. createRoot({
  162. codegenNode: {
  163. type: NodeTypes.COMMENT,
  164. content: 'foo',
  165. loc: locStub
  166. }
  167. })
  168. )
  169. expect(code).toMatch(
  170. `return _${helperNameMap[CREATE_VNODE]}(_${
  171. helperNameMap[COMMENT]
  172. }, null, "foo")`
  173. )
  174. expect(code).toMatchSnapshot()
  175. })
  176. test('compound expression', () => {
  177. const { code } = generate(
  178. createRoot({
  179. codegenNode: createCompoundExpression([
  180. `_ctx.`,
  181. createSimpleExpression(`foo`, false, locStub),
  182. ` + `,
  183. {
  184. type: NodeTypes.INTERPOLATION,
  185. loc: locStub,
  186. content: createSimpleExpression(`bar`, false, locStub)
  187. }
  188. ])
  189. })
  190. )
  191. expect(code).toMatch(`return _ctx.foo + _${helperNameMap[TO_STRING]}(bar)`)
  192. expect(code).toMatchSnapshot()
  193. })
  194. test('ifNode', () => {
  195. const { code } = generate(
  196. createRoot({
  197. codegenNode: {
  198. type: NodeTypes.IF,
  199. loc: locStub,
  200. branches: [],
  201. codegenNode: createSequenceExpression([
  202. createSimpleExpression('foo', false),
  203. createSimpleExpression('bar', false)
  204. ]) as IfCodegenNode
  205. }
  206. })
  207. )
  208. expect(code).toMatch(`return (foo, bar)`)
  209. expect(code).toMatchSnapshot()
  210. })
  211. test('forNode', () => {
  212. const { code } = generate(
  213. createRoot({
  214. codegenNode: {
  215. type: NodeTypes.FOR,
  216. loc: locStub,
  217. source: createSimpleExpression('foo', false),
  218. valueAlias: undefined,
  219. keyAlias: undefined,
  220. objectIndexAlias: undefined,
  221. children: [],
  222. codegenNode: createSequenceExpression([
  223. createSimpleExpression('foo', false),
  224. createSimpleExpression('bar', false)
  225. ]) as ForCodegenNode
  226. }
  227. })
  228. )
  229. expect(code).toMatch(`return (foo, bar)`)
  230. expect(code).toMatchSnapshot()
  231. })
  232. test('Element (callExpression + objectExpression + TemplateChildNode[])', () => {
  233. const { code } = generate(
  234. createRoot({
  235. codegenNode: createElementWithCodegen([
  236. // string
  237. `"div"`,
  238. // ObjectExpression
  239. createObjectExpression(
  240. [
  241. createObjectProperty(
  242. createSimpleExpression(`id`, true, locStub),
  243. createSimpleExpression(`foo`, true, locStub)
  244. ),
  245. createObjectProperty(
  246. createSimpleExpression(`prop`, false, locStub),
  247. createSimpleExpression(`bar`, false, locStub)
  248. ),
  249. // compound expression as computed key
  250. createObjectProperty(
  251. {
  252. type: NodeTypes.COMPOUND_EXPRESSION,
  253. loc: locStub,
  254. children: [
  255. `foo + `,
  256. createSimpleExpression(`bar`, false, locStub)
  257. ]
  258. },
  259. createSimpleExpression(`bar`, false, locStub)
  260. )
  261. ],
  262. locStub
  263. ),
  264. // ChildNode[]
  265. [
  266. createElementWithCodegen([
  267. `"p"`,
  268. createObjectExpression(
  269. [
  270. createObjectProperty(
  271. // should quote the key!
  272. createSimpleExpression(`some-key`, true, locStub),
  273. createSimpleExpression(`foo`, true, locStub)
  274. )
  275. ],
  276. locStub
  277. )
  278. ])
  279. ],
  280. // flag
  281. PatchFlags.FULL_PROPS + ''
  282. ])
  283. })
  284. )
  285. expect(code).toMatch(`
  286. return _${helperNameMap[CREATE_VNODE]}("div", {
  287. id: "foo",
  288. [prop]: bar,
  289. [foo + bar]: bar
  290. }, [
  291. _${helperNameMap[CREATE_VNODE]}("p", { "some-key": "foo" })
  292. ], ${PatchFlags.FULL_PROPS})`)
  293. expect(code).toMatchSnapshot()
  294. })
  295. test('ArrayExpression', () => {
  296. const { code } = generate(
  297. createRoot({
  298. codegenNode: createArrayExpression([
  299. createSimpleExpression(`foo`, false),
  300. createCallExpression(`bar`, [`baz`])
  301. ])
  302. })
  303. )
  304. expect(code).toMatch(`return [
  305. foo,
  306. bar(baz)
  307. ]`)
  308. expect(code).toMatchSnapshot()
  309. })
  310. test('SequenceExpression', () => {
  311. const { code } = generate(
  312. createRoot({
  313. codegenNode: createSequenceExpression([
  314. createSimpleExpression(`foo`, false),
  315. createCallExpression(`bar`, [`baz`])
  316. ])
  317. })
  318. )
  319. expect(code).toMatch(`return (foo, bar(baz))`)
  320. expect(code).toMatchSnapshot()
  321. })
  322. test('ConditionalExpression', () => {
  323. const { code } = generate(
  324. createRoot({
  325. codegenNode: createConditionalExpression(
  326. createSimpleExpression(`ok`, false),
  327. createCallExpression(`foo`),
  328. createConditionalExpression(
  329. createSimpleExpression(`orNot`, false),
  330. createCallExpression(`bar`),
  331. createCallExpression(`baz`)
  332. )
  333. )
  334. })
  335. )
  336. expect(code).toMatch(
  337. `return ok
  338. ? foo()
  339. : orNot
  340. ? bar()
  341. : baz()`
  342. )
  343. expect(code).toMatchSnapshot()
  344. })
  345. })