codegen.spec.ts 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  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. createTemplateLiteral,
  19. createBlockStatement,
  20. createIfStatement,
  21. createAssignmentExpression
  22. } from '../src'
  23. import {
  24. CREATE_VNODE,
  25. TO_DISPLAY_STRING,
  26. RESOLVE_DIRECTIVE,
  27. helperNameMap,
  28. RESOLVE_COMPONENT,
  29. CREATE_COMMENT
  30. } from '../src/runtimeHelpers'
  31. import { createElementWithCodegen } from './testUtils'
  32. import { PatchFlags } from '@vue/shared'
  33. function createRoot(options: Partial<RootNode> = {}): RootNode {
  34. return {
  35. type: NodeTypes.ROOT,
  36. children: [],
  37. helpers: [],
  38. components: [],
  39. directives: [],
  40. imports: [],
  41. hoists: [],
  42. cached: 0,
  43. temps: 0,
  44. codegenNode: createSimpleExpression(`null`, false),
  45. loc: locStub,
  46. ...options
  47. }
  48. }
  49. describe('compiler: codegen', () => {
  50. test('module mode preamble', () => {
  51. const root = createRoot({
  52. helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
  53. })
  54. const { code } = generate(root, { mode: 'module' })
  55. expect(code).toMatch(
  56. `import { ${helperNameMap[CREATE_VNODE]}, ${
  57. helperNameMap[RESOLVE_DIRECTIVE]
  58. } } from "vue"`
  59. )
  60. expect(code).toMatchSnapshot()
  61. })
  62. test('function mode preamble', () => {
  63. const root = createRoot({
  64. helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
  65. })
  66. const { code } = generate(root, { mode: 'function' })
  67. expect(code).toMatch(`const _Vue = Vue`)
  68. expect(code).toMatch(
  69. `const { ${helperNameMap[CREATE_VNODE]}: _${
  70. helperNameMap[CREATE_VNODE]
  71. }, ${helperNameMap[RESOLVE_DIRECTIVE]}: _${
  72. helperNameMap[RESOLVE_DIRECTIVE]
  73. } } = _Vue`
  74. )
  75. expect(code).toMatchSnapshot()
  76. })
  77. test('function mode preamble w/ prefixIdentifiers: true', () => {
  78. const root = createRoot({
  79. helpers: [CREATE_VNODE, RESOLVE_DIRECTIVE]
  80. })
  81. const { code } = generate(root, {
  82. mode: 'function',
  83. prefixIdentifiers: true
  84. })
  85. expect(code).not.toMatch(`const _Vue = Vue`)
  86. expect(code).toMatch(
  87. `const { ${helperNameMap[CREATE_VNODE]}, ${
  88. helperNameMap[RESOLVE_DIRECTIVE]
  89. } } = Vue`
  90. )
  91. expect(code).toMatchSnapshot()
  92. })
  93. test('assets', () => {
  94. const root = createRoot({
  95. components: [`Foo`, `bar-baz`, `barbaz`],
  96. directives: [`my_dir`]
  97. })
  98. const { code } = generate(root, { mode: 'function' })
  99. expect(code).toMatch(
  100. `const _component_Foo = _${helperNameMap[RESOLVE_COMPONENT]}("Foo")\n`
  101. )
  102. expect(code).toMatch(
  103. `const _component_bar_baz = _${
  104. helperNameMap[RESOLVE_COMPONENT]
  105. }("bar-baz")\n`
  106. )
  107. expect(code).toMatch(
  108. `const _component_barbaz = _${
  109. helperNameMap[RESOLVE_COMPONENT]
  110. }("barbaz")\n`
  111. )
  112. expect(code).toMatch(
  113. `const _directive_my_dir = _${
  114. helperNameMap[RESOLVE_DIRECTIVE]
  115. }("my_dir")\n`
  116. )
  117. expect(code).toMatchSnapshot()
  118. })
  119. test('hoists', () => {
  120. const root = createRoot({
  121. hoists: [
  122. createSimpleExpression(`hello`, false, locStub),
  123. createObjectExpression(
  124. [
  125. createObjectProperty(
  126. createSimpleExpression(`id`, true, locStub),
  127. createSimpleExpression(`foo`, true, locStub)
  128. )
  129. ],
  130. locStub
  131. )
  132. ]
  133. })
  134. const { code } = generate(root)
  135. expect(code).toMatch(`const _hoisted_1 = hello`)
  136. expect(code).toMatch(`const _hoisted_2 = { id: "foo" }`)
  137. expect(code).toMatchSnapshot()
  138. })
  139. test('temps', () => {
  140. const root = createRoot({
  141. temps: 3
  142. })
  143. const { code } = generate(root)
  144. expect(code).toMatch(`let _temp0, _temp1, _temp2`)
  145. expect(code).toMatchSnapshot()
  146. })
  147. test('prefixIdentifiers: true should inject _ctx statement', () => {
  148. const { code } = generate(createRoot(), { prefixIdentifiers: true })
  149. expect(code).toMatch(`const _ctx = this\n`)
  150. expect(code).toMatchSnapshot()
  151. })
  152. test('static text', () => {
  153. const { code } = generate(
  154. createRoot({
  155. codegenNode: {
  156. type: NodeTypes.TEXT,
  157. content: 'hello',
  158. loc: locStub
  159. }
  160. })
  161. )
  162. expect(code).toMatch(`return "hello"`)
  163. expect(code).toMatchSnapshot()
  164. })
  165. test('interpolation', () => {
  166. const { code } = generate(
  167. createRoot({
  168. codegenNode: createInterpolation(`hello`, locStub)
  169. })
  170. )
  171. expect(code).toMatch(`return _${helperNameMap[TO_DISPLAY_STRING]}(hello)`)
  172. expect(code).toMatchSnapshot()
  173. })
  174. test('comment', () => {
  175. const { code } = generate(
  176. createRoot({
  177. codegenNode: {
  178. type: NodeTypes.COMMENT,
  179. content: 'foo',
  180. loc: locStub
  181. }
  182. })
  183. )
  184. expect(code).toMatch(`return _${helperNameMap[CREATE_COMMENT]}("foo")`)
  185. expect(code).toMatchSnapshot()
  186. })
  187. test('compound expression', () => {
  188. const { code } = generate(
  189. createRoot({
  190. codegenNode: createCompoundExpression([
  191. `_ctx.`,
  192. createSimpleExpression(`foo`, false, locStub),
  193. ` + `,
  194. {
  195. type: NodeTypes.INTERPOLATION,
  196. loc: locStub,
  197. content: createSimpleExpression(`bar`, false, locStub)
  198. },
  199. // nested compound
  200. createCompoundExpression([` + `, `nested`])
  201. ])
  202. })
  203. )
  204. expect(code).toMatch(
  205. `return _ctx.foo + _${helperNameMap[TO_DISPLAY_STRING]}(bar) + nested`
  206. )
  207. expect(code).toMatchSnapshot()
  208. })
  209. test('ifNode', () => {
  210. const { code } = generate(
  211. createRoot({
  212. codegenNode: {
  213. type: NodeTypes.IF,
  214. loc: locStub,
  215. branches: [],
  216. codegenNode: createSequenceExpression([
  217. createSimpleExpression('foo', false),
  218. createSimpleExpression('bar', false)
  219. ]) as IfCodegenNode
  220. }
  221. })
  222. )
  223. expect(code).toMatch(`return (foo, bar)`)
  224. expect(code).toMatchSnapshot()
  225. })
  226. test('forNode', () => {
  227. const { code } = generate(
  228. createRoot({
  229. codegenNode: {
  230. type: NodeTypes.FOR,
  231. loc: locStub,
  232. source: createSimpleExpression('foo', false),
  233. valueAlias: undefined,
  234. keyAlias: undefined,
  235. objectIndexAlias: undefined,
  236. children: [],
  237. parseResult: {} as any,
  238. codegenNode: createSequenceExpression([
  239. createSimpleExpression('foo', false),
  240. createSimpleExpression('bar', false)
  241. ]) as ForCodegenNode
  242. }
  243. })
  244. )
  245. expect(code).toMatch(`return (foo, bar)`)
  246. expect(code).toMatchSnapshot()
  247. })
  248. test('Element (callExpression + objectExpression + TemplateChildNode[])', () => {
  249. const { code } = generate(
  250. createRoot({
  251. codegenNode: createElementWithCodegen([
  252. // string
  253. `"div"`,
  254. // ObjectExpression
  255. createObjectExpression(
  256. [
  257. createObjectProperty(
  258. createSimpleExpression(`id`, true, locStub),
  259. createSimpleExpression(`foo`, true, locStub)
  260. ),
  261. createObjectProperty(
  262. createSimpleExpression(`prop`, false, locStub),
  263. createSimpleExpression(`bar`, false, locStub)
  264. ),
  265. // compound expression as computed key
  266. createObjectProperty(
  267. {
  268. type: NodeTypes.COMPOUND_EXPRESSION,
  269. loc: locStub,
  270. children: [
  271. `foo + `,
  272. createSimpleExpression(`bar`, false, locStub)
  273. ]
  274. },
  275. createSimpleExpression(`bar`, false, locStub)
  276. )
  277. ],
  278. locStub
  279. ),
  280. // ChildNode[]
  281. [
  282. createElementWithCodegen([
  283. `"p"`,
  284. createObjectExpression(
  285. [
  286. createObjectProperty(
  287. // should quote the key!
  288. createSimpleExpression(`some-key`, true, locStub),
  289. createSimpleExpression(`foo`, true, locStub)
  290. )
  291. ],
  292. locStub
  293. )
  294. ])
  295. ],
  296. // flag
  297. PatchFlags.FULL_PROPS + ''
  298. ])
  299. })
  300. )
  301. expect(code).toMatch(`
  302. return _${helperNameMap[CREATE_VNODE]}("div", {
  303. id: "foo",
  304. [prop]: bar,
  305. [foo + bar]: bar
  306. }, [
  307. _${helperNameMap[CREATE_VNODE]}("p", { "some-key": "foo" })
  308. ], ${PatchFlags.FULL_PROPS})`)
  309. expect(code).toMatchSnapshot()
  310. })
  311. test('ArrayExpression', () => {
  312. const { code } = generate(
  313. createRoot({
  314. codegenNode: createArrayExpression([
  315. createSimpleExpression(`foo`, false),
  316. createCallExpression(`bar`, [`baz`])
  317. ])
  318. })
  319. )
  320. expect(code).toMatch(`return [
  321. foo,
  322. bar(baz)
  323. ]`)
  324. expect(code).toMatchSnapshot()
  325. })
  326. test('SequenceExpression', () => {
  327. const { code } = generate(
  328. createRoot({
  329. codegenNode: createSequenceExpression([
  330. createSimpleExpression(`foo`, false),
  331. createCallExpression(`bar`, [`baz`])
  332. ])
  333. })
  334. )
  335. expect(code).toMatch(`return (foo, bar(baz))`)
  336. expect(code).toMatchSnapshot()
  337. })
  338. test('ConditionalExpression', () => {
  339. const { code } = generate(
  340. createRoot({
  341. codegenNode: createConditionalExpression(
  342. createSimpleExpression(`ok`, false),
  343. createCallExpression(`foo`),
  344. createConditionalExpression(
  345. createSimpleExpression(`orNot`, false),
  346. createCallExpression(`bar`),
  347. createCallExpression(`baz`)
  348. )
  349. )
  350. })
  351. )
  352. expect(code).toMatch(
  353. `return ok
  354. ? foo()
  355. : orNot
  356. ? bar()
  357. : baz()`
  358. )
  359. expect(code).toMatchSnapshot()
  360. })
  361. test('CacheExpression', () => {
  362. const { code } = generate(
  363. createRoot({
  364. cached: 1,
  365. codegenNode: createCacheExpression(
  366. 1,
  367. createSimpleExpression(`foo`, false)
  368. )
  369. }),
  370. {
  371. mode: 'module',
  372. prefixIdentifiers: true
  373. }
  374. )
  375. expect(code).toMatch(`const _cache = _ctx.$cache`)
  376. expect(code).toMatch(`_cache[1] || (_cache[1] = foo)`)
  377. expect(code).toMatchSnapshot()
  378. })
  379. test('CacheExpression w/ isVNode: true', () => {
  380. const { code } = generate(
  381. createRoot({
  382. cached: 1,
  383. codegenNode: createCacheExpression(
  384. 1,
  385. createSimpleExpression(`foo`, false),
  386. true
  387. )
  388. }),
  389. {
  390. mode: 'module',
  391. prefixIdentifiers: true
  392. }
  393. )
  394. expect(code).toMatch(`const _cache = _ctx.$cache`)
  395. expect(code).toMatch(
  396. `
  397. _cache[1] || (
  398. setBlockTracking(-1),
  399. _cache[1] = foo,
  400. setBlockTracking(1),
  401. _cache[1]
  402. )
  403. `.trim()
  404. )
  405. expect(code).toMatchSnapshot()
  406. })
  407. test('TemplateLiteral', () => {
  408. const { code } = generate(
  409. createRoot({
  410. codegenNode: createCallExpression(`_push`, [
  411. createTemplateLiteral([
  412. `foo`,
  413. createCallExpression(`_renderAttr`, ['id', 'foo']),
  414. `bar`
  415. ])
  416. ])
  417. }),
  418. { ssr: true, mode: 'module' }
  419. )
  420. expect(code).toMatchInlineSnapshot(`
  421. "
  422. export function ssrRender(_ctx, _push, _parent) {
  423. _push(\`foo\${_renderAttr(id, foo)}bar\`)
  424. }"
  425. `)
  426. })
  427. describe('IfStatement', () => {
  428. test('if', () => {
  429. const { code } = generate(
  430. createRoot({
  431. codegenNode: createBlockStatement([
  432. createIfStatement(
  433. createSimpleExpression('foo', false),
  434. createBlockStatement([createCallExpression(`ok`)])
  435. )
  436. ])
  437. }),
  438. { ssr: true, mode: 'module' }
  439. )
  440. expect(code).toMatchInlineSnapshot(`
  441. "
  442. export function ssrRender(_ctx, _push, _parent) {
  443. if (foo) {
  444. ok()
  445. }
  446. }"
  447. `)
  448. })
  449. test('if/else', () => {
  450. const { code } = generate(
  451. createRoot({
  452. codegenNode: createBlockStatement([
  453. createIfStatement(
  454. createSimpleExpression('foo', false),
  455. createBlockStatement([createCallExpression(`foo`)]),
  456. createBlockStatement([createCallExpression('bar')])
  457. )
  458. ])
  459. }),
  460. { ssr: true, mode: 'module' }
  461. )
  462. expect(code).toMatchInlineSnapshot(`
  463. "
  464. export function ssrRender(_ctx, _push, _parent) {
  465. if (foo) {
  466. foo()
  467. } else {
  468. bar()
  469. }
  470. }"
  471. `)
  472. })
  473. test('if/else-if', () => {
  474. const { code } = generate(
  475. createRoot({
  476. codegenNode: createBlockStatement([
  477. createIfStatement(
  478. createSimpleExpression('foo', false),
  479. createBlockStatement([createCallExpression(`foo`)]),
  480. createIfStatement(
  481. createSimpleExpression('bar', false),
  482. createBlockStatement([createCallExpression(`bar`)])
  483. )
  484. )
  485. ])
  486. }),
  487. { ssr: true, mode: 'module' }
  488. )
  489. expect(code).toMatchInlineSnapshot(`
  490. "
  491. export function ssrRender(_ctx, _push, _parent) {
  492. if (foo) {
  493. foo()
  494. } else if (bar) {
  495. bar()
  496. }
  497. }"
  498. `)
  499. })
  500. test('if/else-if/else', () => {
  501. const { code } = generate(
  502. createRoot({
  503. codegenNode: createBlockStatement([
  504. createIfStatement(
  505. createSimpleExpression('foo', false),
  506. createBlockStatement([createCallExpression(`foo`)]),
  507. createIfStatement(
  508. createSimpleExpression('bar', false),
  509. createBlockStatement([createCallExpression(`bar`)]),
  510. createBlockStatement([createCallExpression('baz')])
  511. )
  512. )
  513. ])
  514. }),
  515. { ssr: true, mode: 'module' }
  516. )
  517. expect(code).toMatchInlineSnapshot(`
  518. "
  519. export function ssrRender(_ctx, _push, _parent) {
  520. if (foo) {
  521. foo()
  522. } else if (bar) {
  523. bar()
  524. } else {
  525. baz()
  526. }
  527. }"
  528. `)
  529. })
  530. })
  531. test('AssignmentExpression', () => {
  532. const { code } = generate(
  533. createRoot({
  534. codegenNode: createAssignmentExpression(
  535. createSimpleExpression(`foo`, false),
  536. createSimpleExpression(`bar`, false)
  537. )
  538. })
  539. )
  540. expect(code).toMatchInlineSnapshot(`
  541. "
  542. return function render() {
  543. with (this) {
  544. return foo = bar
  545. }
  546. }"
  547. `)
  548. })
  549. })