react-native-bundle-discovery-ui 2.0.0 → 2.1.0-rc.1

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
@@ -99,6 +99,20 @@ npx react-native-bundle-discovery-ui metro-stats.json [--port <port>]
99
99
  npx react-native-bundle-discovery-ui build metro-stats.json
100
100
  ```
101
101
 
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-report.json --format rsdoctor
113
+ ```
114
+
115
+
102
116
  ---
103
117
 
104
118
  ### `react-native-bundle-discovery` JS API
@@ -112,8 +126,7 @@ npx react-native-bundle-discovery-ui build metro-stats.json
112
126
  | outputJsonPath: string | `<root>/metro-stats.json` | The path where the JSON report will be saved. Defaults to `metro-stats.json` in project root. |
113
127
  | includeCode: boolean | `true` | Whether to include the source and output code in the JSON report. |
114
128
 
115
- ### `createProcessModuleFilter(options: ProcessModuleFilterOptions)`
116
-
129
+ ### `createResolveRequest(options: ResolveRequestOptions)`
117
130
 
118
131
  | Prop | Default value | Description |
119
132
  | ---------------------- | ------------------------- | ----------------------------- |
@@ -122,20 +135,20 @@ npx react-native-bundle-discovery-ui build metro-stats.json
122
135
  | removeNewRenderer: boolean | `false` | Whether to remove the new renderer. Set to `true` when New Arch is disabled |
123
136
  | removeUTFSequence: boolean | `false` | Remove useless undocumented RN module. |
124
137
 
125
-
126
- Simple helper that devs can use to filter out unnecessary modules from the bundle report.
138
+ Helper that creates a custom resolve request function to filter out unnecessary modules from the bundle.
127
139
  You can get recommendations during the analysis of the bundle report using the CLI tool.
128
140
 
129
141
  ```js
130
142
  // metro.config.js
131
- const {createProcessModuleFilter} = require('react-native-bundle-discovery');
143
+ const {createResolveRequest} = require('react-native-bundle-discovery');
144
+ const resolveRequest = createResolveRequest({
145
+ removePromisePolyfill: true,
146
+ removeOldRenderer: true,
147
+ removeUTFSequence: true,
148
+ });
132
149
  const config = {
133
- serializer: {
134
- processModuleFilter: createProcessModuleFilter({
135
- removePromisePolyfill: true,
136
- removeOldRenderer: true,
137
- removeUTFSequence: true,
138
- }),
150
+ resolver: {
151
+ resolveRequest,
139
152
  },
140
153
  };
141
154
  ```
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] [--verbose] # <- server can be omitted
9
- react-native-bundle-discovery-ui server <file> [port] [--verbose]
8
+ react-native-bundle-discovery-ui <file> [port] [--format <rsdoctor|metro>] [--verbose] # <- server can be omitted
9
+ react-native-bundle-discovery-ui server <file> [port] [--format <rsdoctor|metro>] [--verbose]
10
10
 
11
- react-native-bundle-discovery-ui build <file> [--output <path>] [--clean] [--single-file] [--verbose]
11
+ react-native-bundle-discovery-ui build <file> [--output <path>] [--clean] [--single-file] [--format <rsdoctor|metro>] [--verbose]
12
12
 
13
13
  Commands:
14
14
  server <file> [port] Run a web server to show a Metro bundler stat report
@@ -19,6 +19,7 @@ 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)
22
23
  -p, --port <port> Port for server command (same as [port], default: 8079)
23
24
  -h, --help Show help
