react-native-bundle-discovery 1.0.0 → 1.1.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/.discoveryrc.js +2 -1
- package/README.md +30 -8
- package/lib/customSerializer.js +1 -1
- package/package.json +5 -6
- package/pages/_common.js +2 -1
- package/pages/default.js +17 -0
- package/pages/module.js +22 -4
- package/prepare.js +0 -3
- package/queryHelpers.js +31 -0
- package/views/global.css +37 -0
- package/views/prettify.js +17 -0
package/.discoveryrc.js
CHANGED
|
@@ -2,7 +2,7 @@ const path = require("path");
|
|
|
2
2
|
|
|
3
3
|
module.exports = {
|
|
4
4
|
name: "react-native-bundle-discovery",
|
|
5
|
-
data: () => require("./tmp/
|
|
5
|
+
data: () => require("./tmp/rainbow-metro-stats.json"),
|
|
6
6
|
setup: path.resolve(__dirname, "setup.js"),
|
|
7
7
|
view: {
|
|
8
8
|
assets: [
|
|
@@ -14,6 +14,7 @@ module.exports = {
|
|
|
14
14
|
path.resolve(__dirname, "pages/package.js"),
|
|
15
15
|
// Custom views
|
|
16
16
|
path.resolve(__dirname, "views/highcharts.css"),
|
|
17
|
+
path.resolve(__dirname, "views/prettify.js"),
|
|
17
18
|
path.resolve(__dirname, "views/highcharts.js"),
|
|
18
19
|
path.resolve(__dirname, "views/foamtree.js"),
|
|
19
20
|
],
|
package/README.md
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
# react-native-bundle-discovery
|
|
2
2
|
|
|
3
|
-
> [!WARNING]
|
|
4
|
-
> Currently, everything is in a very early stage. The project is not yet ready for use.
|
|
5
|
-
|
|
6
|
-
|
|
7
3
|
A simple package that helps developers visualize and analyze the bundle size of React Native apps.
|
|
8
4
|
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
5
|
|
|
10
6
|
<img width="800" alt="" src="./assets/img.png" />
|
|
11
7
|
|
|
8
|
+
|
|
9
|
+
### What you should consider when analyzing your bundle
|
|
10
|
+
|
|
11
|
+
1. if you use **React 19** then it's time to remove a `prop-types` package from your bundle.
|
|
12
|
+
2. Search for `development|debug|dev` modules in your production JS bundle (it's a dead code that should not be there).
|
|
13
|
+
3. Also check polyfills duplicates (search example: `url|fetch|crypto|buffer|base-?64`).
|
|
14
|
+
4. Verify that you don't have any similar packages in your bundle (search example: `object|just-|debounce|ramda|lodash|underscore`). I often see that devs use both `lodash/debounce`, `lodash.debounce` and `debounce` in their projects. Or mix `ramda`, `underscore` and `lodash`._
|
|
15
|
+
5. Try to find `package.json$` files in your bundle. They are often used to get only the package `version/name` but all other fields are not needed in the bundle.
|
|
16
|
+
6. Please provide your ideas soo other devs can benefit from them :)
|
|
17
|
+
|
|
18
|
+
|
|
12
19
|
### Setup:
|
|
13
20
|
|
|
14
21
|
#### 1. Install
|
|
22
|
+
|
|
15
23
|
```bash
|
|
16
24
|
yarn add -D react-native-bundle-discovery
|
|
17
25
|
```
|
|
@@ -23,9 +31,9 @@ yarn add -D react-native-bundle-discovery
|
|
|
23
31
|
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
|
24
32
|
+const {createSerializer} = require('react-native-bundle-discovery');
|
|
25
33
|
|
|
26
|
-
+const mySerializer = createSerializer({
|
|
34
|
+
+const mySerializer = createSerializer({
|
|
27
35
|
+ includeCode: true, // Useful if you want to compare source/bundle code (but a report file will be larger)
|
|
28
|
-
+ projectRoot: __dirname,
|
|
36
|
+
+ projectRoot: __dirname,
|
|
29
37
|
+ //^^^ ⚠️ WARNING: In a monorepo setup, this should point to the monorepo root,
|
|
30
38
|
+ // not the individual package directory.
|
|
31
39
|
+});
|
|
@@ -63,6 +71,21 @@ npx react-native-bundle-discovery metro-stats.json
|
|
|
63
71
|
|
|
64
72
|
---
|
|
65
73
|
|
|
74
|
+
### `createSerializer(optiosn: Options)`
|
|
75
|
+
|
|
76
|
+
| Prop | Default value | Description |
|
|
77
|
+
| ---------------------- | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
|
78
|
+
| serializer: Function | Default serializer | A custom serializer function. If not provided, a default serializer is used. |
|
|
79
|
+
| projectRoot: string | Required | The root directory of the project. ⚠️ In a monorepo setup, this should point to the monorepo root, not the individual package directory. |
|
|
80
|
+
| outputJsonPath: string | `<root>/metro-stats.json` | The path where the JSON report will be saved. Defaults to `metro-stats.json` in project root. |
|
|
81
|
+
| includeCode: boolean | `true` | Whether to include the source and output code in the JSON report. |
|
|
82
|
+
|
|
83
|
+
### Financial Contributors
|
|
84
|
+
|
|
85
|
+
Become a financial contributor at [OpenCollective](https://opencollective.com/react-native-bundle-discovery) or [GitHub Sponsors](https://github.com/sponsors/retyui)
|
|
86
|
+
|
|
87
|
+
### Other
|
|
88
|
+
|
|
66
89
|
**Similar projects:**
|
|
67
90
|
|
|
68
91
|
- https://github.com/expo/atlas
|
|
@@ -72,8 +95,7 @@ npx react-native-bundle-discovery metro-stats.json
|
|
|
72
95
|
- https://github.com/statoscope/statoscope
|
|
73
96
|
- https://github.com/relative-ci/bundle-stats/tree/master/packages/cli
|
|
74
97
|
|
|
75
|
-
|
|
98
|
+
**Built using [Discovery.js](https://github.com/discoveryjs/discovery):**
|
|
76
99
|
|
|
77
|
-
**Built using Discovery.js:**
|
|
78
100
|
- Build blocks for pages: https://discoveryjs.github.io/discovery/#views-showcase
|
|
79
101
|
- Jora syntax: https://discoveryjs.github.io/jora/#article:jora-syntax-operators
|
package/lib/customSerializer.js
CHANGED
|
@@ -106,7 +106,7 @@ function createJsonReport({
|
|
|
106
106
|
return acc;
|
|
107
107
|
}, {}),
|
|
108
108
|
rootFolder,
|
|
109
|
-
packages: toPackages(dependencies),
|
|
109
|
+
packages: toPackages(preModules).concat(toPackages(dependencies)),
|
|
110
110
|
modules: preModules
|
|
111
111
|
.map((m) => toModuleStruct(m, includeCode))
|
|
112
112
|
.concat(dependencies.map((m) => toModuleStruct(m, includeCode))),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-bundle-discovery",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
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,12 @@
|
|
|
12
12
|
"build": "npx discovery-build --config .discoveryrc.js --output build --serve-only-assets --single-file"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
+
"@babel/parser": "^7.0.0",
|
|
15
16
|
"@discoveryjs/cli": "2.14.2",
|
|
16
17
|
"@discoveryjs/discovery": "1.0.0-beta.93",
|
|
17
18
|
"chalk": "^4.1.2",
|
|
18
|
-
"highcharts": "12.2.0"
|
|
19
|
+
"highcharts": "12.2.0",
|
|
20
|
+
"prettier": "^3.5.3"
|
|
19
21
|
},
|
|
20
22
|
"peerDependencies": {
|
|
21
23
|
"metro": "*"
|
|
@@ -35,8 +37,5 @@
|
|
|
35
37
|
"prepare.js",
|
|
36
38
|
"queryHelpers.js",
|
|
37
39
|
".discoveryrc.js"
|
|
38
|
-
]
|
|
39
|
-
"devDependencies": {
|
|
40
|
-
"prettier": "3.5.3"
|
|
41
|
-
}
|
|
40
|
+
]
|
|
42
41
|
}
|
package/pages/_common.js
CHANGED
|
@@ -234,10 +234,11 @@ const metadata = {
|
|
|
234
234
|
|
|
235
235
|
const externalLinkHtml = `<svg class="my-icon my-icon-link" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" ><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6M15 3h6v6M10 14 21 3"></path></svg>`;
|
|
236
236
|
|
|
237
|
-
function getCopyToClipboardButton({ textToCopy, className }) {
|
|
237
|
+
function getCopyToClipboardButton({ textToCopy, className, text }) {
|
|
238
238
|
return {
|
|
239
239
|
view: "button",
|
|
240
240
|
className: `${className ?? ""} copy-to-clipboard`,
|
|
241
|
+
text,
|
|
241
242
|
data: `{ textToCopy: ${textToCopy} }`,
|
|
242
243
|
onClick(elm, data) {
|
|
243
244
|
clearTimeout(elm.__timer);
|
package/pages/default.js
CHANGED
|
@@ -3,6 +3,7 @@ const {
|
|
|
3
3
|
getModulesTree,
|
|
4
4
|
getPackage,
|
|
5
5
|
getPackageList,
|
|
6
|
+
getCopyToClipboardButton,
|
|
6
7
|
} = require("./_common");
|
|
7
8
|
|
|
8
9
|
const topMetaData = [
|
|
@@ -141,9 +142,25 @@ discovery.page.define("default", [
|
|
|
141
142
|
{
|
|
142
143
|
when: `#.id="${TABS.PACKAGES}"`,
|
|
143
144
|
content: [
|
|
145
|
+
getCopyToClipboardButton({
|
|
146
|
+
text: "Copy list",
|
|
147
|
+
className: "copy-before-ask-chatgpt",
|
|
148
|
+
textToCopy: `"- " + modules.filter(=> path has "node_modules").group(=> path.getModulesName()).map(=> $.key).join("\\n- ")`,
|
|
149
|
+
}),
|
|
150
|
+
{
|
|
151
|
+
view: "link",
|
|
152
|
+
className: "ask-chatgpt view-button",
|
|
153
|
+
text: "and Ask ChatGPT",
|
|
154
|
+
external: true,
|
|
155
|
+
data: `{
|
|
156
|
+
href: $.askChatGPTAboutPackages()
|
|
157
|
+
}`,
|
|
158
|
+
},
|
|
144
159
|
{
|
|
145
160
|
view: "content-filter",
|
|
161
|
+
//
|
|
146
162
|
data: `${getPackage(`modules.filter(=> path has "node_modules")`)}.sort(pkgInstances desc, size desc)`,
|
|
163
|
+
className: "packages-content",
|
|
147
164
|
name: "filterByPathStr",
|
|
148
165
|
content: getPackageList({
|
|
149
166
|
showCopiesBadge: true,
|
package/pages/module.js
CHANGED
|
@@ -119,11 +119,29 @@ discovery.page.define("module", {
|
|
|
119
119
|
view: "block",
|
|
120
120
|
className: "width-50p",
|
|
121
121
|
content: [
|
|
122
|
-
"h5: 'Output'",
|
|
123
122
|
{
|
|
124
|
-
view: "
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
view: "h5",
|
|
124
|
+
content: 'text:"Output"',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
view: "context",
|
|
128
|
+
modifiers: [
|
|
129
|
+
{
|
|
130
|
+
view: "checkbox",
|
|
131
|
+
name: "prettify",
|
|
132
|
+
checked: true,
|
|
133
|
+
className: "prettify-checkbox",
|
|
134
|
+
content: 'text:"Prettify"',
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
content: [
|
|
138
|
+
{
|
|
139
|
+
view: "source-prettify",
|
|
140
|
+
syntax: "ts",
|
|
141
|
+
source:
|
|
142
|
+
"=(#.prettify ? $.currentModule.output.code.prettifyJS() : $.currentModule.output.code)",
|
|
143
|
+
},
|
|
144
|
+
],
|
|
127
145
|
},
|
|
128
146
|
],
|
|
129
147
|
},
|
package/prepare.js
CHANGED
package/queryHelpers.js
CHANGED
|
@@ -1,4 +1,35 @@
|
|
|
1
1
|
const helpers = {
|
|
2
|
+
prettifyMap: new Map(),
|
|
3
|
+
prettifyJS(sourceStr) {
|
|
4
|
+
if (helpers.prettifyMap.has(sourceStr)) {
|
|
5
|
+
return helpers.prettifyMap.get(sourceStr);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const prettier = require("prettier/standalone");
|
|
9
|
+
const prettierPluginBabel = require("prettier/plugins/babel");
|
|
10
|
+
const prettierPluginEstree = require("prettier/plugins/estree");
|
|
11
|
+
|
|
12
|
+
return prettier
|
|
13
|
+
.format(sourceStr, {
|
|
14
|
+
parser: "babel",
|
|
15
|
+
plugins: [prettierPluginBabel, prettierPluginEstree],
|
|
16
|
+
})
|
|
17
|
+
.then((formatted) => {
|
|
18
|
+
helpers.prettifyMap.set(sourceStr, formatted);
|
|
19
|
+
return formatted;
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
askChatGPTAboutPackages() {
|
|
23
|
+
const prompt = `I have a list of JavaScript packages from my React Native bundle (see below). I want to optimize bundle size by identifying similar or redundant packages — such as multiple versions of similar libraries (e.g., lodash, lodash-es, underscore, etc.), duplicate utilities, or overlapping functionality (e.g., date libraries like moment, dayjs, date-fns).
|
|
24
|
+
|
|
25
|
+
Please do the following:
|
|
26
|
+
|
|
27
|
+
1. Group similar or overlapping packages together.
|
|
28
|
+
2. For each group, suggest which one to keep and which ones to consider removing.
|
|
29
|
+
3. For each group, provide a regex string that can be used to filter those packages from the list (e.g., in grep, find, or search tools).
|
|
30
|
+
4. Keep the output concise and copy-paste friendly.`;
|
|
31
|
+
return `https://chat.openai.com/?prompt=${encodeURIComponent(prompt)}`;
|
|
32
|
+
},
|
|
2
33
|
plural(count, [singular, plural]) {
|
|
3
34
|
return count === 1 ? singular : plural;
|
|
4
35
|
},
|
package/views/global.css
CHANGED
|
@@ -125,3 +125,40 @@ hr{
|
|
|
125
125
|
background: red;
|
|
126
126
|
opacity: 1;
|
|
127
127
|
}
|
|
128
|
+
.prettify-checkbox{
|
|
129
|
+
position: absolute;
|
|
130
|
+
right: 31px;
|
|
131
|
+
margin-top: -26px;
|
|
132
|
+
z-index: 1;
|
|
133
|
+
}
|
|
134
|
+
.ask-chatgpt{
|
|
135
|
+
color: var(--discovery-view-button-color, #000);
|
|
136
|
+
text-decoration: none;
|
|
137
|
+
vertical-align: middle;
|
|
138
|
+
position: absolute;
|
|
139
|
+
z-index: 9;
|
|
140
|
+
margin-top: 12px;
|
|
141
|
+
margin-left: 105px !important;
|
|
142
|
+
height: 35px;
|
|
143
|
+
}
|
|
144
|
+
.ask-chatgpt::before {
|
|
145
|
+
content: '”';
|
|
146
|
+
font-size: 2em;
|
|
147
|
+
vertical-align: middle;
|
|
148
|
+
font-family: math;
|
|
149
|
+
margin-right: 0.2em;
|
|
150
|
+
display: inline-block;
|
|
151
|
+
height: 15px;
|
|
152
|
+
}
|
|
153
|
+
.packages-content>.view-input{
|
|
154
|
+
padding-left: 265px;
|
|
155
|
+
}
|
|
156
|
+
.copy-before-ask-chatgpt{
|
|
157
|
+
width: 95px;
|
|
158
|
+
background-position: left center;
|
|
159
|
+
padding-left: 25px;
|
|
160
|
+
position: absolute;
|
|
161
|
+
z-index: 9;
|
|
162
|
+
margin-top: 12px;
|
|
163
|
+
height: 35px;
|
|
164
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
discovery.view.define(
|
|
2
|
+
"source-prettify",
|
|
3
|
+
async function renderPrettify(el, config, data, context) {
|
|
4
|
+
await this.render(
|
|
5
|
+
el,
|
|
6
|
+
this.composeConfig([
|
|
7
|
+
{
|
|
8
|
+
...config,
|
|
9
|
+
source: await config.source,
|
|
10
|
+
view: "source",
|
|
11
|
+
},
|
|
12
|
+
]),
|
|
13
|
+
data,
|
|
14
|
+
context,
|
|
15
|
+
);
|
|
16
|
+
},
|
|
17
|
+
);
|