react-native-bundle-discovery 1.0.0-rc.8 → 1.0.0-rc.9

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.
@@ -0,0 +1,19 @@
1
+ module.exports = {
2
+ name: "react-native-bundle-discovery",
3
+ data: () => require("./tmp/before_metro-stats.json"),
4
+ setup: "./setup.js",
5
+ view: {
6
+ assets: [
7
+ // Global styles
8
+ "views/global.css",
9
+ // Pages
10
+ "pages/default.js",
11
+ "pages/module.js",
12
+ "pages/package.js",
13
+ // Custom views
14
+ "views/highcharts.css",
15
+ "views/highcharts.js",
16
+ "views/foamtree.js",
17
+ ],
18
+ },
19
+ };
package/lib/bin.js CHANGED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ const path = require("path");
3
+ const discovery = require("@discoveryjs/cli");
4
+ const config = require("../.discoveryrc.js");
5
+
6
+ const filePath = process.argv[2];
7
+
8
+ if (!filePath) {
9
+ console.error(
10
+ "Usage: `npx react-native-bundle-discovery <path-to-file>`, Please provide a path to a JSON file.",
11
+ );
12
+ process.exit(1);
13
+ }
14
+
15
+ const jsonFilePath = path.resolve(process.cwd(), filePath);
16
+
17
+ let data;
18
+
19
+ try {
20
+ data = require(jsonFilePath);
21
+ } catch (err) {
22
+ console.error(`Error loading file: ${jsonFilePath}`);
23
+ console.error(err.message);
24
+ process.exit(1);
25
+ }
26
+
27
+ const PORT = process.env.PORT || 8079;
28
+
29
+ discovery
30
+ .createServer({ ...config, data: () => data })
31
+ .then((server) =>
32
+ server.listen(PORT, () =>
33
+ console.log(`Server listen on http://localhost:${this.address().port}`),
34
+ ),
35
+ );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-bundle-discovery",
3
- "version": "1.0.0-rc.8",
3
+ "version": "1.0.0-rc.9",
4
4
  "main": "index.js",
5
5
  "bin": "lib/bin.js",
6
6
  "repository": "git@github.com:retyui/react-native-bundle-discovery.git",
@@ -9,10 +9,10 @@
9
9
  "scripts": {
10
10
  "format": "prettier --write .",
11
11
  "start": "NODE_ENV=development npx discovery --config .discoveryrc.js",
12
- "build": "npx discovery-build --config .discoveryrc.js --output build --serve-only-assets --single-file",
13
- "postbuild": "yarn print-size",
14
- "print-size": "npx @delucis/filesize-cli build/index.html",
15
- "postinstall": "yarn patch-package"
12
+ "build": "npx discovery-build --config .discoveryrc.js --output build --serve-only-assets --single-file"
13
+ },
14
+ "dependencies": {
15
+ "@discoveryjs/cli": "2.14.2"
16
16
  },
17
17
  "peerDependencies": {
18
18
  "metro": "*"
@@ -23,16 +23,17 @@
23
23
  }
24
24
  },
25
25
  "files": [
26
+ "vendors",
26
27
  "lib",
27
- "index.js"
28
+ "views",
29
+ "pages",
30
+ "index.js",
31
+ ".discoveryrc.js"
28
32
  ],
29
33
  "devDependencies": {
30
- "@carrotsearch/foamtree": "3.5.1",
31
- "@discoveryjs/cli": "2.14.2",
32
34
  "@discoveryjs/discovery": "1.0.0-beta.93",
33
35
  "highcharts": "12.2.0",
34
36
  "lodash": "4.17.21",
35
- "patch-package": "8.0.0",
36
37
  "prettier": "3.5.3"
37
38
  },
38
39
  "packageManager": "yarn@4.6.0"
