react-native-bundle-discovery-ui 2.7.0 → 2.9.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.
Files changed (38) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +12 -227
  3. package/assets/global.css +769 -53
  4. package/dist/bin.js +61 -16
  5. package/dist/client/issues.js +5 -0
  6. package/dist/client/pages/_common.js +258 -86
  7. package/dist/client/pages/_module.js +347 -0
  8. package/dist/client/pages/default.js +145 -62
  9. package/dist/client/pages/module.js +2 -309
  10. package/dist/client/pages/moduleDiff.js +76 -0
  11. package/dist/client/pages/package.js +172 -154
  12. package/dist/client/prepare.js +15 -0
  13. package/dist/client/queryHelpers.js +445 -81
  14. package/dist/client/views/{_theme.js → _colorScheme.js} +2 -9
  15. package/dist/client/views/_html.js +52 -0
  16. package/dist/client/views/codeDiff.js +66 -0
  17. package/dist/client/views/compare.js +237 -0
  18. package/dist/client/views/importGraph.js +255 -0
  19. package/dist/client/views/insights.js +80 -0
  20. package/dist/client/views/moduleOverview.js +134 -0
  21. package/dist/client/views/packageOverview.js +114 -0
  22. package/dist/client/views/persistedFilterInput.js +27 -0
  23. package/dist/client/views/reportBar.js +89 -0
  24. package/dist/client/views/treemap.js +598 -0
  25. package/dist/discoveryrc.js +26 -7
  26. package/dist/rsdoctor.js +5 -101
  27. package/dist/src-Bp8pPQrQ.js +1545 -0
  28. package/package.json +5 -3
  29. package/assets/highcharts.css +0 -1
  30. package/dist/client/_virtual/_rolldown/runtime.js +0 -37
  31. package/dist/client/views/foamtree.js +0 -103
  32. package/dist/client/views/highcharts.js +0 -33
  33. package/dist/src-BSLX-He2.js +0 -36
  34. package/vendors/foamtree.js +0 -9008
  35. package/vendors/high-contrast-dark.js +0 -1
  36. package/vendors/highcharts-exporting.js +0 -13
  37. package/vendors/highcharts-networkgraph.js +0 -1
  38. package/vendors/highcharts.js +0 -10
