2
0

compileScript.spec.ts 40 KB

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