Skip to content

Commit 9238638

Browse files
refactor(frontend): Migrate the frontend application from Vue.js 2 to 3 (#2119)
Co-authored-by: Guillaume Chau <[email protected]>
1 parent aac74c0 commit 9238638

File tree

86 files changed

+2457
-544
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+2457
-544
lines changed

.eslintrc.js

+12
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ module.exports = {
3939
},
4040
},
4141
],
42+
'func-call-spacing': 'off',
43+
'vue/component-definition-name-casing': 'off',
44+
'vue/multi-word-component-names': 'off',
4245
'@typescript-eslint/ban-ts-comment': 'warn',
4346
'@typescript-eslint/no-use-before-define': 'off',
4447
'@typescript-eslint/explicit-module-boundary-types': 'off',
@@ -76,6 +79,14 @@ module.exports = {
7679
files: ['packages/shell-dev-vue3/**'],
7780
rules: {
7881
'vue/valid-template-root': 'off',
82+
'vue/one-component-per-file': 'off',
83+
'vue/no-v-model-argument': 'off',
84+
},
85+
},
86+
{
87+
files: ['packages/app-frontend/**'],
88+
rules: {
89+
'vue/no-v-model-argument': 'off',
7990
},
8091
},
8192
{
@@ -99,6 +110,7 @@ module.exports = {
99110
},
100111
rules: {
101112
'no-console': 'off',
113+
'vue/no-multiple-template-root': 'off',
102114
},
103115
},
104116
{

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
"eslint-plugin-node": "^11.1.0",
6262
"eslint-plugin-promise": "^5.1.0",
6363
"eslint-plugin-standard": "^5.0.0",
64-
"eslint-plugin-vue": "^6.0.0",
64+
"eslint-plugin-vue": "^9.17.0",
6565
"execa": "^4.0.3",
6666
"inquirer": "^6.2.0",
6767
"lerna": "^4.0.0",
@@ -70,8 +70,9 @@
7070
"semver": "^5.5.1",
7171
"start-server-and-test": "^1.7.1",
7272
"tailwindcss": "npm:@tailwindcss/postcss7-compat",
73-
"vue-loader": "^15.9.0",
74-
"webpack-dev-server": "^4.0.0-beta.0"
73+
"vue-loader": "^17.2.2",
74+
"webpack-dev-server": "^4.0.0-beta.0",
75+
"svg-inline-loader": "^0.8.2"
7576
},
7677
"resolutions": {
7778
"cypress": "=3.4.1",

packages/app-backend-vue2/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
"lodash": "^4.17.21"
1818
},
1919
"devDependencies": {
20+
"vue": "^2.7.10",
21+
"vue-loader": "^15.7.1",
2022
"@types/node": "^13.9.1",
2123
"@types/webpack-env": "^1.15.1",
2224
"core-js": "^3.20.2",

packages/app-frontend/package.json

+9-4
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,23 @@
1010
"@vue/ui": "^0.12.5",
1111
"circular-json-es6": "^2.0.2",
1212
"d3": "^5.16.0",
13+
"floating-vue": "^5.2.2",
1314
"lodash": "^4.17.15",
1415
"lru-cache": "^5.1.1",
1516
"monaco-editor": "^0.24.0",
1617
"pixi.js-legacy": "^6.2.0",
17-
"portal-vue": "^2.1.7",
1818
"scroll-into-view-if-needed": "^2.2.28",
1919
"semver": "^7.3.5",
2020
"stylus": "^0.54.7",
2121
"stylus-loader": "^3.0.2",
2222
"tinycolor2": "^1.4.2",
23-
"vue": "^2.7.10",
24-
"vue-router": "^3.6.5",
25-
"vue-virtual-scroller": "^1.0.10"
23+
"vue": "^3.3.4",
24+
"vue-resize": "^2.0.0-alpha.1",
25+
"vue-router": "^4.2.5",
26+
"vue-safe-teleport": "^0.1.2",
27+
"vue-virtual-scroller": "^2.0.0-alpha.1"
28+
},
29+
"devDependencies": {
30+
"@akryum/md-icons-svg": "^1.0.1"
2631
}
2732
}

packages/app-frontend/src/app.ts

+21-35
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import App from './features/App.vue'
2-
3-
import Vue from 'vue'
2+
import { App as VueApp, createApp as createVueApp } from 'vue'
43
import { isChrome, initEnv, SharedData, initSharedData, destroySharedData, BridgeEvents } from '@vue-devtools/shared-utils'
5-
import { createRouter } from './router'
4+
import { createRouterInstance } from './router'
65
import { getBridge, setBridge } from './features/bridge'
76
import { setAppConnected, setAppInitializing } from './features/connection'
87
import { setupAppsBridgeEvents } from './features/apps'
@@ -12,37 +11,25 @@ import { setupCustomInspectorBridgeEvents } from './features/inspector/custom/co
1211
import { setupPluginsBridgeEvents } from './features/plugin'
1312
import { setupPlugins } from './plugins'
1413

