templateUtils.spec.ts 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. import {
  2. isDataUrl,
  3. isExternalUrl,
  4. isRelativeUrl,
  5. } from '../src/template/templateUtils'
  6. import { compileSFCScript as compile } from './utils'
  7. describe('compiler sfc:templateUtils isRelativeUrl', () => {
  8. test('should return true when The first character of the string path is .', () => {
  9. const url = './**.vue'
  10. const result = isRelativeUrl(url)
  11. expect(result).toBe(true)
  12. })
  13. test('should return true when The first character of the string path is ~', () => {
  14. const url = '~/xx.vue'
  15. const result = isRelativeUrl(url)
  16. expect(result).toBe(true)
  17. })
  18. test('should return true when The first character of the string path is @', () => {
  19. const url = '@/xx.vue'
  20. const result = isRelativeUrl(url)
  21. expect(result).toBe(true)
  22. })
  23. })
  24. describe('compiler sfc:templateUtils isExternalUrl', () => {
  25. test('should return true when String starts with http://', () => {
  26. const url = 'http://vuejs.org/'
  27. const result = isExternalUrl(url)
  28. expect(result).toBe(true)
  29. })
  30. test('should return true when String starts with https://', () => {
  31. const url = 'https://vuejs.org/'
  32. const result = isExternalUrl(url)
  33. expect(result).toBe(true)
  34. })
  35. test('should return true when String starts with //', () => {
  36. const url = '//vuejs.org/'
  37. const result = isExternalUrl(url)
  38. expect(result).toBe(true)
  39. })
  40. })
  41. describe('compiler sfc:templateUtils isDataUrl', () => {
  42. test('should return true w/ hasn`t media type and encode', () => {
  43. expect(isDataUrl('data:,i')).toBe(true)
  44. })
  45. test('should return true w/ media type + encode', () => {
  46. expect(isDataUrl('data:image/png;base64,i')).toBe(true)
  47. })
  48. test('should return true w/ media type + hasn`t encode', () => {
  49. expect(isDataUrl('data:image/png,i')).toBe(true)
  50. })
  51. })
  52. describe('multiRoot metadata', () => {
  53. test('marks a non-inline component as multi-root', () => {
  54. const { content } = compile(
  55. `
  56. <script setup vapor>
  57. const ok = true
  58. </script>
  59. <template>
  60. <div />
  61. <div />
  62. </template>
  63. `,
  64. {
  65. inlineTemplate: false,
  66. vapor: true,
  67. },
  68. )
  69. expect(content).toContain(`__multiRoot: true`)
  70. })
  71. test('treats root control flow as a single owner unit', () => {
  72. const { content } = compile(
  73. `
  74. <script setup vapor>
  75. const ok = true
  76. </script>
  77. <template>
  78. <template v-if="ok">
  79. <div />
  80. <div />
  81. </template>
  82. </template>
  83. `,
  84. {
  85. inlineTemplate: false,
  86. vapor: true,
  87. },
  88. )
  89. expect(content).toContain(`__multiRoot: false`)
  90. })
  91. test('treats a root if / else-if / else chain as a single owner unit', () => {
  92. const { content } = compile(
  93. `
  94. <script setup vapor>
  95. const ok = true
  96. const maybe = false
  97. </script>
  98. <template>
  99. <template v-if="ok">
  100. <div />
  101. </template>
  102. <template v-else-if="maybe">
  103. <div />
  104. </template>
  105. <template v-else>
  106. <div />
  107. </template>
  108. </template>
  109. `,
  110. {
  111. inlineTemplate: false,
  112. vapor: true,
  113. },
  114. )
  115. expect(content).toContain(`__multiRoot: false`)
  116. })
  117. test('treats preserved root comments as root units', () => {
  118. const { content } = compile(
  119. `
  120. <script setup vapor>
  121. const ok = true
  122. </script>
  123. <template>
  124. <!-- root comment -->
  125. <div />
  126. </template>
  127. `,
  128. {
  129. inlineTemplate: false,
  130. vapor: true,
  131. },
  132. )
  133. expect(content).toContain(`__multiRoot: true`)
  134. })
  135. test('respects template comments option when inferring multiRoot', () => {
  136. const { content } = compile(
  137. `
  138. <script setup vapor>
  139. const ok = true
  140. </script>
  141. <template>
  142. <!-- root comment -->
  143. <div />
  144. </template>
  145. `,
  146. {
  147. inlineTemplate: false,
  148. vapor: true,
  149. templateOptions: {
  150. compilerOptions: {
  151. comments: false,
  152. },
  153. },
  154. },
  155. )
  156. expect(content).toContain(`__multiRoot: false`)
  157. })
  158. test('ignores comments between root if branches', () => {
  159. const { content } = compile(
  160. `
  161. <script setup vapor>
  162. const ok = true
  163. </script>
  164. <template>
  165. <template v-if="ok">
  166. <div />
  167. </template>
  168. <!-- branch separator -->
  169. <template v-else>
  170. <div />
  171. </template>
  172. </template>
  173. `,
  174. {
  175. inlineTemplate: false,
  176. vapor: true,
  177. },
  178. )
  179. expect(content).toContain(`__multiRoot: false`)
  180. })
  181. test('ignores preserved whitespace between root if branches', () => {
  182. const { content } = compile(
  183. `
  184. <script setup vapor>
  185. const ok = true
  186. </script>
  187. <template>
  188. <template v-if="ok">
  189. <div />
  190. </template>
  191. <template v-else>
  192. <div />
  193. </template>
  194. </template>
  195. `,
  196. {
  197. inlineTemplate: false,
  198. vapor: true,
  199. },
  200. {
  201. templateParseOptions: {
  202. whitespace: 'preserve',
  203. },
  204. },
  205. )
  206. expect(content).toContain(`__multiRoot: false`)
  207. })
  208. test('treats root v-for as a single owner unit', () => {
  209. const { content } = compile(
  210. `
  211. <script setup vapor>
  212. const list = [1, 2]
  213. </script>
  214. <template>
  215. <template v-for="item in list" :key="item">
  216. <div>{{ item }}</div>
  217. </template>
  218. </template>
  219. `,
  220. {
  221. inlineTemplate: false,
  222. vapor: true,
  223. },
  224. )
  225. expect(content).toContain(`__multiRoot: false`)
  226. })
  227. test('treats a root slot outlet as a single owner unit', () => {
  228. const { content } = compile(
  229. `
  230. <script setup vapor>
  231. const ok = true
  232. </script>
  233. <template>
  234. <slot />
  235. </template>
  236. `,
  237. {
  238. inlineTemplate: false,
  239. vapor: true,
  240. },
  241. )
  242. expect(content).toContain(`__multiRoot: false`)
  243. })
  244. test('treats root text with a sibling as multi-root', () => {
  245. const { content } = compile(
  246. `
  247. <script setup vapor>
  248. const msg = 'hello'
  249. </script>
  250. <template>
  251. hello
  252. <div />
  253. </template>
  254. `,
  255. {
  256. inlineTemplate: false,
  257. vapor: true,
  258. },
  259. )
  260. expect(content).toContain(`__multiRoot: true`)
  261. })
  262. test('treats a root component as a single owner unit', () => {
  263. const { content } = compile(
  264. `
  265. <script setup vapor>
  266. const Foo = {}
  267. </script>
  268. <template>
  269. <Foo />
  270. </template>
  271. `,
  272. {
  273. inlineTemplate: false,
  274. vapor: true,
  275. },
  276. )
  277. expect(content).toContain(`__multiRoot: false`)
  278. })
  279. test('treats a root component with a sibling as multi-root', () => {
  280. const { content } = compile(
  281. `
  282. <script setup vapor>
  283. import { KeepAlive } from 'vue'
  284. </script>
  285. <template>
  286. <KeepAlive>
  287. <div />
  288. </KeepAlive>
  289. <span />
  290. </template>
  291. `,
  292. {
  293. inlineTemplate: true,
  294. vapor: true,
  295. },
  296. )
  297. expect(content).toContain(`__multiRoot: true`)
  298. })
  299. test('treats a plain root template element as a single root', () => {
  300. const { content } = compile(
  301. `
  302. <script setup vapor>
  303. const ok = true
  304. </script>
  305. <template>
  306. <template>
  307. <div />
  308. <div />
  309. </template>
  310. </template>
  311. `,
  312. {
  313. inlineTemplate: false,
  314. vapor: true,
  315. },
  316. )
  317. expect(content).toContain(`__multiRoot: false`)
  318. })
  319. test('marks an inline component as multi-root', () => {
  320. const { content } = compile(
  321. `
  322. <script setup vapor>
  323. const ok = true
  324. </script>
  325. <template>
  326. <div />
  327. <div />
  328. </template>
  329. `,
  330. {
  331. inlineTemplate: true,
  332. vapor: true,
  333. },
  334. )
  335. expect(content).toContain(`__multiRoot: true`)
  336. })
  337. })