compileScript.spec.ts 42 KB

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