24
25
  `);
@@ -37,16 +38,24 @@ const argv = minimist(process.argv.slice(2), {
37
38
  o: "output",
38
39
  c: "clean",
39
40
  s: "single-file",
41
+ f: "format",
40
42
  p: "port",
41
43
  },
42
44
  boolean: ["verbose", "help", "clean", "single-file"],
43
- string: ["output"],
45
+ string: ["output", "format"],
44
46
  default: {
45
47
  clean: true,
46
48
  "single-file": true,
49
+ format: "metro",
47
50
  },
48
51
  });
49
52
 
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
+
50
59
  const command = argv._[0];
51
60
  const isServerCommand = command === "server";
52
61
  const isBuildCommand = command === "build";
@@ -72,7 +81,7 @@ if (isServerCommand || isImplicitServerCommand) {
72
81
  }
73
82
 
74
83
  const { serve } = require("./server.js");
75
- return serve(file, port, Boolean(argv.verbose));
84
+ return serve(file, port, format, Boolean(argv.verbose));
76
85
  }
77
86
 
78
87
  if (isBuildCommand) {
@@ -87,6 +96,7 @@ if (isBuildCommand) {
87
96
  argv.output,
88
97
  argv.clean,
89
98
  argv["single-file"],
99
+ format,
90
100
  Boolean(argv.verbose),
91
101
  );
92
102
  }
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, verbose) {
7
+ function buildHtmlPage(filePath, output, clean, singleFile, format, verbose) {
8
8
  if (verbose) {
9
9
  console.info(
10
10
  `Building HTML page from file: ${chalk.green(filePath)} options: ${JSON.stringify(
@@ -46,13 +46,20 @@ function buildHtmlPage(filePath, output, clean, singleFile, 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");
49
50
  fs.writeFileSync(
50
51
  configFile,
51
- `module.exports = ${JSON.stringify(
52
+ `const {transformRSDoctorData} = require("${transformRcdoctor}");
53
+ module.exports = ${JSON.stringify(
52
54
  { ...config, data: "<tmp>" },
53
55
  null,
54
56
  1,
55
- ).replace(`"<tmp>"`, `() => require("${fullJsonPath}")`)};`,
57
+ ).replace(
58
+ `"<tmp>"`,
59
+ format === "rsdoctor"
60
+ ? `() => transformRSDoctorData(require("${fullJsonPath}"))`
61
+ : `() => require("${fullJsonPath}")`,
62
+ )};`,
56
63
  );
57
64
 
58
65
  if (verbose) {
@@ -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") {
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
+ };
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, verbose) {
8
+ function serve(filePath, port, format, verbose) {
9
9
  const PORT = process.env.PORT || port;
10
10
 
11
11
  if (verbose) {
@@ -45,13 +45,21 @@ function serve(filePath, port, 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");
48
49
  fs.writeFileSync(
49
50
  configFile,
50
- `module.exports = ${JSON.stringify(
51
+ `const {transformRSDoctorData} = require("${transformRcdoctor}");
52
+ module.exports = ${JSON.stringify(
51
53
  { ...config, data: "<tmp>" },
52
54
  null,
53
55
  1,
54
- ).replace(`"<tmp>"`, `() => require("${fullJsonPath}")`)};`,
56
+ ).replace(
57
+ `"<tmp>"`,
58
+
59
+ format === "rsdoctor"
60
+ ? `() => transformRSDoctorData(require("${fullJsonPath}"))`
61
+ : `() => require("${fullJsonPath}")`,
62
+ )};`,
55
63
  );
56
64
 
57
65
  if (verbose) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-native-bundle-discovery-ui",
3
- "version": "2.0.0",
4
- "main": "index.js",
3
+ "version": "2.1.0-rc.1",
4
+ "main": "lib/bin.js",
5
5
  "bin": "lib/bin.js",
6
6
  "repository": "git@github.com:retyui/react-native-bundle-discovery.git",
7
7
  "author": "David <4661784+retyui@users.noreply.github.com>",
@@ -29,6 +29,7 @@
29
29
  "files": [
30
30
  "vendors",
31
31
  "lib",
32
+ "!lib/.tmp.js",
32
33
  "views",
33
34
  "pages",
34
35
  "index.js",
package/queryHelpers.js CHANGED
@@ -211,7 +211,7 @@ Please do the following:
211
211
  return nodeModulesGroup;
212
212
  }
213
213
  if (!nodeModulesGroup.groups) {
214
- return nodeModulesGroup;
214
+ return sourceCodeGroup;
215
215
  }
216
216
 
217
217
  const topLevelNode = { groups: [sourceCodeGroup, nodeModulesGroup] };
package/index.js DELETED
@@ -1 +0,0 @@
1
- module.exports = require("./lib/customSerializer");