react-native-bundle-discovery 2.7.0-rc.0 → 2.7.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.
Files changed (3) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/dist/index.js +956 -602
  3. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -7,19 +7,33 @@ var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __getProtoOf = Object.getPrototypeOf;
8
8
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
9
  var __copyProps = (to, from, except, desc) => {
10
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
- key = keys[i];
12
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
- get: ((k) => from[k]).bind(null, key),
14
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
- });
16
- }
17
- return to;
10
+ if ((from && typeof from === "object") || typeof from === "function")
11
+ for (
12
+ var keys = __getOwnPropNames(from), i = 0, n = keys.length, key;
13
+ i < n;
14
+ i++
15
+ ) {
16
+ key = keys[i];
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, {
19
+ get: ((k) => from[k]).bind(null, key),
20
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable,
21
+ });
22
+ }
23
+ return to;
18
24
  };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
20
- value: mod,
21
- enumerable: true
22
- }) : target, mod));
25
+ var __toESM = (mod, isNodeMode, target) => (
26
+ (target = mod != null ? __create(__getProtoOf(mod)) : {}),
27
+ __copyProps(
28
+ isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default")
29
+ ? __defProp(target, "default", {
30
+ value: mod,
31
+ enumerable: true,
32
+ })
33
+ : target,
34
+ mod,
35
+ )
36
+ );
23
37
  //#endregion
24
38
  let node_buffer = require("node:buffer");
25
39
  let node_fs = require("node:fs");
@@ -41,689 +55,1029 @@ const DEFAULT_TIMEOUT_MS = 5e3;
41
55
  const DEFAULT_CONCURRENCY = 5;
42
56
  const cache = /* @__PURE__ */ new Map();
43
57
  /**
44
- * `@babel/runtime` -> `@babel%2fruntime`
45
- */
58
+ * `@babel/runtime` -> `@babel%2fruntime`
59
+ */
46
60
  function encodePackageName(name) {
47
- return name.startsWith("@") ? `@${encodeURIComponent(name.slice(1))}` : name;
61
+ return name.startsWith("@") ? `@${encodeURIComponent(name.slice(1))}` : name;
48
62
  }
49
63
  async function fetchJson(url, timeoutMs) {
50
- const res = await fetch(url, {
51
- headers: { Accept: "application/json" },
52
- signal: AbortSignal.timeout(timeoutMs)
53
- });
54
- if (res.status === 404) return null;
55
- if (!res.ok) throw new Error(`${res.status} ${res.statusText} (${url})`);
56
- return res.json();
64
+ const res = await fetch(url, {
65
+ headers: { Accept: "application/json" },
66
+ signal: AbortSignal.timeout(timeoutMs),
67
+ });
68
+ if (res.status === 404) return null;
69
+ if (!res.ok) throw new Error(`${res.status} ${res.statusText} (${url})`);
70
+ return res.json();
57
71
  }
58
72
  /**
59
- * The version manifest (`/<name>/<version>`) has no publish date,
60
- * so the packument is fetched once and only the fields we need are kept.
61
- */
73
+ * The version manifest (`/<name>/<version>`) has no publish date,
74
+ * so the packument is fetched once and only the fields we need are kept.
75
+ */
62
76
  async function fetchPackageSummary(name, { registry, timeoutMs }) {
63
- const packument = await fetchJson(`${registry}/${encodePackageName(name)}`, timeoutMs);
64
- if (!packument) return null;
65
- const versions = {};
66
- for (const [version, manifest] of Object.entries(packument.versions ?? {})) versions[version] = {
67
- createdAt: packument.time?.[version] ?? null,
68
- deprecated: manifest.deprecated || false
69
- };
70
- return {
71
- latestVersion: packument["dist-tags"]?.latest ?? null,
72
- versions
73
- };
77
+ const packument = await fetchJson(
78
+ `${registry}/${encodePackageName(name)}`,
79
+ timeoutMs,
80
+ );
81
+ if (!packument) return null;
82
+ const versions = {};
83
+ for (const [version, manifest] of Object.entries(packument.versions ?? {}))
84
+ versions[version] = {
85
+ createdAt: packument.time?.[version] ?? null,
86
+ deprecated: manifest.deprecated || false,
87
+ };
88
+ return {
89
+ latestVersion: packument["dist-tags"]?.latest ?? null,
90
+ versions,
91
+ };
74
92
  }
75
93
  function getPackageSummary(name, { registry, timeoutMs }) {
76
- const key = `${registry}|${name}`;
77
- let promise = cache.get(key);
78
- if (!promise) {
79
- promise = fetchPackageSummary(name, {
80
- registry,
81
- timeoutMs
82
- }).catch((error) => {
83
- cache.delete(key);
84
- throw error;
85
- });
86
- cache.set(key, promise);
87
- }
88
- return promise;
94
+ const key = `${registry}|${name}`;
95
+ let promise = cache.get(key);
96
+ if (!promise) {
97
+ promise = fetchPackageSummary(name, {
98
+ registry,
99
+ timeoutMs,
100
+ }).catch((error) => {
101
+ cache.delete(key);
102
+ throw error;
103
+ });
104
+ cache.set(key, promise);
105
+ }
106
+ return promise;
89
107
  }
