compile.spec.ts 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. import { BindingTypes, type RootNode } from '@vue/compiler-dom'
  2. import { type CompilerOptions, compile as _compile } from '../src'
  3. function compile(template: string | RootNode, options: CompilerOptions = {}) {
  4. let { code } = _compile(template, {
  5. ...options,
  6. mode: 'module',
  7. prefixIdentifiers: true,
  8. })
  9. return code
  10. }
  11. describe('compile', () => {
  12. test('static template', () => {
  13. const code = compile(
  14. `<div>
  15. <p>hello</p>
  16. <input />
  17. <span />
  18. </div>`,
  19. )
  20. expect(code).matchSnapshot()
  21. })
  22. test('dynamic root', () => {
  23. const code = compile(`{{ 1 }}{{ 2 }}`)
  24. expect(code).matchSnapshot()
  25. })
  26. test('dynamic root nodes and interpolation', () => {
  27. const code = compile(
  28. `<button @click="handleClick" :id="count">{{count}}foo{{count}}foo{{count}} </button>`,
  29. )
  30. expect(code).matchSnapshot()
  31. })
  32. test('static + dynamic root', () => {
  33. const code = compile(
  34. `{{ 1 }}{{ 2 }}3{{ 4 }}{{ 5 }}6{{ 7 }}{{ 8 }}9{{ 'A' }}{{ 'B' }}`,
  35. )
  36. expect(code).matchSnapshot()
  37. })
  38. test('fragment', () => {
  39. const code = compile(`<p/><span/><div/>`)
  40. expect(code).matchSnapshot()
  41. })
  42. test('bindings', () => {
  43. const code = compile(`<div>count is {{ count }}.</div>`, {
  44. bindingMetadata: {
  45. count: BindingTypes.SETUP_REF,
  46. },
  47. })
  48. expect(code).matchSnapshot()
  49. })
  50. describe('directives', () => {
  51. describe('v-pre', () => {
  52. test('basic', () => {
  53. const code = compile(`<div v-pre :id="foo"><Comp/>{{ bar }}</div>\n`, {
  54. bindingMetadata: {
  55. foo: BindingTypes.SETUP_REF,
  56. bar: BindingTypes.SETUP_REF,
  57. },
  58. })
  59. expect(code).toMatchSnapshot()
  60. expect(code).contains(
  61. JSON.stringify('<div :id=foo><Comp></Comp>{{ bar }}'),
  62. )
  63. expect(code).not.contains('effect')
  64. })
  65. test('should not affect siblings after it', () => {
  66. const code = compile(
  67. `<div v-pre :id="foo"><Comp/>{{ bar }}</div>\n` +
  68. `<div :id="foo"><Comp/>{{ bar }}</div>`,
  69. {
  70. bindingMetadata: {
  71. foo: BindingTypes.SETUP_REF,
  72. bar: BindingTypes.SETUP_REF,
  73. },
  74. },
  75. )
  76. expect(code).toMatchSnapshot()
  77. })
  78. })
  79. describe('v-cloak', () => {
  80. test('basic', () => {
  81. const code = compile(`<div v-cloak>test</div>`)
  82. expect(code).toMatchSnapshot()
  83. expect(code).not.contains('v-cloak')
  84. })
  85. })
  86. describe('custom directive', () => {
  87. test('basic', () => {
  88. const code = compile(`<div v-example></div>`, {
  89. bindingMetadata: {
  90. vExample: BindingTypes.SETUP_CONST,
  91. },
  92. })
  93. expect(code).matchSnapshot()
  94. })
  95. test('binding value', () => {
  96. const code = compile(`<div v-example="msg"></div>`, {
  97. bindingMetadata: {
  98. msg: BindingTypes.SETUP_REF,
  99. vExample: BindingTypes.SETUP_CONST,
  100. },
  101. })
  102. expect(code).matchSnapshot()
  103. })
  104. test('static parameters', () => {
  105. const code = compile(`<div v-example:foo="msg"></div>`, {
  106. bindingMetadata: {
  107. msg: BindingTypes.SETUP_REF,
  108. vExample: BindingTypes.SETUP_CONST,
  109. },
  110. })
  111. expect(code).matchSnapshot()
  112. })
  113. test('modifiers', () => {
  114. const code = compile(`<div v-example.bar="msg"></div>`, {
  115. bindingMetadata: {
  116. msg: BindingTypes.SETUP_REF,
  117. vExample: BindingTypes.SETUP_CONST,
  118. },
  119. })
  120. expect(code).matchSnapshot()
  121. })
  122. test('modifiers w/o binding', () => {
  123. const code = compile(`<div v-example.foo-bar></div>`, {
  124. bindingMetadata: {
  125. vExample: BindingTypes.SETUP_CONST,
  126. },
  127. })
  128. expect(code).matchSnapshot()
  129. })
  130. test('static parameters and modifiers', () => {
  131. const code = compile(`<div v-example:foo.bar="msg"></div>`, {
  132. bindingMetadata: {
  133. msg: BindingTypes.SETUP_REF,
  134. vExample: BindingTypes.SETUP_CONST,
  135. },
  136. })
  137. expect(code).matchSnapshot()
  138. })
  139. test('dynamic parameters', () => {
  140. const code = compile(`<div v-example:[foo]="msg"></div>`, {
  141. bindingMetadata: {
  142. foo: BindingTypes.SETUP_REF,
  143. vExample: BindingTypes.SETUP_CONST,
  144. },
  145. })
  146. expect(code).matchSnapshot()
  147. })
  148. })
  149. })
  150. describe('expression parsing', () => {
  151. test('interpolation', () => {
  152. const code = compile(`{{ a + b }}`, {
  153. inline: true,
  154. bindingMetadata: {
  155. b: BindingTypes.SETUP_REF,
  156. },
  157. })
  158. expect(code).matchSnapshot()
  159. expect(code).contains('a + b.value')
  160. })
  161. test('v-bind', () => {
  162. const code = compile(`<div :[key+1]="foo[key+1]()" />`, {
  163. inline: true,
  164. bindingMetadata: {
  165. key: BindingTypes.SETUP_REF,
  166. foo: BindingTypes.SETUP_MAYBE_REF,
  167. },
  168. })
  169. expect(code).matchSnapshot()
  170. expect(code).contains('const _key = key.value')
  171. expect(code).contains('_key+1')
  172. expect(code).contains(
  173. '_setDynamicProps(n0, [{ [_key+1]: _unref(foo)[_key+1]() }])',
  174. )
  175. })
  176. test('v-on multi statements', () => {
  177. const code = compile(`<div @click="a++;b++" />`, {
  178. prefixIdentifiers: true,
  179. })
  180. expect(code).matchSnapshot()
  181. })
  182. test('v-slot', () => {
  183. const code = compile(`<Comp #foo="{ a, b }">{{ a + b }}</Comp>`, {
  184. prefixIdentifiers: true,
  185. })
  186. expect(code).matchSnapshot()
  187. })
  188. test('v-for', () => {
  189. const code = compile(`<div v-for="({ a, b }, key, index) of a.b" />`, {
  190. prefixIdentifiers: true,
  191. })
  192. expect(code).matchSnapshot()
  193. })
  194. })
  195. describe('custom directive', () => {
  196. test('basic', () => {
  197. const code = compile(`<div v-test v-hello.world />`)
  198. expect(code).matchSnapshot()
  199. })
  200. test('component', () => {
  201. const code = compile(`
  202. <Comp v-test>
  203. <div v-if="true">
  204. <Bar v-hello.world />
  205. </div>
  206. </Comp>
  207. `)
  208. expect(code).matchSnapshot()
  209. })
  210. })
  211. describe('execution order', () => {
  212. test('basic', () => {
  213. const code = compile(`<div :id="foo">{{ bar }}</div>`)
  214. expect(code).matchSnapshot()
  215. expect(code).contains(
  216. `_setProp(n0, "id", _ctx.foo)
  217. _setText(x0, _toDisplayString(_ctx.bar))`,
  218. )
  219. })
  220. describe('setInsertionState', () => {
  221. test('next, child and nthChild should be above the setInsertionState', () => {
  222. const code = compile(`
  223. <div>
  224. <div />
  225. <Comp />
  226. <div />
  227. <div v-if="true" />
  228. <div>
  229. <button :disabled="foo" />
  230. </div>
  231. </div>
  232. `)
  233. expect(code).toMatchSnapshot()
  234. })
  235. })
  236. test('with v-once', () => {
  237. const code = compile(
  238. `<div>
  239. <span v-once>{{ foo }}</span>
  240. {{ bar }}<br>
  241. {{ baz }}
  242. </div>`,
  243. )
  244. expect(code).matchSnapshot()
  245. expect(code).contains(
  246. `_setText(n1, " " + _toDisplayString(_ctx.bar))
  247. _setText(n2, " " + _toDisplayString(_ctx.baz))`,
  248. )
  249. })
  250. test('with insertionState', () => {
  251. const code = compile(`<div><div><slot /></div><Comp/></div>`)
  252. expect(code).matchSnapshot()
  253. })
  254. })
  255. describe('gen unique helper alias', () => {
  256. test('should avoid conflicts with existing variable names', () => {
  257. const code = compile(`<div>{{ foo }}</div>`, {
  258. bindingMetadata: {
  259. _txt: BindingTypes.LITERAL_CONST,
  260. _txt1: BindingTypes.SETUP_REF,
  261. },
  262. })
  263. expect(code).matchSnapshot()
  264. expect(code).contains('txt as _txt2')
  265. expect(code).contains('const x0 = _txt2(n0)')
  266. })
  267. })
  268. describe('gen unique node variables', () => {
  269. test('should avoid binding conflicts for node vars (n*/x*)', () => {
  270. const code = compile(`<div>{{ foo }}</div><div>{{ foo }}</div>`, {
  271. bindingMetadata: {
  272. n0: BindingTypes.SETUP_REACTIVE_CONST,
  273. x0: BindingTypes.SETUP_MAYBE_REF,
  274. n2: BindingTypes.SETUP_REACTIVE_CONST,
  275. x2: BindingTypes.SETUP_MAYBE_REF,
  276. },
  277. })
  278. expect(code).matchSnapshot()
  279. expect(code).not.contains('const n0')
  280. expect(code).not.contains('const x0')
  281. expect(code).not.contains('const n2')
  282. expect(code).not.contains('const x2')
  283. expect(code).contains('const n1 = t0()')
  284. expect(code).contains('const n3 = t0()')
  285. expect(code).contains('const x1 = _txt(n1)')
  286. expect(code).contains('const x3 = _txt(n3)')
  287. })
  288. test('should bump old ref var (r*) on conflict', () => {
  289. const code = compile(
  290. `<div :ref="bar" /><div :ref="bar" /><div :ref="bar" />`,
  291. {
  292. bindingMetadata: {
  293. r0: BindingTypes.SETUP_REF,
  294. r2: BindingTypes.SETUP_REF,
  295. bar: BindingTypes.SETUP_REF,
  296. },
  297. },
  298. )
  299. expect(code).matchSnapshot()
  300. expect(code).not.contains('let r0')
  301. expect(code).not.contains('let r2')
  302. expect(code).contains('_setTemplateRef(n1, _bar)')
  303. expect(code).contains('_setTemplateRef(n3, _bar)')
  304. expect(code).contains('_setTemplateRef(n4, _bar)')
  305. })
  306. test('should bump template var (t*) on conflict', () => {
  307. const code = compile(`<div/><span/><p/>`, {
  308. bindingMetadata: {
  309. t0: BindingTypes.SETUP_REF,
  310. t2: BindingTypes.SETUP_REF,
  311. },
  312. })
  313. expect(code).matchSnapshot()
  314. expect(code).not.contains('const t0 =')
  315. expect(code).not.contains('const t2 =')
  316. expect(code).contains('const t1 = _template("<div>")')
  317. expect(code).contains('const t3 = _template("<span>")')
  318. expect(code).contains('const t4 = _template("<p>")')
  319. })
  320. test('should bump placeholder var (p*) on conflict', () => {
  321. const code = compile(
  322. `<div><div><div><span :id="foo" /></div></div></div>`,
  323. {
  324. bindingMetadata: {
  325. p0: BindingTypes.SETUP_REF,
  326. p2: BindingTypes.SETUP_REF,
  327. foo: BindingTypes.SETUP_REF,
  328. },
  329. },
  330. )
  331. expect(code).matchSnapshot()
  332. expect(code).not.contains('const p0 = ')
  333. expect(code).not.contains('const p2 = ')
  334. expect(code).contains('const p1 = ')
  335. expect(code).contains('const p3 = ')
  336. })
  337. })
  338. })