codegen.spec.ts 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  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. COMMENT,
  22. TO_STRING,
  23. RESOLVE_DIRECTIVE,
  24. helperNameMap,
  25. RESOLVE_COMPONENT
  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. isEmpty: false,
  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_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(
  172. `return _${helperNameMap[CREATE_VNODE]}(_${
  173. helperNameMap[COMMENT]
  174. }, null, "foo")`
  175. )
  176. expect(code).toMatchSnapshot()
  177. })
  178. test('compound expression', () => {
  179. const { code } = generate(
  180. createRoot({
  181. codegenNode: createCompoundExpression([
  182. `_ctx.`,
  183. createSimpleExpression(`foo`, false, locStub),
  184. ` + `,
  185. {
  186. type: NodeTypes.INTERPOLATION,
  187. loc: locStub,
  188. content: createSimpleExpression(`bar`, false, locStub)
  189. }
  190. ])
  191. })
  192. )
  193. expect(code).toMatch(`return _ctx.foo + _${helperNameMap[TO_STRING]}(bar)`)
  194. expect(code).toMatchSnapshot()
  195. })
  196. test('ifNode', () => {
  197. const { code } = generate(
  198. createRoot({
  199. codegenNode: {
  200. type: NodeTypes.IF,
  201. loc: locStub,
  202. branches: [],
  203. codegenNode: createSequenceExpression([
  204. createSimpleExpression('foo', false),
  205. createSimpleExpression('bar', false)
  206. ]) as IfCodegenNode
  207. }
  208. })
  209. )
  210. expect(code).toMatch(`return (foo, bar)`)
  211. expect(code).toMatchSnapshot()
  212. })
  213. test('forNode', () => {
  214. const { code } = generate(
  215. createRoot({
  216. codegenNode: {
  217. type: NodeTypes.FOR,
  218. loc: locStub,
  219. source: createSimpleExpression('foo', false),
  220. valueAlias: undefined,
  221. keyAlias: undefined,
  222. objectIndexAlias: undefined,
  223. children: [],
  224. codegenNode: createSequenceExpression([
  225. createSimpleExpression('foo', false),
  226. createSimpleExpression('bar', false)
  227. ]) as ForCodegenNode
  228. }
  229. })
  230. )
  231. expect(code).toMatch(`return (foo, bar)`)
  232. expect(code).toMatchSnapshot()
  233. })
  234. test('Element (callExpression + objectExpression + TemplateChildNode[])', () => {
  235. const { code } = generate(
  236. createRoot({
  237. codegenNode: createElementWithCodegen([
  238. // string
  239. `"div"`,
  240. // ObjectExpression
  241. createObjectExpression(
  242. [
  243. createObjectProperty(
  244. createSimpleExpression(`id`, true, locStub),
  245. createSimpleExpression(`foo`, true, locStub)
  246. ),
  247. createObjectProperty(
  248. createSimpleExpression(`prop`, false, locStub),
  249. createSimpleExpression(`bar`, false, locStub)
  250. ),
  251. // compound expression as computed key
  252. createObjectProperty(
  253. {
  254. type: NodeTypes.COMPOUND_EXPRESSION,
  255. loc: locStub,
  256. children: [
  257. `foo + `,
  258. createSimpleExpression(`bar`, false, locStub)
  259. ]
  260. },
  261. createSimpleExpression(`bar`, false, locStub)
  262. )
  263. ],
  264. locStub
  265. ),
  266. // ChildNode[]
  267. [
  268. createElementWithCodegen([
  269. `"p"`,
  270. createObjectExpression(
  271. [
  272. createObjectProperty(
  273. // should quote the key!
  274. createSimpleExpression(`some-key`, true, locStub),
  275. createSimpleExpression(`foo`, true, locStub)
  276. )
  277. ],
  278. locStub
  279. )
  280. ])
  281. ],
  282. // flag
  283. PatchFlags.FULL_PROPS + ''
  284. ])
  285. })
  286. )
  287. expect(code).toMatch(`
  288. return _${helperNameMap[CREATE_VNODE]}("div", {
  289. id: "foo",
  290. [prop]: bar,
  291. [foo + bar]: bar
  292. }, [
  293. _${helperNameMap[CREATE_VNODE]}("p", { "some-key": "foo" })
  294. ], ${PatchFlags.FULL_PROPS})`)
  295. expect(code).toMatchSnapshot()
  296. })
  297. test('ArrayExpression', () => {
  298. const { code } = generate(
  299. createRoot({
  300. codegenNode: createArrayExpression([
  301. createSimpleExpression(`foo`, false),
  302. createCallExpression(`bar`, [`baz`])
  303. ])
  304. })
  305. )
  306. expect(code).toMatch(`return [
  307. foo,
  308. bar(baz)
  309. ]`)
  310. expect(code).toMatchSnapshot()
  311. })
  312. test('SequenceExpression', () => {
  313. const { code } = generate(
  314. createRoot({
  315. codegenNode: createSequenceExpression([
  316. createSimpleExpression(`foo`, false),
  317. createCallExpression(`bar`, [`baz`])
  318. ])
  319. })
  320. )
  321. expect(code).toMatch(`return (foo, bar(baz))`)
  322. expect(code).toMatchSnapshot()
  323. })
  324. test('ConditionalExpression', () => {
  325. const { code } = generate(
  326. createRoot({
  327. codegenNode: createConditionalExpression(
  328. createSimpleExpression(`ok`, false),
  329. createCallExpression(`foo`),
  330. createConditionalExpression(
  331. createSimpleExpression(`orNot`, false),
  332. createCallExpression(`bar`),
  333. createCallExpression(`baz`)
  334. )
  335. )
  336. })
  337. )
  338. expect(code).toMatch(
  339. `return ok
  340. ? foo()
  341. : orNot
  342. ? bar()
  343. : baz()`
  344. )
  345. expect(code).toMatchSnapshot()
  346. })
  347. test('CacheExpression', () => {
  348. const { code } = generate(
  349. createRoot({
  350. cached: 1,
  351. codegenNode: createCacheExpression(
  352. 1,
  353. createSimpleExpression(`foo`, false)
  354. )
  355. }),
  356. {
  357. mode: 'module',
  358. prefixIdentifiers: true
  359. }
  360. )
  361. expect(code).toMatch(`const _cache = _ctx.$cache`)
  362. expect(code).toMatch(`_cache[1] || (_cache[1] = foo)`)
  363. expect(code).toMatchSnapshot()
  364. })
  365. })