90
108
  /**
91
- * Returns metadata of a specific (installed) package version, not the latest one.
92
- *
93
- * @returns `null` when the package/version is not found in the registry.
94
- */
95
- async function getPackageMetadata(name, version, { registry = DEFAULT_REGISTRY, timeoutMs = DEFAULT_TIMEOUT_MS } = {}) {
96
- const summary = await getPackageSummary(name, {
97
- registry,
98
- timeoutMs
99
- });
100
- const info = summary?.versions[version];
101
- if (!summary || !info) return null;
102
- return {
103
- createdAt: info.createdAt,
104
- deprecated: info.deprecated,
105
- isLatest: summary.latestVersion === version,
106
- latestVersion: summary.latestVersion
107
- };
109
+ * Returns metadata of a specific (installed) package version, not the latest one.
110
+ *
111
+ * @returns `null` when the package/version is not found in the registry.
112
+ */
113
+ async function getPackageMetadata(
114
+ name,
115
+ version,
116
+ { registry = DEFAULT_REGISTRY, timeoutMs = DEFAULT_TIMEOUT_MS } = {},
117
+ ) {
118
+ const summary = await getPackageSummary(name, {
119
+ registry,
120
+ timeoutMs,
121
+ });
122
+ const info = summary?.versions[version];
123
+ if (!summary || !info) return null;
124
+ return {
125
+ createdAt: info.createdAt,
126
+ deprecated: info.deprecated,
127
+ isLatest: summary.latestVersion === version,
128
+ latestVersion: summary.latestVersion,
129
+ };
108
130
  }
109
131
  async function mapWithConcurrency(items, limit, fn) {
110
- const results = new Array(items.length);
111
- let index = 0;
112
- async function worker() {
113
- while (index < items.length) {
114
- const i = index++;
115
- results[i] = await fn(items[i], i);
116
- }
117
- }
118
- await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
119
- return results;
132
+ const results = new Array(items.length);
133
+ let index = 0;
134
+ async function worker() {
135
+ while (index < items.length) {
136
+ const i = index++;
137
+ results[i] = await fn(items[i], i);
138
+ }
139
+ }
140
+ await Promise.all(
141
+ Array.from({ length: Math.min(limit, items.length) }, worker),
142
+ );
143
+ return results;
120
144
  }
121
145
  /**
122
- * Adds a `metadata` field to every package of the report.
123
- * Failed lookups result in `metadata: null` and are passed to `onError`.
124
- */
125
- async function withPackagesMetadata(packages, { registry = DEFAULT_REGISTRY, timeoutMs = DEFAULT_TIMEOUT_MS, concurrency = DEFAULT_CONCURRENCY, onError = () => {} } = {}) {
126
- return mapWithConcurrency(packages, concurrency, async (pkg) => {
127
- if (!pkg.version) return {
128
- ...pkg,
129
- metadata: null
130
- };
131
- try {
132
- const metadata = await getPackageMetadata(pkg.name, pkg.version, {
133
- registry,
134
- timeoutMs
135
- });
136
- return {
137
- ...pkg,
138
- metadata
139
- };
140
- } catch (error) {
141
- onError(error, pkg);
142
- return {
143
- ...pkg,
144
- metadata: null
145
- };
146
- }
147
- });
146
+ * Adds a `metadata` field to every package of the report.
147
+ * Failed lookups result in `metadata: null` and are passed to `onError`.
148
+ */
149
+ async function withPackagesMetadata(
150
+ packages,
151
+ {
152
+ registry = DEFAULT_REGISTRY,
153
+ timeoutMs = DEFAULT_TIMEOUT_MS,
154
+ concurrency = DEFAULT_CONCURRENCY,
155
+ onError = () => {},
156
+ } = {},
157
+ ) {
158
+ return mapWithConcurrency(packages, concurrency, async (pkg) => {
159
+ if (!pkg.version)
160
+ return {
161
+ ...pkg,
162
+ metadata: null,
163
+ };
164
+ try {
165
+ const metadata = await getPackageMetadata(pkg.name, pkg.version, {
166
+ registry,
167
+ timeoutMs,
168
+ });
169
+ return {
170
+ ...pkg,
171
+ metadata,
172
+ };
173
+ } catch (error) {
174
+ onError(error, pkg);
175
+ return {
176
+ ...pkg,
177
+ metadata: null,
178
+ };
179
+ }
180
+ });
148
181
  }
149
182
  //#endregion
150
183
  //#region src/parseUtils.ts
151
184
  function isNumericId(node) {
152
- return node.type === "Literal" && node.value !== null && node.value !== void 0 && Number.isInteger(node.value) && node.value >= 0;
185
+ return (
186
+ node.type === "Literal" &&
187
+ node.value !== null &&
188
+ node.value !== void 0 &&
189
+ Number.isInteger(node.value) &&
190
+ node.value >= 0
191
+ );
153
192
  }
154
193
  function isModuleId(node) {
155
- return node !== null && node.type === "Literal" && (isNumericId(node) || typeof node.value === "string");
194
+ return (
195
+ node !== null &&
196
+ node.type === "Literal" &&
197
+ (isNumericId(node) || typeof node.value === "string")
198
+ );
156
199
  }
157
200
  function isModuleWrapper(node) {
158
- return (node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && !node.id || isModuleId(node) || node.type === "ArrayExpression" && node.elements.length > 1 && isModuleId(node.elements[0]);
201
+ return (
202
+ ((node.type === "FunctionExpression" ||
203
+ node.type === "ArrowFunctionExpression") &&
204
+ !node.id) ||
205
+ isModuleId(node) ||
206
+ (node.type === "ArrayExpression" &&
207
+ node.elements.length > 1 &&
208
+ isModuleId(node.elements[0]))
209
+ );
159
210
  }
160
211
  function isModulesHash(node) {
161
- return node !== null && node.type === "ObjectExpression" && node.properties.filter((property) => property.type !== "SpreadElement").map((node) => node.value).every(isModuleWrapper);
212
+ return (
213
+ node !== null &&
214
+ node.type === "ObjectExpression" &&
215
+ node.properties
216
+ .filter((property) => property.type !== "SpreadElement")
217
+ .map((node) => node.value)
218
+ .every(isModuleWrapper)
219
+ );
162
220
  }
163
221
  function isModulesArray(node) {
164
- return node !== null && node.type === "ArrayExpression" && node.elements.every((elem) => !elem || isModuleWrapper(elem));
222
+ return (
223
+ node !== null &&
224
+ node.type === "ArrayExpression" &&
225
+ node.elements.every((elem) => !elem || isModuleWrapper(elem))
226
+ );
165
227
  }
166
228
  function isSimpleModulesList(node) {
167
- return isModulesHash(node) || isModulesArray(node);
229
+ return isModulesHash(node) || isModulesArray(node);
168
230
  }
169
231
  function isOptimizedModulesArray(node) {
170
- return node !== null && node.type === "CallExpression" && node.callee.type === "MemberExpression" && node.callee.object.type === "CallExpression" && node.callee.object.callee.type === "Identifier" && node.callee.object.callee.name === "Array" && node.callee.object.arguments.length === 1 && node.callee.object.arguments[0].type !== "SpreadElement" && isNumericId(node.callee.object.arguments[0]) && node.callee.property.type === "Identifier" && node.callee.property.name === "concat" && node.arguments.length === 1 && isModulesArray(node.arguments[0]);
232
+ return (
233
+ node !== null &&
234
+ node.type === "CallExpression" &&
235
+ node.callee.type === "MemberExpression" &&
236
+ node.callee.object.type === "CallExpression" &&
237
+ node.callee.object.callee.type === "Identifier" &&
238
+ node.callee.object.callee.name === "Array" &&
239
+ node.callee.object.arguments.length === 1 &&
240
+ node.callee.object.arguments[0].type !== "SpreadElement" &&
241
+ isNumericId(node.callee.object.arguments[0]) &&
242
+ node.callee.property.type === "Identifier" &&
243
+ node.callee.property.name === "concat" &&
244
+ node.arguments.length === 1 &&
245
+ isModulesArray(node.arguments[0])
246
+ );
171
247
  }
172
248
  function isModulesList(node) {
173
- return isSimpleModulesList(node) || isOptimizedModulesArray(node);
249
+ return isSimpleModulesList(node) || isOptimizedModulesArray(node);
174
250
  }
175
251
  function getModuleLocation(node) {
176
- return {
177
- start: node.start,
178
- end: node.end
179
- };
252
+ return {
253
+ start: node.start,
254
+ end: node.end,
255
+ };
180
256
  }
181
257
  function getModulesLocations(node) {
182
- if (node.type === "ObjectExpression") return node.properties.reduce((result, moduleNode) => {
183
- if (moduleNode.type !== "Property") return result;
184
- const moduleId = moduleNode.key.type === "Identifier" ? moduleNode.key.name : moduleNode.key.value;
185
- if (moduleId === "undefined") return result;
186
- result[moduleId] = getModuleLocation(moduleNode.value);
187
- return result;
188
- }, {});
189
- const isOptimizedArray = node.type === "CallExpression";
190
- if (node.type === "ArrayExpression" || isOptimizedArray) {
191
- const minId = isOptimizedArray && node.callee.type === "MemberExpression" && node.callee.object.type === "CallExpression" && node.callee.object.arguments[0].type === "Literal" ? node.callee.object.arguments[0].value : 0;
192
- return (isOptimizedArray ? node.arguments[0].type === "ArrayExpression" ? node.arguments[0].elements : [] : node.elements).reduce((result, moduleNode, i) => {
193
- if (moduleNode) result[i + minId] = getModuleLocation(moduleNode);
194
- return result;
195
- }, {});
196
- }
197
- return {};
258
+ if (node.type === "ObjectExpression")
259
+ return node.properties.reduce((result, moduleNode) => {
260
+ if (moduleNode.type !== "Property") return result;
261
+ const moduleId =
262
+ moduleNode.key.type === "Identifier"
263
+ ? moduleNode.key.name
264
+ : moduleNode.key.value;
265
+ if (moduleId === "undefined") return result;
266
+ result[moduleId] = getModuleLocation(moduleNode.value);
267
+ return result;
268
+ }, {});
269
+ const isOptimizedArray = node.type === "CallExpression";
270
+ if (node.type === "ArrayExpression" || isOptimizedArray) {
271
+ const minId =
272
+ isOptimizedArray &&
273
+ node.callee.type === "MemberExpression" &&
274
+ node.callee.object.type === "CallExpression" &&
275
+ node.callee.object.arguments[0].type === "Literal"
276
+ ? node.callee.object.arguments[0].value
277
+ : 0;
278
+ return (
279
+ isOptimizedArray
280
+ ? node.arguments[0].type === "ArrayExpression"
281
+ ? node.arguments[0].elements
282
+ : []
283
+ : node.elements
284
+ ).reduce((result, moduleNode, i) => {
285
+ if (moduleNode) result[i + minId] = getModuleLocation(moduleNode);
286
+ return result;
287
+ }, {});
288
+ }
289
+ return {};
198
290
  }
199
291
  function getIIFECallExpression(node) {
200
- if (node.expression.type === "CallExpression") return node.expression;
201
- if (node.expression.type === "UnaryExpression" && node.expression.argument.type === "CallExpression") return node.expression.argument;
202
- return null;
292
+ if (node.expression.type === "CallExpression") return node.expression;
293
+ if (
294
+ node.expression.type === "UnaryExpression" &&
295
+ node.expression.argument.type === "CallExpression"
296
+ )
297
+ return node.expression.argument;
298
+ return null;
203
299
  }
204
300
  function callsModulesMap(node, modulesVariableName) {
205
- let callsModule = false;
206
- acorn_walk.simple(node, { CallExpression(callExpression) {
207
- const { callee } = callExpression;
208
- if (callee.type === "MemberExpression" && (callee.object.type === "Identifier" && callee.object.name === modulesVariableName || callee.object.type === "MemberExpression" && callee.object.object.type === "Identifier" && callee.object.object.name === modulesVariableName)) callsModule = true;
209
- } });
210
- return callsModule;
301
+ let callsModule = false;
302
+ acorn_walk.simple(node, {
303
+ CallExpression(callExpression) {
304
+ const { callee } = callExpression;
305
+ if (
306
+ callee.type === "MemberExpression" &&
307
+ ((callee.object.type === "Identifier" &&
308
+ callee.object.name === modulesVariableName) ||
309
+ (callee.object.type === "MemberExpression" &&
310
+ callee.object.object.type === "Identifier" &&
311
+ callee.object.object.name === modulesVariableName))
312
+ )
313
+ callsModule = true;
314
+ },
315
+ });
316
+ return callsModule;
211
317
  }
212
- function hasWebpackModulesRuntime(body, modulesDeclaration, modulesVariableName) {
213
- const declarationIndex = body.body.indexOf(modulesDeclaration);
214
- return body.body.slice(declarationIndex + 1).some((statement) => callsModulesMap(statement, modulesVariableName));
318
+ function hasWebpackModulesRuntime(
319
+ body,
320
+ modulesDeclaration,
321
+ modulesVariableName,
322
+ ) {
323
+ const declarationIndex = body.body.indexOf(modulesDeclaration);
324
+ return body.body
325
+ .slice(declarationIndex + 1)
326
+ .some((statement) => callsModulesMap(statement, modulesVariableName));
215
327
  }
216
328
  function getWebpack5IIFEModulesCandidate(node, isTopLevel) {
217
- if (node.arguments.length !== 0 || node.callee.type !== "FunctionExpression" && node.callee.type !== "ArrowFunctionExpression" || node.callee.params.length !== 0 || node.callee.body.type !== "BlockStatement") return null;
218
- const firstVariableDeclaration = node.callee.body.body.find((node) => node.type === "VariableDeclaration");
219
- if (firstVariableDeclaration) {
220
- for (const declaration of firstVariableDeclaration.declarations) if (declaration.init && isModulesList(declaration.init)) {
221
- const locations = getModulesLocations(declaration.init);
222
- if (Object.keys(locations).length === 0) continue;
223
- return {
224
- locations,
225
- hasWebpackRuntime: declaration.id.type === "Identifier" && hasWebpackModulesRuntime(node.callee.body, firstVariableDeclaration, declaration.id.name),
226
- isTopLevel
227
- };
228
- }
229
- }
230
- return null;
329
+ if (
330
+ node.arguments.length !== 0 ||
331
+ (node.callee.type !== "FunctionExpression" &&
332
+ node.callee.type !== "ArrowFunctionExpression") ||
333
+ node.callee.params.length !== 0 ||
334
+ node.callee.body.type !== "BlockStatement"
335
+ )
336
+ return null;
337
+ const firstVariableDeclaration = node.callee.body.body.find(
338
+ (node) => node.type === "VariableDeclaration",
339
+ );
340
+ if (firstVariableDeclaration) {
341
+ for (const declaration of firstVariableDeclaration.declarations)
342
+ if (declaration.init && isModulesList(declaration.init)) {
343
+ const locations = getModulesLocations(declaration.init);
344
+ if (Object.keys(locations).length === 0) continue;
345
+ return {
346
+ locations,
347
+ hasWebpackRuntime:
348
+ declaration.id.type === "Identifier" &&
349
+ hasWebpackModulesRuntime(
350
+ node.callee.body,
351
+ firstVariableDeclaration,
352
+ declaration.id.name,
353
+ ),
354
+ isTopLevel,
355
+ };
356
+ }
357
+ }
358
+ return null;
231
359
  }
232
360
  function selectWebpack5IIFECandidate(candidates) {
233
- return candidates.find((candidate) => candidate.hasWebpackRuntime && candidate.isTopLevel) || candidates.find((candidate) => candidate.hasWebpackRuntime) || candidates.find((candidate) => candidate.isTopLevel) || null;
361
+ return (
362
+ candidates.find(
363
+ (candidate) => candidate.hasWebpackRuntime && candidate.isTopLevel,
364
+ ) ||
365
+ candidates.find((candidate) => candidate.hasWebpackRuntime) ||
366
+ candidates.find((candidate) => candidate.isTopLevel) ||
367
+ null
368
+ );
234
369
  }
235
370
  function selectWebpack5IIFEModulesLocations(candidates, expectedModuleIds) {
236
- if (candidates.length === 0) return null;
237
- if (expectedModuleIds?.length) {
238
- const expectedIds = new Set(expectedModuleIds.map(String));
239
- const rankedCandidates = candidates.map((candidate) => ({
240
- candidate,
241
- expectedIdsIntersection: Object.keys(candidate.locations).filter((moduleId) => expectedIds.has(moduleId)).length
242
- })).toSorted((candidateA, candidateB) => candidateB.expectedIdsIntersection - candidateA.expectedIdsIntersection);
243
- if (rankedCandidates[0].expectedIdsIntersection > 0) return selectWebpack5IIFECandidate(rankedCandidates.filter((rankedCandidate) => rankedCandidate.expectedIdsIntersection === rankedCandidates[0].expectedIdsIntersection).map((rankedCandidate) => rankedCandidate.candidate))?.locations || null;
244
- }
245
- return selectWebpack5IIFECandidate(candidates)?.locations || null;
371
+ if (candidates.length === 0) return null;
372
+ if (expectedModuleIds?.length) {
373
+ const expectedIds = new Set(expectedModuleIds.map(String));
374
+ const rankedCandidates = candidates
375
+ .map((candidate) => ({
376
+ candidate,
377
+ expectedIdsIntersection: Object.keys(candidate.locations).filter(
378
+ (moduleId) => expectedIds.has(moduleId),
379
+ ).length,
380
+ }))
381
+ .toSorted(
382
+ (candidateA, candidateB) =>
383
+ candidateB.expectedIdsIntersection -
384
+ candidateA.expectedIdsIntersection,
385
+ );
386
+ if (rankedCandidates[0].expectedIdsIntersection > 0)
387
+ return (
388
+ selectWebpack5IIFECandidate(
389
+ rankedCandidates
390
+ .filter(
391
+ (rankedCandidate) =>
392
+ rankedCandidate.expectedIdsIntersection ===
393
+ rankedCandidates[0].expectedIdsIntersection,
394
+ )
395
+ .map((rankedCandidate) => rankedCandidate.candidate),
396
+ )?.locations || null
397
+ );
398
+ }
399
+ return selectWebpack5IIFECandidate(candidates)?.locations || null;
246
400
  }
247
401
  function isChunkIds(node) {
248
- return node.type === "ArrayExpression" && node.elements.every(isModuleId);
402
+ return node.type === "ArrayExpression" && node.elements.every(isModuleId);
249
403
  }
250
404
  function mayBeAsyncChunkArguments(args) {
251
- return args.length >= 2 && args[0] !== null && args[0].type !== "SpreadElement" && isChunkIds(args[0]);
405
+ return (
406
+ args.length >= 2 &&
407
+ args[0] !== null &&
408
+ args[0].type !== "SpreadElement" &&
409
+ isChunkIds(args[0])
410
+ );
252
411
  }
253
412
  /** Returns bundle source except modules */
254
413
  function getBundleRuntime(content, modulesLocations) {
255
- const sortedLocations = Object.values(modulesLocations || {}).toSorted((a, b) => a.start - b.start);
256
- let result = "";
257
- let lastIndex = 0;
258
- for (const { start, end } of sortedLocations) {
259
- result += content.slice(lastIndex, start);
260
- lastIndex = end;
261
- }
262
- return result + content.slice(lastIndex);
414
+ const sortedLocations = Object.values(modulesLocations || {}).toSorted(
415
+ (a, b) => a.start - b.start,
416
+ );
417
+ let result = "";
418
+ let lastIndex = 0;
419
+ for (const { start, end } of sortedLocations) {
420
+ result += content.slice(lastIndex, start);
421
+ lastIndex = end;
422
+ }
423
+ return result + content.slice(lastIndex);
263
424
  }
264
425
  function isAsyncChunkPushExpression(node) {
265
- const { callee, arguments: args } = node;
266
- return callee.type === "MemberExpression" && callee.property.type === "Identifier" && callee.property.name === "push" && callee.object.type === "AssignmentExpression" && args.length === 1 && args[0].type === "ArrayExpression" && mayBeAsyncChunkArguments(args[0].elements) && isModulesList(args[0].elements[1]);
426
+ const { callee, arguments: args } = node;
427
+ return (
428
+ callee.type === "MemberExpression" &&
429
+ callee.property.type === "Identifier" &&
430
+ callee.property.name === "push" &&
431
+ callee.object.type === "AssignmentExpression" &&
432
+ args.length === 1 &&
433
+ args[0].type === "ArrayExpression" &&
434
+ mayBeAsyncChunkArguments(args[0].elements) &&
435
+ isModulesList(args[0].elements[1])
436
+ );
267
437
  }
268
438
  function isAsyncWebWorkerChunkExpression(node) {
269
- const { callee, type, arguments: args } = node;
270
- return type === "CallExpression" && callee.type === "MemberExpression" && args.length === 2 && args[0].type !== "SpreadElement" && isChunkIds(args[0]) && isModulesList(args[1]);
439
+ const { callee, type, arguments: args } = node;
440
+ return (
441
+ type === "CallExpression" &&
442
+ callee.type === "MemberExpression" &&
443
+ args.length === 2 &&
444
+ args[0].type !== "SpreadElement" &&
445
+ isChunkIds(args[0]) &&
446
+ isModulesList(args[1])
447
+ );
271
448
  }
272
449
  function parseBundle(bundlePath, opts) {
273
- const { sourceType = "script", expectedModuleIds } = opts || {};
274
- const content = node_fs.default.readFileSync(bundlePath, "utf8");
275
- const ast = acorn.parse(content, {
276
- sourceType,
277
- ecmaVersion: "latest"
278
- });
279
- const topLevelIIFECalls = /* @__PURE__ */ new Set();
280
- for (const node of ast.body) if (node.type === "ExpressionStatement") {
281
- const iifeCall = getIIFECallExpression(node);
282
- if (iifeCall) topLevelIIFECalls.add(iifeCall);
283
- }
284
- const walkState = {
285
- locations: null,
286
- webpack5IIFECandidates: []
287
- };
288
- acorn_walk.recursive(ast, walkState, {
289
- AssignmentExpression(node, state) {
290
- if (state.locations) return;
291
- const { left, right } = node;
292
- if (left && left.type === "MemberExpression" && left.object && left.object.type === "Identifier" && left.object.name === "exports" && left.property && left.property.type === "Identifier" && left.property.name === "modules" && isModulesHash(right)) state.locations = getModulesLocations(right);
293
- },
294
- CallExpression(node, state, callback) {
295
- if (state.locations) return;
296
- const args = node.arguments;
297
- const webpack5IIFEModulesCandidate = getWebpack5IIFEModulesCandidate(node, topLevelIIFECalls.has(node));
298
- if (webpack5IIFEModulesCandidate) state.webpack5IIFECandidates.push(webpack5IIFEModulesCandidate);
299
- if (node.callee.type === "FunctionExpression" && !node.callee.id && args.length === 1 && isSimpleModulesList(args[0])) {
300
- state.locations = getModulesLocations(args[0]);
301
- return;
302
- }
303
- // webpackJsonp([<chunks>], <modules>, ...)
304
- if (node.callee.type === "Identifier" && mayBeAsyncChunkArguments(args) && args[1].type !== "SpreadElement" && isModulesList(args[1])) {
305
- state.locations = getModulesLocations(args[1]);
306
- return;
307
- }
308
- if (isAsyncChunkPushExpression(node) && args[0].type === "ArrayExpression" && args[0].elements[1]) {
309
- state.locations = getModulesLocations(args[0].elements[1]);
310
- return;
311
- }
312
- if (isAsyncWebWorkerChunkExpression(node)) {
313
- state.locations = getModulesLocations(args[1]);
314
- return;
315
- }
316
- for (const arg of args) callback(arg, state);
317
- }
318
- });
319
- const modulesLocations = walkState.locations || selectWebpack5IIFEModulesLocations(walkState.webpack5IIFECandidates, expectedModuleIds);
320
- const modules = {};
321
- if (modulesLocations) for (const [id, loc] of Object.entries(modulesLocations)) modules[id] = content.slice(loc.start, loc.end);
322
- return {
323
- modules,
324
- src: content,
325
- runtimeSrc: getBundleRuntime(content, modulesLocations)
326
- };
450
+ const { sourceType = "script", expectedModuleIds } = opts || {};
451
+ const content = node_fs.default.readFileSync(bundlePath, "utf8");
452
+ const ast = acorn.parse(content, {
453
+ sourceType,
454
+ ecmaVersion: "latest",
455
+ });
456
+ const topLevelIIFECalls = /* @__PURE__ */ new Set();
457
+ for (const node of ast.body)
458
+ if (node.type === "ExpressionStatement") {
459
+ const iifeCall = getIIFECallExpression(node);
460
+ if (iifeCall) topLevelIIFECalls.add(iifeCall);
461
+ }
462
+ const walkState = {
463
+ locations: null,
464
+ webpack5IIFECandidates: [],
465
+ };
466
+ acorn_walk.recursive(ast, walkState, {
467
+ AssignmentExpression(node, state) {
468
+ if (state.locations) return;
469
+ const { left, right } = node;
470
+ if (
471
+ left &&
472
+ left.type === "MemberExpression" &&
473
+ left.object &&
474
+ left.object.type === "Identifier" &&
475
+ left.object.name === "exports" &&
476
+ left.property &&
477
+ left.property.type === "Identifier" &&
478
+ left.property.name === "modules" &&
479
+ isModulesHash(right)
480
+ )
481
+ state.locations = getModulesLocations(right);
482
+ },
483
+ CallExpression(node, state, callback) {
484
+ if (state.locations) return;
485
+ const args = node.arguments;
486
+ const webpack5IIFEModulesCandidate = getWebpack5IIFEModulesCandidate(
487
+ node,
488
+ topLevelIIFECalls.has(node),
489
+ );
490
+ if (webpack5IIFEModulesCandidate)
491
+ state.webpack5IIFECandidates.push(webpack5IIFEModulesCandidate);
492
+ if (
493
+ node.callee.type === "FunctionExpression" &&
494
+ !node.callee.id &&
495
+ args.length === 1 &&
496
+ isSimpleModulesList(args[0])
497
+ ) {
498
+ state.locations = getModulesLocations(args[0]);
499
+ return;
500
+ }
501
+ // webpackJsonp([<chunks>], <modules>, ...)
502
+ if (
503
+ node.callee.type === "Identifier" &&
504
+ mayBeAsyncChunkArguments(args) &&
505
+ args[1].type !== "SpreadElement" &&
506
+ isModulesList(args[1])
507
+ ) {
508
+ state.locations = getModulesLocations(args[1]);
509
+ return;
510
+ }
511
+ if (
512
+ isAsyncChunkPushExpression(node) &&
513
+ args[0].type === "ArrayExpression" &&
514
+ args[0].elements[1]
515
+ ) {
516
+ state.locations = getModulesLocations(args[0].elements[1]);
517
+ return;
518
+ }
519
+ if (isAsyncWebWorkerChunkExpression(node)) {
520
+ state.locations = getModulesLocations(args[1]);
521
+ return;
522
+ }
523
+ for (const arg of args) callback(arg, state);
524
+ },
525
+ });
526
+ const modulesLocations =
527
+ walkState.locations ||
528
+ selectWebpack5IIFEModulesLocations(
529
+ walkState.webpack5IIFECandidates,
530
+ expectedModuleIds,
531
+ );
532
+ const modules = {};
533
+ if (modulesLocations)
534
+ for (const [id, loc] of Object.entries(modulesLocations))
535
+ modules[id] = content.slice(loc.start, loc.end);
536
+ return {
537
+ modules,
538
+ src: content,
539
+ runtimeSrc: getBundleRuntime(content, modulesLocations),
540
+ };
327
541
  }
328
542
  //#endregion
329
543
  //#region src/webpack.ts
330
544
  const NAME$1 = name;
331
545
  const packageRegex = /(?:^|\/)node_modules\/((?:@[^/]+\/)?[^/]+)/g;
332
546
  /**
333
- * Simple Webpack/Rspack plugin
334
- * to adapt stats object https://webpack.js.org/api/stats/
335
- * to metro-stats.json format used by `react-native-bundle-discovery`
336
- */
547
+ * Simple Webpack/Rspack plugin
548
+ * to adapt stats object https://webpack.js.org/api/stats/
549
+ * to metro-stats.json format used by `react-native-bundle-discovery`
550
+ */
337
551
  var BundleDiscoveryPlugin = class {
338
- options;
339
- filename;
340
- enabled;
341
- fetchPackagesMetadata;
342
- constructor({ filename, options, enabled, fetchPackagesMetadata } = {}) {
343
- this.options = options;
344
- this.filename = filename || "metro-stats.json";
345
- this.enabled = enabled ?? true;
346
- this.fetchPackagesMetadata = fetchPackagesMetadata ?? true;
347
- }
348
- extractSizesFromJsBundle(statAsset, options) {
349
- const assetFile = node_path.default.join(options.output.path, statAsset.name);
350
- try {
351
- return parseBundle(assetFile, { sourceType: statAsset.info.javascriptModule ? "module" : "script" });
352
- } catch {
353
- return null;
354
- }
355
- }
356
- apply(compiler) {
357
- if (!this.enabled) return;
358
- compiler.hooks.done.tap("GenerateStatsJsonPlugin", (stats) => {
359
- const json = stats.toJson({
360
- version: true,
361
- builtAt: true,
362
- modules: true,
363
- reasons: true,
364
- assets: true,
365
- ids: true,
366
- ...this.options,
367
- all: false,
368
- source: true
369
- });
370
- const statsJson = this.formatStat(json, this.extractSizesFromJsBundle(json.assets[0], compiler.options), compiler.options);
371
- const outputPath = node_path.default.resolve(compiler.options.context, this.filename);
372
- const keepAlive = setInterval(() => {}, 1e3);
373
- this.writeReport(statsJson, outputPath).catch((error) => {
374
- console.error(`${chalk.default.yellow(`[${NAME$1}]`)}: Failed to create JSON report: ${error.message}`);
375
- }).finally(() => clearInterval(keepAlive));
376
- });
377
- }
378
- async writeReport(statsJson, outputPath) {
379
- if (this.fetchPackagesMetadata) statsJson.packages = await withPackagesMetadata(statsJson.packages, { onError: (error, pkg) => {
380
- console.warn(`${chalk.default.yellow(`[${NAME$1}]`)}: Failed to fetch metadata for ${pkg.name}@${pkg.version}: ${error.message}`);
381
- } });
382
- node_fs.default.writeFileSync(outputPath, JSON.stringify(statsJson, null, 2));
383
- console.log(`${chalk.default.yellow(`[${NAME$1}]`)}: Saved stats to ${chalk.default.green(outputPath)}`);
384
- }
385
- readSource(filePath) {
386
- const fallback = {
387
- code: "",
388
- lineCount: 0,
389
- sizeInBytes: 0
390
- };
391
- try {
392
- const code = node_fs.default.readFileSync(filePath, "utf-8");
393
- const lineCount = code.split("\n").length;
394
- const sizeInBytes = Buffer.byteLength(code, "utf-8");
395
- return {
396
- code: this.options?.source === false ? "" : code,
397
- lineCount,
398
- sizeInBytes
399
- };
400
- } catch {
401
- return fallback;
402
- }
403
- }
404
- getPkgVersionFromPath(path) {
405
- try {
406
- const matches = [...path.matchAll(/\.pnpm\/([^/]+)\/node_modules/g)];
407
- if (!matches.length) return null;
408
- const versionMatch = matches[matches.length - 1][1].match(/^@?[^@]+@([^_]+)/);
409
- return versionMatch ? versionMatch[1] : null;
410
- } catch {
411
- return "";
412
- }
413
- }
414
- getPackages(statsJson) {
415
- const packagesMap = /* @__PURE__ */ new Map();
416
- for (const mdl of statsJson.modules) {
417
- const filePath = mdl.nameForCondition;
418
- if (!filePath) continue;
419
- let lastMatch = null;
420
- packageRegex.lastIndex = 0;
421
- let match = packageRegex.exec(filePath);
422
- while (match !== null) {
423
- lastMatch = match;
424
- match = packageRegex.exec(filePath);
425
- }
426
- if (!lastMatch) continue;
427
- const packageName = lastMatch[1];
428
- const matchIndex = lastMatch.index;
429
- const cutIndex = matchIndex + (filePath[matchIndex] === "/" ? 1 : 0) + 13 + packageName.length;
430
- const absolutePath = filePath.slice(0, cutIndex);
431
- let version = "";
432
- try {
433
- version = require(node_path.default.join(absolutePath, "package.json")).version;
434
- } catch (e) {
435
- const verFromPath = this.getPkgVersionFromPath(absolutePath);
436
- if (verFromPath) version = verFromPath;
437
- else if (e?.code === "MODULE_NOT_FOUND") console.warn(`[${NAME$1}] getPackages: unexpected path resolution`, {
438
- filePath,
439
- absolutePath
440
- });
441
- else throw e;
442
- }
443
- if (!packagesMap.has(absolutePath)) packagesMap.set(absolutePath, {
444
- name: packageName,
445
- absolutePath,
446
- version
447
- });
448
- }
449
- return Array.from(packagesMap.values());
450
- }
451
- getEntryFile(statsJson, rootFolder) {
452
- const fallbackValue = node_path.default.join(rootFolder, "index.js");
453
- return statsJson.modules.find((mdl) => !mdl.nameForCondition?.includes("node_modules/") && mdl.reasons?.length === 1 && mdl.reasons?.[0].type === "entry")?.nameForCondition || fallbackValue;
454
- }
455
- getModuleOutput(mdl, bundleModules) {
456
- const jsCodeStrFromJSBundle = bundleModules?.modules?.[String(mdl.id)];
457
- if (jsCodeStrFromJSBundle) {
458
- const formatted = jsCodeStrFromJSBundle.startsWith("function(") && jsCodeStrFromJSBundle.endsWith("}") ? jsCodeStrFromJSBundle.replace("function(", "function module(") : jsCodeStrFromJSBundle;
459
- return {
460
- code: formatted,
461
- lineCount: formatted.split("\n").length ?? 0,
462
- sizeInBytes: Buffer.byteLength(jsCodeStrFromJSBundle, "utf-8")
463
- };
464
- }
465
- return {
466
- code: mdl.source ?? "",
467
- lineCount: mdl.source?.split("\n").length ?? 0,
468
- sizeInBytes: mdl.size ?? 0
469
- };
470
- }
471
- formatStat(statsJson, bundleModules, compilerOptions) {
472
- const modulesDeps = this.buildImportsMapFromReasons(statsJson, compilerOptions);
473
- const context = compilerOptions.context;
474
- return {
475
- kind: "webpack",
476
- date: statsJson.builtAt || Date.now(),
477
- entryPoint: this.getEntryFile(statsJson, context),
478
- rootFolder: this.findCommonRoot(statsJson.modules.map((m) => m.nameForCondition).filter((p) => Boolean(p))) || context,
479
- transformOptions: {
480
- dev: compilerOptions.mode !== "production",
481
- minify: compilerOptions.optimization?.minimize ?? false,
482
- platform: compilerOptions.name ?? "ios"
483
- },
484
- modules: statsJson.modules.map((mdl) => {
485
- if (mdl.type === "runtime modules") {
486
- if (!bundleModules) return null;
487
- const sourceAndOutput = {
488
- code: bundleModules.runtimeSrc ?? "",
489
- lineCount: bundleModules.runtimeSrc?.split("\n").length ?? 0,
490
- sizeInBytes: Buffer.byteLength(bundleModules.runtimeSrc ?? "", "utf-8")
491
- };
492
- return {
493
- path: "__runtime__",
494
- source: sourceAndOutput,
495
- output: sourceAndOutput,
496
- dependencies: []
497
- };
498
- }
499
- if (!mdl.nameForCondition) return null;
500
- return {
501
- path: mdl.nameForCondition,
502
- source: this.readSource(mdl.nameForCondition),
503
- output: this.getModuleOutput(mdl, bundleModules),
504
- dependencies: modulesDeps.get(mdl.nameForCondition) ?? []
505
- };
506
- }).filter((e) => e !== null && Boolean(e.path)),
507
- packages: this.getPackages(statsJson)
508
- };
509
- }
510
- buildImportsMapFromReasons(statsJson, compilerOptions) {
511
- const modules = Array.isArray(statsJson?.modules) ? statsJson.modules : [];
512
- const importsMap = /* @__PURE__ */ new Map();
513
- const getAbs = (p) => {
514
- if (!p || typeof p !== "string") return null;
515
- if (node_path.default.isAbsolute(p)) return p;
516
- return compilerOptions?.context ? node_path.default.resolve(compilerOptions?.context, p) : p;
517
- };
518
- const getModulePath = (m) => m?.path || m?.nameForCondition || m?.identifier || null;
519
- for (const depModule of modules) {
520
- const depAbsolutePath = getAbs(getModulePath(depModule));
521
- if (!depAbsolutePath) continue;
522
- const reasons = Array.isArray(depModule.reasons) ? depModule.reasons : [];
523
- for (const reason of reasons) {
524
- if (reason?.type === "entry") continue;
525
- const importerPath = getAbs(reason?.modulePath || reason?.moduleNameForCondition || reason?.resolvedModule || reason?.moduleIdentifier);
526
- if (!importerPath) continue;
527
- let deps = importsMap.get(importerPath);
528
- if (!deps) {
529
- deps = [];
530
- importsMap.set(importerPath, deps);
531
- }
532
- if (!deps.some((d) => d.absolutePath === depAbsolutePath)) deps.push({
533
- absolutePath: depAbsolutePath,
534
- name: reason?.userRequest || ""
535
- });
536
- }
537
- }
538
- return importsMap;
539
- }
540
- findCommonRoot(paths) {
541
- if (!paths || paths.length === 0) return "";
542
- if (paths.length === 1) {
543
- const lastSlash = paths[0].lastIndexOf("/");
544
- return lastSlash === -1 ? "" : paths[0].slice(0, lastSlash);
545
- }
546
- const first = paths[0];
547
- let maxLen = first.length;
548
- for (let i = 1; i < paths.length; i++) {
549
- const current = paths[i];
550
- const limit = Math.min(maxLen, current.length);
551
- let j = 0;
552
- while (j < limit && first.charCodeAt(j) === current.charCodeAt(j)) j++;
553
- maxLen = j;
554
- if (maxLen === 0) return "";
555
- }
556
- if (maxLen === first.length) {
557
- const lastSlash = first.lastIndexOf("/");
558
- return lastSlash === -1 ? "" : first.slice(0, lastSlash);
559
- }
560
- const commonSlice = first.slice(0, maxLen);
561
- const lastSlash = commonSlice.lastIndexOf("/");
562
- return lastSlash === -1 ? "" : commonSlice.slice(0, lastSlash);
563
- }
552
+ options;
553
+ filename;
554
+ enabled;
555
+ fetchPackagesMetadata;
556
+ constructor({ filename, options, enabled, fetchPackagesMetadata } = {}) {
557
+ this.options = options;
558
+ this.filename = filename || "metro-stats.json";
559
+ this.enabled = enabled ?? true;
560
+ this.fetchPackagesMetadata = fetchPackagesMetadata ?? true;
561
+ }
562
+ extractSizesFromJsBundle(statAsset, options) {
563
+ const assetFile = node_path.default.join(
564
+ options.output.path,
565
+ statAsset.name,
566
+ );
567
+ try {
568
+ return parseBundle(assetFile, {
569
+ sourceType: statAsset.info.javascriptModule ? "module" : "script",
570
+ });
571
+ } catch {
572
+ return null;
573
+ }
574
+ }
575
+ apply(compiler) {
576
+ if (!this.enabled) return;
577
+ compiler.hooks.done.tap("GenerateStatsJsonPlugin", (stats) => {
578
+ const json = stats.toJson({
579
+ version: true,
580
+ builtAt: true,
581
+ modules: true,
582
+ reasons: true,
583
+ assets: true,
584
+ ids: true,
585
+ ...this.options,
586
+ all: false,
587
+ source: true,
588
+ });
589
+ const statsJson = this.formatStat(
590
+ json,
591
+ this.extractSizesFromJsBundle(json.assets[0], compiler.options),
592
+ compiler.options,
593
+ );
594
+ const outputPath = node_path.default.resolve(
595
+ compiler.options.context,
596
+ this.filename,
597
+ );
598
+ const keepAlive = setInterval(() => {}, 1e3);
599
+ this.writeReport(statsJson, outputPath)
600
+ .catch((error) => {
601
+ console.error(
602
+ `${chalk.default.yellow(`[${NAME$1}]`)}: Failed to create JSON report: ${error.message}`,
603
+ );
604
+ })
605
+ .finally(() => clearInterval(keepAlive));
606
+ });
607
+ }
608
+ async writeReport(statsJson, outputPath) {
609
+ if (this.fetchPackagesMetadata)
610
+ statsJson.packages = await withPackagesMetadata(statsJson.packages, {
611
+ onError: (error, pkg) => {
612
+ console.warn(
613
+ `${chalk.default.yellow(`[${NAME$1}]`)}: Failed to fetch metadata for ${pkg.name}@${pkg.version}: ${error.message}`,
614
+ );
615
+ },
616
+ });
617
+ node_fs.default.writeFileSync(
618
+ outputPath,
619
+ JSON.stringify(statsJson, null, 2),
620
+ );
621
+ console.log(
622
+ `${chalk.default.yellow(`[${NAME$1}]`)}: Saved stats to ${chalk.default.green(outputPath)}`,
623
+ );
624
+ }
625
+ readSource(filePath) {
626
+ const fallback = {
627
+ code: "",
628
+ lineCount: 0,
629
+ sizeInBytes: 0,
630
+ };
631
+ try {
632
+ const code = node_fs.default.readFileSync(filePath, "utf-8");
633
+ const lineCount = code.split("\n").length;
634
+ const sizeInBytes = Buffer.byteLength(code, "utf-8");
635
+ return {
636
+ code: this.options?.source === false ? "" : code,
637
+ lineCount,
638
+ sizeInBytes,
639
+ };
640
+ } catch {
641
+ return fallback;
642
+ }
643
+ }
644
+ getPkgVersionFromPath(path) {
645
+ try {
646
+ const matches = [...path.matchAll(/\.pnpm\/([^/]+)\/node_modules/g)];
647
+ if (!matches.length) return null;
648
+ const versionMatch =
649
+ matches[matches.length - 1][1].match(/^@?[^@]+@([^_]+)/);
650
+ return versionMatch ? versionMatch[1] : null;
651
+ } catch {
652
+ return "";
653
+ }
654
+ }
655
+ getPackages(statsJson) {
656
+ const packagesMap = /* @__PURE__ */ new Map();
657
+ for (const mdl of statsJson.modules) {
658
+ const filePath = mdl.nameForCondition;
659
+ if (!filePath) continue;
660
+ let lastMatch = null;
661
+ packageRegex.lastIndex = 0;
662
+ let match = packageRegex.exec(filePath);
663
+ while (match !== null) {
664
+ lastMatch = match;
665
+ match = packageRegex.exec(filePath);
666
+ }
667
+ if (!lastMatch) continue;
668
+ const packageName = lastMatch[1];
669
+ const matchIndex = lastMatch.index;
670
+ const cutIndex =
671
+ matchIndex +
672
+ (filePath[matchIndex] === "/" ? 1 : 0) +
673
+ 13 +
674
+ packageName.length;
675
+ const absolutePath = filePath.slice(0, cutIndex);
676
+ let version = "";
677
+ try {
678
+ version = require(
679
+ node_path.default.join(absolutePath, "package.json"),
680
+ ).version;
681
+ } catch (e) {
682
+ const verFromPath = this.getPkgVersionFromPath(absolutePath);
683
+ if (verFromPath) version = verFromPath;
684
+ else if (e?.code === "MODULE_NOT_FOUND")
685
+ console.warn(`[${NAME$1}] getPackages: unexpected path resolution`, {
686
+ filePath,
687
+ absolutePath,
688
+ });
689
+ else throw e;
690
+ }
691
+ if (!packagesMap.has(absolutePath))
692
+ packagesMap.set(absolutePath, {
693
+ name: packageName,
694
+ absolutePath,
695
+ version,
696
+ });
697
+ }
698
+ return Array.from(packagesMap.values());
699
+ }
700
+ getEntryFile(statsJson, rootFolder) {
701
+ const fallbackValue = node_path.default.join(rootFolder, "index.js");
702
+ return (
703
+ statsJson.modules.find(
704
+ (mdl) =>
705
+ !mdl.nameForCondition?.includes("node_modules/") &&
706
+ mdl.reasons?.length === 1 &&
707
+ mdl.reasons?.[0].type === "entry",
708
+ )?.nameForCondition || fallbackValue
709
+ );
710
+ }
711
+ getModuleOutput(mdl, bundleModules) {
712
+ const jsCodeStrFromJSBundle = bundleModules?.modules?.[String(mdl.id)];
713
+ if (jsCodeStrFromJSBundle) {
714
+ const formatted =
715
+ jsCodeStrFromJSBundle.startsWith("function(") &&
716
+ jsCodeStrFromJSBundle.endsWith("}")
717
+ ? jsCodeStrFromJSBundle.replace("function(", "function module(")
718
+ : jsCodeStrFromJSBundle;
719
+ return {
720
+ code: formatted,
721
+ lineCount: formatted.split("\n").length ?? 0,
722
+ sizeInBytes: Buffer.byteLength(jsCodeStrFromJSBundle, "utf-8"),
723
+ };
724
+ }
725
+ return {
726
+ code: mdl.source ?? "",
727
+ lineCount: mdl.source?.split("\n").length ?? 0,
728
+ sizeInBytes: mdl.size ?? 0,
729
+ };
730
+ }
731
+ formatStat(statsJson, bundleModules, compilerOptions) {
732
+ const modulesDeps = this.buildImportsMapFromReasons(
733
+ statsJson,
734
+ compilerOptions,
735
+ );
736
+ const context = compilerOptions.context;
737
+ return {
738
+ kind: "webpack",
739
+ date: statsJson.builtAt || Date.now(),
740
+ entryPoint: this.getEntryFile(statsJson, context),
741
+ rootFolder:
742
+ this.findCommonRoot(
743
+ statsJson.modules
744
+ .map((m) => m.nameForCondition)
745
+ .filter((p) => Boolean(p)),
746
+ ) || context,
747
+ transformOptions: {
748
+ dev: compilerOptions.mode !== "production",
749
+ minify: compilerOptions.optimization?.minimize ?? false,
750
+ platform: compilerOptions.name ?? "ios",
751
+ },
752
+ modules: statsJson.modules
753
+ .map((mdl) => {
754
+ if (mdl.type === "runtime modules") {
755
+ if (!bundleModules) return null;
756
+ const sourceAndOutput = {
757
+ code: bundleModules.runtimeSrc ?? "",
758
+ lineCount: bundleModules.runtimeSrc?.split("\n").length ?? 0,
759
+ sizeInBytes: Buffer.byteLength(
760
+ bundleModules.runtimeSrc ?? "",
761
+ "utf-8",
762
+ ),
763
+ };
764
+ return {
765
+ path: "__runtime__",
766
+ source: sourceAndOutput,
767
+ output: sourceAndOutput,
768
+ dependencies: [],
769
+ };
770
+ }
771
+ if (!mdl.nameForCondition) return null;
772
+ return {
773
+ path: mdl.nameForCondition,
774
+ source: this.readSource(mdl.nameForCondition),
775
+ output: this.getModuleOutput(mdl, bundleModules),
776
+ dependencies: modulesDeps.get(mdl.nameForCondition) ?? [],
777
+ };
778
+ })
779
+ .filter((e) => e !== null && Boolean(e.path)),
780
+ packages: this.getPackages(statsJson),
781
+ };
782
+ }
783
+ buildImportsMapFromReasons(statsJson, compilerOptions) {
784
+ const modules = Array.isArray(statsJson?.modules) ? statsJson.modules : [];
785
+ const importsMap = /* @__PURE__ */ new Map();
786
+ const getAbs = (p) => {
787
+ if (!p || typeof p !== "string") return null;
788
+ if (node_path.default.isAbsolute(p)) return p;
789
+ return compilerOptions?.context
790
+ ? node_path.default.resolve(compilerOptions?.context, p)
791
+ : p;
792
+ };
793
+ const getModulePath = (m) =>
794
+ m?.path || m?.nameForCondition || m?.identifier || null;
795
+ for (const depModule of modules) {
796
+ const depAbsolutePath = getAbs(getModulePath(depModule));
797
+ if (!depAbsolutePath) continue;
798
+ const reasons = Array.isArray(depModule.reasons) ? depModule.reasons : [];
799
+ for (const reason of reasons) {
800
+ if (reason?.type === "entry") continue;
801
+ const importerPath = getAbs(
802
+ reason?.modulePath ||
803
+ reason?.moduleNameForCondition ||
804
+ reason?.resolvedModule ||
805
+ reason?.moduleIdentifier,
806
+ );
807
+ if (!importerPath) continue;
808
+ let deps = importsMap.get(importerPath);
809
+ if (!deps) {
810
+ deps = [];
811
+ importsMap.set(importerPath, deps);
812
+ }
813
+ if (!deps.some((d) => d.absolutePath === depAbsolutePath))
814
+ deps.push({
815
+ absolutePath: depAbsolutePath,
816
+ name: reason?.userRequest || "",
817
+ });
818
+ }
819
+ }
820
+ return importsMap;
821
+ }
822
+ findCommonRoot(paths) {
823
+ if (!paths || paths.length === 0) return "";
824
+ if (paths.length === 1) {
825
+ const lastSlash = paths[0].lastIndexOf("/");
826
+ return lastSlash === -1 ? "" : paths[0].slice(0, lastSlash);
827
+ }
828
+ const first = paths[0];
829
+ let maxLen = first.length;
830
+ for (let i = 1; i < paths.length; i++) {
831
+ const current = paths[i];
832
+ const limit = Math.min(maxLen, current.length);
833
+ let j = 0;
834
+ while (j < limit && first.charCodeAt(j) === current.charCodeAt(j)) j++;
835
+ maxLen = j;
836
+ if (maxLen === 0) return "";
837
+ }
838
+ if (maxLen === first.length) {
839
+ const lastSlash = first.lastIndexOf("/");
840
+ return lastSlash === -1 ? "" : first.slice(0, lastSlash);
841
+ }
842
+ const commonSlice = first.slice(0, maxLen);
843
+ const lastSlash = commonSlice.lastIndexOf("/");
844
+ return lastSlash === -1 ? "" : commonSlice.slice(0, lastSlash);
845
+ }
564
846
  };
565
847
  //#endregion
566
848
  //#region src/index.ts
567
849
  const NAME = name;
568
850
  function getDefault(module) {
569
- return module.__esModule ? module.default : module;
851
+ return module.__esModule ? module.default : module;
570
852
  }
571
853
  function getDefaultSerializer() {
572
- const metroPath = (0, node_path.parse)(require.resolve("metro/package.json")).dir;
573
- const bundleToString = getDefault(require(`${metroPath}/src/lib/bundleToString.js`));
574
- const baseJSBundle = getDefault(require(`${metroPath}/src/DeltaBundler/Serializers/baseJSBundle.js`));
575
- return function defaultSerializer(entryPoint, preModules, graph, options) {
576
- let bundle = baseJSBundle(entryPoint, preModules, graph, options);
577
- if (typeof options?.sentryBundleCallback === "function") bundle = options.sentryBundleCallback(bundle);
578
- return bundleToString(bundle).code;
579
- };
854
+ const metroPath = (0, node_path.parse)(
855
+ require.resolve("metro/package.json"),
856
+ ).dir;
857
+ const bundleToString = getDefault(
858
+ require(`${metroPath}/src/lib/bundleToString.js`),
859
+ );
860
+ const baseJSBundle = getDefault(
861
+ require(`${metroPath}/src/DeltaBundler/Serializers/baseJSBundle.js`),
862
+ );
863
+ return function defaultSerializer(entryPoint, preModules, graph, options) {
864
+ let bundle = baseJSBundle(entryPoint, preModules, graph, options);
865
+ if (typeof options?.sentryBundleCallback === "function")
866
+ bundle = options.sentryBundleCallback(bundle);
867
+ return bundleToString(bundle).code;
868
+ };
580
869
  }
581
870
  function getStringSizeInBytes(str) {
582
- return node_buffer.Buffer.byteLength(str, "utf8");
871
+ return node_buffer.Buffer.byteLength(str, "utf8");
583
872
  }
584
873
  /**
585
- * `/Users/i/app/node_modules/metro/node_modules/@babel/runtime/helpers/createClass.js` -> `@babel/runtime`
586
- * `/Users/i/app/node_modules/react/jsx-runtime.js` -> `react`
587
- */
874
+ * `/Users/i/app/node_modules/metro/node_modules/@babel/runtime/helpers/createClass.js` -> `@babel/runtime`
875
+ * `/Users/i/app/node_modules/react/jsx-runtime.js` -> `react`
876
+ */
588
877
  function getPackageNameFromPath(path) {
589
- const parts = path.split("node_modules/");
590
- const lastPart = parts[parts.length - 1];
591
- if (lastPart.startsWith("@")) return lastPart.split("/").slice(0, 2).join("/");
592
- return lastPart.split("/")[0];
878
+ const parts = path.split("node_modules/");
879
+ const lastPart = parts[parts.length - 1];
880
+ if (lastPart.startsWith("@"))
881
+ return lastPart.split("/").slice(0, 2).join("/");
882
+ return lastPart.split("/")[0];
593
883
  }
594
884
  /**
595
- * `/Users/i/app/node_modules/metro/node_modules/@babel/runtime/helpers/createClass.js` -> `/Users/i/app/node_modules/metro/node_modules/@babel/runtime`
596
- * `/Users/i/app/node_modules/react/jsx-runtime.js` -> `/Users/i/app/node_modules/react`
597
- */
885
+ * `/Users/i/app/node_modules/metro/node_modules/@babel/runtime/helpers/createClass.js` -> `/Users/i/app/node_modules/metro/node_modules/@babel/runtime`
886
+ * `/Users/i/app/node_modules/react/jsx-runtime.js` -> `/Users/i/app/node_modules/react`
887
+ */
598
888
  function getPackageAbsolutePath(path, pkgName) {
599
- const parts = path.split("node_modules/");
600
- parts[parts.length - 1] = pkgName;
601
- return parts.join("node_modules/");
889
+ const parts = path.split("node_modules/");
890
+ parts[parts.length - 1] = pkgName;
891
+ return parts.join("node_modules/");
602
892
  }
603
893
  function toPackages(modules) {
604
- const packages = /* @__PURE__ */ new Map();
605
- modules.forEach((module) => {
606
- if (!module.path.includes("node_modules/")) return;
607
- const pkgName = getPackageNameFromPath(module.path);
608
- const absolutePkgPath = getPackageAbsolutePath(module.path, pkgName);
609
- if (!packages.has(absolutePkgPath)) packages.set(absolutePkgPath, {
610
- name: pkgName,
611
- absolutePath: absolutePkgPath,
612
- version: require((0, node_path.resolve)(absolutePkgPath, "package.json")).version
613
- });
614
- });
615
- return Array.from(packages.values()).sort((a, b) => a.name.localeCompare(b.name));
894
+ const packages = /* @__PURE__ */ new Map();
895
+ modules.forEach((module) => {
896
+ if (!module.path.includes("node_modules/")) return;
897
+ const pkgName = getPackageNameFromPath(module.path);
898
+ const absolutePkgPath = getPackageAbsolutePath(module.path, pkgName);
899
+ if (!packages.has(absolutePkgPath))
900
+ packages.set(absolutePkgPath, {
901
+ name: pkgName,
902
+ absolutePath: absolutePkgPath,
903
+ version: require(
904
+ (0, node_path.resolve)(absolutePkgPath, "package.json"),
905
+ ).version,
906
+ });
907
+ });
908
+ return Array.from(packages.values()).sort((a, b) =>
909
+ a.name.localeCompare(b.name),
910
+ );
616
911
  }
617
912
  function toModuleStruct(m, includeCode) {
618
- const sourceCode = m.getSource().toString("utf8");
619
- const outputCode = m.output[0].data.code;
620
- return {
621
- path: m.path,
622
- source: {
623
- code: includeCode ? sourceCode : "",
624
- lineCount: sourceCode.split("\n").length,
625
- sizeInBytes: getStringSizeInBytes(sourceCode)
626
- },
627
- output: {
628
- code: includeCode ? outputCode : "",
629
- lineCount: m.output[0].data.lineCount,
630
- sizeInBytes: getStringSizeInBytes(outputCode)
631
- },
632
- dependencies: Array.from(m?.dependencies?.values?.() ?? []).filter((e) => e.absolutePath).map((e) => ({
633
- absolutePath: e.absolutePath,
634
- name: e.data.name
635
- }))
636
- };
637
- }
638
- async function createJsonReport({ graph, entryPoint, includeEnvs, preModules, includeCode, outputJsonPath, rootFolder, silent, options, fetchPackagesMetadata }) {
639
- const { processModuleFilter = () => true } = options || {};
640
- const dependencies = Array.from(graph.dependencies.values()).filter(processModuleFilter);
641
- let packages = toPackages(preModules).concat(toPackages(dependencies));
642
- const modules = preModules.map((m) => toModuleStruct(m, includeCode)).concat(dependencies.map((m) => toModuleStruct(m, includeCode)));
643
- if (fetchPackagesMetadata) packages = await withPackagesMetadata(packages, { onError: (error, pkg) => {
644
- if (!silent) console.warn(`${chalk.default.yellow(`[${NAME}]`)}: Failed to fetch metadata for ${pkg.name}@${pkg.version}: ${error.message}`);
645
- } });
646
- const stats = {
647
- date: Date.now(),
648
- entryPoint,
649
- transformOptions: graph.transformOptions,
650
- envs: includeEnvs.reduce((acc, envName) => {
651
- acc[envName] = process.env[envName];
652
- return acc;
653
- }, {}),
654
- rootFolder,
655
- packages,
656
- modules
657
- };
658
- (0, node_fs.writeFileSync)(outputJsonPath, JSON.stringify(stats));
659
- if (!silent) console.log(`${chalk.default.yellow(`[${NAME}]`)}: Saved stats to ${chalk.default.green(outputJsonPath)}`);
913
+ const sourceCode = m.getSource().toString("utf8");
914
+ const outputCode = m.output[0].data.code;
915
+ return {
916
+ path: m.path,
917
+ source: {
918
+ code: includeCode ? sourceCode : "",
919
+ lineCount: sourceCode.split("\n").length,
920
+ sizeInBytes: getStringSizeInBytes(sourceCode),
921
+ },
922
+ output: {
923
+ code: includeCode ? outputCode : "",
924
+ lineCount: m.output[0].data.lineCount,
925
+ sizeInBytes: getStringSizeInBytes(outputCode),
926
+ },
927
+ dependencies: Array.from(m?.dependencies?.values?.() ?? [])
928
+ .filter((e) => e.absolutePath)
929
+ .map((e) => ({
930
+ absolutePath: e.absolutePath,
931
+ name: e.data.name,
932
+ })),
933
+ };
934
+ }
935
+ async function createJsonReport({
936
+ graph,
937
+ entryPoint,
938
+ includeEnvs,
939
+ preModules,
940
+ includeCode,
941
+ outputJsonPath,
942
+ rootFolder,
943
+ silent,
944
+ options,
945
+ fetchPackagesMetadata,
946
+ }) {
947
+ const { processModuleFilter = () => true } = options || {};
948
+ const dependencies = Array.from(graph.dependencies.values()).filter(
949
+ processModuleFilter,
950
+ );
951
+ let packages = toPackages(preModules).concat(toPackages(dependencies));
952
+ const modules = preModules
953
+ .map((m) => toModuleStruct(m, includeCode))
954
+ .concat(dependencies.map((m) => toModuleStruct(m, includeCode)));
955
+ if (fetchPackagesMetadata)
956
+ packages = await withPackagesMetadata(packages, {
957
+ onError: (error, pkg) => {
958
+ if (!silent)
959
+ console.warn(
960
+ `${chalk.default.yellow(`[${NAME}]`)}: Failed to fetch metadata for ${pkg.name}@${pkg.version}: ${error.message}`,
961
+ );
962
+ },
963
+ });
964
+ const stats = {
965
+ date: Date.now(),
966
+ entryPoint,
967
+ transformOptions: graph.transformOptions,
968
+ envs: includeEnvs.reduce((acc, envName) => {
969
+ acc[envName] = process.env[envName];
970
+ return acc;
971
+ }, {}),
972
+ rootFolder,
973
+ packages,
974
+ modules,
975
+ };
976
+ (0, node_fs.writeFileSync)(outputJsonPath, JSON.stringify(stats));
977
+ if (!silent)
978
+ console.log(
979
+ `${chalk.default.yellow(`[${NAME}]`)}: Saved stats to ${chalk.default.green(outputJsonPath)}`,
980
+ );
660
981
  }
661
982
  /**
662
- * Creates a custom serializer function for Metro bundler, which generates a JSON report
663
- * and optionally modifies the serialization process.
664
- *
665
- * @param options - Configuration options for the serializer.
666
- * @returns A custom serializer function to be used by Metro.
667
- * @throws Throws an error if the project root does not exist.
668
- */
669
- function createSerializer({ serializer, projectRoot, outputJsonPath, includeCode = true, silent = false, includeEnvs = [], fetchPackagesMetadata = true } = {}) {
670
- const mySerializer = serializer || getDefaultSerializer();
671
- if (!(0, node_fs.existsSync)(projectRoot)) throw new Error(`[${NAME}]: Project root does not exist: ${projectRoot}`);
672
- const myOutputJsonPath = outputJsonPath ?? (0, node_path.resolve)(projectRoot, "metro-stats.json");
673
- function customSerializer(entryPoint, preModules, graph, options) {
674
- const code = mySerializer(entryPoint, preModules, graph, options);
675
- const keepAlive = setInterval(() => {}, 1e3);
676
- createJsonReport({
677
- graph,
678
- entryPoint,
679
- includeEnvs,
680
- preModules,
681
- includeCode,
682
- outputJsonPath: myOutputJsonPath,
683
- rootFolder: projectRoot,
684
- silent,
685
- options,
686
- fetchPackagesMetadata
687
- }).finally(() => clearInterval(keepAlive));
688
- return code;
689
- }
690
- return customSerializer;
983
+ * Creates a custom serializer function for Metro bundler, which generates a JSON report
984
+ * and optionally modifies the serialization process.
985
+ *
986
+ * @param options - Configuration options for the serializer.
987
+ * @returns A custom serializer function to be used by Metro.
988
+ * @throws Throws an error if the project root does not exist.
989
+ */
990
+ function createSerializer({
991
+ serializer,
992
+ projectRoot,
993
+ outputJsonPath,
994
+ includeCode = true,
995
+ silent = false,
996
+ includeEnvs = [],
997
+ fetchPackagesMetadata = true,
998
+ } = {}) {
999
+ const mySerializer = serializer || getDefaultSerializer();
1000
+ if (!(0, node_fs.existsSync)(projectRoot))
1001
+ throw new Error(`[${NAME}]: Project root does not exist: ${projectRoot}`);
1002
+ const myOutputJsonPath =
1003
+ outputJsonPath ?? (0, node_path.resolve)(projectRoot, "metro-stats.json");
1004
+ function customSerializer(entryPoint, preModules, graph, options) {
1005
+ const code = mySerializer(entryPoint, preModules, graph, options);
1006
+ const keepAlive = setInterval(() => {}, 1e3);
1007
+ createJsonReport({
1008
+ graph,
1009
+ entryPoint,
1010
+ includeEnvs,
1011
+ preModules,
1012
+ includeCode,
1013
+ outputJsonPath: myOutputJsonPath,
1014
+ rootFolder: projectRoot,
1015
+ silent,
1016
+ options,
1017
+ fetchPackagesMetadata,
1018
+ }).finally(() => clearInterval(keepAlive));
1019
+ return code;
1020
+ }
1021
+ return customSerializer;
691
1022
  }
692
1023
  const empty = { type: "empty" };
693
1024
  /**
694
- * Usage:
695
- *
696
- * ```js
697
- * // metro.config.js
698
- * const { createResolveRequest } = require("react-native-bundle-discovery");
699
- * const resolveRequest = createResolveRequest({
700
- * removePromisePolyfill: true,
701
- * });
702
- * const config = {
703
- * resolver: {
704
- * resolveRequest,
705
- * },
706
- * };
707
- * ```
708
- */
709
- const createResolveRequest = ({ removeNewRenderer = false, removeUTFSequence = false } = {}) => {
710
- const resolveRequest = (context, ...rest) => {
711
- const result = context.resolveRequest(context, ...rest);
712
- if (context.dev) return result;
713
- if (removeUTFSequence && result?.filePath?.endsWith("/react-native/Libraries/UTFSequence.js")) return empty;
714
- if (removeNewRenderer && result?.filePath?.endsWith("/react-native/Libraries/Renderer/shims/ReactFabric.js")) return empty;
715
- return result;
716
- };
717
- return resolveRequest;
1025
+ * Usage:
1026
+ *
1027
+ * ```js
1028
+ * // metro.config.js
1029
+ * const { createResolveRequest } = require("react-native-bundle-discovery");
1030
+ * const resolveRequest = createResolveRequest({
1031
+ * removePromisePolyfill: true,
1032
+ * });
1033
+ * const config = {
1034
+ * resolver: {
1035
+ * resolveRequest,
1036
+ * },
1037
+ * };
1038
+ * ```
1039
+ */
1040
+ const createResolveRequest = ({
1041
+ removeNewRenderer = false,
1042
+ removeUTFSequence = false,
1043
+ } = {}) => {
1044
+ const resolveRequest = (context, ...rest) => {
1045
+ const result = context.resolveRequest(context, ...rest);
1046
+ if (context.dev) return result;
1047
+ if (
1048
+ removeUTFSequence &&
1049
+ result?.filePath?.endsWith("/react-native/Libraries/UTFSequence.js")
1050
+ )
1051
+ return empty;
1052
+ if (
1053
+ removeNewRenderer &&
1054
+ result?.filePath?.endsWith(
1055
+ "/react-native/Libraries/Renderer/shims/ReactFabric.js",
1056
+ )
1057
+ )
1058
+ return empty;
1059
+ return result;
1060
+ };
1061
+ return resolveRequest;
718
1062
  };
719
1063
  /**
720
- * @deprecated Use `createResolveRequest` instead.
721
- */
722
- const createProcessModuleFilter = ({ removePromisePolyfill = false, removeUTFSequence = false } = {}) => (module) => {
723
- if (removePromisePolyfill && module.path.endsWith("/react-native/Libraries/Promise.js")) return false;
724
- if (removeUTFSequence && module.path.endsWith("/react-native/Libraries/UTFSequence.js")) return false;
725
- return true;
726
- };
1064
+ * @deprecated Use `createResolveRequest` instead.
1065
+ */
1066
+ const createProcessModuleFilter =
1067
+ ({ removePromisePolyfill = false, removeUTFSequence = false } = {}) =>
1068
+ (module) => {
1069
+ if (
1070
+ removePromisePolyfill &&
1071
+ module.path.endsWith("/react-native/Libraries/Promise.js")
1072
+ )
1073
+ return false;
1074
+ if (
1075
+ removeUTFSequence &&
1076
+ module.path.endsWith("/react-native/Libraries/UTFSequence.js")
1077
+ )
1078
+ return false;
1079
+ return true;
1080
+ };
727
1081
  //#endregion
728
1082
  exports.BundleDiscoveryPlugin = BundleDiscoveryPlugin;
729
1083
  exports.createProcessModuleFilter = createProcessModuleFilter;