@@ -0,0 +1,347 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_pages__common = require("./_common.js");
3
+ //#region src/client/pages/_module.ts
4
+ function getMetroWarn({ when, href, linkText, content }) {
5
+ return {
6
+ view: "alert-warning",
7
+ when,
8
+ className: "m-v-8",
9
+ content: [
10
+ ...content,
11
+ linkText && href
12
+ ? {
13
+ view: "link",
14
+ data: {
15
+ external: true,
16
+ href,
17
+ text: linkText,
18
+ },
19
+ }
20
+ : null,
21
+ ].filter(Boolean),
22
+ };
23
+ }
24
+ function getCodePanes() {
25
+ return {
26
+ view: "hstack",
27
+ className: "flex-no-wrap",
28
+ content: [
29
+ {
30
+ view: "block",
31
+ when: "not isImageOrFontAsset",
32
+ className: "width-50p",
33
+ content: [
34
+ {
35
+ view: "h5",
36
+ className: "mo-pane-title",
37
+ content: [
38
+ "text: 'Source'",
39
+ "text: ' · ' + overview.sourceLines.pluralWithCount(['line', 'lines']) + ' · ' + overview.sourceSize.formatBytes()",
40
+ ],
41
+ },
42
+ {
43
+ view: "source",
44
+ syntax: "ts",
45
+ source: "=$.currentModule.source.code",
46
+ },
47
+ ],
48
+ },
49
+ {
50
+ view: "block",
51
+ className: (data) => (data.isImageOrFontAsset ? "" : "width-50p"),
52
+ content: [
53
+ {
54
+ view: "h5",
55
+ className: "mo-pane-title",
56
+ content: [
57
+ "text: 'Output'",
58
+ "text: ' · ' + overview.outputLines.pluralWithCount(['line', 'lines']) + ' · ' + overview.outputSize.formatBytes()",
59
+ ],
60
+ },
61
+ {
62
+ view: "context",
63
+ modifiers: [
64
+ {
65
+ view: "checkbox",
66
+ name: "prettify",
67
+ checked: true,
68
+ className: "prettify-checkbox",
69
+ content: 'text:"Prettify"',
70
+ },
71
+ ],
72
+ content: [
73
+ {
74
+ view: "source-prettify",
75
+ syntax: "ts",
76
+ source:
77
+ "=(#.prettify ? $.currentModule.output.code.prettifyJS() : $.currentModule.output.code)",
78
+ },
79
+ ],
80
+ },
81
+ ],
82
+ },
83
+ ],
84
+ };
85
+ }
86
+ /** Module page config, shared by the "module" and "module-diff" pages */
87
+ function getModulePage({
88
+ codeTabText = "Code",
89
+ extraData = "",
90
+ codeContent = getCodePanes(),
91
+ } = {}) {
92
+ return {
93
+ view: "context",
94
+ data: `{
95
+ $currentModule: $.modules.[path = #.id].pick(0);
96
+ ...$,
97
+ currentModule: $currentModule,
98
+ overview: $.moduleOverview(#.id),
99
+ isEntry: $currentModule.isEntry,
100
+ isInjectedByMetro: $currentModule.path = '__prelude__' or $currentModule.path has '@react-native/js-polyfills',
101
+ isWebpackRuntime: $currentModule.path has '__runtime__',
102
+ // Keep in sync with IMAGE_FONT_ASSET_RE in packages/serializer
103
+ isImageOrFontAsset: $currentModule.path ~= /\\.(png|jpe?g|gif|webp|bmp|psd|tiff?|ico|heic|avif|ttf|otf|woff2?|eot)$/i,
104
+ ${extraData}
105
+ }`,
106
+ content: [
107
+ {
108
+ view: "block",
109
+ className: "mo-wrap",
110
+ content: [
111
+ {
112
+ view: "module-overview",
113
+ data: "overview",
114
+ },
115
+ require_pages__common.getCopyToClipboardButton({
116
+ textToCopy: `$.currentModule.path`,
117
+ className: "mo-copy",
118
+ }),
119
+ ],
120
+ },
121
+ {
122
+ view: "tabs",
123
+ name: "tabs",
124
+ className: "mo-tabs",
125
+ tabs: [
126
+ {
127
+ value: "mcontent",
128
+ text: codeTabText,
129
+ },
130
+ {
131
+ value: "mimports",
132
+ when: "overview.imports",
133
+ content: ["text:'Imports '", "pill-badge: overview.imports.size()"],
134
+ },
135
+ {
136
+ value: "networkGraph",
137
+ when: "not isInjectedByMetro and not isEntry and not isWebpackRuntime",
138
+ content: ["text:'Imported by '", "pill-badge: overview.importedBy"],
139
+ },
140
+ {
141
+ value: "mduplicates",
142
+ when: "not isInjectedByMetro and not isEntry and not isWebpackRuntime and currentModule.duplicates",
143
+ content: ["text:'Duplicates '", "pill-badge: overview.duplicates"],
144
+ },
145
+ ],
146
+ content: {
147
+ view: "switch",
148
+ content: [
149
+ {
150
+ when: '#.tabs="mcontent"',
151
+ content: [
152
+ getMetroWarn({
153
+ when: "$.currentModule.path = '__prelude__'",
154
+ content: [
155
+ "html: '<b>__prelude__</b>'",
156
+ "text: ' is a special runtime code that was generated by the Metro bundler, see: '",
157
+ ],
158
+ href: "https://github.com/facebook/metro/blob/f595e9e1c9021ad372b489be8958cc298eb1e0d9/packages/metro/src/lib/getPreludeCode.js#L25-L33",
159
+ linkText: "getPreludeCode.js",
160
+ }),
161
+ getMetroWarn({
162
+ when: "$.currentModule.path = '__runtime__'",
163
+ content: [
164
+ "html: '<b>__runtime__</b>'",
165
+ "text: ' is a special runtime code that was generated by the Webpack/Rspack bundler'",
166
+ ],
167
+ }),
168
+ getMetroWarn({
169
+ when: "$.currentModule.path has '@react-native/js-polyfills'",
170
+ content: [
171
+ "html: '<b>@react-native/js-polyfills/*</b>'",
172
+ "text: ' is a special package that was injected by the Metro bundler, see: '",
173
+ ],
174
+ href: "https://github.com/facebook/react-native/blob/198c6fc81cda67a46fff457cc39eb2a297eddc01/packages/metro-config/src/index.flow.js#L61",
175
+ linkText: "rn/metro-config/index.js",
176
+ }),
177
+ getMetroWarn({
178
+ when: "$.currentModule.path has 'metro-runtime'",
179
+ content: [
180
+ "html: '<b>metro-runtime/*</b>'",
181
+ "text: ' is a special package that was injected by the Metro bundler, see: '",
182
+ ],
183
+ href: "https://github.com/facebook/metro/blob/f595e9e1c9021ad372b489be8958cc298eb1e0d9/packages/metro-config/src/defaults/defaults.js#L61-L63",
184
+ linkText: "metro-config/index.js",
185
+ }),
186
+ codeContent,
187
+ ],
188
+ },
189
+ {
190
+ when: '#.tabs="mimports"',
191
+ content: {
192
+ view: "content-filter",
193
+ data: "overview.imports",
194
+ name: "filterByPathStr",
195
+ content: {
196
+ view: "list",
197
+ data: ".[name ~= #.filterByPathStr or specifier ~= #.filterByPathStr]",
198
+ emptyText: "This module doesn't import anything",
199
+ item: {
200
+ view: "block",
201
+ className: "mo-import",
202
+ content: [
203
+ {
204
+ view: "text-match",
205
+ className: "mo-import-specifier",
206
+ data: "{ text: specifier, match: #.filterByPathStr }",
207
+ },
208
+ "html: '<span class=\"mo-arrow\">→</span>'",
209
+ {
210
+ view: "switch",
211
+ content: [
212
+ {
213
+ when: "missing",
214
+ content: [
215
+ "text: name",
216
+ "badge: { text: 'not in bundle', color: 'rgba(128,128,128,.25)' }",
217
+ ],
218
+ },
219
+ {
220
+ content: require_pages__common.getTreeModule({
221
+ hasTextMatch: true,
222
+ }),
223
+ },
224
+ ],
225
+ },
226
+ ],
227
+ },
228
+ },
229
+ },
230
+ },
231
+ {
232
+ when: '#.tabs="networkGraph"',
233
+ content: {
234
+ view: "context",
235
+ context: `{ ...#, maxParentDepth: $.currentModule.getBestNetworkGraphSize() }`,
236
+ modifiers: [
237
+ "html: '<br/>'",
238
+ "text:'Max parent depth: '",
239
+ {
240
+ view: "input",
241
+ name: "maxParentDepth",
242
+ className: "inline-block",
243
+ htmlType: "number",
244
+ htmlMin: 1,
245
+ htmlMax: 10,
246
+ },
247
+ ],
248
+ content: [
249
+ {
250
+ view: "context",
251
+ data: `{
252
+ // Vars
253
+ $tmp: $.currentModule.getNetworkGraph({ maxParentDepth: #.maxParentDepth });
254
+ $limit: 150;
255
+ // Return data
256
+ currentModule: $.currentModule,
257
+ entryPointPath: $tmp.entryPointPath,
258
+ limit: $limit,
259
+ data: $tmp.data,
260
+ graph: { nodes: $tmp.nodes, links: $tmp.links },
261
+ isGraphTooBig: $tmp.data.size() > $limit,
262
+ }`,
263
+ content: [
264
+ {
265
+ when: "isGraphTooBig",
266
+ view: "block",
267
+ className: "m-v-8",
268
+ content: [
269
+ {
270
+ view: "alert-warning",
271
+ data: '"Amount of dependencies "+$.data.size()+" (limit: "+$.limit+") are too big to display as a graph :("',
272
+ },
273
+ ],
274
+ },
275
+ {
276
+ view: "hstack",
277
+ className: "flex-no-wrap two-column",
278
+ content: [
279
+ {
280
+ when: "not isGraphTooBig",
281
+ view: "import-graph",
282
+ className: "ig",
283
+ data: "graph",
284
+ },
285
+ {
286
+ view: "block",
287
+ content: [
288
+ "h4: 'Modules that import this file: ' + $.currentModule.path",
289
+ {
290
+ view: "list",
291
+ data: `
292
+ $totalSize: modules.sum(=>output.sizeInBytes);
293
+ $toModule: => {
294
+ $deepIdx: $$ + 1;
295
+ ext: $.path.getFileExtension(),
296
+ name: $.path,
297
+ size: $.output.sizeInBytes.formatBytes(),
298
+ percent: ($.output.sizeInBytes / $totalSize).percent(3),
299
+ children: $deepIdx < 2 ? $.dependents.map(=> $.$toModule()) : null,
300
+ };
301
+ $.currentModule.dependents.map(=> $.$toModule(0))
302
+ `,
303
+ emptyText: "⚠️ No importing modules found",
304
+ item: {
305
+ view: "tree",
306
+ expanded: false,
307
+ itemConfig: {
308
+ content:
309
+ require_pages__common.getTreeModule({
310
+ hasTextMatch: true,
311
+ }),
312
+ children: `$.children`,
313
+ },
314
+ },
315
+ },
316
+ ],
317
+ },
318
+ ],
319
+ },
320
+ ],
321
+ },
322
+ ],
323
+ },
324
+ },
325
+ {
326
+ when: '#.tabs="mduplicates"',
327
+ content: require_pages__common.getModulesTree({
328
+ data: `
329
+ $totalSize: modules.sum(=>output.sizeInBytes);
330
+ $toModule: => {
331
+ ext: $.path.getFileExtension(),
332
+ name: $.path,
333
+ size: $.output.sizeInBytes.formatBytes(),
334
+ percent: ($.output.sizeInBytes / $totalSize).percent(3),
335
+ };
336
+ currentModule.duplicates.map(=> $.$toModule())
337
+ `,
338
+ }),
339
+ },
340
+ ],
341
+ },
342
+ },
343
+ ],
344
+ };
345
+ }
346
+ //#endregion
347
+ exports.getModulePage = getModulePage;
@@ -1,34 +1,44 @@
1
1
  const require_pages__common = require("./_common.js");
