react-native-bundle-discovery-ui 2.1.0-rc.2 → 2.2.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 +6 -27
- package/lib/bin.js +5 -15
- package/lib/build.js +3 -10
- package/lib/server.js +3 -11
- package/package.json +1 -1
- package/lib/rsdoctor.js +0 -149
package/README.md
CHANGED
|
@@ -3,13 +3,11 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/react-native-bundle-discovery)
|
|
4
4
|
[](https://www.npmtrends.com/react-native-bundle-discovery)
|
|
5
5
|
|
|
6
|
-
|
|
7
6
|
A simple package that helps developers visualize and analyze the bundle size of React Native apps.
|
|
8
7
|
With this tool, you can easily explore your app's codebase, identify large or heavy packages, and inspect the structure of modules and code within your project.
|
|
9
8
|
|
|
10
9
|
<img width="800" alt="" src="./assets/img.png" />
|
|
11
10
|
|
|
12
|
-
|
|
13
11
|
### Packages:
|
|
14
12
|
|
|
15
13
|
- `react-native-bundle-discovery` - simple JS library to generate a JSON report of the bundle.
|
|
@@ -21,7 +19,7 @@ With this tool, you can easily explore your app's codebase, identify large or he
|
|
|
21
19
|
|
|
22
20
|
There are two ways to install the package:
|
|
23
21
|
|
|
24
|
-
1. As in independent tool (UI + CLI)
|
|
22
|
+
1. As in independent tool (UI + CLI)
|
|
25
23
|
2. Or as a [Rozenite](_rozenite/README.md) plugin (see: [_rozenite/README.md](_rozenite/README.md))
|
|
26
24
|
|
|
27
25
|
#### 1. Install (independent tool)
|
|
@@ -99,20 +97,6 @@ npx react-native-bundle-discovery-ui metro-stats.json [--port <port>]
|
|
|
99
97
|
npx react-native-bundle-discovery-ui build metro-stats.json
|
|
100
98
|
```
|
|
101
99
|
|
|
102
|
-
---
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
### Re.Pack Support
|
|
106
|
-
|
|
107
|
-
If you've migrated to [Re.Pack](https://re-pack.dev/docs/guides/bundle-analysis), you can still use this package to analyze bundles generated by Re.Pack.
|
|
108
|
-
|
|
109
|
-
Learn more: [Rsdoctor.md](./Rsdoctor.md)
|
|
110
|
-
|
|
111
|
-
```bash
|
|
112
|
-
npx react-native-bundle-discovery-ui rsdoctor-data.json --format rsdoctor
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
|
|
116
100
|
---
|
|
117
101
|
|
|
118
102
|
### `react-native-bundle-discovery` JS API
|
|
@@ -126,8 +110,6 @@ npx react-native-bundle-discovery-ui rsdoctor-data.json --format rsdoctor
|
|
|
126
110
|
| outputJsonPath: string | `<root>/metro-stats.json` | The path where the JSON report will be saved. Defaults to `metro-stats.json` in project root. |
|
|
127
111
|
| includeCode: boolean | `true` | Whether to include the source and output code in the JSON report. |
|
|
128
112
|
|
|
129
|
-
|
|
130
|
-
|
|
131
113
|
**⚠️ Be cautious sharing reports with `includeCode` enabled**
|
|
132
114
|
|
|
133
115
|
Enabling this option significantly increases report size and **embeds the source code** of your assets.
|
|
@@ -138,22 +120,19 @@ If your code is proprietary 🔒, share these reports responsibly 🤝!
|
|
|
138
120
|
|
|
139
121
|
### `createResolveRequest(options: ResolveRequestOptions)`
|
|
140
122
|
|
|
141
|
-
| Prop
|
|
142
|
-
|
|
|
143
|
-
| removePromisePolyfill: boolean | `false`
|
|
144
|
-
|
|
|
145
|
-
| removeNewRenderer: boolean | `false` | Whether to remove the new renderer. Set to `true` when New Arch is disabled |
|
|
146
|
-
| removeUTFSequence: boolean | `false` | Remove useless undocumented RN module. |
|
|
123
|
+
| Prop | Default value | Description |
|
|
124
|
+
| ------------------------------ | ------------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
125
|
+
| removePromisePolyfill: boolean | `false` | Remove the Promise polyfill as Hermes provide own impl. (issue: [#57702](https://github.com/react/react-native/issues/57702)) |
|
|
126
|
+
| removeUTFSequence: boolean | `false` | Remove useless undocumented RN module. |
|
|
147
127
|
|
|
148
128
|
Helper that creates a custom resolve request function to filter out unnecessary modules from the bundle.
|
|
149
129
|
You can get recommendations during the analysis of the bundle report using the CLI tool.
|
|
150
130
|
|
|
151
131
|
```js
|
|
152
132
|
// metro.config.js
|
|
153
|
-
const {createResolveRequest} = require(
|
|
133
|
+
const { createResolveRequest } = require("react-native-bundle-discovery");
|
|
154
134
|
const resolveRequest = createResolveRequest({
|
|
155
135
|
removePromisePolyfill: true,
|
|
156
|
-
removeOldRenderer: true,
|
|
157
136
|
removeUTFSequence: true,
|
|
158
137
|
});
|
|
159
138
|
const config = {
|
package/lib/bin.js
CHANGED
|
@@ -5,10 +5,10 @@ function printHelp() {
|
|
|
5
5
|
console.log(`react-native-bundle-discovery-ui
|
|
6
6
|
|
|
7
7
|
Usage:
|
|
8
|
-
react-native-bundle-discovery-ui <file> [port] [--
|
|
9
|
-
react-native-bundle-discovery-ui server <file> [port] [--
|
|
8
|
+
react-native-bundle-discovery-ui <file> [port] [--verbose] # <- server can be omitted
|
|
9
|
+
react-native-bundle-discovery-ui server <file> [port] [--verbose]
|
|
10
10
|
|
|
11
|
-
react-native-bundle-discovery-ui build <file> [--output <path>] [--clean] [--single-file] [--
|
|
11
|
+
react-native-bundle-discovery-ui build <file> [--output <path>] [--clean] [--single-file] [--verbose]
|
|
12
12
|
|
|
13
13
|
Commands:
|
|
14
14
|
server <file> [port] Run a web server to show a Metro bundler stat report
|
|
@@ -19,7 +19,6 @@ Options:
|
|
|
19
19
|
-o, --output <path> Path for a build result (default: .bundle-discovery)
|
|
20
20
|
-c, --clean Clean output directory before writing build files (default: true)
|
|
21
21
|
-s, --single-file Output report build as a single HTML file (default: true)
|
|
22
|
-
-f, --format <name> Report format: rsdoctor | metro (default: metro)
|
|
23
22
|
-p, --port <port> Port for server command (same as [port], default: 8079)
|
|
24
23
|
-h, --help Show help
|
|
25
24
|
`);
|
|
@@ -38,24 +37,16 @@ const argv = minimist(process.argv.slice(2), {
|
|
|
38
37
|
o: "output",
|
|
39
38
|
c: "clean",
|
|
40
39
|
s: "single-file",
|
|
41
|
-
f: "format",
|
|
42
40
|
p: "port",
|
|
43
41
|
},
|
|
44
42
|
boolean: ["verbose", "help", "clean", "single-file"],
|
|
45
|
-
string: ["output"
|
|
43
|
+
string: ["output"],
|
|
46
44
|
default: {
|
|
47
45
|
clean: true,
|
|
48
46
|
"single-file": true,
|
|
49
|
-
format: "metro",
|
|
50
47
|
},
|
|
51
48
|
});
|
|
52
49
|
|
|
53
|
-
const SUPPORTED_FORMATS = new Set(["rsdoctor", "metro"]);
|
|
54
|
-
const format = argv.format;
|
|
55
|
-
if (!SUPPORTED_FORMATS.has(format)) {
|
|
56
|
-
fail(`Invalid format: ${format}. Supported formats: rsdoctor | metro`);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
50
|
const command = argv._[0];
|
|
60
51
|
const isServerCommand = command === "server";
|
|
61
52
|
const isBuildCommand = command === "build";
|
|
@@ -81,7 +72,7 @@ if (isServerCommand || isImplicitServerCommand) {
|
|
|
81
72
|
}
|
|
82
73
|
|
|
83
74
|
const { serve } = require("./server.js");
|
|
84
|
-
return serve(file, port,
|
|
75
|
+
return serve(file, port, Boolean(argv.verbose));
|
|
85
76
|
}
|
|
86
77
|
|
|
87
78
|
if (isBuildCommand) {
|
|
@@ -96,7 +87,6 @@ if (isBuildCommand) {
|
|
|
96
87
|
argv.output,
|
|
97
88
|
argv.clean,
|
|
98
89
|
argv["single-file"],
|
|
99
|
-
format,
|
|
100
90
|
Boolean(argv.verbose),
|
|
101
91
|
);
|
|
102
92
|
}
|
package/lib/build.js
CHANGED
|
@@ -4,7 +4,7 @@ const chalk = require("chalk");
|
|
|
4
4
|
const { build } = require("@discoveryjs/cli");
|
|
5
5
|
const config = require("../.discoveryrc.js");
|
|
6
6
|
|
|
7
|
-
function buildHtmlPage(filePath, output, clean, singleFile,
|
|
7
|
+
function buildHtmlPage(filePath, output, clean, singleFile, verbose) {
|
|
8
8
|
if (verbose) {
|
|
9
9
|
console.info(
|
|
10
10
|
`Building HTML page from file: ${chalk.green(filePath)} options: ${JSON.stringify(
|
|
@@ -46,20 +46,13 @@ function buildHtmlPage(filePath, output, clean, singleFile, format, verbose) {
|
|
|
46
46
|
`Creating temporary config file at: ${chalk.green(configFile)}, for discovery.js`,
|
|
47
47
|
);
|
|
48
48
|
}
|
|
49
|
-
const transformRcdoctor = require.resolve("./rsdoctor.js");
|
|
50
49
|
fs.writeFileSync(
|
|
51
50
|
configFile,
|
|
52
|
-
`
|
|
53
|
-
module.exports = ${JSON.stringify(
|
|
51
|
+
`module.exports = ${JSON.stringify(
|
|
54
52
|
{ ...config, data: "<tmp>" },
|
|
55
53
|
null,
|
|
56
54
|
1,
|
|
57
|
-
).replace(
|
|
58
|
-
`"<tmp>"`,
|
|
59
|
-
format === "rsdoctor"
|
|
60
|
-
? `() => transformRSDoctorData(require("${fullJsonPath}"))`
|
|
61
|
-
: `() => require("${fullJsonPath}")`,
|
|
62
|
-
)};`,
|
|
55
|
+
).replace(`"<tmp>"`, `() => require("${fullJsonPath}")`)};`,
|
|
63
56
|
);
|
|
64
57
|
|
|
65
58
|
if (verbose) {
|
package/lib/server.js
CHANGED
|
@@ -5,7 +5,7 @@ const { createServer } = require("@discoveryjs/cli");
|
|
|
5
5
|
const { silent } = require("@discoveryjs/cli/lib/shared/utils.js");
|
|
6
6
|
const config = require("../.discoveryrc.js");
|
|
7
7
|
|
|
8
|
-
function serve(filePath, port,
|
|
8
|
+
function serve(filePath, port, verbose) {
|
|
9
9
|
const PORT = process.env.PORT || port;
|
|
10
10
|
|
|
11
11
|
if (verbose) {
|
|
@@ -45,21 +45,13 @@ function serve(filePath, port, format, verbose) {
|
|
|
45
45
|
`Creating temporary config file at: ${chalk.green(configFile)}, for discovery.js`,
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
|
-
const transformRcdoctor = require.resolve("./rsdoctor.js");
|
|
49
48
|
fs.writeFileSync(
|
|
50
49
|
configFile,
|
|
51
|
-
`
|
|
52
|
-
module.exports = ${JSON.stringify(
|
|
50
|
+
`module.exports = ${JSON.stringify(
|
|
53
51
|
{ ...config, data: "<tmp>" },
|
|
54
52
|
null,
|
|
55
53
|
1,
|
|
56
|
-
).replace(
|
|
57
|
-
`"<tmp>"`,
|
|
58
|
-
|
|
59
|
-
format === "rsdoctor"
|
|
60
|
-
? `() => transformRSDoctorData(require("${fullJsonPath}"))`
|
|
61
|
-
: `() => require("${fullJsonPath}")`,
|
|
62
|
-
)};`,
|
|
54
|
+
).replace(`"<tmp>"`, `() => require("${fullJsonPath}")`)};`,
|
|
63
55
|
);
|
|
64
56
|
|
|
65
57
|
if (verbose) {
|
package/package.json
CHANGED
package/lib/rsdoctor.js
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
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' is 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
|
-
};
|