react-native-bundle-discovery 1.0.0-rc.8 → 1.0.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.
@@ -0,0 +1,296 @@
1
+ const {
2
+ getModulesTree,
3
+ getTreeModule,
4
+ getCopyToClipboardButton,
5
+ } = require("./_common");
6
+
7
+ function getMetroWarn({ when, href, linkText, content }) {
8
+ return {
9
+ view: "alert-warning",
10
+ when,
11
+ className: "m-v-8",
12
+ content: [
13
+ ...content,
14
+ {
15
+ view: "link",
16
+ data: {
17
+ external: true,
18
+ href,
19
+ text: linkText,
20
+ },
21
+ },
22
+ ],
23
+ };
24
+ }
25
+
26
+ discovery.page.define("module", {
27
+ view: "context",
28
+ data: `{
29
+ $currentModule: $.modules.[path = #.id].pick(0);
30
+ ...$,
31
+ currentModule: $currentModule,
32
+ isEntry: $currentModule.isEntry,
33
+ isInjectedByMetro: $currentModule.path = '__prelude__' or $currentModule.path has '@react-native/js-polyfills',
34
+ }`,
35
+ content: [
36
+ {
37
+ view: "h2",
38
+ content: [
39
+ //
40
+ 'text: "Module: "',
41
+ "text: $.currentModule.path",
42
+ getCopyToClipboardButton({
43
+ textToCopy: `$.currentModule.path`,
44
+ className: "m-l-0_5em",
45
+ }),
46
+ ],
47
+ },
48
+
49
+ {
50
+ view: "tabs",
51
+ name: "tabs",
52
+ // value: "networkGraph", //FIXME
53
+ tabs: [
54
+ {
55
+ value: "mcontent",
56
+ text: "Module content",
57
+ },
58
+ {
59
+ value: "networkGraph",
60
+ when: "not isInjectedByMetro and not isEntry",
61
+ text: "Imported by modules",
62
+ },
63
+ {
64
+ value: "mduplicates",
65
+ when: "not isInjectedByMetro and not isEntry and currentModule.duplicates",
66
+ text: "Duplicates",
67
+ },
68
+ ],
69
+ content: {
70
+ view: "switch",
71
+ content: [
72
+ {
73
+ when: '#.tabs="mcontent"',
74
+ content: [
75
+ getMetroWarn({
76
+ when: "$.currentModule.path = '__prelude__'",
77
+ content: [
78
+ "html: '<b>__prelude__</b>'",
79
+ "text: ' is a special runtime code that was generated by the Metro bundler, see: '",
80
+ ],
81
+ href: "https://github.com/facebook/metro/blob/f595e9e1c9021ad372b489be8958cc298eb1e0d9/packages/metro/src/lib/getPreludeCode.js#L25-L33",
82
+ linkText: "getPreludeCode.js",
83
+ }),
84
+ getMetroWarn({
85
+ when: "$.currentModule.path has '@react-native/js-polyfills'",
86
+ content: [
87
+ "html: '<b>@react-native/js-polyfills/*</b>'",
88
+ "text: ' is a special package that was injected by the Metro bundler, see: '",
89
+ ],
90
+ href: "https://github.com/facebook/react-native/blob/198c6fc81cda67a46fff457cc39eb2a297eddc01/packages/metro-config/src/index.flow.js#L61",
91
+ linkText: "rn/metro-config/index.js",
92
+ }),
93
+ getMetroWarn({
94
+ when: "$.currentModule.path has 'metro-runtime'",
95
+ content: [
96
+ "html: '<b>metro-runtime/*</b>'",
97
+ "text: ' is a special package that was injected by the Metro bundler, see: '",
98
+ ],
99
+ href: "https://github.com/facebook/metro/blob/f595e9e1c9021ad372b489be8958cc298eb1e0d9/packages/metro-config/src/defaults/defaults.js#L61-L63",
100
+ linkText: "metro-config/index.js",
101
+ }),
102
+ {
103
+ view: "hstack",
104
+ className: "flex-no-wrap",
105
+ content: [
106
+ {
107
+ view: "block",
108
+ className: "width-50p",
109
+ content: [
110
+ "h5: 'Source'",
111
+ {
112
+ view: "source",
113
+ syntax: "ts",
114
+ source: "=$.currentModule.source.code",
115
+ },
116
+ ],
117
+ },
118
+ {
119
+ view: "block",
120
+ className: "width-50p",
121
+ content: [
122
+ "h5: 'Output'",
123
+ {
124
+ view: "source",
125
+ syntax: "ts",
126
+ source: "=$.currentModule.output.code",
127
+ },
128
+ ],
129
+ },
130
+ ],
131
+ },
132
+ ],
133
+ },
134
+ {
135
+ when: '#.tabs="networkGraph"',
136
+ content: {
137
+ view: "context",
138
+ context: `{ ...#, maxParentDepth: $.currentModule.getBestNetworkGraphSize() }`,
139
+ modifiers: [
140
+ "html: '<br/>'",
141
+ "text:'Max parent depth: '",
142
+ {
143
+ view: "input",
144
+ name: "maxParentDepth",
145
+ className: "inline-block",
146
+ htmlType: "number",
147
+ htmlMin: 1,
148
+ htmlMax: 10,
149
+ },
150
+ ],
151
+ content: [
152
+ {
153
+ view: "context",
154
+ data: `{
155
+ // Vars
156
+ $tmp: $.currentModule.getNetworkGraph({ maxParentDepth: #.maxParentDepth });
157
+ $limit: 150;
158
+ // Return data
159
+ currentModule: $.currentModule,
160
+ entryPointPath: $tmp.entryPointPath,
161
+ limit: $limit,
162
+ data: $tmp.data,
163
+ isGraphTooBig: $tmp.data.size() > $limit,
164
+ }`,
165
+ content: [
166
+ // TODO use for debugging
167
+ // {
168
+ // view: "struct",
169
+ // expanded: 2,
170
+ // },
171
+ {
172
+ when: "isGraphTooBig",
173
+ view: "block",
174
+ className: "m-v-8",
175
+ content: [
176
+ {
177
+ view: "alert-warning",
178
+ data: '"Amount of dependencies "+$.data.size()+" (limit: "+$.limit+") are too big to display as a graph :("',
179
+ },
180
+ ],
181
+ },
182
+ {
183
+ view: "hstack",
184
+ className: "flex-no-wrap two-column",
185
+ content: [
186
+ {
187
+ when: "not isGraphTooBig",
188
+ view: "highcharts",
189
+ data: `{
190
+ options: {
191
+ chart: {
192
+ type: "networkgraph",
193
+ height: "500px",
194
+ },
195
+ title: {
196
+ text: "Import Dependency Graph",
197
+ align: "left",
198
+ },
199
+ subtitle: {
200
+ text: "It shows which modules import this file (Red - current module, Gold - entry point)",
201
+ align: "left",
202
+ },
203
+ plotOptions: {
204
+ networkgraph: {
205
+ keys: ["from", "to"],
206
+ layoutAlgorithm: {
207
+ enableSimulation: true,
208
+ friction: -0.9,
209
+ gravitationalConstant: 0.06,
210
+ },
211
+ },
212
+ },
213
+ series: [
214
+ {
215
+ accessibility: {
216
+ enabled: false,
217
+ },
218
+ dataLabels: {
219
+ enabled: true,
220
+ linkFormat: "",
221
+ style: {
222
+ fontSize: "0.8em",
223
+ fontWeight: "normal",
224
+ },
225
+ },
226
+ id: "lang-tree",
227
+ data: $.data,
228
+ nodes:[
229
+ { id: $.currentModule.path, marker: { radius: 15, fillColor: 'red' }, },
230
+ { id: $.entryPointPath, marker: { radius: 15, fillColor: 'gold' } }
231
+ ].filter(=> id),
232
+ },
233
+ ],
234
+ },
235
+ }`,
236
+ },
237
+
238
+ {
239
+ view: "block",
240
+ content: [
241
+ "h4: 'Modules that import this file: ' + $.currentModule.path",
242
+ {
243
+ view: "list",
244
+ data: `
245
+ $totalSize: modules.sum(=>output.sizeInBytes);
246
+ $toModule: => {
247
+ $deepIdx: $$ + 1;
248
+ ext: $.path.getFileExtension(),
249
+ name: $.path,
250
+ size: $.output.sizeInBytes.formatBytes(),
251
+ percent: ($.output.sizeInBytes / $totalSize).percent(3),
252
+ children: $deepIdx < 2 ? $.dependents.map(=> $.$toModule()) : null,
253
+ };
254
+ $.currentModule.dependents.map(=> $.$toModule(0))
255
+ `,
256
+ emptyText: "⚠️ No importing modules found",
257
+ item: {
258
+ view: "tree",
259
+ expanded: false,
260
+ itemConfig: {
261
+ content: getTreeModule({
262
+ hasTextMatch: true,
263
+ }),
264
+ children: `$.children`,
265
+ },
266
+ },
267
+ },
268
+ ],
269
+ },
270
+ ],
271
+ },
272
+ ],
273
+ },
274
+ ],
275
+ },
276
+ },
277
+ {
278
+ when: '#.tabs="mduplicates"',
279
+ content: getModulesTree({
280
+ data: `
281
+ $totalSize: modules.sum(=>output.sizeInBytes);
282
+ $toModule: => {
283
+ ext: $.path.getFileExtension(),
284
+ name: $.path,
285
+ size: $.output.sizeInBytes.formatBytes(),
286
+ percent: ($.output.sizeInBytes / $totalSize).percent(3),
287
+ };
288
+ currentModule.duplicates.map(=> $.$toModule())
289
+ `,
290
+ }),
291
+ },
292
+ ],
293
+ },
294
+ },
295
+ ],
296
+ });
@@ -0,0 +1,150 @@
1
+ const { externalLinkHtml } = require("./_common.js");
2
+ const {
3
+ getTreeModule,
4
+ getPackage,
5
+ getPackageList,
6
+ getCopyToClipboardButton,
7
+ } = require("./_common");
8
+
9
+ discovery.page.define("package", {
10
+ view: "context",
11
+ data: `
12
+ // Tmp variables
13
+ $currentPkgName: #.id;
14
+ ${getPackage(`$pkg: modules.filter(=> path has "node_modules" and path has $currentPkgName)`)}[0];
15
+ $copies: $pkg.pkgInstances.size() - 1;
16
+ $ver: $pkg.pkgInstances[0].version;
17
+
18
+ // Return value
19
+ {
20
+ ...$,
21
+ currentPkgHasCopies: $copies > 0,
22
+ currentPkgCopiesCount: $copies,
23
+ currentPkgVersion: $ver,
24
+ currentPkgWithUniqVer: $pkg.pkgName + ($copies = 0 ? '@' + $ver : ''),
25
+ currentPkgWithUniqVerNpm: $pkg.pkgName + ($copies = 0 ? '/v/' + $ver : ''),
26
+ currentPkg: $pkg,
27
+ }
28
+ `,
29
+ content: [
30
+ // TODO uncomment for debugging
31
+ // {
32
+ // view: "struct",
33
+ // data: "$.currentPkg",
34
+ // },
35
+
36
+ {
37
+ view: "block",
38
+ content: [
39
+ {
40
+ view: "h1",
41
+ className: "inline-block no-margin",
42
+ content: `badge: {
43
+ text: $.currentPkgWithUniqVer,
44
+ color: "#fffb5a",
45
+ }`,
46
+ },
47
+ {
48
+ when: "$.currentPkgCopiesCount > 0",
49
+ view: "h2",
50
+ className: "inline-block no-margin",
51
+ content: [
52
+ `badge: {
53
+ text: '+' + $.currentPkgCopiesCount,
54
+ postfix: $.currentPkgCopiesCount = 1 ? 'copy' : 'copies',
55
+ color: "rgba(255, 0, 0, 0.35)"
56
+ }`,
57
+ ],
58
+ },
59
+ getCopyToClipboardButton({
60
+ textToCopy: `$.currentPkg.pkgName`,
61
+ className: "m-l-0_5em",
62
+ }),
63
+ ],
64
+ },
65
+
66
+ "html: '<br>'",
67
+
68
+ {
69
+ view: "block",
70
+ content: [
71
+ "text: 'Links: '",
72
+ {
73
+ view: "link",
74
+ external: true,
75
+ data: `{ href: "https://www.npmjs.com/package/" + currentPkgWithUniqVerNpm }`,
76
+ content: [
77
+ "text: 'npmjs.com'",
78
+ `html: '<span class="my-icon-inline my-icon-12">${externalLinkHtml}</span>'`,
79
+ ],
80
+ },
81
+ "text: ' '",
82
+ {
83
+ view: "link",
84
+ external: true,
85
+ data: `{ href: "https://bundlephobia.com/package/" + currentPkgWithUniqVer }`,
86
+ content: [
87
+ "text: 'bundlephobia.com'",
88
+ `html: '<span class="my-icon-inline my-icon-12">${externalLinkHtml}</span>'`,
89
+ ],
90
+ },
91
+ "text: ' '",
92
+ {
93
+ view: "link",
94
+ external: true,
95
+ data: `{ href: "https://packagephobia.com/result?p=" + currentPkgWithUniqVer }`,
96
+ content: [
97
+ "text: 'packagephobia.com'",
98
+ `html: '<span class="my-icon-inline my-icon-12">${externalLinkHtml}</span>'`,
99
+ ],
100
+ },
101
+ ],
102
+ },
103
+
104
+ "html: '<hr>'",
105
+
106
+ {
107
+ view: "block",
108
+ content: getPackageList({
109
+ data: "$.currentPkg",
110
+ itemPkgName: "text: pkgName",
111
+ showCopiesBadge: false,
112
+ expanded: true,
113
+ }),
114
+
115
+ /*[
116
+ {
117
+ view: "list",
118
+ data: "$.currentPkg",
119
+ item: {
120
+ view: "tree",
121
+ expanded: true,
122
+ itemConfig: {
123
+ content: [
124
+ "text: pkgName",
125
+ "text: ' '",
126
+ "pill-badge:{ text: size.formatBytes(), color: 'rgba(120, 177, 9, 0.35)' }",
127
+ ],
128
+ children: `$.pkgInstances`,
129
+ itemConfig: {
130
+ view: "tree-leaf",
131
+ content: [
132
+ //
133
+ "text:pkgName",
134
+ "text:' '",
135
+ "pill-badge:{ text: 'v' + version, color: '#0af' }",
136
+ "pill-badge:{ text: size.formatBytes(), color: 'rgba(120, 177, 9, 0.35)' }",
137
+ ],
138
+ children: `$.modules`,
139
+ itemConfig: {
140
+ view: "tree-leaf",
141
+ content: getTreeModule({ hasPercent: false }),
142
+ },
143
+ },
144
+ },
145
+ },
146
+ },
147
+ ]*/
148
+ },
149
+ ],
150
+ });
package/prepare.js ADDED
@@ -0,0 +1,105 @@
1
+ function getDuplicateId(path) {
2
+ const uniqueNodeModulePath = path.split("node_modules/").pop();
3
+ const ids = [uniqueNodeModulePath];
4
+
5
+ // Special case for lodash
6
+ if (
7
+ uniqueNodeModulePath.startsWith("lodash.") ||
8
+ uniqueNodeModulePath.startsWith("lodash-es/")
9
+ ) {
10
+ // node_modules/lodash.debounce/index.js => node_modules/lodash/debounce.js
11
+ // node_modules/lodash-es/get.js => node_modules/lodash/get.js
12
+ const uniqueLodashPath = uniqueNodeModulePath
13
+ .replace("lodash-es/", "lodash/")
14
+ .replace("lodash.", "lodash/")
15
+ .replace("/index.js", ".js");
16
+
17
+ ids.push(uniqueLodashPath);
18
+ }
19
+
20
+ return ids;
21
+ }
22
+
23
+ function prepare(data) {
24
+ // data.modules = data.modules.slice(875, 880); TODO for debugging a formtree chart
25
+ let moduleMap = new Map();
26
+ let duplicatesMap = new Map();
27
+ let allLodashModules = new Set();
28
+
29
+ data.packages.forEach((pkg) => {
30
+ pkg.path = pkg.absolutePath.replace(data.rootFolder + "/", "");
31
+ });
32
+ data.modules.forEach((m) => {
33
+ // 0. Data transformation
34
+ m.absolutePath = m.path;
35
+ if (m.absolutePath === data.entryPoint) {
36
+ m.isEntry = true;
37
+ }
38
+ m.path = m.path.replace(data.rootFolder + "/", "");
39
+ m.dependencies.forEach((d) => {
40
+ d.path = d.absolutePath.replace(data.rootFolder + "/", "");
41
+ });
42
+
43
+ // 1. Duplicates
44
+ m._tmp_ids = getDuplicateId(m.path);
45
+ m.duplicates = [];
46
+ m._tmp_ids.forEach((id) => {
47
+ if (!duplicatesMap.has(id)) {
48
+ duplicatesMap.set(id, []);
49
+ }
50
+ duplicatesMap.get(id).push(m);
51
+ });
52
+ // lodash/*
53
+ // lodash-es/*
54
+ // lodash.*
55
+ if (m.path.includes("node_modules/lodash")) {
56
+ allLodashModules.add(m);
57
+ }
58
+
59
+ // 2. Dependencies
60
+ m.dependents = [];
61
+ moduleMap.set(m.absolutePath, m);
62
+
63
+ // 3. Other
64
+ // 3.1 Format prelude
65
+ if (m.path === "__prelude__") {
66
+ m.source.code = m.source.code
67
+ .replaceAll(";", ";\n\n")
68
+ .replaceAll(",", ",\n ");
69
+ m.output.code = m.output.code
70
+ .replaceAll(";", ";\n\n")
71
+ .replaceAll(",", ",\n ");
72
+ }
73
+ });
74
+
75
+ data.modules.forEach((m) => {
76
+ // 1. Duplicates
77
+ m._tmp_ids.forEach((id) => {
78
+ const duplicates = duplicatesMap.get(id);
79
+ if (duplicates.length > 1) {
80
+ m.duplicates.push(...duplicates.filter((d) => d !== m));
81
+ }
82
+ });
83
+
84
+ // lodash/index.js is a special case (as index.js includes all lodash functions)
85
+ if (m.path.endsWith("node_modules/lodash/index.js")) {
86
+ m.duplicates.push(...allLodashModules);
87
+ }
88
+ delete m._tmp_ids;
89
+
90
+ // 2. Dependencies
91
+ m.dependencies.forEach((dependency) => {
92
+ const dependentModule = moduleMap.get(dependency.absolutePath);
93
+ if (dependentModule) {
94
+ dependentModule.dependents.push(m); //add to the dependent module directly
95
+ }
96
+ });
97
+ });
98
+
99
+ allLodashModules = null;
100
+ moduleMap = null;
101
+ duplicatesMap = null;
102
+ return data;
103
+ }
104
+
105
+ module.exports = prepare;