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 +8 -1
- package/lib/build.js +10 -2
- package/lib/isRsdoctorReport.js +33 -0
- package/lib/rsdoctor.js +149 -0
- package/lib/server.js +10 -2
- package/package.json +6 -2
- package/queryHelpers.js +3 -0
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
|
-
|
|
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
|
-
`
|
|
53
|
+
`const {transformRSDoctorData} = require("${transformRcdoctor}");
|
|
54
|
+
module.exports = ${JSON.stringify(
|
|
52
55
|
{ ...config, data: "<tmp>" },
|
|
53
56
|
null,
|
|
54
57
|
1,
|
|
55
|
-
).replace(
|
|
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
|
+
};
|
package/lib/rsdoctor.js
ADDED
|
@@ -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
|
-
`
|
|
52
|
+
`const {transformRSDoctorData} = require("${transformRcdoctor}");
|
|
53
|
+
module.exports = ${JSON.stringify(
|
|
51
54
|
{ ...config, data: "<tmp>" },
|
|
52
55
|
null,
|
|
53
56
|
1,
|
|
54
|
-
).replace(
|
|
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.
|
|
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
|
}
|