codegen.spec.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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`],
  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_barbaz = _${
  96. helperNameMap[RESOLVE_COMPONENT]
  97. }("bar-baz")\n`
  98. )
  99. expect(code).toMatch(
  100. `const _directive_my_dir = _${
  101. helperNameMap[RESOLVE_DIRECTIVE]
  102. }("my_dir")\n`
  103. )
  104. expect(code).toMatchSnapshot()
  105. })
  106. test('hoists', () => {
  107. const root = createRoot({
  108. hoists: [
  109. createSimpleExpression(`hello`, false, locStub),
  110. createObjectExpression(
  111. [
  112. createObjectProperty(
  113. createSimpleExpression(`id`, true, locStub),
  114. createSimpleExpression(`foo`, true, locStub)
  115. )
  116. ],
  117. locStub
  118. )
  119. ]
  120. })
  121. const { code } = generate(root)
  122. expect(code).toMatch(`const _hoisted_1 = hello`)
  123. expect(code).toMatch(`const _hoisted_2 = { id: "foo" }`)
  124. expect(code).toMatchSnapshot()
  125. })
  126. test('prefixIdentifiers: true should inject _ctx statement', () => {
  127. const { code } = generate(createRoot(), { prefixIdentifiers: true })
  128. expect(code).toMatch(`const _ctx = this\n`)
  129. expect(code).toMatchSnapshot()
  130. })
  131. test('static text', () => {
  132. const { code } = generate(
  133. createRoot({
  134. codegenNode: {
  135. type: NodeTypes.TEXT,
  136. content: 'hello',
  137. isEmpty: false,
  138. loc: locStub
  139. }
  140. })
  141. )
  142. expect(code).toMatch(`return "hello"`)
  143. expect(code).toMatchSnapshot()
  144. })
  145. test('interpolation', () => {
  146. const { code } = generate(
  147. createRoot({
  148. codegenNode: createInterpolation(`hello`, locStub)
  149. })
  150. )
  151. expect(code).toMatch(`return _${helperNameMap[TO_STRING]}(hello)`)
  152. expect(code).toMatchSnapshot()
  153. })
  154. test('comment', () => {
  155. const { code } = generate(
  156. createRoot({
  157. codegenNode: {
  158. type: NodeTypes.COMMENT,
  159. content: 'foo',
  160. loc: locStub
  161. }
  162. })
  163. )
  164. expect(code).toMatch(
  165. `return _${helperNameMap[CREATE_VNODE]}(_${
  166. helperNameMap[COMMENT]
  167. }, 0, "foo")`
  168. )
  169. expect(code).toMatchSnapshot()
  170. })
  171. test('compound expression', () => {
  172. const { code } = generate(
  173. createRoot({
  174. codegenNode: createCompoundExpression([
  175. `_ctx.`,
  176. createSimpleExpression(`foo`, false, locStub),
  177. ` + `,
  178. {
  179. type: NodeTypes.INTERPOLATION,
  180. loc: locStub,
  181. content: createSimpleExpression(`bar`, false, locStub)
  182. }
  183. ])
  184. })
  185. )
  186. expect(code).toMatch(`return _ctx.foo + _${helperNameMap[TO_STRING]}(bar)`)
  187. expect(code).toMatchSnapshot()
  188. })
  189. test('ifNode', () => {
  190. const { code } = generate(
  191. createRoot({
  192. codegenNode: {
  193. type: NodeTypes.IF,
  194. loc: locStub,
  195. branches: [],
  196. codegenNode: createSequenceExpression([
  197. createSimpleExpression('foo', false),
  198. createSimpleExpression('bar', false)
  199. ]) as IfCodegenNode
  200. }
  201. })
  202. )
  203. expect(code).toMatch(`return (foo, bar)`)
  204. expect(code).toMatchSnapshot()
  205. })
  206. test('forNode', () => {
  207. const { code } = generate(
  208. createRoot({
  209. codegenNode: {
  210. type: NodeTypes.FOR,
  211. loc: locStub,
  212. source: createSimpleExpression('foo', false),
  213. valueAlias: undefined,
  214. keyAlias: undefined,
  215. objectIndexAlias: undefined,
  216. children: [],
  217. codegenNode: createSequenceExpression([
  218. createSimpleExpression('foo', false),
  219. createSimpleExpression('bar', false)
  220. ]) as ForCodegenNode
  221. }
  222. })
  223. )
  224. expect(code).toMatch(`return (foo, bar)`)
  225. expect(code).toMatchSnapshot()
  226. })
  227. test('Element (callExpression + objectExpression + TemplateChildNode[])', () => {
  228. const { code } = generate(
  229. createRoot({
  230. codegenNode: createElementWithCodegen([
  231. // string
  232. `"div"`,
  233. // ObjectExpression
  234. createObjectExpression(
  235. [
  236. createObjectProperty(
  237. createSimpleExpression(`id`, true, locStub),
  238. createSimpleExpression(`foo`, true, locStub)
  239. ),
  240. createObjectProperty(
  241. createSimpleExpression(`prop`, false, locStub),
  242. createSimpleExpression(`bar`, false, locStub)
  243. ),
  244. // compound expression as computed key
  245. createObjectProperty(
  246. {
  247. type: NodeTypes.COMPOUND_EXPRESSION,
  248. loc: locStub,
  249. children: [
  250. `foo + `,
  251. createSimpleExpression(`bar`, false, locStub)
  252. ]
  253. },
  254. createSimpleExpression(`bar`, false, locStub)
  255. )
  256. ],
  257. locStub
  258. ),
  259. // ChildNode[]
  260. [
  261. createElementWithCodegen([
  262. `"p"`,
  263. createObjectExpression(
  264. [
  265. createObjectProperty(
  266. // should quote the key!
  267. createSimpleExpression(`some-key`, true, locStub),
  268. createSimpleExpression(`foo`, true, locStub)
  269. )
  270. ],
  271. locStub
  272. )
  273. ])
  274. ],
  275. // flag
  276. PatchFlags.FULL_PROPS + ''
  277. ])
  278. })
  279. )
  280. expect(code).toMatch(`
  281. return _${helperNameMap[CREATE_VNODE]}("div", {
  282. id: "foo",
  283. [prop]: bar,
  284. [foo + bar]: bar
  285. }, [
  286. _${helperNameMap[CREATE_VNODE]}("p", { "some-key": "foo" })
  287. ], ${PatchFlags.FULL_PROPS})`)
  288. expect(code).toMatchSnapshot()
  289. })
  290. test('ArrayExpression', () => {
  291. const { code } = generate(
  292. createRoot({
  293. codegenNode: createArrayExpression([
  294. createSimpleExpression(`foo`, false),
  295. createCallExpression(`bar`, [`baz`])
  296. ])
  297. })
  298. )
  299. expect(code).toMatch(`return [
  300. foo,
  301. bar(baz)
  302. ]`)
  303. expect(code).toMatchSnapshot()
  304. })
  305. test('SequenceExpression', () => {
  306. const { code } = generate(
  307. createRoot({
  308. codegenNode: createSequenceExpression([
  309. createSimpleExpression(`foo`, false),
  310. createCallExpression(`bar`, [`baz`])
  311. ])
  312. })
  313. )
  314. expect(code).toMatch(`return (foo, bar(baz))`)
  315. expect(code).toMatchSnapshot()
  316. })
  317. test('ConditionalExpression', () => {
  318. const { code } = generate(
  319. createRoot({
  320. codegenNode: createConditionalExpression(
  321. createSimpleExpression(`ok`, false),
  322. createCallExpression(`foo`),
  323. createConditionalExpression(
  324. createSimpleExpression(`orNot`, false),
  325. createCallExpression(`bar`),
  326. createCallExpression(`baz`)
  327. )
  328. )
  329. })
  330. )
  331. expect(code).toMatch(
  332. `return ok
  333. ? foo()
  334. : orNot
  335. ? bar()
  336. : baz()`
  337. )
  338. expect(code).toMatchSnapshot()
  339. })
  340. })