2
0

codegen.spec.ts 10 KB

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