react-native-bundle-discovery-ui 2.2.0 → 2.3.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.
package/README.md CHANGED
@@ -15,6 +15,8 @@ With this tool, you can easily explore your app's codebase, identify large or he
15
15
  - `react-native-bundle-discovery-cli` - CLI to analyze the bundle report.
16
16
  - `react-native-bundle-discovery-rozenite-plugin` - Rozenite plugin to integrate UI tool to [React Native DevTools](https://reactnative.dev/docs/react-native-devtools).
17
17
 
18
+ ---
19
+
18
20
  ### Setup:
19
21
 
20
22
  There are two ways to install the package:
@@ -22,6 +24,11 @@ There are two ways to install the package:
22
24
  1. As in independent tool (UI + CLI)
23
25
  2. Or as a [Rozenite](_rozenite/README.md) plugin (see: [_rozenite/README.md](_rozenite/README.md))
24
26
 
27
+
28
+ > 🚀 If you've migrated to [Re.Pack](https://re-pack.dev/), you can still use this package to analyze 📊 JS bundles 📦 generated by Re.Pack.
29
+ >
30
+ > 💡 Learn more: 📖 [Re.Pack.md](./Re.Pack.md)
31
+
25
32
  #### 1. Install (independent tool)
26
33
 
27
34
  ```bash
@@ -60,7 +67,7 @@ See: [_rozenite/README.md](_rozenite/README.md)
60
67
 
61
68
  ### 3. Build the app
62
69
 
63
- 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:
70
+ For example, for iOS you can run the following command, and it will generate the `metro-stats.json` file in the root of your project:
64
71
 
65
72
  ```bash
66
73
  npx react-native bundle \
package/lib/build.js CHANGED
@@ -3,6 +3,7 @@ const path = require("path");
3
3
  const chalk = require("chalk");
4
4
  const { build } = require("@discoveryjs/cli");
5
5
  const config = require("../.discoveryrc.js");
6
+ const { isRsdoctorReportPath } = require("./isRsdoctorReport.js");
6
7
 
7
8
  function buildHtmlPage(filePath, output, clean, singleFile, verbose) {
8
9
  if (verbose) {
@@ -46,13 +47,20 @@ function buildHtmlPage(filePath, output, clean, singleFile, verbose) {
46
47
  `Creating temporary config file at: ${chalk.green(configFile)}, for discovery.js`,
47
48
  );
48
49
  }
50
+ const transformRcdoctor = require.resolve("./rsdoctor.js");
49
51
  fs.writeFileSync(
50
52
  configFile,
51
- `module.exports = ${JSON.stringify(
53
+ `const {transformRSDoctorData} = require("${transformRcdoctor}");
54
+ module.exports = ${JSON.stringify(
52
55
  { ...config, data: "<tmp>" },
53
56
  null,
54
57
  1,
55
- ).replace(`"<tmp>"`, `() => require("${fullJsonPath}")`)};`,
58
+ ).replace(
59
+ `"<tmp>"`,
60
+ isRsdoctorReportPath(fullJsonPath)
61
+ ? `() => transformRSDoctorData(require("${fullJsonPath}"))`
62
+ : `() => require("${fullJsonPath}")`,
63
+ )};`,
56
64
  );
57
65
 
58
66
  if (verbose) {
@@ -0,0 +1,33 @@
1
+ const chalk = require("chalk");
2
+
3
+ const isRsdoctorReportPath = (reportPath) => {
4
+ try {
5
+ const reportJson = require(reportPath);
6
+ return isRsdoctorReport(reportJson, reportPath);
7
+ } catch {
8
+ return false;
9
+ }
10
+ };
11
+
12
+ const isRsdoctorReport = (reportJson, reportPath, noLogs) => {
13
+ try {
14
+ if (
15
+ Array.isArray(reportJson?.data?.moduleGraph?.modules) ||
16
+ Array.isArray(reportJson?.data?.packageGraph?.packages)
17
+ ) {
18
+ !noLogs &&
19
+ console.info(
20
+ `Detected ${chalk.red("rsdoctor")} format in the input file: ${chalk.green(reportPath)}`,
21
+ );
22
+ return true;
23
+ }
24
+ return false;
25
+ } catch {
26
+ return false;
27
+ }
28
+ };
29
+
30
+ module.exports = {
31
+ isRsdoctorReport,
32
+ isRsdoctorReportPath,
33
+ };
@@ -0,0 +1,149 @@
1
+ const { resolve } = require("path");
2
+
3
+ const possibleEntryFiles = [
4
+ "index.js",
5
+ "index.jsx",
6
+ "index.ts",
7
+ "index.tsx",
8
+ "index.mjs",
9
+ "index.cjs",
10
+ ];
11
+
12
+ const doStructureCheck = (rsdoctorData) => {
13
+ if (!(
14
+ Array.isArray(rsdoctorData?.data?.configs) &&
15
+ rsdoctorData.data.configs.length === 1
16
+ )) {
17
+ throw new Error(
18
+ `Unexpected number of "data.configs[]" in rsdoctor-data.json. Work only with single config. Found: ${rsdoctorData?.data?.configs?.length}`,
19
+ );
20
+ }
21
+
22
+ if (!Array.isArray(rsdoctorData?.data?.packageGraph?.packages)) {
23
+ throw new Error(
24
+ `Unexpected "data.packageGraph.packages[]" in rsdoctor-data.json. Expected array. Found: ${typeof rsdoctorData?.data?.packageGraph?.packages}`,
25
+ );
26
+ }
27
+
28
+ const firstConfig = rsdoctorData.data.configs[0];
29
+
30
+ if (!(firstConfig.name === "rspack" || firstConfig.name === "webpack")) {
31
+ throw new Error(
32
+ `Unsupported bundler: ${firstConfig.name}@${firstConfig.version}. Only 'rspack' and 'webpack' are supported.`,
33
+ );
34
+ }
35
+ };
36
+
37
+ const getDependenciesMap = (rsdoctorData) => {
38
+ const depsMap = new Map(
39
+ rsdoctorData.data.moduleGraph.dependencies.map((dep) => [dep.id, dep]),
40
+ );
41
+ return depsMap;
42
+ };
43
+
44
+ const getEntryFile = (rsdoctorData) => {
45
+ const firstConfig = rsdoctorData?.data?.configs?.[0];
46
+ const entry = firstConfig?.config?.entry;
47
+ const entryFile =
48
+ typeof entry?.main === "string"
49
+ ? entry.main
50
+ : typeof entry?.main?.import === "string"
51
+ ? entry.main.import
52
+ : entry?.main.import[0];
53
+
54
+ if (entryFile) {
55
+ return resolve(firstConfig.root, entryFile);
56
+ }
57
+
58
+ const pathsToCheck = possibleEntryFiles.map((f) =>
59
+ resolve(firstConfig.root, f),
60
+ );
61
+ const indexFile = rsdoctorData.data.moduleGraph.modules.find((mdl) =>
62
+ pathsToCheck.some((entry) => mdl.path === entry),
63
+ );
64
+
65
+ if (indexFile && indexFile.imported?.length === 0) {
66
+ return indexFile.path;
67
+ }
68
+
69
+ return `${firstConfig.root}/index.js`;
70
+ };
71
+
72
+ const transformRSDoctorData = (rsdoctorData) => {
73
+ doStructureCheck(rsdoctorData);
74
+ const tmpDependenciesMap = getDependenciesMap(rsdoctorData);
75
+ const firstConfig = rsdoctorData.data.configs[0];
76
+ return {
77
+ date: Date.now(), // no info in rspack report
78
+ entryPoint: getEntryFile(rsdoctorData),
79
+ rootFolder: firstConfig.root,
80
+ // envs: {},
81
+ transformOptions: {
82
+ // customTransformOptions: {},
83
+ dev: firstConfig.config.mode !== "production",
84
+ minify: firstConfig.config.optimization?.minimize ?? false,
85
+ platform: firstConfig.config.name ?? "ios",
86
+ // type: "module",
87
+ // unstable_transformProfile: "default",
88
+ },
89
+ // Example of package:
90
+ // {
91
+ // name: "@react-native/js-polyfills",
92
+ // absolutePath:
93
+ // "/Users/xd/tmp/RN84/node_modules/@react-native/js-polyfills",
94
+ // version: "0.84.1",
95
+ // }
96
+ packages: rsdoctorData.data.packageGraph.packages.map((pkg) => ({
97
+ name: pkg.name,
98
+ absolutePath: pkg.root,
99
+ version: pkg.version,
100
+ source: { sizeInBytes: pkg.size?.sourceSize ?? 1 },
101
+ output: { sizeInBytes: pkg.size?.parsedSize ?? 1 },
102
+ })),
103
+ // Example of module:
104
+ // {
105
+ // "path": "/Users/xd/tmp/RN84/node_modules/react-native/Libraries/BatchedBridge/BatchedBridge.js",
106
+ // "source": {
107
+ // "code": "",
108
+ // "lineCount": 32,
109
+ // "sizeInBytes": 954
110
+ // },
111
+ // "output": {
112
+ // "code": "",
113
+ // "lineCount": 1,
114
+ // "sizeInBytes": 219
115
+ // },
116
+ // "dependencies": [
117
+ // {
118
+ // "absolutePath": "/Users/xd/tmp/RN84/node_modules/react-native/Libraries/BatchedBridge/MessageQueue.js",
119
+ // "name": "./MessageQueue"
120
+ // }
121
+ // ]
122
+ // },
123
+ modules: rsdoctorData.data.moduleGraph.modules.map((mdl) => ({
124
+ path: mdl.path,
125
+ source: {
126
+ code: "", // no code in rspack report
127
+ lineCount: 0, // no code to count lines in rspack report
128
+ sizeInBytes: mdl.size?.sourceSize ?? 0,
129
+ },
130
+ output: {
131
+ code: "",
132
+ lineCount: 0,
133
+ sizeInBytes: mdl.size?.parsedSize ?? 0,
134
+ },
135
+ dependencies: mdl.dependencies
136
+ .map((depNumId) => {
137
+ const dep = tmpDependenciesMap.get(depNumId);
138
+ return dep
139
+ ? { absolutePath: dep.resolvedRequest, name: dep.request }
140
+ : null;
141
+ })
142
+ .filter((e) => e !== null),
143
+ })),
144
+ };
145
+ };
146
+
147
+ module.exports = {
148
+ transformRSDoctorData,
149
+ };
package/lib/server.js CHANGED
@@ -4,6 +4,7 @@ const chalk = require("chalk");
4
4
  const { createServer } = require("@discoveryjs/cli");
5
5
  const { silent } = require("@discoveryjs/cli/lib/shared/utils.js");
6
6
  const config = require("../.discoveryrc.js");
7
+ const { isRsdoctorReportPath } = require("./isRsdoctorReport.js");
7
8
 
8
9
  function serve(filePath, port, verbose) {
9
10
  const PORT = process.env.PORT || port;
@@ -45,13 +46,20 @@ function serve(filePath, port, verbose) {
45
46
  `Creating temporary config file at: ${chalk.green(configFile)}, for discovery.js`,
46
47
  );
47
48
  }
49
+ const transformRcdoctor = require.resolve("./rsdoctor.js");
48
50
  fs.writeFileSync(
49
51
  configFile,
50
- `module.exports = ${JSON.stringify(
52
+ `const {transformRSDoctorData} = require("${transformRcdoctor}");
53
+ module.exports = ${JSON.stringify(
51
54
  { ...config, data: "<tmp>" },
52
55
  null,
53
56
  1,
54
- ).replace(`"<tmp>"`, `() => require("${fullJsonPath}")`)};`,
57
+ ).replace(
58
+ `"<tmp>"`,
59
+ isRsdoctorReportPath(fullJsonPath)
60
+ ? `() => transformRSDoctorData(require("${fullJsonPath}"))`
61
+ : `() => require("${fullJsonPath}")`,
62
+ )};`,
55
63
  );
56
64
 
57
65
  if (verbose) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-bundle-discovery-ui",
3
- "version": "2.2.0",
3
+ "version": "2.3.0",
4
4
  "main": "lib/bin.js",
5
5
  "bin": "lib/bin.js",
6
6
  "repository": "git@github.com:retyui/react-native-bundle-discovery.git",
@@ -38,5 +38,9 @@
38
38
  "queryHelpers.js",
39
39
  ".discoveryrc.js"
40
40
  ],
41
- "packageManager": "yarn@4.18.0"
41
+ "packageManager": "yarn@4.18.0",
42
+ "devDependencies": {
43
+ "acorn": "^8.18.0",
44
+ "acorn-walk": "^8.3.5"
45
+ }
42
46
  }
package/queryHelpers.js CHANGED
@@ -17,6 +17,9 @@ const helpers = {
17
17
  .then((formatted) => {
18
18
  helpers.prettifyMap.set(sourceStr, formatted);
19
19
  return formatted;
20
+ })
21
+ .catch(() => {
22
+ return sourceStr;
20
23
  });
21
24
  },
22
25
  askChatGPTAboutPackages() {