compileScript.spec.ts 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467
  1. import { BindingTypes } from '@vue/compiler-dom'
  2. import { compileSFCScript as compile, assertCode } from './utils'
  3. describe('SFC compile <script setup>', () => {
  4. test('should expose top level declarations', () => {
  5. const { content } = compile(`
  6. <script setup>
  7. import { x } from './x'
  8. let a = 1
  9. const b = 2
  10. function c() {}
  11. class d {}
  12. </script>
  13. `)
  14. assertCode(content)
  15. expect(content).toMatch('return { a, b, c, d, x }')
  16. })
  17. test('defineProps()', () => {
  18. const { content, bindings } = compile(`
  19. <script setup>
  20. const props = defineProps({
  21. foo: String
  22. })
  23. const bar = 1
  24. </script>
  25. `)
  26. // should generate working code
  27. assertCode(content)
  28. // should anayze bindings
  29. expect(bindings).toStrictEqual({
  30. foo: BindingTypes.PROPS,
  31. bar: BindingTypes.SETUP_CONST,
  32. props: BindingTypes.SETUP_CONST
  33. })
  34. // should remove defineOptions import and call
  35. expect(content).not.toMatch('defineProps')
  36. // should generate correct setup signature
  37. expect(content).toMatch(`setup(__props, { expose }) {`)
  38. // should assign user identifier to it
  39. expect(content).toMatch(`const props = __props`)
  40. // should include context options in default export
  41. expect(content).toMatch(`export default {
  42. props: {
  43. foo: String
  44. },`)
  45. })
  46. test('defineProps w/ external definition', () => {
  47. const { content } = compile(`
  48. <script setup>
  49. import { propsModel } from './props'
  50. const props = defineProps(propsModel)
  51. </script>
  52. `)
  53. assertCode(content)
  54. expect(content).toMatch(`export default {
  55. props: propsModel,`)
  56. })
  57. test('defineEmit() (deprecated)', () => {
  58. const { content, bindings } = compile(`
  59. <script setup>
  60. const myEmit = defineEmit(['foo', 'bar'])
  61. </script>
  62. `)
  63. assertCode(content)
  64. expect(bindings).toStrictEqual({
  65. myEmit: BindingTypes.SETUP_CONST
  66. })
  67. // should remove defineOptions import and call
  68. expect(content).not.toMatch(/defineEmits?/)
  69. // should generate correct setup signature
  70. expect(content).toMatch(`setup(__props, { expose, emit: myEmit }) {`)
  71. // should include context options in default export
  72. expect(content).toMatch(`export default {
  73. emits: ['foo', 'bar'],`)
  74. })
  75. test('defineEmits()', () => {
  76. const { content, bindings } = compile(`
  77. <script setup>
  78. const myEmit = defineEmits(['foo', 'bar'])
  79. </script>
  80. `)
  81. assertCode(content)
  82. expect(bindings).toStrictEqual({
  83. myEmit: BindingTypes.SETUP_CONST
  84. })
  85. // should remove defineOptions import and call
  86. expect(content).not.toMatch('defineEmits')
  87. // should generate correct setup signature
  88. expect(content).toMatch(`setup(__props, { expose, emit: myEmit }) {`)
  89. // should include context options in default export
  90. expect(content).toMatch(`export default {
  91. emits: ['foo', 'bar'],`)
  92. })
  93. test('defineProps/defineEmits in multi-variable decalration', () => {
  94. const { content } = compile(`
  95. <script setup>
  96. const props = defineProps(['item']),
  97. a = 1,
  98. emit = defineEmits(['a']);
  99. </script>
  100. `)
  101. assertCode(content)
  102. expect(content).toMatch(`const a = 1;`) // test correct removal
  103. expect(content).toMatch(`props: ['item'],`)
  104. expect(content).toMatch(`emits: ['a'],`)
  105. })
  106. test('defineProps/defineEmits in multi-variable decalration (full removal)', () => {
  107. const { content } = compile(`
  108. <script setup>
  109. const props = defineProps(['item']),
  110. emit = defineEmits(['a']);
  111. </script>
  112. `)
  113. assertCode(content)
  114. expect(content).toMatch(`props: ['item'],`)
  115. expect(content).toMatch(`emits: ['a'],`)
  116. })
  117. test('defineExpose()', () => {
  118. const { content } = compile(`
  119. <script setup>
  120. defineExpose({ foo: 123 })
  121. </script>
  122. `)
  123. assertCode(content)
  124. // should remove defineOptions import and call
  125. expect(content).not.toMatch('defineExpose')
  126. // should generate correct setup signature
  127. expect(content).toMatch(`setup(__props, { expose }) {`)
  128. // should replace callee
  129. expect(content).toMatch(/\bexpose\(\{ foo: 123 \}\)/)
  130. })
  131. describe('<script> and <script setup> co-usage', () => {
  132. test('script first', () => {
  133. const { content } = compile(`
  134. <script>
  135. export const n = 1
  136. </script>
  137. <script setup>
  138. import { x } from './x'
  139. x()
  140. </script>
  141. `)
  142. assertCode(content)
  143. })
  144. test('script setup first', () => {
  145. const { content } = compile(`
  146. <script setup>
  147. import { x } from './x'
  148. x()
  149. </script>
  150. <script>
  151. export const n = 1
  152. </script>
  153. `)
  154. assertCode(content)
  155. })
  156. })
  157. describe('imports', () => {
  158. test('should hoist and expose imports', () => {
  159. assertCode(
  160. compile(`<script setup>
  161. import { ref } from 'vue'
  162. import 'foo/css'
  163. </script>`).content
  164. )
  165. })
  166. test('should extract comment for import or type declarations', () => {
  167. assertCode(
  168. compile(`
  169. <script setup>
  170. import a from 'a' // comment
  171. import b from 'b'
  172. </script>
  173. `).content
  174. )
  175. })
  176. // #2740
  177. test('should allow defineProps/Emit at the start of imports', () => {
  178. assertCode(
  179. compile(`<script setup>
  180. import { ref } from 'vue'
  181. defineProps(['foo'])
  182. defineEmits(['bar'])
  183. const r = ref(0)
  184. </script>`).content
  185. )
  186. })
  187. test('dedupe between user & helper', () => {
  188. const { content } = compile(`
  189. <script setup>
  190. import { ref } from 'vue'
  191. ref: foo = 1
  192. </script>
  193. `)
  194. assertCode(content)
  195. expect(content).toMatch(`import { ref } from 'vue'`)
  196. })
  197. test('import dedupe between <script> and <script setup>', () => {
  198. const { content } = compile(`
  199. <script>
  200. import { x } from './x'
  201. </script>
  202. <script setup>
  203. import { x } from './x'
  204. x()
  205. </script>
  206. `)
  207. assertCode(content)
  208. expect(content.indexOf(`import { x }`)).toEqual(
  209. content.lastIndexOf(`import { x }`)
  210. )
  211. })
  212. })
  213. describe('inlineTemplate mode', () => {
  214. test('should work', () => {
  215. const { content } = compile(
  216. `
  217. <script setup>
  218. import { ref } from 'vue'
  219. const count = ref(0)
  220. </script>
  221. <template>
  222. <div>{{ count }}</div>
  223. <div>static</div>
  224. </template>
  225. `,
  226. { inlineTemplate: true }
  227. )
  228. // check snapshot and make sure helper imports and
  229. // hoists are placed correctly.
  230. assertCode(content)
  231. // in inline mode, no need to call expose() since nothing is exposed
  232. // anyway!
  233. expect(content).not.toMatch(`expose()`)
  234. })
  235. test('with defineExpose()', () => {
  236. const { content } = compile(
  237. `
  238. <script setup>
  239. const count = ref(0)
  240. defineExpose({ count })
  241. </script>
  242. `,
  243. { inlineTemplate: true }
  244. )
  245. assertCode(content)
  246. expect(content).toMatch(`setup(__props, { expose })`)
  247. expect(content).toMatch(`expose({ count })`)
  248. })
  249. test('referencing scope components and directives', () => {
  250. const { content } = compile(
  251. `
  252. <script setup>
  253. import ChildComp from './Child.vue'
  254. import SomeOtherComp from './Other.vue'
  255. import myDir from './my-dir'
  256. </script>
  257. <template>
  258. <div v-my-dir></div>
  259. <ChildComp/>
  260. <some-other-comp/>
  261. </template>
  262. `,
  263. { inlineTemplate: true }
  264. )
  265. expect(content).toMatch('[_unref(myDir)]')
  266. expect(content).toMatch('_createVNode(ChildComp)')
  267. // kebab-case component support
  268. expect(content).toMatch('_createVNode(SomeOtherComp)')
  269. assertCode(content)
  270. })
  271. test('avoid unref() when necessary', () => {
  272. // function, const, component import
  273. const { content } = compile(
  274. `<script setup>
  275. import { ref } from 'vue'
  276. import Foo, { bar } from './Foo.vue'
  277. import other from './util'
  278. const count = ref(0)
  279. const constant = {}
  280. const maybe = foo()
  281. let lett = 1
  282. function fn() {}
  283. </script>
  284. <template>
  285. <Foo>{{ bar }}</Foo>
  286. <div @click="fn">{{ count }} {{ constant }} {{ maybe }} {{ lett }} {{ other }}</div>
  287. </template>
  288. `,
  289. { inlineTemplate: true }
  290. )
  291. // no need to unref vue component import
  292. expect(content).toMatch(`createVNode(Foo,`)
  293. // #2699 should unref named imports from .vue
  294. expect(content).toMatch(`unref(bar)`)
  295. // should unref other imports
  296. expect(content).toMatch(`unref(other)`)
  297. // no need to unref constant literals
  298. expect(content).not.toMatch(`unref(constant)`)
  299. // should directly use .value for known refs
  300. expect(content).toMatch(`count.value`)
  301. // should unref() on const bindings that may be refs
  302. expect(content).toMatch(`unref(maybe)`)
  303. // should unref() on let bindings
  304. expect(content).toMatch(`unref(lett)`)
  305. // no need to unref function declarations
  306. expect(content).toMatch(`{ onClick: fn }`)
  307. // no need to mark constant fns in patch flag
  308. expect(content).not.toMatch(`PROPS`)
  309. assertCode(content)
  310. })
  311. test('v-model codegen', () => {
  312. const { content } = compile(
  313. `<script setup>
  314. import { ref } from 'vue'
  315. const count = ref(0)
  316. const maybe = foo()
  317. let lett = 1
  318. </script>
  319. <template>
  320. <input v-model="count">
  321. <input v-model="maybe">
  322. <input v-model="lett">
  323. </template>
  324. `,
  325. { inlineTemplate: true }
  326. )
  327. // known const ref: set value
  328. expect(content).toMatch(`count.value = $event`)
  329. // const but maybe ref: also assign .value directly since non-ref
  330. // won't work
  331. expect(content).toMatch(`maybe.value = $event`)
  332. // let: handle both cases
  333. expect(content).toMatch(
  334. `_isRef(lett) ? lett.value = $event : lett = $event`
  335. )
  336. assertCode(content)
  337. })
  338. test('template assignment expression codegen', () => {
  339. const { content } = compile(
  340. `<script setup>
  341. import { ref } from 'vue'
  342. const count = ref(0)
  343. const maybe = foo()
  344. let lett = 1
  345. let v = ref(1)
  346. </script>
  347. <template>
  348. <div @click="count = 1"/>
  349. <div @click="maybe = count"/>
  350. <div @click="lett = count"/>
  351. <div @click="v += 1"/>
  352. <div @click="v -= 1"/>
  353. </template>
  354. `,
  355. { inlineTemplate: true }
  356. )
  357. // known const ref: set value
  358. expect(content).toMatch(`count.value = 1`)
  359. // const but maybe ref: only assign after check
  360. expect(content).toMatch(`maybe.value = count.value`)
  361. // let: handle both cases
  362. expect(content).toMatch(
  363. `_isRef(lett) ? lett.value = count.value : lett = count.value`
  364. )
  365. expect(content).toMatch(`_isRef(v) ? v.value += 1 : v += 1`)
  366. expect(content).toMatch(`_isRef(v) ? v.value -= 1 : v -= 1`)
  367. assertCode(content)
  368. })
  369. test('template update expression codegen', () => {
  370. const { content } = compile(
  371. `<script setup>
  372. import { ref } from 'vue'
  373. const count = ref(0)
  374. const maybe = foo()
  375. let lett = 1
  376. </script>
  377. <template>
  378. <div @click="count++"/>
  379. <div @click="--count"/>
  380. <div @click="maybe++"/>
  381. <div @click="--maybe"/>
  382. <div @click="lett++"/>
  383. <div @click="--lett"/>
  384. </template>
  385. `,
  386. { inlineTemplate: true }
  387. )
  388. // known const ref: set value
  389. expect(content).toMatch(`count.value++`)
  390. expect(content).toMatch(`--count.value`)
  391. // const but maybe ref (non-ref case ignored)
  392. expect(content).toMatch(`maybe.value++`)
  393. expect(content).toMatch(`--maybe.value`)
  394. // let: handle both cases
  395. expect(content).toMatch(`_isRef(lett) ? lett.value++ : lett++`)
  396. expect(content).toMatch(`_isRef(lett) ? --lett.value : --lett`)
  397. assertCode(content)
  398. })
  399. test('template destructure assignment codegen', () => {
  400. const { content } = compile(
  401. `<script setup>
  402. import { ref } from 'vue'
  403. const val = {}
  404. const count = ref(0)
  405. const maybe = foo()
  406. let lett = 1
  407. </script>
  408. <template>
  409. <div @click="({ count } = val)"/>
  410. <div @click="[maybe] = val"/>
  411. <div @click="({ lett } = val)"/>
  412. </template>
  413. `,
  414. { inlineTemplate: true }
  415. )
  416. // known const ref: set value
  417. expect(content).toMatch(`({ count: count.value } = val)`)
  418. // const but maybe ref (non-ref case ignored)
  419. expect(content).toMatch(`[maybe.value] = val`)
  420. // let: assumes non-ref
  421. expect(content).toMatch(`{ lett: lett } = val`)
  422. assertCode(content)
  423. })
  424. test('ssr codegen', () => {
  425. const { content } = compile(
  426. `
  427. <script setup>
  428. import { ref } from 'vue'
  429. const count = ref(0)
  430. </script>
  431. <template>
  432. <div>{{ count }}</div>
  433. <div>static</div>
  434. </template>
  435. <style>
  436. div { color: v-bind(count) }
  437. </style>
  438. `,
  439. {
  440. inlineTemplate: true,
  441. templateOptions: {
  442. ssr: true
  443. }
  444. }
  445. )
  446. expect(content).toMatch(`\n __ssrInlineRender: true,\n`)
  447. expect(content).toMatch(`return (_ctx, _push`)
  448. expect(content).toMatch(`ssrInterpolate`)
  449. assertCode(content)
  450. })
  451. // _withId is only generated for backwards compat and is a noop when called
  452. // in module scope.
  453. // when inside setup(), currentInstance will be non-null and _withId will
  454. // no longer be noop and cause scopeId errors.
  455. // TODO: this test should no longer be necessary if we remove _withId
  456. // codegen in 3.1
  457. test('should not wrap render fn with withId when having scoped styles', async () => {
  458. const { content } = compile(
  459. `
  460. <script setup>
  461. const msg = 1
  462. </script>
  463. <template><h1>{{ msg }}</h1></template>
  464. <style scoped>
  465. h1 { color: red; }
  466. </style>
  467. `,
  468. {
  469. inlineTemplate: true
  470. }
  471. )
  472. expect(content).toMatch(`return (_ctx, _cache`)
  473. expect(content).not.toMatch(`_withId(`)
  474. assertCode(content)
  475. })
  476. })
  477. describe('with TypeScript', () => {
  478. test('hoist type declarations', () => {
  479. const { content } = compile(`
  480. <script setup lang="ts">
  481. export interface Foo {}
  482. type Bar = {}
  483. </script>`)
  484. assertCode(content)
  485. })
  486. test('defineProps/Emit w/ runtime options', () => {
  487. const { content } = compile(`
  488. <script setup lang="ts">
  489. const props = defineProps({ foo: String })
  490. const emit = defineEmits(['a', 'b'])
  491. </script>
  492. `)
  493. assertCode(content)
  494. expect(content).toMatch(`export default _defineComponent({
  495. props: { foo: String },
  496. emits: ['a', 'b'],
  497. setup(__props, { expose, emit }) {`)
  498. })
  499. test('defineProps w/ type', () => {
  500. const { content, bindings } = compile(`
  501. <script setup lang="ts">
  502. interface Test {}
  503. type Alias = number[]
  504. defineProps<{
  505. string: string
  506. number: number
  507. boolean: boolean
  508. object: object
  509. objectLiteral: { a: number }
  510. fn: (n: number) => void
  511. functionRef: Function
  512. objectRef: Object
  513. array: string[]
  514. arrayRef: Array<any>
  515. tuple: [number, number]
  516. set: Set<string>
  517. literal: 'foo'
  518. optional?: any
  519. recordRef: Record<string, null>
  520. interface: Test
  521. alias: Alias
  522. method(): void
  523. union: string | number
  524. literalUnion: 'foo' | 'bar'
  525. literalUnionMixed: 'foo' | 1 | boolean
  526. intersection: Test & {}
  527. }>()
  528. </script>`)
  529. assertCode(content)
  530. expect(content).toMatch(`string: { type: String, required: true }`)
  531. expect(content).toMatch(`number: { type: Number, required: true }`)
  532. expect(content).toMatch(`boolean: { type: Boolean, required: true }`)
  533. expect(content).toMatch(`object: { type: Object, required: true }`)
  534. expect(content).toMatch(`objectLiteral: { type: Object, required: true }`)
  535. expect(content).toMatch(`fn: { type: Function, required: true }`)
  536. expect(content).toMatch(`functionRef: { type: Function, required: true }`)
  537. expect(content).toMatch(`objectRef: { type: Object, required: true }`)
  538. expect(content).toMatch(`array: { type: Array, required: true }`)
  539. expect(content).toMatch(`arrayRef: { type: Array, required: true }`)
  540. expect(content).toMatch(`tuple: { type: Array, required: true }`)
  541. expect(content).toMatch(`set: { type: Set, required: true }`)
  542. expect(content).toMatch(`literal: { type: String, required: true }`)
  543. expect(content).toMatch(`optional: { type: null, required: false }`)
  544. expect(content).toMatch(`recordRef: { type: Object, required: true }`)
  545. expect(content).toMatch(`interface: { type: Object, required: true }`)
  546. expect(content).toMatch(`alias: { type: Array, required: true }`)
  547. expect(content).toMatch(`method: { type: Function, required: true }`)
  548. expect(content).toMatch(
  549. `union: { type: [String, Number], required: true }`
  550. )
  551. expect(content).toMatch(
  552. `literalUnion: { type: [String, String], required: true }`
  553. )
  554. expect(content).toMatch(
  555. `literalUnionMixed: { type: [String, Number, Boolean], required: true }`
  556. )
  557. expect(content).toMatch(`intersection: { type: Object, required: true }`)
  558. expect(bindings).toStrictEqual({
  559. string: BindingTypes.PROPS,
  560. number: BindingTypes.PROPS,
  561. boolean: BindingTypes.PROPS,
  562. object: BindingTypes.PROPS,
  563. objectLiteral: BindingTypes.PROPS,
  564. fn: BindingTypes.PROPS,
  565. functionRef: BindingTypes.PROPS,
  566. objectRef: BindingTypes.PROPS,
  567. array: BindingTypes.PROPS,
  568. arrayRef: BindingTypes.PROPS,
  569. tuple: BindingTypes.PROPS,
  570. set: BindingTypes.PROPS,
  571. literal: BindingTypes.PROPS,
  572. optional: BindingTypes.PROPS,
  573. recordRef: BindingTypes.PROPS,
  574. interface: BindingTypes.PROPS,
  575. alias: BindingTypes.PROPS,
  576. method: BindingTypes.PROPS,
  577. union: BindingTypes.PROPS,
  578. literalUnion: BindingTypes.PROPS,
  579. literalUnionMixed: BindingTypes.PROPS,
  580. intersection: BindingTypes.PROPS
  581. })
  582. })
  583. test('defineProps w/ interface', () => {
  584. const { content, bindings } = compile(`
  585. <script setup lang="ts">
  586. interface Props { x?: number }
  587. defineProps<Props>()
  588. </script>
  589. `)
  590. assertCode(content)
  591. expect(content).toMatch(`x: { type: Number, required: false }`)
  592. expect(bindings).toStrictEqual({
  593. x: BindingTypes.PROPS
  594. })
  595. })
  596. test('defineProps w/ exported interface', () => {
  597. const { content, bindings } = compile(`
  598. <script setup lang="ts">
  599. export interface Props { x?: number }
  600. defineProps<Props>()
  601. </script>
  602. `)
  603. assertCode(content)
  604. expect(content).toMatch(`x: { type: Number, required: false }`)
  605. expect(bindings).toStrictEqual({
  606. x: BindingTypes.PROPS
  607. })
  608. })
  609. test('defineProps w/ type alias', () => {
  610. const { content, bindings } = compile(`
  611. <script setup lang="ts">
  612. type Props = { x?: number }
  613. defineProps<Props>()
  614. </script>
  615. `)
  616. assertCode(content)
  617. expect(content).toMatch(`x: { type: Number, required: false }`)
  618. expect(bindings).toStrictEqual({
  619. x: BindingTypes.PROPS
  620. })
  621. })
  622. test('defineProps w/ exported type alias', () => {
  623. const { content, bindings } = compile(`
  624. <script setup lang="ts">
  625. export type Props = { x?: number }
  626. defineProps<Props>()
  627. </script>
  628. `)
  629. assertCode(content)
  630. expect(content).toMatch(`x: { type: Number, required: false }`)
  631. expect(bindings).toStrictEqual({
  632. x: BindingTypes.PROPS
  633. })
  634. })
  635. test('withDefaults (static)', () => {
  636. const { content, bindings } = compile(`
  637. <script setup lang="ts">
  638. const props = withDefaults(defineProps<{
  639. foo?: string
  640. bar?: number
  641. }>(), {
  642. foo: 'hi'
  643. })
  644. </script>
  645. `)
  646. assertCode(content)
  647. expect(content).toMatch(
  648. `foo: { type: String, required: false, default: 'hi' }`
  649. )
  650. expect(content).toMatch(`bar: { type: Number, required: false }`)
  651. expect(content).toMatch(`const props = __props`)
  652. expect(bindings).toStrictEqual({
  653. foo: BindingTypes.PROPS,
  654. bar: BindingTypes.PROPS,
  655. props: BindingTypes.SETUP_CONST
  656. })
  657. })
  658. test('withDefaults (dynamic)', () => {
  659. const { content } = compile(`
  660. <script setup lang="ts">
  661. import { defaults } from './foo'
  662. const props = withDefaults(defineProps<{
  663. foo?: string
  664. bar?: number
  665. }>(), { ...defaults })
  666. </script>
  667. `)
  668. assertCode(content)
  669. expect(content).toMatch(`import { mergeDefaults as _mergeDefaults`)
  670. expect(content).toMatch(
  671. `
  672. _mergeDefaults({
  673. foo: { type: String, required: false },
  674. bar: { type: Number, required: false }
  675. }, { ...defaults })`.trim()
  676. )
  677. })
  678. test('defineEmits w/ type', () => {
  679. const { content } = compile(`
  680. <script setup lang="ts">
  681. const emit = defineEmits<(e: 'foo' | 'bar') => void>()
  682. </script>
  683. `)
  684. assertCode(content)
  685. expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
  686. expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
  687. })
  688. test('defineEmits w/ type (union)', () => {
  689. const type = `((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void)`
  690. expect(() =>
  691. compile(`
  692. <script setup lang="ts">
  693. const emit = defineEmits<${type}>()
  694. </script>
  695. `)
  696. ).toThrow()
  697. })
  698. test('defineEmits w/ type (type literal w/ call signatures)', () => {
  699. const type = `{(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}`
  700. const { content } = compile(`
  701. <script setup lang="ts">
  702. const emit = defineEmits<${type}>()
  703. </script>
  704. `)
  705. assertCode(content)
  706. expect(content).toMatch(`emit: (${type}),`)
  707. expect(content).toMatch(
  708. `emits: ["foo", "bar", "baz"] as unknown as undefined`
  709. )
  710. })
  711. test('defineEmits w/ type (interface)', () => {
  712. const { content } = compile(`
  713. <script setup lang="ts">
  714. interface Emits { (e: 'foo' | 'bar'): void }
  715. const emit = defineEmits<Emits>()
  716. </script>
  717. `)
  718. assertCode(content)
  719. expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
  720. expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
  721. })
  722. test('defineEmits w/ type (exported interface)', () => {
  723. const { content } = compile(`
  724. <script setup lang="ts">
  725. export interface Emits { (e: 'foo' | 'bar'): void }
  726. const emit = defineEmits<Emits>()
  727. </script>
  728. `)
  729. assertCode(content)
  730. expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
  731. expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
  732. })
  733. test('defineEmits w/ type (type alias)', () => {
  734. const { content } = compile(`
  735. <script setup lang="ts">
  736. type Emits = { (e: 'foo' | 'bar'): void }
  737. const emit = defineEmits<Emits>()
  738. </script>
  739. `)
  740. assertCode(content)
  741. expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
  742. expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
  743. })
  744. test('defineEmits w/ type (exported type alias)', () => {
  745. const { content } = compile(`
  746. <script setup lang="ts">
  747. export type Emits = { (e: 'foo' | 'bar'): void }
  748. const emit = defineEmits<Emits>()
  749. </script>
  750. `)
  751. assertCode(content)
  752. expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
  753. expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
  754. })
  755. test('defineEmits w/ type (referenced function type)', () => {
  756. const { content } = compile(`
  757. <script setup lang="ts">
  758. type Emits = (e: 'foo' | 'bar') => void
  759. const emit = defineEmits<Emits>()
  760. </script>
  761. `)
  762. assertCode(content)
  763. expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
  764. expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
  765. })
  766. test('defineEmits w/ type (referenced exported function type)', () => {
  767. const { content } = compile(`
  768. <script setup lang="ts">
  769. export type Emits = (e: 'foo' | 'bar') => void
  770. const emit = defineEmits<Emits>()
  771. </script>
  772. `)
  773. assertCode(content)
  774. expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
  775. expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
  776. })
  777. })
  778. describe('async/await detection', () => {
  779. function assertAwaitDetection(code: string, shouldAsync = true) {
  780. const { content } = compile(`<script setup>${code}</script>`)
  781. expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup(`)
  782. }
  783. test('expression statement', () => {
  784. assertAwaitDetection(`await foo`)
  785. })
  786. test('variable', () => {
  787. assertAwaitDetection(`const a = 1 + (await foo)`)
  788. })
  789. test('ref', () => {
  790. assertAwaitDetection(`ref: a = 1 + (await foo)`)
  791. })
  792. test('nested statements', () => {
  793. assertAwaitDetection(`if (ok) { await foo } else { await bar }`)
  794. })
  795. test('should ignore await inside functions', () => {
  796. // function declaration
  797. assertAwaitDetection(`async function foo() { await bar }`, false)
  798. // function expression
  799. assertAwaitDetection(`const foo = async () => { await bar }`, false)
  800. // object method
  801. assertAwaitDetection(`const obj = { async method() { await bar }}`, false)
  802. // class method
  803. assertAwaitDetection(
  804. `const cls = class Foo { async method() { await bar }}`,
  805. false
  806. )
  807. })
  808. })
  809. describe('ref: syntax sugar', () => {
  810. test('convert ref declarations', () => {
  811. const { content, bindings } = compile(`<script setup>
  812. ref: foo
  813. ref: a = 1
  814. ref: b = {
  815. count: 0
  816. }
  817. let c = () => {}
  818. let d
  819. </script>`)
  820. expect(content).toMatch(`import { ref as _ref } from 'vue'`)
  821. expect(content).not.toMatch(`ref: foo`)
  822. expect(content).not.toMatch(`ref: a`)
  823. expect(content).not.toMatch(`ref: b`)
  824. expect(content).toMatch(`const foo = _ref()`)
  825. expect(content).toMatch(`const a = _ref(1)`)
  826. expect(content).toMatch(`
  827. const b = _ref({
  828. count: 0
  829. })
  830. `)
  831. // normal declarations left untouched
  832. expect(content).toMatch(`let c = () => {}`)
  833. expect(content).toMatch(`let d`)
  834. assertCode(content)
  835. expect(bindings).toStrictEqual({
  836. foo: BindingTypes.SETUP_REF,
  837. a: BindingTypes.SETUP_REF,
  838. b: BindingTypes.SETUP_REF,
  839. c: BindingTypes.SETUP_LET,
  840. d: BindingTypes.SETUP_LET
  841. })
  842. })
  843. test('multi ref declarations', () => {
  844. const { content, bindings } = compile(`<script setup>
  845. ref: a = 1, b = 2, c = {
  846. count: 0
  847. }
  848. </script>`)
  849. expect(content).toMatch(`
  850. const a = _ref(1), b = _ref(2), c = _ref({
  851. count: 0
  852. })
  853. `)
  854. expect(content).toMatch(`return { a, b, c }`)
  855. assertCode(content)
  856. expect(bindings).toStrictEqual({
  857. a: BindingTypes.SETUP_REF,
  858. b: BindingTypes.SETUP_REF,
  859. c: BindingTypes.SETUP_REF
  860. })
  861. })
  862. test('should not convert non ref labels', () => {
  863. const { content } = compile(`<script setup>
  864. foo: a = 1, b = 2, c = {
  865. count: 0
  866. }
  867. </script>`)
  868. expect(content).toMatch(`foo: a = 1, b = 2`)
  869. assertCode(content)
  870. })
  871. test('accessing ref binding', () => {
  872. const { content } = compile(`<script setup>
  873. ref: a = 1
  874. console.log(a)
  875. function get() {
  876. return a + 1
  877. }
  878. </script>`)
  879. expect(content).toMatch(`console.log(a.value)`)
  880. expect(content).toMatch(`return a.value + 1`)
  881. assertCode(content)
  882. })
  883. test('cases that should not append .value', () => {
  884. const { content } = compile(`<script setup>
  885. ref: a = 1
  886. console.log(b.a)
  887. function get(a) {
  888. return a + 1
  889. }
  890. </script>`)
  891. expect(content).not.toMatch(`a.value`)
  892. })
  893. test('mutating ref binding', () => {
  894. const { content } = compile(`<script setup>
  895. ref: a = 1
  896. ref: b = { count: 0 }
  897. function inc() {
  898. a++
  899. a = a + 1
  900. b.count++
  901. b.count = b.count + 1
  902. ;({ a } = { a: 2 })
  903. ;[a] = [1]
  904. }
  905. </script>`)
  906. expect(content).toMatch(`a.value++`)
  907. expect(content).toMatch(`a.value = a.value + 1`)
  908. expect(content).toMatch(`b.value.count++`)
  909. expect(content).toMatch(`b.value.count = b.value.count + 1`)
  910. expect(content).toMatch(`;({ a: a.value } = { a: 2 })`)
  911. expect(content).toMatch(`;[a.value] = [1]`)
  912. assertCode(content)
  913. })
  914. test('using ref binding in property shorthand', () => {
  915. const { content } = compile(`<script setup>
  916. ref: a = 1
  917. const b = { a }
  918. function test() {
  919. const { a } = b
  920. }
  921. </script>`)
  922. expect(content).toMatch(`const b = { a: a.value }`)
  923. // should not convert destructure
  924. expect(content).toMatch(`const { a } = b`)
  925. assertCode(content)
  926. })
  927. test('should not rewrite scope variable', () => {
  928. const { content } = compile(`
  929. <script setup>
  930. ref: a = 1
  931. ref: b = 1
  932. ref: d = 1
  933. const e = 1
  934. function test() {
  935. const a = 2
  936. console.log(a)
  937. console.log(b)
  938. let c = { c: 3 }
  939. console.log(c)
  940. let $d
  941. console.log($d)
  942. console.log(d)
  943. console.log(e)
  944. }
  945. </script>`)
  946. expect(content).toMatch('console.log(a)')
  947. expect(content).toMatch('console.log(b.value)')
  948. expect(content).toMatch('console.log(c)')
  949. expect(content).toMatch('console.log($d)')
  950. expect(content).toMatch('console.log(d.value)')
  951. expect(content).toMatch('console.log(e)')
  952. assertCode(content)
  953. })
  954. test('object destructure', () => {
  955. const { content, bindings } = compile(`<script setup>
  956. ref: n = 1, ({ a, b: c, d = 1, e: f = 2, ...g } = useFoo())
  957. ref: ({ foo } = useSomthing(() => 1));
  958. console.log(n, a, c, d, f, g, foo)
  959. </script>`)
  960. expect(content).toMatch(
  961. `const n = _ref(1), { a: __a, b: __c, d: __d = 1, e: __f = 2, ...__g } = useFoo()`
  962. )
  963. expect(content).toMatch(`const { foo: __foo } = useSomthing(() => 1)`)
  964. expect(content).toMatch(`\nconst a = _ref(__a);`)
  965. expect(content).not.toMatch(`\nconst b = _ref(__b);`)
  966. expect(content).toMatch(`\nconst c = _ref(__c);`)
  967. expect(content).toMatch(`\nconst d = _ref(__d);`)
  968. expect(content).not.toMatch(`\nconst e = _ref(__e);`)
  969. expect(content).toMatch(`\nconst f = _ref(__f);`)
  970. expect(content).toMatch(`\nconst g = _ref(__g);`)
  971. expect(content).toMatch(`\nconst foo = _ref(__foo);`)
  972. expect(content).toMatch(
  973. `console.log(n.value, a.value, c.value, d.value, f.value, g.value, foo.value)`
  974. )
  975. expect(content).toMatch(`return { n, a, c, d, f, g, foo }`)
  976. expect(bindings).toStrictEqual({
  977. n: BindingTypes.SETUP_REF,
  978. a: BindingTypes.SETUP_REF,
  979. c: BindingTypes.SETUP_REF,
  980. d: BindingTypes.SETUP_REF,
  981. f: BindingTypes.SETUP_REF,
  982. g: BindingTypes.SETUP_REF,
  983. foo: BindingTypes.SETUP_REF
  984. })
  985. assertCode(content)
  986. })
  987. test('array destructure', () => {
  988. const { content, bindings } = compile(`<script setup>
  989. ref: n = 1, [a, b = 1, ...c] = useFoo()
  990. console.log(n, a, b, c)
  991. </script>`)
  992. expect(content).toMatch(
  993. `const n = _ref(1), [__a, __b = 1, ...__c] = useFoo()`
  994. )
  995. expect(content).toMatch(`\nconst a = _ref(__a);`)
  996. expect(content).toMatch(`\nconst b = _ref(__b);`)
  997. expect(content).toMatch(`\nconst c = _ref(__c);`)
  998. expect(content).toMatch(`console.log(n.value, a.value, b.value, c.value)`)
  999. expect(content).toMatch(`return { n, a, b, c }`)
  1000. expect(bindings).toStrictEqual({
  1001. n: BindingTypes.SETUP_REF,
  1002. a: BindingTypes.SETUP_REF,
  1003. b: BindingTypes.SETUP_REF,
  1004. c: BindingTypes.SETUP_REF
  1005. })
  1006. assertCode(content)
  1007. })
  1008. test('nested destructure', () => {
  1009. const { content, bindings } = compile(`<script setup>
  1010. ref: [{ a: { b }}] = useFoo()
  1011. ref: ({ c: [d, e] } = useBar())
  1012. console.log(b, d, e)
  1013. </script>`)
  1014. expect(content).toMatch(`const [{ a: { b: __b }}] = useFoo()`)
  1015. expect(content).toMatch(`const { c: [__d, __e] } = useBar()`)
  1016. expect(content).not.toMatch(`\nconst a = _ref(__a);`)
  1017. expect(content).not.toMatch(`\nconst c = _ref(__c);`)
  1018. expect(content).toMatch(`\nconst b = _ref(__b);`)
  1019. expect(content).toMatch(`\nconst d = _ref(__d);`)
  1020. expect(content).toMatch(`\nconst e = _ref(__e);`)
  1021. expect(content).toMatch(`return { b, d, e }`)
  1022. expect(bindings).toStrictEqual({
  1023. b: BindingTypes.SETUP_REF,
  1024. d: BindingTypes.SETUP_REF,
  1025. e: BindingTypes.SETUP_REF
  1026. })
  1027. assertCode(content)
  1028. })
  1029. })
  1030. describe('errors', () => {
  1031. test('<script> and <script setup> must have same lang', () => {
  1032. expect(() =>
  1033. compile(`<script>foo()</script><script setup lang="ts">bar()</script>`)
  1034. ).toThrow(`<script> and <script setup> must have the same language type`)
  1035. })
  1036. const moduleErrorMsg = `cannot contain ES module exports`
  1037. test('non-type named exports', () => {
  1038. expect(() =>
  1039. compile(`<script setup>
  1040. export const a = 1
  1041. </script>`)
  1042. ).toThrow(moduleErrorMsg)
  1043. expect(() =>
  1044. compile(`<script setup>
  1045. export * from './foo'
  1046. </script>`)
  1047. ).toThrow(moduleErrorMsg)
  1048. expect(() =>
  1049. compile(`<script setup>
  1050. const bar = 1
  1051. export { bar as default }
  1052. </script>`)
  1053. ).toThrow(moduleErrorMsg)
  1054. })
  1055. test('ref: non-assignment expressions', () => {
  1056. expect(() =>
  1057. compile(`<script setup>
  1058. ref: a = 1, foo()
  1059. </script>`)
  1060. ).toThrow(`ref: statements can only contain assignment expressions`)
  1061. })
  1062. test('defineProps/Emit() w/ both type and non-type args', () => {
  1063. expect(() => {
  1064. compile(`<script setup lang="ts">
  1065. defineProps<{}>({})
  1066. </script>`)
  1067. }).toThrow(`cannot accept both type and non-type arguments`)
  1068. expect(() => {
  1069. compile(`<script setup lang="ts">
  1070. defineEmits<{}>({})
  1071. </script>`)
  1072. }).toThrow(`cannot accept both type and non-type arguments`)
  1073. })
  1074. test('defineProps/Emit() referencing local var', () => {
  1075. expect(() =>
  1076. compile(`<script setup>
  1077. const bar = 1
  1078. defineProps({
  1079. foo: {
  1080. default: () => bar
  1081. }
  1082. })
  1083. </script>`)
  1084. ).toThrow(`cannot reference locally declared variables`)
  1085. expect(() =>
  1086. compile(`<script setup>
  1087. const bar = 'hello'
  1088. defineEmits([bar])
  1089. </script>`)
  1090. ).toThrow(`cannot reference locally declared variables`)
  1091. })
  1092. test('defineProps/Emit() referencing ref declarations', () => {
  1093. expect(() =>
  1094. compile(`<script setup>
  1095. ref: bar = 1
  1096. defineProps({
  1097. bar
  1098. })
  1099. </script>`)
  1100. ).toThrow(`cannot reference locally declared variables`)
  1101. expect(() =>
  1102. compile(`<script setup>
  1103. ref: bar = 1
  1104. defineEmits({
  1105. bar
  1106. })
  1107. </script>`)
  1108. ).toThrow(`cannot reference locally declared variables`)
  1109. })
  1110. test('should allow defineProps/Emit() referencing scope var', () => {
  1111. assertCode(
  1112. compile(`<script setup>
  1113. const bar = 1
  1114. defineProps({
  1115. foo: {
  1116. default: bar => bar + 1
  1117. }
  1118. })
  1119. defineEmits({
  1120. foo: bar => bar > 1
  1121. })
  1122. </script>`).content
  1123. )
  1124. })
  1125. test('should allow defineProps/Emit() referencing imported binding', () => {
  1126. assertCode(
  1127. compile(`<script setup>
  1128. import { bar } from './bar'
  1129. defineProps({
  1130. foo: {
  1131. default: () => bar
  1132. }
  1133. })
  1134. defineEmits({
  1135. foo: () => bar > 1
  1136. })
  1137. </script>`).content
  1138. )
  1139. })
  1140. })
  1141. })
  1142. describe('SFC analyze <script> bindings', () => {
  1143. it('can parse decorators syntax in typescript block', () => {
  1144. const { scriptAst } = compile(`
  1145. <script lang="ts">
  1146. import { Options, Vue } from 'vue-class-component';
  1147. @Options({
  1148. components: {
  1149. HelloWorld,
  1150. },
  1151. props: ['foo', 'bar']
  1152. })
  1153. export default class Home extends Vue {}
  1154. </script>
  1155. `)
  1156. expect(scriptAst).toBeDefined()
  1157. })
  1158. it('recognizes props array declaration', () => {
  1159. const { bindings } = compile(`
  1160. <script>
  1161. export default {
  1162. props: ['foo', 'bar']
  1163. }
  1164. </script>
  1165. `)
  1166. expect(bindings).toStrictEqual({
  1167. foo: BindingTypes.PROPS,
  1168. bar: BindingTypes.PROPS
  1169. })
  1170. expect(bindings!.__isScriptSetup).toBe(false)
  1171. })
  1172. it('recognizes props object declaration', () => {
  1173. const { bindings } = compile(`
  1174. <script>
  1175. export default {
  1176. props: {
  1177. foo: String,
  1178. bar: {
  1179. type: String,
  1180. },
  1181. baz: null,
  1182. qux: [String, Number]
  1183. }
  1184. }
  1185. </script>
  1186. `)
  1187. expect(bindings).toStrictEqual({
  1188. foo: BindingTypes.PROPS,
  1189. bar: BindingTypes.PROPS,
  1190. baz: BindingTypes.PROPS,
  1191. qux: BindingTypes.PROPS
  1192. })
  1193. expect(bindings!.__isScriptSetup).toBe(false)
  1194. })
  1195. it('recognizes setup return', () => {
  1196. const { bindings } = compile(`
  1197. <script>
  1198. const bar = 2
  1199. export default {
  1200. setup() {
  1201. return {
  1202. foo: 1,
  1203. bar
  1204. }
  1205. }
  1206. }
  1207. </script>
  1208. `)
  1209. expect(bindings).toStrictEqual({
  1210. foo: BindingTypes.SETUP_MAYBE_REF,
  1211. bar: BindingTypes.SETUP_MAYBE_REF
  1212. })
  1213. expect(bindings!.__isScriptSetup).toBe(false)
  1214. })
  1215. it('recognizes async setup return', () => {
  1216. const { bindings } = compile(`
  1217. <script>
  1218. const bar = 2
  1219. export default {
  1220. async setup() {
  1221. return {
  1222. foo: 1,
  1223. bar
  1224. }
  1225. }
  1226. }
  1227. </script>
  1228. `)
  1229. expect(bindings).toStrictEqual({
  1230. foo: BindingTypes.SETUP_MAYBE_REF,
  1231. bar: BindingTypes.SETUP_MAYBE_REF
  1232. })
  1233. expect(bindings!.__isScriptSetup).toBe(false)
  1234. })
  1235. it('recognizes data return', () => {
  1236. const { bindings } = compile(`
  1237. <script>
  1238. const bar = 2
  1239. export default {
  1240. data() {
  1241. return {
  1242. foo: null,
  1243. bar
  1244. }
  1245. }
  1246. }
  1247. </script>
  1248. `)
  1249. expect(bindings).toStrictEqual({
  1250. foo: BindingTypes.DATA,
  1251. bar: BindingTypes.DATA
  1252. })
  1253. })
  1254. it('recognizes methods', () => {
  1255. const { bindings } = compile(`
  1256. <script>
  1257. export default {
  1258. methods: {
  1259. foo() {}
  1260. }
  1261. }
  1262. </script>
  1263. `)
  1264. expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS })
  1265. })
  1266. it('recognizes computeds', () => {
  1267. const { bindings } = compile(`
  1268. <script>
  1269. export default {
  1270. computed: {
  1271. foo() {},
  1272. bar: {
  1273. get() {},
  1274. set() {},
  1275. }
  1276. }
  1277. }
  1278. </script>
  1279. `)
  1280. expect(bindings).toStrictEqual({
  1281. foo: BindingTypes.OPTIONS,
  1282. bar: BindingTypes.OPTIONS
  1283. })
  1284. })
  1285. it('recognizes injections array declaration', () => {
  1286. const { bindings } = compile(`
  1287. <script>
  1288. export default {
  1289. inject: ['foo', 'bar']
  1290. }
  1291. </script>
  1292. `)
  1293. expect(bindings).toStrictEqual({
  1294. foo: BindingTypes.OPTIONS,
  1295. bar: BindingTypes.OPTIONS
  1296. })
  1297. })
  1298. it('recognizes injections object declaration', () => {
  1299. const { bindings } = compile(`
  1300. <script>
  1301. export default {
  1302. inject: {
  1303. foo: {},
  1304. bar: {},
  1305. }
  1306. }
  1307. </script>
  1308. `)
  1309. expect(bindings).toStrictEqual({
  1310. foo: BindingTypes.OPTIONS,
  1311. bar: BindingTypes.OPTIONS
  1312. })
  1313. })
  1314. it('works for mixed bindings', () => {
  1315. const { bindings } = compile(`
  1316. <script>
  1317. export default {
  1318. inject: ['foo'],
  1319. props: {
  1320. bar: String,
  1321. },
  1322. setup() {
  1323. return {
  1324. baz: null,
  1325. }
  1326. },
  1327. data() {
  1328. return {
  1329. qux: null
  1330. }
  1331. },
  1332. methods: {
  1333. quux() {}
  1334. },
  1335. computed: {
  1336. quuz() {}
  1337. }
  1338. }
  1339. </script>
  1340. `)
  1341. expect(bindings).toStrictEqual({
  1342. foo: BindingTypes.OPTIONS,
  1343. bar: BindingTypes.PROPS,
  1344. baz: BindingTypes.SETUP_MAYBE_REF,
  1345. qux: BindingTypes.DATA,
  1346. quux: BindingTypes.OPTIONS,
  1347. quuz: BindingTypes.OPTIONS
  1348. })
  1349. })
  1350. it('works for script setup', () => {
  1351. const { bindings } = compile(`
  1352. <script setup>
  1353. import { ref as r } from 'vue'
  1354. defineProps({
  1355. foo: String
  1356. })
  1357. const a = r(1)
  1358. let b = 2
  1359. const c = 3
  1360. const { d } = someFoo()
  1361. let { e } = someBar()
  1362. </script>
  1363. `)
  1364. expect(bindings).toStrictEqual({
  1365. r: BindingTypes.SETUP_CONST,
  1366. a: BindingTypes.SETUP_REF,
  1367. b: BindingTypes.SETUP_LET,
  1368. c: BindingTypes.SETUP_CONST,
  1369. d: BindingTypes.SETUP_MAYBE_REF,
  1370. e: BindingTypes.SETUP_LET,
  1371. foo: BindingTypes.PROPS
  1372. })
  1373. })
  1374. })