compileScriptPropsTransform.spec.ts 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import { BindingTypes } from '@vue/compiler-core'
  2. import { SFCScriptCompileOptions } from '../src'
  3. import { compileSFCScript, assertCode } from './utils'
  4. describe('sfc props transform', () => {
  5. function compile(src: string, options?: Partial<SFCScriptCompileOptions>) {
  6. return compileSFCScript(src, {
  7. inlineTemplate: true,
  8. propsDestructureTransform: true,
  9. ...options
  10. })
  11. }
  12. test('basic usage', () => {
  13. const { content, bindings } = compile(`
  14. <script setup>
  15. const { foo } = defineProps(['foo'])
  16. console.log(foo)
  17. </script>
  18. <template>{{ foo }}</template>
  19. `)
  20. expect(content).not.toMatch(`const { foo } =`)
  21. expect(content).toMatch(`console.log(__props.foo)`)
  22. expect(content).toMatch(`_toDisplayString(__props.foo)`)
  23. assertCode(content)
  24. expect(bindings).toStrictEqual({
  25. foo: BindingTypes.PROPS
  26. })
  27. })
  28. test('nested scope', () => {
  29. const { content, bindings } = compile(`
  30. <script setup>
  31. const { foo, bar } = defineProps(['foo', 'bar'])
  32. function test(foo) {
  33. console.log(foo)
  34. console.log(bar)
  35. }
  36. </script>
  37. `)
  38. expect(content).not.toMatch(`const { foo, bar } =`)
  39. expect(content).toMatch(`console.log(foo)`)
  40. expect(content).toMatch(`console.log(__props.bar)`)
  41. assertCode(content)
  42. expect(bindings).toStrictEqual({
  43. foo: BindingTypes.PROPS,
  44. bar: BindingTypes.PROPS,
  45. test: BindingTypes.SETUP_CONST
  46. })
  47. })
  48. test('default values w/ runtime declaration', () => {
  49. const { content } = compile(`
  50. <script setup>
  51. const { foo = 1, bar = {} } = defineProps(['foo', 'bar'])
  52. </script>
  53. `)
  54. // literals can be used as-is, non-literals are always returned from a
  55. // function
  56. expect(content).toMatch(`props: _mergeDefaults(['foo', 'bar'], {
  57. foo: 1,
  58. bar: () => {}
  59. })`)
  60. assertCode(content)
  61. })
  62. test('default values w/ type declaration', () => {
  63. const { content } = compile(`
  64. <script setup lang="ts">
  65. const { foo = 1, bar = {} } = defineProps<{ foo?: number, bar?: object }>()
  66. </script>
  67. `)
  68. // literals can be used as-is, non-literals are always returned from a
  69. // function
  70. expect(content).toMatch(`props: {
  71. foo: { type: Number, required: false, default: 1 },
  72. bar: { type: Object, required: false, default: () => {} }
  73. }`)
  74. assertCode(content)
  75. })
  76. test('default values w/ type declaration, prod mode', () => {
  77. const { content } = compile(
  78. `
  79. <script setup lang="ts">
  80. const { foo = 1, bar = {} } = defineProps<{ foo?: number, bar?: object, baz?: any }>()
  81. </script>
  82. `,
  83. { isProd: true }
  84. )
  85. // literals can be used as-is, non-literals are always returned from a
  86. // function
  87. expect(content).toMatch(`props: {
  88. foo: { default: 1 },
  89. bar: { default: () => {} },
  90. baz: null
  91. }`)
  92. assertCode(content)
  93. })
  94. test('aliasing', () => {
  95. const { content, bindings } = compile(`
  96. <script setup>
  97. const { foo: bar } = defineProps(['foo'])
  98. let x = foo
  99. let y = bar
  100. </script>
  101. <template>{{ foo + bar }}</template>
  102. `)
  103. expect(content).not.toMatch(`const { foo: bar } =`)
  104. expect(content).toMatch(`let x = foo`) // should not process
  105. expect(content).toMatch(`let y = __props.foo`)
  106. // should convert bar to __props.foo in template expressions
  107. expect(content).toMatch(`_toDisplayString(__props.foo + __props.foo)`)
  108. assertCode(content)
  109. expect(bindings).toStrictEqual({
  110. x: BindingTypes.SETUP_LET,
  111. y: BindingTypes.SETUP_LET,
  112. foo: BindingTypes.PROPS,
  113. bar: BindingTypes.PROPS_ALIASED,
  114. __propsAliases: {
  115. bar: 'foo'
  116. }
  117. })
  118. })
  119. test('rest spread', () => {
  120. const { content, bindings } = compile(`
  121. <script setup>
  122. const { foo, bar, ...rest } = defineProps(['foo', 'bar', 'baz'])
  123. </script>
  124. `)
  125. expect(content).toMatch(
  126. `const rest = _createPropsRestProxy(__props, ["foo","bar"])`
  127. )
  128. assertCode(content)
  129. expect(bindings).toStrictEqual({
  130. foo: BindingTypes.PROPS,
  131. bar: BindingTypes.PROPS,
  132. baz: BindingTypes.PROPS,
  133. rest: BindingTypes.SETUP_CONST
  134. })
  135. })
  136. describe('errors', () => {
  137. test('should error on deep destructure', () => {
  138. expect(() =>
  139. compile(
  140. `<script setup>const { foo: [bar] } = defineProps(['foo'])</script>`
  141. )
  142. ).toThrow(`destructure does not support nested patterns`)
  143. expect(() =>
  144. compile(
  145. `<script setup>const { foo: { bar } } = defineProps(['foo'])</script>`
  146. )
  147. ).toThrow(`destructure does not support nested patterns`)
  148. })
  149. test('should error on computed key', () => {
  150. expect(() =>
  151. compile(
  152. `<script setup>const { [foo]: bar } = defineProps(['foo'])</script>`
  153. )
  154. ).toThrow(`destructure cannot use computed key`)
  155. })
  156. test('should error when used with withDefaults', () => {
  157. expect(() =>
  158. compile(
  159. `<script setup lang="ts">
  160. const { foo } = withDefaults(defineProps<{ foo: string }>(), { foo: 'foo' })
  161. </script>`
  162. )
  163. ).toThrow(`withDefaults() is unnecessary when using destructure`)
  164. })
  165. test('should error if destructure reference local vars', () => {
  166. expect(() =>
  167. compile(
  168. `<script setup>
  169. const x = 1
  170. const {
  171. foo = () => x
  172. } = defineProps(['foo'])
  173. </script>`
  174. )
  175. ).toThrow(`cannot reference locally declared variables`)
  176. })
  177. })
  178. })