compileTemplate.spec.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  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('preprocess pug with indents and blank lines', () => {
  54. const template = parse(
  55. `
  56. <template lang="pug">
  57. body
  58. h1 The next line contains four spaces.
  59. div.container
  60. p The next line is empty.
  61. p This is the last line.
  62. </template>
  63. `,
  64. { filename: 'example.vue', sourceMap: true }
  65. ).descriptor.template as SFCTemplateBlock
  66. const result = compile({
  67. filename: 'example.vue',
  68. source: template.content,
  69. preprocessLang: template.lang
  70. })
  71. expect(result.errors.length).toBe(0)
  72. expect(result.source).toBe(
  73. '<body><h1>The next line contains four spaces.</h1><div class="container"><p>The next line is empty.</p></div><p>This is the last line.</p></body>'
  74. )
  75. })
  76. test('warn missing preprocessor', () => {
  77. const template = parse(`<template lang="unknownLang">hi</template>\n`, {
  78. filename: 'example.vue',
  79. sourceMap: true
  80. }).descriptor.template as SFCTemplateBlock
  81. const result = compile({
  82. filename: 'example.vue',
  83. source: template.content,
  84. preprocessLang: template.lang
  85. })
  86. expect(result.errors.length).toBe(1)
  87. })
  88. test('transform asset url options', () => {
  89. const input = { source: `<foo bar="~baz"/>`, filename: 'example.vue' }
  90. // Object option
  91. const { code: code1 } = compile({
  92. ...input,
  93. transformAssetUrls: {
  94. tags: { foo: ['bar'] }
  95. }
  96. })
  97. expect(code1).toMatch(`import _imports_0 from 'baz'\n`)
  98. // legacy object option (direct tags config)
  99. const { code: code2 } = compile({
  100. ...input,
  101. transformAssetUrls: {
  102. foo: ['bar']
  103. }
  104. })
  105. expect(code2).toMatch(`import _imports_0 from 'baz'\n`)
  106. // false option
  107. const { code: code3 } = compile({
  108. ...input,
  109. transformAssetUrls: false
  110. })
  111. expect(code3).not.toMatch(`import _imports_0 from 'baz'\n`)
  112. })
  113. test('source map', () => {
  114. const template = parse(
  115. `
  116. <template>
  117. <div><p>{{ render }}</p></div>
  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. })
  126. expect(result.map).toMatchSnapshot()
  127. })
  128. test('template errors', () => {
  129. const result = compile({
  130. filename: 'example.vue',
  131. source: `<div :foo
  132. :bar="a[" v-model="baz"/>`
  133. })
  134. expect(result.errors).toMatchSnapshot()
  135. })
  136. test('preprocessor errors', () => {
  137. const template = parse(
  138. `
  139. <template lang="pug">
  140. div(class='class)
  141. </template>
  142. `,
  143. { filename: 'example.vue', sourceMap: true }
  144. ).descriptor.template as SFCTemplateBlock
  145. const result = compile({
  146. filename: 'example.vue',
  147. source: template.content,
  148. preprocessLang: template.lang
  149. })
  150. expect(result.errors.length).toBe(1)
  151. const message = result.errors[0].toString()
  152. expect(message).toMatch(`Error: example.vue:3:1`)
  153. expect(message).toMatch(
  154. `The end of the string reached with no closing bracket ) found.`
  155. )
  156. })
  157. // #3447
  158. test('should generate the correct imports expression', () => {
  159. const { code } = compile({
  160. filename: 'example.vue',
  161. source: `
  162. <img src="./foo.svg"/>
  163. <Comp>
  164. <img src="./bar.svg"/>
  165. </Comp>
  166. `,
  167. ssr: true
  168. })
  169. expect(code).toMatch(`_ssrRenderAttr(\"src\", _imports_1)`)
  170. expect(code).toMatch(`_createVNode(\"img\", { src: _imports_1 })`)
  171. })
  172. // #3874
  173. test('should not hoist srcset URLs in SSR mode', () => {
  174. const { code } = compile({
  175. filename: 'example.vue',
  176. source: `
  177. <picture>
  178. <source srcset="./img/foo.svg"/>
  179. <img src="./img/foo.svg"/>
  180. </picture>
  181. <router-link>
  182. <picture>
  183. <source srcset="./img/bar.svg"/>
  184. <img src="./img/bar.svg"/>
  185. </picture>
  186. </router-link>
  187. `,
  188. ssr: true
  189. })
  190. expect(code).toMatchSnapshot()
  191. })
  192. // #6742
  193. test('dynamic v-on + static v-on should merged', () => {
  194. const source = `<input @blur="onBlur" @[validateEvent]="onValidateEvent">`
  195. const result = compile({ filename: 'example.vue', source })
  196. expect(result.code).toMatchSnapshot()
  197. })