@presto1314w/vite-devtools-browser 0.1.0
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/LICENSE +21 -0
- package/README.md +112 -0
- package/dist/browser.d.ts +22 -0
- package/dist/browser.js +238 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +168 -0
- package/dist/client.d.ts +8 -0
- package/dist/client.js +72 -0
- package/dist/daemon.d.ts +1 -0
- package/dist/daemon.js +132 -0
- package/dist/network.d.ts +5 -0
- package/dist/network.js +97 -0
- package/dist/paths.d.ts +3 -0
- package/dist/paths.js +9 -0
- package/dist/react/devtools.d.ts +16 -0
- package/dist/react/devtools.js +218 -0
- package/dist/svelte/devtools.d.ts +3 -0
- package/dist/svelte/devtools.js +177 -0
- package/dist/vue/devtools.d.ts +29 -0
- package/dist/vue/devtools.js +277 -0
- package/package.json +38 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vue DevTools integration
|
|
3
|
+
*
|
|
4
|
+
* Uses @vue/devtools-kit to access Vue component tree and state
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Get Vue component tree from the page
|
|
8
|
+
*/
|
|
9
|
+
export async function getComponentTree(page) {
|
|
10
|
+
const result = await page.evaluate(() => {
|
|
11
|
+
// Access Vue DevTools global hook
|
|
12
|
+
const hook = window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
|
|
13
|
+
if (!hook)
|
|
14
|
+
return "Vue DevTools not found";
|
|
15
|
+
const apps = hook.apps || [];
|
|
16
|
+
if (apps.length === 0)
|
|
17
|
+
return "No Vue apps found";
|
|
18
|
+
const output = [];
|
|
19
|
+
output.push("# Vue Component Tree");
|
|
20
|
+
output.push(`# ${apps.length} app(s) detected\n`);
|
|
21
|
+
apps.forEach((app, appIndex) => {
|
|
22
|
+
const appName = app._component?.name || app.config?.globalProperties?.$options?.name || `App ${appIndex}`;
|
|
23
|
+
output.push(`## App: ${appName}`);
|
|
24
|
+
// Get root instance
|
|
25
|
+
const rootInstance = app._instance || app._container?._vnode?.component;
|
|
26
|
+
if (!rootInstance) {
|
|
27
|
+
output.push(" (no root instance)");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// Traverse component tree
|
|
31
|
+
const traverse = (instance, depth = 0) => {
|
|
32
|
+
if (!instance)
|
|
33
|
+
return;
|
|
34
|
+
const indent = " ".repeat(depth);
|
|
35
|
+
const name = instance.type?.name || instance.type?.__name || "Anonymous";
|
|
36
|
+
const uid = instance.uid ?? "?";
|
|
37
|
+
output.push(`${indent}[${uid}] ${name}`);
|
|
38
|
+
// Traverse children
|
|
39
|
+
const subTree = instance.subTree;
|
|
40
|
+
if (subTree?.component) {
|
|
41
|
+
traverse(subTree.component, depth + 1);
|
|
42
|
+
}
|
|
43
|
+
if (subTree?.children) {
|
|
44
|
+
subTree.children.forEach((child) => {
|
|
45
|
+
if (child?.component) {
|
|
46
|
+
traverse(child.component, depth + 1);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// Traverse direct children
|
|
51
|
+
if (instance.children) {
|
|
52
|
+
instance.children.forEach((child) => {
|
|
53
|
+
traverse(child, depth + 1);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
traverse(rootInstance, 1);
|
|
58
|
+
output.push("");
|
|
59
|
+
});
|
|
60
|
+
return output.join("\n");
|
|
61
|
+
});
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Get component details by ID
|
|
66
|
+
*/
|
|
67
|
+
export async function getComponentDetails(page, id) {
|
|
68
|
+
const result = await page.evaluate((componentId) => {
|
|
69
|
+
const hook = window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
|
|
70
|
+
if (!hook)
|
|
71
|
+
return "Vue DevTools not found";
|
|
72
|
+
const apps = hook.apps || [];
|
|
73
|
+
let targetInstance = null;
|
|
74
|
+
// Find component by uid
|
|
75
|
+
const findComponent = (instance) => {
|
|
76
|
+
if (!instance)
|
|
77
|
+
return null;
|
|
78
|
+
if (String(instance.uid) === componentId)
|
|
79
|
+
return instance;
|
|
80
|
+
// Search in subTree
|
|
81
|
+
const subTree = instance.subTree;
|
|
82
|
+
if (subTree?.component) {
|
|
83
|
+
const found = findComponent(subTree.component);
|
|
84
|
+
if (found)
|
|
85
|
+
return found;
|
|
86
|
+
}
|
|
87
|
+
if (subTree?.children) {
|
|
88
|
+
for (const child of subTree.children) {
|
|
89
|
+
if (child?.component) {
|
|
90
|
+
const found = findComponent(child.component);
|
|
91
|
+
if (found)
|
|
92
|
+
return found;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Search in children
|
|
97
|
+
if (instance.children) {
|
|
98
|
+
for (const child of instance.children) {
|
|
99
|
+
const found = findComponent(child);
|
|
100
|
+
if (found)
|
|
101
|
+
return found;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
};
|
|
106
|
+
// Search all apps
|
|
107
|
+
for (const app of apps) {
|
|
108
|
+
const rootInstance = app._instance || app._container?._vnode?.component;
|
|
109
|
+
targetInstance = findComponent(rootInstance);
|
|
110
|
+
if (targetInstance)
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
if (!targetInstance)
|
|
114
|
+
return `Component ${componentId} not found`;
|
|
115
|
+
const output = [];
|
|
116
|
+
const name = targetInstance.type?.name || targetInstance.type?.__name || "Anonymous";
|
|
117
|
+
output.push(`# Component: ${name}`);
|
|
118
|
+
output.push(`# UID: ${targetInstance.uid}\n`);
|
|
119
|
+
// Props
|
|
120
|
+
if (targetInstance.props && Object.keys(targetInstance.props).length > 0) {
|
|
121
|
+
output.push("## Props");
|
|
122
|
+
for (const [key, value] of Object.entries(targetInstance.props)) {
|
|
123
|
+
output.push(` ${key}: ${JSON.stringify(value)}`);
|
|
124
|
+
}
|
|
125
|
+
output.push("");
|
|
126
|
+
}
|
|
127
|
+
// Data
|
|
128
|
+
if (targetInstance.data && Object.keys(targetInstance.data).length > 0) {
|
|
129
|
+
output.push("## Data");
|
|
130
|
+
for (const [key, value] of Object.entries(targetInstance.data)) {
|
|
131
|
+
output.push(` ${key}: ${JSON.stringify(value)}`);
|
|
132
|
+
}
|
|
133
|
+
output.push("");
|
|
134
|
+
}
|
|
135
|
+
// Setup state (Composition API)
|
|
136
|
+
const setupState = targetInstance.setupState || targetInstance.devtoolsRawSetupState;
|
|
137
|
+
if (setupState && Object.keys(setupState).length > 0) {
|
|
138
|
+
output.push("## Setup State");
|
|
139
|
+
for (const [key, value] of Object.entries(setupState)) {
|
|
140
|
+
if (typeof value === 'function') {
|
|
141
|
+
output.push(` ${key}: [Function]`);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
output.push(` ${key}: ${JSON.stringify(value)}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
output.push("");
|
|
148
|
+
}
|
|
149
|
+
// Computed
|
|
150
|
+
const computed = targetInstance.type?.computed;
|
|
151
|
+
if (computed && Object.keys(computed).length > 0) {
|
|
152
|
+
output.push("## Computed");
|
|
153
|
+
for (const key of Object.keys(computed)) {
|
|
154
|
+
try {
|
|
155
|
+
const value = targetInstance.proxy?.[key];
|
|
156
|
+
output.push(` ${key}: ${JSON.stringify(value)}`);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
output.push(` ${key}: [Error]`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
output.push("");
|
|
163
|
+
}
|
|
164
|
+
// File location
|
|
165
|
+
const file = targetInstance.type?.__file;
|
|
166
|
+
if (file) {
|
|
167
|
+
output.push(`## Source`);
|
|
168
|
+
output.push(` ${file}`);
|
|
169
|
+
}
|
|
170
|
+
return output.join("\n");
|
|
171
|
+
}, id);
|
|
172
|
+
return result;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Get Pinia stores
|
|
176
|
+
*/
|
|
177
|
+
export async function getPiniaStores(page, storeName) {
|
|
178
|
+
const result = await page.evaluate((name) => {
|
|
179
|
+
// Try to find Pinia instance
|
|
180
|
+
const pinia = window.__PINIA__ || window.pinia;
|
|
181
|
+
if (!pinia)
|
|
182
|
+
return "Pinia not found";
|
|
183
|
+
const stores = pinia._s || pinia.state?.value || {};
|
|
184
|
+
const storeKeys = Object.keys(stores);
|
|
185
|
+
if (storeKeys.length === 0)
|
|
186
|
+
return "No Pinia stores found";
|
|
187
|
+
const output = [];
|
|
188
|
+
if (!name) {
|
|
189
|
+
// List all stores
|
|
190
|
+
output.push("# Pinia Stores\n");
|
|
191
|
+
storeKeys.forEach((key) => {
|
|
192
|
+
output.push(`- ${key}`);
|
|
193
|
+
});
|
|
194
|
+
output.push("\nUse 'vite-browser vue pinia <store-name>' to inspect a specific store");
|
|
195
|
+
return output.join("\n");
|
|
196
|
+
}
|
|
197
|
+
// Get specific store
|
|
198
|
+
const store = stores[name];
|
|
199
|
+
if (!store)
|
|
200
|
+
return `Store '${name}' not found`;
|
|
201
|
+
output.push(`# Pinia Store: ${name}\n`);
|
|
202
|
+
// State
|
|
203
|
+
const state = store.$state || store.state || store;
|
|
204
|
+
if (state && typeof state === 'object') {
|
|
205
|
+
output.push("## State");
|
|
206
|
+
for (const [key, value] of Object.entries(state)) {
|
|
207
|
+
if (key.startsWith('$'))
|
|
208
|
+
continue; // Skip Pinia internals
|
|
209
|
+
output.push(` ${key}: ${JSON.stringify(value)}`);
|
|
210
|
+
}
|
|
211
|
+
output.push("");
|
|
212
|
+
}
|
|
213
|
+
// Getters
|
|
214
|
+
const getters = store._getters || {};
|
|
215
|
+
if (Object.keys(getters).length > 0) {
|
|
216
|
+
output.push("## Getters");
|
|
217
|
+
for (const key of Object.keys(getters)) {
|
|
218
|
+
try {
|
|
219
|
+
const value = store[key];
|
|
220
|
+
output.push(` ${key}: ${JSON.stringify(value)}`);
|
|
221
|
+
}
|
|
222
|
+
catch {
|
|
223
|
+
output.push(` ${key}: [Error]`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
output.push("");
|
|
227
|
+
}
|
|
228
|
+
return output.join("\n");
|
|
229
|
+
}, storeName);
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get Vue Router information
|
|
234
|
+
*/
|
|
235
|
+
export async function getRouterInfo(page) {
|
|
236
|
+
const result = await page.evaluate(() => {
|
|
237
|
+
// Try to find Vue Router instance
|
|
238
|
+
const router = window.$router || window.__VUE_ROUTER__;
|
|
239
|
+
// Also try to get from Vue app
|
|
240
|
+
const hook = window.__VUE_DEVTOOLS_GLOBAL_HOOK__;
|
|
241
|
+
let routerFromApp = null;
|
|
242
|
+
if (hook?.apps?.[0]) {
|
|
243
|
+
const app = hook.apps[0];
|
|
244
|
+
routerFromApp = app.config?.globalProperties?.$router;
|
|
245
|
+
}
|
|
246
|
+
const actualRouter = router || routerFromApp;
|
|
247
|
+
if (!actualRouter)
|
|
248
|
+
return "Vue Router not found";
|
|
249
|
+
const output = [];
|
|
250
|
+
output.push("# Vue Router\n");
|
|
251
|
+
// Current route
|
|
252
|
+
const currentRoute = actualRouter.currentRoute?.value || actualRouter.currentRoute;
|
|
253
|
+
if (currentRoute) {
|
|
254
|
+
output.push("## Current Route");
|
|
255
|
+
output.push(` Path: ${currentRoute.path}`);
|
|
256
|
+
output.push(` Name: ${currentRoute.name || "(unnamed)"}`);
|
|
257
|
+
if (currentRoute.params && Object.keys(currentRoute.params).length > 0) {
|
|
258
|
+
output.push(` Params: ${JSON.stringify(currentRoute.params)}`);
|
|
259
|
+
}
|
|
260
|
+
if (currentRoute.query && Object.keys(currentRoute.query).length > 0) {
|
|
261
|
+
output.push(` Query: ${JSON.stringify(currentRoute.query)}`);
|
|
262
|
+
}
|
|
263
|
+
output.push("");
|
|
264
|
+
}
|
|
265
|
+
// All routes
|
|
266
|
+
const routes = actualRouter.getRoutes?.() || actualRouter.options?.routes || [];
|
|
267
|
+
if (routes.length > 0) {
|
|
268
|
+
output.push("## All Routes");
|
|
269
|
+
routes.forEach((route) => {
|
|
270
|
+
const name = route.name ? ` (${route.name})` : "";
|
|
271
|
+
output.push(` ${route.path}${name}`);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return output.join("\n");
|
|
275
|
+
});
|
|
276
|
+
return result;
|
|
277
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@presto1314w/vite-devtools-browser",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI for programmatic access to Vue/React DevTools in Vite applications",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=20"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"bin": {
|
|
14
|
+
"vite-browser": "./dist/cli.js"
|
|
15
|
+
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"start": "node --loader tsx src/cli.ts",
|
|
18
|
+
"dev": "tsx src/cli.ts",
|
|
19
|
+
"typecheck": "tsc --noEmit",
|
|
20
|
+
"build": "tsc -p tsconfig.build.json",
|
|
21
|
+
"prepack": "pnpm build",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest"
|
|
24
|
+
},
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"playwright": "^1.50.0",
|
|
27
|
+
"source-map-js": "^1.2.1"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^22.0.0",
|
|
31
|
+
"@vue/devtools-kit": "^7.3.2",
|
|
32
|
+
"@vue/devtools-api": "^7.3.2",
|
|
33
|
+
"tsx": "^4.20.6",
|
|
34
|
+
"typescript": "^5.7.0",
|
|
35
|
+
"vitest": "^4.0.16"
|
|
36
|
+
},
|
|
37
|
+
"packageManager": "pnpm@10.29.2"
|
|
38
|
+
}
|