@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.
@@ -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
+ }