compileTemplate.spec.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import {
  2. compileTemplate,
  3. SFCTemplateCompileOptions
  4. } from '../src/compileTemplate'
  5. import { parse, SFCTemplateBlock } from '../src/parse'
  6. function compile(opts: Omit<SFCTemplateCompileOptions, 'id'>) {
  7. return compileTemplate({
  8. ...opts,
  9. id: ''
  10. })
  11. }
  12. test('should work', () => {
  13. const source = `<div><p>{{ render }}</p></div>`
  14. const result = compile({ filename: 'example.vue', source })
  15. expect(result.errors.length).toBe(0)
  16. expect(result.source).toBe(source)
  17. // should expose render fn
  18. expect(result.code).toMatch(`export function render(`)
  19. })
  20. // #6807
  21. test('should work with style comment', () => {
  22. const source = `
  23. <div style="
  24. /* nothing */
  25. width: 300px;
  26. height: 100px/* nothing */
  27. ">{{ render }}</div>
  28. `
  29. const result = compile({ filename: 'example.vue', source })
  30. expect(result.errors.length).toBe(0)
  31. expect(result.source).toBe(source)
  32. expect(result.code).toMatch(`{"width":"300px","height":"100px"}`)
  33. })
  34. test('preprocess pug', () => {
  35. const template = parse(
  36. `
  37. <template lang="pug">
  38. body
  39. h1 Pug Examples
  40. div.container
  41. p Cool Pug example!
  42. </template>
  43. `,
  44. { filename: 'example.vue', sourceMap: true }
  45. ).descriptor.template as SFCTemplateBlock
  46. const result = compile({
  47. filename: 'example.vue',
  48. source: template.content,
  49. preprocessLang: template.lang
  50. })
  51. expect(result.errors.length).toBe(0)
  52. })
  53. test('warn missing preprocessor', () => {
  54. const template = parse(`<template lang="unknownLang">hi</template>\n`, {
  55. filename: 'example.vue',
  56. sourceMap: true
  57. }).descriptor.template as SFCTemplateBlock
  58. const result = compile({
  59. filename: 'example.vue',
  60. source: template.content,
  61. preprocessLang: template.lang
  62. })
  63. expect(result.errors.length).toBe(1)
  64. })
  65. test('transform asset url options', () => {
  66. const input = { source: `<foo bar="~baz"/>`, filename: 'example.vue' }
  67. // Object option
  68. const { code: code1 } = compile({
  69. ...input,
  70. transformAssetUrls: {
  71. tags: { foo: ['bar'] }
  72. }
  73. })
  74. expect(code1).toMatch(`import _imports_0 from 'baz'\n`)
  75. // legacy object option (direct tags config)
  76. const { code: code2 } = compile({
  77. ...input,
  78. transformAssetUrls: {
  79. foo: ['bar']
  80. }
  81. })
  82. expect(code2).toMatch(`import _imports_0 from 'baz'\n`)
  83. // false option
  84. const { code: code3 } = compile({
  85. ...input,
  86. transformAssetUrls: false
  87. })
  88. expect(code3).not.toMatch(`import _imports_0 from 'baz'\n`)
  89. })
  90. test('source map', () => {
  91. const template = parse(
  92. `
  93. <template>
  94. <div><p>{{ render }}</p></div>
  95. </template>
  96. `,
  97. { filename: 'example.vue', sourceMap: true }
  98. ).descriptor.template as SFCTemplateBlock
  99. const result = compile({
  100. filename: 'example.vue',
  101. source: template.content
  102. })
  103. expect(result.map).toMatchSnapshot()
  104. })
  105. test('template errors', () => {
  106. const result = compile({
  107. filename: 'example.vue',
  108. source: `<div :foo
  109. :bar="a[" v-model="baz"/>`
  110. })
  111. expect(result.errors).toMatchSnapshot()
  112. })
  113. test('preprocessor errors', () => {
  114. const template = parse(
  115. `
  116. <template lang="pug">
  117. div(class='class)
  118. </template>
  119. `,
  120. { filename: 'example.vue', sourceMap: true }
  121. ).descriptor.template as SFCTemplateBlock
  122. const result = compile({
  123. filename: 'example.vue',
  124. source: template.content,
  125. preprocessLang: template.lang
  126. })
  127. expect(result.errors.length).toBe(1)
  128. const message = result.errors[0].toString()
  129. expect(message).toMatch(`Error: example.vue:3:1`)
  130. expect(message).toMatch(
  131. `The end of the string reached with no closing bracket ) found.`
  132. )
  133. })
  134. // #3447
  135. test('should generate the correct imports expression', () => {
  136. const { code } = compile({
  137. filename: 'example.vue',
  138. source: `
  139. <img src="./foo.svg"/>
  140. <Comp>
  141. <img src="./bar.svg"/>
  142. </Comp>
  143. `,
  144. ssr: true
  145. })
  146. expect(code).toMatch(`_ssrRenderAttr(\"src\", _imports_1)`)
  147. expect(code).toMatch(`_createVNode(\"img\", { src: _imports_1 })`)
  148. })
  149. // #3874
  150. test('should not hoist srcset URLs in SSR mode', () => {
  151. const { code } = compile({
  152. filename: 'example.vue',
  153. source: `
  154. <picture>
  155. <source srcset="./img/foo.svg"/>
  156. <img src="./img/foo.svg"/>
  157. </picture>
  158. <router-link>
  159. <picture>
  160. <source srcset="./img/bar.svg"/>
  161. <img src="./img/bar.svg"/>
  162. </picture>
  163. </router-link>
  164. `,
  165. ssr: true
  166. })
  167. expect(code).toMatchSnapshot()
  168. })
  169. // #6742
  170. test('dynamic v-on + static v-on should merged', () => {
  171. const source = `<input @blur="onBlur" @[validateEvent]="onValidateEvent">`
  172. const result = compile({ filename: 'example.vue', source })
  173. expect(result.code).toMatchSnapshot()
  174. })