compileScriptPropsTransform.spec.ts 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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, boola?: boolean, boolb?: boolean | number }>()
  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. boola: { type: Boolean },
  92. boolb: { type: [Boolean, Number] }
  93. }`)
  94. assertCode(content)
  95. })
  96. test('aliasing', () => {
  97. const { content, bindings } = compile(`
  98. <script setup>
  99. const { foo: bar } = defineProps(['foo'])
  100. let x = foo
  101. let y = bar
  102. </script>
  103. <template>{{ foo + bar }}</template>
  104. `)
  105. expect(content).not.toMatch(`const { foo: bar } =`)
  106. expect(content).toMatch(`let x = foo`) // should not process
  107. expect(content).toMatch(`let y = __props.foo`)
  108. // should convert bar to __props.foo in template expressions
  109. expect(content).toMatch(`_toDisplayString(__props.foo + __props.foo)`)
  110. assertCode(content)
  111. expect(bindings).toStrictEqual({
  112. x: BindingTypes.SETUP_LET,
  113. y: BindingTypes.SETUP_LET,
  114. foo: BindingTypes.PROPS,
  115. bar: BindingTypes.PROPS_ALIASED,
  116. __propsAliases: {
  117. bar: 'foo'
  118. }
  119. })
  120. })
  121. test('rest spread', () => {
  122. const { content, bindings } = compile(`
  123. <script setup>
  124. const { foo, bar, ...rest } = defineProps(['foo', 'bar', 'baz'])
  125. </script>
  126. `)
  127. expect(content).toMatch(
  128. `const rest = _createPropsRestProxy(__props, ["foo","bar"])`
  129. )
  130. assertCode(content)
  131. expect(bindings).toStrictEqual({
  132. foo: BindingTypes.PROPS,
  133. bar: BindingTypes.PROPS,
  134. baz: BindingTypes.PROPS,
  135. rest: BindingTypes.SETUP_CONST
  136. })
  137. })
  138. describe('errors', () => {
  139. test('should error on deep destructure', () => {
  140. expect(() =>
  141. compile(
  142. `<script setup>const { foo: [bar] } = defineProps(['foo'])</script>`
  143. )
  144. ).toThrow(`destructure does not support nested patterns`)
  145. expect(() =>
  146. compile(
  147. `<script setup>const { foo: { bar } } = defineProps(['foo'])</script>`
  148. )
  149. ).toThrow(`destructure does not support nested patterns`)
  150. })
  151. test('should error on computed key', () => {
  152. expect(() =>
  153. compile(
  154. `<script setup>const { [foo]: bar } = defineProps(['foo'])</script>`
  155. )
  156. ).toThrow(`destructure cannot use computed key`)
  157. })
  158. test('should error when used with withDefaults', () => {
  159. expect(() =>
  160. compile(
  161. `<script setup lang="ts">
  162. const { foo } = withDefaults(defineProps<{ foo: string }>(), { foo: 'foo' })
  163. </script>`
  164. )
  165. ).toThrow(`withDefaults() is unnecessary when using destructure`)
  166. })
  167. test('should error if destructure reference local vars', () => {
  168. expect(() =>
  169. compile(
  170. `<script setup>
  171. const x = 1
  172. const {
  173. foo = () => x
  174. } = defineProps(['foo'])
  175. </script>`
  176. )
  177. ).toThrow(`cannot reference locally declared variables`)
  178. })
  179. })
  180. })