Skip to content

Commit 6413564

Browse files
committed
refactor: new dev shell with URL browser
1 parent 2e3107b commit 6413564

File tree

6 files changed

+191
-51
lines changed

6 files changed

+191
-51
lines changed

.eslintrc.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,5 +88,16 @@ module.exports = {
8888
'no-console': 'off',
8989
},
9090
},
91+
{
92+
files: [
93+
'packages/shell-host/**',
94+
],
95+
globals: {
96+
localStorage: false,
97+
},
98+
rules: {
99+
'no-console': 'off',
100+
},
101+
},
91102
],
92103
}

packages/shell-dev-vue2/public/target.html

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
<div id="app"></div>
99
<div id="shadow"></div>
1010
<iframe src="/target-iframe.html" width="100%"></iframe>
11-
<script src="target/hook.js"></script>
1211
<script src="target/target.js"></script>
1312
</body>
1413
</html>

packages/shell-host/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@
66
"dev": "cross-env TAILWIND_MODE=watch webpack serve"
77
},
88
"dependencies": {
9+
"@vue/composition-api": "^1.1.3",
10+
"@vue/ui": "^0.12.5",
911
"@vue-devtools/app-backend-core": "^0.0.0",
1012
"@vue-devtools/app-frontend": "^0.0.0",
11-
"@vue-devtools/shared-utils": "^0.0.0"
13+
"@vue-devtools/shared-utils": "^0.0.0",
14+
"vue": "^2.6.11"
1215
},
1316
"devDependencies": {
1417
"cross-env": "^5.2.0",

packages/shell-host/public/index.html

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66
<meta name="viewport" content="width=device-width, initial-scale=1">
77
<link rel="shortcut icon" type="image/svg" href="/favicon.svg"/>
88
<style>
9-
#target {
10-
flex: 1;
11-
border: none;
12-
}
139
#container {
1410
display: flex;
1511
height: 400px;
@@ -27,11 +23,14 @@
2723
margin: 0;
2824
padding: 0;
2925
}
26+
.not-vue {
27+
display: none;
28+
}
3029
</style>
3130
</head>
3231
<body>
33-
<iframe id="target" name="target" src="about:blank"></iframe>
34-
<div>Not Vue</div>
32+
<div id="iframe-target"></div>
33+
<div class="not-vue">Not Vue</div>
3534
<div id="container">
3635
<div id="app"></div>
3736
</div>

