react-native-bundle-discovery 1.0.0-rc.13 → 1.0.0-rc.9

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/.discoveryrc.js CHANGED
@@ -1,21 +1,19 @@
1
- const path = require("path");
2
-
3
1
  module.exports = {
4
2
  name: "react-native-bundle-discovery",
5
3
  data: () => require("./tmp/before_metro-stats.json"),
6
- setup: path.resolve(__dirname, "setup.js"),
4
+ setup: "./setup.js",
7
5
  view: {
8
6
  assets: [
9
7
  // Global styles
10
- path.resolve(__dirname, "views/global.css"),
8
+ "views/global.css",
11
9
  // Pages
12
- path.resolve(__dirname, "pages/default.js"),
13
- path.resolve(__dirname, "pages/module.js"),
14
- path.resolve(__dirname, "pages/package.js"),
10
+ "pages/default.js",
11
+ "pages/module.js",
12
+ "pages/package.js",
15
13
  // Custom views
16
- path.resolve(__dirname, "views/highcharts.css"),
17
- path.resolve(__dirname, "views/highcharts.js"),
18
- path.resolve(__dirname, "views/foamtree.js"),
14
+ "views/highcharts.css",
15
+ "views/highcharts.js",
16
+ "views/foamtree.js",
19
17
  ],
20
18
  },
21
19
  };
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
  > Currently, everything is in a very early stage. The project is not yet ready for use.
5
5
 
6
6
 
7
- <img width="800" alt="" src="./assets/img.png" />
7
+ <img width="800" src="https://github.com/user-attachments/assets/211145d4-8fe7-499b-a372-9d752e878772" />
8
8
 
9
9
  ### Setup:
10
10
 
@@ -25,7 +25,7 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
25
25
  + projectRoot: __dirname,
26
26
  + //^^^ ⚠️ WARNING: In a monorepo setup, this should point to the monorepo root,
27
27
  + // not the individual package directory.
28
- +});
28
+ +})
29
29
 
30
30
  -const config = {};
