Skip to content

Commit 569071a

Browse files
committed
feat: implement a utility function that extracts vue component options
1 parent 423c73d commit 569071a

File tree

2 files changed

+690
-43
lines changed

2 files changed

+690
-43
lines changed
Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
import type { Transform } from 'jscodeshift'
2+
import { getVueOptions } from '../astUtils'
3+
4+
import { defineInlineTest } from 'jscodeshift/src/testUtils'
5+
6+
const printVueOptions: Transform = function (
7+
file,
8+
api,
9+
{ filename = 'test.js' }: { filename?: string } = {}
10+
) {
11+
const j = api.jscodeshift
12+
const root = j(file.source)
13+
const context = { root, j, filename }
14+
15+
const options = getVueOptions(context)
16+
17+
if (!options.length) {
18+
return ''
19+
}
20+
21+
const result = options.toSource({ lineTerminator: '\n' })
22+
23+
if (Array.isArray(result)) {
24+
return result.map((source, index) => `${index}: ${source}`).join('\n\n')
25+
}
26+
27+
return result
28+
}
29+
30+
// @ts-ignore
31+
printVueOptions.parser = 'babylon'
32+
33+
defineInlineTest(
34+
printVueOptions,
35+
{ filename: 'test.vue' },
36+
`export default {
37+
data () {
38+
return { foo: 1 }
39+
}
40+
}`,
41+
`{
42+
data () {
43+
return { foo: 1 }
44+
}
45+
}`,
46+
'should print default object export from a .vue file'
47+
)
48+
49+
defineInlineTest(
50+
printVueOptions,
51+
{ filename: 'test.vue' },
52+
`const obj = {
53+
data () {
54+
return { foo: 1 }
55+
}
56+
}
57+
export default obj`,
58+
`{
59+
data () {
60+
return { foo: 1 }
61+
}
62+
}`,
63+
'should print default variable export from a .vue file if it is actually an object'
64+
)
65+
66+
// TODO: a `var` that is never re-assigned
67+
68+
defineInlineTest(
69+
printVueOptions,
70+
{},
71+
`export default {
72+
data () {
73+
return { foo: 1 }
74+
},
75+
template: '<div>foo: {foo}</div>'
76+
}`,
77+
`{
78+
data () {
79+
return { foo: 1 }
80+
},
81+
82+
template: '<div>foo: {foo}</div>'
83+
}`,
84+
'should recognize the default export from a .js file, if it is an object and contains a `template` property'
85+
)
86+
87+
defineInlineTest(
88+
printVueOptions,
89+
{},
90+
`const obj = {
91+
data () {
92+
return { foo: 1 }
93+
},
94+
template: '<div>foo: {foo}</div>'
95+
}
96+
export default obj`,
97+
`{
98+
data () {
99+
return { foo: 1 }
100+
},
101+
template: '<div>foo: {foo}</div>'
102+
}`,
103+
'should recognize the default export from a .js file, if it is a const object and was initially assigned a `template` property'
104+
)
105+
106+
defineInlineTest(
107+
printVueOptions,
108+
{},
109+
`var obj = {
110+
data () {
111+
return { foo: 1 }
112+
},
113+
template: '<div>foo: {foo}</div>'
114+
}
115+
obj = {}
116+
export default obj`,
117+
'',
118+
'should ignore the default export from a .js file, if it is ever reassigned'
119+
)
120+
121+
defineInlineTest(
122+
printVueOptions,
123+
{},
124+
`export default {
125+
data () {
126+
return { foo: 1 }
127+
},
128+
render(h) {
129+
return h("div", ["foo: ", this.foo]);
130+
}
131+
}`,
132+
`{
133+
data () {
134+
return { foo: 1 }
135+
},
136+
137+
render(h) {
138+
return h("div", ["foo: ", this.foo]);
139+
}
140+
}`,
141+
'should recognize the default export from a .js file, if it is an object and contains a `render` property'
142+
)
143+
144+
defineInlineTest(
145+
printVueOptions,
146+
{},
147+
`export default { foo: 1 }`,
148+
'',
149+
'should ignore the default export from a .js file, if it is an object but has neither a `render` property or a `template` property'
150+
)
151+
152+
defineInlineTest(
153+
printVueOptions,
154+
{},
155+
`new Vue({
156+
template: '#my-component',
157+
data() { return { foo: 1 } }
158+
})`,
159+
`{
160+
template: '#my-component',
161+
data() { return { foo: 1 } }
162+
}`,
163+
'should recognize the argument of a `new Vue` call'
164+
)
165+
166+
defineInlineTest(
167+
printVueOptions,
168+
{},
169+
`Vue.component('my-component', {
170+
template: '#my-component',
171+
data() { return { foo: 1 } }
172+
})`,
173+
`{
174+
template: '#my-component',
175+
data() { return { foo: 1 } }
176+
}`,
177+
'should recognize the argument of a `Vue.component` call'
178+
)
179+
180+
defineInlineTest(
181+
printVueOptions,
182+
{ filename: 'test.vue' },
183+
`export default {
184+
components: {
185+
PropagateDisable: {
186+
mixins: [
187+
DisabledParent,
188+
],
189+
190+
render (h) {
191+
return h('div', { staticClass: 'vue-ui-disable' }, this.$scopedSlots.default())
192+
}
193+
}
194+
},
195+
196+
data () {
197+
return { foo: 1 }
198+
}
199+
}`,
200+
`0: {
201+
components: {
202+
PropagateDisable: {
203+
mixins: [
204+
DisabledParent,
205+
],
206+
207+
render (h) {
208+
return h('div', { staticClass: 'vue-ui-disable' }, this.$scopedSlots.default())
209+
}
210+
}
211+
},
212+
213+
data () {
214+
return { foo: 1 }
215+
}
216+
}
217+
218+
1: {
219+
mixins: [
220+
DisabledParent,
221+
],
222+
223+
render (h) {
224+
return h('div', { staticClass: 'vue-ui-disable' }, this.$scopedSlots.default())
225+
}
226+
}`,
227+
'should recognize inline components via the `components` property'
228+
)
229+
230+
defineInlineTest(
231+
printVueOptions,
232+
{},
233+
`Vue.component(
234+
'async-webpack-example',
235+
() => import('./my-async-component')
236+
)
237+
`,
238+
`() => import('./my-async-component')`,
239+
'should recognize simple async components'
240+
)
241+
242+
defineInlineTest(
243+
printVueOptions,
244+
{},
245+
`Vue.component(
246+
'async-webpack-example',
247+
() => (111, import('./my-async-component'))
248+
)
249+
`,
250+
`() => (111, import('./my-async-component'))`,
251+
'should recognize async components with a comma expression'
252+
)
253+
254+
defineInlineTest(
255+
printVueOptions,
256+
{},
257+
`Vue.component(
258+
'async-webpack-example',
259+
() => { return import('./my-async-component') }
260+
)
261+
`,
262+
`() => { return import('./my-async-component') }`,
263+
'should recognize the async component as a arrow function with a function body'
264+
)
265+
266+
defineInlineTest(
267+
printVueOptions,
268+
{},
269+
`Vue.component(
270+
'async-webpack-example',
271+
function () { return import('./my-async-component') }
272+
)
273+
`,
274+
`function () { return import('./my-async-component') }`,
275+
'should recognize the async component as a function with a function body'
276+
)
277+
278+
defineInlineTest(
279+
printVueOptions,
280+
{},
281+
`Vue.component(
282+
'async-webpack-example',
283+
async function () { const componentName = await apiCall(); return await customLoad(componentName); }
284+
)
285+
`,
286+
`async function () { const componentName = await apiCall(); return await customLoad(componentName); }`,
287+
'should recognize the async component as an async function'
288+
)
289+
290+
defineInlineTest(
291+
printVueOptions,
292+
{},
293+
`Vue.component(
294+
'async-webpack-example',
295+
async () => await customLoad('./example.vue')
296+
)
297+
`,
298+
`async () => await customLoad('./example.vue')`,
299+
'should recognize the async component as an async arrow function'
300+
)
301+
302+
defineInlineTest(
303+
printVueOptions,
304+
{},
305+
`Vue.component('async-example', function () {
306+
return new Promise(resolve => resolve({
307+
template: '<div>I am async!</div>'
308+
}))
309+
})
310+
`,
311+
`function () {
312+
return new Promise(resolve => resolve({
313+
template: '<div>I am async!</div>'
314+
}))
315+
}`,
316+
'should recognize the async component as a function returning a new Promise'
317+
)
318+
319+
defineInlineTest(
320+
printVueOptions,
321+
{},
322+
`Vue.component(
323+
'async-webpack-example',
324+
function() {
325+
if (a) {
326+
return import('./A.vue')
327+
}
328+
return import('./my-async-component')
329+
}
330+
)`,
331+
`function() {
332+
if (a) {
333+
return import('./A.vue')
334+
}
335+
return import('./my-async-component')
336+
}`,
337+
'should recognize async component function with more than one return statements'
338+
)
339+
340+
defineInlineTest(
341+
printVueOptions,
342+
{ filename: 'test.vue' },
343+
`export default {
344+
components: {
345+
AsyncExample: () => import('./async-example.vue')
346+
},
347+
348+
data () {
349+
return { foo: 1 }
350+
}
351+
}`,
352+
`0: {
353+
components: {
354+
AsyncExample: () => import('./async-example.vue')
355+
},
356+
357+
data () {
358+
return { foo: 1 }
359+
}
360+
}
361+
362+
1: () => import('./async-example.vue')`,
363+
'should recognize inline async components via the `components` property'
364+
)
365+
366+
// Vue.component(
367+
// 'async-webpack-example',
368+
// () => { return (111, import('./my-async-component')) }
369+
// )
370+
371+
// Vue.component('async-example', function (resolve, reject) {
372+
// setTimeout(function () {
373+
// resolve({
374+
// template: '<div>I am async!</div>'
375+
// })
376+
// }, 1000)
377+
// })

0 commit comments

Comments
 (0)