packages/shell-host/src/DevIframe.vue

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
<script lang="ts">
2+
import { defineComponent, ref, watch } from '@vue/composition-api'
3+
import { Bridge } from '@vue-devtools/shared-utils'
4+
import { initDevTools } from '@front'
5+
import { installHook } from '@back/hook'
6+
7+
const STORAGE_URL = 'vue-devtools.dev-iframe.url'
8+
9+
export default defineComponent({
10+
setup () {
11+
const src = ref(localStorage.getItem(STORAGE_URL) ?? 'target.html')
12+
const url = ref(src.value)
13+
const iframe = ref(null)
14+
const loading = ref(true)
15+
const error = ref(false)
16+
17+
watch(src, (value) => {
18+
localStorage.setItem(STORAGE_URL, value)
19+
})
20+
21+
function inject (src, done) {
22+
if (!src || src === 'false') {
23+
return done()
24+
}
25+
const script = iframe.value.contentDocument.createElement('script')
26+
script.src = src.replace(/\$ORIGIN/g, location.origin)
27+
script.onload = done
28+
iframe.value.contentDocument.body.appendChild(script)
29+
}
30+
31+
function onLoad () {
32+
loading.value = false
33+
try {
34+
console.log('%cInstalling hook...', 'color:#42B983;')
35+
installHook(iframe.value.contentWindow)
36+
37+
let loadListener
38+
39+
// 2. init devtools
40+
console.log('%cInit devtools...', 'color:#42B983;')
41+
initDevTools({
42+
connect (cb) {
43+
// 3. called by devtools: inject backend
44+
console.log('%cInjecting backend...', 'color:#42B983;')
45+
inject('$ORIGIN/target/backend.js', () => {
46+
// 4. send back bridge
47+
console.log('%cInit bridge...', 'color:#42B983;')
48+
cb(new Bridge({
49+
listen (fn) {
50+
iframe.value.contentWindow.parent.addEventListener('message', evt => fn(evt.data))
51+
},
52+
send (data) {
53+
if (process.env.NODE_ENV !== 'production') {
54+
console.log('devtools -> backend', data)
55+
}
56+
iframe.value.contentWindow.postMessage(data, '*')
57+
},
58+
}))
59+
})
60+
},
61+
onReload (reloadFn) {
62+
loadListener = reloadFn
63+
iframe.value.addEventListener('load', reloadFn)
64+
},
65+
})
66+
67+
iframe.value.contentWindow.addEventListener('unload', () => {
68+
if (loadListener) iframe.value.removeEventListener('load', loadListener)
69+
loading.value = true
70+
})
71+
} catch (e) {
72+
console.error(e)
73+
error.value = true
74+
}
75+
}
76+
77+
function openUrl () {
78+
let value = url.value
79+
if (value === src.value) {
80+
reload()
81+
} else {
82+
if (!value.startsWith('http')) {
83+
value = `http://${value}`
84+
}
85+
src.value = value
86+
url.value = value
87+
}
88+
}
89+
90+
function reload () {
91+
iframe.value.contentWindow.location.reload()
92+
}
93+
94+
function reset () {
95+
src.value = 'target.html'
96+
}
97+
98+
return {
99+
src,
100+
url,
101+
loading,
102+
openUrl,
103+
iframe,
104+
onLoad,
105+
reset,
106+
error,
107+
}
108+
},
109+
})
110+
</script>
111+
112+
<template>
113+
<div class="dev-iframe flex-1 flex flex-col">
114+
<iframe
115+
id="target"
116+
ref="iframe"
117+
name="target"
118+
class="flex-1 border-none"
119+
:src="src"
120+
@load="onLoad"
121+
/>
122+
<div class="border-t border-gray-300 flex relative">
123+
<VueLoadingBar
124+
v-if="loading"
125+
class="primary ghost absolute left-0 top-0 w-full"
126+
unknown
127+
/>
128+
129+
<VueInput
130+
v-model="url"
131+
name="url"
132+
placeholder="Enter target URL..."
133+
class="min-w-0 flex-1 flat"
134+
@keyup.enter="openUrl()"
135+
/>
136+
137+
<VueButton
138+
v-tooltip="'Refresh page'"
139+
icon-left="refresh"
140+
class="icon-button flat"
141+
@click="openUrl()"
142+
/>
143+
144+
<VueButton
145+
v-tooltip="'Reset URL'"
146+
icon-left="backspace"
147+
:disabled="src === 'target.html'"
148+
class="icon-button flat"
149+
@click="reset()"
150+
/>
151+
</div>
152+
153+
<VueModal
154+
v-if="error"
155+
title="Error"
156+
@close="error = false"
157+
>
158+
<div class="p-8">
159+
Please <a
160+
href="https://stackoverflow.com/questions/3102819/disable-same-origin-policy-in-chrome"
161+
target="_blank"
162+
class="text-green-500"
163+
>disable Chrome web security</a> to allow manipulating an iframe on a different origin.
164+
</div>
165+
</VueModal>
166+
</div>
167+
</template>

packages/shell-host/src/devtools.js

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,5 @@
1-
import { initDevTools } from '@front'
2-
import { Bridge } from '@vue-devtools/shared-utils'
1+
import Vue from 'vue'
2+
import DevIframe from './DevIframe.vue'
33

4-
const target = document.getElementById('target')
5-
const targetWindow = target.contentWindow
6-
7-
// 1. load user app
8-
target.src = 'target.html'
9-
target.onload = () => {
10-
// 2. init devtools
11-
initDevTools({
12-
connect (cb) {
13-
// 3. called by devtools: inject backend
14-
inject('./target/backend.js', () => {
15-
// 4. send back bridge
16-
cb(new Bridge({
17-
listen (fn) {
18-
targetWindow.parent.addEventListener('message', evt => fn(evt.data))
19-
},
20-
send (data) {
21-
if (process.env.NODE_ENV !== 'production') {
22-
// eslint-disable-next-line no-console
23-
console.log('devtools -> backend', data)
24-
}
25-
targetWindow.postMessage(data, '*')
26-
},
27-
}))
28-
})
29-
},
30-
onReload (reloadFn) {
31-
target.onload = reloadFn
32-
},
33-
})
34-
}
35-
36-
function inject (src, done) {
37-
if (!src || src === 'false') {
38-
return done()
39-
}
40-
const script = target.contentDocument.createElement('script')
41-
script.src = src
42-
script.onload = done
43-
target.contentDocument.body.appendChild(script)
44-
}
4+
const app = new Vue(DevIframe)
5+
app.$mount('#iframe-target')

0 commit comments

Comments
 (0)