31
31
  +const config = {
@@ -37,31 +37,7 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
37
37
  module.exports = mergeConfig(getDefaultConfig(__dirname), config);
38
38
  ```
39
39
 
40
- #### 3. Build the app
41
-
42
- As example, for iOS you can run the following command, and it will generate the `metro-stats.json` file in the root of your project:
43
-
44
- ```bash
45
- npx react-native bundle \
46
- --entry-file index.js \
47
- --platform ios \
48
- --dev false \
49
- --bundle-output ios/main.jsbundle \
50
- --assets-dest ios/assets
51
- ```
52
-
53
- #### 4. View the report
54
-
55
- Run webserver to view the report:
56
-
57
- ```bash
58
- npx react-native-bundle-discovery metro-stats.json
59
- ```
60
-
61
- ---
62
-
63
40
  **Similar projects:**
64
-
65
41
  - https://github.com/expo/atlas
66
42
  - https://github.com/v3ron/expo-atlas-without-expo
67
43
  - https://github.com/callstack/react-native-bundle-visualizer
@@ -69,8 +45,7 @@ npx react-native-bundle-discovery metro-stats.json
69
45
  - https://github.com/statoscope/statoscope
70
46
  - https://github.com/relative-ci/bundle-stats/tree/master/packages/cli
71
47
 
72
- ---
73
48
 
74
- **Built using Discovery.js:**
49
+ **Links:**
75
50
  - Build blocks for pages: https://discoveryjs.github.io/discovery/#views-showcase
76
51
  - Jora syntax: https://discoveryjs.github.io/jora/#article:jora-syntax-operators
package/lib/bin.js CHANGED
@@ -1,52 +1,35 @@
1
1
  #!/usr/bin/env node
2
- const fs = require("fs");
3
2
  const path = require("path");
4
- const chalk = require("chalk");
5
- const { createServer } = require("@discoveryjs/cli");
3
+ const discovery = require("@discoveryjs/cli");
6
4
  const config = require("../.discoveryrc.js");
7
5
 
8
6
  const filePath = process.argv[2];
9
7
 
10
8
  if (!filePath) {
11
9
  console.error(
12
- `Usage: '${chalk.green("npx react-native-bundle-discovery <path-to-file>")}', Please provide a path to a JSON file.`,
10
+ "Usage: `npx react-native-bundle-discovery <path-to-file>`, Please provide a path to a JSON file.",
13
11
  );
14
12
  process.exit(1);
15
13
  }
16
14
 
17
15
  const jsonFilePath = path.resolve(process.cwd(), filePath);
18
16
 
19
- let fullJsonPath;
17
+ let data;
20
18
 
21
19
  try {
22
- fullJsonPath = require.resolve(jsonFilePath);
20
+ data = require(jsonFilePath);
23
21
  } catch (err) {
24
- console.error(`Error loading file: ${chalk.red(jsonFilePath)}`);
22
+ console.error(`Error loading file: ${jsonFilePath}`);
25
23
  console.error(err.message);
26
24
  process.exit(1);
27
25
  }
28
26
 
29
27
  const PORT = process.env.PORT || 8079;
30
28
 
31
- const configFile = path.resolve(__dirname, "./.tmp.js");
32
-
33
- fs.writeFileSync(
34
- configFile,
35
- `module.exports = ${JSON.stringify(
36
- { ...config, data: "<tmp>" },
37
- null,
38
- 1,
39
- ).replace(`"<tmp>"`, `() => require("${fullJsonPath}")`)};`,
40
- );
41
-
42
- createServer({
43
- cache: false,
44
- minify: true,
45
- dev: false,
46
- config: configFile,
47
- configFile,
48
- }).then((server) =>
49
- server.listen(PORT, () =>
50
- console.log(`Server listen on ${chalk.green(`http://localhost:${PORT}`)}`),
51
- ),
52
- );
29
+ discovery
30
+ .createServer({ ...config, data: () => data })
31
+ .then((server) =>
32
+ server.listen(PORT, () =>
33
+ console.log(`Server listen on http://localhost:${this.address().port}`),
34
+ ),
35
+ );
@@ -1,7 +1,6 @@
1
- const { writeFileSync, existsSync } = require("fs");
2
- const { resolve } = require("path");
3
- const { Buffer } = require("buffer");
4
- const chalk = require("chalk");
1
+ const { writeFileSync, existsSync } = require("node:fs");
2
+ const { resolve } = require("node:path");
3
+ const { Buffer } = require("node:buffer");
5
4
 
6
5
  const NAME = require("../package.json").name;
7
6
 
@@ -114,9 +113,7 @@ function createJsonReport({
114
113
 
115
114
  writeFileSync(outputJsonPath, JSON.stringify(stats));
116
115
 
117
- console.log(
118
- `${chalk.yellow(`[${NAME}]`)}: Saved stats to ${chalk.green(outputJsonPath)}`,
119
- );
116
+ console.log(`[${NAME}]: Saved stats to`, outputJsonPath);
120
117
  }
121
118
 
122
119
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-bundle-discovery",
3
- "version": "1.0.0-rc.13",
3
+ "version": "1.0.0-rc.9",
4
4
  "main": "index.js",
5
5
  "bin": "lib/bin.js",
6
6
  "repository": "git@github.com:retyui/react-native-bundle-discovery.git",
@@ -12,10 +12,7 @@
12
12
  "build": "npx discovery-build --config .discoveryrc.js --output build --serve-only-assets --single-file"
13
13
  },
14
14
  "dependencies": {
15
- "@discoveryjs/cli": "2.14.2",
16
- "@discoveryjs/discovery": "1.0.0-beta.93",
17
- "chalk": "^4.1.2",
18
- "highcharts": "12.2.0"
15
+ "@discoveryjs/cli": "2.14.2"
19
16
  },
20
17
  "peerDependencies": {
21
18
  "metro": "*"
@@ -31,12 +28,13 @@
31
28
  "views",
32
29
  "pages",
33
30
  "index.js",
34
- "setup.js",
35
- "prepare.js",
36
- "queryHelpers.js",
37
31
  ".discoveryrc.js"
38
32
  ],
