parse.spec.ts 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import { parse } from '../src'
  2. import { baseParse, baseCompile } from '@vue/compiler-core'
  3. import { SourceMapConsumer } from 'source-map'
  4. describe('compiler:sfc', () => {
  5. describe('source map', () => {
  6. test('style block', () => {
  7. // Padding determines how many blank lines will there be before the style block
  8. const padding = Math.round(Math.random() * 10)
  9. const style = parse(
  10. `${'\n'.repeat(padding)}<style>\n.color {\n color: red;\n }\n</style>\n`
  11. ).descriptor.styles[0]
  12. expect(style.map).not.toBeUndefined()
  13. const consumer = new SourceMapConsumer(style.map!)
  14. consumer.eachMapping(mapping => {
  15. expect(mapping.originalLine - mapping.generatedLine).toBe(padding)
  16. })
  17. })
  18. test('script block', () => {
  19. // Padding determines how many blank lines will there be before the style block
  20. const padding = Math.round(Math.random() * 10)
  21. const script = parse(
  22. `${'\n'.repeat(padding)}<script>\nconsole.log(1)\n }\n</script>\n`
  23. ).descriptor.script
  24. expect(script!.map).not.toBeUndefined()
  25. const consumer = new SourceMapConsumer(script!.map!)
  26. consumer.eachMapping(mapping => {
  27. expect(mapping.originalLine - mapping.generatedLine).toBe(padding)
  28. })
  29. })
  30. })
  31. test('pad content', () => {
  32. const content = `
  33. <template>
  34. <div></div>
  35. </template>
  36. <script>
  37. export default {}
  38. </script>
  39. <style>
  40. h1 { color: red }
  41. </style>`
  42. const padFalse = parse(content.trim(), { pad: false }).descriptor
  43. expect(padFalse.template!.content).toBe('\n<div></div>\n')
  44. expect(padFalse.script!.content).toBe('\nexport default {}\n')
  45. expect(padFalse.styles[0].content).toBe('\nh1 { color: red }\n')
  46. const padTrue = parse(content.trim(), { pad: true }).descriptor
  47. expect(padTrue.script!.content).toBe(
  48. Array(3 + 1).join('//\n') + '\nexport default {}\n'
  49. )
  50. expect(padTrue.styles[0].content).toBe(
  51. Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
  52. )
  53. const padLine = parse(content.trim(), { pad: 'line' }).descriptor
  54. expect(padLine.script!.content).toBe(
  55. Array(3 + 1).join('//\n') + '\nexport default {}\n'
  56. )
  57. expect(padLine.styles[0].content).toBe(
  58. Array(6 + 1).join('\n') + '\nh1 { color: red }\n'
  59. )
  60. const padSpace = parse(content.trim(), { pad: 'space' }).descriptor
  61. expect(padSpace.script!.content).toBe(
  62. `<template>\n<div></div>\n</template>\n<script>`.replace(/./g, ' ') +
  63. '\nexport default {}\n'
  64. )
  65. expect(padSpace.styles[0].content).toBe(
  66. `<template>\n<div></div>\n</template>\n<script>\nexport default {}\n</script>\n<style>`.replace(
  67. /./g,
  68. ' '
  69. ) + '\nh1 { color: red }\n'
  70. )
  71. })
  72. test('should ignore nodes with no content', () => {
  73. expect(parse(`<template/>`).descriptor.template).toBe(null)
  74. expect(parse(`<script/>`).descriptor.script).toBe(null)
  75. expect(parse(`<style/>`).descriptor.styles.length).toBe(0)
  76. expect(parse(`<custom/>`).descriptor.customBlocks.length).toBe(0)
  77. })
  78. test('handle empty nodes with src attribute', () => {
  79. const { descriptor } = parse(`<script src="com"/>`)
  80. expect(descriptor.script).toBeTruthy()
  81. expect(descriptor.script!.content).toBeFalsy()
  82. expect(descriptor.script!.attrs['src']).toBe('com')
  83. })
  84. test('nested templates', () => {
  85. const content = `
  86. <template v-if="ok">ok</template>
  87. <div><div></div></div>
  88. `
  89. const { descriptor } = parse(`<template>${content}</template>`)
  90. expect(descriptor.template!.content).toBe(content)
  91. })
  92. // #1120
  93. test('alternative template lang should be treated as plain text', () => {
  94. const content = `p(v-if="1 < 2") test`
  95. const { descriptor, errors } = parse(
  96. `<template lang="pug">` + content + `</template>`
  97. )
  98. expect(errors.length).toBe(0)
  99. expect(descriptor.template!.content).toBe(content)
  100. })
  101. test('error tolerance', () => {
  102. const { errors } = parse(`<template>`)
  103. expect(errors.length).toBe(1)
  104. })
  105. test('should parse as DOM by default', () => {
  106. const { errors } = parse(`<template><input></template>`)
  107. expect(errors.length).toBe(0)
  108. })
  109. test('custom compiler', () => {
  110. const { errors } = parse(`<template><input></template>`, {
  111. compiler: {
  112. parse: baseParse,
  113. compile: baseCompile
  114. }
  115. })
  116. expect(errors.length).toBe(1)
  117. })
  118. test('treat custom blocks as raw text', () => {
  119. const { errors, descriptor } = parse(`<foo> <-& </foo>`)
  120. expect(errors.length).toBe(0)
  121. expect(descriptor.customBlocks[0].content).toBe(` <-& `)
  122. })
  123. describe('warnings', () => {
  124. function assertWarning(errors: Error[], msg: string) {
  125. expect(errors.some(e => e.message.match(msg))).toBe(true)
  126. }
  127. test('should only allow single template element', () => {
  128. assertWarning(
  129. parse(`<template><div/></template><template><div/></template>`).errors,
  130. `Single file component can contain only one <template> element`
  131. )
  132. })
  133. test('should only allow single script element', () => {
  134. assertWarning(
  135. parse(`<script>console.log(1)</script><script>console.log(1)</script>`)
  136. .errors,
  137. `Single file component can contain only one <script> element`
  138. )
  139. })
  140. test('should only allow single script setup element', () => {
  141. assertWarning(
  142. parse(
  143. `<script setup>console.log(1)</script><script setup>console.log(1)</script>`
  144. ).errors,
  145. `Single file component can contain only one <script setup> element`
  146. )
  147. })
  148. test('should not warn script & script setup', () => {
  149. expect(
  150. parse(
  151. `<script setup>console.log(1)</script><script>console.log(1)</script>`
  152. ).errors.length
  153. ).toBe(0)
  154. })
  155. })
  156. })