compileScript.spec.ts 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645
  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. const style = { color: 'red' }
  780. </script>
  781. <template>
  782. <div>{{ count }}</div>
  783. <div>static</div>
  784. </template>
  785. <style>
  786. div { color: v-bind(count) }
  787. span { color: v-bind(style.color) }
  788. </style>
  789. `,
  790. {
  791. inlineTemplate: true,
  792. templateOptions: {
  793. ssr: true
  794. }
  795. }
  796. )
  797. expect(content).toMatch(`\n __ssrInlineRender: true,\n`)
  798. expect(content).toMatch(`return (_ctx, _push`)
  799. expect(content).toMatch(`ssrInterpolate`)
  800. expect(content).not.toMatch(`useCssVars`)
  801. expect(content).toMatch(`"--${mockId}-count": (count.value)`)
  802. expect(content).toMatch(`"--${mockId}-style\\\\.color": (style.color)`)
  803. assertCode(content)
  804. })
  805. test('the v-for wrapped in parentheses can be correctly parsed & inline is false', () => {
  806. expect(() =>
  807. compile(
  808. `
  809. <script setup lang="ts">
  810. import { ref } from 'vue'
  811. const stacks = ref([])
  812. </script>
  813. <template>
  814. <div v-for="({ file: efile }) of stacks"></div>
  815. </template>
  816. `,
  817. {
  818. inlineTemplate: false
  819. }
  820. )
  821. ).not.toThrowError()
  822. })
  823. })
  824. describe('with TypeScript', () => {
  825. test('hoist type declarations', () => {
  826. const { content } = compile(`
  827. <script setup lang="ts">
  828. export interface Foo {}
  829. type Bar = {}
  830. </script>`)
  831. assertCode(content)
  832. })
  833. test('runtime Enum', () => {
  834. const { content, bindings } = compile(
  835. `<script setup lang="ts">
  836. enum Foo { A = 123 }
  837. </script>`
  838. )
  839. assertCode(content)
  840. expect(bindings).toStrictEqual({
  841. Foo: BindingTypes.LITERAL_CONST
  842. })
  843. })
  844. test('runtime Enum in normal script', () => {
  845. const { content, bindings } = compile(
  846. `<script lang="ts">
  847. export enum D { D = "D" }
  848. const enum C { C = "C" }
  849. enum B { B = "B" }
  850. </script>
  851. <script setup lang="ts">
  852. enum Foo { A = 123 }
  853. </script>`
  854. )
  855. assertCode(content)
  856. expect(bindings).toStrictEqual({
  857. D: BindingTypes.LITERAL_CONST,
  858. C: BindingTypes.LITERAL_CONST,
  859. B: BindingTypes.LITERAL_CONST,
  860. Foo: BindingTypes.LITERAL_CONST
  861. })
  862. })
  863. test('const Enum', () => {
  864. const { content, bindings } = compile(
  865. `<script setup lang="ts">
  866. const enum Foo { A = 123 }
  867. </script>`,
  868. { hoistStatic: true }
  869. )
  870. assertCode(content)
  871. expect(bindings).toStrictEqual({
  872. Foo: BindingTypes.LITERAL_CONST
  873. })
  874. })
  875. test('import type', () => {
  876. const { content } = compile(
  877. `<script setup lang="ts">
  878. import type { Foo } from './main.ts'
  879. import { type Bar, Baz } from './main.ts'
  880. </script>`
  881. )
  882. expect(content).toMatch(`return { get Baz() { return Baz } }`)
  883. assertCode(content)
  884. })
  885. test('with generic attribute', () => {
  886. const { content } = compile(`
  887. <script setup lang="ts" generic="T extends Record<string,string>">
  888. type Bar = {}
  889. </script>`)
  890. assertCode(content)
  891. })
  892. })
  893. describe('async/await detection', () => {
  894. function assertAwaitDetection(code: string, shouldAsync = true) {
  895. const { content } = compile(`<script setup>${code}</script>`)
  896. if (shouldAsync) {
  897. expect(content).toMatch(`let __temp, __restore`)
  898. }
  899. expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup(`)
  900. assertCode(content)
  901. return content
  902. }
  903. test('expression statement', () => {
  904. assertAwaitDetection(`await foo`)
  905. })
  906. test('variable', () => {
  907. assertAwaitDetection(`const a = 1 + (await foo)`)
  908. })
  909. test('ref', () => {
  910. assertAwaitDetection(`let a = ref(1 + (await foo))`)
  911. })
  912. // #4448
  913. test('nested await', () => {
  914. assertAwaitDetection(`await (await foo)`)
  915. assertAwaitDetection(`await ((await foo))`)
  916. assertAwaitDetection(`await (await (await foo))`)
  917. })
  918. // should prepend semicolon
  919. test('nested leading await in expression statement', () => {
  920. const code = assertAwaitDetection(`foo()\nawait 1 + await 2`)
  921. expect(code).toMatch(`foo()\n;(`)
  922. })
  923. // #4596 should NOT prepend semicolon
  924. test('single line conditions', () => {
  925. const code = assertAwaitDetection(`if (false) await foo()`)
  926. expect(code).not.toMatch(`if (false) ;(`)
  927. })
  928. test('nested statements', () => {
  929. assertAwaitDetection(`if (ok) { await foo } else { await bar }`)
  930. })
  931. test('multiple `if` nested statements', () => {
  932. assertAwaitDetection(`if (ok) {
  933. let a = 'foo'
  934. await 0 + await 1
  935. await 2
  936. } else if (a) {
  937. await 10
  938. if (b) {
  939. await 0 + await 1
  940. } else {
  941. let a = 'foo'
  942. await 2
  943. }
  944. if (b) {
  945. await 3
  946. await 4
  947. }
  948. } else {
  949. await 5
  950. }`)
  951. })
  952. test('multiple `if while` nested statements', () => {
  953. assertAwaitDetection(`if (ok) {
  954. while (d) {
  955. await 5
  956. }
  957. while (d) {
  958. await 5
  959. await 6
  960. if (c) {
  961. let f = 10
  962. 10 + await 7
  963. } else {
  964. await 8
  965. await 9
  966. }
  967. }
  968. }`)
  969. })
  970. test('multiple `if for` nested statements', () => {
  971. assertAwaitDetection(`if (ok) {
  972. for (let a of [1,2,3]) {
  973. await a
  974. }
  975. for (let a of [1,2,3]) {
  976. await a
  977. await a
  978. }
  979. }`)
  980. })
  981. test('should ignore await inside functions', () => {
  982. // function declaration
  983. assertAwaitDetection(`async function foo() { await bar }`, false)
  984. // function expression
  985. assertAwaitDetection(`const foo = async () => { await bar }`, false)
  986. // object method
  987. assertAwaitDetection(`const obj = { async method() { await bar }}`, false)
  988. // class method
  989. assertAwaitDetection(
  990. `const cls = class Foo { async method() { await bar }}`,
  991. false
  992. )
  993. })
  994. })
  995. describe('errors', () => {
  996. test('<script> and <script setup> must have same lang', () => {
  997. expect(() =>
  998. compile(`<script>foo()</script><script setup lang="ts">bar()</script>`)
  999. ).toThrow(`<script> and <script setup> must have the same language type`)
  1000. })
  1001. const moduleErrorMsg = `cannot contain ES module exports`
  1002. test('non-type named exports', () => {
  1003. expect(() =>
  1004. compile(`<script setup>
  1005. export const a = 1
  1006. </script>`)
  1007. ).toThrow(moduleErrorMsg)
  1008. expect(() =>
  1009. compile(`<script setup>
  1010. export * from './foo'
  1011. </script>`)
  1012. ).toThrow(moduleErrorMsg)
  1013. expect(() =>
  1014. compile(`<script setup>
  1015. const bar = 1
  1016. export { bar as default }
  1017. </script>`)
  1018. ).toThrow(moduleErrorMsg)
  1019. })
  1020. test('defineProps/Emit() referencing local var', () => {
  1021. expect(() =>
  1022. compile(`<script setup>
  1023. let bar = 1
  1024. defineProps({
  1025. foo: {
  1026. default: () => bar
  1027. }
  1028. })
  1029. </script>`)
  1030. ).toThrow(`cannot reference locally declared variables`)
  1031. expect(() =>
  1032. compile(`<script setup>
  1033. let bar = 'hello'
  1034. defineEmits([bar])
  1035. </script>`)
  1036. ).toThrow(`cannot reference locally declared variables`)
  1037. // #4644
  1038. expect(() =>
  1039. compile(`
  1040. <script>const bar = 1</script>
  1041. <script setup>
  1042. defineProps({
  1043. foo: {
  1044. default: () => bar
  1045. }
  1046. })
  1047. </script>`)
  1048. ).not.toThrow(`cannot reference locally declared variables`)
  1049. })
  1050. test('should allow defineProps/Emit() referencing scope var', () => {
  1051. assertCode(
  1052. compile(`<script setup>
  1053. const bar = 1
  1054. defineProps({
  1055. foo: {
  1056. default: bar => bar + 1
  1057. }
  1058. })
  1059. defineEmits({
  1060. foo: bar => bar > 1
  1061. })
  1062. </script>`).content
  1063. )
  1064. })
  1065. test('should allow defineProps/Emit() referencing imported binding', () => {
  1066. assertCode(
  1067. compile(`<script setup>
  1068. import { bar } from './bar'
  1069. defineProps({
  1070. foo: {
  1071. default: () => bar
  1072. }
  1073. })
  1074. defineEmits({
  1075. foo: () => bar > 1
  1076. })
  1077. </script>`).content
  1078. )
  1079. })
  1080. })
  1081. })
  1082. describe('SFC analyze <script> bindings', () => {
  1083. it('can parse decorators syntax in typescript block', () => {
  1084. const { scriptAst } = compile(`
  1085. <script lang="ts">
  1086. import { Options, Vue } from 'vue-class-component';
  1087. @Options({
  1088. components: {
  1089. HelloWorld,
  1090. },
  1091. props: ['foo', 'bar']
  1092. })
  1093. export default class Home extends Vue {}
  1094. </script>
  1095. `)
  1096. expect(scriptAst).toBeDefined()
  1097. })
  1098. it('recognizes props array declaration', () => {
  1099. const { bindings } = compile(`
  1100. <script>
  1101. export default {
  1102. props: ['foo', 'bar']
  1103. }
  1104. </script>
  1105. `)
  1106. expect(bindings).toStrictEqual({
  1107. foo: BindingTypes.PROPS,
  1108. bar: BindingTypes.PROPS
  1109. })
  1110. expect(bindings!.__isScriptSetup).toBe(false)
  1111. })
  1112. it('recognizes props object declaration', () => {
  1113. const { bindings } = compile(`
  1114. <script>
  1115. export default {
  1116. props: {
  1117. foo: String,
  1118. bar: {
  1119. type: String,
  1120. },
  1121. baz: null,
  1122. qux: [String, Number]
  1123. }
  1124. }
  1125. </script>
  1126. `)
  1127. expect(bindings).toStrictEqual({
  1128. foo: BindingTypes.PROPS,
  1129. bar: BindingTypes.PROPS,
  1130. baz: BindingTypes.PROPS,
  1131. qux: BindingTypes.PROPS
  1132. })
  1133. expect(bindings!.__isScriptSetup).toBe(false)
  1134. })
  1135. it('recognizes setup return', () => {
  1136. const { bindings } = compile(`
  1137. <script>
  1138. const bar = 2
  1139. export default {
  1140. setup() {
  1141. return {
  1142. foo: 1,
  1143. bar
  1144. }
  1145. }
  1146. }
  1147. </script>
  1148. `)
  1149. expect(bindings).toStrictEqual({
  1150. foo: BindingTypes.SETUP_MAYBE_REF,
  1151. bar: BindingTypes.SETUP_MAYBE_REF
  1152. })
  1153. expect(bindings!.__isScriptSetup).toBe(false)
  1154. })
  1155. it('recognizes exported vars', () => {
  1156. const { bindings } = compile(`
  1157. <script>
  1158. export const foo = 2
  1159. </script>
  1160. <script setup>
  1161. console.log(foo)
  1162. </script>
  1163. `)
  1164. expect(bindings).toStrictEqual({
  1165. foo: BindingTypes.LITERAL_CONST
  1166. })
  1167. })
  1168. it('recognizes async setup return', () => {
  1169. const { bindings } = compile(`
  1170. <script>
  1171. const bar = 2
  1172. export default {
  1173. async setup() {
  1174. return {
  1175. foo: 1,
  1176. bar
  1177. }
  1178. }
  1179. }
  1180. </script>
  1181. `)
  1182. expect(bindings).toStrictEqual({
  1183. foo: BindingTypes.SETUP_MAYBE_REF,
  1184. bar: BindingTypes.SETUP_MAYBE_REF
  1185. })
  1186. expect(bindings!.__isScriptSetup).toBe(false)
  1187. })
  1188. it('recognizes data return', () => {
  1189. const { bindings } = compile(`
  1190. <script>
  1191. const bar = 2
  1192. export default {
  1193. data() {
  1194. return {
  1195. foo: null,
  1196. bar
  1197. }
  1198. }
  1199. }
  1200. </script>
  1201. `)
  1202. expect(bindings).toStrictEqual({
  1203. foo: BindingTypes.DATA,
  1204. bar: BindingTypes.DATA
  1205. })
  1206. })
  1207. it('recognizes methods', () => {
  1208. const { bindings } = compile(`
  1209. <script>
  1210. export default {
  1211. methods: {
  1212. foo() {}
  1213. }
  1214. }
  1215. </script>
  1216. `)
  1217. expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS })
  1218. })
  1219. it('recognizes computeds', () => {
  1220. const { bindings } = compile(`
  1221. <script>
  1222. export default {
  1223. computed: {
  1224. foo() {},
  1225. bar: {
  1226. get() {},
  1227. set() {},
  1228. }
  1229. }
  1230. }
  1231. </script>
  1232. `)
  1233. expect(bindings).toStrictEqual({
  1234. foo: BindingTypes.OPTIONS,
  1235. bar: BindingTypes.OPTIONS
  1236. })
  1237. })
  1238. it('recognizes injections array declaration', () => {
  1239. const { bindings } = compile(`
  1240. <script>
  1241. export default {
  1242. inject: ['foo', 'bar']
  1243. }
  1244. </script>
  1245. `)
  1246. expect(bindings).toStrictEqual({
  1247. foo: BindingTypes.OPTIONS,
  1248. bar: BindingTypes.OPTIONS
  1249. })
  1250. })
  1251. it('recognizes injections object declaration', () => {
  1252. const { bindings } = compile(`
  1253. <script>
  1254. export default {
  1255. inject: {
  1256. foo: {},
  1257. bar: {},
  1258. }
  1259. }
  1260. </script>
  1261. `)
  1262. expect(bindings).toStrictEqual({
  1263. foo: BindingTypes.OPTIONS,
  1264. bar: BindingTypes.OPTIONS
  1265. })
  1266. })
  1267. it('works for mixed bindings', () => {
  1268. const { bindings } = compile(`
  1269. <script>
  1270. export default {
  1271. inject: ['foo'],
  1272. props: {
  1273. bar: String,
  1274. },
  1275. setup() {
  1276. return {
  1277. baz: null,
  1278. }
  1279. },
  1280. data() {
  1281. return {
  1282. qux: null
  1283. }
  1284. },
  1285. methods: {
  1286. quux() {}
  1287. },
  1288. computed: {
  1289. quuz() {}
  1290. }
  1291. }
  1292. </script>
  1293. `)
  1294. expect(bindings).toStrictEqual({
  1295. foo: BindingTypes.OPTIONS,
  1296. bar: BindingTypes.PROPS,
  1297. baz: BindingTypes.SETUP_MAYBE_REF,
  1298. qux: BindingTypes.DATA,
  1299. quux: BindingTypes.OPTIONS,
  1300. quuz: BindingTypes.OPTIONS
  1301. })
  1302. })
  1303. it('works for script setup', () => {
  1304. const { bindings } = compile(`
  1305. <script setup>
  1306. import { ref as r } from 'vue'
  1307. defineProps({
  1308. foo: String
  1309. })
  1310. const a = r(1)
  1311. let b = 2
  1312. const c = 3
  1313. const { d } = someFoo()
  1314. let { e } = someBar()
  1315. </script>
  1316. `)
  1317. expect(bindings).toStrictEqual({
  1318. r: BindingTypes.SETUP_CONST,
  1319. a: BindingTypes.SETUP_REF,
  1320. b: BindingTypes.SETUP_LET,
  1321. c: BindingTypes.LITERAL_CONST,
  1322. d: BindingTypes.SETUP_MAYBE_REF,
  1323. e: BindingTypes.SETUP_LET,
  1324. foo: BindingTypes.PROPS
  1325. })
  1326. })
  1327. describe('auto name inference', () => {
  1328. test('basic', () => {
  1329. const { content } = compile(
  1330. `<script setup>const a = 1</script>
  1331. <template>{{ a }}</template>`,
  1332. undefined,
  1333. {
  1334. filename: 'FooBar.vue'
  1335. }
  1336. )
  1337. expect(content).toMatch(`export default {
  1338. __name: 'FooBar'`)
  1339. assertCode(content)
  1340. })
  1341. test('do not overwrite manual name (object)', () => {
  1342. const { content } = compile(
  1343. `<script>
  1344. export default {
  1345. name: 'Baz'
  1346. }
  1347. </script>
  1348. <script setup>const a = 1</script>
  1349. <template>{{ a }}</template>`,
  1350. undefined,
  1351. {
  1352. filename: 'FooBar.vue'
  1353. }
  1354. )
  1355. expect(content).not.toMatch(`name: 'FooBar'`)
  1356. expect(content).toMatch(`name: 'Baz'`)
  1357. assertCode(content)
  1358. })
  1359. test('do not overwrite manual name (call)', () => {
  1360. const { content } = compile(
  1361. `<script>
  1362. import { defineComponent } from 'vue'
  1363. export default defineComponent({
  1364. name: 'Baz'
  1365. })
  1366. </script>
  1367. <script setup>const a = 1</script>
  1368. <template>{{ a }}</template>`,
  1369. undefined,
  1370. {
  1371. filename: 'FooBar.vue'
  1372. }
  1373. )
  1374. expect(content).not.toMatch(`name: 'FooBar'`)
  1375. expect(content).toMatch(`name: 'Baz'`)
  1376. assertCode(content)
  1377. })
  1378. })
  1379. })
  1380. describe('SFC genDefaultAs', () => {
  1381. test('normal <script> only', () => {
  1382. const { content } = compile(
  1383. `<script>
  1384. export default {}
  1385. </script>`,
  1386. {
  1387. genDefaultAs: '_sfc_'
  1388. }
  1389. )
  1390. expect(content).not.toMatch('export default')
  1391. expect(content).toMatch(`const _sfc_ = {}`)
  1392. assertCode(content)
  1393. })
  1394. test('normal <script> w/ cssVars', () => {
  1395. const { content } = compile(
  1396. `<script>
  1397. export default {}
  1398. </script>
  1399. <style>
  1400. .foo { color: v-bind(x) }
  1401. </style>`,
  1402. {
  1403. genDefaultAs: '_sfc_'
  1404. }
  1405. )
  1406. expect(content).not.toMatch('export default')
  1407. expect(content).not.toMatch('__default__')
  1408. expect(content).toMatch(`const _sfc_ = {}`)
  1409. assertCode(content)
  1410. })
  1411. test('<script> + <script setup>', () => {
  1412. const { content } = compile(
  1413. `<script>
  1414. export default {}
  1415. </script>
  1416. <script setup>
  1417. const a = 1
  1418. </script>`,
  1419. {
  1420. genDefaultAs: '_sfc_'
  1421. }
  1422. )
  1423. expect(content).not.toMatch('export default')
  1424. expect(content).toMatch(
  1425. `const _sfc_ = /*#__PURE__*/Object.assign(__default__`
  1426. )
  1427. assertCode(content)
  1428. })
  1429. test('<script> + <script setup>', () => {
  1430. const { content } = compile(
  1431. `<script>
  1432. export default {}
  1433. </script>
  1434. <script setup>
  1435. const a = 1
  1436. </script>`,
  1437. {
  1438. genDefaultAs: '_sfc_'
  1439. }
  1440. )
  1441. expect(content).not.toMatch('export default')
  1442. expect(content).toMatch(
  1443. `const _sfc_ = /*#__PURE__*/Object.assign(__default__`
  1444. )
  1445. assertCode(content)
  1446. })
  1447. test('<script setup> only', () => {
  1448. const { content } = compile(
  1449. `<script setup>
  1450. const a = 1
  1451. </script>`,
  1452. {
  1453. genDefaultAs: '_sfc_'
  1454. }
  1455. )
  1456. expect(content).not.toMatch('export default')
  1457. expect(content).toMatch(`const _sfc_ = {\n setup`)
  1458. assertCode(content)
  1459. })
  1460. test('<script setup> only w/ ts', () => {
  1461. const { content } = compile(
  1462. `<script setup lang="ts">
  1463. const a = 1
  1464. </script>`,
  1465. {
  1466. genDefaultAs: '_sfc_'
  1467. }
  1468. )
  1469. expect(content).not.toMatch('export default')
  1470. expect(content).toMatch(`const _sfc_ = /*#__PURE__*/_defineComponent(`)
  1471. assertCode(content)
  1472. })
  1473. test('<script> + <script setup> w/ ts', () => {
  1474. const { content } = compile(
  1475. `<script lang="ts">
  1476. export default {}
  1477. </script>
  1478. <script setup lang="ts">
  1479. const a = 1
  1480. </script>`,
  1481. {
  1482. genDefaultAs: '_sfc_'
  1483. }
  1484. )
  1485. expect(content).not.toMatch('export default')
  1486. expect(content).toMatch(
  1487. `const _sfc_ = /*#__PURE__*/_defineComponent({\n ...__default__`
  1488. )
  1489. assertCode(content)
  1490. })
  1491. test('binding type for edge cases', () => {
  1492. const { bindings } = compile(
  1493. `<script setup lang="ts">
  1494. import { toRef } from 'vue'
  1495. const props = defineProps<{foo: string}>()
  1496. const foo = toRef(() => props.foo)
  1497. </script>`
  1498. )
  1499. expect(bindings).toStrictEqual({
  1500. toRef: BindingTypes.SETUP_CONST,
  1501. props: BindingTypes.SETUP_REACTIVE_CONST,
  1502. foo: BindingTypes.SETUP_REF
  1503. })
  1504. })
  1505. describe('parser plugins', () => {
  1506. test('import attributes', () => {
  1507. const { content } = compile(`
  1508. <script setup>
  1509. import { foo } from './foo.js' with { type: 'foobar' }
  1510. </script>
  1511. `)
  1512. assertCode(content)
  1513. expect(() =>
  1514. compile(`
  1515. <script setup>
  1516. import { foo } from './foo.js' assert { type: 'foobar' }
  1517. </script>`)
  1518. ).toThrow()
  1519. })
  1520. test('import attributes (user override for deprecated syntax)', () => {
  1521. const { content } = compile(
  1522. `
  1523. <script setup>
  1524. import { foo } from './foo.js' assert { type: 'foobar' }
  1525. </script>
  1526. `,
  1527. {
  1528. babelParserPlugins: [
  1529. ['importAttributes', { deprecatedAssertSyntax: true }]
  1530. ]
  1531. }
  1532. )
  1533. assertCode(content)
  1534. })
  1535. })
  1536. })