forked from hzmming/vue-force-dev
-
Notifications
You must be signed in to change notification settings - Fork 0
/
detector.js
148 lines (127 loc) · 3.98 KB
/
detector.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
(() => {
const isBrowser = typeof navigator !== "undefined";
const isFirefox = isBrowser && navigator.userAgent.indexOf("Firefox") > -1;
// Light up my extension icon
window.addEventListener("message", (e) => {
if (e.source === window && e.data.vueDetected) {
chrome.runtime.sendMessage(e.data);
}
});
// Receive the message of vue devtools, crack and replay it.
function listenVueDevtoolsMessage() {
const LOG_MARK = [
"%c vue-force-dev ",
`padding: 1px; border-radius: 0 3px 3px 0; color: #fff; background: #42b883`,
];
const messageHander = (e) => {
try {
if (!window.__VUE_DEVTOOLS_GLOBAL_HOOK__) return;
if (e.source === window && e.data.vueDetected) {
const data = e.data;
// skip
if (data.devtoolsEnabled) {
window.removeEventListener("message", messageHander);
return;
}
// force devtools enabled
const result = crack(data);
if (!result) return;
// replay
window.postMessage(data, "*");
}
} catch (e) {
console.error(
...LOG_MARK,
e,
"\n\nreport issues: https://github.com/hzmming/vue-force-dev/issues"
);
window.removeEventListener("message", messageHander);
}
};
window.addEventListener("message", messageHander);
function crack(data) {
let result;
// Nuxt.js
if (data.nuxtDetected) {
let Vue;
if (window.$nuxt) {
Vue = window.$nuxt.$root && window.$nuxt.$root.constructor;
}
// Vue 2
if (Vue) {
result = crackVue2(Vue);
} else {
// Vue 3.2.14+
result = crackVue3();
}
}
// Vue 3
else if (window.__VUE__) {
result = crackVue3();
}
// Vue 2
else {
result = crackVue2();
}
if (result) data.devtoolsEnabled = true;
return result;
}
function crackVue2(Vue) {
if (!Vue) {
const app = getVueRootInstance(2);
if (!app) return false; // Vue may not be finished yet
Vue = Object.getPrototypeOf(app).constructor;
while (Vue.super) {
Vue = Vue.super;
}
}
const devtools = window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
Vue.config.devtools = true;
devtools.emit("init", Vue);
return true;
}
function crackVue3() {
const app = getVueRootInstance(3);
if (!app) return false; // Vue may not be finished yet
const devtools = window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
devtools.enabled = true;
const version = app.version;
devtools.emit("app:init" /* APP_INIT */, app, version, {
// TODO I can't get the right value.
// Now only one use has found. (packages/app-backend-vue3/src/components/util.ts - isFragment)
// Fragment,
// Text,
// Comment,
// Static,
});
// TODO How to trigger the devtools refresh when vue instance changed.
// Maybe `devtools.emit("flush")` can be used, but i don't know when, where and how to use it.
return true;
}
function getVueRootInstance(version) {
const signProperty = version === 2 ? "__vue__" : "__vue_app__";
const all = document.querySelectorAll("*");
for (let i = 0; i < all.length; i++) {
if (all[i][signProperty]) {
return all[i][signProperty];
}
}
}
}
// inject the hook
if (document instanceof Document) {
installScript(listenVueDevtoolsMessage);
}
function installScript(fn) {
const source = ";(" + fn.toString() + ")(window)";
if (isFirefox) {
// eslint-disable-next-line no-eval
window.eval(source); // in Firefox, this evaluates on the content window
} else {
const script = document.createElement("script");
script.textContent = source;
document.documentElement.appendChild(script);
script.parentNode.removeChild(script);
}
}
})();