compileScript.spec.ts 64 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334
  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.LITERAL_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. test('defineProps()', () => {
  67. const { content, bindings } = compile(`
  68. <script setup>
  69. const props = defineProps({
  70. foo: String
  71. })
  72. const bar = 1
  73. </script>
  74. `)
  75. // should generate working code
  76. assertCode(content)
  77. // should analyze bindings
  78. expect(bindings).toStrictEqual({
  79. foo: BindingTypes.PROPS,
  80. bar: BindingTypes.LITERAL_CONST,
  81. props: BindingTypes.SETUP_REACTIVE_CONST
  82. })
  83. // should remove defineOptions import and call
  84. expect(content).not.toMatch('defineProps')
  85. // should generate correct setup signature
  86. expect(content).toMatch(`setup(__props, { expose: __expose }) {`)
  87. // should assign user identifier to it
  88. expect(content).toMatch(`const props = __props`)
  89. // should include context options in default export
  90. expect(content).toMatch(`export default {
  91. props: {
  92. foo: String
  93. },`)
  94. })
  95. test('defineProps w/ external definition', () => {
  96. const { content } = compile(`
  97. <script setup>
  98. import { propsModel } from './props'
  99. const props = defineProps(propsModel)
  100. </script>
  101. `)
  102. assertCode(content)
  103. expect(content).toMatch(`export default {
  104. props: propsModel,`)
  105. })
  106. // #4764
  107. test('defineProps w/ leading code', () => {
  108. const { content } = compile(`
  109. <script setup>import { x } from './x'
  110. const props = defineProps({})
  111. </script>
  112. `)
  113. // props declaration should be inside setup, not moved along with the import
  114. expect(content).not.toMatch(`const props = __props\nimport`)
  115. assertCode(content)
  116. })
  117. test('defineEmits()', () => {
  118. const { content, bindings } = compile(`
  119. <script setup>
  120. const myEmit = defineEmits(['foo', 'bar'])
  121. </script>
  122. `)
  123. assertCode(content)
  124. expect(bindings).toStrictEqual({
  125. myEmit: BindingTypes.SETUP_CONST
  126. })
  127. // should remove defineOptions import and call
  128. expect(content).not.toMatch('defineEmits')
  129. // should generate correct setup signature
  130. expect(content).toMatch(
  131. `setup(__props, { expose: __expose, emit: myEmit }) {`
  132. )
  133. // should include context options in default export
  134. expect(content).toMatch(`export default {
  135. emits: ['foo', 'bar'],`)
  136. })
  137. test('defineProps/defineEmits in multi-variable declaration', () => {
  138. const { content } = compile(`
  139. <script setup>
  140. const props = defineProps(['item']),
  141. a = 1,
  142. emit = defineEmits(['a']);
  143. </script>
  144. `)
  145. assertCode(content)
  146. expect(content).toMatch(`const a = 1;`) // test correct removal
  147. expect(content).toMatch(`props: ['item'],`)
  148. expect(content).toMatch(`emits: ['a'],`)
  149. })
  150. // #6757
  151. test('defineProps/defineEmits in multi-variable declaration fix #6757 ', () => {
  152. const { content } = compile(`
  153. <script setup>
  154. const a = 1,
  155. props = defineProps(['item']),
  156. emit = defineEmits(['a']);
  157. </script>
  158. `)
  159. assertCode(content)
  160. expect(content).toMatch(`const a = 1;`) // test correct removal
  161. expect(content).toMatch(`props: ['item'],`)
  162. expect(content).toMatch(`emits: ['a'],`)
  163. })
  164. // #7422
  165. test('defineProps/defineEmits in multi-variable declaration fix #7422', () => {
  166. const { content } = compile(`
  167. <script setup>
  168. const props = defineProps(['item']),
  169. emits = defineEmits(['foo']),
  170. a = 0,
  171. b = 0;
  172. </script>
  173. `)
  174. assertCode(content)
  175. expect(content).toMatch(`props: ['item'],`)
  176. expect(content).toMatch(`emits: ['foo'],`)
  177. expect(content).toMatch(`const a = 0,`)
  178. expect(content).toMatch(`b = 0;`)
  179. })
  180. test('defineProps/defineEmits in multi-variable declaration (full removal)', () => {
  181. const { content } = compile(`
  182. <script setup>
  183. const props = defineProps(['item']),
  184. emit = defineEmits(['a']);
  185. </script>
  186. `)
  187. assertCode(content)
  188. expect(content).toMatch(`props: ['item'],`)
  189. expect(content).toMatch(`emits: ['a'],`)
  190. })
  191. describe('defineOptions()', () => {
  192. test('basic usage', () => {
  193. const { content } = compile(`
  194. <script setup>
  195. defineOptions({ name: 'FooApp' })
  196. </script>
  197. `)
  198. assertCode(content)
  199. // should remove defineOptions import and call
  200. expect(content).not.toMatch('defineOptions')
  201. // should include context options in default export
  202. expect(content).toMatch(
  203. `export default /*#__PURE__*/Object.assign({ name: 'FooApp' }, `
  204. )
  205. })
  206. it('should emit an error with two defineProps', () => {
  207. expect(() =>
  208. compile(`
  209. <script setup>
  210. defineOptions({ name: 'FooApp' })
  211. defineOptions({ name: 'BarApp' })
  212. </script>
  213. `)
  214. ).toThrowError('[@vue/compiler-sfc] duplicate defineOptions() call')
  215. })
  216. it('should emit an error with props or emits property', () => {
  217. expect(() =>
  218. compile(`
  219. <script setup>
  220. defineOptions({ props: { foo: String } })
  221. </script>
  222. `)
  223. ).toThrowError(
  224. '[@vue/compiler-sfc] defineOptions() cannot be used to declare props. Use defineProps() instead.'
  225. )
  226. expect(() =>
  227. compile(`
  228. <script setup>
  229. defineOptions({ emits: ['update'] })
  230. </script>
  231. `)
  232. ).toThrowError(
  233. '[@vue/compiler-sfc] defineOptions() cannot be used to declare emits. Use defineEmits() instead.'
  234. )
  235. })
  236. it('should emit an error with type generic', () => {
  237. expect(() =>
  238. compile(`
  239. <script setup lang="ts">
  240. defineOptions<{ name: 'FooApp' }>()
  241. </script>
  242. `)
  243. ).toThrowError(
  244. '[@vue/compiler-sfc] defineOptions() cannot accept type arguments'
  245. )
  246. })
  247. })
  248. test('defineExpose()', () => {
  249. const { content } = compile(`
  250. <script setup>
  251. defineExpose({ foo: 123 })
  252. </script>
  253. `)
  254. assertCode(content)
  255. // should remove defineOptions import and call
  256. expect(content).not.toMatch('defineExpose')
  257. // should generate correct setup signature
  258. expect(content).toMatch(`setup(__props, { expose: __expose }) {`)
  259. // should replace callee
  260. expect(content).toMatch(/\b__expose\(\{ foo: 123 \}\)/)
  261. })
  262. test('<script> after <script setup> the script content not end with `\\n`', () => {
  263. const { content } = compile(`
  264. <script setup>
  265. import { x } from './x'
  266. </script>
  267. <script>const n = 1</script>
  268. `)
  269. assertCode(content)
  270. })
  271. describe('<script> and <script setup> co-usage', () => {
  272. test('script first', () => {
  273. const { content } = compile(`
  274. <script>
  275. export const n = 1
  276. export default {}
  277. </script>
  278. <script setup>
  279. import { x } from './x'
  280. x()
  281. </script>
  282. `)
  283. assertCode(content)
  284. })
  285. test('script setup first', () => {
  286. const { content } = compile(`
  287. <script setup>
  288. import { x } from './x'
  289. x()
  290. </script>
  291. <script>
  292. export const n = 1
  293. export default {}
  294. </script>
  295. `)
  296. assertCode(content)
  297. })
  298. test('script setup first, named default export', () => {
  299. const { content } = compile(`
  300. <script setup>
  301. import { x } from './x'
  302. x()
  303. </script>
  304. <script>
  305. export const n = 1
  306. const def = {}
  307. export { def as default }
  308. </script>
  309. `)
  310. assertCode(content)
  311. })
  312. // #4395
  313. test('script setup first, lang="ts", script block content export default', () => {
  314. const { content } = compile(`
  315. <script setup lang="ts">
  316. import { x } from './x'
  317. x()
  318. </script>
  319. <script lang="ts">
  320. export default {
  321. name: "test"
  322. }
  323. </script>
  324. `)
  325. // ensure __default__ is declared before used
  326. expect(content).toMatch(/const __default__[\S\s]*\.\.\.__default__/m)
  327. assertCode(content)
  328. })
  329. describe('spaces in ExportDefaultDeclaration node', () => {
  330. // #4371
  331. test('with many spaces and newline', () => {
  332. // #4371
  333. const { content } = compile(`
  334. <script>
  335. export const n = 1
  336. export default
  337. {
  338. some:'option'
  339. }
  340. </script>
  341. <script setup>
  342. import { x } from './x'
  343. x()
  344. </script>
  345. `)
  346. assertCode(content)
  347. })
  348. test('with minimal spaces', () => {
  349. const { content } = compile(`
  350. <script>
  351. export const n = 1
  352. export default{
  353. some:'option'
  354. }
  355. </script>
  356. <script setup>
  357. import { x } from './x'
  358. x()
  359. </script>
  360. `)
  361. assertCode(content)
  362. })
  363. })
  364. test('export call expression as default', () => {
  365. const { content } = compile(`
  366. <script>
  367. function fn() {
  368. return "hello, world";
  369. }
  370. export default fn();
  371. </script>
  372. <script setup>
  373. console.log('foo')
  374. </script>
  375. `)
  376. assertCode(content)
  377. })
  378. })
  379. describe('imports', () => {
  380. test('should hoist and expose imports', () => {
  381. assertCode(
  382. compile(`<script setup>
  383. import { ref } from 'vue'
  384. import 'foo/css'
  385. </script>`).content
  386. )
  387. })
  388. test('should extract comment for import or type declarations', () => {
  389. assertCode(
  390. compile(`
  391. <script setup>
  392. import a from 'a' // comment
  393. import b from 'b'
  394. </script>
  395. `).content
  396. )
  397. })
  398. // #2740
  399. test('should allow defineProps/Emit at the start of imports', () => {
  400. assertCode(
  401. compile(`<script setup>
  402. import { ref } from 'vue'
  403. defineProps(['foo'])
  404. defineEmits(['bar'])
  405. const r = ref(0)
  406. </script>`).content
  407. )
  408. })
  409. test('dedupe between user & helper', () => {
  410. const { content } = compile(
  411. `
  412. <script setup>
  413. import { ref } from 'vue'
  414. let foo = $ref(1)
  415. </script>
  416. `,
  417. { reactivityTransform: true }
  418. )
  419. assertCode(content)
  420. expect(content).toMatch(`import { ref } from 'vue'`)
  421. })
  422. test('import dedupe between <script> and <script setup>', () => {
  423. const { content } = compile(`
  424. <script>
  425. import { x } from './x'
  426. </script>
  427. <script setup>
  428. import { x } from './x'
  429. x()
  430. </script>
  431. `)
  432. assertCode(content)
  433. expect(content.indexOf(`import { x }`)).toEqual(
  434. content.lastIndexOf(`import { x }`)
  435. )
  436. })
  437. describe('import ref/reactive function from other place', () => {
  438. test('import directly', () => {
  439. const { bindings } = compile(`
  440. <script setup>
  441. import { ref, reactive } from './foo'
  442. const foo = ref(1)
  443. const bar = reactive(1)
  444. </script>
  445. `)
  446. expect(bindings).toStrictEqual({
  447. ref: BindingTypes.SETUP_MAYBE_REF,
  448. reactive: BindingTypes.SETUP_MAYBE_REF,
  449. foo: BindingTypes.SETUP_MAYBE_REF,
  450. bar: BindingTypes.SETUP_MAYBE_REF
  451. })
  452. })
  453. test('import w/ alias', () => {
  454. const { bindings } = compile(`
  455. <script setup>
  456. import { ref as _ref, reactive as _reactive } from './foo'
  457. const foo = ref(1)
  458. const bar = reactive(1)
  459. </script>
  460. `)
  461. expect(bindings).toStrictEqual({
  462. _reactive: BindingTypes.SETUP_MAYBE_REF,
  463. _ref: BindingTypes.SETUP_MAYBE_REF,
  464. foo: BindingTypes.SETUP_MAYBE_REF,
  465. bar: BindingTypes.SETUP_MAYBE_REF
  466. })
  467. })
  468. test('aliased usage before import site', () => {
  469. const { bindings } = compile(`
  470. <script setup>
  471. const bar = x(1)
  472. import { reactive as x } from 'vue'
  473. </script>
  474. `)
  475. expect(bindings).toStrictEqual({
  476. bar: BindingTypes.SETUP_REACTIVE_CONST,
  477. x: BindingTypes.SETUP_CONST
  478. })
  479. })
  480. })
  481. test('should support module string names syntax', () => {
  482. const { content, bindings } = compile(`
  483. <script>
  484. import { "😏" as foo } from './foo'
  485. </script>
  486. <script setup>
  487. import { "😏" as foo } from './foo'
  488. </script>
  489. `)
  490. assertCode(content)
  491. expect(bindings).toStrictEqual({
  492. foo: BindingTypes.SETUP_MAYBE_REF
  493. })
  494. })
  495. })
  496. // in dev mode, declared bindings are returned as an object from setup()
  497. // when using TS, users may import types which should not be returned as
  498. // values, so we need to check import usage in the template to determine
  499. // what to be returned.
  500. describe('dev mode import usage check', () => {
  501. test('components', () => {
  502. const { content } = compile(`
  503. <script setup lang="ts">
  504. import { FooBar, FooBaz, FooQux, foo } from './x'
  505. const fooBar: FooBar = 1
  506. </script>
  507. <template>
  508. <FooBaz></FooBaz>
  509. <foo-qux/>
  510. <foo/>
  511. FooBar
  512. </template>
  513. `)
  514. // FooBar: should not be matched by plain text or incorrect case
  515. // FooBaz: used as PascalCase component
  516. // FooQux: used as kebab-case component
  517. // foo: lowercase component
  518. expect(content).toMatch(
  519. `return { fooBar, get FooBaz() { return FooBaz }, ` +
  520. `get FooQux() { return FooQux }, get foo() { return foo } }`
  521. )
  522. assertCode(content)
  523. })
  524. test('directive', () => {
  525. const { content } = compile(`
  526. <script setup lang="ts">
  527. import { vMyDir } from './x'
  528. </script>
  529. <template>
  530. <div v-my-dir></div>
  531. </template>
  532. `)
  533. expect(content).toMatch(`return { get vMyDir() { return vMyDir } }`)
  534. assertCode(content)
  535. })
  536. // https://github.com/vuejs/core/issues/4599
  537. test('attribute expressions', () => {
  538. const { content } = compile(`
  539. <script setup lang="ts">
  540. import { bar, baz } from './x'
  541. const cond = true
  542. </script>
  543. <template>
  544. <div :class="[cond ? '' : bar(), 'default']" :style="baz"></div>
  545. </template>
  546. `)
  547. expect(content).toMatch(
  548. `return { cond, get bar() { return bar }, get baz() { return baz } }`
  549. )
  550. assertCode(content)
  551. })
  552. test('vue interpolations', () => {
  553. const { content } = compile(`
  554. <script setup lang="ts">
  555. import { x, y, z, x$y } from './x'
  556. </script>
  557. <template>
  558. <div :id="z + 'y'">{{ x }} {{ yy }} {{ x$y }}</div>
  559. </template>
  560. `)
  561. // x: used in interpolation
  562. // y: should not be matched by {{ yy }} or 'y' in binding exps
  563. // x$y: #4274 should escape special chars when creating Regex
  564. expect(content).toMatch(
  565. `return { get x() { return x }, get z() { return z }, get x$y() { return x$y } }`
  566. )
  567. assertCode(content)
  568. })
  569. // #4340 interpolations in template strings
  570. test('js template string interpolations', () => {
  571. const { content } = compile(`
  572. <script setup lang="ts">
  573. import { VAR, VAR2, VAR3 } from './x'
  574. </script>
  575. <template>
  576. {{ \`\${VAR}VAR2\${VAR3}\` }}
  577. </template>
  578. `)
  579. // VAR2 should not be matched
  580. expect(content).toMatch(
  581. `return { get VAR() { return VAR }, get VAR3() { return VAR3 } }`
  582. )
  583. assertCode(content)
  584. })
  585. // edge case: last tag in template
  586. test('last tag', () => {
  587. const { content } = compile(`
  588. <script setup lang="ts">
  589. import { FooBaz, Last } from './x'
  590. </script>
  591. <template>
  592. <FooBaz></FooBaz>
  593. <Last/>
  594. </template>
  595. `)
  596. expect(content).toMatch(
  597. `return { get FooBaz() { return FooBaz }, get Last() { return Last } }`
  598. )
  599. assertCode(content)
  600. })
  601. test('TS annotations', () => {
  602. const { content } = compile(`
  603. <script setup lang="ts">
  604. import { Foo, Bar, Baz, Qux, Fred } from './x'
  605. const a = 1
  606. function b() {}
  607. </script>
  608. <template>
  609. {{ a as Foo }}
  610. {{ b<Bar>() }}
  611. {{ Baz }}
  612. <Comp v-slot="{ data }: Qux">{{ data }}</Comp>
  613. <div v-for="{ z = x as Qux } in list as Fred"/>
  614. </template>
  615. `)
  616. expect(content).toMatch(`return { a, b, get Baz() { return Baz } }`)
  617. assertCode(content)
  618. })
  619. // vuejs/vue#12591
  620. test('v-on inline statement', () => {
  621. // should not error
  622. compile(`
  623. <script setup lang="ts">
  624. import { foo } from './foo'
  625. </script>
  626. <template>
  627. <div @click="$emit('update:a');"></div>
  628. </template>
  629. `)
  630. })
  631. })
  632. describe('inlineTemplate mode', () => {
  633. test('should work', () => {
  634. const { content } = compile(
  635. `
  636. <script setup>
  637. import { ref } from 'vue'
  638. const count = ref(0)
  639. </script>
  640. <template>
  641. <div>{{ count }}</div>
  642. <div>static</div>
  643. </template>
  644. `,
  645. { inlineTemplate: true }
  646. )
  647. // check snapshot and make sure helper imports and
  648. // hoists are placed correctly.
  649. assertCode(content)
  650. // in inline mode, no need to call expose() since nothing is exposed
  651. // anyway!
  652. expect(content).not.toMatch(`expose()`)
  653. })
  654. test('with defineExpose()', () => {
  655. const { content } = compile(
  656. `
  657. <script setup>
  658. const count = ref(0)
  659. defineExpose({ count })
  660. </script>
  661. `,
  662. { inlineTemplate: true }
  663. )
  664. assertCode(content)
  665. expect(content).toMatch(`setup(__props, { expose: __expose })`)
  666. expect(content).toMatch(`expose({ count })`)
  667. })
  668. test('referencing scope components and directives', () => {
  669. const { content } = compile(
  670. `
  671. <script setup>
  672. import ChildComp from './Child.vue'
  673. import SomeOtherComp from './Other.vue'
  674. import vMyDir from './my-dir'
  675. </script>
  676. <template>
  677. <div v-my-dir></div>
  678. <ChildComp/>
  679. <some-other-comp/>
  680. </template>
  681. `,
  682. { inlineTemplate: true }
  683. )
  684. expect(content).toMatch('[_unref(vMyDir)]')
  685. expect(content).toMatch('_createVNode(ChildComp)')
  686. // kebab-case component support
  687. expect(content).toMatch('_createVNode(SomeOtherComp)')
  688. assertCode(content)
  689. })
  690. test('avoid unref() when necessary', () => {
  691. // function, const, component import
  692. const { content } = compile(
  693. `<script setup>
  694. import { ref } from 'vue'
  695. import Foo, { bar } from './Foo.vue'
  696. import other from './util'
  697. import * as tree from './tree'
  698. const count = ref(0)
  699. const constant = {}
  700. const maybe = foo()
  701. let lett = 1
  702. function fn() {}
  703. </script>
  704. <template>
  705. <Foo>{{ bar }}</Foo>
  706. <div @click="fn">{{ count }} {{ constant }} {{ maybe }} {{ lett }} {{ other }}</div>
  707. {{ tree.foo() }}
  708. </template>
  709. `,
  710. { inlineTemplate: true }
  711. )
  712. // no need to unref vue component import
  713. expect(content).toMatch(`createVNode(Foo,`)
  714. // #2699 should unref named imports from .vue
  715. expect(content).toMatch(`unref(bar)`)
  716. // should unref other imports
  717. expect(content).toMatch(`unref(other)`)
  718. // no need to unref constant literals
  719. expect(content).not.toMatch(`unref(constant)`)
  720. // should directly use .value for known refs
  721. expect(content).toMatch(`count.value`)
  722. // should unref() on const bindings that may be refs
  723. expect(content).toMatch(`unref(maybe)`)
  724. // should unref() on let bindings
  725. expect(content).toMatch(`unref(lett)`)
  726. // no need to unref namespace import (this also preserves tree-shaking)
  727. expect(content).toMatch(`tree.foo()`)
  728. // no need to unref function declarations
  729. expect(content).toMatch(`{ onClick: fn }`)
  730. // no need to mark constant fns in patch flag
  731. expect(content).not.toMatch(`PROPS`)
  732. assertCode(content)
  733. })
  734. test('v-model codegen', () => {
  735. const { content } = compile(
  736. `<script setup>
  737. import { ref } from 'vue'
  738. const count = ref(0)
  739. const maybe = foo()
  740. let lett = 1
  741. </script>
  742. <template>
  743. <input v-model="count">
  744. <input v-model="maybe">
  745. <input v-model="lett">
  746. </template>
  747. `,
  748. { inlineTemplate: true }
  749. )
  750. // known const ref: set value
  751. expect(content).toMatch(`(count).value = $event`)
  752. // const but maybe ref: assign if ref, otherwise do nothing
  753. expect(content).toMatch(`_isRef(maybe) ? (maybe).value = $event : null`)
  754. // let: handle both cases
  755. expect(content).toMatch(
  756. `_isRef(lett) ? (lett).value = $event : lett = $event`
  757. )
  758. assertCode(content)
  759. })
  760. test('v-model should not generate ref assignment code for non-setup bindings', () => {
  761. const { content } = compile(
  762. `<script setup>
  763. import { ref } from 'vue'
  764. const count = ref(0)
  765. </script>
  766. <script>
  767. export default {
  768. data() { return { foo: 123 } }
  769. }
  770. </script>
  771. <template>
  772. <input v-model="foo">
  773. </template>
  774. `,
  775. { inlineTemplate: true }
  776. )
  777. expect(content).not.toMatch(`_isRef(foo)`)
  778. })
  779. test('template assignment expression codegen', () => {
  780. const { content } = compile(
  781. `<script setup>
  782. import { ref } from 'vue'
  783. const count = ref(0)
  784. const maybe = foo()
  785. let lett = 1
  786. let v = ref(1)
  787. </script>
  788. <template>
  789. <div @click="count = 1"/>
  790. <div @click="maybe = count"/>
  791. <div @click="lett = count"/>
  792. <div @click="v += 1"/>
  793. <div @click="v -= 1"/>
  794. <div @click="() => {
  795. let a = '' + lett
  796. v = a
  797. }"/>
  798. <div @click="() => {
  799. // nested scopes
  800. (()=>{
  801. let x = a
  802. (()=>{
  803. let z = x
  804. let z2 = z
  805. })
  806. let lz = z
  807. })
  808. v = a
  809. }"/>
  810. </template>
  811. `,
  812. { inlineTemplate: true }
  813. )
  814. // known const ref: set value
  815. expect(content).toMatch(`count.value = 1`)
  816. // const but maybe ref: only assign after check
  817. expect(content).toMatch(`maybe.value = count.value`)
  818. // let: handle both cases
  819. expect(content).toMatch(
  820. `_isRef(lett) ? lett.value = count.value : lett = count.value`
  821. )
  822. expect(content).toMatch(`_isRef(v) ? v.value += 1 : v += 1`)
  823. expect(content).toMatch(`_isRef(v) ? v.value -= 1 : v -= 1`)
  824. expect(content).toMatch(`_isRef(v) ? v.value = a : v = a`)
  825. expect(content).toMatch(`_isRef(v) ? v.value = _ctx.a : v = _ctx.a`)
  826. assertCode(content)
  827. })
  828. test('template update expression codegen', () => {
  829. const { content } = compile(
  830. `<script setup>
  831. import { ref } from 'vue'
  832. const count = ref(0)
  833. const maybe = foo()
  834. let lett = 1
  835. </script>
  836. <template>
  837. <div @click="count++"/>
  838. <div @click="--count"/>
  839. <div @click="maybe++"/>
  840. <div @click="--maybe"/>
  841. <div @click="lett++"/>
  842. <div @click="--lett"/>
  843. </template>
  844. `,
  845. { inlineTemplate: true }
  846. )
  847. // known const ref: set value
  848. expect(content).toMatch(`count.value++`)
  849. expect(content).toMatch(`--count.value`)
  850. // const but maybe ref (non-ref case ignored)
  851. expect(content).toMatch(`maybe.value++`)
  852. expect(content).toMatch(`--maybe.value`)
  853. // let: handle both cases
  854. expect(content).toMatch(`_isRef(lett) ? lett.value++ : lett++`)
  855. expect(content).toMatch(`_isRef(lett) ? --lett.value : --lett`)
  856. assertCode(content)
  857. })
  858. test('template destructure assignment codegen', () => {
  859. const { content } = compile(
  860. `<script setup>
  861. import { ref } from 'vue'
  862. const val = {}
  863. const count = ref(0)
  864. const maybe = foo()
  865. let lett = 1
  866. </script>
  867. <template>
  868. <div @click="({ count } = val)"/>
  869. <div @click="[maybe] = val"/>
  870. <div @click="({ lett } = val)"/>
  871. </template>
  872. `,
  873. { inlineTemplate: true }
  874. )
  875. // known const ref: set value
  876. expect(content).toMatch(`({ count: count.value } = val)`)
  877. // const but maybe ref (non-ref case ignored)
  878. expect(content).toMatch(`[maybe.value] = val`)
  879. // let: assumes non-ref
  880. expect(content).toMatch(`{ lett: lett } = val`)
  881. assertCode(content)
  882. })
  883. test('ssr codegen', () => {
  884. const { content } = compile(
  885. `
  886. <script setup>
  887. import { ref } from 'vue'
  888. const count = ref(0)
  889. </script>
  890. <template>
  891. <div>{{ count }}</div>
  892. <div>static</div>
  893. </template>
  894. <style>
  895. div { color: v-bind(count) }
  896. </style>
  897. `,
  898. {
  899. inlineTemplate: true,
  900. templateOptions: {
  901. ssr: true
  902. }
  903. }
  904. )
  905. expect(content).toMatch(`\n __ssrInlineRender: true,\n`)
  906. expect(content).toMatch(`return (_ctx, _push`)
  907. expect(content).toMatch(`ssrInterpolate`)
  908. expect(content).not.toMatch(`useCssVars`)
  909. expect(content).toMatch(`"--${mockId}-count": (count.value)`)
  910. assertCode(content)
  911. })
  912. })
  913. describe('with TypeScript', () => {
  914. test('hoist type declarations', () => {
  915. const { content } = compile(`
  916. <script setup lang="ts">
  917. export interface Foo {}
  918. type Bar = {}
  919. </script>`)
  920. assertCode(content)
  921. })
  922. test('defineProps/Emit w/ runtime options', () => {
  923. const { content } = compile(`
  924. <script setup lang="ts">
  925. const props = defineProps({ foo: String })
  926. const emit = defineEmits(['a', 'b'])
  927. </script>
  928. `)
  929. assertCode(content)
  930. expect(content).toMatch(`export default /*#__PURE__*/_defineComponent({
  931. props: { foo: String },
  932. emits: ['a', 'b'],
  933. setup(__props, { expose: __expose, emit }) {`)
  934. })
  935. test('defineProps w/ type', () => {
  936. const { content, bindings } = compile(`
  937. <script setup lang="ts">
  938. interface Test {}
  939. type Alias = number[]
  940. defineProps<{
  941. string: string
  942. number: number
  943. boolean: boolean
  944. object: object
  945. objectLiteral: { a: number }
  946. fn: (n: number) => void
  947. functionRef: Function
  948. objectRef: Object
  949. dateTime: Date
  950. array: string[]
  951. arrayRef: Array<any>
  952. tuple: [number, number]
  953. set: Set<string>
  954. literal: 'foo'
  955. optional?: any
  956. recordRef: Record<string, null>
  957. interface: Test
  958. alias: Alias
  959. method(): void
  960. symbol: symbol
  961. extract: Extract<1 | 2 | boolean, 2>
  962. exclude: Exclude<1 | 2 | boolean, 2>
  963. uppercase: Uppercase<'foo'>
  964. params: Parameters<(foo: any) => void>
  965. nonNull: NonNullable<string | null>
  966. objectOrFn: {
  967. (): void
  968. foo: string
  969. }
  970. union: string | number
  971. literalUnion: 'foo' | 'bar'
  972. literalUnionNumber: 1 | 2 | 3 | 4 | 5
  973. literalUnionMixed: 'foo' | 1 | boolean
  974. intersection: Test & {}
  975. intersection2: 'foo' & ('foo' | 'bar')
  976. foo: ((item: any) => boolean) | null
  977. unknown: UnknownType
  978. unknownUnion: UnknownType | string
  979. unknownIntersection: UnknownType & Object
  980. unknownUnionWithBoolean: UnknownType | boolean
  981. unknownUnionWithFunction: UnknownType | (() => any)
  982. }>()
  983. </script>`)
  984. assertCode(content)
  985. expect(content).toMatch(`string: { type: String, required: true }`)
  986. expect(content).toMatch(`number: { type: Number, required: true }`)
  987. expect(content).toMatch(`boolean: { type: Boolean, required: true }`)
  988. expect(content).toMatch(`object: { type: Object, required: true }`)
  989. expect(content).toMatch(`objectLiteral: { type: Object, required: true }`)
  990. expect(content).toMatch(`fn: { type: Function, required: true }`)
  991. expect(content).toMatch(`functionRef: { type: Function, required: true }`)
  992. expect(content).toMatch(`objectRef: { type: Object, required: true }`)
  993. expect(content).toMatch(`dateTime: { type: Date, required: true }`)
  994. expect(content).toMatch(`array: { type: Array, required: true }`)
  995. expect(content).toMatch(`arrayRef: { type: Array, required: true }`)
  996. expect(content).toMatch(`tuple: { type: Array, required: true }`)
  997. expect(content).toMatch(`set: { type: Set, required: true }`)
  998. expect(content).toMatch(`literal: { type: String, required: true }`)
  999. expect(content).toMatch(`optional: { type: null, required: false }`)
  1000. expect(content).toMatch(`recordRef: { type: Object, required: true }`)
  1001. expect(content).toMatch(`interface: { type: Object, required: true }`)
  1002. expect(content).toMatch(`alias: { type: Array, required: true }`)
  1003. expect(content).toMatch(`method: { type: Function, required: true }`)
  1004. expect(content).toMatch(`symbol: { type: Symbol, required: true }`)
  1005. expect(content).toMatch(
  1006. `objectOrFn: { type: [Function, Object], required: true },`
  1007. )
  1008. expect(content).toMatch(`extract: { type: Number, required: true }`)
  1009. expect(content).toMatch(
  1010. `exclude: { type: [Number, Boolean], required: true }`
  1011. )
  1012. expect(content).toMatch(`uppercase: { type: String, required: true }`)
  1013. expect(content).toMatch(`params: { type: Array, required: true }`)
  1014. expect(content).toMatch(`nonNull: { type: String, required: true }`)
  1015. expect(content).toMatch(
  1016. `union: { type: [String, Number], required: true }`
  1017. )
  1018. expect(content).toMatch(`literalUnion: { type: String, required: true }`)
  1019. expect(content).toMatch(
  1020. `literalUnionNumber: { type: Number, required: true }`
  1021. )
  1022. expect(content).toMatch(
  1023. `literalUnionMixed: { type: [String, Number, Boolean], required: true }`
  1024. )
  1025. expect(content).toMatch(`intersection: { type: Object, required: true }`)
  1026. expect(content).toMatch(`intersection2: { type: String, required: true }`)
  1027. expect(content).toMatch(`foo: { type: [Function, null], required: true }`)
  1028. expect(content).toMatch(`unknown: { type: null, required: true }`)
  1029. // uninon containing unknown type: skip check
  1030. expect(content).toMatch(`unknownUnion: { type: null, required: true }`)
  1031. // intersection containing unknown type: narrow to the known types
  1032. expect(content).toMatch(
  1033. `unknownIntersection: { type: Object, required: true },`
  1034. )
  1035. expect(content).toMatch(
  1036. `unknownUnionWithBoolean: { type: Boolean, required: true, skipCheck: true },`
  1037. )
  1038. expect(content).toMatch(
  1039. `unknownUnionWithFunction: { type: Function, required: true, skipCheck: true }`
  1040. )
  1041. expect(bindings).toStrictEqual({
  1042. string: BindingTypes.PROPS,
  1043. number: BindingTypes.PROPS,
  1044. boolean: BindingTypes.PROPS,
  1045. object: BindingTypes.PROPS,
  1046. objectLiteral: BindingTypes.PROPS,
  1047. fn: BindingTypes.PROPS,
  1048. functionRef: BindingTypes.PROPS,
  1049. objectRef: BindingTypes.PROPS,
  1050. dateTime: BindingTypes.PROPS,
  1051. array: BindingTypes.PROPS,
  1052. arrayRef: BindingTypes.PROPS,
  1053. tuple: BindingTypes.PROPS,
  1054. set: BindingTypes.PROPS,
  1055. literal: BindingTypes.PROPS,
  1056. optional: BindingTypes.PROPS,
  1057. recordRef: BindingTypes.PROPS,
  1058. interface: BindingTypes.PROPS,
  1059. alias: BindingTypes.PROPS,
  1060. method: BindingTypes.PROPS,
  1061. symbol: BindingTypes.PROPS,
  1062. objectOrFn: BindingTypes.PROPS,
  1063. extract: BindingTypes.PROPS,
  1064. exclude: BindingTypes.PROPS,
  1065. union: BindingTypes.PROPS,
  1066. literalUnion: BindingTypes.PROPS,
  1067. literalUnionNumber: BindingTypes.PROPS,
  1068. literalUnionMixed: BindingTypes.PROPS,
  1069. intersection: BindingTypes.PROPS,
  1070. intersection2: BindingTypes.PROPS,
  1071. foo: BindingTypes.PROPS,
  1072. uppercase: BindingTypes.PROPS,
  1073. params: BindingTypes.PROPS,
  1074. nonNull: BindingTypes.PROPS,
  1075. unknown: BindingTypes.PROPS,
  1076. unknownUnion: BindingTypes.PROPS,
  1077. unknownIntersection: BindingTypes.PROPS,
  1078. unknownUnionWithBoolean: BindingTypes.PROPS,
  1079. unknownUnionWithFunction: BindingTypes.PROPS
  1080. })
  1081. })
  1082. test('defineProps w/ interface', () => {
  1083. const { content, bindings } = compile(`
  1084. <script setup lang="ts">
  1085. interface Props { x?: number }
  1086. defineProps<Props>()
  1087. </script>
  1088. `)
  1089. assertCode(content)
  1090. expect(content).toMatch(`x: { type: Number, required: false }`)
  1091. expect(bindings).toStrictEqual({
  1092. x: BindingTypes.PROPS
  1093. })
  1094. })
  1095. test('defineProps w/ extends interface', () => {
  1096. const { content, bindings } = compile(`
  1097. <script lang="ts">
  1098. interface Foo { x?: number }
  1099. </script>
  1100. <script setup lang="ts">
  1101. interface Bar extends Foo { y?: number }
  1102. interface Props extends Bar {
  1103. z: number
  1104. y: string
  1105. }
  1106. defineProps<Props>()
  1107. </script>
  1108. `)
  1109. assertCode(content)
  1110. expect(content).toMatch(`z: { type: Number, required: true }`)
  1111. expect(content).toMatch(`y: { type: String, required: true }`)
  1112. expect(content).toMatch(`x: { type: Number, required: false }`)
  1113. expect(bindings).toStrictEqual({
  1114. x: BindingTypes.PROPS,
  1115. y: BindingTypes.PROPS,
  1116. z: BindingTypes.PROPS
  1117. })
  1118. })
  1119. test('defineProps w/ exported interface', () => {
  1120. const { content, bindings } = compile(`
  1121. <script setup lang="ts">
  1122. export interface Props { x?: number }
  1123. defineProps<Props>()
  1124. </script>
  1125. `)
  1126. assertCode(content)
  1127. expect(content).toMatch(`x: { type: Number, required: false }`)
  1128. expect(bindings).toStrictEqual({
  1129. x: BindingTypes.PROPS
  1130. })
  1131. })
  1132. test('defineProps w/ exported interface in normal script', () => {
  1133. const { content, bindings } = compile(`
  1134. <script lang="ts">
  1135. export interface Props { x?: number }
  1136. </script>
  1137. <script setup lang="ts">
  1138. defineProps<Props>()
  1139. </script>
  1140. `)
  1141. assertCode(content)
  1142. expect(content).toMatch(`x: { type: Number, required: false }`)
  1143. expect(bindings).toStrictEqual({
  1144. x: BindingTypes.PROPS
  1145. })
  1146. })
  1147. test('defineProps w/ type alias', () => {
  1148. const { content, bindings } = compile(`
  1149. <script setup lang="ts">
  1150. type Props = { x?: number }
  1151. defineProps<Props>()
  1152. </script>
  1153. `)
  1154. assertCode(content)
  1155. expect(content).toMatch(`x: { type: Number, required: false }`)
  1156. expect(bindings).toStrictEqual({
  1157. x: BindingTypes.PROPS
  1158. })
  1159. })
  1160. test('defineProps w/ exported type alias', () => {
  1161. const { content, bindings } = compile(`
  1162. <script setup lang="ts">
  1163. export type Props = { x?: number }
  1164. defineProps<Props>()
  1165. </script>
  1166. `)
  1167. assertCode(content)
  1168. expect(content).toMatch(`x: { type: Number, required: false }`)
  1169. expect(bindings).toStrictEqual({
  1170. x: BindingTypes.PROPS
  1171. })
  1172. })
  1173. test('defineProps w/ TS assertion', () => {
  1174. const { content, bindings } = compile(`
  1175. <script setup lang="ts">
  1176. defineProps(['foo'])! as any
  1177. </script>
  1178. `)
  1179. expect(content).toMatch(`props: ['foo']`)
  1180. assertCode(content)
  1181. expect(bindings).toStrictEqual({
  1182. foo: BindingTypes.PROPS
  1183. })
  1184. })
  1185. test('withDefaults (static)', () => {
  1186. const { content, bindings } = compile(`
  1187. <script setup lang="ts">
  1188. const props = withDefaults(defineProps<{
  1189. foo?: string
  1190. bar?: number;
  1191. baz: boolean;
  1192. qux?(): number;
  1193. quux?(): void
  1194. quuxx?: Promise<string>;
  1195. fred?: string
  1196. }>(), {
  1197. foo: 'hi',
  1198. qux() { return 1 },
  1199. ['quux']() { },
  1200. async quuxx() { return await Promise.resolve('hi') },
  1201. get fred() { return 'fred' }
  1202. })
  1203. </script>
  1204. `)
  1205. assertCode(content)
  1206. expect(content).toMatch(
  1207. `foo: { type: String, required: false, default: 'hi' }`
  1208. )
  1209. expect(content).toMatch(`bar: { type: Number, required: false }`)
  1210. expect(content).toMatch(`baz: { type: Boolean, required: true }`)
  1211. expect(content).toMatch(
  1212. `qux: { type: Function, required: false, default() { return 1 } }`
  1213. )
  1214. expect(content).toMatch(
  1215. `quux: { type: Function, required: false, default() { } }`
  1216. )
  1217. expect(content).toMatch(
  1218. `quuxx: { type: Promise, required: false, async default() { return await Promise.resolve('hi') } }`
  1219. )
  1220. expect(content).toMatch(
  1221. `fred: { type: String, required: false, get default() { return 'fred' } }`
  1222. )
  1223. expect(content).toMatch(
  1224. `{ foo: string, bar?: number, baz: boolean, qux(): number, quux(): void, quuxx: Promise<string>, fred: string }`
  1225. )
  1226. expect(content).toMatch(`const props = __props`)
  1227. expect(bindings).toStrictEqual({
  1228. foo: BindingTypes.PROPS,
  1229. bar: BindingTypes.PROPS,
  1230. baz: BindingTypes.PROPS,
  1231. qux: BindingTypes.PROPS,
  1232. quux: BindingTypes.PROPS,
  1233. quuxx: BindingTypes.PROPS,
  1234. fred: BindingTypes.PROPS,
  1235. props: BindingTypes.SETUP_CONST
  1236. })
  1237. })
  1238. test('withDefaults (static) + normal script', () => {
  1239. const { content } = compile(`
  1240. <script lang="ts">
  1241. interface Props {
  1242. a?: string;
  1243. }
  1244. </script>
  1245. <script setup lang="ts">
  1246. const props = withDefaults(defineProps<Props>(), {
  1247. a: "a",
  1248. });
  1249. </script>
  1250. `)
  1251. assertCode(content)
  1252. })
  1253. // #7111
  1254. test('withDefaults (static) w/ production mode', () => {
  1255. const { content } = compile(
  1256. `
  1257. <script setup lang="ts">
  1258. const props = withDefaults(defineProps<{
  1259. foo: () => void
  1260. bar: boolean
  1261. baz: boolean | (() => void)
  1262. qux: string | number
  1263. }>(), {
  1264. baz: true,
  1265. qux: 'hi'
  1266. })
  1267. </script>
  1268. `,
  1269. { isProd: true }
  1270. )
  1271. assertCode(content)
  1272. expect(content).toMatch(`const props = __props`)
  1273. // foo has no default value, the Function can be dropped
  1274. expect(content).toMatch(`foo: null`)
  1275. expect(content).toMatch(`bar: { type: Boolean }`)
  1276. expect(content).toMatch(
  1277. `baz: { type: [Boolean, Function], default: true }`
  1278. )
  1279. expect(content).toMatch(`qux: { default: 'hi' }`)
  1280. })
  1281. test('withDefaults (dynamic)', () => {
  1282. const { content } = compile(`
  1283. <script setup lang="ts">
  1284. import { defaults } from './foo'
  1285. const props = withDefaults(defineProps<{
  1286. foo?: string
  1287. bar?: number
  1288. baz: boolean
  1289. }>(), { ...defaults })
  1290. </script>
  1291. `)
  1292. assertCode(content)
  1293. expect(content).toMatch(`import { mergeDefaults as _mergeDefaults`)
  1294. expect(content).toMatch(
  1295. `
  1296. _mergeDefaults({
  1297. foo: { type: String, required: false },
  1298. bar: { type: Number, required: false },
  1299. baz: { type: Boolean, required: true }
  1300. }, { ...defaults })`.trim()
  1301. )
  1302. })
  1303. // #7111
  1304. test('withDefaults (dynamic) w/ production mode', () => {
  1305. const { content } = compile(
  1306. `
  1307. <script setup lang="ts">
  1308. import { defaults } from './foo'
  1309. const props = withDefaults(defineProps<{
  1310. foo: () => void
  1311. bar: boolean
  1312. baz: boolean | (() => void)
  1313. qux: string | number
  1314. }>(), { ...defaults })
  1315. </script>
  1316. `,
  1317. { isProd: true }
  1318. )
  1319. assertCode(content)
  1320. expect(content).toMatch(`import { mergeDefaults as _mergeDefaults`)
  1321. expect(content).toMatch(
  1322. `
  1323. _mergeDefaults({
  1324. foo: { type: Function },
  1325. bar: { type: Boolean },
  1326. baz: { type: [Boolean, Function] },
  1327. qux: null
  1328. }, { ...defaults })`.trim()
  1329. )
  1330. })
  1331. test('defineEmits w/ type', () => {
  1332. const { content } = compile(`
  1333. <script setup lang="ts">
  1334. const emit = defineEmits<(e: 'foo' | 'bar') => void>()
  1335. </script>
  1336. `)
  1337. assertCode(content)
  1338. expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
  1339. expect(content).toMatch(`emits: ["foo", "bar"]`)
  1340. })
  1341. test('defineEmits w/ type (union)', () => {
  1342. const type = `((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void)`
  1343. expect(() =>
  1344. compile(`
  1345. <script setup lang="ts">
  1346. const emit = defineEmits<${type}>()
  1347. </script>
  1348. `)
  1349. ).toThrow()
  1350. })
  1351. test('defineEmits w/ type (type literal w/ call signatures)', () => {
  1352. const type = `{(e: 'foo' | 'bar'): void; (e: 'baz', id: number): void;}`
  1353. const { content } = compile(`
  1354. <script setup lang="ts">
  1355. const emit = defineEmits<${type}>()
  1356. </script>
  1357. `)
  1358. assertCode(content)
  1359. expect(content).toMatch(`emit: (${type}),`)
  1360. expect(content).toMatch(`emits: ["foo", "bar", "baz"]`)
  1361. })
  1362. test('defineEmits w/ type (interface)', () => {
  1363. const { content } = compile(`
  1364. <script setup lang="ts">
  1365. interface Emits { (e: 'foo' | 'bar'): void }
  1366. const emit = defineEmits<Emits>()
  1367. </script>
  1368. `)
  1369. assertCode(content)
  1370. expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
  1371. expect(content).toMatch(`emits: ["foo", "bar"]`)
  1372. })
  1373. test('defineEmits w/ type (exported interface)', () => {
  1374. const { content } = compile(`
  1375. <script setup lang="ts">
  1376. export interface Emits { (e: 'foo' | 'bar'): void }
  1377. const emit = defineEmits<Emits>()
  1378. </script>
  1379. `)
  1380. assertCode(content)
  1381. expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
  1382. expect(content).toMatch(`emits: ["foo", "bar"]`)
  1383. })
  1384. test('defineEmits w/ type from normal script', () => {
  1385. const { content } = compile(`
  1386. <script lang="ts">
  1387. export interface Emits { (e: 'foo' | 'bar'): void }
  1388. </script>
  1389. <script setup lang="ts">
  1390. const emit = defineEmits<Emits>()
  1391. </script>
  1392. `)
  1393. assertCode(content)
  1394. expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
  1395. expect(content).toMatch(`emits: ["foo", "bar"]`)
  1396. })
  1397. test('defineEmits w/ type (type alias)', () => {
  1398. const { content } = compile(`
  1399. <script setup lang="ts">
  1400. type Emits = { (e: 'foo' | 'bar'): void }
  1401. const emit = defineEmits<Emits>()
  1402. </script>
  1403. `)
  1404. assertCode(content)
  1405. expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
  1406. expect(content).toMatch(`emits: ["foo", "bar"]`)
  1407. })
  1408. test('defineEmits w/ type (exported type alias)', () => {
  1409. const { content } = compile(`
  1410. <script setup lang="ts">
  1411. export type Emits = { (e: 'foo' | 'bar'): void }
  1412. const emit = defineEmits<Emits>()
  1413. </script>
  1414. `)
  1415. assertCode(content)
  1416. expect(content).toMatch(`emit: ({ (e: 'foo' | 'bar'): void }),`)
  1417. expect(content).toMatch(`emits: ["foo", "bar"]`)
  1418. })
  1419. test('defineEmits w/ type (referenced function type)', () => {
  1420. const { content } = compile(`
  1421. <script setup lang="ts">
  1422. type Emits = (e: 'foo' | 'bar') => void
  1423. const emit = defineEmits<Emits>()
  1424. </script>
  1425. `)
  1426. assertCode(content)
  1427. expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
  1428. expect(content).toMatch(`emits: ["foo", "bar"]`)
  1429. })
  1430. test('defineEmits w/ type (referenced exported function type)', () => {
  1431. const { content } = compile(`
  1432. <script setup lang="ts">
  1433. export type Emits = (e: 'foo' | 'bar') => void
  1434. const emit = defineEmits<Emits>()
  1435. </script>
  1436. `)
  1437. assertCode(content)
  1438. expect(content).toMatch(`emit: ((e: 'foo' | 'bar') => void),`)
  1439. expect(content).toMatch(`emits: ["foo", "bar"]`)
  1440. })
  1441. // #5393
  1442. test('defineEmits w/ type (interface ts type)', () => {
  1443. const { content } = compile(`
  1444. <script setup lang="ts">
  1445. interface Emits { (e: 'foo'): void }
  1446. const emit: Emits = defineEmits(['foo'])
  1447. </script>
  1448. `)
  1449. assertCode(content)
  1450. expect(content).toMatch(`setup(__props, { expose: __expose, emit }) {`)
  1451. expect(content).toMatch(`emits: ['foo']`)
  1452. })
  1453. test('defineEmits w/ type (tuple syntax)', () => {
  1454. const { content } = compile(`
  1455. <script setup lang="ts">
  1456. const emit = defineEmits<{ foo: [], bar: [] }>()
  1457. </script>
  1458. `)
  1459. expect(content).toMatch(`emits: ["foo", "bar"]`)
  1460. assertCode(content)
  1461. })
  1462. test('runtime Enum', () => {
  1463. const { content, bindings } = compile(
  1464. `<script setup lang="ts">
  1465. enum Foo { A = 123 }
  1466. </script>`
  1467. )
  1468. assertCode(content)
  1469. expect(bindings).toStrictEqual({
  1470. Foo: BindingTypes.LITERAL_CONST
  1471. })
  1472. })
  1473. test('runtime Enum in normal script', () => {
  1474. const { content, bindings } = compile(
  1475. `<script lang="ts">
  1476. export enum D { D = "D" }
  1477. const enum C { C = "C" }
  1478. enum B { B = "B" }
  1479. </script>
  1480. <script setup lang="ts">
  1481. enum Foo { A = 123 }
  1482. </script>`
  1483. )
  1484. assertCode(content)
  1485. expect(bindings).toStrictEqual({
  1486. D: BindingTypes.LITERAL_CONST,
  1487. C: BindingTypes.LITERAL_CONST,
  1488. B: BindingTypes.LITERAL_CONST,
  1489. Foo: BindingTypes.LITERAL_CONST
  1490. })
  1491. })
  1492. test('const Enum', () => {
  1493. const { content, bindings } = compile(
  1494. `<script setup lang="ts">
  1495. const enum Foo { A = 123 }
  1496. </script>`,
  1497. { hoistStatic: true }
  1498. )
  1499. assertCode(content)
  1500. expect(bindings).toStrictEqual({
  1501. Foo: BindingTypes.LITERAL_CONST
  1502. })
  1503. })
  1504. test('runtime inference for Enum in defineProps', () => {
  1505. expect(
  1506. compile(
  1507. `<script setup lang="ts">
  1508. const enum Foo { A = 123 }
  1509. defineProps<{
  1510. foo: Foo
  1511. }>()
  1512. </script>`,
  1513. { hoistStatic: true }
  1514. ).content
  1515. ).toMatch(`foo: { type: Number`)
  1516. expect(
  1517. compile(
  1518. `<script setup lang="ts">
  1519. const enum Foo { A = '123' }
  1520. defineProps<{
  1521. foo: Foo
  1522. }>()
  1523. </script>`,
  1524. { hoistStatic: true }
  1525. ).content
  1526. ).toMatch(`foo: { type: String`)
  1527. expect(
  1528. compile(
  1529. `<script setup lang="ts">
  1530. const enum Foo { A = '123', B = 123 }
  1531. defineProps<{
  1532. foo: Foo
  1533. }>()
  1534. </script>`,
  1535. { hoistStatic: true }
  1536. ).content
  1537. ).toMatch(`foo: { type: [String, Number]`)
  1538. expect(
  1539. compile(
  1540. `<script setup lang="ts">
  1541. const enum Foo { A, B }
  1542. defineProps<{
  1543. foo: Foo
  1544. }>()
  1545. </script>`,
  1546. { hoistStatic: true }
  1547. ).content
  1548. ).toMatch(`foo: { type: Number`)
  1549. })
  1550. test('import type', () => {
  1551. const { content } = compile(
  1552. `<script setup lang="ts">
  1553. import type { Foo } from './main.ts'
  1554. import { type Bar, Baz } from './main.ts'
  1555. </script>`
  1556. )
  1557. expect(content).toMatch(`return { get Baz() { return Baz } }`)
  1558. assertCode(content)
  1559. })
  1560. })
  1561. describe('async/await detection', () => {
  1562. function assertAwaitDetection(code: string, shouldAsync = true) {
  1563. const { content } = compile(`<script setup>${code}</script>`, {
  1564. reactivityTransform: true
  1565. })
  1566. if (shouldAsync) {
  1567. expect(content).toMatch(`let __temp, __restore`)
  1568. }
  1569. expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup(`)
  1570. assertCode(content)
  1571. return content
  1572. }
  1573. test('expression statement', () => {
  1574. assertAwaitDetection(`await foo`)
  1575. })
  1576. test('variable', () => {
  1577. assertAwaitDetection(`const a = 1 + (await foo)`)
  1578. })
  1579. test('ref', () => {
  1580. assertAwaitDetection(`let a = $ref(1 + (await foo))`)
  1581. })
  1582. // #4448
  1583. test('nested await', () => {
  1584. assertAwaitDetection(`await (await foo)`)
  1585. assertAwaitDetection(`await ((await foo))`)
  1586. assertAwaitDetection(`await (await (await foo))`)
  1587. })
  1588. // should prepend semicolon
  1589. test('nested leading await in expression statement', () => {
  1590. const code = assertAwaitDetection(`foo()\nawait 1 + await 2`)
  1591. expect(code).toMatch(`foo()\n;(`)
  1592. })
  1593. // #4596 should NOT prepend semicolon
  1594. test('single line conditions', () => {
  1595. const code = assertAwaitDetection(`if (false) await foo()`)
  1596. expect(code).not.toMatch(`if (false) ;(`)
  1597. })
  1598. test('nested statements', () => {
  1599. assertAwaitDetection(`if (ok) { await foo } else { await bar }`)
  1600. })
  1601. test('multiple `if` nested statements', () => {
  1602. assertAwaitDetection(`if (ok) {
  1603. let a = 'foo'
  1604. await 0 + await 1
  1605. await 2
  1606. } else if (a) {
  1607. await 10
  1608. if (b) {
  1609. await 0 + await 1
  1610. } else {
  1611. let a = 'foo'
  1612. await 2
  1613. }
  1614. if (b) {
  1615. await 3
  1616. await 4
  1617. }
  1618. } else {
  1619. await 5
  1620. }`)
  1621. })
  1622. test('multiple `if while` nested statements', () => {
  1623. assertAwaitDetection(`if (ok) {
  1624. while (d) {
  1625. await 5
  1626. }
  1627. while (d) {
  1628. await 5
  1629. await 6
  1630. if (c) {
  1631. let f = 10
  1632. 10 + await 7
  1633. } else {
  1634. await 8
  1635. await 9
  1636. }
  1637. }
  1638. }`)
  1639. })
  1640. test('multiple `if for` nested statements', () => {
  1641. assertAwaitDetection(`if (ok) {
  1642. for (let a of [1,2,3]) {
  1643. await a
  1644. }
  1645. for (let a of [1,2,3]) {
  1646. await a
  1647. await a
  1648. }
  1649. }`)
  1650. })
  1651. test('should ignore await inside functions', () => {
  1652. // function declaration
  1653. assertAwaitDetection(`async function foo() { await bar }`, false)
  1654. // function expression
  1655. assertAwaitDetection(`const foo = async () => { await bar }`, false)
  1656. // object method
  1657. assertAwaitDetection(`const obj = { async method() { await bar }}`, false)
  1658. // class method
  1659. assertAwaitDetection(
  1660. `const cls = class Foo { async method() { await bar }}`,
  1661. false
  1662. )
  1663. })
  1664. })
  1665. describe('errors', () => {
  1666. test('<script> and <script setup> must have same lang', () => {
  1667. expect(() =>
  1668. compile(`<script>foo()</script><script setup lang="ts">bar()</script>`)
  1669. ).toThrow(`<script> and <script setup> must have the same language type`)
  1670. })
  1671. const moduleErrorMsg = `cannot contain ES module exports`
  1672. test('non-type named exports', () => {
  1673. expect(() =>
  1674. compile(`<script setup>
  1675. export const a = 1
  1676. </script>`)
  1677. ).toThrow(moduleErrorMsg)
  1678. expect(() =>
  1679. compile(`<script setup>
  1680. export * from './foo'
  1681. </script>`)
  1682. ).toThrow(moduleErrorMsg)
  1683. expect(() =>
  1684. compile(`<script setup>
  1685. const bar = 1
  1686. export { bar as default }
  1687. </script>`)
  1688. ).toThrow(moduleErrorMsg)
  1689. })
  1690. test('defineProps/Emit() w/ both type and non-type args', () => {
  1691. expect(() => {
  1692. compile(`<script setup lang="ts">
  1693. defineProps<{}>({})
  1694. </script>`)
  1695. }).toThrow(`cannot accept both type and non-type arguments`)
  1696. expect(() => {
  1697. compile(`<script setup lang="ts">
  1698. defineEmits<{}>({})
  1699. </script>`)
  1700. }).toThrow(`cannot accept both type and non-type arguments`)
  1701. })
  1702. test('defineProps/Emit() referencing local var', () => {
  1703. expect(() =>
  1704. compile(`<script setup>
  1705. let bar = 1
  1706. defineProps({
  1707. foo: {
  1708. default: () => bar
  1709. }
  1710. })
  1711. </script>`)
  1712. ).toThrow(`cannot reference locally declared variables`)
  1713. expect(() =>
  1714. compile(`<script setup>
  1715. let bar = 'hello'
  1716. defineEmits([bar])
  1717. </script>`)
  1718. ).toThrow(`cannot reference locally declared variables`)
  1719. // #4644
  1720. expect(() =>
  1721. compile(`
  1722. <script>const bar = 1</script>
  1723. <script setup>
  1724. defineProps({
  1725. foo: {
  1726. default: () => bar
  1727. }
  1728. })
  1729. </script>`)
  1730. ).not.toThrow(`cannot reference locally declared variables`)
  1731. })
  1732. test('should allow defineProps/Emit() referencing scope var', () => {
  1733. assertCode(
  1734. compile(`<script setup>
  1735. const bar = 1
  1736. defineProps({
  1737. foo: {
  1738. default: bar => bar + 1
  1739. }
  1740. })
  1741. defineEmits({
  1742. foo: bar => bar > 1
  1743. })
  1744. </script>`).content
  1745. )
  1746. })
  1747. test('should allow defineProps/Emit() referencing imported binding', () => {
  1748. assertCode(
  1749. compile(`<script setup>
  1750. import { bar } from './bar'
  1751. defineProps({
  1752. foo: {
  1753. default: () => bar
  1754. }
  1755. })
  1756. defineEmits({
  1757. foo: () => bar > 1
  1758. })
  1759. </script>`).content
  1760. )
  1761. })
  1762. test('mixed usage of tuple / call signature in defineEmits', () => {
  1763. expect(() =>
  1764. compile(`<script setup lang="ts">
  1765. defineEmits<{
  1766. foo: []
  1767. (e: 'hi'): void
  1768. }>()
  1769. </script>`)
  1770. ).toThrow(
  1771. `defineEmits() type cannot mixed call signature and property syntax.`
  1772. )
  1773. })
  1774. })
  1775. })
  1776. describe('SFC analyze <script> bindings', () => {
  1777. it('can parse decorators syntax in typescript block', () => {
  1778. const { scriptAst } = compile(`
  1779. <script lang="ts">
  1780. import { Options, Vue } from 'vue-class-component';
  1781. @Options({
  1782. components: {
  1783. HelloWorld,
  1784. },
  1785. props: ['foo', 'bar']
  1786. })
  1787. export default class Home extends Vue {}
  1788. </script>
  1789. `)
  1790. expect(scriptAst).toBeDefined()
  1791. })
  1792. it('recognizes props array declaration', () => {
  1793. const { bindings } = compile(`
  1794. <script>
  1795. export default {
  1796. props: ['foo', 'bar']
  1797. }
  1798. </script>
  1799. `)
  1800. expect(bindings).toStrictEqual({
  1801. foo: BindingTypes.PROPS,
  1802. bar: BindingTypes.PROPS
  1803. })
  1804. expect(bindings!.__isScriptSetup).toBe(false)
  1805. })
  1806. it('recognizes props object declaration', () => {
  1807. const { bindings } = compile(`
  1808. <script>
  1809. export default {
  1810. props: {
  1811. foo: String,
  1812. bar: {
  1813. type: String,
  1814. },
  1815. baz: null,
  1816. qux: [String, Number]
  1817. }
  1818. }
  1819. </script>
  1820. `)
  1821. expect(bindings).toStrictEqual({
  1822. foo: BindingTypes.PROPS,
  1823. bar: BindingTypes.PROPS,
  1824. baz: BindingTypes.PROPS,
  1825. qux: BindingTypes.PROPS
  1826. })
  1827. expect(bindings!.__isScriptSetup).toBe(false)
  1828. })
  1829. it('recognizes setup return', () => {
  1830. const { bindings } = compile(`
  1831. <script>
  1832. const bar = 2
  1833. export default {
  1834. setup() {
  1835. return {
  1836. foo: 1,
  1837. bar
  1838. }
  1839. }
  1840. }
  1841. </script>
  1842. `)
  1843. expect(bindings).toStrictEqual({
  1844. foo: BindingTypes.SETUP_MAYBE_REF,
  1845. bar: BindingTypes.SETUP_MAYBE_REF
  1846. })
  1847. expect(bindings!.__isScriptSetup).toBe(false)
  1848. })
  1849. it('recognizes exported vars', () => {
  1850. const { bindings } = compile(`
  1851. <script>
  1852. export const foo = 2
  1853. </script>
  1854. <script setup>
  1855. console.log(foo)
  1856. </script>
  1857. `)
  1858. expect(bindings).toStrictEqual({
  1859. foo: BindingTypes.LITERAL_CONST
  1860. })
  1861. })
  1862. it('recognizes async setup return', () => {
  1863. const { bindings } = compile(`
  1864. <script>
  1865. const bar = 2
  1866. export default {
  1867. async setup() {
  1868. return {
  1869. foo: 1,
  1870. bar
  1871. }
  1872. }
  1873. }
  1874. </script>
  1875. `)
  1876. expect(bindings).toStrictEqual({
  1877. foo: BindingTypes.SETUP_MAYBE_REF,
  1878. bar: BindingTypes.SETUP_MAYBE_REF
  1879. })
  1880. expect(bindings!.__isScriptSetup).toBe(false)
  1881. })
  1882. it('recognizes data return', () => {
  1883. const { bindings } = compile(`
  1884. <script>
  1885. const bar = 2
  1886. export default {
  1887. data() {
  1888. return {
  1889. foo: null,
  1890. bar
  1891. }
  1892. }
  1893. }
  1894. </script>
  1895. `)
  1896. expect(bindings).toStrictEqual({
  1897. foo: BindingTypes.DATA,
  1898. bar: BindingTypes.DATA
  1899. })
  1900. })
  1901. it('recognizes methods', () => {
  1902. const { bindings } = compile(`
  1903. <script>
  1904. export default {
  1905. methods: {
  1906. foo() {}
  1907. }
  1908. }
  1909. </script>
  1910. `)
  1911. expect(bindings).toStrictEqual({ foo: BindingTypes.OPTIONS })
  1912. })
  1913. it('recognizes computeds', () => {
  1914. const { bindings } = compile(`
  1915. <script>
  1916. export default {
  1917. computed: {
  1918. foo() {},
  1919. bar: {
  1920. get() {},
  1921. set() {},
  1922. }
  1923. }
  1924. }
  1925. </script>
  1926. `)
  1927. expect(bindings).toStrictEqual({
  1928. foo: BindingTypes.OPTIONS,
  1929. bar: BindingTypes.OPTIONS
  1930. })
  1931. })
  1932. it('recognizes injections array declaration', () => {
  1933. const { bindings } = compile(`
  1934. <script>
  1935. export default {
  1936. inject: ['foo', 'bar']
  1937. }
  1938. </script>
  1939. `)
  1940. expect(bindings).toStrictEqual({
  1941. foo: BindingTypes.OPTIONS,
  1942. bar: BindingTypes.OPTIONS
  1943. })
  1944. })
  1945. it('recognizes injections object declaration', () => {
  1946. const { bindings } = compile(`
  1947. <script>
  1948. export default {
  1949. inject: {
  1950. foo: {},
  1951. bar: {},
  1952. }
  1953. }
  1954. </script>
  1955. `)
  1956. expect(bindings).toStrictEqual({
  1957. foo: BindingTypes.OPTIONS,
  1958. bar: BindingTypes.OPTIONS
  1959. })
  1960. })
  1961. it('works for mixed bindings', () => {
  1962. const { bindings } = compile(`
  1963. <script>
  1964. export default {
  1965. inject: ['foo'],
  1966. props: {
  1967. bar: String,
  1968. },
  1969. setup() {
  1970. return {
  1971. baz: null,
  1972. }
  1973. },
  1974. data() {
  1975. return {
  1976. qux: null
  1977. }
  1978. },
  1979. methods: {
  1980. quux() {}
  1981. },
  1982. computed: {
  1983. quuz() {}
  1984. }
  1985. }
  1986. </script>
  1987. `)
  1988. expect(bindings).toStrictEqual({
  1989. foo: BindingTypes.OPTIONS,
  1990. bar: BindingTypes.PROPS,
  1991. baz: BindingTypes.SETUP_MAYBE_REF,
  1992. qux: BindingTypes.DATA,
  1993. quux: BindingTypes.OPTIONS,
  1994. quuz: BindingTypes.OPTIONS
  1995. })
  1996. })
  1997. it('works for script setup', () => {
  1998. const { bindings } = compile(`
  1999. <script setup>
  2000. import { ref as r } from 'vue'
  2001. defineProps({
  2002. foo: String
  2003. })
  2004. const a = r(1)
  2005. let b = 2
  2006. const c = 3
  2007. const { d } = someFoo()
  2008. let { e } = someBar()
  2009. </script>
  2010. `)
  2011. expect(bindings).toStrictEqual({
  2012. r: BindingTypes.SETUP_CONST,
  2013. a: BindingTypes.SETUP_REF,
  2014. b: BindingTypes.SETUP_LET,
  2015. c: BindingTypes.LITERAL_CONST,
  2016. d: BindingTypes.SETUP_MAYBE_REF,
  2017. e: BindingTypes.SETUP_LET,
  2018. foo: BindingTypes.PROPS
  2019. })
  2020. })
  2021. describe('auto name inference', () => {
  2022. test('basic', () => {
  2023. const { content } = compile(
  2024. `<script setup>const a = 1</script>
  2025. <template>{{ a }}</template>`,
  2026. undefined,
  2027. {
  2028. filename: 'FooBar.vue'
  2029. }
  2030. )
  2031. expect(content).toMatch(`export default {
  2032. __name: 'FooBar'`)
  2033. assertCode(content)
  2034. })
  2035. test('do not overwrite manual name (object)', () => {
  2036. const { content } = compile(
  2037. `<script>
  2038. export default {
  2039. name: 'Baz'
  2040. }
  2041. </script>
  2042. <script setup>const a = 1</script>
  2043. <template>{{ a }}</template>`,
  2044. undefined,
  2045. {
  2046. filename: 'FooBar.vue'
  2047. }
  2048. )
  2049. expect(content).not.toMatch(`name: 'FooBar'`)
  2050. expect(content).toMatch(`name: 'Baz'`)
  2051. assertCode(content)
  2052. })
  2053. test('do not overwrite manual name (call)', () => {
  2054. const { content } = compile(
  2055. `<script>
  2056. import { defineComponent } from 'vue'
  2057. export default defineComponent({
  2058. name: 'Baz'
  2059. })
  2060. </script>
  2061. <script setup>const a = 1</script>
  2062. <template>{{ a }}</template>`,
  2063. undefined,
  2064. {
  2065. filename: 'FooBar.vue'
  2066. }
  2067. )
  2068. expect(content).not.toMatch(`name: 'FooBar'`)
  2069. expect(content).toMatch(`name: 'Baz'`)
  2070. assertCode(content)
  2071. })
  2072. })
  2073. })
  2074. describe('SFC genDefaultAs', () => {
  2075. test('normal <script> only', () => {
  2076. const { content } = compile(
  2077. `<script>
  2078. export default {}
  2079. </script>`,
  2080. {
  2081. genDefaultAs: '_sfc_'
  2082. }
  2083. )
  2084. expect(content).not.toMatch('export default')
  2085. expect(content).toMatch(`const _sfc_ = {}`)
  2086. assertCode(content)
  2087. })
  2088. test('normal <script> w/ cssVars', () => {
  2089. const { content } = compile(
  2090. `<script>
  2091. export default {}
  2092. </script>
  2093. <style>
  2094. .foo { color: v-bind(x) }
  2095. </style>`,
  2096. {
  2097. genDefaultAs: '_sfc_'
  2098. }
  2099. )
  2100. expect(content).not.toMatch('export default')
  2101. expect(content).not.toMatch('__default__')
  2102. expect(content).toMatch(`const _sfc_ = {}`)
  2103. assertCode(content)
  2104. })
  2105. test('<script> + <script setup>', () => {
  2106. const { content } = compile(
  2107. `<script>
  2108. export default {}
  2109. </script>
  2110. <script setup>
  2111. const a = 1
  2112. </script>`,
  2113. {
  2114. genDefaultAs: '_sfc_'
  2115. }
  2116. )
  2117. expect(content).not.toMatch('export default')
  2118. expect(content).toMatch(
  2119. `const _sfc_ = /*#__PURE__*/Object.assign(__default__`
  2120. )
  2121. assertCode(content)
  2122. })
  2123. test('<script> + <script setup>', () => {
  2124. const { content } = compile(
  2125. `<script>
  2126. export default {}
  2127. </script>
  2128. <script setup>
  2129. const a = 1
  2130. </script>`,
  2131. {
  2132. genDefaultAs: '_sfc_'
  2133. }
  2134. )
  2135. expect(content).not.toMatch('export default')
  2136. expect(content).toMatch(
  2137. `const _sfc_ = /*#__PURE__*/Object.assign(__default__`
  2138. )
  2139. assertCode(content)
  2140. })
  2141. test('<script setup> only', () => {
  2142. const { content } = compile(
  2143. `<script setup>
  2144. const a = 1
  2145. </script>`,
  2146. {
  2147. genDefaultAs: '_sfc_'
  2148. }
  2149. )
  2150. expect(content).not.toMatch('export default')
  2151. expect(content).toMatch(`const _sfc_ = {\n setup`)
  2152. assertCode(content)
  2153. })
  2154. test('<script setup> only w/ ts', () => {
  2155. const { content } = compile(
  2156. `<script setup lang="ts">
  2157. const a = 1
  2158. </script>`,
  2159. {
  2160. genDefaultAs: '_sfc_'
  2161. }
  2162. )
  2163. expect(content).not.toMatch('export default')
  2164. expect(content).toMatch(`const _sfc_ = /*#__PURE__*/_defineComponent(`)
  2165. assertCode(content)
  2166. })
  2167. test('<script> + <script setup> w/ ts', () => {
  2168. const { content } = compile(
  2169. `<script lang="ts">
  2170. export default {}
  2171. </script>
  2172. <script setup lang="ts">
  2173. const a = 1
  2174. </script>`,
  2175. {
  2176. genDefaultAs: '_sfc_'
  2177. }
  2178. )
  2179. expect(content).not.toMatch('export default')
  2180. expect(content).toMatch(
  2181. `const _sfc_ = /*#__PURE__*/_defineComponent({\n ...__default__`
  2182. )
  2183. assertCode(content)
  2184. })
  2185. })