compileScript.spec.ts 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600
  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. // https://github.com/nuxt/nuxt/issues/22416
  485. test('property access', () => {
  486. const { content } = compile(`
  487. <script setup lang="ts">
  488. import { Foo, Bar, Baz } from './foo'
  489. </script>
  490. <template>
  491. <div>{{ Foo.Bar.Baz }}</div>
  492. </template>
  493. `)
  494. expect(content).toMatch('return { get Foo() { return Foo } }')
  495. assertCode(content)
  496. })
  497. test('spread operator', () => {
  498. const { content } = compile(`
  499. <script setup lang="ts">
  500. import { Foo, Bar, Baz } from './foo'
  501. </script>
  502. <template>
  503. <div v-bind="{ ...Foo.Bar.Baz }"></div>
  504. </template>
  505. `)
  506. expect(content).toMatch('return { get Foo() { return Foo } }')
  507. assertCode(content)
  508. })
  509. test('property access (whitespace)', () => {
  510. const { content } = compile(`
  511. <script setup lang="ts">
  512. import { Foo, Bar, Baz } from './foo'
  513. </script>
  514. <template>
  515. <div>{{ Foo . Bar . Baz }}</div>
  516. </template>
  517. `)
  518. expect(content).toMatch('return { get Foo() { return Foo } }')
  519. assertCode(content)
  520. })
  521. })
  522. describe('inlineTemplate mode', () => {
  523. test('should work', () => {
  524. const { content } = compile(
  525. `
  526. <script setup>
  527. import { ref } from 'vue'
  528. const count = ref(0)
  529. </script>
  530. <template>
  531. <div>{{ count }}</div>
  532. <div>static</div>
  533. </template>
  534. `,
  535. { inlineTemplate: true }
  536. )
  537. // check snapshot and make sure helper imports and
  538. // hoists are placed correctly.
  539. assertCode(content)
  540. // in inline mode, no need to call expose() since nothing is exposed
  541. // anyway!
  542. expect(content).not.toMatch(`expose()`)
  543. })
  544. test('with defineExpose()', () => {
  545. const { content } = compile(
  546. `
  547. <script setup>
  548. const count = ref(0)
  549. defineExpose({ count })
  550. </script>
  551. `,
  552. { inlineTemplate: true }
  553. )
  554. assertCode(content)
  555. expect(content).toMatch(`setup(__props, { expose: __expose })`)
  556. expect(content).toMatch(`expose({ count })`)
  557. })
  558. test('referencing scope components and directives', () => {
  559. const { content } = compile(
  560. `
  561. <script setup>
  562. import ChildComp from './Child.vue'
  563. import SomeOtherComp from './Other.vue'
  564. import vMyDir from './my-dir'
  565. </script>
  566. <template>
  567. <div v-my-dir></div>
  568. <ChildComp/>
  569. <some-other-comp/>
  570. </template>
  571. `,
  572. { inlineTemplate: true }
  573. )
  574. expect(content).toMatch('[_unref(vMyDir)]')
  575. expect(content).toMatch('_createVNode(ChildComp)')
  576. // kebab-case component support
  577. expect(content).toMatch('_createVNode(SomeOtherComp)')
  578. assertCode(content)
  579. })
  580. test('avoid unref() when necessary', () => {
  581. // function, const, component import
  582. const { content } = compile(
  583. `<script setup>
  584. import { ref } from 'vue'
  585. import Foo, { bar } from './Foo.vue'
  586. import other from './util'
  587. import * as tree from './tree'
  588. const count = ref(0)
  589. const constant = {}
  590. const maybe = foo()
  591. let lett = 1
  592. function fn() {}
  593. </script>
  594. <template>
  595. <Foo>{{ bar }}</Foo>
  596. <div @click="fn">{{ count }} {{ constant }} {{ maybe }} {{ lett }} {{ other }}</div>
  597. {{ tree.foo() }}
  598. </template>
  599. `,
  600. { inlineTemplate: true }
  601. )
  602. // no need to unref vue component import
  603. expect(content).toMatch(`createVNode(Foo,`)
  604. // #2699 should unref named imports from .vue
  605. expect(content).toMatch(`unref(bar)`)
  606. // should unref other imports
  607. expect(content).toMatch(`unref(other)`)
  608. // no need to unref constant literals
  609. expect(content).not.toMatch(`unref(constant)`)
  610. // should directly use .value for known refs
  611. expect(content).toMatch(`count.value`)
  612. // should unref() on const bindings that may be refs
  613. expect(content).toMatch(`unref(maybe)`)
  614. // should unref() on let bindings
  615. expect(content).toMatch(`unref(lett)`)
  616. // no need to unref namespace import (this also preserves tree-shaking)
  617. expect(content).toMatch(`tree.foo()`)
  618. // no need to unref function declarations
  619. expect(content).toMatch(`{ onClick: fn }`)
  620. // no need to mark constant fns in patch flag
  621. expect(content).not.toMatch(`PROPS`)
  622. assertCode(content)
  623. })
  624. test('v-model codegen', () => {
  625. const { content } = compile(
  626. `<script setup>
  627. import { ref } from 'vue'
  628. const count = ref(0)
  629. const maybe = foo()
  630. let lett = 1
  631. </script>
  632. <template>
  633. <input v-model="count">
  634. <input v-model="maybe">
  635. <input v-model="lett">
  636. </template>
  637. `,
  638. { inlineTemplate: true }
  639. )
  640. // known const ref: set value
  641. expect(content).toMatch(`(count).value = $event`)
  642. // const but maybe ref: assign if ref, otherwise do nothing
  643. expect(content).toMatch(`_isRef(maybe) ? (maybe).value = $event : null`)
  644. // let: handle both cases
  645. expect(content).toMatch(
  646. `_isRef(lett) ? (lett).value = $event : lett = $event`
  647. )
  648. assertCode(content)
  649. })
  650. test('v-model should not generate ref assignment code for non-setup bindings', () => {
  651. const { content } = compile(
  652. `<script setup>
  653. import { ref } from 'vue'
  654. const count = ref(0)
  655. </script>
  656. <script>
  657. export default {
  658. data() { return { foo: 123 } }
  659. }
  660. </script>
  661. <template>
  662. <input v-model="foo">
  663. </template>
  664. `,
  665. { inlineTemplate: true }
  666. )
  667. expect(content).not.toMatch(`_isRef(foo)`)
  668. })
  669. test('template assignment expression codegen', () => {
  670. const { content } = compile(
  671. `<script setup>
  672. import { ref } from 'vue'
  673. const count = ref(0)
  674. const maybe = foo()
  675. let lett = 1
  676. let v = ref(1)
  677. </script>
  678. <template>
  679. <div @click="count = 1"/>
  680. <div @click="maybe = count"/>
  681. <div @click="lett = count"/>
  682. <div @click="v += 1"/>
  683. <div @click="v -= 1"/>
  684. <div @click="() => {
  685. let a = '' + lett
  686. v = a
  687. }"/>
  688. <div @click="() => {
  689. // nested scopes
  690. (()=>{
  691. let x = a
  692. (()=>{
  693. let z = x
  694. let z2 = z
  695. })
  696. let lz = z
  697. })
  698. v = a
  699. }"/>
  700. </template>
  701. `,
  702. { inlineTemplate: true }
  703. )
  704. // known const ref: set value
  705. expect(content).toMatch(`count.value = 1`)
  706. // const but maybe ref: only assign after check
  707. expect(content).toMatch(`maybe.value = count.value`)
  708. // let: handle both cases
  709. expect(content).toMatch(
  710. `_isRef(lett) ? lett.value = count.value : lett = count.value`
  711. )
  712. expect(content).toMatch(`_isRef(v) ? v.value += 1 : v += 1`)
  713. expect(content).toMatch(`_isRef(v) ? v.value -= 1 : v -= 1`)
  714. expect(content).toMatch(`_isRef(v) ? v.value = a : v = a`)
  715. expect(content).toMatch(`_isRef(v) ? v.value = _ctx.a : v = _ctx.a`)
  716. assertCode(content)
  717. })
  718. test('template update expression codegen', () => {
  719. const { content } = compile(
  720. `<script setup>
  721. import { ref } from 'vue'
  722. const count = ref(0)
  723. const maybe = foo()
  724. let lett = 1
  725. </script>
  726. <template>
  727. <div @click="count++"/>
  728. <div @click="--count"/>
  729. <div @click="maybe++"/>
  730. <div @click="--maybe"/>
  731. <div @click="lett++"/>
  732. <div @click="--lett"/>
  733. </template>
  734. `,
  735. { inlineTemplate: true }
  736. )
  737. // known const ref: set value
  738. expect(content).toMatch(`count.value++`)
  739. expect(content).toMatch(`--count.value`)
  740. // const but maybe ref (non-ref case ignored)
  741. expect(content).toMatch(`maybe.value++`)
  742. expect(content).toMatch(`--maybe.value`)
  743. // let: handle both cases
  744. expect(content).toMatch(`_isRef(lett) ? lett.value++ : lett++`)
  745. expect(content).toMatch(`_isRef(lett) ? --lett.value : --lett`)
  746. assertCode(content)
  747. })
  748. test('template destructure assignment codegen', () => {
  749. const { content } = compile(
  750. `<script setup>
  751. import { ref } from 'vue'
  752. const val = {}
  753. const count = ref(0)
  754. const maybe = foo()
  755. let lett = 1
  756. </script>
  757. <template>
  758. <div @click="({ count } = val)"/>
  759. <div @click="[maybe] = val"/>
  760. <div @click="({ lett } = val)"/>
  761. </template>
  762. `,
  763. { inlineTemplate: true }
  764. )
  765. // known const ref: set value
  766. expect(content).toMatch(`({ count: count.value } = val)`)
  767. // const but maybe ref (non-ref case ignored)
  768. expect(content).toMatch(`[maybe.value] = val`)
  769. // let: assumes non-ref
  770. expect(content).toMatch(`{ lett: lett } = val`)
  771. assertCode(content)
  772. })
  773. test('ssr codegen', () => {
  774. const { content } = compile(
  775. `
  776. <script setup>
  777. import { ref } from 'vue'
  778. const count = ref(0)
  779. </script>
  780. <template>
  781. <div>{{ count }}</div>
  782. <div>static</div>
  783. </template>
  784. <style>
  785. div { color: v-bind(count) }
  786. </style>
  787. `,
  788. {
  789. inlineTemplate: true,
  790. templateOptions: {
  791. ssr: true
  792. }
  793. }
  794. )
  795. expect(content).toMatch(`\n __ssrInlineRender: true,\n`)
  796. expect(content).toMatch(`return (_ctx, _push`)
  797. expect(content).toMatch(`ssrInterpolate`)
  798. expect(content).not.toMatch(`useCssVars`)
  799. expect(content).toMatch(`"--${mockId}-count": (count.value)`)
  800. assertCode(content)
  801. })
  802. test('the v-for wrapped in parentheses can be correctly parsed & inline is false', () => {
  803. expect(() =>
  804. compile(
  805. `
  806. <script setup lang="ts">
  807. import { ref } from 'vue'
  808. const stacks = ref([])
  809. </script>
  810. <template>
  811. <div v-for="({ file: efile }) of stacks"></div>
  812. </template>
  813. `,
  814. {
  815. inlineTemplate: false
  816. }
  817. )
  818. ).not.toThrowError()
  819. })
  820. })
  821. describe('with TypeScript', () => {
  822. test('hoist type declarations', () => {
  823. const { content } = compile(`
  824. <script setup lang="ts">
  825. export interface Foo {}
  826. type Bar = {}
  827. </script>`)
  828. assertCode(content)
  829. })
  830. test('runtime Enum', () => {
  831. const { content, bindings } = compile(
  832. `<script setup lang="ts">
  833. enum Foo { A = 123 }
  834. </script>`
  835. )
  836. assertCode(content)
  837. expect(bindings).toStrictEqual({
  838. Foo: BindingTypes.LITERAL_CONST
  839. })
  840. })
  841. test('runtime Enum in normal script', () => {
  842. const { content, bindings } = compile(
  843. `<script lang="ts">
  844. export enum D { D = "D" }
  845. const enum C { C = "C" }
  846. enum B { B = "B" }
  847. </script>
  848. <script setup lang="ts">
  849. enum Foo { A = 123 }
  850. </script>`
  851. )
  852. assertCode(content)
  853. expect(bindings).toStrictEqual({
  854. D: BindingTypes.LITERAL_CONST,
  855. C: BindingTypes.LITERAL_CONST,
  856. B: BindingTypes.LITERAL_CONST,
  857. Foo: BindingTypes.LITERAL_CONST
  858. })
  859. })
  860. test('const Enum', () => {
  861. const { content, bindings } = compile(
  862. `<script setup lang="ts">
  863. const enum Foo { A = 123 }
  864. </script>`,
  865. { hoistStatic: true }
  866. )
  867. assertCode(content)
  868. expect(bindings).toStrictEqual({
  869. Foo: BindingTypes.LITERAL_CONST
  870. })
  871. })
  872. test('import type', () => {
  873. const { content } = compile(
  874. `<script setup lang="ts">
  875. import type { Foo } from './main.ts'
  876. import { type Bar, Baz } from './main.ts'
  877. </script>`
  878. )
  879. expect(content).toMatch(`return { get Baz() { return Baz } }`)
  880. assertCode(content)
  881. })
  882. })
  883. describe('async/await detection', () => {
  884. function assertAwaitDetection(code: string, shouldAsync = true) {
  885. const { content } = compile(`<script setup>${code}</script>`)
  886. if (shouldAsync) {
  887. expect(content).toMatch(`let __temp, __restore`)
  888. }
  889. expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup(`)
  890. assertCode(content)
  891. return content
  892. }
  893. test('expression statement', () => {
  894. assertAwaitDetection(`await foo`)
  895. })
  896. test('variable', () => {
  897. assertAwaitDetection(`const a = 1 + (await foo)`)
  898. })
  899. test('ref', () => {
  900. assertAwaitDetection(`let a = ref(1 + (await foo))`)
  901. })
  902. // #4448
  903. test('nested await', () => {
  904. assertAwaitDetection(`await (await foo)`)
  905. assertAwaitDetection(`await ((await foo))`)
  906. assertAwaitDetection(`await (await (await foo))`)
  907. })
  908. // should prepend semicolon
  909. test('nested leading await in expression statement', () => {
  910. const code = assertAwaitDetection(`foo()\nawait 1 + await 2`)
  911. expect(code).toMatch(`foo()\n;(`)
  912. })
  913. // #4596 should NOT prepend semicolon
  914. test('single line conditions', () => {
  915. const code = assertAwaitDetection(`if (false) await foo()`)
  916. expect(code).not.toMatch(`if (false) ;(`)
  917. })
  918. test('nested statements', () => {
  919. assertAwaitDetection(`if (ok) { await foo } else { await bar }`)
  920. })
  921. test('multiple `if` nested statements', () => {
  922. assertAwaitDetection(`if (ok) {
  923. let a = 'foo'
  924. await 0 + await 1
  925. await 2
  926. } else if (a) {
  927. await 10
  928. if (b) {
  929. await 0 + await 1
  930. } else {
  931. let a = 'foo'
  932. await 2
  933. }
  934. if (b) {
  935. await 3
  936. await 4
  937. }
  938. } else {
  939. await 5
  940. }`)
  941. })
  942. test('multiple `if while` nested statements', () => {
  943. assertAwaitDetection(`if (ok) {
  944. while (d) {
  945. await 5
  946. }
  947. while (d) {
  948. await 5
  949. await 6
  950. if (c) {
  951. let f = 10
  952. 10 + await 7
  953. } else {
  954. await 8
  955. await 9
  956. }
  957. }
  958. }`)
  959. })
  960. test('multiple `if for` nested statements', () => {
  961. assertAwaitDetection(`if (ok) {
  962. for (let a of [1,2,3]) {
  963. await a
  964. }
  965. for (let a of [1,2,3]) {
  966. await a
  967. await a
  968. }
  969. }`)
  970. })
  971. test('should ignore await inside functions', () => {
  972. // function declaration
  973. assertAwaitDetection(`async function foo() { await bar }`, false)
  974. // function expression
  975. assertAwaitDetection(`const foo = async () => { await bar }`, false)
  976. // object method
  977. assertAwaitDetection(`const obj = { async method() { await bar }}`, false)
  978. // class method
  979. assertAwaitDetection(
  980. `const cls = class Foo { async method() { await bar }}`,
  981. false
  982. )
  983. })
  984. })
  985. describe('errors', () => {
  986. test('<script> and <script setup> must have same lang', () => {
  987. expect(() =>
  988. compile(`<script>foo()</script><script setup lang="ts">bar()</script>`)
  989. ).toThrow(`<script> and <script setup> must have the same language type`)
  990. })
  991. const moduleErrorMsg = `cannot contain ES module exports`
  992. test('non-type named exports', () => {
  993. expect(() =>
  994. compile(`<script setup>
  995. export const a = 1
  996. </script>`)
  997. ).toThrow(moduleErrorMsg)
  998. expect(() =>
  999. compile(`<script setup>
  1000. export * from './foo'
  1001. </script>`)
  1002. ).toThrow(moduleErrorMsg)
  1003. expect(() =>
  1004. compile(`<script setup>
  1005. const bar = 1
  1006. export { bar as default }
  1007. </script>`)
  1008. ).toThrow(moduleErrorMsg)
  1009. })
  1010. test('defineProps/Emit() referencing local var', () => {
  1011. expect(() =>
  1012. compile(`<script setup>
  1013. let bar = 1
  1014. defineProps({
  1015. foo: {
  1016. default: () => bar
  1017. }
  1018. })
  1019. </script>`)
  1020. ).toThrow(`cannot reference locally declared variables`)
  1021. expect(() =>
  1022. compile(`<script setup>
  1023. let bar = 'hello'
  1024. defineEmits([bar])
  1025. </script>`)
  1026. ).toThrow(`cannot reference locally declared variables`)
  1027. // #4644
  1028. expect(() =>
  1029. compile(`
  1030. <script>const bar = 1</script>
  1031. <script setup>
  1032. defineProps({
  1033. foo: {
  1034. default: () => bar
  1035. }
  1036. })
  1037. </script>`)
  1038. ).not.toThrow(`cannot reference locally declared variables`)
  1039. })
  1040. test('should allow defineProps/Emit() referencing scope var', () => {
  1041. assertCode(
  1042. compile(`<script setup>
  1043. const bar = 1
  1044. defineProps({
  1045. foo: {
  1046. default: bar => bar + 1
  1047. }
  1048. })
  1049. defineEmits({
  1050. foo: bar => bar > 1
  1051. })
  1052. </script>`).content
  1053. )
  1054. })
  1055. test('should allow defineProps/Emit() referencing imported binding', () => {
  1056. assertCode(
  1057. compile(`<script setup>
  1058. import { bar } from './bar'
  1059. defineProps({
  1060. foo: {
  1061. default: () => bar
  1062. }
  1063. })
  1064. defineEmits({
  1065. foo: () => bar > 1
  1066. })
  1067. </script>`).content
  1068. )
  1069. })
  1070. })
  1071. })
  1072. describe('SFC analyze <script> bindings', () => {
  1073. it('can parse decorators syntax in typescript block', () => {
  1074. const { scriptAst } = compile(`
  1075. <script lang="ts">
  1076. import { Options, Vue } from 'vue-class-component';
  1077. @Options({
  1078. components: {
  1079. HelloWorld,
  1080. },
  1081. props: ['foo', 'bar']
  1082. })
  1083. export default class Home extends Vue {}
  1084. </script>
  1085. `)
  1086. expect(scriptAst).toBeDefined()
  1087. })
  1088. it('recognizes props array declaration', () => {
  1089. const { bindings } = compile(`
  1090. <script>
  1091. export default {
  1092. props: ['foo', 'bar']
  1093. }
  1094. </script>
  1095. `)
  1096. expect(bindings).toStrictEqual({
  1097. foo: BindingTypes.PROPS,
  1098. bar: BindingTypes.PROPS
  1099. })
  1100. expect(bindings!.__isScriptSetup).toBe(false)
  1101. })
  1102. it('recognizes props object declaration', () => {
  1103. const { bindings } = compile(`
  1104. <script>
  1105. export default {
  1106. props: {
  1107. foo: String,
  1108. bar: {
  1109. type: String,
  1110. },
  1111. baz: null,
  1112. qux: [String, Number]
  1113. }
  1114. }
  1115. </script>
  1116. `)
  1117. expect(bindings).toStrictEqual({
  1118. foo: BindingTypes.PROPS,
  1119. bar: BindingTypes.PROPS,
  1120. baz: BindingTypes.PROPS,
  1121. qux: BindingTypes.PROPS
  1122. })
  1123. expect(bindings!.__isScriptSetup).toBe(false)
  1124. })
  1125. it('recognizes setup return', () => {
  1126. const { bindings } = compile(`
  1127. <script>
  1128. const bar = 2
  1129. export default {
  1130. setup() {
  1131. return {
  1132. foo: 1,
  1133. bar
  1134. }
  1135. }
  1136. }
  1137. </script>
  1138. `)
  1139. expect(bindings).toStrictEqual({
  1140. foo: BindingTypes.SETUP_MAYBE_REF,
  1141. bar: BindingTypes.SETUP_MAYBE_REF
  1142. })
  1143. expect(bindings!.__isScriptSetup).toBe(false)
  1144. })
  1145. it('recognizes exported vars', () => {
  1146. const { bindings } = compile(`
  1147. <script>
  1148. export const foo = 2
  1149. </script>
  1150. <script setup>
  1151. console.log(foo)
  1152. </script>
  1153. `)
  1154. expect(bindings).toStrictEqual({
  1155. foo: BindingTypes.LITERAL_CONST
  1156. })
  1157. })
  1158. it('recognizes async setup return', () => {
  1159. const { bindings } = compile(`
  1160. <script>
  1161. const bar = 2
  1162. export default {
  1163. async setup() {
  1164. return {
  1165. foo: 1,
  1166. bar
  1167. }
  1168. }
  1169. }
  1170. </script>
  1171. `)
  1172. expect(bindings).toStrictEqual({
  1173. foo: BindingTypes.SETUP_MAYBE_REF,
  1174. bar: BindingTypes.SETUP_MAYBE_REF
  1175. })
  1176. expect(bindings!.__isScriptSetup).toBe(false)
  1177. })
  1178. it('recognizes data return', () => {
  1179. const { bindings } = compile(`
  1180. <script>
  1181. const bar = 2
  1182. export default {
  1183. data() {
  1184. return {
  1185. foo: null,
  1186. bar
  1187. }
  1188. }
  1189. }
  1190. </script>
  1191. `)
  1192. expect(bindings).toStrictEqual({
  1193. foo: BindingTypes.DATA,
  1194. bar: BindingTypes.DATA
  1195. })
  1196. })
  1197. it('recognizes methods', () => {
  1198. const { bindings } = compile(`
  1199. <script>
  1200. export default {
  1201. methods: {
  1202. foo() {}
  1203. }
  1204. }
  1205. </script>
  1206. `)
  1207. expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS })
  1208. })
  1209. it('recognizes computeds', () => {
  1210. const { bindings } = compile(`
  1211. <script>
  1212. export default {
  1213. computed: {
  1214. foo() {},
  1215. bar: {
  1216. get() {},
  1217. set() {},
  1218. }
  1219. }
  1220. }
  1221. </script>
  1222. `)
  1223. expect(bindings).toStrictEqual({
  1224. foo: BindingTypes.OPTIONS,
  1225. bar: BindingTypes.OPTIONS
  1226. })
  1227. })
  1228. it('recognizes injections array declaration', () => {
  1229. const { bindings } = compile(`
  1230. <script>
  1231. export default {
  1232. inject: ['foo', 'bar']
  1233. }
  1234. </script>
  1235. `)
  1236. expect(bindings).toStrictEqual({
  1237. foo: BindingTypes.OPTIONS,
  1238. bar: BindingTypes.OPTIONS
  1239. })
  1240. })
  1241. it('recognizes injections object declaration', () => {
  1242. const { bindings } = compile(`
  1243. <script>
  1244. export default {
  1245. inject: {
  1246. foo: {},
  1247. bar: {},
  1248. }
  1249. }
  1250. </script>
  1251. `)
  1252. expect(bindings).toStrictEqual({
  1253. foo: BindingTypes.OPTIONS,
  1254. bar: BindingTypes.OPTIONS
  1255. })
  1256. })
  1257. it('works for mixed bindings', () => {
  1258. const { bindings } = compile(`
  1259. <script>
  1260. export default {
  1261. inject: ['foo'],
  1262. props: {
  1263. bar: String,
  1264. },
  1265. setup() {
  1266. return {
  1267. baz: null,
  1268. }
  1269. },
  1270. data() {
  1271. return {
  1272. qux: null
  1273. }
  1274. },
  1275. methods: {
  1276. quux() {}
  1277. },
  1278. computed: {
  1279. quuz() {}
  1280. }
  1281. }
  1282. </script>
  1283. `)
  1284. expect(bindings).toStrictEqual({
  1285. foo: BindingTypes.OPTIONS,
  1286. bar: BindingTypes.PROPS,
  1287. baz: BindingTypes.SETUP_MAYBE_REF,
  1288. qux: BindingTypes.DATA,
  1289. quux: BindingTypes.OPTIONS,
  1290. quuz: BindingTypes.OPTIONS
  1291. })
  1292. })
  1293. it('works for script setup', () => {
  1294. const { bindings } = compile(`
  1295. <script setup>
  1296. import { ref as r } from 'vue'
  1297. defineProps({
  1298. foo: String
  1299. })
  1300. const a = r(1)
  1301. let b = 2
  1302. const c = 3
  1303. const { d } = someFoo()
  1304. let { e } = someBar()
  1305. </script>
  1306. `)
  1307. expect(bindings).toStrictEqual({
  1308. r: BindingTypes.SETUP_CONST,
  1309. a: BindingTypes.SETUP_REF,
  1310. b: BindingTypes.SETUP_LET,
  1311. c: BindingTypes.LITERAL_CONST,
  1312. d: BindingTypes.SETUP_MAYBE_REF,
  1313. e: BindingTypes.SETUP_LET,
  1314. foo: BindingTypes.PROPS
  1315. })
  1316. })
  1317. describe('auto name inference', () => {
  1318. test('basic', () => {
  1319. const { content } = compile(
  1320. `<script setup>const a = 1</script>
  1321. <template>{{ a }}</template>`,
  1322. undefined,
  1323. {
  1324. filename: 'FooBar.vue'
  1325. }
  1326. )
  1327. expect(content).toMatch(`export default {
  1328. __name: 'FooBar'`)
  1329. assertCode(content)
  1330. })
  1331. test('do not overwrite manual name (object)', () => {
  1332. const { content } = compile(
  1333. `<script>
  1334. export default {
  1335. name: 'Baz'
  1336. }
  1337. </script>
  1338. <script setup>const a = 1</script>
  1339. <template>{{ a }}</template>`,
  1340. undefined,
  1341. {
  1342. filename: 'FooBar.vue'
  1343. }
  1344. )
  1345. expect(content).not.toMatch(`name: 'FooBar'`)
  1346. expect(content).toMatch(`name: 'Baz'`)
  1347. assertCode(content)
  1348. })
  1349. test('do not overwrite manual name (call)', () => {
  1350. const { content } = compile(
  1351. `<script>
  1352. import { defineComponent } from 'vue'
  1353. export default defineComponent({
  1354. name: 'Baz'
  1355. })
  1356. </script>
  1357. <script setup>const a = 1</script>
  1358. <template>{{ a }}</template>`,
  1359. undefined,
  1360. {
  1361. filename: 'FooBar.vue'
  1362. }
  1363. )
  1364. expect(content).not.toMatch(`name: 'FooBar'`)
  1365. expect(content).toMatch(`name: 'Baz'`)
  1366. assertCode(content)
  1367. })
  1368. })
  1369. })
  1370. describe('SFC genDefaultAs', () => {
  1371. test('normal <script> only', () => {
  1372. const { content } = compile(
  1373. `<script>
  1374. export default {}
  1375. </script>`,
  1376. {
  1377. genDefaultAs: '_sfc_'
  1378. }
  1379. )
  1380. expect(content).not.toMatch('export default')
  1381. expect(content).toMatch(`const _sfc_ = {}`)
  1382. assertCode(content)
  1383. })
  1384. test('normal <script> w/ cssVars', () => {
  1385. const { content } = compile(
  1386. `<script>
  1387. export default {}
  1388. </script>
  1389. <style>
  1390. .foo { color: v-bind(x) }
  1391. </style>`,
  1392. {
  1393. genDefaultAs: '_sfc_'
  1394. }
  1395. )
  1396. expect(content).not.toMatch('export default')
  1397. expect(content).not.toMatch('__default__')
  1398. expect(content).toMatch(`const _sfc_ = {}`)
  1399. assertCode(content)
  1400. })
  1401. test('<script> + <script setup>', () => {
  1402. const { content } = compile(
  1403. `<script>
  1404. export default {}
  1405. </script>
  1406. <script setup>
  1407. const a = 1
  1408. </script>`,
  1409. {
  1410. genDefaultAs: '_sfc_'
  1411. }
  1412. )
  1413. expect(content).not.toMatch('export default')
  1414. expect(content).toMatch(
  1415. `const _sfc_ = /*#__PURE__*/Object.assign(__default__`
  1416. )
  1417. assertCode(content)
  1418. })
  1419. test('<script> + <script setup>', () => {
  1420. const { content } = compile(
  1421. `<script>
  1422. export default {}
  1423. </script>
  1424. <script setup>
  1425. const a = 1
  1426. </script>`,
  1427. {
  1428. genDefaultAs: '_sfc_'
  1429. }
  1430. )
  1431. expect(content).not.toMatch('export default')
  1432. expect(content).toMatch(
  1433. `const _sfc_ = /*#__PURE__*/Object.assign(__default__`
  1434. )
  1435. assertCode(content)
  1436. })
  1437. test('<script setup> only', () => {
  1438. const { content } = compile(
  1439. `<script setup>
  1440. const a = 1
  1441. </script>`,
  1442. {
  1443. genDefaultAs: '_sfc_'
  1444. }
  1445. )
  1446. expect(content).not.toMatch('export default')
  1447. expect(content).toMatch(`const _sfc_ = {\n setup`)
  1448. assertCode(content)
  1449. })
  1450. test('<script setup> only w/ ts', () => {
  1451. const { content } = compile(
  1452. `<script setup lang="ts">
  1453. const a = 1
  1454. </script>`,
  1455. {
  1456. genDefaultAs: '_sfc_'
  1457. }
  1458. )
  1459. expect(content).not.toMatch('export default')
  1460. expect(content).toMatch(`const _sfc_ = /*#__PURE__*/_defineComponent(`)
  1461. assertCode(content)
  1462. })
  1463. test('<script> + <script setup> w/ ts', () => {
  1464. const { content } = compile(
  1465. `<script lang="ts">
  1466. export default {}
  1467. </script>
  1468. <script setup lang="ts">
  1469. const a = 1
  1470. </script>`,
  1471. {
  1472. genDefaultAs: '_sfc_'
  1473. }
  1474. )
  1475. expect(content).not.toMatch('export default')
  1476. expect(content).toMatch(
  1477. `const _sfc_ = /*#__PURE__*/_defineComponent({\n ...__default__`
  1478. )
  1479. assertCode(content)
  1480. })
  1481. test('binding type for edge cases', () => {
  1482. const { bindings } = compile(
  1483. `<script setup lang="ts">
  1484. import { toRef } from 'vue'
  1485. const props = defineProps<{foo: string}>()
  1486. const foo = toRef(() => props.foo)
  1487. </script>`
  1488. )
  1489. expect(bindings).toStrictEqual({
  1490. toRef: BindingTypes.SETUP_CONST,
  1491. props: BindingTypes.SETUP_REACTIVE_CONST,
  1492. foo: BindingTypes.SETUP_REF
  1493. })
  1494. })
  1495. })