@presto1314w/vite-devtools-browser 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.js +35 -14
- package/dist/vue/devtools.js +61 -11
- package/package.json +2 -2
package/dist/browser.js
CHANGED
|
@@ -15,6 +15,7 @@ const installHook = hasReactExtension
|
|
|
15
15
|
let context = null;
|
|
16
16
|
let page = null;
|
|
17
17
|
let framework = "unknown";
|
|
18
|
+
let extensionModeDisabled = false;
|
|
18
19
|
const consoleLogs = [];
|
|
19
20
|
const MAX_LOGS = 200;
|
|
20
21
|
let lastReactSnapshot = [];
|
|
@@ -48,7 +49,17 @@ async function ensurePage() {
|
|
|
48
49
|
if (!context)
|
|
49
50
|
throw new Error("browser not open");
|
|
50
51
|
if (!page || page.isClosed()) {
|
|
51
|
-
|
|
52
|
+
try {
|
|
53
|
+
page = context.pages()[0] ?? (await context.newPage());
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
if (!isClosedTargetError(error))
|
|
57
|
+
throw error;
|
|
58
|
+
await close();
|
|
59
|
+
extensionModeDisabled = true;
|
|
60
|
+
context = await launch();
|
|
61
|
+
page = context.pages()[0] ?? (await context.newPage());
|
|
62
|
+
}
|
|
52
63
|
attachListeners(page);
|
|
53
64
|
networkLog.attach(page);
|
|
54
65
|
}
|
|
@@ -65,20 +76,30 @@ function contextUsable(current) {
|
|
|
65
76
|
return false;
|
|
66
77
|
}
|
|
67
78
|
}
|
|
79
|
+
function isClosedTargetError(error) {
|
|
80
|
+
if (!(error instanceof Error))
|
|
81
|
+
return false;
|
|
82
|
+
return /Target page, context or browser has been closed/i.test(error.message);
|
|
83
|
+
}
|
|
68
84
|
async function launch() {
|
|
69
|
-
if (hasReactExtension && installHook) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
if (hasReactExtension && installHook && !extensionModeDisabled) {
|
|
86
|
+
try {
|
|
87
|
+
const ctx = await chromium.launchPersistentContext("", {
|
|
88
|
+
headless: false,
|
|
89
|
+
viewport: { width: 1280, height: 720 },
|
|
90
|
+
args: [
|
|
91
|
+
`--disable-extensions-except=${extensionPath}`,
|
|
92
|
+
`--load-extension=${extensionPath}`,
|
|
93
|
+
"--auto-open-devtools-for-tabs",
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
await ctx.waitForEvent("serviceworker").catch(() => { });
|
|
97
|
+
await ctx.addInitScript(installHook);
|
|
98
|
+
return ctx;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
extensionModeDisabled = true;
|
|
102
|
+
}
|
|
82
103
|
}
|
|
83
104
|
const browser = await chromium.launch({
|
|
84
105
|
headless: false,
|
package/dist/vue/devtools.js
CHANGED
|
@@ -176,12 +176,51 @@ export async function getComponentDetails(page, id) {
|
|
|
176
176
|
*/
|
|
177
177
|
export async function getPiniaStores(page, storeName) {
|
|
178
178
|
const result = await page.evaluate((name) => {
|
|
179
|
+
const safeJson = (value) => {
|
|
180
|
+
if (typeof value === "function")
|
|
181
|
+
return "[Function]";
|
|
182
|
+
if (typeof value === "bigint")
|
|
183
|
+
return value.toString();
|
|
184
|
+
const seen = new WeakSet();
|
|
185
|
+
try {
|
|
186
|
+
return JSON.stringify(value, (_, v) => {
|
|
187
|
+
if (typeof v === "function")
|
|
188
|
+
return "[Function]";
|
|
189
|
+
if (typeof v === "bigint")
|
|
190
|
+
return v.toString();
|
|
191
|
+
if (v && typeof v === "object") {
|
|
192
|
+
if (seen.has(v))
|
|
193
|
+
return "[Circular]";
|
|
194
|
+
seen.add(v);
|
|
195
|
+
}
|
|
196
|
+
return v;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return String(value);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
const hook = window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
|
|
204
|
+
const piniaFromApp = hook?.apps?.[0]?.config?.globalProperties?.$pinia;
|
|
179
205
|
// Try to find Pinia instance
|
|
180
|
-
const pinia = window.__PINIA__ || window.pinia;
|
|
206
|
+
const pinia = window.__PINIA__ || window.pinia || piniaFromApp;
|
|
181
207
|
if (!pinia)
|
|
182
208
|
return "Pinia not found";
|
|
183
|
-
|
|
184
|
-
const
|
|
209
|
+
// Pinia v3 uses Map for _s, older integrations can expose plain objects.
|
|
210
|
+
const storesById = {};
|
|
211
|
+
const registry = pinia._s;
|
|
212
|
+
if (registry instanceof Map) {
|
|
213
|
+
registry.forEach((store, id) => {
|
|
214
|
+
storesById[String(id)] = store;
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
else if (registry && typeof registry === "object") {
|
|
218
|
+
for (const [id, store] of Object.entries(registry)) {
|
|
219
|
+
storesById[id] = store;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const stateById = pinia.state?.value && typeof pinia.state.value === "object" ? pinia.state.value : {};
|
|
223
|
+
const storeKeys = Array.from(new Set([...Object.keys(storesById), ...Object.keys(stateById)]));
|
|
185
224
|
if (storeKeys.length === 0)
|
|
186
225
|
return "No Pinia stores found";
|
|
187
226
|
const output = [];
|
|
@@ -195,29 +234,40 @@ export async function getPiniaStores(page, storeName) {
|
|
|
195
234
|
return output.join("\n");
|
|
196
235
|
}
|
|
197
236
|
// Get specific store
|
|
198
|
-
const store =
|
|
199
|
-
|
|
237
|
+
const store = storesById[name] ?? null;
|
|
238
|
+
const stateOnly = stateById[name];
|
|
239
|
+
if (!store && !stateOnly)
|
|
200
240
|
return `Store '${name}' not found`;
|
|
201
241
|
output.push(`# Pinia Store: ${name}\n`);
|
|
202
242
|
// State
|
|
203
|
-
const state = store
|
|
243
|
+
const state = store?.$state || store?.state || stateOnly || store;
|
|
204
244
|
if (state && typeof state === 'object') {
|
|
205
245
|
output.push("## State");
|
|
206
246
|
for (const [key, value] of Object.entries(state)) {
|
|
207
247
|
if (key.startsWith('$'))
|
|
208
248
|
continue; // Skip Pinia internals
|
|
209
|
-
output.push(` ${key}: ${
|
|
249
|
+
output.push(` ${key}: ${safeJson(value)}`);
|
|
210
250
|
}
|
|
211
251
|
output.push("");
|
|
212
252
|
}
|
|
213
253
|
// Getters
|
|
214
|
-
const
|
|
215
|
-
|
|
254
|
+
const getterNames = [];
|
|
255
|
+
const rawGetters = store?._getters;
|
|
256
|
+
if (Array.isArray(rawGetters)) {
|
|
257
|
+
getterNames.push(...rawGetters.map((g) => String(g)));
|
|
258
|
+
}
|
|
259
|
+
else if (rawGetters instanceof Set) {
|
|
260
|
+
getterNames.push(...Array.from(rawGetters).map((g) => String(g)));
|
|
261
|
+
}
|
|
262
|
+
else if (rawGetters && typeof rawGetters === "object") {
|
|
263
|
+
getterNames.push(...Object.keys(rawGetters));
|
|
264
|
+
}
|
|
265
|
+
if (getterNames.length > 0) {
|
|
216
266
|
output.push("## Getters");
|
|
217
|
-
for (const key of
|
|
267
|
+
for (const key of getterNames) {
|
|
218
268
|
try {
|
|
219
269
|
const value = store[key];
|
|
220
|
-
output.push(` ${key}: ${
|
|
270
|
+
output.push(` ${key}: ${safeJson(value)}`);
|
|
221
271
|
}
|
|
222
272
|
catch {
|
|
223
273
|
output.push(` ${key}: [Error]`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@presto1314w/vite-devtools-browser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "CLI for programmatic access to Vue/React DevTools in Vite applications",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"bin": {
|
|
22
|
-
"vite-browser": "
|
|
22
|
+
"vite-browser": "dist/cli.js"
|
|
23
23
|
},
|
|
24
24
|
"scripts": {
|
|
25
25
|
"start": "node --import tsx src/cli.ts",
|