@presto1314w/vite-devtools-browser 0.1.3 → 0.2.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.
@@ -37,7 +37,7 @@ export function path(nodes, id) {
37
37
  }
38
38
  return names.reverse().join(" > ");
39
39
  }
40
- function typeName(type) {
40
+ export function typeName(type) {
41
41
  const names = {
42
42
  11: "Root",
43
43
  12: "Suspense",
@@ -45,118 +45,102 @@ function typeName(type) {
45
45
  };
46
46
  return names[type] ?? `(${type})`;
47
47
  }
48
- async function inPageSnapshot() {
49
- const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
50
- if (!hook)
51
- throw new Error("React DevTools hook not installed");
52
- const ri = hook.rendererInterfaces?.get?.(1);
53
- if (!ri)
54
- throw new Error("no React renderer attached");
55
- const batches = await collect(ri);
56
- return batches.flatMap(decode);
57
- function collect(ri) {
58
- return new Promise((resolve) => {
59
- const out = [];
60
- const listener = (e) => {
61
- const payload = e.data?.payload;
62
- if (e.data?.source === "react-devtools-bridge" && payload?.event === "operations") {
63
- out.push(payload.payload);
64
- }
65
- };
66
- window.addEventListener("message", listener);
67
- ri.flushInitialOperations();
68
- setTimeout(() => {
69
- window.removeEventListener("message", listener);
70
- resolve(out);
71
- }, 80);
72
- });
48
+ export function decodeOperations(ops) {
49
+ let i = 2;
50
+ const strings = [null];
51
+ const tableEnd = ++i + ops[i - 1];
52
+ while (i < tableEnd) {
53
+ const len = ops[i++];
54
+ strings.push(String.fromCodePoint(...ops.slice(i, i + len)));
55
+ i += len;
73
56
  }
74
- function decode(ops) {
75
- let i = 2;
76
- const strings = [null];
77
- const tableEnd = ++i + ops[i - 1];
78
- while (i < tableEnd) {
79
- const len = ops[i++];
80
- strings.push(String.fromCodePoint(...ops.slice(i, i + len)));
81
- i += len;
82
- }
83
- const nodes = [];
84
- while (i < ops.length) {
85
- const op = ops[i];
86
- if (op === 1) {
87
- const id = ops[i + 1];
88
- const type = ops[i + 2];
89
- i += 3;
90
- if (type === 11) {
91
- nodes.push({ id, type, name: null, key: null, parent: 0 });
92
- i += 4;
93
- }
94
- else {
95
- nodes.push({
96
- id,
97
- type,
98
- name: strings[ops[i + 2]] ?? null,
99
- key: strings[ops[i + 3]] ?? null,
100
- parent: ops[i],
101
- });
102
- i += 5;
103
- }
57
+ const nodes = [];
58
+ while (i < ops.length) {
59
+ const op = ops[i];
60
+ if (op === 1) {
61
+ const id = ops[i + 1];
62
+ const type = ops[i + 2];
63
+ i += 3;
64
+ if (type === 11) {
65
+ nodes.push({ id, type, name: null, key: null, parent: 0 });
66
+ i += 4;
104
67
  }
105
68
  else {
106
- i += skip(op, ops, i);
69
+ nodes.push({
70
+ id,
71
+ type,
72
+ name: strings[ops[i + 2]] ?? null,
73
+ key: strings[ops[i + 3]] ?? null,
74
+ parent: ops[i],
75
+ });
76
+ i += 5;
107
77
  }
108
78
  }
109
- return nodes;
79
+ else {
80
+ i += skipOperation(op, ops, i);
81
+ }
110
82
  }
111
- function skip(op, ops, i) {
112
- if (op === 2)
113
- return 2 + ops[i + 1];
114
- if (op === 3)
115
- return 3 + ops[i + 2];
116
- if (op === 4)
117
- return 3;
118
- if (op === 5)
119
- return 4;
120
- if (op === 6)
121
- return 1;
122
- if (op === 7)
123
- return 3;
124
- if (op === 8)
125
- return 6 + rects(ops[i + 5]);
126
- if (op === 9)
127
- return 2 + ops[i + 1];
128
- if (op === 10)
129
- return 3 + ops[i + 2];
130
- if (op === 11)
131
- return 3 + rects(ops[i + 2]);
132
- if (op === 12)
133
- return suspenders(ops, i);
134
- if (op === 13)
135
- return 2;
83
+ return nodes;
84
+ }
85
+ export function skipOperation(op, ops, i) {
86
+ if (op === 2)
87
+ return 2 + ops[i + 1];
88
+ if (op === 3)
89
+ return 3 + ops[i + 2];
90
+ if (op === 4)
91
+ return 3;
92
+ if (op === 5)
93
+ return 4;
94
+ if (op === 6)
136
95
  return 1;
137
- }
138
- function rects(n) {
139
- return n === -1 ? 0 : n * 4;
140
- }
141
- function suspenders(ops, i) {
142
- let j = i + 2;
143
- for (let c = 0; c < ops[i + 1]; c++)
144
- j += 5 + ops[j + 4];
145
- return j - i;
146
- }
96
+ if (op === 7)
97
+ return 3;
98
+ if (op === 8)
99
+ return 6 + rectCount(ops[i + 5]);
100
+ if (op === 9)
101
+ return 2 + ops[i + 1];
102
+ if (op === 10)
103
+ return 3 + ops[i + 2];
104
+ if (op === 11)
105
+ return 3 + rectCount(ops[i + 2]);
106
+ if (op === 12)
107
+ return suspenseSkip(ops, i);
108
+ if (op === 13)
109
+ return 2;
110
+ return 1;
147
111
  }
148
- function inPageInspect(id) {
149
- const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
150
- const ri = hook?.rendererInterfaces?.get?.(1);
151
- if (!ri)
152
- throw new Error("no React renderer attached");
153
- if (!ri.hasElementWithId(id))
154
- throw new Error(`element ${id} not found (page reloaded?)`);
155
- const result = ri.inspectElement(1, id, null, true);
156
- if (result?.type !== "full-data")
157
- throw new Error(`inspect failed: ${result?.type}`);
158
- const value = result.value;
159
- const name = ri.getDisplayNameForElementID(id);
112
+ export function rectCount(n) {
113
+ return n === -1 ? 0 : n * 4;
114
+ }
115
+ export function suspenseSkip(ops, i) {
116
+ let j = i + 2;
117
+ for (let c = 0; c < ops[i + 1]; c++)
118
+ j += 5 + ops[j + 4];
119
+ return j - i;
120
+ }
121
+ export function previewValue(v) {
122
+ if (v == null)
123
+ return String(v);
124
+ if (typeof v !== "object")
125
+ return JSON.stringify(v);
126
+ const d = v;
127
+ if (d.type === "undefined")
128
+ return "undefined";
129
+ if (d.preview_long)
130
+ return d.preview_long;
131
+ if (d.preview_short)
132
+ return d.preview_short;
133
+ if (Array.isArray(v))
134
+ return `[${v.map(previewValue).join(", ")}]`;
135
+ const entries = Object.entries(v).map(([k, val]) => `${k}: ${previewValue(val)}`);
136
+ return `{${entries.join(", ")}}`;
137
+ }
138
+ export function formatHookLine(h) {
139
+ const idx = h.id != null ? `[${h.id}] ` : "";
140
+ const sub = h.subHooks?.length ? ` (${h.subHooks.length} sub)` : "";
141
+ return `${idx}${h.name}: ${previewValue(h.value)}${sub}`;
142
+ }
143
+ export function formatInspectionResult(name, id, value) {
160
144
  const lines = [`${name} #${id}`];
161
145
  if (value.key != null)
162
146
  lines.push(`key: ${JSON.stringify(value.key)}`);
@@ -181,7 +165,7 @@ function inPageInspect(id) {
181
165
  return;
182
166
  lines.push(`${label}:`);
183
167
  for (const item of data)
184
- lines.push(` ${hookLine(item)}`);
168
+ lines.push(` ${formatHookLine(item)}`);
185
169
  return;
186
170
  }
187
171
  if (typeof data === "object") {
@@ -190,29 +174,47 @@ function inPageInspect(id) {
190
174
  return;
191
175
  lines.push(`${label}:`);
192
176
  for (const [k, v] of entries)
193
- lines.push(` ${k}: ${preview(v)}`);
177
+ lines.push(` ${k}: ${previewValue(v)}`);
194
178
  }
195
179
  }
196
- function hookLine(h) {
197
- const idx = h.id != null ? `[${h.id}] ` : "";
198
- const sub = h.subHooks?.length ? ` (${h.subHooks.length} sub)` : "";
199
- return `${idx}${h.name}: ${preview(h.value)}${sub}`;
200
- }
201
- function preview(v) {
202
- if (v == null)
203
- return String(v);
204
- if (typeof v !== "object")
205
- return JSON.stringify(v);
206
- const d = v;
207
- if (d.type === "undefined")
208
- return "undefined";
209
- if (d.preview_long)
210
- return d.preview_long;
211
- if (d.preview_short)
212
- return d.preview_short;
213
- if (Array.isArray(v))
214
- return `[${v.map(preview).join(", ")}]`;
215
- const entries = Object.entries(v).map(([k, val]) => `${k}: ${preview(val)}`);
216
- return `{${entries.join(", ")}}`;
180
+ }
181
+ async function inPageSnapshot() {
182
+ const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
183
+ if (!hook)
184
+ throw new Error("React DevTools hook not installed");
185
+ const ri = hook.rendererInterfaces?.get?.(1);
186
+ if (!ri)
187
+ throw new Error("no React renderer attached");
188
+ const batches = await collect(ri);
189
+ return batches.flatMap(decodeOperations);
190
+ function collect(ri) {
191
+ return new Promise((resolve) => {
192
+ const out = [];
193
+ const listener = (e) => {
194
+ const payload = e.data?.payload;
195
+ if (e.data?.source === "react-devtools-bridge" && payload?.event === "operations") {
196
+ out.push(payload.payload);
197
+ }
198
+ };
199
+ window.addEventListener("message", listener);
200
+ ri.flushInitialOperations();
201
+ setTimeout(() => {
202
+ window.removeEventListener("message", listener);
203
+ resolve(out);
204
+ }, 80);
205
+ });
217
206
  }
218
207
  }
208
+ function inPageInspect(id) {
209
+ const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
210
+ const ri = hook?.rendererInterfaces?.get?.(1);
211
+ if (!ri)
212
+ throw new Error("no React renderer attached");
213
+ if (!ri.hasElementWithId(id))
214
+ throw new Error(`element ${id} not found (page reloaded?)`);
215
+ const result = ri.inspectElement(1, id, null, true);
216
+ if (result?.type !== "full-data")
217
+ throw new Error(`inspect failed: ${result?.type}`);
218
+ const name = ri.getDisplayNameForElementID(id);
219
+ return formatInspectionResult(name, id, result.value);
220
+ }
@@ -1,8 +1,14 @@
1
+ import { SourceMapConsumer } from "source-map-js";
1
2
  type MappedLocation = {
2
3
  file: string;
3
4
  line: number;
4
5
  column: number;
5
6
  snippet?: string;
6
7
  };
7
- export declare function resolveViaSourceMap(origin: string, fileUrl: string, line: number, column: number, includeSnippet?: boolean): Promise<MappedLocation | null>;
8
+ type FetchImpl = typeof fetch;
9
+ export declare function resolveViaSourceMap(origin: string, fileUrl: string, line: number, column: number, includeSnippet?: boolean, fetchImpl?: FetchImpl): Promise<MappedLocation | null>;
10
+ export declare function clearSourceMapCache(): void;
11
+ export declare function buildMapCandidates(origin: string, fileUrl: string): string[];
12
+ export declare function cleanSource(source: string): string;
13
+ export declare function snippetFor(consumer: SourceMapConsumer, source: string, line: number): string | undefined;
8
14
  export {};
package/dist/sourcemap.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { SourceMapConsumer } from "source-map-js";
2
2
  const consumers = new Map();
3
- export async function resolveViaSourceMap(origin, fileUrl, line, column, includeSnippet = false) {
4
- const consumer = await loadConsumer(origin, fileUrl);
3
+ export async function resolveViaSourceMap(origin, fileUrl, line, column, includeSnippet = false, fetchImpl = fetch) {
4
+ const consumer = await loadConsumer(origin, fileUrl, fetchImpl);
5
5
  if (!consumer)
6
6
  return null;
7
7
  const position = consumer.originalPositionFor({
@@ -20,7 +20,10 @@ export async function resolveViaSourceMap(origin, fileUrl, line, column, include
20
20
  }
21
21
  return mapped;
22
22
  }
23
- async function loadConsumer(origin, fileUrl) {
23
+ export function clearSourceMapCache() {
24
+ consumers.clear();
25
+ }
26
+ async function loadConsumer(origin, fileUrl, fetchImpl) {
24
27
  const candidates = buildMapCandidates(origin, fileUrl);
25
28
  for (const url of candidates) {
26
29
  if (consumers.has(url)) {
@@ -29,7 +32,7 @@ async function loadConsumer(origin, fileUrl) {
29
32
  return cached;
30
33
  continue;
31
34
  }
32
- const response = await fetch(url, { signal: AbortSignal.timeout(5000) }).catch(() => null);
35
+ const response = await fetchImpl(url, { signal: AbortSignal.timeout(5000) }).catch(() => null);
33
36
  if (!response?.ok) {
34
37
  consumers.set(url, null);
35
38
  continue;
@@ -45,21 +48,21 @@ async function loadConsumer(origin, fileUrl) {
45
48
  }
46
49
  return null;
47
50
  }
48
- function buildMapCandidates(origin, fileUrl) {
51
+ export function buildMapCandidates(origin, fileUrl) {
49
52
  const url = new URL(fileUrl, origin);
50
53
  const basePath = `${url.pathname}.map`;
51
54
  const withSearch = url.search ? `${basePath}${url.search}` : basePath;
52
55
  // Try preserving query first (Vite often keeps cache-busting query params).
53
56
  return [`${origin}${withSearch}`, `${origin}${basePath}`];
54
57
  }
55
- function cleanSource(source) {
58
+ export function cleanSource(source) {
56
59
  const decoded = decodeURIComponent(source.replace(/^file:\/\//, ""));
57
60
  const nodeModulesIndex = decoded.lastIndexOf("/node_modules/");
58
61
  if (nodeModulesIndex >= 0)
59
62
  return decoded.slice(nodeModulesIndex + 1);
60
63
  return decoded;
61
64
  }
62
- function snippetFor(consumer, source, line) {
65
+ export function snippetFor(consumer, source, line) {
63
66
  const content = consumer.sourceContentFor(source, true);
64
67
  if (!content)
65
68
  return undefined;
@@ -1,3 +1,12 @@
1
1
  import type { Page } from "playwright";
2
+ export type SvelteNode = {
3
+ id: number;
4
+ parent: number;
5
+ name: string;
6
+ props?: unknown;
7
+ file?: string;
8
+ };
9
+ export declare function formatComponentTree(nodes: SvelteNode[]): string;
10
+ export declare function formatComponentDetails(nodes: SvelteNode[], id: string): string;
2
11
  export declare function getComponentTree(page: Page): Promise<string>;
3
12
  export declare function getComponentDetails(page: Page, id: string): Promise<string>;