@@ -0,0 +1,266 @@
1
+ const platformColor = `transformOptions.platform = 'android' ? 'rgba(194, 239, 116, .4)' : 'rgba(119, 31, 218, .4)'`;
2
+
3
+ function getPackage(entry) {
4
+ return `
5
+ $packages: $.packages;
6
+ $totalSize: $.modules.sum(=>output.sizeInBytes);
7
+ $toModule: => {
8
+ ext: $.path.getFileExtension(),
9
+ name: $.path,
10
+ size: $.output.sizeInBytes.formatBytes(),
11
+ percent: ($.output.sizeInBytes / $totalSize).percent(3),
12
+ };
13
+
14
+ ${entry}.group(=> path.getModulesName())
15
+ .map(=> ({
16
+ $pkgName: $.key;
17
+ pkgName: $pkgName, // example: lodash
18
+ size: $.value.sum(=>output.sizeInBytes),
19
+ pkgInstances: $.value
20
+ .group(=> path.split($pkgName).pick(0) + $pkgName)
21
+ .map(=> {
22
+ $pkgNameWithPath: $.key;
23
+ pkgName: $pkgNameWithPath, // example: node_modules/lodash
24
+ version: $packages.[path = $pkgNameWithPath][0].version,
25
+ size: $.value.sum(=> output.sizeInBytes),
26
+ modules: $.value.map(=> $.$toModule()),
27
+ }),
28
+ }))`;
29
+ }
30
+
31
+ function getPackageList({
32
+ data,
33
+ itemPkgName,
34
+ showCopiesBadge,
35
+ expanded,
36
+ limit,
37
+ subLimit,
38
+ }) {
39
+ return {
40
+ view: "list",
41
+ data,
42
+ emptyText: "⚠️ No packages found",
43
+ limit,
44
+ item: {
45
+ view: "tree",
46
+ expanded,
47
+ itemConfig: {
48
+ content: [
49
+ itemPkgName,
50
+ "text: ' '",
51
+ "pill-badge:{ text: size.formatBytes(), color: 'rgba(120, 177, 9, 0.35)' }",
52
+ showCopiesBadge
53
+ ? {
54
+ view: "pill-badge",
55
+ when: "pkgInstances.size() > 1",
56
+ data: "(pkgInstances.size() - 1).pluralBadge(['copy','copies'], '+')",
57
+ color: "rgba(255, 0, 0, 0.35)",
58
+ }
59
+ : null,
60
+ {
61
+ view: "pill-badge",
62
+ data: "pkgInstances.modules.size().pluralBadge(['file','files'])",
63
+ },
64
+ ].filter(Boolean),
65
+ children: `$.pkgInstances`,
66
+ itemConfig: {
67
+ view: "tree-leaf",
68
+ limit: subLimit,
69
+ content: [
70
+ //
71
+ "text:pkgName",
72
+ "text:' '",
73
+ "pill-badge:{ text: 'v' + version, color: '#0af' }",
74
+ "pill-badge:{ text: size.formatBytes(), color: 'rgba(120, 177, 9, 0.35)' }",
75
+ {
76
+ view: "pill-badge",
77
+ data: "modules.size().pluralBadge(['file','files'])",
78
+ },
79
+ ],
80
+ children: `$.modules`,
81
+ itemConfig: {
82
+ view: "tree-leaf",
83
+ content: getTreeModule({ hasPercent: true }),
84
+ },
85
+ },
86
+ },
87
+ },
88
+ };
89
+ }
90
+
91
+ function getTreeModule({ hasTextMatch = false, hasPercent = false } = {}) {
92
+ return [
93
+ "pill-badge:{ text: ext, color: ext.getExtColor() }",
94
+ hasTextMatch
95
+ ? {
96
+ view: "link",
97
+ content: hasTextMatch ? "text-match" : "text",
98
+ data: `{
99
+ href: name.pageLink("module", {}),
100
+ text: name,
101
+ match: #.filterByPathStr
102
+ }`,
103
+ }
104
+ : {
105
+ view: "link",
106
+ data: `{ href: $.name.pageLink("module", {}), text: $.name }`,
107
+ },
108
+ "text:' '",
109
+ {
110
+ view: "badge",
111
+ when: "isEntry",
112
+ text: "Entrypoint",
113
+ color: "gold",
114
+ textColor: "black",
115
+ },
116
+ "pill-badge:{ text: size, color: 'rgba(120, 177, 9, 0.35)' }",
117
+ hasPercent
118
+ ? "pill-badge:{ text: percent, color: 'rgba(120, 177, 9, 0.35)' }"
119
+ : null,
120
+ ].filter(Boolean);
121
+ }
122
+ function getModulesTree({ data, limit }) {
123
+ if (!data) {
124
+ throw new Error("[getModulesTree]: data is required");
125
+ }
126
+ return {
127
+ view: "content-filter",
128
+ data,
129
+ name: "filterByPathStr",
130
+ content: {
131
+ view: "list",
132
+ limit,
133
+ data: ".[name ~= #.filterByPathStr]",
134
+ emptyText: "⚠️ No modules found",
135
+ item: {
136
+ view: "tree",
137
+ expanded: false,
138
+ itemConfig: {
139
+ content: getTreeModule({ hasTextMatch: true }),
140
+ children: `
141
+ [
142
+ {
143
+ title:'Imported by modules',
144
+ data: $.reasons,
145
+ type: 'reasons',
146
+ },
147
+ {
148
+ title:'Similar copies',
149
+ data: $.duplicates,
150
+ type: 'duplicates',
151
+ }
152
+ ].filter(=> $.data.size() > 0)
153
+ `,
154
+ itemConfig: {
155
+ view: "switch",
156
+ content: [
157
+ {
158
+ when: 'type="reasons"',
159
+ content: {
160
+ view: "tree-leaf",
161
+ content: [
162
+ "text:title",
163
+ "text:' '",
164
+ "badge:{ text: $.data.size() }",
165
+ ],
166
+ children: `$.data`,
167
+ itemConfig: {
168
+ view: "tree-leaf",
169
+ content: getTreeModule(),
170
+ },
171
+ },
172
+ },
173
+ {
174
+ when: 'type="duplicates"',
175
+ content: {
176
+ view: "tree-leaf",
177
+ content: [
178
+ "text:title",
179
+ "text:' '",
180
+ "badge:{ text: $.data.size() }",
181
+ ],
182
+ children: `$.data`,
183
+ itemConfig: {
184
+ view: "tree-leaf",
185
+ content: getTreeModule(),
186
+ },
187
+ },
188
+ },
189
+ ],
190
+ },
191
+ },
192
+ },
193
+ },
194
+ };
195
+ }
196
+
197
+ const metadata = {
198
+ platform: {
199
+ when: "transformOptions.platform",
200
+ view: "badge",
201
+ data: `{ prefix: 'Platform: ', text: transformOptions.platform, color: ${platformColor} }`,
202
+ },
203
+ size: {
204
+ when: "modules.filter(=> $.path has 'node_modules').size()",
205
+ view: "badge",
206
+ data: `{ prefix: 'Size: ', text: modules.sum(=>output.sizeInBytes).formatBytes(), color: ${platformColor} }`,
207
+ },
208
+ node_modules_size: {
209
+ when: "modules.filter(=> $.path has 'node_modules').size()",
210
+ view: "badge",
211
+ data: "{ prefix: 'node_modules: ', text: modules.filter(=> $.path has 'node_modules').sum(=>output.sizeInBytes).formatBytes(), color: 'rgba(255, 0, 0, 0.35)' }",
212
+ },
213
+ source_code_size: {
214
+ when: "modules.filter(=> $.path has 'node_modules').size()",
215
+ view: "badge",
216
+ data: `
217
+ // vars
218
+ $totalSize: modules.sum(=>output.sizeInBytes);
219
+ $thirdPartySize: modules.filter(=> $.path has 'node_modules').sum(=>output.sizeInBytes);
220
+ // return data
221
+ { prefix: 'Source code: ', text: ($totalSize - $thirdPartySize).formatBytes(), color: 'rgba(148, 111, 234, 0.5)' }`,
222
+ },
223
+ is_dev: {
224
+ when: "transformOptions.dev != null",
225
+ view: "badge",
226
+ data: "{ prefix: '__DEV__: ', text: transformOptions.dev }",
227
+ },
228
+ is_minified: {
229
+ when: "transformOptions.minify != null",
230
+ view: "badge",
231
+ data: "{ prefix: 'Minify: ', text: transformOptions.minify }",
232
+ },
233
+ };
234
+
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
+
237
+ function getCopyToClipboardButton({ textToCopy, className }) {
238
+ return {
239
+ view: "button",
240
+ className: `${className ?? ""} copy-to-clipboard`,
241
+ data: `{ textToCopy: ${textToCopy} }`,
242
+ onClick(elm, data) {
243
+ clearTimeout(elm.__timer);
244
+ navigator.clipboard
245
+ .writeText(data.textToCopy)
246
+ .then(() => {
247
+ elm.classList.add("done");
248
+ elm.__timer = setTimeout(() => elm.classList.remove("done"), 2000);
249
+ })
250
+ .catch(() => {
251
+ elm.classList.add("err");
252
+ elm.__timer = setTimeout(() => elm.classList.remove("err"), 2000);
253
+ });
254
+ },
255
+ };
256
+ }
257
+
258
+ module.exports = {
259
+ getCopyToClipboardButton,
260
+ getPackage,
261
+ getPackageList,
262
+ externalLinkHtml,
263
+ getTreeModule,
264
+ getModulesTree,
265
+ metadata,
266
+ };
@@ -0,0 +1,194 @@
1
+ const {
2
+ metadata,
3
+ getModulesTree,
4
+ getPackage,
5
+ getPackageList,
6
+ } = require("./_common");
7
+
8
+ const topMetaData = [
9
+ metadata.platform,
10
+ metadata.size,
11
+ metadata.source_code_size,
12
+ metadata.node_modules_size,
13
+ metadata.is_dev,
14
+ metadata.is_minified,
15
+ ];
16
+
17
+ const TABS = {
18
+ TREEMAP_FOAMTREE: "treemap-foamtree",
19
+ MODULES: "modules",
20
+ PACKAGES: "packages",
21
+ DUPLICATES: "duplicates",
22
+ };
23
+
24
+ function parseHashRef(url = window.location.href) {
25
+ return new URL(url).hash.split(":")?.[1];
26
+ }
27
+
28
+ discovery.page.define("default", [
29
+ ...topMetaData,
30
+
31
+ {
32
+ view: "tabs",
33
+ name: "mainTabs",
34
+ className: "main-tabs",
35
+ value: parseHashRef() ?? TABS.TREEMAP_FOAMTREE,
36
+ tabs: [
37
+ {
38
+ value: TABS.TREEMAP_FOAMTREE,
39
+ text: "Treemap chart",
40
+ className: `main-tabs-${TABS.TREEMAP_FOAMTREE}`,
41
+ },
42
+ {
43
+ value: TABS.PACKAGES,
44
+ className: `main-tabs-${TABS.PACKAGES}`,
45
+ content: [
46
+ "text:'Packages '",
47
+ `pill-badge: modules.filter(=> path has "node_modules").group(=> path.getModulesName(), => 0).size()`,
48
+ ],
49
+ },
50
+ {
51
+ value: TABS.MODULES,
52
+ className: `main-tabs-${TABS.MODULES}`,
53
+ content: ["text:'Modules '", "pill-badge: modules.size()"],
54
+ },
55
+ {
56
+ value: TABS.DUPLICATES,
57
+ className: `main-tabs-${TABS.DUPLICATES}`,
58
+ content: [
59
+ "text:'Duplicate modules '",
60
+ "pill-badge: modules.filter(=> duplicates).size()",
61
+ ],
62
+ },
63
+ ],
64
+ content: {
65
+ view: "switch",
66
+ context(data, context) {
67
+ // FIXME - common nobody handle navigation in such way :(
68
+ const isHashChangeEvent = new Error("").stack.includes("Promise.all");
69
+ discovery.overridePageHashStateWithAnchor({
70
+ id: "default",
71
+ ref: isHashChangeEvent ? discovery.pageRef : context.mainTabs,
72
+ });
73
+ discovery.cancelScheduledRender();
74
+
75
+ const id = discovery.pageRef ?? TABS.TREEMAP_FOAMTREE;
76
+
77
+ setTimeout(() => {
78
+ discovery.dom.root
79
+ .querySelectorAll(".main-tabs>div>.onclick")
80
+ .forEach((e) => e.classList.remove("active"));
81
+ discovery.dom.root
82
+ .querySelectorAll(`.main-tabs .main-tabs-${id}`)
83
+ .forEach((e) => e.classList.add("active"));
84
+ }, 50);
85
+
86
+ return {
87
+ ...context,
88
+ id,
89
+ };
90
+ },
91
+ content: [
92
+ {
93
+ when: `#.id="${TABS.TREEMAP_FOAMTREE}"`,
94
+ content: {
95
+ view: "content-filter",
96
+ name: "filterByPathStr",
97
+ debounce: 300,
98
+ className: "foamtree-filter",
99
+ content: {
100
+ view: "foamtree",
101
+ data: `
102
+ $root: $.rootFolder;
103
+ $applyFilter: => #.filterByPathStr ? $.modules.filter(=> $.path ~= #.filterByPathStr) : $.modules;
104
+ $dataObject: $.$applyFilter()
105
+ .map(=> {path, size: $.output.sizeInBytes})
106
+ .transformFilesList($root, "foamtree");
107
+
108
+ {
109
+ options: {
110
+ dataObject: $dataObject,
111
+ descriptionGroupSize: 0.05,
112
+ descriptionGroupMinHeight: 30,
113
+ }
114
+ }
115
+ `,
116
+ },
117
+ },
118
+ },
119
+ {
120
+ when: `#.id="${TABS.MODULES}"`,
121
+ content: getModulesTree({
122
+ limit: 200,
123
+ data: `
124
+ $entryPoint: $.entryPointPath;
125
+ $totalSize: modules.sum(=>output.sizeInBytes);
126
+ $toModule: => {
127
+ ext: $.path.getFileExtension(),
128
+ name: $.path,
129
+ size: $.output.sizeInBytes.formatBytes(),
130
+ percent: ($.output.sizeInBytes / $totalSize).percent(3),
131
+ };
132
+ modules.map(=> {
133
+ ...$.$toModule(),
134
+ isEntry: $.isEntry,
135
+ reasons: $.dependents.map(=> $.$toModule()),
136
+ duplicates: $.duplicates.map(=> $.$toModule()),
137
+ })
138
+ `,
139
+ }),
140
+ },
141
+ {
142
+ when: `#.id="${TABS.PACKAGES}"`,
143
+ content: [
144
+ {
145
+ view: "content-filter",
146
+ data: `${getPackage(`modules.filter(=> path has "node_modules")`)}.sort(pkgInstances desc, size desc)`,
147
+ name: "filterByPathStr",
148
+ content: getPackageList({
149
+ showCopiesBadge: true,
150
+ expanded: false,
151
+ limit: 200,
152
+ subLimit: 50,
153
+ data: ".[pkgName ~= #.filterByPathStr]",
154
+ itemPkgName: {
155
+ view: "link",
156
+ content: "text-match",
157
+ data: `{
158
+ href: pkgName.pageLink("package", {}),
159
+ text: pkgName,
160
+ match: #.filterByPathStr
161
+ }`,
162
+ },
163
+ }),
164
+ },
165
+ ],
166
+ },
167
+ {
168
+ when: `#.id="${TABS.DUPLICATES}"`,
169
+ content: getModulesTree({
170
+ data: `
171
+ // values
172
+ $duplicatesOnly: modules.filter(=> duplicates);
173
+ $totalSize: $duplicatesOnly.sum(=>output.sizeInBytes);
174
+ $toModule: => {
175
+ ext: $.path.getFileExtension(),
176
+ name: $.path,
177
+ size: $.output.sizeInBytes.formatBytes(),
178
+ percent: ($.output.sizeInBytes / $totalSize).percent(3),
179
+ };
180
+ // return
181
+ $duplicatesOnly.map(=> {
182
+ ...$.$toModule(),
183
+ reasons: $.dependents.map(=> $.$toModule()),
184
+ duplicates: $.duplicates.map(=> $.$toModule()),
185
+ })
186
+ `,
187
+ }),
188
+ },
189
+ ],
190
+ },
191
+ },
192
+
193
+ // END
194
+ ]);