react-native-inapp-inspector 1.1.24 → 1.1.25
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/commonjs/components/AnalyticsEventCard.js +94 -88
- package/dist/commonjs/components/ConsoleLogCard.js +49 -25
- package/dist/commonjs/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
- package/dist/commonjs/components/Inspector/AnalyticsFilterModal.js +1002 -0
- package/dist/commonjs/components/Inspector/AnalyticsTab.js +637 -10
- package/dist/commonjs/components/Inspector/BundleTab.d.ts +1 -1
- package/dist/commonjs/components/Inspector/BundleTab.js +826 -406
- package/dist/commonjs/components/Inspector/InspectorContext.js +6 -1
- package/dist/commonjs/components/Inspector/InspectorHeader.js +13 -5
- package/dist/commonjs/components/Inspector/LogDetail.js +223 -111
- package/dist/commonjs/components/Inspector/MainScreen.js +1 -1
- package/dist/commonjs/components/Inspector/NetworkDetail.js +131 -81
- package/dist/commonjs/components/Inspector/PerformanceTab.js +20 -10
- package/dist/commonjs/components/NetworkIcons.d.ts +14 -0
- package/dist/commonjs/components/NetworkIcons.js +93 -1
- package/dist/commonjs/components/SegmentedTabs.js +31 -22
- package/dist/commonjs/components/TreeNode.js +13 -12
- package/dist/commonjs/constants/version.d.ts +1 -1
- package/dist/commonjs/constants/version.js +1 -1
- package/dist/commonjs/customHooks/bundleAnalyzer.d.ts +5 -1
- package/dist/commonjs/customHooks/bundleAnalyzer.js +497 -126
- package/dist/commonjs/customHooks/consoleLogger.js +83 -45
- package/dist/commonjs/customHooks/networkLogger.js +10 -1
- package/dist/commonjs/customHooks/performanceTracker.d.ts +8 -0
- package/dist/commonjs/customHooks/performanceTracker.js +44 -3
- package/dist/commonjs/decorators/index.d.ts +49 -0
- package/dist/commonjs/decorators/index.js +142 -0
- package/dist/commonjs/helpers/gaAnalyticsRegistry.d.ts +18 -0
- package/dist/commonjs/helpers/gaAnalyticsRegistry.js +174 -0
- package/dist/commonjs/helpers/index.d.ts +7 -4
- package/dist/commonjs/helpers/index.js +102 -64
- package/dist/commonjs/i18n/locales/en.json +51 -4
- package/dist/commonjs/index.d.ts +4 -0
- package/dist/commonjs/index.js +216 -8
- package/dist/commonjs/styles/index.d.ts +4 -1
- package/dist/commonjs/styles/index.js +22 -19
- package/dist/commonjs/types/enums.d.ts +115 -9
- package/dist/commonjs/types/enums.js +103 -2
- package/dist/commonjs/types/interfaces.d.ts +19 -0
- package/dist/esm/components/AnalyticsEventCard.js +95 -89
- package/dist/esm/components/ConsoleLogCard.js +49 -25
- package/dist/esm/components/Inspector/AnalyticsFilterModal.d.ts +7 -0
- package/dist/esm/components/Inspector/AnalyticsFilterModal.js +964 -0
- package/dist/esm/components/Inspector/AnalyticsTab.js +639 -12
- package/dist/esm/components/Inspector/BundleTab.d.ts +1 -1
- package/dist/esm/components/Inspector/BundleTab.js +827 -407
- package/dist/esm/components/Inspector/InspectorContext.js +1 -0
- package/dist/esm/components/Inspector/InspectorHeader.js +14 -6
- package/dist/esm/components/Inspector/LogDetail.js +223 -111
- package/dist/esm/components/Inspector/MainScreen.js +2 -2
- package/dist/esm/components/Inspector/NetworkDetail.js +132 -49
- package/dist/esm/components/Inspector/PerformanceTab.js +20 -10
- package/dist/esm/components/NetworkIcons.d.ts +14 -0
- package/dist/esm/components/NetworkIcons.js +78 -0
- package/dist/esm/components/SegmentedTabs.js +31 -22
- package/dist/esm/components/TreeNode.js +13 -12
- package/dist/esm/constants/version.d.ts +1 -1
- package/dist/esm/constants/version.js +1 -1
- package/dist/esm/customHooks/bundleAnalyzer.d.ts +5 -1
- package/dist/esm/customHooks/bundleAnalyzer.js +493 -125
- package/dist/esm/customHooks/consoleLogger.js +83 -45
- package/dist/esm/customHooks/networkLogger.js +10 -1
- package/dist/esm/customHooks/performanceTracker.d.ts +8 -0
- package/dist/esm/customHooks/performanceTracker.js +38 -1
- package/dist/esm/decorators/index.d.ts +49 -0
- package/dist/esm/decorators/index.js +137 -0
- package/dist/esm/helpers/gaAnalyticsRegistry.d.ts +18 -0
- package/dist/esm/helpers/gaAnalyticsRegistry.js +169 -0
- package/dist/esm/helpers/index.d.ts +7 -4
- package/dist/esm/helpers/index.js +100 -63
- package/dist/esm/i18n/locales/en.json +51 -4
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +190 -8
- package/dist/esm/styles/index.d.ts +4 -1
- package/dist/esm/styles/index.js +22 -19
- package/dist/esm/types/enums.d.ts +115 -9
- package/dist/esm/types/enums.js +102 -3
- package/dist/esm/types/interfaces.d.ts +19 -0
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getCachedBundleAnalysis = exports.analyzeHostAppBundle = exports.parseBundleSource = exports.getHostScriptURL = void 0;
|
|
3
|
+
exports.getCachedBundleAnalysis = exports.clearCachedBundleAnalysis = exports.analyzeHostAppBundle = exports.parseBundleSource = exports.trackRuntimeDefine = exports.trackRuntimeModuleExecution = exports.getHostScriptURL = void 0;
|
|
4
4
|
const AppColors_1 = require("../styles/AppColors");
|
|
5
5
|
// ─── Real Host App Bundle Analyzer ──────────────────────────────────────────
|
|
6
6
|
//
|
|
@@ -36,62 +36,356 @@ const getSourceCodeModule = () => {
|
|
|
36
36
|
};
|
|
37
37
|
const getHostScriptURL = () => {
|
|
38
38
|
// 1. NativeModules.SourceCode (bridged) or SourceCode TurboModule (bridgeless / new arch)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
try {
|
|
40
|
+
const scriptURL = getSourceCodeModule()?.scriptURL;
|
|
41
|
+
if (typeof scriptURL === 'string' && scriptURL.length > 0) {
|
|
42
|
+
return scriptURL;
|
|
43
|
+
}
|
|
42
44
|
}
|
|
43
|
-
|
|
45
|
+
catch { }
|
|
46
|
+
// 2. Android legacy & new arch: PlatformConstants.serverHost, DevSettings, or AndroidConstants
|
|
44
47
|
try {
|
|
45
|
-
const serverHost = react_native_1.NativeModules?.PlatformConstants?.serverHost
|
|
48
|
+
const serverHost = react_native_1.NativeModules?.PlatformConstants?.serverHost ||
|
|
49
|
+
react_native_1.NativeModules?.AndroidConstants?.serverHost ||
|
|
50
|
+
react_native_1.NativeModules?.DevSettings?.serverHost ||
|
|
51
|
+
react_native_1.NativeModules?.DevSettings?.getConstants?.()?.serverHost;
|
|
46
52
|
if (typeof serverHost === 'string' && serverHost.length > 0) {
|
|
47
|
-
|
|
53
|
+
const formattedHost = serverHost.startsWith('http') ? serverHost : `http://${serverHost}`;
|
|
54
|
+
return `${formattedHost}/index.bundle?platform=${react_native_1.Platform.OS}&dev=true`;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch { }
|
|
58
|
+
// 3. Fallback to global dev config if defined
|
|
59
|
+
try {
|
|
60
|
+
const globalHost = globalThis?.__DEV_SERVER_URL__ ||
|
|
61
|
+
globalThis?.__METRO_SERVER_HOST__;
|
|
62
|
+
if (typeof globalHost === 'string' && globalHost.length > 0) {
|
|
63
|
+
return globalHost.startsWith('http')
|
|
64
|
+
? globalHost
|
|
65
|
+
: `http://${globalHost}/index.bundle?platform=${react_native_1.Platform.OS}&dev=true`;
|
|
48
66
|
}
|
|
49
67
|
}
|
|
50
68
|
catch { }
|
|
51
69
|
return '';
|
|
52
70
|
};
|
|
53
71
|
exports.getHostScriptURL = getHostScriptURL;
|
|
54
|
-
|
|
72
|
+
// Expanded list of common Metro & dev server ports
|
|
73
|
+
const DEV_SERVER_PORTS = [8081, 8082, 8083, 8084, 8085, 8086, 8087, 8088, 8089, 8090, 8080, 19000, 19001, 3000];
|
|
55
74
|
const buildProbeUrls = (scriptURL) => {
|
|
56
75
|
const urls = [];
|
|
76
|
+
let extractedHost = null;
|
|
77
|
+
let extractedPort = null;
|
|
57
78
|
if (typeof scriptURL === 'string' && scriptURL.startsWith('http')) {
|
|
58
79
|
urls.push(scriptURL);
|
|
80
|
+
const urlMatch = scriptURL.match(/^https?:\/\/([^:/]+)(?::(\d+))?/);
|
|
81
|
+
if (urlMatch) {
|
|
82
|
+
extractedHost = urlMatch[1];
|
|
83
|
+
if (urlMatch[2]) {
|
|
84
|
+
extractedPort = parseInt(urlMatch[2], 10);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
59
87
|
}
|
|
60
|
-
|
|
61
|
-
const ports =
|
|
88
|
+
// Prioritize dynamically extracted port first
|
|
89
|
+
const ports = extractedPort
|
|
90
|
+
? Array.from(new Set([extractedPort, ...DEV_SERVER_PORTS]))
|
|
91
|
+
: DEV_SERVER_PORTS;
|
|
62
92
|
const hosts = react_native_1.Platform.OS === 'android'
|
|
63
|
-
? ['localhost', '127.0.0.1', '10.0.2.2']
|
|
93
|
+
? ['localhost', '127.0.0.1', '10.0.2.2', '10.0.3.2']
|
|
64
94
|
: ['localhost', '127.0.0.1'];
|
|
65
|
-
|
|
66
|
-
|
|
95
|
+
if (extractedHost && !hosts.includes(extractedHost)) {
|
|
96
|
+
hosts.unshift(extractedHost);
|
|
97
|
+
}
|
|
98
|
+
for (const port of ports) {
|
|
99
|
+
for (const host of hosts) {
|
|
67
100
|
urls.push(`http://${host}:${port}/index.bundle?platform=${react_native_1.Platform.OS}&dev=true`);
|
|
68
101
|
}
|
|
69
102
|
}
|
|
70
103
|
return Array.from(new Set(urls));
|
|
71
104
|
};
|
|
72
|
-
const
|
|
105
|
+
const promiseAny = (promises) => {
|
|
106
|
+
return new Promise((resolve, reject) => {
|
|
107
|
+
let pendingCount = promises.length;
|
|
108
|
+
if (pendingCount === 0) {
|
|
109
|
+
reject(new Error('All promises rejected'));
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
promises.forEach(p => {
|
|
113
|
+
Promise.resolve(p)
|
|
114
|
+
.then(resolve)
|
|
115
|
+
.catch(() => {
|
|
116
|
+
pendingCount--;
|
|
117
|
+
if (pendingCount === 0) {
|
|
118
|
+
reject(new Error('All promises rejected'));
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
};
|
|
124
|
+
const probeCandidateUrlsInParallel = async (urls, timeoutMs = 4000) => {
|
|
125
|
+
if (urls.length === 0)
|
|
126
|
+
return null;
|
|
127
|
+
const controller = new AbortController();
|
|
128
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
129
|
+
const fetchSingle = async (url) => {
|
|
130
|
+
const res = await fetch(url, { signal: controller.signal });
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
throw new Error(`HTTP ${res.status}`);
|
|
133
|
+
}
|
|
134
|
+
const contentLength = res.headers.get('content-length');
|
|
135
|
+
const text = await res.text();
|
|
136
|
+
if (!text || text.length === 0) {
|
|
137
|
+
throw new Error('Empty response');
|
|
138
|
+
}
|
|
139
|
+
const bytes = contentLength ? parseInt(contentLength, 10) || text.length : text.length;
|
|
140
|
+
return { text, bytes, url };
|
|
141
|
+
};
|
|
73
142
|
try {
|
|
74
|
-
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
143
|
+
// Probe candidate dev servers concurrently — whichever responds first wins
|
|
144
|
+
const result = await promiseAny(urls.map(url => fetchSingle(url)));
|
|
145
|
+
controller.abort();
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
clearTimeout(timer);
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const MAX_ARG_SCAN_LEN = 200000;
|
|
156
|
+
const scanCallArguments = (text, openParenIndex) => {
|
|
157
|
+
let depth = 0;
|
|
158
|
+
let bracketDepth = 0;
|
|
159
|
+
let quote = null;
|
|
160
|
+
const commas = [];
|
|
161
|
+
const end = Math.min(text.length, openParenIndex + MAX_ARG_SCAN_LEN);
|
|
162
|
+
for (let i = openParenIndex; i < end; i++) {
|
|
163
|
+
const ch = text[i];
|
|
164
|
+
if (quote) {
|
|
165
|
+
if (ch === '\\') {
|
|
166
|
+
i++;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (ch === quote)
|
|
170
|
+
quote = null;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (ch === '"' || ch === "'" || ch === '`') {
|
|
174
|
+
quote = ch;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (ch === '(') {
|
|
178
|
+
depth++;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (ch === ')') {
|
|
182
|
+
if (depth === 0)
|
|
183
|
+
return null;
|
|
184
|
+
depth--;
|
|
185
|
+
if (depth === 0) {
|
|
186
|
+
if (commas.length === 0)
|
|
187
|
+
return null;
|
|
188
|
+
const args = [];
|
|
189
|
+
let prev = openParenIndex + 1;
|
|
190
|
+
for (const c of commas) {
|
|
191
|
+
args.push(text.slice(prev, c));
|
|
192
|
+
prev = c + 1;
|
|
193
|
+
}
|
|
194
|
+
args.push(text.slice(prev, i));
|
|
195
|
+
return args;
|
|
196
|
+
}
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (ch === '[') {
|
|
200
|
+
bracketDepth++;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (ch === ']') {
|
|
204
|
+
if (bracketDepth > 0)
|
|
205
|
+
bracketDepth--;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (ch === ',' && depth === 1 && bracketDepth === 0)
|
|
209
|
+
commas.push(i);
|
|
210
|
+
}
|
|
211
|
+
return null;
|
|
212
|
+
};
|
|
213
|
+
// ─── Runtime Module & Define Tracking ────────────────────────────────────────
|
|
214
|
+
// Captures modules registered via Metro's global __d and executed via __r.
|
|
215
|
+
const executedModuleIds = new Set();
|
|
216
|
+
const runtimeModules = new Map();
|
|
217
|
+
const trackRuntimeModuleExecution = () => {
|
|
218
|
+
try {
|
|
219
|
+
const g = globalThis;
|
|
220
|
+
const r = g.__r;
|
|
221
|
+
if (typeof r === 'function' && !r.__inspectorTracked) {
|
|
222
|
+
const tracked = function (moduleId, ...rest) {
|
|
223
|
+
executedModuleIds.add(Number(moduleId));
|
|
224
|
+
return r.call(this, moduleId, ...rest);
|
|
86
225
|
};
|
|
226
|
+
Object.defineProperty(tracked, '__inspectorTracked', { value: true, enumerable: false });
|
|
227
|
+
g.__r = tracked;
|
|
87
228
|
}
|
|
88
|
-
|
|
89
|
-
|
|
229
|
+
}
|
|
230
|
+
catch { }
|
|
231
|
+
};
|
|
232
|
+
exports.trackRuntimeModuleExecution = trackRuntimeModuleExecution;
|
|
233
|
+
const trackRuntimeDefine = () => {
|
|
234
|
+
try {
|
|
235
|
+
const g = globalThis;
|
|
236
|
+
const originalD = g.__d;
|
|
237
|
+
if (typeof originalD === 'function' && !originalD.__inspectorTracked) {
|
|
238
|
+
const trackedD = function (factory, moduleId, dependencyMap, verboseName, ...rest) {
|
|
239
|
+
if (typeof verboseName === 'string' && verboseName.length > 0) {
|
|
240
|
+
runtimeModules.set(Number(moduleId), { id: Number(moduleId), path: verboseName });
|
|
241
|
+
}
|
|
242
|
+
return originalD.call(this, factory, moduleId, dependencyMap, verboseName, ...rest);
|
|
243
|
+
};
|
|
244
|
+
Object.defineProperty(trackedD, '__inspectorTracked', { value: true, enumerable: false });
|
|
245
|
+
g.__d = trackedD;
|
|
90
246
|
}
|
|
91
247
|
}
|
|
92
|
-
catch {
|
|
93
|
-
|
|
248
|
+
catch { }
|
|
249
|
+
};
|
|
250
|
+
exports.trackRuntimeDefine = trackRuntimeDefine;
|
|
251
|
+
(0, exports.trackRuntimeModuleExecution)();
|
|
252
|
+
(0, exports.trackRuntimeDefine)();
|
|
253
|
+
const extractBundleModules = (bundleText) => {
|
|
254
|
+
const modules = [];
|
|
255
|
+
const seenIds = new Set();
|
|
256
|
+
if (bundleText && bundleText.length > 0) {
|
|
257
|
+
// Primary high-speed Metro tail regex: matches }, <id>, [<deps>], "<path>"
|
|
258
|
+
const tailRegex = /\}\s*,\s*(\d+)\s*,\s*\[([\d,\s]*)\]\s*,\s*(?:"([^"\r\n]+)"|'([^'\r\n]+)')/g;
|
|
259
|
+
let match;
|
|
260
|
+
while ((match = tailRegex.exec(bundleText)) !== null) {
|
|
261
|
+
const id = parseInt(match[1], 10);
|
|
262
|
+
const depsStr = match[2];
|
|
263
|
+
const path = match[3] || match[4] || '';
|
|
264
|
+
const deps = depsStr
|
|
265
|
+
? depsStr
|
|
266
|
+
.split(',')
|
|
267
|
+
.map(s => parseInt(s.trim(), 10))
|
|
268
|
+
.filter(n => !isNaN(n))
|
|
269
|
+
: [];
|
|
270
|
+
if (!seenIds.has(id) && path.length > 0) {
|
|
271
|
+
seenIds.add(id);
|
|
272
|
+
modules.push({ id, deps, path });
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
// Secondary fallback if tailRegex didn't find modules
|
|
276
|
+
if (modules.length === 0) {
|
|
277
|
+
const defineRe = /__d\s*\(/g;
|
|
278
|
+
let m;
|
|
279
|
+
while ((m = defineRe.exec(bundleText)) !== null) {
|
|
280
|
+
const openParen = m.index + m[0].length - 1;
|
|
281
|
+
const args = scanCallArguments(bundleText, openParen);
|
|
282
|
+
if (!args || args.length < 4)
|
|
283
|
+
continue;
|
|
284
|
+
const idMatch = args[1].trim().match(/^(\d+)$/);
|
|
285
|
+
if (!idMatch)
|
|
286
|
+
continue;
|
|
287
|
+
const idNum = parseInt(idMatch[1], 10);
|
|
288
|
+
if (seenIds.has(idNum))
|
|
289
|
+
continue;
|
|
290
|
+
const depsMatch = args[2].match(/\[([^\]]*)\]/);
|
|
291
|
+
const deps = [];
|
|
292
|
+
if (depsMatch) {
|
|
293
|
+
const depRe = /\d+/g;
|
|
294
|
+
let d;
|
|
295
|
+
while ((d = depRe.exec(depsMatch[1])) !== null) {
|
|
296
|
+
deps.push(parseInt(d[0], 10));
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const pathRaw = args[3].trim();
|
|
300
|
+
const pathMatch = pathRaw.match(/^["']([^"']+)["']$/);
|
|
301
|
+
const path = pathMatch ? pathMatch[1] : pathRaw;
|
|
302
|
+
if (path.length === 0 || path.length > 500)
|
|
303
|
+
continue;
|
|
304
|
+
seenIds.add(idNum);
|
|
305
|
+
modules.push({ id: idNum, deps, path });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// Merge any runtime modules captured via global.__d
|
|
310
|
+
runtimeModules.forEach((item, id) => {
|
|
311
|
+
if (!seenIds.has(id)) {
|
|
312
|
+
seenIds.add(id);
|
|
313
|
+
modules.push({ id, deps: [], path: item.path });
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
return modules;
|
|
317
|
+
};
|
|
318
|
+
const getStaticStartupModuleIds = (bundleText) => {
|
|
319
|
+
const ids = new Set();
|
|
320
|
+
const startupRe = /__r\s*\(\s*(\d+)\s*\)/g;
|
|
321
|
+
let m;
|
|
322
|
+
while ((m = startupRe.exec(bundleText)) !== null) {
|
|
323
|
+
ids.add(parseInt(m[1], 10));
|
|
324
|
+
}
|
|
325
|
+
return ids;
|
|
326
|
+
};
|
|
327
|
+
const findCommonDirectoryPrefix = (paths) => {
|
|
328
|
+
const absPaths = paths.filter(p => p.startsWith('/'));
|
|
329
|
+
if (absPaths.length === 0)
|
|
330
|
+
return '';
|
|
331
|
+
if (absPaths.length === 1) {
|
|
332
|
+
const lastSlash = absPaths[0].lastIndexOf('/');
|
|
333
|
+
return lastSlash > 0 ? absPaths[0].slice(0, lastSlash + 1) : '';
|
|
334
|
+
}
|
|
335
|
+
const splitPaths = absPaths.map(p => p.split('/'));
|
|
336
|
+
let commonIndex = 0;
|
|
337
|
+
const first = splitPaths[0];
|
|
338
|
+
while (commonIndex < first.length - 1) {
|
|
339
|
+
const segment = first[commonIndex];
|
|
340
|
+
if (splitPaths.every(p => p[commonIndex] === segment)) {
|
|
341
|
+
commonIndex++;
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
break;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
if (commonIndex === 0)
|
|
348
|
+
return '';
|
|
349
|
+
return first.slice(0, commonIndex).join('/') + '/';
|
|
350
|
+
};
|
|
351
|
+
const isExternalPackage = (rawPath) => {
|
|
352
|
+
// 1. Standard node_modules
|
|
353
|
+
if (rawPath.includes('node_modules/')) {
|
|
354
|
+
const pkgMatch = rawPath.match(/node_modules\/(?:@([^/]+)\/([^/]+)|([^/]+))/);
|
|
355
|
+
if (pkgMatch) {
|
|
356
|
+
const pkgName = pkgMatch[1] && pkgMatch[2] ? `@${pkgMatch[1]}/${pkgMatch[2]}` : pkgMatch[3];
|
|
357
|
+
return { isPkg: true, pkgName };
|
|
358
|
+
}
|
|
359
|
+
return { isPkg: true };
|
|
360
|
+
}
|
|
361
|
+
// 2. React Native In-App Inspector (when linked or embedded)
|
|
362
|
+
if (rawPath.includes('react-native-inapp-inspector')) {
|
|
363
|
+
return { isPkg: true, pkgName: 'react-native-inapp-inspector' };
|
|
364
|
+
}
|
|
365
|
+
// 3. Yarn berry / pnpm virtual stores
|
|
366
|
+
if (rawPath.includes('.yarn/') || rawPath.includes('.pnpm/')) {
|
|
367
|
+
return { isPkg: true };
|
|
368
|
+
}
|
|
369
|
+
// 4. Compiled package dist / build folders outside project source
|
|
370
|
+
if (rawPath.includes('/dist/esm/') ||
|
|
371
|
+
rawPath.includes('/dist/commonjs/') ||
|
|
372
|
+
rawPath.includes('/dist/cjs/') ||
|
|
373
|
+
rawPath.startsWith('dist/esm/') ||
|
|
374
|
+
rawPath.startsWith('dist/commonjs/') ||
|
|
375
|
+
rawPath.startsWith('dist/') ||
|
|
376
|
+
rawPath.includes('/react-native-inapp-inspector/dist/')) {
|
|
377
|
+
return { isPkg: true, pkgName: 'react-native-inapp-inspector' };
|
|
94
378
|
}
|
|
379
|
+
return { isPkg: false };
|
|
380
|
+
};
|
|
381
|
+
const isInternalModule = (rawPath) => {
|
|
382
|
+
return (rawPath.startsWith('<<') ||
|
|
383
|
+
rawPath.startsWith('[metro]') ||
|
|
384
|
+
rawPath.includes('metro-runtime') ||
|
|
385
|
+
rawPath.includes('polyfills/require.js') ||
|
|
386
|
+
rawPath.includes('prelude_commonjs') ||
|
|
387
|
+
rawPath.includes('Libraries/Core/InitializeCore.js') ||
|
|
388
|
+
rawPath.includes('@babel/runtime'));
|
|
95
389
|
};
|
|
96
390
|
/**
|
|
97
391
|
* Parses raw Metro bundle source code to discover real modules and packages in the host app.
|
|
@@ -100,79 +394,156 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
100
394
|
const isHermes = Boolean(globalThis.HermesInternal);
|
|
101
395
|
const totalDevMb = Number((totalBytes / (1024 * 1024)).toFixed(2));
|
|
102
396
|
const totalDevKb = Math.round(totalBytes / 1024);
|
|
397
|
+
const modules = extractBundleModules(bundleText);
|
|
398
|
+
const startupIds = getStaticStartupModuleIds(bundleText);
|
|
399
|
+
// Build module map for dependency graph traversal
|
|
400
|
+
const modMap = new Map();
|
|
401
|
+
for (const mod of modules) {
|
|
402
|
+
modMap.set(mod.id, mod);
|
|
403
|
+
}
|
|
404
|
+
// Transitive Reachability: traverse the dependency graph starting from
|
|
405
|
+
// entrypoints (index.js, App.tsx, app.json, startupIds, executedModuleIds)
|
|
406
|
+
const consumedIds = new Set();
|
|
407
|
+
const queue = [];
|
|
408
|
+
const markConsumed = (id) => {
|
|
409
|
+
if (!consumedIds.has(id)) {
|
|
410
|
+
consumedIds.add(id);
|
|
411
|
+
queue.push(id);
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
// Seed with root entrypoints and runtime executions
|
|
415
|
+
markConsumed(0);
|
|
416
|
+
startupIds.forEach(id => markConsumed(id));
|
|
417
|
+
executedModuleIds.forEach(id => markConsumed(id));
|
|
418
|
+
for (const mod of modules) {
|
|
419
|
+
const lower = mod.path.toLowerCase().replace(/\\/g, '/');
|
|
420
|
+
if (lower.endsWith('/index.js') ||
|
|
421
|
+
lower.endsWith('/index.ts') ||
|
|
422
|
+
lower.endsWith('/index.tsx') ||
|
|
423
|
+
lower === 'index.js' ||
|
|
424
|
+
lower.endsWith('/app.tsx') ||
|
|
425
|
+
lower.endsWith('/app.js') ||
|
|
426
|
+
lower.endsWith('/app.jsx') ||
|
|
427
|
+
lower === 'app.tsx' ||
|
|
428
|
+
lower === 'app.js' ||
|
|
429
|
+
lower.endsWith('app.json')) {
|
|
430
|
+
markConsumed(mod.id);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
// Transitive BFS dependency traversal
|
|
434
|
+
while (queue.length > 0) {
|
|
435
|
+
const currId = queue.shift();
|
|
436
|
+
const currMod = modMap.get(currId);
|
|
437
|
+
if (currMod && currMod.deps) {
|
|
438
|
+
for (const depId of currMod.deps) {
|
|
439
|
+
markConsumed(depId);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
103
443
|
const discoveredPackagesMap = new Map();
|
|
444
|
+
const rawProjectItems = [];
|
|
445
|
+
for (const mod of modules) {
|
|
446
|
+
const rawPath = mod.path.replace(/\\/g, '/'); // Normalize Windows backslashes
|
|
447
|
+
const { isPkg, pkgName } = isExternalPackage(rawPath);
|
|
448
|
+
if (isPkg) {
|
|
449
|
+
if (pkgName && !pkgName.startsWith('.')) {
|
|
450
|
+
discoveredPackagesMap.set(pkgName, (discoveredPackagesMap.get(pkgName) || 0) + 1);
|
|
451
|
+
}
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
// Filter out internal Metro polyfills / virtual modules
|
|
455
|
+
if (isInternalModule(rawPath)) {
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
rawProjectItems.push({ mod, rawPath });
|
|
459
|
+
}
|
|
460
|
+
// Calculate common prefix for absolute paths to cleanly extract host app project files
|
|
461
|
+
const commonPrefix = findCommonDirectoryPrefix(rawProjectItems.map(item => item.rawPath));
|
|
104
462
|
const discoveredFiles = [];
|
|
105
|
-
// Match module declarations: __d(function(...), id, [...], "path/to/module.js") or comments
|
|
106
|
-
// In Metro: __d(function(...), 42, [1, 2], "node_modules/lodash/index.js")
|
|
107
|
-
const modulePathRegex = /(?:__d\s*\([^,]+,[^,]+,[^,]+,["']([^"']+)["']|\/\/\s*@metro-module-path\s+([^\n\r]+)|["']((?:node_modules|\.|\/|[a-zA-Z0-9_-]+)\/[^"']+\.[a-zA-Z0-9]+)["'])/g;
|
|
108
|
-
let match;
|
|
109
463
|
let fileIdx = 0;
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
464
|
+
for (const { mod, rawPath } of rawProjectItems) {
|
|
465
|
+
let cleanPath = rawPath;
|
|
466
|
+
if (commonPrefix && cleanPath.startsWith(commonPrefix)) {
|
|
467
|
+
cleanPath = cleanPath.slice(commonPrefix.length);
|
|
468
|
+
}
|
|
469
|
+
// Clean leading ../ sequences, ./, and leading /
|
|
470
|
+
cleanPath = cleanPath.replace(/^(\.\.\/)+/, '').replace(/^\.\//, '').replace(/^\/+/, '');
|
|
471
|
+
if (!cleanPath)
|
|
472
|
+
cleanPath = rawPath.split('/').pop() || rawPath;
|
|
473
|
+
const isLocalFile = cleanPath.endsWith('.tsx') ||
|
|
474
|
+
cleanPath.endsWith('.ts') ||
|
|
475
|
+
cleanPath.endsWith('.jsx') ||
|
|
476
|
+
cleanPath.endsWith('.js') ||
|
|
477
|
+
cleanPath.endsWith('.mjs') ||
|
|
478
|
+
cleanPath.endsWith('.cjs') ||
|
|
479
|
+
cleanPath.endsWith('.json') ||
|
|
480
|
+
cleanPath.endsWith('.png') ||
|
|
481
|
+
cleanPath.endsWith('.jpg') ||
|
|
482
|
+
cleanPath.endsWith('.jpeg') ||
|
|
483
|
+
cleanPath.endsWith('.gif') ||
|
|
484
|
+
cleanPath.endsWith('.webp') ||
|
|
485
|
+
cleanPath.endsWith('.svg') ||
|
|
486
|
+
cleanPath.endsWith('.bmp') ||
|
|
487
|
+
cleanPath.endsWith('.ico') ||
|
|
488
|
+
cleanPath.endsWith('.ttf') ||
|
|
489
|
+
cleanPath.endsWith('.otf') ||
|
|
490
|
+
cleanPath.endsWith('.woff') ||
|
|
491
|
+
cleanPath.endsWith('.woff2') ||
|
|
492
|
+
cleanPath.endsWith('.css') ||
|
|
493
|
+
cleanPath.endsWith('.scss') ||
|
|
494
|
+
cleanPath.endsWith('.html') ||
|
|
495
|
+
cleanPath.endsWith('.md') ||
|
|
496
|
+
cleanPath.endsWith('.graphql') ||
|
|
497
|
+
cleanPath.endsWith('.gql');
|
|
498
|
+
if (!isLocalFile)
|
|
115
499
|
continue;
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
discoveredPackagesMap.set(pkgName, (discoveredPackagesMap.get(pkgName) || 0) + 1);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
500
|
+
const ext = cleanPath.split('.').pop()?.toUpperCase() || 'JS';
|
|
501
|
+
const name = cleanPath.split('/').pop() || cleanPath;
|
|
502
|
+
let category = 'javascript';
|
|
503
|
+
let color = AppColors_1.AppColors.indigo500;
|
|
504
|
+
if (ext === 'TSX' || ext === 'TS') {
|
|
505
|
+
category = 'typescript';
|
|
506
|
+
color = AppColors_1.AppColors.sky500;
|
|
126
507
|
}
|
|
127
|
-
else if (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
category = 'image';
|
|
146
|
-
color = AppColors_1.AppColors.pink500;
|
|
147
|
-
}
|
|
148
|
-
else if (ext === 'TTF' || ext === 'OTF') {
|
|
149
|
-
category = 'font';
|
|
150
|
-
color = AppColors_1.AppColors.purple500;
|
|
151
|
-
}
|
|
152
|
-
else if (ext === 'JSON') {
|
|
153
|
-
category = 'json';
|
|
154
|
-
color = AppColors_1.AppColors.emerald500;
|
|
155
|
-
}
|
|
156
|
-
// Approximate module size from total and count
|
|
157
|
-
const approxKb = Math.max(2, Math.round((totalDevKb * 0.15) / Math.max(seenPaths.size, 20)));
|
|
158
|
-
discoveredFiles.push({
|
|
159
|
-
id: `host-file-${fileIdx++}`,
|
|
160
|
-
name,
|
|
161
|
-
path: rawPath,
|
|
162
|
-
ext,
|
|
163
|
-
category,
|
|
164
|
-
sizeKb: approxKb,
|
|
165
|
-
meta: `Active Host App Module • ${category.toUpperCase()}`,
|
|
166
|
-
color,
|
|
167
|
-
status: 'optimal',
|
|
168
|
-
advice: 'Bundled into Host App Development Runtime',
|
|
169
|
-
isConsumed: true,
|
|
170
|
-
});
|
|
508
|
+
else if (ext === 'PNG' ||
|
|
509
|
+
ext === 'JPG' ||
|
|
510
|
+
ext === 'JPEG' ||
|
|
511
|
+
ext === 'GIF' ||
|
|
512
|
+
ext === 'WEBP' ||
|
|
513
|
+
ext === 'SVG' ||
|
|
514
|
+
ext === 'BMP' ||
|
|
515
|
+
ext === 'ICO') {
|
|
516
|
+
category = 'image';
|
|
517
|
+
color = AppColors_1.AppColors.pink500;
|
|
518
|
+
}
|
|
519
|
+
else if (ext === 'TTF' || ext === 'OTF' || ext === 'WOFF' || ext === 'WOFF2') {
|
|
520
|
+
category = 'font';
|
|
521
|
+
color = AppColors_1.AppColors.purple500;
|
|
522
|
+
}
|
|
523
|
+
else if (ext === 'JSON' || ext === 'CSS' || ext === 'SCSS' || ext === 'HTML' || ext === 'MD' || ext === 'GRAPHQL' || ext === 'GQL') {
|
|
524
|
+
category = 'json';
|
|
525
|
+
color = AppColors_1.AppColors.emerald500;
|
|
171
526
|
}
|
|
172
|
-
|
|
173
|
-
|
|
527
|
+
// Approximate module size from total and module count
|
|
528
|
+
const approxKb = Math.max(2, Math.round((totalDevKb * 0.15) / Math.max(modules.length, 20)));
|
|
529
|
+
const isConsumed = consumedIds.has(mod.id);
|
|
530
|
+
discoveredFiles.push({
|
|
531
|
+
id: `host-file-${fileIdx++}`,
|
|
532
|
+
name,
|
|
533
|
+
path: cleanPath,
|
|
534
|
+
ext,
|
|
535
|
+
category,
|
|
536
|
+
sizeKb: approxKb,
|
|
537
|
+
meta: `Active Host App Module • ${category.toUpperCase()}`,
|
|
538
|
+
color,
|
|
539
|
+
status: isConsumed ? 'optimal' : 'warning',
|
|
540
|
+
advice: isConsumed
|
|
541
|
+
? 'In-Use: Bundled and active in host application dependency tree'
|
|
542
|
+
: 'Not consumed: Defined in the bundle but not referenced in active execution tree',
|
|
543
|
+
isConsumed,
|
|
544
|
+
});
|
|
174
545
|
}
|
|
175
|
-
// If no
|
|
546
|
+
// If no modules could be parsed (e.g. Hermes bytecode / obfuscated bundle), synthesize from loaded modules
|
|
176
547
|
if (discoveredFiles.length === 0) {
|
|
177
548
|
discoveredFiles.push({
|
|
178
549
|
id: 'hf-1',
|
|
@@ -361,7 +732,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
361
732
|
const iosComponents = [
|
|
362
733
|
{
|
|
363
734
|
id: 'ios-c1',
|
|
364
|
-
name: '
|
|
735
|
+
name: 'Frameworks & Dynamic Pods',
|
|
365
736
|
category: 'frameworks',
|
|
366
737
|
sizeMb: nativeFrameworksMb,
|
|
367
738
|
pct: Number(((nativeFrameworksMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -371,7 +742,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
371
742
|
},
|
|
372
743
|
{
|
|
373
744
|
id: 'ios-c2',
|
|
374
|
-
name: '
|
|
745
|
+
name: 'Mach-O Executable (ARM64)',
|
|
375
746
|
category: 'native',
|
|
376
747
|
sizeMb: nativeMachoMb,
|
|
377
748
|
pct: Number(((nativeMachoMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -381,7 +752,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
381
752
|
},
|
|
382
753
|
{
|
|
383
754
|
id: 'ios-c3',
|
|
384
|
-
name: 'Asset Catalog (Assets.car
|
|
755
|
+
name: 'Asset Catalog (Assets.car)',
|
|
385
756
|
category: 'assets',
|
|
386
757
|
sizeMb: assetsCatalogMb,
|
|
387
758
|
pct: Number(((assetsCatalogMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -391,7 +762,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
391
762
|
},
|
|
392
763
|
{
|
|
393
764
|
id: 'ios-c4',
|
|
394
|
-
name: 'Hermes Bytecode (main.jsbundle
|
|
765
|
+
name: 'Hermes Bytecode (main.jsbundle)',
|
|
395
766
|
category: 'js',
|
|
396
767
|
sizeMb: releaseJsMb,
|
|
397
768
|
pct: Number(((releaseJsMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -401,7 +772,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
401
772
|
},
|
|
402
773
|
{
|
|
403
774
|
id: 'ios-c5',
|
|
404
|
-
name: '
|
|
775
|
+
name: 'Metadata & Code Signatures',
|
|
405
776
|
category: 'meta',
|
|
406
777
|
sizeMb: metadataMb,
|
|
407
778
|
pct: Number(((metadataMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -413,7 +784,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
413
784
|
const androidComponents = [
|
|
414
785
|
{
|
|
415
786
|
id: 'and-c1',
|
|
416
|
-
name: 'Native C++
|
|
787
|
+
name: 'Native C++ Libraries (.so)',
|
|
417
788
|
category: 'native',
|
|
418
789
|
sizeMb: androidCppMb,
|
|
419
790
|
pct: Number(((androidCppMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -423,7 +794,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
423
794
|
},
|
|
424
795
|
{
|
|
425
796
|
id: 'and-c2',
|
|
426
|
-
name: 'Compiled DEX
|
|
797
|
+
name: 'Compiled DEX (classes.dex)',
|
|
427
798
|
category: 'frameworks',
|
|
428
799
|
sizeMb: androidDexMb,
|
|
429
800
|
pct: Number(((androidDexMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -433,7 +804,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
433
804
|
},
|
|
434
805
|
{
|
|
435
806
|
id: 'and-c3',
|
|
436
|
-
name: 'Android Resources
|
|
807
|
+
name: 'Android Resources (res/)',
|
|
437
808
|
category: 'assets',
|
|
438
809
|
sizeMb: androidResMb,
|
|
439
810
|
pct: Number(((androidResMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -443,7 +814,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
443
814
|
},
|
|
444
815
|
{
|
|
445
816
|
id: 'and-c4',
|
|
446
|
-
name: 'Hermes Bytecode (
|
|
817
|
+
name: 'Hermes Bytecode (Android)',
|
|
447
818
|
category: 'js',
|
|
448
819
|
sizeMb: releaseJsMb,
|
|
449
820
|
pct: Number(((releaseJsMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -453,7 +824,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
453
824
|
},
|
|
454
825
|
{
|
|
455
826
|
id: 'and-c5',
|
|
456
|
-
name: '
|
|
827
|
+
name: 'Manifest & Signatures (META-INF)',
|
|
457
828
|
category: 'meta',
|
|
458
829
|
sizeMb: 2.6,
|
|
459
830
|
pct: Number(((2.6 / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -469,7 +840,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
469
840
|
const androidApkComponents = [
|
|
470
841
|
{
|
|
471
842
|
id: 'apk-c1',
|
|
472
|
-
name: 'Multi-ABI
|
|
843
|
+
name: 'Multi-ABI C++ Libraries (.so)',
|
|
473
844
|
category: 'native',
|
|
474
845
|
sizeMb: androidMultiAbiCppMb,
|
|
475
846
|
pct: Number(((androidMultiAbiCppMb / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -489,7 +860,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
489
860
|
},
|
|
490
861
|
{
|
|
491
862
|
id: 'apk-c3',
|
|
492
|
-
name: 'Android Resources & Assets (res
|
|
863
|
+
name: 'Android Resources & Assets (res/)',
|
|
493
864
|
category: 'assets',
|
|
494
865
|
sizeMb: androidResMb,
|
|
495
866
|
pct: Number(((androidResMb / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -499,7 +870,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
499
870
|
},
|
|
500
871
|
{
|
|
501
872
|
id: 'apk-c4',
|
|
502
|
-
name: 'Hermes Bytecode Bundle (
|
|
873
|
+
name: 'Hermes Bytecode Bundle (Android)',
|
|
503
874
|
category: 'js',
|
|
504
875
|
sizeMb: releaseJsMb,
|
|
505
876
|
pct: Number(((releaseJsMb / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -509,7 +880,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
509
880
|
},
|
|
510
881
|
{
|
|
511
882
|
id: 'apk-c5',
|
|
512
|
-
name: '
|
|
883
|
+
name: 'Manifest & Signatures (META-INF)',
|
|
513
884
|
category: 'meta',
|
|
514
885
|
sizeMb: 3.2,
|
|
515
886
|
pct: Number(((3.2 / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -525,7 +896,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
525
896
|
totalDevMb,
|
|
526
897
|
totalDevKb,
|
|
527
898
|
isHermes,
|
|
528
|
-
moduleCount:
|
|
899
|
+
moduleCount: modules.length || discoveredFiles.length + packagesList.length,
|
|
529
900
|
packageCount: packagesList.length,
|
|
530
901
|
filesCount: discoveredFiles.length,
|
|
531
902
|
splitUp: {
|
|
@@ -583,9 +954,13 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) =>
|
|
|
583
954
|
exports.parseBundleSource = parseBundleSource;
|
|
584
955
|
/**
|
|
585
956
|
* Automatically fetch and analyze the real running bundle for the host app.
|
|
957
|
+
* Supports dynamic port detection (8081, 8082, 8083, etc.) and parallel dev server probing.
|
|
586
958
|
*/
|
|
587
|
-
const analyzeHostAppBundle = async () => {
|
|
588
|
-
if (
|
|
959
|
+
const analyzeHostAppBundle = async (forceRefresh = false) => {
|
|
960
|
+
if (forceRefresh) {
|
|
961
|
+
cachedAnalysis = null;
|
|
962
|
+
}
|
|
963
|
+
if (cachedAnalysis && !forceRefresh)
|
|
589
964
|
return cachedAnalysis;
|
|
590
965
|
if (isAnalyzing) {
|
|
591
966
|
return new Promise(resolve => {
|
|
@@ -594,19 +969,11 @@ const analyzeHostAppBundle = async () => {
|
|
|
594
969
|
}
|
|
595
970
|
isAnalyzing = true;
|
|
596
971
|
const scriptURL = (0, exports.getHostScriptURL)();
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const
|
|
602
|
-
if (probe.ok && probe.text && probe.text.length > 0) {
|
|
603
|
-
fetched = { text: probe.text, bytes: probe.bytes ?? probe.text.length };
|
|
604
|
-
fetchedUrl = url;
|
|
605
|
-
break;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
if (fetched) {
|
|
609
|
-
const result = (0, exports.parseBundleSource)(fetched.text, fetched.bytes, fetchedUrl);
|
|
972
|
+
const probeUrls = buildProbeUrls(scriptURL);
|
|
973
|
+
// Probe all candidate dev-server ports and hosts in parallel for instant response
|
|
974
|
+
const fetched = await probeCandidateUrlsInParallel(probeUrls, 3500);
|
|
975
|
+
if (fetched && fetched.text && fetched.text.length > 0) {
|
|
976
|
+
const result = (0, exports.parseBundleSource)(fetched.text, fetched.bytes, fetched.url);
|
|
610
977
|
cachedAnalysis = result;
|
|
611
978
|
isAnalyzing = false;
|
|
612
979
|
subscribers.forEach(cb => cb(result));
|
|
@@ -624,5 +991,9 @@ const analyzeHostAppBundle = async () => {
|
|
|
624
991
|
return result;
|
|
625
992
|
};
|
|
626
993
|
exports.analyzeHostAppBundle = analyzeHostAppBundle;
|
|
994
|
+
const clearCachedBundleAnalysis = () => {
|
|
995
|
+
cachedAnalysis = null;
|
|
996
|
+
};
|
|
997
|
+
exports.clearCachedBundleAnalysis = clearCachedBundleAnalysis;
|
|
627
998
|
const getCachedBundleAnalysis = () => cachedAnalysis;
|
|
628
999
|
exports.getCachedBundleAnalysis = getCachedBundleAnalysis;
|