hoistStatic.spec.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import { BindingTypes } from '@vue/compiler-core'
  2. import type { SFCScriptCompileOptions } from '../../src'
  3. import { assertCode, compileSFCScript } from '../utils'
  4. describe('sfc hoist static', () => {
  5. function compile(src: string, options?: Partial<SFCScriptCompileOptions>) {
  6. return compileSFCScript(src, {
  7. inlineTemplate: true,
  8. hoistStatic: true,
  9. ...options,
  10. })
  11. }
  12. test('should hoist literal value', () => {
  13. const code = `
  14. const string = 'default value'
  15. const number = 123
  16. const boolean = false
  17. const nil = null
  18. const bigint = 100n
  19. const template = \`str\`
  20. `.trim()
  21. const { content, bindings } = compile(`
  22. <script setup>
  23. ${code}
  24. </script>
  25. `)
  26. // should hoist to first line
  27. expect(content.startsWith(code)).toBe(true)
  28. expect(bindings).toStrictEqual({
  29. string: BindingTypes.LITERAL_CONST,
  30. number: BindingTypes.LITERAL_CONST,
  31. boolean: BindingTypes.LITERAL_CONST,
  32. nil: BindingTypes.LITERAL_CONST,
  33. bigint: BindingTypes.LITERAL_CONST,
  34. template: BindingTypes.LITERAL_CONST,
  35. })
  36. assertCode(content)
  37. })
  38. test('should hoist expressions', () => {
  39. const code = `
  40. const unary = !false
  41. const binary = 1 + 2
  42. const conditional = 1 ? 2 : 3
  43. const sequence = (1, true, 'foo', 1)
  44. `.trim()
  45. const { content, bindings } = compile(`
  46. <script setup>
  47. ${code}
  48. </script>
  49. `)
  50. // should hoist to first line
  51. expect(content.startsWith(code)).toBe(true)
  52. expect(bindings).toStrictEqual({
  53. binary: BindingTypes.LITERAL_CONST,
  54. conditional: BindingTypes.LITERAL_CONST,
  55. unary: BindingTypes.LITERAL_CONST,
  56. sequence: BindingTypes.LITERAL_CONST,
  57. })
  58. assertCode(content)
  59. })
  60. test('should hoist w/ defineProps/Emits', () => {
  61. const hoistCode = `const defaultValue = 'default value'`
  62. const { content, bindings } = compile(`
  63. <script setup>
  64. ${hoistCode}
  65. defineProps({
  66. foo: {
  67. default: defaultValue
  68. }
  69. })
  70. </script>
  71. `)
  72. // should hoist to first line
  73. expect(content.startsWith(hoistCode)).toBe(true)
  74. expect(bindings).toStrictEqual({
  75. foo: BindingTypes.PROPS,
  76. defaultValue: BindingTypes.LITERAL_CONST,
  77. })
  78. assertCode(content)
  79. })
  80. test('should not hoist a variable', () => {
  81. const code = `
  82. let KEY1 = 'default value'
  83. var KEY2 = 123
  84. const regex = /.*/g
  85. const undef = undefined
  86. `.trim()
  87. const { content, bindings } = compile(`
  88. <script setup>
  89. ${code}
  90. </script>
  91. `)
  92. expect(bindings).toStrictEqual({
  93. KEY1: BindingTypes.SETUP_LET,
  94. KEY2: BindingTypes.SETUP_LET,
  95. regex: BindingTypes.SETUP_CONST,
  96. undef: BindingTypes.SETUP_MAYBE_REF,
  97. })
  98. expect(content).toMatch(`setup(__props) {\n\n ${code}`)
  99. assertCode(content)
  100. })
  101. test('should not hoist a constant initialized to a reference value', () => {
  102. const code = `
  103. const KEY1 = Boolean
  104. const KEY2 = [Boolean]
  105. const KEY3 = [getCurrentInstance()]
  106. let i = 0;
  107. const KEY4 = (i++, 'foo')
  108. enum KEY5 {
  109. FOO = 1,
  110. BAR = getCurrentInstance(),
  111. }
  112. const KEY6 = \`template\${i}\`
  113. `.trim()
  114. const { content, bindings } = compile(`
  115. <script setup lang="ts">
  116. ${code}
  117. </script>
  118. `)
  119. expect(bindings).toStrictEqual({
  120. KEY1: BindingTypes.SETUP_MAYBE_REF,
  121. KEY2: BindingTypes.SETUP_CONST,
  122. KEY3: BindingTypes.SETUP_CONST,
  123. KEY4: BindingTypes.SETUP_CONST,
  124. KEY5: BindingTypes.SETUP_CONST,
  125. KEY6: BindingTypes.SETUP_CONST,
  126. i: BindingTypes.SETUP_LET,
  127. })
  128. expect(content).toMatch(`setup(__props) {\n\n ${code}`)
  129. assertCode(content)
  130. })
  131. test('should not hoist a object or array', () => {
  132. const code = `
  133. const obj = { foo: 'bar' }
  134. const arr = [1, 2, 3]
  135. `.trim()
  136. const { content, bindings } = compile(`
  137. <script setup>
  138. ${code}
  139. </script>
  140. `)
  141. expect(bindings).toStrictEqual({
  142. arr: BindingTypes.SETUP_CONST,
  143. obj: BindingTypes.SETUP_CONST,
  144. })
  145. expect(content).toMatch(`setup(__props) {\n\n ${code}`)
  146. assertCode(content)
  147. })
  148. test('should not hoist a function or class', () => {
  149. const code = `
  150. const fn = () => {}
  151. function fn2() {}
  152. class Foo {}
  153. `.trim()
  154. const { content, bindings } = compile(`
  155. <script setup>
  156. ${code}
  157. </script>
  158. `)
  159. expect(bindings).toStrictEqual({
  160. Foo: BindingTypes.SETUP_CONST,
  161. fn: BindingTypes.SETUP_CONST,
  162. fn2: BindingTypes.SETUP_CONST,
  163. })
  164. expect(content).toMatch(`setup(__props) {\n\n ${code}`)
  165. assertCode(content)
  166. })
  167. test('should enable when only script setup', () => {
  168. const { content, bindings } = compile(`
  169. <script>
  170. const foo = 'bar'
  171. </script>
  172. <script setup>
  173. const foo = 'bar'
  174. </script>
  175. `)
  176. expect(bindings).toStrictEqual({
  177. foo: BindingTypes.SETUP_CONST,
  178. })
  179. assertCode(content)
  180. })
  181. test('should not hoist when disabled', () => {
  182. const { content, bindings } = compile(
  183. `
  184. <script setup>
  185. const foo = 'bar'
  186. </script>
  187. `,
  188. { hoistStatic: false },
  189. )
  190. expect(bindings).toStrictEqual({
  191. foo: BindingTypes.SETUP_CONST,
  192. })
  193. assertCode(content)
  194. })
  195. test('template binding access in inline mode', () => {
  196. const { content } = compile(
  197. `
  198. <script setup>
  199. const foo = 'bar'
  200. </script>
  201. <template>{{ foo }}</template>
  202. `,
  203. )
  204. expect(content).toMatch('_toDisplayString(foo)')
  205. })
  206. })