react-native-bundle-discovery-ui 2.3.0 → 2.5.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 +29 -1
- package/package.json +4 -1
- package/pages/_common.js +60 -3
- package/pages/module.js +22 -12
- package/pages/package.js +94 -0
- package/queryHelpers.js +26 -8
- package/vendors/highcharts.js +16601 -10
- package/views/foamtree.js +1 -0
package/README.md
CHANGED
|
@@ -24,7 +24,6 @@ There are two ways to install the package:
|
|
|
24
24
|
1. As in independent tool (UI + CLI)
|
|
25
25
|
2. Or as a [Rozenite](_rozenite/README.md) plugin (see: [_rozenite/README.md](_rozenite/README.md))
|
|
26
26
|
|
|
27
|
-
|
|
28
27
|
> 🚀 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
28
|
>
|
|
30
29
|
> 💡 Learn more: 📖 [Re.Pack.md](./Re.Pack.md)
|
|
@@ -90,6 +89,35 @@ npx react-native-bundle-discovery-cli metro-stats.json
|
|
|
90
89
|
|
|
91
90
|
# Display all packages in the bundle report
|
|
92
91
|
npx react-native-bundle-discovery-cli packages metro-stats.json [--sort size|name] [--format json|table|default]
|
|
92
|
+
|
|
93
|
+
# Display the heaviest modules in the bundle report (default limit: 50, --limit 0 shows all)
|
|
94
|
+
# --filter accepts plain text (case-insensitive) or a regexp: --filter '/\.json/i'
|
|
95
|
+
npx react-native-bundle-discovery-cli modules metro-stats.json [--limit 50] [--filter <text|/regexp/>] [--sort size|name] [--format json|table|default]
|
|
96
|
+
|
|
97
|
+
# Compare two bundle reports: total size, added/removed/changed modules and packages,
|
|
98
|
+
# version changes, new duplicates and new deprecated packages
|
|
99
|
+
npx react-native-bundle-discovery-cli compare --before main-stats.json --after pr-stats.json [--limit 50] [--format json|markdown|default]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
##### Bundle size checks in CI
|
|
103
|
+
|
|
104
|
+
`compare` exits with code `1` when one of the checks fails:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# --fail-on-increase: max growth, in bytes (50KB, 0.5MB, 51200) or percent of the "before" size (5%)
|
|
108
|
+
# --max-size: absolute limit for the "after" bundle
|
|
109
|
+
# --fail-on: fail when new duplicate or new deprecated packages appear
|
|
110
|
+
npx react-native-bundle-discovery-cli compare --before main-stats.json --after pr-stats.json \
|
|
111
|
+
--fail-on-increase 50KB \
|
|
112
|
+
--max-size 3MB \
|
|
113
|
+
--fail-on new-duplicates,new-deprecated
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
On GitHub Actions no extra setup is needed: the markdown report is added to the job summary
|
|
117
|
+
and failed checks are shown as error annotations. Use `--format markdown` to get the same report for a PR comment.
|
|
118
|
+
|
|
119
|
+
```yaml
|
|
120
|
+
- run: npx react-native-bundle-discovery-cli compare --before main-stats.json --after metro-stats.json --fail-on-increase 5%
|
|
93
121
|
```
|
|
94
122
|
|
|
95
123
|
#### UI package
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-bundle-discovery-ui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0-rc.1",
|
|
4
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>",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"scripts": {
|
|
10
|
+
"srv": "node ./lib/bin.js server",
|
|
11
|
+
"analyze": "node ./_cli/bin.js analyze",
|
|
12
|
+
"tmp": "yarn srv /Users/davyd.narbutovich/tmp/RNDiscovery/metro-stats.json",
|
|
10
13
|
"format": "prettier --write .",
|
|
11
14
|
"start": "NODE_ENV=development npx discovery --config .discoveryrc.js",
|
|
12
15
|
"build": "npx discovery-build --config .discoveryrc.js --output build --serve-only-assets --single-file"
|
package/pages/_common.js
CHANGED
|
@@ -17,17 +17,56 @@ function getPackage(entry) {
|
|
|
17
17
|
pkgName: $pkgName, // example: lodash
|
|
18
18
|
size: $.value.sum(=>output.sizeInBytes),
|
|
19
19
|
pkgInstances: $.value
|
|
20
|
-
.group(=> path.split($pkgName).pick(0) + $pkgName)
|
|
20
|
+
.group(=> path.split('node_modules/' + $pkgName).pick(0) + 'node_modules/' + $pkgName)
|
|
21
21
|
.map(=> {
|
|
22
22
|
$pkgNameWithPath: $.key;
|
|
23
|
+
$pkgInfo: $packages.[path = $pkgNameWithPath][0];
|
|
23
24
|
pkgName: $pkgNameWithPath, // example: node_modules/lodash
|
|
24
|
-
version: $
|
|
25
|
+
version: $pkgInfo.version,
|
|
26
|
+
metadata: $pkgInfo.metadata,
|
|
25
27
|
size: $.value.sum(=> output.sizeInBytes),
|
|
26
28
|
modules: $.value.map(=> $.$toModule()),
|
|
27
29
|
}),
|
|
28
30
|
}))`;
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
const metadataTooltip = {
|
|
34
|
+
view: "text",
|
|
35
|
+
when: "metadata.createdAt",
|
|
36
|
+
data: "'Published ' + metadata.createdAt.formatDate() + ' (' + metadata.createdAt.timeAgo() + ')'",
|
|
37
|
+
};
|
|
38
|
+
const outdatedColor = "rgba(255, 165, 0, 0.35)";
|
|
39
|
+
|
|
40
|
+
// Compact npm metadata badges (`metadata` from the report)
|
|
41
|
+
function getMetadataBadges({
|
|
42
|
+
deprecated,
|
|
43
|
+
outdated,
|
|
44
|
+
latestVersion,
|
|
45
|
+
renderLatestVersion,
|
|
46
|
+
}) {
|
|
47
|
+
return [
|
|
48
|
+
{
|
|
49
|
+
view: "pill-badge",
|
|
50
|
+
when: deprecated,
|
|
51
|
+
data: `{ text: '⚠️ deprecated', message: ${deprecated} }`,
|
|
52
|
+
color: "#cf222e",
|
|
53
|
+
textColor: "white",
|
|
54
|
+
darkColor: "#f85149",
|
|
55
|
+
darkTextColor: "white",
|
|
56
|
+
tooltip: "text: message",
|
|
57
|
+
},
|
|
58
|
+
renderLatestVersion
|
|
59
|
+
? {
|
|
60
|
+
view: "pill-badge",
|
|
61
|
+
when: outdated,
|
|
62
|
+
data: `{ text: '↑ v' + ${latestVersion} }`,
|
|
63
|
+
color: outdatedColor,
|
|
64
|
+
tooltip: "text: 'Latest version on npm'",
|
|
65
|
+
}
|
|
66
|
+
: null,
|
|
67
|
+
].filter(Boolean);
|
|
68
|
+
}
|
|
69
|
+
|
|
31
70
|
function getPackageList({
|
|
32
71
|
data,
|
|
33
72
|
itemPkgName,
|
|
@@ -49,6 +88,13 @@ function getPackageList({
|
|
|
49
88
|
itemPkgName,
|
|
50
89
|
"text: ' '",
|
|
51
90
|
"pill-badge:{ text: size.formatBytes(), color: 'rgba(120, 177, 9, 0.35)' }",
|
|
91
|
+
// Summary of all instances (visible when the tree is collapsed)
|
|
92
|
+
...getMetadataBadges({
|
|
93
|
+
deprecated: "pkgInstances.metadata.deprecated[0]",
|
|
94
|
+
outdated: "pkgInstances.[metadata and not metadata.isLatest]",
|
|
95
|
+
latestVersion: "pkgInstances.metadata.latestVersion[0]",
|
|
96
|
+
renderLatestVersion: false,
|
|
97
|
+
}),
|
|
52
98
|
showCopiesBadge
|
|
53
99
|
? {
|
|
54
100
|
view: "pill-badge",
|
|
@@ -70,12 +116,23 @@ function getPackageList({
|
|
|
70
116
|
//
|
|
71
117
|
"text:pkgName",
|
|
72
118
|
"text:' '",
|
|
73
|
-
|
|
119
|
+
{
|
|
120
|
+
view: "pill-badge",
|
|
121
|
+
data: "{ text: 'v' + version, metadata }",
|
|
122
|
+
color: "#0af",
|
|
123
|
+
tooltip: metadataTooltip,
|
|
124
|
+
},
|
|
74
125
|
"pill-badge:{ text: size.formatBytes(), color: 'rgba(120, 177, 9, 0.35)' }",
|
|
75
126
|
{
|
|
76
127
|
view: "pill-badge",
|
|
77
128
|
data: "modules.size().pluralBadge(['file','files'])",
|
|
78
129
|
},
|
|
130
|
+
...getMetadataBadges({
|
|
131
|
+
deprecated: "metadata.deprecated",
|
|
132
|
+
outdated: "metadata and not metadata.isLatest",
|
|
133
|
+
latestVersion: "metadata.latestVersion",
|
|
134
|
+
renderLatestVersion: true,
|
|
135
|
+
}),
|
|
79
136
|
],
|
|
80
137
|
children: `$.modules`,
|
|
81
138
|
itemConfig: {
|
package/pages/module.js
CHANGED
|
@@ -11,15 +11,17 @@ function getMetroWarn({ when, href, linkText, content }) {
|
|
|
11
11
|
className: "m-v-8",
|
|
12
12
|
content: [
|
|
13
13
|
...content,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
14
|
+
linkText && href
|
|
15
|
+
? {
|
|
16
|
+
view: "link",
|
|
17
|
+
data: {
|
|
18
|
+
external: true,
|
|
19
|
+
href,
|
|
20
|
+
text: linkText,
|
|
21
|
+
},
|
|
22
|
+
}
|
|
23
|
+
: null,
|
|
24
|
+
].filter(Boolean),
|
|
23
25
|
};
|
|
24
26
|
}
|
|
25
27
|
|
|
@@ -30,7 +32,8 @@ discovery.page.define("module", {
|
|
|
30
32
|
...$,
|
|
31
33
|
currentModule: $currentModule,
|
|
32
34
|
isEntry: $currentModule.isEntry,
|
|
33
|
-
isInjectedByMetro: $currentModule.path = '__prelude__' or $currentModule.path has '@react-native/js-polyfills',
|
|
35
|
+
isInjectedByMetro: $currentModule.path = '__prelude__' or $currentModule.path has '@react-native/js-polyfills',
|
|
36
|
+
isWebpackRuntime: $currentModule.path has '__runtime__',
|
|
34
37
|
}`,
|
|
35
38
|
content: [
|
|
36
39
|
{
|
|
@@ -57,12 +60,12 @@ discovery.page.define("module", {
|
|
|
57
60
|
},
|
|
58
61
|
{
|
|
59
62
|
value: "networkGraph",
|
|
60
|
-
when: "not isInjectedByMetro and not isEntry",
|
|
63
|
+
when: "not isInjectedByMetro and not isEntry and not isWebpackRuntime",
|
|
61
64
|
text: "Imported by modules",
|
|
62
65
|
},
|
|
63
66
|
{
|
|
64
67
|
value: "mduplicates",
|
|
65
|
-
when: "not isInjectedByMetro and not isEntry and currentModule.duplicates",
|
|
68
|
+
when: "not isInjectedByMetro and not isEntry and not isWebpackRuntime and currentModule.duplicates",
|
|
66
69
|
text: "Duplicates",
|
|
67
70
|
},
|
|
68
71
|
],
|
|
@@ -81,6 +84,13 @@ discovery.page.define("module", {
|
|
|
81
84
|
href: "https://github.com/facebook/metro/blob/f595e9e1c9021ad372b489be8958cc298eb1e0d9/packages/metro/src/lib/getPreludeCode.js#L25-L33",
|
|
82
85
|
linkText: "getPreludeCode.js",
|
|
83
86
|
}),
|
|
87
|
+
getMetroWarn({
|
|
88
|
+
when: "$.currentModule.path = '__runtime__'",
|
|
89
|
+
content: [
|
|
90
|
+
"html: '<b>__runtime__</b>'",
|
|
91
|
+
"text: ' is a special runtime code that was generated by the Webpack/Rspack bundler'",
|
|
92
|
+
],
|
|
93
|
+
}),
|
|
84
94
|
getMetroWarn({
|
|
85
95
|
when: "$.currentModule.path has '@react-native/js-polyfills'",
|
|
86
96
|
content: [
|
package/pages/package.js
CHANGED
|
@@ -24,6 +24,10 @@ discovery.page.define("package", {
|
|
|
24
24
|
currentPkgWithUniqVer: $pkg.pkgName + ($copies = 0 ? '@' + $ver : ''),
|
|
25
25
|
currentPkgWithUniqVerNpm: $pkg.pkgName + ($copies = 0 ? '/v/' + $ver : ''),
|
|
26
26
|
currentPkg: $pkg,
|
|
27
|
+
currentPkgInstancesWithMetadata: $pkg.pkgInstances.[metadata],
|
|
28
|
+
currentPkgDeprecated: $pkg.pkgInstances.[metadata.deprecated],
|
|
29
|
+
currentPkgOutdated: $pkg.pkgInstances.[metadata and not metadata.isLatest],
|
|
30
|
+
currentPkgLatestVersion: $pkg.pkgInstances.metadata.latestVersion[0],
|
|
27
31
|
}
|
|
28
32
|
`,
|
|
29
33
|
content: [
|
|
@@ -56,6 +60,22 @@ discovery.page.define("package", {
|
|
|
56
60
|
}`,
|
|
57
61
|
],
|
|
58
62
|
},
|
|
63
|
+
{
|
|
64
|
+
when: "$.currentPkgDeprecated",
|
|
65
|
+
view: "h2",
|
|
66
|
+
className: "inline-block no-margin",
|
|
67
|
+
content: `badge: { text: 'deprecated', color: "rgba(255, 0, 0, 0.35)" }`,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
when: "$.currentPkgOutdated",
|
|
71
|
+
view: "h2",
|
|
72
|
+
className: "inline-block no-margin",
|
|
73
|
+
content: `badge: {
|
|
74
|
+
prefix: 'latest',
|
|
75
|
+
text: 'v' + $.currentPkgLatestVersion,
|
|
76
|
+
color: "rgba(255, 165, 0, 0.35)"
|
|
77
|
+
}`,
|
|
78
|
+
},
|
|
59
79
|
getCopyToClipboardButton({
|
|
60
80
|
textToCopy: `$.currentPkg.pkgName`,
|
|
61
81
|
className: "m-l-0_5em",
|
|
@@ -63,6 +83,20 @@ discovery.page.define("package", {
|
|
|
63
83
|
],
|
|
64
84
|
},
|
|
65
85
|
|
|
86
|
+
{
|
|
87
|
+
when: "$.currentPkgDeprecated",
|
|
88
|
+
view: "list",
|
|
89
|
+
data: "$.currentPkgDeprecated",
|
|
90
|
+
item: {
|
|
91
|
+
view: "alert-danger",
|
|
92
|
+
content: [
|
|
93
|
+
"html: '<b>Deprecated</b> '",
|
|
94
|
+
"text: 'v' + version + (#.data.currentPkgHasCopies ? ' (' + pkgName + ')' : '') + ': '",
|
|
95
|
+
"text: metadata.deprecated",
|
|
96
|
+
],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
|
|
66
100
|
"html: '<br>'",
|
|
67
101
|
|
|
68
102
|
{
|
|
@@ -112,6 +146,66 @@ discovery.page.define("package", {
|
|
|
112
146
|
],
|
|
113
147
|
},
|
|
114
148
|
|
|
149
|
+
{
|
|
150
|
+
when: "$.currentPkgInstancesWithMetadata",
|
|
151
|
+
view: "block",
|
|
152
|
+
content: [
|
|
153
|
+
"h3: 'Versions'",
|
|
154
|
+
{
|
|
155
|
+
view: "table",
|
|
156
|
+
data: "$.currentPkgInstancesWithMetadata",
|
|
157
|
+
cols: [
|
|
158
|
+
{ header: "Path", content: "text: pkgName" },
|
|
159
|
+
{
|
|
160
|
+
header: "Version",
|
|
161
|
+
content: "pill-badge:{ text: 'v' + version, color: '#0af' }",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
header: "Published",
|
|
165
|
+
data: "metadata.createdAt",
|
|
166
|
+
content: [
|
|
167
|
+
"text: formatDate()",
|
|
168
|
+
"text: ' '",
|
|
169
|
+
"pill-badge:{ text: timeAgo() }",
|
|
170
|
+
],
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
header: "Latest",
|
|
174
|
+
content: [
|
|
175
|
+
{
|
|
176
|
+
view: "link",
|
|
177
|
+
external: true,
|
|
178
|
+
data: `{
|
|
179
|
+
href: "https://www.npmjs.com/package/" + #.data.currentPkg.pkgName + "/v/" + metadata.latestVersion,
|
|
180
|
+
text: 'v' + metadata.latestVersion,
|
|
181
|
+
}`,
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
header: "Status",
|
|
187
|
+
content: {
|
|
188
|
+
view: "switch",
|
|
189
|
+
content: [
|
|
190
|
+
{
|
|
191
|
+
when: "metadata.deprecated",
|
|
192
|
+
content: `badge: { text: 'deprecated', color: "rgba(255, 0, 0, 0.35)" }`,
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
when: "metadata.isLatest",
|
|
196
|
+
content: `badge: { text: 'latest', color: "rgba(120, 177, 9, 0.35)" }`,
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
content: `badge: { text: 'outdated', color: "rgba(255, 165, 0, 0.35)" }`,
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
|
|
115
209
|
"html: '<hr>'",
|
|
116
210
|
|
|
117
211
|
{
|
package/queryHelpers.js
CHANGED
|
@@ -144,6 +144,24 @@ Please do the following:
|
|
|
144
144
|
const idx = filename.lastIndexOf(".");
|
|
145
145
|
return idx === -1 ? "js" : filename.slice(idx + 1);
|
|
146
146
|
},
|
|
147
|
+
// "2022-06-14T19:46:38.369Z" -> "2022-06-14"
|
|
148
|
+
formatDate(isoDate) {
|
|
149
|
+
return isoDate ? new Date(isoDate).toISOString().slice(0, 10) : "";
|
|
150
|
+
},
|
|
151
|
+
// "2022-06-14T19:46:38.369Z" -> "3 years ago"
|
|
152
|
+
timeAgo(isoDate) {
|
|
153
|
+
if (!isoDate) return "";
|
|
154
|
+
const days = Math.floor((Date.now() - new Date(isoDate)) / 86_400_000);
|
|
155
|
+
const [count, unit] =
|
|
156
|
+
days >= 365
|
|
157
|
+
? [Math.floor(days / 365), ["year", "years"]]
|
|
158
|
+
: days >= 30
|
|
159
|
+
? [Math.floor(days / 30), ["month", "months"]]
|
|
160
|
+
: [days, ["day", "days"]];
|
|
161
|
+
return count === 0
|
|
162
|
+
? "today"
|
|
163
|
+
: `${helpers.pluralWithCount(count, unit)} ago`;
|
|
164
|
+
},
|
|
147
165
|
toFixed(value, fractionDigits = 2) {
|
|
148
166
|
return Number(value).toFixed(fractionDigits);
|
|
149
167
|
},
|
|
@@ -161,14 +179,6 @@ Please do the following:
|
|
|
161
179
|
isPackageImport(moduleName) {
|
|
162
180
|
return moduleName?.[0] !== ".";
|
|
163
181
|
},
|
|
164
|
-
// isRuntimeCode(moduleName) {
|
|
165
|
-
// return (
|
|
166
|
-
// moduleName === "__prelude__" ||
|
|
167
|
-
// moduleName.includes("@babel/runtime") ||
|
|
168
|
-
// moduleName.includes("metro-runtime") ||
|
|
169
|
-
// moduleName.includes("@react-native/js-polyfills")
|
|
170
|
-
// );
|
|
171
|
-
// },
|
|
172
182
|
transformFilesList(files, rootFolder, type) {
|
|
173
183
|
const nodeModulesMap = { children: {}, size: 0 };
|
|
174
184
|
const sourceCodeMap = { children: {}, size: 0 };
|
|
@@ -177,6 +187,9 @@ Please do the following:
|
|
|
177
187
|
if (path === "__prelude__") {
|
|
178
188
|
path = "node_modules/__prelude__";
|
|
179
189
|
}
|
|
190
|
+
if (path === "__runtime__") {
|
|
191
|
+
path = "node_modules/__runtime__";
|
|
192
|
+
}
|
|
180
193
|
|
|
181
194
|
const shortPath = path.replace(rootFolder + "/", "");
|
|
182
195
|
const isNodeModule = shortPath.includes("node_modules/");
|
|
@@ -195,6 +208,7 @@ Please do the following:
|
|
|
195
208
|
if (index === parts.length - 1) {
|
|
196
209
|
current[part].size = size;
|
|
197
210
|
current[part].path = shortPath;
|
|
211
|
+
current[part].fullPath = path;
|
|
198
212
|
}
|
|
199
213
|
|
|
200
214
|
current = current[part].children;
|
|
@@ -310,6 +324,7 @@ function toGroups(node, label) {
|
|
|
310
324
|
const common = {
|
|
311
325
|
label,
|
|
312
326
|
weight: node.size,
|
|
327
|
+
fullPath: node.fullPath,
|
|
313
328
|
files: node.files,
|
|
314
329
|
type: node.type,
|
|
315
330
|
size: helpers.formatBytes(node.size),
|
|
@@ -332,6 +347,9 @@ function randomInt(min, max) {
|
|
|
332
347
|
|
|
333
348
|
const nm = "node_modules/";
|
|
334
349
|
function shortenPath(path, nodeModulesMap) {
|
|
350
|
+
if (path.startsWith(nm + ".pnpm/")) {
|
|
351
|
+
path = path.replaceAll(nm + ".pnpm/", "");
|
|
352
|
+
}
|
|
335
353
|
let index = path.lastIndexOf(nm);
|
|
336
354
|
|
|
337
355
|
if (index > 0) {
|