compileScript.spec.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916
  1. import { parse, SFCScriptCompileOptions, compileScript } from '../src'
  2. import { parse as babelParse } from '@babel/parser'
  3. import { babelParserDefaultPlugins } from '@vue/shared'
  4. function compile(src: string, options?: SFCScriptCompileOptions) {
  5. const { descriptor } = parse(src)
  6. return compileScript(descriptor, options)
  7. }
  8. function assertCode(code: string) {
  9. // parse the generated code to make sure it is valid
  10. try {
  11. babelParse(code, {
  12. sourceType: 'module',
  13. plugins: [...babelParserDefaultPlugins, 'typescript']
  14. })
  15. } catch (e) {
  16. console.log(code)
  17. throw e
  18. }
  19. expect(code).toMatchSnapshot()
  20. }
  21. describe('SFC compile <script setup>', () => {
  22. test('should expose top level declarations', () => {
  23. const { content } = compile(`
  24. <script setup>
  25. import { x } from './x'
  26. let a = 1
  27. const b = 2
  28. function c() {}
  29. class d {}
  30. </script>
  31. `)
  32. assertCode(content)
  33. expect(content).toMatch('return { a, b, c, d, x }')
  34. })
  35. test('useOptions()', () => {
  36. const { content, bindings } = compile(`
  37. <script setup>
  38. import { useOptions } from 'vue'
  39. const { props, emit } = useOptions({
  40. props: {
  41. foo: String
  42. },
  43. emit: ['a', 'b']
  44. })
  45. const bar = 1
  46. </script>
  47. `)
  48. // should generate working code
  49. assertCode(content)
  50. // should anayze bindings
  51. expect(bindings).toStrictEqual({
  52. foo: 'props',
  53. bar: 'const',
  54. props: 'const',
  55. emit: 'const'
  56. })
  57. // should remove useOptions import and call
  58. expect(content).not.toMatch('useOptions')
  59. // should generate correct setup signature
  60. expect(content).toMatch(`setup(__props, { props, emit }) {`)
  61. // should include context options in default export
  62. expect(content).toMatch(`export default {
  63. props: {
  64. foo: String
  65. },
  66. emit: ['a', 'b'],`)
  67. })
  68. describe('imports', () => {
  69. test('should hoist and expose imports', () => {
  70. assertCode(
  71. compile(`<script setup>import { ref } from 'vue'</script>`).content
  72. )
  73. })
  74. test('should extract comment for import or type declarations', () => {
  75. assertCode(
  76. compile(`
  77. <script setup>
  78. import a from 'a' // comment
  79. import b from 'b'
  80. </script>
  81. `).content
  82. )
  83. })
  84. test('dedupe between user & helper', () => {
  85. const { content } = compile(`
  86. <script setup>
  87. import { ref } from 'vue'
  88. ref: foo = 1
  89. </script>
  90. `)
  91. assertCode(content)
  92. expect(content).toMatch(`import { ref } from 'vue'`)
  93. })
  94. test('import dedupe between <script> and <script setup>', () => {
  95. const { content } = compile(`
  96. <script>
  97. import { x } from './x'
  98. </script>
  99. <script setup>
  100. import { x } from './x'
  101. x()
  102. </script>
  103. `)
  104. assertCode(content)
  105. expect(content.indexOf(`import { x }`)).toEqual(
  106. content.lastIndexOf(`import { x }`)
  107. )
  108. })
  109. })
  110. describe('inlineTemplate mode', () => {
  111. test('should work', () => {
  112. const { content } = compile(
  113. `
  114. <script setup>
  115. import { ref } from 'vue'
  116. const count = ref(0)
  117. </script>
  118. <template>
  119. <div>{{ count }}</div>
  120. <div>static</div>
  121. </template>
  122. `,
  123. { inlineTemplate: true }
  124. )
  125. // check snapshot and make sure helper imports and
  126. // hoists are placed correctly.
  127. assertCode(content)
  128. })
  129. test('avoid unref() when necessary', () => {
  130. // function, const, component import
  131. const { content } = compile(
  132. `
  133. <script setup>
  134. import { ref, useOptions } from 'vue'
  135. import Foo from './Foo.vue'
  136. import other from './util'
  137. const count = ref(0)
  138. const constant = {}
  139. function fn() {}
  140. </script>
  141. <template>
  142. <Foo/>
  143. <div @click="fn">{{ count }} {{ constant }} {{ other }}</div>
  144. </template>
  145. `,
  146. { inlineTemplate: true }
  147. )
  148. assertCode(content)
  149. // no need to unref vue component import
  150. expect(content).toMatch(`createVNode(Foo)`)
  151. // should unref other imports
  152. expect(content).toMatch(`unref(other)`)
  153. // no need to unref constant literals
  154. expect(content).not.toMatch(`unref(constant)`)
  155. // should unref const w/ call init (e.g. ref())
  156. expect(content).toMatch(`unref(count)`)
  157. // no need to unref function declarations
  158. expect(content).toMatch(`{ onClick: fn }`)
  159. // no need to mark constant fns in patch flag
  160. expect(content).not.toMatch(`PROPS`)
  161. })
  162. })
  163. describe('with TypeScript', () => {
  164. test('hoist type declarations', () => {
  165. const { content } = compile(`
  166. <script setup lang="ts">
  167. export interface Foo {}
  168. type Bar = {}
  169. </script>`)
  170. assertCode(content)
  171. })
  172. test('useOptions w/ runtime options', () => {
  173. const { content } = compile(`
  174. <script setup lang="ts">
  175. import { useOptions } from 'vue'
  176. const { props, emit } = useOptions({
  177. props: { foo: String },
  178. emits: ['a', 'b']
  179. })
  180. </script>
  181. `)
  182. assertCode(content)
  183. expect(content).toMatch(`export default defineComponent({
  184. props: { foo: String },
  185. emits: ['a', 'b'],
  186. setup(__props, { props, emit }) {`)
  187. })
  188. test('useOptions w/ type / extract props', () => {
  189. const { content, bindings } = compile(`
  190. <script setup lang="ts">
  191. import { useOptions } from 'vue'
  192. interface Test {}
  193. type Alias = number[]
  194. useOptions<{
  195. props: {
  196. string: string
  197. number: number
  198. boolean: boolean
  199. object: object
  200. objectLiteral: { a: number }
  201. fn: (n: number) => void
  202. functionRef: Function
  203. objectRef: Object
  204. array: string[]
  205. arrayRef: Array<any>
  206. tuple: [number, number]
  207. set: Set<string>
  208. literal: 'foo'
  209. optional?: any
  210. recordRef: Record<string, null>
  211. interface: Test
  212. alias: Alias
  213. union: string | number
  214. literalUnion: 'foo' | 'bar'
  215. literalUnionMixed: 'foo' | 1 | boolean
  216. intersection: Test & {}
  217. }
  218. }>()
  219. </script>`)
  220. assertCode(content)
  221. expect(content).toMatch(`string: { type: String, required: true }`)
  222. expect(content).toMatch(`number: { type: Number, required: true }`)
  223. expect(content).toMatch(`boolean: { type: Boolean, required: true }`)
  224. expect(content).toMatch(`object: { type: Object, required: true }`)
  225. expect(content).toMatch(`objectLiteral: { type: Object, required: true }`)
  226. expect(content).toMatch(`fn: { type: Function, required: true }`)
  227. expect(content).toMatch(`functionRef: { type: Function, required: true }`)
  228. expect(content).toMatch(`objectRef: { type: Object, required: true }`)
  229. expect(content).toMatch(`array: { type: Array, required: true }`)
  230. expect(content).toMatch(`arrayRef: { type: Array, required: true }`)
  231. expect(content).toMatch(`tuple: { type: Array, required: true }`)
  232. expect(content).toMatch(`set: { type: Set, required: true }`)
  233. expect(content).toMatch(`literal: { type: String, required: true }`)
  234. expect(content).toMatch(`optional: { type: null, required: false }`)
  235. expect(content).toMatch(`recordRef: { type: Object, required: true }`)
  236. expect(content).toMatch(`interface: { type: Object, required: true }`)
  237. expect(content).toMatch(`alias: { type: Array, required: true }`)
  238. expect(content).toMatch(
  239. `union: { type: [String, Number], required: true }`
  240. )
  241. expect(content).toMatch(
  242. `literalUnion: { type: [String, String], required: true }`
  243. )
  244. expect(content).toMatch(
  245. `literalUnionMixed: { type: [String, Number, Boolean], required: true }`
  246. )
  247. expect(content).toMatch(`intersection: { type: Object, required: true }`)
  248. expect(bindings).toStrictEqual({
  249. string: 'props',
  250. number: 'props',
  251. boolean: 'props',
  252. object: 'props',
  253. objectLiteral: 'props',
  254. fn: 'props',
  255. functionRef: 'props',
  256. objectRef: 'props',
  257. array: 'props',
  258. arrayRef: 'props',
  259. tuple: 'props',
  260. set: 'props',
  261. literal: 'props',
  262. optional: 'props',
  263. recordRef: 'props',
  264. interface: 'props',
  265. alias: 'props',
  266. union: 'props',
  267. literalUnion: 'props',
  268. literalUnionMixed: 'props',
  269. intersection: 'props'
  270. })
  271. })
  272. test('useOptions w/ type / extract emits', () => {
  273. const { content } = compile(`
  274. <script setup lang="ts">
  275. import { useOptions } from 'vue'
  276. const { emit } = useOptions<{
  277. emit: (e: 'foo' | 'bar') => void
  278. }>()
  279. </script>
  280. `)
  281. assertCode(content)
  282. expect(content).toMatch(`props: {},\n emit: (e: 'foo' | 'bar') => void,`)
  283. expect(content).toMatch(`emits: ["foo", "bar"] as unknown as undefined`)
  284. })
  285. test('useOptions w/ type / extract emits (union)', () => {
  286. const { content } = compile(`
  287. <script setup lang="ts">
  288. import { useOptions } from 'vue'
  289. const { emit } = useOptions<{
  290. emit: ((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void)
  291. }>()
  292. </script>
  293. `)
  294. assertCode(content)
  295. expect(content).toMatch(
  296. `props: {},\n emit: ((e: 'foo' | 'bar') => void) | ((e: 'baz', id: number) => void),`
  297. )
  298. expect(content).toMatch(
  299. `emits: ["foo", "bar", "baz"] as unknown as undefined`
  300. )
  301. })
  302. })
  303. describe('CSS vars injection', () => {
  304. test('<script> w/ no default export', () => {
  305. assertCode(
  306. compile(
  307. `<script>const a = 1</script>\n` +
  308. `<style vars="{ color }">div{ color: var(--color); }</style>`
  309. ).content
  310. )
  311. })
  312. test('<script> w/ default export', () => {
  313. assertCode(
  314. compile(
  315. `<script>export default { setup() {} }</script>\n` +
  316. `<style vars="{ color }">div{ color: var(--color); }</style>`
  317. ).content
  318. )
  319. })
  320. test('<script> w/ default export in strings/comments', () => {
  321. assertCode(
  322. compile(
  323. `<script>
  324. // export default {}
  325. export default {}
  326. </script>\n` +
  327. `<style vars="{ color }">div{ color: var(--color); }</style>`
  328. ).content
  329. )
  330. })
  331. test('w/ <script setup>', () => {
  332. assertCode(
  333. compile(
  334. `<script setup>const color = 'red'</script>\n` +
  335. `<style vars="{ color }">div{ color: var(--color); }</style>`
  336. ).content
  337. )
  338. })
  339. })
  340. describe('async/await detection', () => {
  341. function assertAwaitDetection(code: string, shouldAsync = true) {
  342. const { content } = compile(`<script setup>${code}</script>`)
  343. expect(content).toMatch(`${shouldAsync ? `async ` : ``}setup()`)
  344. }
  345. test('expression statement', () => {
  346. assertAwaitDetection(`await foo`)
  347. })
  348. test('variable', () => {
  349. assertAwaitDetection(`const a = 1 + (await foo)`)
  350. })
  351. test('ref', () => {
  352. assertAwaitDetection(`ref: a = 1 + (await foo)`)
  353. })
  354. test('nested statements', () => {
  355. assertAwaitDetection(`if (ok) { await foo } else { await bar }`)
  356. })
  357. test('should ignore await inside functions', () => {
  358. // function declaration
  359. assertAwaitDetection(`async function foo() { await bar }`, false)
  360. // function expression
  361. assertAwaitDetection(`const foo = async () => { await bar }`, false)
  362. // object method
  363. assertAwaitDetection(`const obj = { async method() { await bar }}`, false)
  364. // class method
  365. assertAwaitDetection(
  366. `const cls = class Foo { async method() { await bar }}`,
  367. false
  368. )
  369. })
  370. })
  371. describe('ref: syntax sugar', () => {
  372. test('convert ref declarations', () => {
  373. const { content, bindings } = compile(`<script setup>
  374. ref: foo
  375. ref: a = 1
  376. ref: b = {
  377. count: 0
  378. }
  379. let c = () => {}
  380. let d
  381. </script>`)
  382. expect(content).toMatch(`import { ref } from 'vue'`)
  383. expect(content).not.toMatch(`ref: foo`)
  384. expect(content).not.toMatch(`ref: a`)
  385. expect(content).not.toMatch(`ref: b`)
  386. expect(content).toMatch(`const foo = ref()`)
  387. expect(content).toMatch(`const a = ref(1)`)
  388. expect(content).toMatch(`
  389. const b = ref({
  390. count: 0
  391. })
  392. `)
  393. // normal declarations left untouched
  394. expect(content).toMatch(`let c = () => {}`)
  395. expect(content).toMatch(`let d`)
  396. assertCode(content)
  397. expect(bindings).toStrictEqual({
  398. foo: 'setup',
  399. a: 'setup',
  400. b: 'setup',
  401. c: 'setup',
  402. d: 'setup'
  403. })
  404. })
  405. test('multi ref declarations', () => {
  406. const { content, bindings } = compile(`<script setup>
  407. ref: a = 1, b = 2, c = {
  408. count: 0
  409. }
  410. </script>`)
  411. expect(content).toMatch(`
  412. const a = ref(1), b = ref(2), c = ref({
  413. count: 0
  414. })
  415. `)
  416. expect(content).toMatch(`return { a, b, c }`)
  417. assertCode(content)
  418. expect(bindings).toStrictEqual({
  419. a: 'setup',
  420. b: 'setup',
  421. c: 'setup'
  422. })
  423. })
  424. test('should not convert non ref labels', () => {
  425. const { content } = compile(`<script setup>
  426. foo: a = 1, b = 2, c = {
  427. count: 0
  428. }
  429. </script>`)
  430. expect(content).toMatch(`foo: a = 1, b = 2`)
  431. assertCode(content)
  432. })
  433. test('accessing ref binding', () => {
  434. const { content } = compile(`<script setup>
  435. ref: a = 1
  436. console.log(a)
  437. function get() {
  438. return a + 1
  439. }
  440. </script>`)
  441. expect(content).toMatch(`console.log(a.value)`)
  442. expect(content).toMatch(`return a.value + 1`)
  443. assertCode(content)
  444. })
  445. test('cases that should not append .value', () => {
  446. const { content } = compile(`<script setup>
  447. ref: a = 1
  448. console.log(b.a)
  449. function get(a) {
  450. return a + 1
  451. }
  452. </script>`)
  453. expect(content).not.toMatch(`a.value`)
  454. })
  455. test('mutating ref binding', () => {
  456. const { content } = compile(`<script setup>
  457. ref: a = 1
  458. ref: b = { count: 0 }
  459. function inc() {
  460. a++
  461. a = a + 1
  462. b.count++
  463. b.count = b.count + 1
  464. }
  465. </script>`)
  466. expect(content).toMatch(`a.value++`)
  467. expect(content).toMatch(`a.value = a.value + 1`)
  468. expect(content).toMatch(`b.value.count++`)
  469. expect(content).toMatch(`b.value.count = b.value.count + 1`)
  470. assertCode(content)
  471. })
  472. test('using ref binding in property shorthand', () => {
  473. const { content } = compile(`<script setup>
  474. ref: a = 1
  475. const b = { a }
  476. function test() {
  477. const { a } = b
  478. }
  479. </script>`)
  480. expect(content).toMatch(`const b = { a: a.value }`)
  481. // should not convert destructure
  482. expect(content).toMatch(`const { a } = b`)
  483. assertCode(content)
  484. })
  485. test('object destructure', () => {
  486. const { content, bindings } = compile(`<script setup>
  487. ref: n = 1, ({ a, b: c, d = 1, e: f = 2, ...g } = useFoo())
  488. console.log(n, a, c, d, f, g)
  489. </script>`)
  490. expect(content).toMatch(
  491. `const n = ref(1), { a: __a, b: __c, d: __d = 1, e: __f = 2, ...__g } = useFoo()`
  492. )
  493. expect(content).toMatch(`\nconst a = ref(__a);`)
  494. expect(content).not.toMatch(`\nconst b = ref(__b);`)
  495. expect(content).toMatch(`\nconst c = ref(__c);`)
  496. expect(content).toMatch(`\nconst d = ref(__d);`)
  497. expect(content).not.toMatch(`\nconst e = ref(__e);`)
  498. expect(content).toMatch(`\nconst f = ref(__f);`)
  499. expect(content).toMatch(`\nconst g = ref(__g);`)
  500. expect(content).toMatch(
  501. `console.log(n.value, a.value, c.value, d.value, f.value, g.value)`
  502. )
  503. expect(content).toMatch(`return { n, a, c, d, f, g }`)
  504. expect(bindings).toStrictEqual({
  505. n: 'setup',
  506. a: 'setup',
  507. c: 'setup',
  508. d: 'setup',
  509. f: 'setup',
  510. g: 'setup'
  511. })
  512. assertCode(content)
  513. })
  514. test('array destructure', () => {
  515. const { content, bindings } = compile(`<script setup>
  516. ref: n = 1, [a, b = 1, ...c] = useFoo()
  517. console.log(n, a, b, c)
  518. </script>`)
  519. expect(content).toMatch(
  520. `const n = ref(1), [__a, __b = 1, ...__c] = useFoo()`
  521. )
  522. expect(content).toMatch(`\nconst a = ref(__a);`)
  523. expect(content).toMatch(`\nconst b = ref(__b);`)
  524. expect(content).toMatch(`\nconst c = ref(__c);`)
  525. expect(content).toMatch(`console.log(n.value, a.value, b.value, c.value)`)
  526. expect(content).toMatch(`return { n, a, b, c }`)
  527. expect(bindings).toStrictEqual({
  528. n: 'setup',
  529. a: 'setup',
  530. b: 'setup',
  531. c: 'setup'
  532. })
  533. assertCode(content)
  534. })
  535. test('nested destructure', () => {
  536. const { content, bindings } = compile(`<script setup>
  537. ref: [{ a: { b }}] = useFoo()
  538. ref: ({ c: [d, e] } = useBar())
  539. console.log(b, d, e)
  540. </script>`)
  541. expect(content).toMatch(`const [{ a: { b: __b }}] = useFoo()`)
  542. expect(content).toMatch(`const { c: [__d, __e] } = useBar()`)
  543. expect(content).not.toMatch(`\nconst a = ref(__a);`)
  544. expect(content).not.toMatch(`\nconst c = ref(__c);`)
  545. expect(content).toMatch(`\nconst b = ref(__b);`)
  546. expect(content).toMatch(`\nconst d = ref(__d);`)
  547. expect(content).toMatch(`\nconst e = ref(__e);`)
  548. expect(content).toMatch(`return { b, d, e }`)
  549. expect(bindings).toStrictEqual({
  550. b: 'setup',
  551. d: 'setup',
  552. e: 'setup'
  553. })
  554. assertCode(content)
  555. })
  556. })
  557. describe('errors', () => {
  558. test('<script> and <script setup> must have same lang', () => {
  559. expect(() =>
  560. compile(`<script>foo()</script><script setup lang="ts">bar()</script>`)
  561. ).toThrow(`<script> and <script setup> must have the same language type`)
  562. })
  563. const moduleErrorMsg = `cannot contain ES module exports`
  564. test('non-type named exports', () => {
  565. expect(() =>
  566. compile(`<script setup>
  567. export const a = 1
  568. </script>`)
  569. ).toThrow(moduleErrorMsg)
  570. expect(() =>
  571. compile(`<script setup>
  572. export * from './foo'
  573. </script>`)
  574. ).toThrow(moduleErrorMsg)
  575. expect(() =>
  576. compile(`<script setup>
  577. const bar = 1
  578. export { bar as default }
  579. </script>`)
  580. ).toThrow(moduleErrorMsg)
  581. })
  582. test('ref: non-assignment expressions', () => {
  583. expect(() =>
  584. compile(`<script setup>
  585. ref: a = 1, foo()
  586. </script>`)
  587. ).toThrow(`ref: statements can only contain assignment expressions`)
  588. })
  589. test('useOptions() w/ both type and non-type args', () => {
  590. expect(() => {
  591. compile(`<script setup lang="ts">
  592. import { useOptions } from 'vue'
  593. useOptions<{}>({})
  594. </script>`)
  595. }).toThrow(`cannot accept both type and non-type arguments`)
  596. })
  597. test('useOptions() referencing local var', () => {
  598. expect(() =>
  599. compile(`<script setup>
  600. import { useOptions } from 'vue'
  601. const bar = 1
  602. useOptions({
  603. props: {
  604. foo: {
  605. default: () => bar
  606. }
  607. }
  608. })
  609. </script>`)
  610. ).toThrow(`cannot reference locally declared variables`)
  611. })
  612. test('useOptions() referencing ref declarations', () => {
  613. expect(() =>
  614. compile(`<script setup>
  615. import { useOptions } from 'vue'
  616. ref: bar = 1
  617. useOptions({
  618. props: { bar }
  619. })
  620. </script>`)
  621. ).toThrow(`cannot reference locally declared variables`)
  622. })
  623. test('should allow useOptions() referencing scope var', () => {
  624. assertCode(
  625. compile(`<script setup>
  626. import { useOptions } from 'vue'
  627. const bar = 1
  628. useOptions({
  629. props: {
  630. foo: {
  631. default: bar => bar + 1
  632. }
  633. }
  634. })
  635. </script>`).content
  636. )
  637. })
  638. test('should allow useOptions() referencing imported binding', () => {
  639. assertCode(
  640. compile(`<script setup>
  641. import { useOptions } from 'vue'
  642. import { bar } from './bar'
  643. useOptions({
  644. props: {
  645. foo: {
  646. default: () => bar
  647. }
  648. }
  649. })
  650. </script>`).content
  651. )
  652. })
  653. })
  654. })
  655. describe('SFC analyze <script> bindings', () => {
  656. it('can parse decorators syntax in typescript block', () => {
  657. const { scriptAst } = compile(`
  658. <script lang="ts">
  659. import { Options, Vue } from 'vue-class-component';
  660. @Options({
  661. components: {
  662. HelloWorld,
  663. },
  664. props: ['foo', 'bar']
  665. })
  666. export default class Home extends Vue {}
  667. </script>
  668. `)
  669. expect(scriptAst).toBeDefined()
  670. })
  671. it('recognizes props array declaration', () => {
  672. const { bindings } = compile(`
  673. <script>
  674. export default {
  675. props: ['foo', 'bar']
  676. }
  677. </script>
  678. `)
  679. expect(bindings).toStrictEqual({ foo: 'props', bar: 'props' })
  680. })
  681. it('recognizes props object declaration', () => {
  682. const { bindings } = compile(`
  683. <script>
  684. export default {
  685. props: {
  686. foo: String,
  687. bar: {
  688. type: String,
  689. },
  690. baz: null,
  691. qux: [String, Number]
  692. }
  693. }
  694. </script>
  695. `)
  696. expect(bindings).toStrictEqual({
  697. foo: 'props',
  698. bar: 'props',
  699. baz: 'props',
  700. qux: 'props'
  701. })
  702. })
  703. it('recognizes setup return', () => {
  704. const { bindings } = compile(`
  705. <script>
  706. const bar = 2
  707. export default {
  708. setup() {
  709. return {
  710. foo: 1,
  711. bar
  712. }
  713. }
  714. }
  715. </script>
  716. `)
  717. expect(bindings).toStrictEqual({ foo: 'setup', bar: 'setup' })
  718. })
  719. it('recognizes async setup return', () => {
  720. const { bindings } = compile(`
  721. <script>
  722. const bar = 2
  723. export default {
  724. async setup() {
  725. return {
  726. foo: 1,
  727. bar
  728. }
  729. }
  730. }
  731. </script>
  732. `)
  733. expect(bindings).toStrictEqual({ foo: 'setup', bar: 'setup' })
  734. })
  735. it('recognizes data return', () => {
  736. const { bindings } = compile(`
  737. <script>
  738. const bar = 2
  739. export default {
  740. data() {
  741. return {
  742. foo: null,
  743. bar
  744. }
  745. }
  746. }
  747. </script>
  748. `)
  749. expect(bindings).toStrictEqual({ foo: 'data', bar: 'data' })
  750. })
  751. it('recognizes methods', () => {
  752. const { bindings } = compile(`
  753. <script>
  754. export default {
  755. methods: {
  756. foo() {}
  757. }
  758. }
  759. </script>
  760. `)
  761. expect(bindings).toStrictEqual({ foo: 'options' })
  762. })
  763. it('recognizes computeds', () => {
  764. const { bindings } = compile(`
  765. <script>
  766. export default {
  767. computed: {
  768. foo() {},
  769. bar: {
  770. get() {},
  771. set() {},
  772. }
  773. }
  774. }
  775. </script>
  776. `)
  777. expect(bindings).toStrictEqual({ foo: 'options', bar: 'options' })
  778. })
  779. it('recognizes injections array declaration', () => {
  780. const { bindings } = compile(`
  781. <script>
  782. export default {
  783. inject: ['foo', 'bar']
  784. }
  785. </script>
  786. `)
  787. expect(bindings).toStrictEqual({ foo: 'options', bar: 'options' })
  788. })
  789. it('recognizes injections object declaration', () => {
  790. const { bindings } = compile(`
  791. <script>
  792. export default {
  793. inject: {
  794. foo: {},
  795. bar: {},
  796. }
  797. }
  798. </script>
  799. `)
  800. expect(bindings).toStrictEqual({ foo: 'options', bar: 'options' })
  801. })
  802. it('works for mixed bindings', () => {
  803. const { bindings } = compile(`
  804. <script>
  805. export default {
  806. inject: ['foo'],
  807. props: {
  808. bar: String,
  809. },
  810. setup() {
  811. return {
  812. baz: null,
  813. }
  814. },
  815. data() {
  816. return {
  817. qux: null
  818. }
  819. },
  820. methods: {
  821. quux() {}
  822. },
  823. computed: {
  824. quuz() {}
  825. }
  826. }
  827. </script>
  828. `)
  829. expect(bindings).toStrictEqual({
  830. foo: 'options',
  831. bar: 'props',
  832. baz: 'setup',
  833. qux: 'data',
  834. quux: 'options',
  835. quuz: 'options'
  836. })
  837. })
  838. it('works for script setup', () => {
  839. const { bindings } = compile(`
  840. <script setup>
  841. import { useOptions } from 'vue'
  842. useOptions({
  843. props: {
  844. foo: String,
  845. }
  846. })
  847. </script>
  848. `)
  849. expect(bindings).toStrictEqual({
  850. foo: 'props'
  851. })
  852. })
  853. })