108 | {{ toMarried(data.married) }}
109 |
110 | ```
111 |
112 | ```javascript
113 | function toMarried(value) {
114 | return value ? 'Yes' : 'No'
115 | }
116 | ```
117 |
118 | ## Vue2.7中延用Router3.x、Vuex3.x
119 |
120 | 如若不想在 `Vue2.7` 项目中更新 `Router4, Vuex4` ,可以从 `Vue` 实例中获取 `Router, Route, Store`
121 |
122 | ```javascript
123 | import { getCurrentInstance } from 'vue'
124 |
125 | const $vm = getCurrentInstance()
126 | const router = $vm.proxy.$router
127 | const route = $vm.proxy.$route
128 | const store = $vm.proxy.$store
129 | ```
130 |
131 | ---
132 |
133 | ## 无法解析的内容
134 |
135 | 动态变量或者拼接的内容无法被解析或解析错误
136 |
137 | ```javascript
138 | export default {
139 | methods: {
140 | onSubmit(propName) {
141 | this[propName] = '123'
142 | this.$emit(propName + '-change')
143 | }
144 | }
145 | }
146 | ```
147 |
148 | ---
149 |
150 | Package: vue2-to-composition-api
151 |
152 | E-mail: diquick@qq.com
153 |
154 | Author: wd3322
155 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * package: vue2-to-composition-api
3 | * e-mail: diquick@qq.com
4 | * author: wd3322
5 | */
6 |
7 | declare global {
8 | interface Window {
9 | Vue2ToCompositionApiVmBody: any,
10 | require: any
11 | }
12 | }
13 |
14 | interface VmContent {
15 | props: any,
16 | data: Function,
17 | dataOptions: any,
18 | computed: any,
19 | watch: any,
20 | methods: any,
21 | filters: any,
22 | hooks: any,
23 | emits: string[],
24 | refs: string[],
25 | use: any,
26 | import: any
27 | }
28 |
29 | interface VmKeys {
30 | props: string[],
31 | data: string[],
32 | computed: string[],
33 | watch: string[],
34 | methods: string[],
35 | filters: string[],
36 | hooks: string[],
37 | use: Function,
38 | import: Function
39 | }
40 |
41 | interface VmOutput {
42 | import: string,
43 | use: string,
44 | props: string,
45 | emits: string,
46 | refs: string,
47 | data: string,
48 | computed: string,
49 | watch: string,
50 | hooks: string,
51 | methods: string,
52 | filters: string
53 | }
54 |
55 | interface VmSetContentMethods {
56 | props: Function,
57 | data: Function,
58 | computed: Function,
59 | watch: Function,
60 | hooks: Function,
61 | methods: Function,
62 | filters: Function,
63 | emits: Function,
64 | refs: Function,
65 | use: Function,
66 | import: Function,
67 | output: Function
68 | }
69 |
70 | interface UtilsMethods {
71 | getContentStr: Function,
72 | replaceKey: Function,
73 | replaceValue: Function,
74 | addImport: Function,
75 | addUse: Function
76 | }
77 |
78 | function getPrototype(value: any): string {
79 | return Object.prototype.toString.call(value).replace(/^\[object (\S+)\]$/, '$1').toLowerCase()
80 | }
81 |
82 | function Vue2ToCompositionApi(
83 | entryScriptContent: string = '',
84 | options: {
85 | isDebug?: boolean
86 | } = {
87 | isDebug: false
88 | }
89 | ): string | undefined {
90 | if (getPrototype(entryScriptContent) !== 'string') {
91 | throw new Error(`Vue2ToCompositionApi ${entryScriptContent} is not a string`)
92 | }
93 | if (getPrototype(options) !== 'object') {
94 | throw new Error(`Vue2ToCompositionApi ${options} is not a object`)
95 | }
96 | try {
97 | // output script content init
98 | let outputScriptContent: string = ''
99 |
100 | // js-beautify init
101 | const jsBeautify: any = require('js-beautify')
102 | const jsBeautifyOptions: any = {
103 | indent_size: 4,
104 | indent_char: '',
105 | indent_with_tabs: true,
106 | eol: '\n',
107 | brace_style: 'collapse-preserve-inline'
108 | }
109 |
110 | // reg-exp init
111 | const braceRegExp: RegExp = /\{[\s\S]*\}/g
112 | const parenthesisRegExp: RegExp = /\((.*)\)/g
113 |
114 | // vm body init
115 | window.Vue2ToCompositionApiVmBody = {}
116 | window.require = function() {}
117 | const beautifyScriptContent: string = jsBeautify(entryScriptContent, jsBeautifyOptions)
118 | const modelScriptContent: string | undefined = (function() {
119 | const componentsRegExp: RegExp = /components: ((\{\})|(\{[\s\S]+?\}))[\,\n]/
120 | const mixinsRegExp: RegExp = /mixins: ((\[\])|(\[([\s\S]+?)\]))[\,\n]/
121 | return beautifyScriptContent
122 | .match(braceRegExp)?.[0]
123 | .replace(componentsRegExp, '')
124 | .replace(mixinsRegExp, '')
125 | })()
126 | if (modelScriptContent) {
127 | eval(`window.Vue2ToCompositionApiVmBody = ${modelScriptContent}`)
128 | } else {
129 | throw new Error(`Vue2ToCompositionApi entry script content not a valid content`)
130 | }
131 | const vmBody: any = window.Vue2ToCompositionApiVmBody
132 |
133 | // vm content init
134 | const vmContent: VmContent = {
135 | props: getPrototype(vmBody.props) === 'object' ? vmBody.props : {},
136 | data: getPrototype(vmBody.data).indexOf('function') !== -1 ? vmBody.data : () => ({}),
137 | dataOptions: getPrototype(vmBody.data).indexOf('function') !== -1 ? vmBody.data() : {},
138 | computed: getPrototype(vmBody.computed) === 'object' ? vmBody.computed : {},
139 | watch: getPrototype(vmBody.watch) === 'object' ? vmBody.watch : {},
140 | methods: getPrototype(vmBody.methods) === 'object' ? vmBody.methods : {},
141 | filters: getPrototype(vmBody.filters) === 'object' ? vmBody.filters : {},
142 | hooks: {},
143 | emits: [],
144 | refs: [],
145 | use: {},
146 | import: { vue: [], 'vue-router': [], vuex: [] }
147 | }
148 |
149 | // vm hooks content init
150 | for (const prop in vmBody) {
151 | if (
152 | ['beforeCreate', 'created', 'beforeMount', 'mounted',
153 | 'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed',
154 | 'activated', 'deactivated', 'errorCaptured'].includes(prop) &&
155 | getPrototype(vmBody[prop]).indexOf('function') !== -1
156 | ) {
157 | vmContent.hooks[prop] = vmBody[prop]
158 | }
159 | }
160 |
161 | // vm keys init
162 | const vmKeys: VmKeys = {
163 | props: Object.keys(vmContent.props),
164 | data: Object.keys(vmContent.dataOptions),
165 | computed: Object.keys(vmContent.computed),
166 | watch: Object.keys(vmContent.watch),
167 | methods: Object.keys(vmContent.methods),
168 | filters: Object.keys(vmContent.filters),
169 | hooks: Object.keys(vmContent.hooks),
170 | use: (): string[] => Object.keys(vmContent.use),
171 | import: (): string[] => Object.keys(vmContent.import)
172 | }
173 |
174 | // vm output init
175 | const vmOutput: VmOutput = {
176 | import: '',
177 | use: '',
178 | props: '',
179 | emits: '',
180 | refs: '',
181 | data: '',
182 | computed: '',
183 | watch: '',
184 | hooks: '',
185 | methods: '',
186 | filters: ''
187 | }
188 |
189 | // vm set content methods init
190 | const vmSetContentMethods: VmSetContentMethods = {
191 | props(): void {
192 | if (vmKeys.props.length > 0) {
193 | const propsContentStr: string = utilMethods.getContentStr(vmContent.props, null, {
194 | function: (params: any) => {
195 | const { type, content } = params
196 | if (type === 'custom') {
197 | return content
198 | }
199 | }
200 | })
201 | if (propsContentStr) {
202 | vmOutput.props = `const props = defineProps(${propsContentStr})`
203 | }
204 | }
205 | },
206 | data(): void {
207 | if (vmKeys.data.length > 0) {
208 | const dataFunctionStr: string = utilMethods.getContentStr(vmContent.data, true, {
209 | function: (params: any) => {
210 | const { type, body } = params
211 | if (type === 'custom') {
212 | return body
213 | }
214 | }
215 | })
216 | if (dataFunctionStr) {
217 | const dataContentRegExp: RegExp = /return ([\s\S]*)\}/
218 | const dataContentStr: string = dataFunctionStr.match(dataContentRegExp)?.[1] || '{}'
219 | vmOutput.data = `const data = reactive(${dataContentStr})`
220 | utilMethods.addImport('vue', 'reactive')
221 | }
222 | }
223 | },
224 | computed(): void {
225 | if (vmKeys.computed.length > 0) {
226 | const computedValues: string[] = []
227 | for (const prop in vmContent.computed) {
228 | const computedContent: any = vmContent.computed[prop]
229 | if (
230 | computedContent &&
231 | ['object', 'function', 'asyncfunction'].includes(getPrototype(computedContent))
232 | ) {
233 | const computedName: string = getPrototype(computedContent).indexOf('function') !== -1 ? computedContent.name : prop
234 | const computedFunctionStr: string = utilMethods.getContentStr(computedContent)
235 | if (computedName && computedFunctionStr) {
236 | computedValues.push(`const ${computedName} = computed(${computedFunctionStr})`)
237 | }
238 | }
239 | }
240 | if (computedValues.length > 0) {
241 | vmOutput.computed = computedValues.join('\n\n')
242 | utilMethods.addImport('vue', 'computed')
243 | }
244 | }
245 | },
246 | watch(): void {
247 | if (vmKeys.watch.length > 0) {
248 | const watchValues: string[] = []
249 | for (const prop in vmContent.watch) {
250 | const watchContent: any = vmContent.watch[prop]
251 | if (getPrototype(watchContent).indexOf('function') !== -1) {
252 | const watchName: string = utilMethods.replaceKey(watchContent.name)
253 | const watchFunctionStr: string = utilMethods.getContentStr(watchContent)
254 | if (watchName && watchFunctionStr) {
255 | watchValues.push(`watch(() => ${watchName}, ${watchFunctionStr})`)
256 | }
257 | } else if (
258 | watchContent &&
259 | getPrototype(watchContent) === 'object' &&
260 | getPrototype(watchContent.handler).indexOf('function') !== -1
261 | ) {
262 | const watchName: string = utilMethods.replaceKey(prop)
263 | const watchFunctionStr: string = utilMethods.getContentStr(watchContent.handler)
264 | const watchOptionsStr: string = utilMethods.getContentStr(watchContent, null, {
265 | object: (params: any) => {
266 | const { value, values } = params
267 | if (value.handler) {
268 | const index = values.findIndex((item: string) => /^handler\:/.test(item))
269 | values.splice(index, 1)
270 | }
271 | return values.length > 0 ? `{\n${values.join(',\n')}\n}` : '{}'
272 | }
273 | })
274 | if (watchName && watchFunctionStr && watchOptionsStr) {
275 | watchValues.push(
276 | watchOptionsStr !== '{}'
277 | ? `watch(() => ${watchName}, ${watchFunctionStr}, ${watchOptionsStr})`
278 | : `watch(() => ${watchName}, ${watchFunctionStr})`
279 | )
280 | }
281 | }
282 | }
283 | if (watchValues.length > 0) {
284 | vmOutput.watch = watchValues.join('\n\n')
285 | utilMethods.addImport('vue', 'watch')
286 | }
287 | }
288 | },
289 | hooks(): void {
290 | if (vmKeys.hooks.length > 0) {
291 | const hookValues: string[] = []
292 | for (const prop in vmContent.hooks) {
293 | const hookContent: any = vmContent.hooks[prop]
294 | if (getPrototype(hookContent).indexOf('function') !== -1) {
295 | if (['beforeCreate', 'created'].includes(hookContent.name)) {
296 | const hookName: string = `on${hookContent.name.substring(0, 1).toUpperCase()}${hookContent.name.substring(1)}`
297 | const hookFunctionStr: string = utilMethods.getContentStr(hookContent, null, {
298 | function: (params: any) => {
299 | const { type, value, arg, body } = params
300 | if (type === 'custom') {
301 | return value.constructor.name === 'AsyncFunction'
302 | ? `async function ${hookName} ${arg} ${body}\n${hookName}()`
303 | : `function ${hookName} ${arg} ${body}\n${hookName}()`
304 | }
305 | }
306 | })
307 | if (hookName && hookFunctionStr) {
308 | hookValues.push(hookFunctionStr)
309 | }
310 | } else if (
311 | ['beforeMount', 'mounted', 'beforeUpdate', 'updated', 'beforeDestroy', 'destroyed',
312 | 'activated', 'deactivated', 'errorCaptured'].includes(hookContent.name)
313 | ) {
314 | const v3HooksNameDist: any = {
315 | beforeMount: 'onBeforeMount',
316 | mounted: 'onMounted',
317 | beforeUpdate: 'onBeforeUpdate',
318 | updated: 'onUpdated',
319 | beforeDestroy: 'onBeforeUnmount',
320 | destroyed: 'onUnmounted',
321 | activated: 'onActivated',
322 | deactivated: 'onDeactivated',
323 | errorCaptured: 'onErrorCaptured'
324 | }
325 | const hookName: string = v3HooksNameDist[hookContent.name as string]
326 | const hookFunctionStr: string = utilMethods.getContentStr(hookContent, null, {
327 | function: (params: any) => {
328 | const { type, value, arg, body } = params
329 | if (type === 'custom') {
330 | return value.constructor.name === 'AsyncFunction'
331 | ? `${hookName} (async ${arg} => ${body})`
332 | : `${hookName} (${arg} => ${body})`
333 | }
334 | }
335 | })
336 | if (hookName && hookFunctionStr) {
337 | hookValues.push(hookFunctionStr)
338 | utilMethods.addImport('vue', hookName)
339 | }
340 | }
341 | }
342 | }
343 | if (hookValues.length > 0) {
344 | vmOutput.hooks = hookValues.join('\n\n')
345 | }
346 | }
347 | },
348 | methods(): void {
349 | if (vmKeys.methods.length > 0) {
350 | const methodValues: string[] = []
351 | for (const prop in vmContent.methods) {
352 | const methodContent: any = vmContent.methods[prop]
353 | if (getPrototype(methodContent).indexOf('function') !== -1) {
354 | const methodName: string = methodContent.name
355 | const methodFunctionStr: string = utilMethods.getContentStr(methodContent, null, {
356 | function: (params: any) => {
357 | const { type, value, arg, body } = params
358 | if (type === 'custom') {
359 | return value.constructor.name === 'AsyncFunction'
360 | ? `async function ${methodName} ${arg} ${body}`
361 | : `function ${methodName} ${arg} ${body}`
362 | }
363 | }
364 | })
365 | if (methodName && methodFunctionStr) {
366 | methodValues.push(methodFunctionStr)
367 | }
368 | }
369 | }
370 | if (methodValues.length > 0) {
371 | vmOutput.methods = methodValues.join('\n\n')
372 | }
373 | }
374 | },
375 | filters(): void {
376 | if (vmKeys.filters.length > 0) {
377 | const filterValues: string[] = []
378 | for (const prop in vmContent.filters) {
379 | const filterContent: any = vmContent.filters[prop]
380 | if (getPrototype(filterContent).indexOf('function') !== -1) {
381 | const filterName: string = filterContent.name
382 | const filterFunctionStr: string = utilMethods.getContentStr(filterContent, null, {
383 | function: (params: any) => {
384 | const { type, value, arg, body } = params
385 | if (type === 'custom') {
386 | return value.constructor.name === 'AsyncFunction'
387 | ? `async function ${filterName} ${arg} ${body}`
388 | : `function ${filterName} ${arg} ${body}`
389 | }
390 | }
391 | })
392 | if (filterName && filterFunctionStr) {
393 | filterValues.push(filterFunctionStr)
394 | }
395 | }
396 | }
397 | if (filterValues.length > 0) {
398 | vmOutput.filters = filterValues.join('\n\n')
399 | }
400 | }
401 | },
402 | emits(): void {
403 | if (vmContent.emits.length > 0) {
404 | const emitValues: string[] = []
405 | for (const emit of vmContent.emits) {
406 | if (emit) {
407 | emitValues.push(`\'${emit}\'`)
408 | }
409 | }
410 | if (emitValues.length > 0) {
411 | vmOutput.emits = `const emit = defineEmits([${emitValues.join(', ')}])`
412 | }
413 | }
414 | },
415 | refs(): void {
416 | if (vmContent.refs.length > 0) {
417 | const refValues: string[] = []
418 | for (const ref of vmContent.refs) {
419 | if (ref) {
420 | refValues.push(`const ${ref} = ref(null)`)
421 | }
422 | }
423 | if (refValues.length > 0) {
424 | vmOutput.refs = refValues.join('\n')
425 | utilMethods.addImport('vue', 'ref')
426 | }
427 | }
428 | },
429 | use(): void {
430 | if (vmKeys.use().length > 0) {
431 | const useValues: string[] = []
432 | for (const prop in vmContent.use) {
433 | const useContent: string = vmContent.use[prop]
434 | if (useContent) {
435 | useValues.push(useContent)
436 | }
437 | }
438 | if (useValues.length > 0) {
439 | vmOutput.use = useValues.sort().join('\n')
440 | }
441 | }
442 | },
443 | import(): void {
444 | if (vmKeys.import().length > 0) {
445 | const importValues: string[] = []
446 | for (const prop in vmContent.import) {
447 | const importContent: string[] = vmContent.import[prop]
448 | if (importContent.length > 0) {
449 | importValues.push(`import { ${importContent.sort().join(', ')} } from \'${prop}\'`)
450 | }
451 | }
452 | if (importValues.length > 0) {
453 | vmOutput.import = importValues.join('\n')
454 | }
455 | }
456 | },
457 | output(): void {
458 | const outputValues: string[] = []
459 | for (const prop in vmOutput) {
460 | const outputContent: string = vmOutput[prop as keyof VmOutput]
461 | if (outputContent) {
462 | outputValues.push(outputContent)
463 | }
464 | }
465 | if (outputValues.length > 0) {
466 | outputScriptContent = outputValues.join('\n\n')
467 | }
468 | }
469 | }
470 |
471 | // util methods init
472 | const utilMethods: UtilsMethods = {
473 | getContentStr(
474 | value: any,
475 | replaceDataKeyToUseData: boolean = false,
476 | resultCallbackContent: {
477 | string?: Function,
478 | object?: Function,
479 | array?: Function,
480 | function?: Function,
481 | other?: Function,
482 | } = {
483 | string: undefined,
484 | object: undefined,
485 | array: undefined,
486 | function: undefined,
487 | other: undefined
488 | }
489 | ): string | undefined {
490 | let result: string = ''
491 | // string prototype
492 | if (getPrototype(value) === 'string') {
493 | result = `\'${value}\'`
494 | if (resultCallbackContent.string) {
495 | result = resultCallbackContent.string({ value, result })
496 | }
497 | }
498 | // object prototype
499 | else if (getPrototype(value) === 'object') {
500 | const values: string[] = []
501 | for (const prop in value) {
502 | const content: string = utilMethods.getContentStr(value[prop], replaceDataKeyToUseData, resultCallbackContent)
503 | values.push(`${prop}: ${content}`)
504 | }
505 | result = values.length > 0 ? `{\n${values.join(',\n')}\n}` : '{}'
506 | if (resultCallbackContent.object) {
507 | result = resultCallbackContent.object({ value, values, result }) || result
508 | }
509 | }
510 | // array prototype
511 | else if (getPrototype(value) === 'array') {
512 | const values: string[] = []
513 | for (const item of value) {
514 | const content: string = utilMethods.getContentStr(item, replaceDataKeyToUseData, resultCallbackContent)
515 | values.push(content)
516 | }
517 | result = values.length > 0 ? `[${values.join(', ')}]` : '[]'
518 | if (resultCallbackContent.array) {
519 | result = resultCallbackContent.array({ value, values, result }) || result
520 | }
521 | }
522 | // function prototype
523 | else if (getPrototype(value).indexOf('function') !== -1) {
524 | let content: string = value.toString()
525 | // native code
526 | if (
527 | ['String', 'Number', 'Boolean', 'Array', 'Object', 'Date', 'Function', 'Symbol'].includes(value.name) &&
528 | content.match(braceRegExp)?.[0] === '{ [native code] }'
529 | ) {
530 | result = `${value.name}`
531 | if (resultCallbackContent.function) {
532 | result = resultCallbackContent.function({ type: 'native', value, content, result }) || result
533 | }
534 | }
535 | // custom code
536 | else {
537 | content = utilMethods.replaceValue(content, replaceDataKeyToUseData)
538 | const arg: string = content.match(parenthesisRegExp)?.[0] || '()'
539 | const body: string = content.substring(content.indexOf(arg) + arg.length).match(braceRegExp)?.[0] || '{}'
540 | result = value.constructor.name === 'AsyncFunction'
541 | ? `async ${arg} => ${body}`
542 | : `${arg} => ${body}`
543 | if (resultCallbackContent.function) {
544 | result = resultCallbackContent.function({ type: 'custom', value, content, arg, body, result }) || result
545 | }
546 | }
547 | }
548 | // other prototype
549 | else {
550 | result = `${value}`
551 | if (resultCallbackContent.other) {
552 | result = resultCallbackContent.other({ value, result }) || result
553 | }
554 | }
555 | return result
556 | },
557 | replaceKey(key: string, dataKeyToUseData: boolean = false): string | void {
558 | let result: string = ''
559 | // props key
560 | if (vmKeys.props.includes(key)) {
561 | result = 'props.' + key
562 | }
563 | // computed key
564 | else if (vmKeys.computed.includes(key)) {
565 | result = key + '.value'
566 | }
567 | // methods key
568 | else if (vmKeys.methods.includes(key)) {
569 | result = key
570 | }
571 | // data key
572 | else if (vmKeys.data.includes(key) && !dataKeyToUseData) {
573 | result = 'data.' + key
574 | }
575 | // useData key
576 | else if (vmKeys.data.includes(key) && dataKeyToUseData) {
577 | utilMethods.addUse('data')
578 | result = 'useData().' + key
579 | }
580 | // unknown key
581 | else if (key) {
582 | utilMethods.addImport('vue', 'getCurrentInstance')
583 | utilMethods.addUse('vm')
584 | result = `/* Warn: Unknown source: ${key} */ $vm.${key}`
585 | }
586 | return result
587 | },
588 | replaceValue(value: string, dataKeyToUseData: boolean = false): string {
589 | let result: string = ''
590 | const thisKeyRegExp: RegExp = /this(\.{1})([$a-zA-Z0-9_]+)/g
591 | const refKeyRegExp: RegExp = /\$REFS_KEY(\.|\?\.)([$a-zA-Z0-9_]+)/g
592 | result = value
593 | .replace(thisKeyRegExp, function(
594 | str: string,
595 | separator: string,
596 | key: string,
597 | index: number,
598 | content: string
599 | ): string {
600 | // props key
601 | if (vmKeys.props.includes(key)) {
602 | return 'props.' + key
603 | }
604 | // computed key
605 | else if (vmKeys.computed.includes(key)) {
606 | return key + '.value'
607 | }
608 | // methods key
609 | else if (vmKeys.methods.includes(key)) {
610 | return key
611 | }
612 | // data key
613 | else if (vmKeys.data.includes(key) && !dataKeyToUseData) {
614 | return 'data.' + key
615 | }
616 | // useData key
617 | else if (vmKeys.data.includes(key) && dataKeyToUseData) {
618 | utilMethods.addUse('data')
619 | return 'useData().' + key
620 | }
621 | // attrs key
622 | else if (key === '$attrs') {
623 | utilMethods.addImport('vue', 'useAttrs')
624 | utilMethods.addUse('attrs')
625 | return key.substring(1)
626 | }
627 | // slots key
628 | else if (key === '$slots') {
629 | utilMethods.addImport('vue', 'useSlots')
630 | utilMethods.addUse('slots')
631 | return key.substring(1)
632 | }
633 | // router key
634 | else if (key === '$router') {
635 | utilMethods.addImport('vue-router', 'useRouter')
636 | utilMethods.addUse('router')
637 | return key.substring(1)
638 | }
639 | // route key
640 | else if (key === '$route') {
641 | utilMethods.addImport('vue-router', 'useRoute')
642 | utilMethods.addUse('route')
643 | return key.substring(1)
644 | }
645 | // store key
646 | else if (key === '$store') {
647 | utilMethods.addImport('vuex', 'useStore')
648 | utilMethods.addUse('store')
649 | return key.substring(1)
650 | }
651 | // nextTick key
652 | else if (key === '$nextTick') {
653 | utilMethods.addImport('vue', 'nextTick')
654 | return key.substring(1)
655 | }
656 | // set key
657 | else if (key === '$set') {
658 | utilMethods.addImport('vue', 'set')
659 | return key.substring(1)
660 | }
661 | // delete key
662 | else if (key === '$delete') {
663 | utilMethods.addImport('vue', 'del')
664 | return key.substring(1)
665 | }
666 | // emit key
667 | else if (key === '$emit') {
668 | const nameRegExp: RegExp = /^\([\'\"\`](update:){0,1}([$a-zA-Z0-9_-]+)[\'\"\`]/
669 | const name: string = content.substring(index + str.length).match(nameRegExp)?.[2] || ''
670 | if (name) {
671 | !vmContent.emits.includes(name) && vmContent.emits.push(name)
672 | } else {
673 | utilMethods.addImport('vue', 'getCurrentInstance')
674 | utilMethods.addUse('vm')
675 | }
676 | return name
677 | ? key.substring(1)
678 | : `/* Warn: Cannot find emit name */ $vm.$emit`
679 | }
680 | // refs key
681 | else if (key === '$refs') {
682 | const nameRegExp: RegExp = /(^\.|^\?\.)([$a-zA-Z0-9_]+)/
683 | const name: string = content.substring(index + str.length).match(nameRegExp)?.[2] || ''
684 | if (name) {
685 | !vmContent.refs.includes(name) && vmContent.refs.push(name)
686 | } else {
687 | utilMethods.addImport('vue', 'getCurrentInstance')
688 | utilMethods.addUse('vm')
689 | }
690 | return name
691 | ? '$REFS_KEY'
692 | : `/* Warn: Cannot find refs name */ $vm.$refs`
693 | }
694 | // other key
695 | else if (
696 | ['$data', '$props', '$el', '$options', '$parent', '$root', '$children', '$isServer',
697 | '$listeners', '$watch', '$on', '$once', '$off', '$mount', '$forceUpdate', '$destroy'].includes(key)
698 | ) {
699 | utilMethods.addImport('vue', 'getCurrentInstance')
700 | utilMethods.addUse('vm')
701 | return '$vm.' + key
702 | }
703 | // unknown key
704 | else if (key) {
705 | utilMethods.addImport('vue', 'getCurrentInstance')
706 | utilMethods.addUse('vm')
707 | return `/* Warn: Unknown source: ${key} */ $vm.${key}`
708 | }
709 | // nonexistent key
710 | else {
711 | utilMethods.addImport('vue', 'getCurrentInstance')
712 | utilMethods.addUse('vm')
713 | return `/* Warn: Cannot find key */ $vm${separator}`
714 | }
715 | })
716 | .replace(refKeyRegExp, function(
717 | str: string,
718 | separator: string,
719 | name: string
720 | ): string {
721 | // reset refs key
722 | return name + '.value'
723 | })
724 | return result
725 | },
726 | addImport(type: string, value: string): void {
727 | if (['vue', 'vue-router', 'vuex'].includes(type)) {
728 | const importContent: string[] = vmContent.import[type]
729 | if (!importContent?.includes(value)) {
730 | importContent.push(value)
731 | }
732 | }
733 | },
734 | addUse(type: string): void {
735 | if (['data', 'vm', 'attrs', 'slots', 'router', 'route', 'store'].includes(type)) {
736 | const contentDist: any = {
737 | vm: 'const { proxy: $vm } = getCurrentInstance()',
738 | data: 'const useData = () => data',
739 | attrs: 'const attrs = useAttrs()',
740 | slots: 'const slots = useSlots()',
741 | router: 'const router = useRouter()',
742 | route: 'const route = useRoute()',
743 | store: 'const store = useStore()'
744 | }
745 | const useContent: string = contentDist[type]
746 | if (useContent) {
747 | vmContent.use[type] = useContent
748 | }
749 | }
750 | }
751 | }
752 |
753 | // vm set content methods runing
754 | for (const prop in vmSetContentMethods) {
755 | const vmSetContentMethod: Function = vmSetContentMethods[prop as keyof VmSetContentMethods]
756 | if (getPrototype(vmSetContentMethod).indexOf('function') !== -1) {
757 | vmSetContentMethod()
758 | }
759 | }
760 |
761 | // output script content beautify
762 | outputScriptContent = jsBeautify(outputScriptContent, jsBeautifyOptions)
763 |
764 | // debug console log
765 | if (options.isDebug) {
766 | console.log('Vue2ToCompositionApi', {
767 | entryScriptContent,
768 | outputScriptContent,
769 | vmBody,
770 | vmContent,
771 | vmOutput,
772 | vmKeys
773 | })
774 | }
775 |
776 | // done
777 | return outputScriptContent
778 | } catch (err: any) {
779 | throw new Error(err)
780 | }
781 | }
782 |
783 | export default Vue2ToCompositionApi
784 |
--------------------------------------------------------------------------------