codegen.spec.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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. createCacheExpression
  18. } from '../src'
  19. import {
  20. CREATE_VNODE,
  21. TO_DISPLAY_STRING,
  22. RESOLVE_DIRECTIVE,
  23. helperNameMap,
  24. RESOLVE_COMPONENT,
  25. CREATE_COMMENT
  26. } from '../src/runtimeHelpers'
  27. import { createElementWithCodegen } from './testUtils'
  28. import { PatchFlags } from '@vue/shared'
  29. function createRoot(options: Partial<RootNode> = {}): RootNode {
  30. return {
  31. type: NodeTypes.ROOT,
  32. children: [],
  33. helpers: [],
  34. components: [],
  35. directives: [],
  36. imports: [],
  37. hoists: [],
  38. cached: 0,
  39. codegenNode: createSimpleExpression(`null`, false),
  40. loc: locStub,
  41. ...options
  42. }
  43. }
  44. describe('compiler: codegen', () => {
  45. test('module mode preamble', () => {
  46. const root = createRoot({
  47. helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
  48. })
  49. const { code } = generate(root, { mode: 'module' })
  50. expect(code).toMatch(
  51. `import { ${helperNameMap[CREATE_VNODE]}, ${
  52. helperNameMap[RESOLVE_DIRECTIVE]
  53. } } from "vue"`
  54. )
  55. expect(code).toMatchSnapshot()
  56. })
  57. test('function mode preamble', () => {
  58. const root = createRoot({
  59. helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
  60. })
  61. const { code } = generate(root, { mode: 'function' })
  62. expect(code).toMatch(`const _Vue = Vue`)
  63. expect(code).toMatch(
  64. `const { ${helperNameMap[CREATE_VNODE]}: _${
  65. helperNameMap[CREATE_VNODE]
  66. }, ${helperNameMap[RESOLVE_DIRECTIVE]}: _${
  67. helperNameMap[RESOLVE_DIRECTIVE]
  68. } } = _Vue`
  69. )
  70. expect(code).toMatchSnapshot()
  71. })
  72. test('function mode preamble w/ prefixIdentifiers: true', () => {
  73. const root = createRoot({
  74. helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
  75. })
  76. const { code } = generate(root, {
  77. mode: 'function',
  78. prefixIdentifiers: true
  79. })
  80. expect(code).not.toMatch(`const _Vue = Vue`)
  81. expect(code).toMatch(
  82. `const { ${helperNameMap[CREATE_VNODE]}, ${
  83. helperNameMap[RESOLVE_DIRECTIVE]
  84. } } = Vue`
  85. )
  86. expect(code).toMatchSnapshot()
  87. })
  88. test('assets', () => {
  89. const root = createRoot({
  90. components: [`Foo`, `bar-baz`, `barbaz`],
  91. directives: [`my_dir`]
  92. })
  93. const { code } = generate(root, { mode: 'function' })
  94. expect(code).toMatch(
  95. `const _component_Foo = _${helperNameMap[RESOLVE_COMPONENT]}("Foo")\n`
  96. )
  97. expect(code).toMatch(
  98. `const _component_bar_baz = _${
  99. helperNameMap[RESOLVE_COMPONENT]
  100. }("bar-baz")\n`
  101. )
  102. expect(code).toMatch(
  103. `const _component_barbaz = _${
  104. helperNameMap[RESOLVE_COMPONENT]
  105. }("barbaz")\n`
  106. )
  107. expect(code).toMatch(
  108. `const _directive_my_dir = _${
  109. helperNameMap[RESOLVE_DIRECTIVE]
  110. }("my_dir")\n`
  111. )
  112. expect(code).toMatchSnapshot()
  113. })
  114. test('hoists', () => {
  115. const root = createRoot({
  116. hoists: [
  117. createSimpleExpression(`hello`, false, locStub),
  118. createObjectExpression(
  119. [
  120. createObjectProperty(
  121. createSimpleExpression(`id`, true, locStub),
  122. createSimpleExpression(`foo`, true, locStub)
  123. )
  124. ],
  125. locStub
  126. )
  127. ]
  128. })
  129. const { code } = generate(root)
  130. expect(code).toMatch(`const _hoisted_1 = hello`)
  131. expect(code).toMatch(`const _hoisted_2 = { id: "foo" }`)
  132. expect(code).toMatchSnapshot()
  133. })
  134. test('prefixIdentifiers: true should inject _ctx statement', () => {
  135. const { code } = generate(createRoot(), { prefixIdentifiers: true })
  136. expect(code).toMatch(`const _ctx = this\n`)
  137. expect(code).toMatchSnapshot()
  138. })
  139. test('static text', () => {
  140. const { code } = generate(
  141. createRoot({
  142. codegenNode: {
  143. type: NodeTypes.TEXT,
  144. content: 'hello',
  145. loc: locStub
  146. }
  147. })
  148. )
  149. expect(code).toMatch(`return "hello"`)
  150. expect(code).toMatchSnapshot()
  151. })
  152. test('interpolation', () => {
  153. const { code } = generate(
  154. createRoot({
  155. codegenNode: createInterpolation(`hello`, locStub)
  156. })
  157. )
  158. expect(code).toMatch(`return _${helperNameMap[TO_DISPLAY_STRING]}(hello)`)
  159. expect(code).toMatchSnapshot()
  160. })
  161. test('comment', () => {
  162. const { code } = generate(
  163. createRoot({
  164. codegenNode: {
  165. type: NodeTypes.COMMENT,
  166. content: 'foo',
  167. loc: locStub
  168. }
  169. })
  170. )
  171. expect(code).toMatch(`return _${helperNameMap[CREATE_COMMENT]}("foo")`)
  172. expect(code).toMatchSnapshot()
  173. })
  174. test('compound expression', () => {
  175. const { code } = generate(
  176. createRoot({
  177. codegenNode: createCompoundExpression([
  178. `_ctx.`,
  179. createSimpleExpression(`foo`, false, locStub),
  180. ` + `,
  181. {
  182. type: NodeTypes.INTERPOLATION,
  183. loc: locStub,
  184. content: createSimpleExpression(`bar`, false, locStub)
  185. }
  186. ])
  187. })
  188. )
  189. expect(code).toMatch(
  190. `return _ctx.foo + _${helperNameMap[TO_DISPLAY_STRING]}(bar)`
  191. )
  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. test('CacheExpression', () => {
  346. const { code } = generate(
  347. createRoot({
  348. cached: 1,
  349. codegenNode: createCacheExpression(
  350. 1,
  351. createSimpleExpression(`foo`, false)
  352. )
  353. }),
  354. {
  355. mode: 'module',
  356. prefixIdentifiers: true
  357. }
  358. )
  359. expect(code).toMatch(`const _cache = _ctx.$cache`)
  360. expect(code).toMatch(`_cache[1] || (_cache[1] = foo)`)
  361. expect(code).toMatchSnapshot()
  362. })
  363. test('CacheExpression w/ isVNode: true', () => {
  364. const { code } = generate(
  365. createRoot({
  366. cached: 1,
  367. codegenNode: createCacheExpression(
  368. 1,
  369. createSimpleExpression(`foo`, false),
  370. true
  371. )
  372. }),
  373. {
  374. mode: 'module',
  375. prefixIdentifiers: true
  376. }
  377. )
  378. expect(code).toMatch(`const _cache = _ctx.$cache`)
  379. expect(code).toMatch(
  380. `
  381. _cache[1] || (
  382. setBlockTracking(-1),
  383. _cache[1] = foo,
  384. setBlockTracking(1),
  385. _cache[1]
  386. )
  387. `.trim()
  388. )
  389. expect(code).toMatchSnapshot()
  390. })
  391. })