39
33
  "devDependencies": {
34
+ "@discoveryjs/discovery": "1.0.0-beta.93",
35
+ "highcharts": "12.2.0",
36
+ "lodash": "4.17.21",
40
37
  "prettier": "3.5.3"
41
- }
38
+ },
39
+ "packageManager": "yarn@4.6.0"
42
40
  }
package/views/foamtree.js CHANGED
@@ -1,4 +1,4 @@
1
- const { FoamTree } = require("../vendors/foamtree.js");
1
+ const FoamTree = require("../vendors/foamtree.js").default;
2
2
 
3
3
  function injectTooltip(root) {
4
4
  const t = root.querySelector(".tooltip");
package/prepare.js DELETED
@@ -1,105 +0,0 @@
1
- function getDuplicateId(path) {
2
- const uniqueNodeModulePath = path.split("node_modules/").pop();
3
- const ids = [uniqueNodeModulePath];
4
-
5
- // Special case for lodash
6
- if (
7
- uniqueNodeModulePath.startsWith("lodash.") ||
8
- uniqueNodeModulePath.startsWith("lodash-es/")
9
- ) {
10
- // node_modules/lodash.debounce/index.js => node_modules/lodash/debounce.js
11
- // node_modules/lodash-es/get.js => node_modules/lodash/get.js
12
- const uniqueLodashPath = uniqueNodeModulePath
13
- .replace("lodash-es/", "lodash/")
14
- .replace("lodash.", "lodash/")
15
- .replace("/index.js", ".js");
16
-
17
- ids.push(uniqueLodashPath);
18
- }
19
-
20
- return ids;
21
- }
22
-
23
- function prepare(data) {
24
- // data.modules = data.modules.slice(875, 880); TODO for debugging a formtree chart
25
- let moduleMap = new Map();
26
- let duplicatesMap = new Map();
27
- let allLodashModules = new Set();
28
-
29
- data.packages.forEach((pkg) => {
30
- pkg.path = pkg.absolutePath.replace(data.rootFolder + "/", "");
31
- });
32
- data.modules.forEach((m) => {
33
- // 0. Data transformation
34
- m.absolutePath = m.path;
35
- if (m.absolutePath === data.entryPoint) {
36
- m.isEntry = true;
37
- }
38
- m.path = m.path.replace(data.rootFolder + "/", "");
39
- m.dependencies.forEach((d) => {
40
- d.path = d.absolutePath.replace(data.rootFolder + "/", "");
41
- });
42
-
43
- // 1. Duplicates
44
- m._tmp_ids = getDuplicateId(m.path);
45
- m.duplicates = [];
46
- m._tmp_ids.forEach((id) => {
47
- if (!duplicatesMap.has(id)) {
48
- duplicatesMap.set(id, []);
49
- }
50
- duplicatesMap.get(id).push(m);
51
- });
52
- // lodash/*
53
- // lodash-es/*
54
- // lodash.*
55
- if (m.path.includes("node_modules/lodash")) {
56
- allLodashModules.add(m);
57
- }
58
-
59
- // 2. Dependencies
60
- m.dependents = [];
61
- moduleMap.set(m.absolutePath, m);
62
-
63
- // 3. Other
64
- // 3.1 Format prelude
65
- if (m.path === "__prelude__") {
66
- m.source.code = m.source.code
67
- .replaceAll(";", ";\n\n")
68
- .replaceAll(",", ",\n ");
69
- m.output.code = m.output.code
70
- .replaceAll(";", ";\n\n")
71
- .replaceAll(",", ",\n ");
72
- }
73
- });
74
-
75
- data.modules.forEach((m) => {
76
- // 1. Duplicates
77
- m._tmp_ids.forEach((id) => {
78
- const duplicates = duplicatesMap.get(id);
79
- if (duplicates.length > 1) {
80
- m.duplicates.push(...duplicates.filter((d) => d !== m));
81
- }
82
- });
83
-
84
- // lodash/index.js is a special case (as index.js includes all lodash functions)
85
- if (m.path.endsWith("node_modules/lodash/index.js")) {
86
- m.duplicates.push(...allLodashModules);
87
- }
88
- delete m._tmp_ids;
89
-
90
- // 2. Dependencies
91
- m.dependencies.forEach((dependency) => {
92
- const dependentModule = moduleMap.get(dependency.absolutePath);
93
- if (dependentModule) {
94
- dependentModule.dependents.push(m); //add to the dependent module directly
95
- }
96
- });
97
- });
98
-
99
- allLodashModules = null;
100
- moduleMap = null;
101
- duplicatesMap = null;
102
- return data;
103
- }
104
-
105
- module.exports = prepare;
package/queryHelpers.js DELETED
@@ -1,327 +0,0 @@
1
- const helpers = {
2
- plural(count, [singular, plural]) {
3
- return count === 1 ? singular : plural;
4
- },
5
- pluralWithCount(count, [singular, plural]) {
6
- return `${count} ${helpers.plural(count, [singular, plural])}`;
7
- },
8
- pluralBadge(count, [singular, plural], prefix = "") {
9
- return {
10
- text: prefix + count,
11
- postfix: helpers.plural(count, [singular, plural]),
12
- };
13
- },
14
- getHighchartsColors() {
15
- const Highcharts = require("highcharts");
16
- return Highcharts.getOptions().colors;
17
- },
18
- getModulesName(path) {
19
- const modules = path.split("node_modules/");
20
- const lastModule = modules[modules.length - 1];
21
- const [folder, subFolder] = lastModule.split("/");
22
- if (folder.startsWith("@")) {
23
- return `${folder}/${subFolder}`;
24
- }
25
- return folder;
26
- },
27
- getBestNetworkGraphSize(module, params) {
28
- let maxParentDepth = 0;
29
- let itemsCount = 0;
30
- do {
31
- maxParentDepth++;
32
- itemsCount = helpers.getNetworkGraph(module, {
33
- ...params,
34
- maxParentDepth,
35
- }).data.length;
36
- if (itemsCount < 10) {
37
- const limit = itemsCount - 1;
38
- return limit > 2 ? limit : 2;
39
- }
40
- } while (maxParentDepth < 6);
41
- return maxParentDepth;
42
- },
43
- getNetworkGraph(
44
- module,
45
- { maxParentDepth = 2, omitVisitedModules = true } = {},
46
- ) {
47
- const queue = [{ module, parentId: "", level: 0 }];
48
- const visited = new Set();
49
- const result = [];
50
- let entryPointPath = null;
51
- const data = [];
52
-
53
- while (queue.length > 0) {
54
- const { module: currentModule, parentId, level } = queue.shift();
55
-
56
- if (level >= Number(maxParentDepth)) {
57
- break;
58
- }
59
-
60
- const id = currentModule.path;
61
-
62
- if (omitVisitedModules) {
63
- if (visited.has(id)) {
64
- continue;
65
- }
66
- visited.add(id);
67
- }
68
-
69
- if (parentId) {
70
- const isEntryPoint = currentModule.dependents.length === 0;
71
- result.push({ isEntryPoint, id, parentId });
72
- data.push([parentId, id]);
73
- if (isEntryPoint) {
74
- entryPointPath = id;
75
- }
76
- }
77
-
78
- if (Array.isArray(currentModule.dependents)) {
79
- for (const dependentModule of currentModule.dependents) {
80
- queue.push({
81
- module: dependentModule,
82
- parentId: id,
83
- level: level + 1,
84
- });
85
- }
86
- }
87
- }
88
-
89
- if (!entryPointPath) {
90
- for (const item of result) {
91
- if (item.isEntryPoint) {
92
- entryPointPath = item.id;
93
- break;
94
- }
95
- }
96
- }
97
-
98
- return { entryPointPath, data };
99
- },
100
-
101
- getExtColor(extName) {
102
- const colors = {
103
- js: "#f1e05a50",
104
- ts: "#2b748950",
105
- tsx: "#2b748950",
106
- json: "#e34c2650",
107
- svg: "#e69f0d50",
108
- css: "#563d7c50",
109
- png: "#e44b2350",
110
- };
111
- return colors[extName] ?? colors["js"];
112
- },
113
- getFileExtension(filename) {
114
- const idx = filename.lastIndexOf(".");
115
- return idx === -1 ? "js" : filename.slice(idx + 1);
116
- },
117
- toFixed(value, fractionDigits = 2) {
118
- return Number(value).toFixed(fractionDigits);
119
- },
120
- percent(value, fractionDigits = 2) {
121
- return (100 * value).toFixed(fractionDigits) + "%";
122
- },
123
- formatBytes(bytes, decimals) {
124
- if (bytes == 0) return "0 Bytes";
125
- const k = 1024,
126
- dm = decimals || 2,
127
- sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"],
128
- i = Math.floor(Math.log(bytes) / Math.log(k));
129
- return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
130
- },
131
- isPackageImport(moduleName) {
132
- return moduleName?.[0] !== ".";
133
- },
134
- // isRuntimeCode(moduleName) {
135
- // return (
136
- // moduleName === "__prelude__" ||
137
- // moduleName.includes("@babel/runtime") ||
138
- // moduleName.includes("metro-runtime") ||
139
- // moduleName.includes("@react-native/js-polyfills")
140
- // );
141
- // },
142
- transformFilesList(files, rootFolder, type) {
143
- const nodeModulesMap = { children: {}, size: 0 };
144
- const sourceCodeMap = { children: {}, size: 0 };
145
-
146
- files.forEach(({ path, size }) => {
147
- if (path === "__prelude__") {
148
- path = "node_modules/__prelude__";
149
- }
150
-
151
- const shortPath = path.replace(rootFolder + "/", "");
152
- const isNodeModule = shortPath.includes("node_modules/");
153
- const parts = shortenPath(shortPath, nodeModulesMap).split("/");
154
- let current = (isNodeModule ? nodeModulesMap : sourceCodeMap).children;
155
-
156
- parts.forEach((part, index) => {
157
- // console.log((" --- xdebug " + index + " ".repeat(50)).substr(0, 40), {
158
- // part,
159
- // parts,
160
- // });
161
- if (!current[part]) {
162
- current[part] = { size: 0, children: {} };
163
- }
164
-
165
- if (index === parts.length - 1) {
166
- current[part].size = size;
167
- current[part].path = shortPath;
168
- }
169
-
170
- current = current[part].children;
171
- });
172
- });
173
-
174
- if (type === "foamtree") {
175
- sumSizes(nodeModulesMap);
176
- sumSizes(sourceCodeMap);
177
-
178
- const sourceCodeGroup = toGroups(sourceCodeMap, "Source Code");
179
- const nodeModulesGroup = nodeModulesMap?.children?.node_modules
180
- ? toGroups(nodeModulesMap.children.node_modules, "node_modules")
181
- : {};
182
-
183
- if (!sourceCodeGroup.groups) {
184
- return nodeModulesGroup;
185
- }
186
- if (!nodeModulesGroup.groups) {
187
- return nodeModulesGroup;
188
- }
189
-
190
- const topLevelNode = { groups: [sourceCodeGroup, nodeModulesGroup] };
191
- topLevelNode.weight = topLevelNode.groups.reduce(
192
- (acc, group) => acc + group.weight,
193
- 0,
194
- );
195
-
196
- return topLevelNode;
197
- }
198
-
199
- if (type === "highcharts-treemap") {
200
- const ROOT_ID_1 = "~";
201
- const ROOT_ID_2 = ".";
202
- return [
203
- { id: ROOT_ID_1, name: "node_modules" },
204
- { id: ROOT_ID_2, name: "Source Code" },
205
- ]
206
- .concat(flattenTree(nodeModulesMap.children.node_modules, ROOT_ID_1))
207
- .concat(flattenTree(sourceCodeMap, ROOT_ID_2));
208
- }
209
-
210
- throw new Error("Unsupported type: " + type);
211
- },
212
- };
213
-
214
- function flattenTree(
215
- node,
216
- parentId,
217
- prevWasSkipped = false,
218
- lvl = 0,
219
- result = [],
220
- overrideParentId,
221
- ) {
222
- const nodeChildrenCount = Object.keys(node.children);
223
-
224
- for (const key of nodeChildrenCount) {
225
- const childNode = node.children[key];
226
- const childId = parentId + "/" + key;
227
- const childrenCount = Object.keys(childNode.children).length;
228
- const hasChildren = childrenCount > 0;
229
- const item = {
230
- id: childId,
231
- name: prevWasSkipped ? parentId : key,
232
- parent: overrideParentId ?? parentId,
233
- };
234
-
235
- if (!hasChildren) {
236
- item.value = childNode.size;
237
- }
238
-
239
- if (lvl === 0) {
240
- item.color = Highcharts.getOptions().colors[randomInt(0, 9)];
241
- }
242
-
243
- const skipThisNode = nodeChildrenCount.length === 1 && childrenCount === 1;
244
-
245
- if (!skipThisNode) {
246
- result.push(item);
247
- }
248
-
249
- flattenTree(
250
- childNode,
251
- childId,
252
- skipThisNode,
253
- lvl + 1,
254
- result,
255
- skipThisNode ? (overrideParentId ?? parentId) : undefined,
256
- );
257
- }
258
-
259
- return result;
260
- }
261
-
262
- function sumSizes(node) {
263
- const isFile = Object.keys(node.children).length === 0;
264
- let totalFiles = isFile ? 1 : 0;
265
- let totalSize = node.size || 0;
266
- for (const key in node.children) {
267
- const result = sumSizes(node.children[key]);
268
- totalSize += result.totalSize;
269
- totalFiles += result.totalFiles;
270
- }
271
- node.type = isFile ? "file" : "folder";
272
- node.size = totalSize;
273
- node.files = totalFiles;
274
- return { totalSize, totalFiles };
275
- }
276
-
277
- function toGroups(node, label) {
278
- const keys = Object.keys(node.children);
279
-
280
- const common = {
281
- label,
282
- weight: node.size,
283
- files: node.files,
284
- type: node.type,
285
- size: helpers.formatBytes(node.size),
286
- };
287
-
288
- if (keys.length === 0) {
289
- // File
290
- return common;
291
- }
292
-
293
- // Folder
294
- return Object.assign(common, {
295
- groups: keys.map((key) => toGroups(node.children[key], key)),
296
- });
297
- }
298
-
299
- function randomInt(min, max) {
300
- return Math.floor(Math.random() * (max - min + 1)) + min;
301
- }
302
-
303
- const nm = "node_modules/";
304
- function shortenPath(path, nodeModulesMap) {
305
- let index = path.lastIndexOf(nm);
306
-
307
- if (index > 0) {
308
- const pathWithoutNestedNM = path.slice(index);
309
- // Find the package name after "node_modules/"
310
- const firstSlash = pathWithoutNestedNM.indexOf("/");
311
- const secondSlash = pathWithoutNestedNM.indexOf("/", firstSlash + 1);
312
- const pkgName =
313
- secondSlash === -1
314
- ? pathWithoutNestedNM.slice(firstSlash + 1)
315
- : pathWithoutNestedNM.slice(firstSlash + 1, secondSlash);
316
-
317
- if (nodeModulesMap?.children?.node_modules?.children?.[pkgName]) {
318
- const parentPackage = helpers.getModulesName(path.slice(0, index));
319
- return nm + parentPackage + " ~ " + pathWithoutNestedNM.slice(nm.length);
320
- }
321
-
322
- return pathWithoutNestedNM;
323
- }
324
- return path;
325
- }
326
-
327
- module.exports = helpers;
package/setup.js DELETED
@@ -1,12 +0,0 @@
1
- const prepare = require("./prepare");
2
- const queryHelpers = require("./queryHelpers");
3
-
4
- module.exports = function setup({
5
- defineObjectMarker,
6
- addQueryHelpers,
7
- setPrepare,
8
- }) {
9
- // extend queries with custom methods
10
- addQueryHelpers(queryHelpers);
11
- setPrepare(prepare);
12
- };