15-
setupPlugins()
16-
1714
// Capture and log devtool errors when running as actual extension
1815
// so that we can debug it by inspecting the background page.
1916
// We do want the errors to be thrown in the dev shell though.
20-
if (isChrome) {
21-
Vue.config.errorHandler = (e, vm) => {
22-
getBridge()?.send('ERROR', {
23-
message: e.message,
24-
stack: e.stack,
25-
component: vm.$options.name || (vm.$options as any)._componentTag || 'anonymous',
26-
})
27-
}
28-
}
29-
30-
// @ts-ignore
31-
Vue.options.renderError = (h, e) => {
32-
return h('pre', {
33-
class: 'text-white bg-red-500 p-2 rounded text-xs overflow-auto',
34-
}, e.stack)
35-
}
36-
3717
export function createApp () {
38-
const router = createRouter()
39-
40-
const app = new Vue({
41-
router,
42-
render: h => h(App as any),
43-
})
44-
45-
// @TODO [Vue 3] Setup plugins
18+
const router = createRouterInstance()
19+
20+
const app = createVueApp(App)
21+
app.use(router)
22+
setupPlugins(app)
23+
24+
if (isChrome) {
25+
app.config.errorHandler = (e, vm) => {
26+
getBridge()?.send('ERROR', {
27+
message: (e as Error).message,
28+
stack: (e as Error).stack,
29+
component: vm?.$options.name || (vm?.$options as any)._componentTag || 'anonymous',
30+
})
31+
}
32+
}
4633

4734
return app
4835
}
@@ -51,22 +38,22 @@ export function createApp () {
5138
* Connect then init the app. We need to reconnect on every reload, because a
5239
* new backend will be injected.
5340
*/
54-
export function connectApp (app, shell) {
41+
export function connectApp (app: VueApp, shell) {
5542
shell.connect(async bridge => {
5643
setBridge(bridge)
5744
// @TODO remove
5845
// @ts-ignore
5946
window.bridge = bridge
6047

61-
if (Object.prototype.hasOwnProperty.call(Vue.prototype, '$shared')) {
48+
if (app.config.globalProperties.$shared) {
6249
destroySharedData()
6350
} else {
64-
Object.defineProperty(Vue.prototype, '$shared', {
51+
Object.defineProperty(app.config.globalProperties, '$shared', {
6552
get: () => SharedData,
6653
})
6754
}
6855

69-
initEnv(Vue)
56+
initEnv(app)
7057

7158
bridge.on(BridgeEvents.TO_FRONT_TITLE, ({ title }: { title: string }) => {
7259
document.title = `${title} - Vue devtools`
@@ -75,7 +62,6 @@ export function connectApp (app, shell) {
7562
await initSharedData({
7663
bridge,
7764
persist: true,
78-
Vue,
7965
})
8066

8167
if (SharedData.logDetected) {

packages/app-frontend/src/assets/style/index.styl

+4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ html, body
3030
body
3131
overflow hidden
3232

33+
#app
34+
width: 100%
35+
height: 100%
36+
3337
button:focus
3438
outline none
3539

packages/app-frontend/src/features/App.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ export default defineComponent({
122122
</SplitPane>
123123
</template>
124124

125-
<portal-target name="root" />
125+
<TeleportTarget id="root" />
126126

127127
<ErrorOverlay />
128128
</div>

packages/app-frontend/src/features/apps/AppSelect.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { watch, defineComponent, computed } from 'vue'
66
import { BridgeEvents, SharedData } from '@vue-devtools/shared-utils'
77
import { useApps, pendingSelectAppId, scanLegacyApps } from '@front/features/apps'
88
import { useOrientation } from '@front/features/layout/orientation'
9-
import { useRouter } from '@front/util/router'
9+
import { useRouter } from 'vue-router'
1010
import { useBridge } from '../bridge'
1111
import { useVueVersionCheck } from './vue-version-check'
1212

packages/app-frontend/src/features/apps/AppSelectPane.vue

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import AppSelectPaneItem from './AppSelectPaneItem.vue'
44
import { watch, defineComponent, ref, computed } from 'vue'
55
import { BridgeEvents, SharedData } from '@vue-devtools/shared-utils'
66
import { useApps, pendingSelectAppId, scanLegacyApps } from '@front/features/apps'
7-
import { useRouter } from '@front/util/router'
7+
import { useRouter } from 'vue-router'
88
import { useBridge } from '../bridge'
99
1010
export default defineComponent({

packages/app-frontend/src/features/apps/AppSelectPaneItem.vue

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export default defineComponent({
1414
default: false,
1515
},
1616
},
17+
emits: ['select'],
1718
1819
setup (props) {
1920
const { getLatestVersion } = useVueVersionCheck()
@@ -89,7 +90,7 @@ export default defineComponent({
8990
.app-button {
9091
@apply rounded-none text-left h-auto py-1.5;
9192
92-
> >>> .content {
93+
> :deep(.content) {
9394
@apply min-w-full justify-start;
9495
9596
> .default-slot {

packages/app-frontend/src/features/apps/index.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { ref, computed } from 'vue'
22
import { BridgeEvents, Bridge } from '@vue-devtools/shared-utils'
33
import { getBridge } from '@front/features/bridge'
4-
import { useRoute, useRouter } from '@front/util/router'
4+
import { useRoute, useRouter } from 'vue-router'
55
import { fetchLayers } from '../timeline/composable'
66

77
export interface AppRecord {
@@ -15,7 +15,7 @@ const apps = ref<AppRecord[]>([])
1515

1616
export function useCurrentApp () {
1717
const route = useRoute()
18-
const currentAppId = computed(() => route.value.params.appId)
18+
const currentAppId = computed(() => route.params.appId as string)
1919
const currentApp = computed(() => apps.value.find(a => currentAppId.value === a.id))
2020

2121
return {
@@ -71,7 +71,7 @@ function fetchApps () {
7171
getBridge().send(BridgeEvents.TO_BACK_APP_LIST, {})
7272
}
7373

74-
export const pendingSelectAppId = ref<string>(null)
74+
export const pendingSelectAppId = ref<string | null>(null)
7575

7676
const pendingSelectPromises: (() => void)[] = []
7777

packages/app-frontend/src/features/chrome/pane-visibility.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { isChrome } from '@vue-devtools/shared-utils'
22

33
let panelShown = !isChrome
4-
let pendingAction = null
4+
let pendingAction: (() => void | Promise<void>) | null = null
55

66
if (isChrome) {
77
chrome.runtime.onMessage.addListener(request => {

packages/app-frontend/src/features/code/CodeEditor.vue

+12-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<script>
22
// Fork of https://github.com/egoist/vue-monaco/
3-
43
import * as monaco from 'monaco-editor'
54
import assign from 'lodash/merge'
65
@@ -11,17 +10,12 @@ monaco.editor.defineTheme('github-dark', require('@front/assets/github-theme/dar
1110
1211
export default {
1312
name: 'MonacoEditor',
14-
15-
model: {
16-
event: 'change',
17-
},
18-
1913
props: {
2014
original: {
2115
type: String,
2216
default: null,
2317
},
24-
value: {
18+
modelValue: {
2519
type: String,
2620
required: true,
2721
},
@@ -42,7 +36,7 @@ export default {
4236
default: false,
4337
},
4438
},
45-
39+
emits: ['update:modelValue'],
4640
watch: {
4741
options: {
4842
deep: true,
@@ -54,7 +48,7 @@ export default {
5448
},
5549
},
5650
57-
value (newValue) {
51+
modelValue (newValue) {
5852
if (this.editor) {
5953
const editor = this.getModifiedEditor()
6054
if (newValue !== editor.getValue()) {
@@ -93,7 +87,7 @@ export default {
9387
})
9488
},
9589
96-
beforeDestroy () {
90+
beforeUnmount () {
9791
this.editor && this.editor.dispose()
9892
},
9993
@@ -103,37 +97,38 @@ export default {
10397
10498
const options = assign(
10599
{
106-
value: this.value,
100+
value: this.modelValue,
107101
theme: this.theme,
108102
language: this.language,
109103
},
110104
this.options,
111105
)
106+
const root = this.$refs.root
112107
113108
if (this.diffEditor) {
114-
this.editor = monaco.editor.createDiffEditor(this.$el, options)
109+
this.editor = monaco.editor.createDiffEditor(root, options)
115110
const originalModel = monaco.editor.createModel(
116111
this.original,
117112
this.language,
118113
)
119114
const modifiedModel = monaco.editor.createModel(
120-
this.value,
115+
this.modelValue,
121116
this.language,
122117
)
123118
this.editor.setModel({
124119
original: originalModel,
125120
modified: modifiedModel,
126121
})
127122
} else {
128-
this.editor = monaco.editor.create(this.$el, options)
123+
this.editor = monaco.editor.create(root, options)
129124
}
130125
131126
// @event `change`
132127
const editor = this.getModifiedEditor()
133128
editor.onDidChangeModelContent(event => {
134129
const value = editor.getValue()
135-
if (this.value !== value) {
136-
this.$emit('change', value, event)
130+
if (this.modelValue !== value) {
131+
this.$emit('update:modelValue', value, event)
137132
}
138133
})
139134
@@ -165,5 +160,5 @@ export default {
165160
</script>
166161

167162
<template>
168-
<div />
163+
<div ref="root" />
169164
</template>

0 commit comments

Comments
 (0)