2
2
  //#region src/client/pages/default.ts
3
- const topMetaData = [
4
- require_pages__common.metadata.platform,
5
- require_pages__common.metadata.size,
6
- require_pages__common.metadata.source_code_size,
7
- require_pages__common.metadata.node_modules_size,
8
- require_pages__common.metadata.is_dev,
9
- require_pages__common.metadata.is_minified,
10
- ];
11
3
  const TABS = {
12
- TREEMAP_FOAMTREE: "treemap-foamtree",
4
+ INSIGHTS: "insights",
5
+ TREEMAP: "treemap",
13
6
  MODULES: "modules",
14
7
  PACKAGES: "packages",
15
8
  DUPLICATES: "duplicates",
9
+ COMPARE: "compare",
16
10
  };
17
11
  function parseHashRef(url = window.location.href) {
18
12
  return new URL(url).hash.split(":")?.[1];
19
13
  }
20
14
  discovery.page.define("default", [
21
- ...topMetaData,
15
+ {
16
+ view: "report-bar",
17
+ className: "report-bar",
18
+ data: "$.reportSummary()",
19
+ },
22
20
  {
23
21
  view: "tabs",
24
22
  name: "mainTabs",
25
23
  className: "main-tabs",
26
- value: parseHashRef() ?? TABS.TREEMAP_FOAMTREE,
24
+ value: parseHashRef() ?? TABS.INSIGHTS,
27
25
  tabs: [
28
26
  {
29
- value: TABS.TREEMAP_FOAMTREE,
27
+ value: TABS.INSIGHTS,
28
+ className: `main-tabs-${TABS.INSIGHTS}`,
29
+ content: [
30
+ "text:'Insights '",
31
+ {
32
+ view: "pill-badge",
33
+ when: "recommendations",
34
+ data: "recommendations.size()",
35
+ },
36
+ ],
37
+ },
38
+ {
39
+ value: TABS.TREEMAP,
30
40
  text: "Treemap chart",
31
- className: `main-tabs-${TABS.TREEMAP_FOAMTREE}`,
41
+ className: `main-tabs-${TABS.TREEMAP}`,
32
42
  },
33
43
  {
34
44
  value: TABS.PACKAGES,
@@ -45,12 +55,28 @@ discovery.page.define("default", [
45
55
  },
46
56
  {
47
57
  value: TABS.DUPLICATES,
58
+ when: "modules.filter(=> duplicates)",
48
59
  className: `main-tabs-${TABS.DUPLICATES}`,
49
60
  content: [
50
- "text:'Duplicate modules '",
61
+ "text:'Duplicates '",
51
62
  "pill-badge: modules.filter(=> duplicates).size()",
52
63
  ],
53
64
  },
65
+ {
66
+ value: TABS.COMPARE,
67
+ when: "comparison",
68
+ className: `main-tabs-${TABS.COMPARE}`,
69
+ content: [
70
+ "text:'Compare '",
71
+ {
72
+ view: "pill-badge",
73
+ data: "comparison.summary.deltaInBytes",
74
+ className: (delta) =>
75
+ `cmp-tab-delta-${delta > 0 ? "bad" : delta < 0 ? "good" : "ok"}`,
76
+ text: `=($ > 0 ? '+' : $ < 0 ? '−' : '') + ($ < 0 ? -$ : $).formatBytes()`,
77
+ },
78
+ ],
79
+ },
54
80
  ],
55
81
  content: {
56
82
  view: "switch",
@@ -63,7 +89,7 @@ discovery.page.define("default", [
63
89
  ref: isHashChangeEvent ? discovery.pageRef : context.mainTabs,
64
90
  });
65
91
  discovery.cancelScheduledRender();
66
- const id = discovery.pageRef ?? TABS.TREEMAP_FOAMTREE;
92
+ const id = discovery.pageRef ?? TABS.INSIGHTS;
67
93
  setTimeout(() => {
68
94
  discovery.dom.root
69
95
  .querySelectorAll(".main-tabs>div>.onclick")
@@ -83,28 +109,24 @@ discovery.page.define("default", [
83
109
  },
84
110
  content: [
85
111
  {
86
- when: `#.id="${TABS.TREEMAP_FOAMTREE}"`,
112
+ when: `#.id="${TABS.INSIGHTS}"`,
113
+ content: require_pages__common.getInsightsTab(),
114
+ },
115
+ {
116
+ when: `#.id="${TABS.TREEMAP}"`,
87
117
  content: {
88
118
  view: "content-filter",
89
119
  name: "filterByPathStr",
90
120
  debounce: 300,
91
- className: "foamtree-filter",
121
+ className: "treemap-filter",
92
122
  content: {
93
- view: "foamtree",
123
+ view: "treemap",
94
124
  data: `
95
125
  $root: $.rootFolder;
96
126
  $applyFilter: => #.filterByPathStr ? $.modules.filter(=> $.path ~= #.filterByPathStr) : $.modules;
97
- $dataObject: $.$applyFilter()
98
- .map(=> {path, size: $.output.sizeInBytes})
99
- .transformFilesList($root, "foamtree");
100
-
101
- {
102
- options: {
103
- dataObject: $dataObject,
104
- descriptionGroupSize: 0.05,
105
- descriptionGroupMinHeight: 30,
106
- }
107
- }
127
+ $.$applyFilter()
128
+ .map(=> {path, size: $.output.sizeInBytes, issues})
129
+ .transformFilesList($root, "treemap")
108
130
  `,
109
131
  },
110
132
  },
@@ -113,6 +135,7 @@ discovery.page.define("default", [
113
135
  when: `#.id="${TABS.MODULES}"`,
114
136
  content: require_pages__common.getModulesTree({
115
137
  limit: 200,
138
+ sortable: true,
116
139
  data: `
117
140
  $entryPoint: $.entryPointPath;
118
141
  $totalSize: modules.sum(=>output.sizeInBytes);
@@ -124,6 +147,7 @@ discovery.page.define("default", [
124
147
  };
125
148
  modules.map(=> {
126
149
  ...$.$toModule(),
150
+ sizeInBytes: $.output.sizeInBytes,
127
151
  isEntry: $.isEntry,
128
152
  reasons: $.dependents.map(=> $.$toModule()),
129
153
  duplicates: $.duplicates.map(=> $.$toModule()),
@@ -134,48 +158,94 @@ discovery.page.define("default", [
134
158
  {
135
159
  when: `#.id="${TABS.PACKAGES}"`,
136
160
  content: [
137
- require_pages__common.getCopyToClipboardButton({
138
- text: "Copy list",
139
- className: "copy-before-ask-chatgpt",
140
- textToCopy: `"- " + modules.filter(=> path has "node_modules").group(=> path.getModulesName()).map(=> $.key).join("\\n- ")`,
141
- }),
142
- {
143
- view: "link",
144
- className: "ask-chatgpt view-button",
145
- text: "and Ask ChatGPT",
146
- external: true,
147
- data: `{
148
- href: $.askChatGPTAboutPackages()
149
- }`,
150
- },
151
- {
152
- view: "content-filter",
153
- data: `${require_pages__common.getPackage(`modules.filter(=> path has "node_modules")`)}.sort(pkgInstances desc, size desc)`,
161
+ require_pages__common.getSortableContentFilter({
162
+ data: require_pages__common.getPackage(
163
+ `modules.filter(=> path has "node_modules")`,
164
+ ),
154
165
  className: "packages-content",
155
- name: "filterByPathStr",
156
- content: require_pages__common.getPackageList({
157
- showCopiesBadge: true,
158
- expanded: false,
159
- limit: 200,
160
- subLimit: 50,
161
- data: ".[pkgName ~= #.filterByPathStr]",
162
- itemPkgName: {
163
- view: "link",
164
- content: "text-match",
165
- data: `{
166
+ nameField: "pkgName",
167
+ sizeField: "size",
168
+ duplicatesCount: "(pkgInstances.size() - 1)",
169
+ defaultSort: "pkgInstances desc, size desc",
170
+ content: (listData) =>
171
+ require_pages__common.getPackageList({
172
+ showCopiesBadge: true,
173
+ expanded: false,
174
+ limit: 200,
175
+ subLimit: 50,
176
+ data: listData,
177
+ itemPkgName: {
178
+ view: "link",
179
+ content: "text-match",
180
+ data: `{
166
181
  href: pkgName.pageLink("package", {}),
167
182
  text: pkgName,
168
183
  match: #.filterByPathStr
169
184
  }`,
170
- },
171
- }),
172
- },
185
+ },
186
+ }),
187
+ }),
173
188
  ],
174
189
  },
175
190
  {
176
191
  when: `#.id="${TABS.DUPLICATES}"`,
177
- content: require_pages__common.getModulesTree({
192
+ content: {
193
+ view: "block",
194
+ className: "tab-scroll",
178
195
  data: `
196
+ $allPackages: (${require_pages__common.getPackage(`modules.filter(=> path has "node_modules")`)});
197
+ {
198
+ ...$,
199
+ duplicatePackages: $allPackages
200
+ .[pkgInstances.size() > 1]
201
+ // Keeping only the heaviest copy saves the rest
202
+ .({ ...$, savings: size - pkgInstances.sort(size desc)[0].size })
203
+ .sort(savings desc),
204
+ }
205
+ `,
206
+ content: [
207
+ {
208
+ view: "h3",
209
+ className: "dup-title",
210
+ content: [
211
+ "text: 'Duplicate packages '",
212
+ "pill-badge: duplicatePackages.size()",
213
+ {
214
+ view: "pill-badge",
215
+ when: "duplicatePackages",
216
+ data: "{ text: '~' + duplicatePackages.sum(=> savings).formatBytes(), postfix: 'can be saved', color: 'rgba(120, 177, 9, 0.35)' }",
217
+ },
218
+ ],
219
+ },
220
+ {
221
+ view: "context",
222
+ when: "duplicatePackages",
223
+ content: require_pages__common.getPackageList({
224
+ showCopiesBadge: true,
225
+ expanded: false,
226
+ data: "duplicatePackages",
227
+ itemPkgName: {
228
+ view: "link",
229
+ data: `{ href: pkgName.pageLink("package", {}), text: pkgName }`,
230
+ },
231
+ }),
232
+ },
233
+ {
234
+ view: "text",
235
+ when: "not duplicatePackages",
236
+ className: "dup-empty",
237
+ data: "'✅ Every package is bundled only once'",
238
+ },
239
+ {
240
+ view: "h3",
241
+ className: "dup-title",
242
+ content: [
243
+ "text: 'Duplicate modules '",
244
+ "pill-badge: modules.filter(=> duplicates).size()",
245
+ ],
246
+ },
247
+ require_pages__common.getModulesTree({
248
+ data: `
179
249
  // values
180
250
  $duplicatesOnly: modules.filter(=> duplicates);
181
251
  $totalSize: $duplicatesOnly.sum(=>output.sizeInBytes);
@@ -192,7 +262,20 @@ discovery.page.define("default", [
192
262
  duplicates: $.duplicates.map(=> $.$toModule()),
193
263
  })
194
264
  `,
195
- }),
265
+ }),
266
+ ],
267
+ },
268
+ },
269
+ {
270
+ when: `#.id="${TABS.COMPARE}"`,
271
+ content: {
272
+ view: "block",
273
+ className: "tab-scroll compare",
274
+ content: {
275
+ view: "bundle-compare",
276
+ data: "{ comparison, modulePaths: modules.path }",
277
+ },
278
+ },
196
279
  },
197
280
  ],
198
281
  },