react-native-inapp-inspector 1.1.23 → 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 +859 -402
- 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 +6 -2
- package/dist/commonjs/customHooks/bundleAnalyzer.js +552 -114
- 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 +53 -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 +860 -403
- 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 +6 -2
- package/dist/esm/customHooks/bundleAnalyzer.js +549 -114
- 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 +53 -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
|
//
|
|
@@ -15,96 +15,535 @@ const react_native_1 = require("react-native");
|
|
|
15
15
|
let cachedAnalysis = null;
|
|
16
16
|
let isAnalyzing = false;
|
|
17
17
|
const subscribers = [];
|
|
18
|
+
const getSourceCodeModule = () => {
|
|
19
|
+
try {
|
|
20
|
+
const legacy = react_native_1.NativeModules?.SourceCode;
|
|
21
|
+
if (legacy && typeof legacy.scriptURL === 'string' && legacy.scriptURL.length > 0) {
|
|
22
|
+
return legacy;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch { }
|
|
26
|
+
try {
|
|
27
|
+
const turbo = react_native_1.TurboModuleRegistry?.get?.('SourceCode');
|
|
28
|
+
const constants = turbo?.getConstants?.();
|
|
29
|
+
const scriptURL = turbo?.scriptURL || constants?.scriptURL;
|
|
30
|
+
if (typeof scriptURL === 'string' && scriptURL.length > 0) {
|
|
31
|
+
return { scriptURL };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch { }
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
18
37
|
const getHostScriptURL = () => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
38
|
+
// 1. NativeModules.SourceCode (bridged) or SourceCode TurboModule (bridgeless / new arch)
|
|
39
|
+
try {
|
|
40
|
+
const scriptURL = getSourceCodeModule()?.scriptURL;
|
|
41
|
+
if (typeof scriptURL === 'string' && scriptURL.length > 0) {
|
|
42
|
+
return scriptURL;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch { }
|
|
46
|
+
// 2. Android legacy & new arch: PlatformConstants.serverHost, DevSettings, or AndroidConstants
|
|
47
|
+
try {
|
|
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;
|
|
52
|
+
if (typeof serverHost === 'string' && serverHost.length > 0) {
|
|
53
|
+
const formattedHost = serverHost.startsWith('http') ? serverHost : `http://${serverHost}`;
|
|
54
|
+
return `${formattedHost}/index.bundle?platform=${react_native_1.Platform.OS}&dev=true`;
|
|
55
|
+
}
|
|
22
56
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch { }
|
|
69
|
+
return '';
|
|
26
70
|
};
|
|
27
71
|
exports.getHostScriptURL = getHostScriptURL;
|
|
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];
|
|
74
|
+
const buildProbeUrls = (scriptURL) => {
|
|
75
|
+
const urls = [];
|
|
76
|
+
let extractedHost = null;
|
|
77
|
+
let extractedPort = null;
|
|
78
|
+
if (typeof scriptURL === 'string' && scriptURL.startsWith('http')) {
|
|
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
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// Prioritize dynamically extracted port first
|
|
89
|
+
const ports = extractedPort
|
|
90
|
+
? Array.from(new Set([extractedPort, ...DEV_SERVER_PORTS]))
|
|
91
|
+
: DEV_SERVER_PORTS;
|
|
92
|
+
const hosts = react_native_1.Platform.OS === 'android'
|
|
93
|
+
? ['localhost', '127.0.0.1', '10.0.2.2', '10.0.3.2']
|
|
94
|
+
: ['localhost', '127.0.0.1'];
|
|
95
|
+
if (extractedHost && !hosts.includes(extractedHost)) {
|
|
96
|
+
hosts.unshift(extractedHost);
|
|
97
|
+
}
|
|
98
|
+
for (const port of ports) {
|
|
99
|
+
for (const host of hosts) {
|
|
100
|
+
urls.push(`http://${host}:${port}/index.bundle?platform=${react_native_1.Platform.OS}&dev=true`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return Array.from(new Set(urls));
|
|
104
|
+
};
|
|
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
|
+
};
|
|
142
|
+
try {
|
|
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);
|
|
225
|
+
};
|
|
226
|
+
Object.defineProperty(tracked, '__inspectorTracked', { value: true, enumerable: false });
|
|
227
|
+
g.__r = tracked;
|
|
228
|
+
}
|
|
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;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
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' };
|
|
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'));
|
|
389
|
+
};
|
|
28
390
|
/**
|
|
29
391
|
* Parses raw Metro bundle source code to discover real modules and packages in the host app.
|
|
30
392
|
*/
|
|
31
|
-
const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
393
|
+
const parseBundleSource = (bundleText, totalBytes, scriptURL, isLive = true) => {
|
|
32
394
|
const isHermes = Boolean(globalThis.HermesInternal);
|
|
33
395
|
const totalDevMb = Number((totalBytes / (1024 * 1024)).toFixed(2));
|
|
34
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
|
+
}
|
|
35
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));
|
|
36
462
|
const discoveredFiles = [];
|
|
37
|
-
// Match module declarations: __d(function(...), id, [...], "path/to/module.js") or comments
|
|
38
|
-
// In Metro: __d(function(...), 42, [1, 2], "node_modules/lodash/index.js")
|
|
39
|
-
const modulePathRegex = /(?:__d\s*\([^,]+,[^,]+,[^,]+,["']([^"']+)["']|\/\/\s*@metro-module-path\s+([^\n\r]+)|["']((?:node_modules|\.|\/|[a-zA-Z0-9_-]+)\/[^"']+\.[a-zA-Z0-9]+)["'])/g;
|
|
40
|
-
let match;
|
|
41
463
|
let fileIdx = 0;
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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)
|
|
47
499
|
continue;
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
discoveredPackagesMap.set(pkgName, (discoveredPackagesMap.get(pkgName) || 0) + 1);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
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;
|
|
58
507
|
}
|
|
59
|
-
else if (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
category = 'typescript';
|
|
74
|
-
color = AppColors_1.AppColors.sky500;
|
|
75
|
-
}
|
|
76
|
-
else if (ext === 'PNG' || ext === 'JPG' || ext === 'SVG') {
|
|
77
|
-
category = 'image';
|
|
78
|
-
color = AppColors_1.AppColors.pink500;
|
|
79
|
-
}
|
|
80
|
-
else if (ext === 'TTF' || ext === 'OTF') {
|
|
81
|
-
category = 'font';
|
|
82
|
-
color = AppColors_1.AppColors.purple500;
|
|
83
|
-
}
|
|
84
|
-
else if (ext === 'JSON') {
|
|
85
|
-
category = 'json';
|
|
86
|
-
color = AppColors_1.AppColors.emerald500;
|
|
87
|
-
}
|
|
88
|
-
// Approximate module size from total and count
|
|
89
|
-
const approxKb = Math.max(2, Math.round((totalDevKb * 0.15) / Math.max(seenPaths.size, 20)));
|
|
90
|
-
discoveredFiles.push({
|
|
91
|
-
id: `host-file-${fileIdx++}`,
|
|
92
|
-
name,
|
|
93
|
-
path: rawPath,
|
|
94
|
-
ext,
|
|
95
|
-
category,
|
|
96
|
-
sizeKb: approxKb,
|
|
97
|
-
meta: `Active Host App Module • ${category.toUpperCase()}`,
|
|
98
|
-
color,
|
|
99
|
-
status: 'optimal',
|
|
100
|
-
advice: 'Bundled into Host App Development Runtime',
|
|
101
|
-
isConsumed: true,
|
|
102
|
-
});
|
|
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;
|
|
103
522
|
}
|
|
104
|
-
if (
|
|
105
|
-
|
|
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;
|
|
526
|
+
}
|
|
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
|
+
});
|
|
106
545
|
}
|
|
107
|
-
// If no
|
|
546
|
+
// If no modules could be parsed (e.g. Hermes bytecode / obfuscated bundle), synthesize from loaded modules
|
|
108
547
|
if (discoveredFiles.length === 0) {
|
|
109
548
|
discoveredFiles.push({
|
|
110
549
|
id: 'hf-1',
|
|
@@ -293,7 +732,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
293
732
|
const iosComponents = [
|
|
294
733
|
{
|
|
295
734
|
id: 'ios-c1',
|
|
296
|
-
name: '
|
|
735
|
+
name: 'Frameworks & Dynamic Pods',
|
|
297
736
|
category: 'frameworks',
|
|
298
737
|
sizeMb: nativeFrameworksMb,
|
|
299
738
|
pct: Number(((nativeFrameworksMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -303,7 +742,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
303
742
|
},
|
|
304
743
|
{
|
|
305
744
|
id: 'ios-c2',
|
|
306
|
-
name: '
|
|
745
|
+
name: 'Mach-O Executable (ARM64)',
|
|
307
746
|
category: 'native',
|
|
308
747
|
sizeMb: nativeMachoMb,
|
|
309
748
|
pct: Number(((nativeMachoMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -313,7 +752,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
313
752
|
},
|
|
314
753
|
{
|
|
315
754
|
id: 'ios-c3',
|
|
316
|
-
name: 'Asset Catalog (Assets.car
|
|
755
|
+
name: 'Asset Catalog (Assets.car)',
|
|
317
756
|
category: 'assets',
|
|
318
757
|
sizeMb: assetsCatalogMb,
|
|
319
758
|
pct: Number(((assetsCatalogMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -323,7 +762,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
323
762
|
},
|
|
324
763
|
{
|
|
325
764
|
id: 'ios-c4',
|
|
326
|
-
name: 'Hermes Bytecode (main.jsbundle
|
|
765
|
+
name: 'Hermes Bytecode (main.jsbundle)',
|
|
327
766
|
category: 'js',
|
|
328
767
|
sizeMb: releaseJsMb,
|
|
329
768
|
pct: Number(((releaseJsMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -333,7 +772,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
333
772
|
},
|
|
334
773
|
{
|
|
335
774
|
id: 'ios-c5',
|
|
336
|
-
name: '
|
|
775
|
+
name: 'Metadata & Code Signatures',
|
|
337
776
|
category: 'meta',
|
|
338
777
|
sizeMb: metadataMb,
|
|
339
778
|
pct: Number(((metadataMb / iosInstallMb) * 100).toFixed(1)),
|
|
@@ -345,7 +784,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
345
784
|
const androidComponents = [
|
|
346
785
|
{
|
|
347
786
|
id: 'and-c1',
|
|
348
|
-
name: 'Native C++
|
|
787
|
+
name: 'Native C++ Libraries (.so)',
|
|
349
788
|
category: 'native',
|
|
350
789
|
sizeMb: androidCppMb,
|
|
351
790
|
pct: Number(((androidCppMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -355,7 +794,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
355
794
|
},
|
|
356
795
|
{
|
|
357
796
|
id: 'and-c2',
|
|
358
|
-
name: 'Compiled DEX
|
|
797
|
+
name: 'Compiled DEX (classes.dex)',
|
|
359
798
|
category: 'frameworks',
|
|
360
799
|
sizeMb: androidDexMb,
|
|
361
800
|
pct: Number(((androidDexMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -365,7 +804,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
365
804
|
},
|
|
366
805
|
{
|
|
367
806
|
id: 'and-c3',
|
|
368
|
-
name: 'Android Resources
|
|
807
|
+
name: 'Android Resources (res/)',
|
|
369
808
|
category: 'assets',
|
|
370
809
|
sizeMb: androidResMb,
|
|
371
810
|
pct: Number(((androidResMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -375,7 +814,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
375
814
|
},
|
|
376
815
|
{
|
|
377
816
|
id: 'and-c4',
|
|
378
|
-
name: 'Hermes Bytecode (
|
|
817
|
+
name: 'Hermes Bytecode (Android)',
|
|
379
818
|
category: 'js',
|
|
380
819
|
sizeMb: releaseJsMb,
|
|
381
820
|
pct: Number(((releaseJsMb / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -385,7 +824,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
385
824
|
},
|
|
386
825
|
{
|
|
387
826
|
id: 'and-c5',
|
|
388
|
-
name: '
|
|
827
|
+
name: 'Manifest & Signatures (META-INF)',
|
|
389
828
|
category: 'meta',
|
|
390
829
|
sizeMb: 2.6,
|
|
391
830
|
pct: Number(((2.6 / androidInstallMb) * 100).toFixed(1)),
|
|
@@ -401,7 +840,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
401
840
|
const androidApkComponents = [
|
|
402
841
|
{
|
|
403
842
|
id: 'apk-c1',
|
|
404
|
-
name: 'Multi-ABI
|
|
843
|
+
name: 'Multi-ABI C++ Libraries (.so)',
|
|
405
844
|
category: 'native',
|
|
406
845
|
sizeMb: androidMultiAbiCppMb,
|
|
407
846
|
pct: Number(((androidMultiAbiCppMb / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -421,7 +860,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
421
860
|
},
|
|
422
861
|
{
|
|
423
862
|
id: 'apk-c3',
|
|
424
|
-
name: 'Android Resources & Assets (res
|
|
863
|
+
name: 'Android Resources & Assets (res/)',
|
|
425
864
|
category: 'assets',
|
|
426
865
|
sizeMb: androidResMb,
|
|
427
866
|
pct: Number(((androidResMb / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -431,7 +870,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
431
870
|
},
|
|
432
871
|
{
|
|
433
872
|
id: 'apk-c4',
|
|
434
|
-
name: 'Hermes Bytecode Bundle (
|
|
873
|
+
name: 'Hermes Bytecode Bundle (Android)',
|
|
435
874
|
category: 'js',
|
|
436
875
|
sizeMb: releaseJsMb,
|
|
437
876
|
pct: Number(((releaseJsMb / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -441,7 +880,7 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
441
880
|
},
|
|
442
881
|
{
|
|
443
882
|
id: 'apk-c5',
|
|
444
|
-
name: '
|
|
883
|
+
name: 'Manifest & Signatures (META-INF)',
|
|
445
884
|
category: 'meta',
|
|
446
885
|
sizeMb: 3.2,
|
|
447
886
|
pct: Number(((3.2 / androidApkInstallMb) * 100).toFixed(1)),
|
|
@@ -451,13 +890,13 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
451
890
|
},
|
|
452
891
|
];
|
|
453
892
|
return {
|
|
454
|
-
isLive
|
|
893
|
+
isLive,
|
|
455
894
|
scriptURL,
|
|
456
895
|
totalDevBytes: totalBytes,
|
|
457
896
|
totalDevMb,
|
|
458
897
|
totalDevKb,
|
|
459
898
|
isHermes,
|
|
460
|
-
moduleCount:
|
|
899
|
+
moduleCount: modules.length || discoveredFiles.length + packagesList.length,
|
|
461
900
|
packageCount: packagesList.length,
|
|
462
901
|
filesCount: discoveredFiles.length,
|
|
463
902
|
splitUp: {
|
|
@@ -515,9 +954,13 @@ const parseBundleSource = (bundleText, totalBytes, scriptURL) => {
|
|
|
515
954
|
exports.parseBundleSource = parseBundleSource;
|
|
516
955
|
/**
|
|
517
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.
|
|
518
958
|
*/
|
|
519
|
-
const analyzeHostAppBundle = async () => {
|
|
520
|
-
if (
|
|
959
|
+
const analyzeHostAppBundle = async (forceRefresh = false) => {
|
|
960
|
+
if (forceRefresh) {
|
|
961
|
+
cachedAnalysis = null;
|
|
962
|
+
}
|
|
963
|
+
if (cachedAnalysis && !forceRefresh)
|
|
521
964
|
return cachedAnalysis;
|
|
522
965
|
if (isAnalyzing) {
|
|
523
966
|
return new Promise(resolve => {
|
|
@@ -526,30 +969,21 @@ const analyzeHostAppBundle = async () => {
|
|
|
526
969
|
}
|
|
527
970
|
isAnalyzing = true;
|
|
528
971
|
const scriptURL = (0, exports.getHostScriptURL)();
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const result = (0, exports.parseBundleSource)(bundleText, byteLength, scriptURL);
|
|
540
|
-
cachedAnalysis = result;
|
|
541
|
-
isAnalyzing = false;
|
|
542
|
-
subscribers.forEach(cb => cb(result));
|
|
543
|
-
subscribers.length = 0;
|
|
544
|
-
return result;
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
|
-
catch (err) {
|
|
548
|
-
// If fetch failed (e.g. standalone production build or offline)
|
|
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);
|
|
977
|
+
cachedAnalysis = result;
|
|
978
|
+
isAnalyzing = false;
|
|
979
|
+
subscribers.forEach(cb => cb(result));
|
|
980
|
+
subscribers.length = 0;
|
|
981
|
+
return result;
|
|
549
982
|
}
|
|
550
|
-
// Fallback
|
|
983
|
+
// Fallback: could not reach a Metro dev server (release build, offline, or
|
|
984
|
+
// custom dev-server host). Return estimated values so the UI never crashes.
|
|
551
985
|
const fallbackBytes = 6840000; // ~6.8MB standard RN dev bundle
|
|
552
|
-
const result = (0, exports.parseBundleSource)('', fallbackBytes, scriptURL);
|
|
986
|
+
const result = (0, exports.parseBundleSource)('', fallbackBytes, scriptURL || 'unknown', false);
|
|
553
987
|
cachedAnalysis = result;
|
|
554
988
|
isAnalyzing = false;
|
|
555
989
|
subscribers.forEach(cb => cb(result));
|
|
@@ -557,5 +991,9 @@ const analyzeHostAppBundle = async () => {
|
|
|
557
991
|
return result;
|
|
558
992
|
};
|
|
559
993
|
exports.analyzeHostAppBundle = analyzeHostAppBundle;
|
|
994
|
+
const clearCachedBundleAnalysis = () => {
|
|
995
|
+
cachedAnalysis = null;
|
|
996
|
+
};
|
|
997
|
+
exports.clearCachedBundleAnalysis = clearCachedBundleAnalysis;
|
|
560
998
|
const getCachedBundleAnalysis = () => cachedAnalysis;
|
|
561
999
|
exports.getCachedBundleAnalysis = getCachedBundleAnalysis;
|