@bundle-stats/ui 4.14.0-beta.0 → 4.14.0-beta.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/lib/components/asset-info/asset-info.js +1 -1
- package/lib/components/asset-info/asset-info.js.map +1 -1
- package/lib/components/bundle-modules/bundle-modules.js +16 -11
- package/lib/components/bundle-modules/bundle-modules.js.map +1 -1
- package/lib/components/bundle-modules/bundle-modules.utils.js +18 -4
- package/lib/components/bundle-modules/bundle-modules.utils.js.map +1 -1
- package/lib/components/bundle-modules/index.js +5 -5
- package/lib/components/bundle-modules/index.js.map +1 -1
- package/lib/components/entry-info/entry-info.js +16 -9
- package/lib/components/entry-info/entry-info.js.map +1 -1
- package/lib/components/entry-info/entry-info.module.css +34 -8
- package/lib/components/module-info/module-info.js +80 -16
- package/lib/components/module-info/module-info.js.map +1 -1
- package/lib/components/module-info/module-info.module.css +38 -0
- package/lib/components/package-info/package-info.js +1 -1
- package/lib/components/package-info/package-info.js.map +1 -1
- package/lib/components/run-info/run-info.module.css +1 -1
- package/lib/constants.js +21 -1
- package/lib/constants.js.map +1 -1
- package/lib/hooks/rows-sort.js.map +1 -1
- package/lib-esm/components/asset-info/asset-info.js +1 -1
- package/lib-esm/components/asset-info/asset-info.js.map +1 -1
- package/lib-esm/components/bundle-modules/bundle-modules.js +17 -12
- package/lib-esm/components/bundle-modules/bundle-modules.js.map +1 -1
- package/lib-esm/components/bundle-modules/bundle-modules.utils.js +19 -4
- package/lib-esm/components/bundle-modules/bundle-modules.utils.js.map +1 -1
- package/lib-esm/components/bundle-modules/index.js +5 -5
- package/lib-esm/components/bundle-modules/index.js.map +1 -1
- package/lib-esm/components/entry-info/entry-info.js +16 -9
- package/lib-esm/components/entry-info/entry-info.js.map +1 -1
- package/lib-esm/components/entry-info/entry-info.module.css +34 -8
- package/lib-esm/components/module-info/module-info.js +84 -19
- package/lib-esm/components/module-info/module-info.js.map +1 -1
- package/lib-esm/components/module-info/module-info.module.css +38 -0
- package/lib-esm/components/package-info/package-info.js +1 -1
- package/lib-esm/components/package-info/package-info.js.map +1 -1
- package/lib-esm/components/run-info/run-info.module.css +1 -1
- package/lib-esm/constants.js +20 -0
- package/lib-esm/constants.js.map +1 -1
- package/lib-esm/hooks/rows-sort.js.map +1 -1
- package/package.json +9 -9
- package/src/components/asset-info/asset-info.tsx +5 -2
- package/src/components/bundle-modules/bundle-modules.tsx +34 -22
- package/src/components/bundle-modules/bundle-modules.utils.ts +37 -14
- package/src/components/bundle-modules/index.tsx +15 -14
- package/src/components/entry-info/entry-info.module.css +34 -8
- package/src/components/entry-info/entry-info.tsx +47 -32
- package/src/components/module-info/module-info.module.css +38 -0
- package/src/components/module-info/module-info.stories.tsx +57 -3
- package/src/components/module-info/module-info.tsx +221 -40
- package/src/components/package-info/package-info.tsx +2 -3
- package/src/components/run-info/run-info.module.css +1 -1
- package/src/constants.ts +22 -1
- package/src/hooks/rows-sort.ts +17 -10
- package/src/types.d.ts +13 -0
- package/types/components/bundle-modules/bundle-modules.d.ts +4 -5
- package/types/components/bundle-modules/bundle-modules.utils.d.ts +3 -5
- package/types/components/entry-info/entry-info.d.ts +8 -10
- package/types/components/module-info/module-info.d.ts +4 -10
- package/types/constants.d.ts +21 -2
- package/types/hooks/rows-sort.d.ts +9 -14
- package/lib/components/bundle-modules/bundle-modules.constants.js +0 -10
- package/lib/components/bundle-modules/bundle-modules.constants.js.map +0 -1
- package/lib-esm/components/bundle-modules/bundle-modules.constants.js +0 -7
- package/lib-esm/components/bundle-modules/bundle-modules.constants.js.map +0 -1
- package/src/components/bundle-modules/bundle-modules.constants.ts +0 -5
- package/src/components/bundle-modules/bundle-modules.types.d.ts +0 -9
- package/types/components/bundle-modules/bundle-modules.constants.d.ts +0 -5
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import type { ElementType, ReactNode } from 'react';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import cx from 'classnames';
|
|
3
4
|
import { Portal } from 'ariakit/portal';
|
|
4
|
-
import {
|
|
5
|
+
import type { MetricRunInfo, ReportMetricRow } from '@bundle-stats/utils';
|
|
6
|
+
import { METRIC_TYPE_CONFIGS, getMetricRunInfo } from '@bundle-stats/utils';
|
|
5
7
|
|
|
6
8
|
import { Box } from '../../layout/box';
|
|
7
9
|
import { Stack } from '../../layout/stack';
|
|
@@ -12,9 +14,10 @@ import { Table } from '../../ui/table';
|
|
|
12
14
|
import { RunInfo } from '../run-info';
|
|
13
15
|
import * as I18N from './entry-info.i18n';
|
|
14
16
|
import css from './entry-info.module.css';
|
|
17
|
+
import { Tooltip } from '../../ui';
|
|
15
18
|
|
|
16
19
|
interface EntryInfoMetaLinkProps {
|
|
17
|
-
as?:
|
|
20
|
+
as?: ElementType;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export const EntryInfoMetaLink = (props: EntryInfoMetaLinkProps & React.ComponentProps<'a'>) => {
|
|
@@ -24,34 +27,60 @@ export const EntryInfoMetaLink = (props: EntryInfoMetaLinkProps & React.Componen
|
|
|
24
27
|
|
|
25
28
|
interface EntryInfoMetaProps {
|
|
26
29
|
label: string;
|
|
30
|
+
tooltip?: ReactNode;
|
|
27
31
|
}
|
|
28
32
|
|
|
29
33
|
const EntryInfoMeta = ({
|
|
30
34
|
className = '',
|
|
31
35
|
label,
|
|
36
|
+
tooltip,
|
|
32
37
|
children,
|
|
33
38
|
}: EntryInfoMetaProps & React.ComponentProps<'p'>) => (
|
|
34
|
-
<
|
|
35
|
-
<span className={css.metaLabel}>
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
<div className={cx(css.meta, className)}>
|
|
40
|
+
<span className={css.metaLabel}>
|
|
41
|
+
{label}
|
|
42
|
+
{tooltip && (
|
|
43
|
+
<Tooltip title={tooltip} className={css.metaLabelTooltip}>
|
|
44
|
+
<Icon glyph="help" />
|
|
45
|
+
</Tooltip>
|
|
46
|
+
)}
|
|
47
|
+
</span>
|
|
48
|
+
<div className={css.metaContent}>{children}</div>
|
|
49
|
+
</div>
|
|
38
50
|
);
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
|
|
52
|
+
function defaultRenderRunInfo(item: ReportMetricRow) {
|
|
53
|
+
const baselineRun = item.runs.length > 1 ? item.runs?.[item.runs.length - 1] : null;
|
|
54
|
+
|
|
55
|
+
// Get the metric run info to handle added/removed cases when there are more than 2 jobs
|
|
56
|
+
const metricRunInfo = getMetricRunInfo(
|
|
57
|
+
METRIC_TYPE_CONFIGS.METRIC_TYPE_FILE_SIZE,
|
|
58
|
+
item.runs?.[0]?.value || 0,
|
|
59
|
+
baselineRun?.value || 0,
|
|
60
|
+
) as MetricRunInfo;
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<RunInfo
|
|
64
|
+
current={metricRunInfo.displayValue}
|
|
65
|
+
delta={metricRunInfo.displayDeltaPercentage}
|
|
66
|
+
deltaPercentage={metricRunInfo.displayDelta}
|
|
67
|
+
deltaType={metricRunInfo.deltaType}
|
|
68
|
+
baseline={baselineRun?.displayValue || '0B'}
|
|
69
|
+
size="large"
|
|
70
|
+
/>
|
|
71
|
+
);
|
|
42
72
|
}
|
|
43
73
|
|
|
44
74
|
interface EntryInfoProps {
|
|
45
75
|
itemTitle?: React.ReactNode;
|
|
46
|
-
item:
|
|
47
|
-
label: string;
|
|
48
|
-
runs: Array<EntryRun & MetricRunInfo>;
|
|
49
|
-
};
|
|
76
|
+
item: ReportMetricRow;
|
|
50
77
|
labels: Array<string>;
|
|
51
78
|
runNameSelector?: string;
|
|
52
79
|
runNameLabel?: string;
|
|
80
|
+
runSizeLabel?: string;
|
|
53
81
|
tags?: React.ReactNode;
|
|
54
82
|
onClose: () => void;
|
|
83
|
+
renderRunInfo?: (item: ReportMetricRow) => React.ReactNode;
|
|
55
84
|
}
|
|
56
85
|
|
|
57
86
|
export const EntryInfo = (props: EntryInfoProps & React.ComponentProps<'div'>) => {
|
|
@@ -62,38 +91,22 @@ export const EntryInfo = (props: EntryInfoProps & React.ComponentProps<'div'>) =
|
|
|
62
91
|
labels,
|
|
63
92
|
runNameSelector = 'name',
|
|
64
93
|
runNameLabel = I18N.PATH,
|
|
94
|
+
runSizeLabel = I18N.SIZE,
|
|
65
95
|
children,
|
|
66
96
|
tags = null,
|
|
67
97
|
onClose,
|
|
98
|
+
renderRunInfo = defaultRenderRunInfo,
|
|
68
99
|
} = props;
|
|
69
100
|
|
|
70
|
-
const baselineRun = item.runs.length > 1 ? item.runs?.[item.runs.length - 1] : null;
|
|
71
|
-
|
|
72
|
-
// Get the metric run info to handle added/removed cases
|
|
73
|
-
const metricRunInfo = getMetricRunInfo(
|
|
74
|
-
METRIC_TYPE_CONFIGS.METRIC_TYPE_FILE_SIZE,
|
|
75
|
-
item.runs?.[0]?.value,
|
|
76
|
-
baselineRun?.value || 0,
|
|
77
|
-
) as MetricRunInfo;
|
|
78
|
-
|
|
79
101
|
return (
|
|
80
102
|
<Portal className={cx(css.root, className)}>
|
|
81
103
|
<Box padding="medium" as="header" className={css.header}>
|
|
82
104
|
<Stack space="small">
|
|
83
|
-
{tags && <div>{tags}</div>}
|
|
84
|
-
|
|
85
105
|
<h3 className={css.label}>
|
|
86
106
|
<FileName as="code" name={itemTitle || item.label} className={css.fileName} />
|
|
87
107
|
</h3>
|
|
88
108
|
|
|
89
|
-
<
|
|
90
|
-
current={metricRunInfo.displayValue}
|
|
91
|
-
delta={metricRunInfo.displayDeltaPercentage}
|
|
92
|
-
deltaPercentage={metricRunInfo.displayDelta}
|
|
93
|
-
deltaType={metricRunInfo.deltaType}
|
|
94
|
-
baseline={baselineRun?.displayValue || '0B'}
|
|
95
|
-
size="large"
|
|
96
|
-
/>
|
|
109
|
+
<div>{renderRunInfo(item)}</div>
|
|
97
110
|
</Stack>
|
|
98
111
|
<Button
|
|
99
112
|
radius="circle"
|
|
@@ -107,6 +120,8 @@ export const EntryInfo = (props: EntryInfoProps & React.ComponentProps<'div'>) =
|
|
|
107
120
|
</Box>
|
|
108
121
|
<Box padding="medium" as="main" className={css.contentWrapper}>
|
|
109
122
|
<Stack space="small" className={css.content}>
|
|
123
|
+
{tags && <div>{tags}</div>}
|
|
124
|
+
|
|
110
125
|
{children}
|
|
111
126
|
|
|
112
127
|
<Table outline compact className={css.runs}>
|
|
@@ -114,7 +129,7 @@ export const EntryInfo = (props: EntryInfoProps & React.ComponentProps<'div'>) =
|
|
|
114
129
|
<Table.Tr>
|
|
115
130
|
<Table.Th className={cx(css.runsCell, css.runsColJob)}> </Table.Th>
|
|
116
131
|
<Table.Th className={cx(css.runsCell, css.runsColName)}>{runNameLabel}</Table.Th>
|
|
117
|
-
<Table.Th className={cx(css.runsCell, css.runsColSize)}>{
|
|
132
|
+
<Table.Th className={cx(css.runsCell, css.runsColSize)}>{runSizeLabel}</Table.Th>
|
|
118
133
|
</Table.Tr>
|
|
119
134
|
</Table.THead>
|
|
120
135
|
<Table.TBody>
|
|
@@ -1,3 +1,41 @@
|
|
|
1
|
+
.moduleSizes {
|
|
2
|
+
flex-wrap: wrap;
|
|
3
|
+
}
|
|
4
|
+
|
|
1
5
|
.chunksItems {
|
|
2
6
|
display: inline;
|
|
3
7
|
}
|
|
8
|
+
|
|
9
|
+
.chunksItem-info {
|
|
10
|
+
color: var(--color-info);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.chunksItem-info:hover,
|
|
14
|
+
.chunksItem-info:active,
|
|
15
|
+
.chunksItem-info:focus {
|
|
16
|
+
color: var(--color-info-dark);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.chunksItem-danger {
|
|
20
|
+
color: var(--color-danger);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.chunksItem-danger:hover,
|
|
24
|
+
.chunksItem-danger:active,
|
|
25
|
+
.chunksItem-danger:focus {
|
|
26
|
+
color: var(--color-danger-dark);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.chunksItem-default {
|
|
30
|
+
color: var(--color-text-light);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.chunksItem-default:hover,
|
|
34
|
+
.chunksItem-default:active,
|
|
35
|
+
.chunksItem-default:focus {
|
|
36
|
+
color: var(--color-text);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.duplicateInstances {
|
|
40
|
+
padding: 2px 0; /* compensate for the Entry Label padding */
|
|
41
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
3
|
+
import { action } from '@storybook/addon-actions';
|
|
2
4
|
|
|
3
5
|
import { getWrapperDecorator } from '../../stories';
|
|
4
6
|
import { ModuleInfo } from '.';
|
|
@@ -17,12 +19,12 @@ export const Default = () => (
|
|
|
17
19
|
runs: [
|
|
18
20
|
{
|
|
19
21
|
name: './node_modules/lodash/fp/merge.js',
|
|
20
|
-
chunkIds: ['1'
|
|
22
|
+
chunkIds: ['1'],
|
|
21
23
|
value: 1024 * 4,
|
|
22
24
|
},
|
|
23
25
|
{
|
|
24
26
|
name: './node_modules/lodash/fp/merge.js',
|
|
25
|
-
chunkIds: ['1'
|
|
27
|
+
chunkIds: ['1'],
|
|
26
28
|
value: 1024 * 3,
|
|
27
29
|
displayValue: '3KiB',
|
|
28
30
|
},
|
|
@@ -37,8 +39,60 @@ export const Default = () => (
|
|
|
37
39
|
id: '2',
|
|
38
40
|
name: 'chunk-2',
|
|
39
41
|
},
|
|
42
|
+
{
|
|
43
|
+
id: '3',
|
|
44
|
+
name: 'chunk-3',
|
|
45
|
+
},
|
|
46
|
+
]}
|
|
47
|
+
chunkIds={['1', '2', '3']}
|
|
48
|
+
labels={['Job #2', 'Job #1']}
|
|
49
|
+
onClose={action('CLOSE')}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
export const Duplicates = () => (
|
|
54
|
+
<ModuleInfo
|
|
55
|
+
item={{
|
|
56
|
+
label: './node_modules/lodash/fp/merge.js',
|
|
57
|
+
duplicated: true,
|
|
58
|
+
runs: [
|
|
59
|
+
{
|
|
60
|
+
name: './node_modules/lodash/fp/merge.js',
|
|
61
|
+
chunkIds: ['1', '2', '3', '5'],
|
|
62
|
+
value: 1024 * 4,
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: './node_modules/lodash/fp/merge.js',
|
|
66
|
+
chunkIds: ['1', '2', '4'],
|
|
67
|
+
value: 1024 * 3,
|
|
68
|
+
displayValue: '3KiB',
|
|
69
|
+
},
|
|
70
|
+
] as any,
|
|
71
|
+
}}
|
|
72
|
+
chunks={[
|
|
73
|
+
{
|
|
74
|
+
id: '1',
|
|
75
|
+
name: 'chunk-1',
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: '2',
|
|
79
|
+
name: 'chunk-2',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
id: '3',
|
|
83
|
+
name: 'chunk-3',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: '4',
|
|
87
|
+
name: 'chunk-4',
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: '5',
|
|
91
|
+
name: 'chunk-5',
|
|
92
|
+
},
|
|
40
93
|
]}
|
|
41
|
-
chunkIds={['1', '2']}
|
|
94
|
+
chunkIds={['1', '2', '3', '4', '5']}
|
|
42
95
|
labels={['Job #2', 'Job #1']}
|
|
96
|
+
onClose={action('CLOSE')}
|
|
43
97
|
/>
|
|
44
98
|
);
|
|
@@ -1,36 +1,210 @@
|
|
|
1
|
+
import type { ElementType } from 'react';
|
|
1
2
|
import React, { useMemo } from 'react';
|
|
2
3
|
import cx from 'classnames';
|
|
3
|
-
import
|
|
4
|
+
import orderBy from 'lodash/orderBy';
|
|
4
5
|
import noop from 'lodash/noop';
|
|
6
|
+
import type { MetricRunInfo, MetricTypeConfig, ReportMetricRow } from '@bundle-stats/utils';
|
|
5
7
|
import {
|
|
8
|
+
METRIC_TYPE_CONFIGS,
|
|
6
9
|
BUNDLE_MODULES_DUPLICATE,
|
|
7
10
|
FILE_TYPE_LABELS,
|
|
8
11
|
MODULE_SOURCE_TYPE_LABELS,
|
|
9
|
-
|
|
12
|
+
formatNumber,
|
|
10
13
|
getBundleModulesByChunk,
|
|
11
14
|
getBundleModulesByFileTpe,
|
|
12
15
|
getBundleModulesBySource,
|
|
16
|
+
getMetricRunInfo,
|
|
13
17
|
} from '@bundle-stats/utils';
|
|
14
|
-
import { Module, MetaChunk } from '@bundle-stats/utils/types/webpack';
|
|
18
|
+
import type { Module, MetaChunk } from '@bundle-stats/utils/types/webpack';
|
|
15
19
|
|
|
20
|
+
import type { ReportMetricModuleRow } from '../../types';
|
|
16
21
|
import { Stack } from '../../layout/stack';
|
|
17
22
|
import { Tag } from '../../ui/tag';
|
|
18
23
|
import { ComponentLink } from '../component-link';
|
|
24
|
+
import { RunInfo } from '../run-info';
|
|
19
25
|
import { EntryInfo, EntryInfoMetaLink } from '../entry-info';
|
|
20
26
|
import css from './module-info.module.css';
|
|
27
|
+
import { FlexStack } from '../../layout';
|
|
28
|
+
import { ModuleSizeMetric, ModuleSizeMetrics } from '../../constants';
|
|
29
|
+
|
|
30
|
+
interface DuplicateInstancesProps {
|
|
31
|
+
current: number;
|
|
32
|
+
baseline: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const DuplicateInstances = ({ current, baseline }: DuplicateInstancesProps) => {
|
|
36
|
+
const runInfo = getMetricRunInfo(
|
|
37
|
+
{ biggerIsBetter: false, formatter: formatNumber },
|
|
38
|
+
current,
|
|
39
|
+
baseline,
|
|
40
|
+
) as MetricRunInfo;
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<RunInfo
|
|
44
|
+
current={runInfo.displayValue}
|
|
45
|
+
delta={runInfo.displayDeltaPercentage}
|
|
46
|
+
deltaPercentage={runInfo.displayDelta}
|
|
47
|
+
deltaType={runInfo.deltaType}
|
|
48
|
+
size="small"
|
|
49
|
+
className={css.duplicateInstances}
|
|
50
|
+
/>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
enum ChunkDataState {
|
|
55
|
+
ADDED = 2,
|
|
56
|
+
REMOVED = 1,
|
|
57
|
+
NOT_CHANGED = 0,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const ChunkDataStateKind = {
|
|
61
|
+
[ChunkDataState.ADDED]: 'info',
|
|
62
|
+
[ChunkDataState.REMOVED]: 'danger',
|
|
63
|
+
[ChunkDataState.NOT_CHANGED]: 'default',
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
interface ChunkData {
|
|
67
|
+
id: string;
|
|
68
|
+
state: ChunkDataState;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function compareChunksData(
|
|
72
|
+
currentChunkIds: Array<string>,
|
|
73
|
+
baselineChunkIds: Array<string>,
|
|
74
|
+
): Array<ChunkData> {
|
|
75
|
+
const result: Array<ChunkData> = [];
|
|
76
|
+
|
|
77
|
+
currentChunkIds.forEach((chunkId) => {
|
|
78
|
+
if (!baselineChunkIds.includes(chunkId)) {
|
|
79
|
+
result.push({ id: chunkId, state: ChunkDataState.ADDED });
|
|
80
|
+
} else {
|
|
81
|
+
result.push({ id: chunkId, state: ChunkDataState.NOT_CHANGED });
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
baselineChunkIds.forEach((chunk) => {
|
|
86
|
+
if (!currentChunkIds.includes(chunk)) {
|
|
87
|
+
result.push({ id: chunk, state: ChunkDataState.REMOVED });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
return orderBy(result, 'state', 'desc');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface ChunksDeltaProps {
|
|
95
|
+
customComponentLink: ElementType;
|
|
96
|
+
runs: Array<Module & MetricRunInfo>;
|
|
97
|
+
chunks: Array<MetaChunk>;
|
|
98
|
+
chunkIds: Array<string>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const ChunksDelta = (props: ChunksDeltaProps) => {
|
|
102
|
+
const { customComponentLink: CustomComponentLink, runs, chunks, chunkIds } = props;
|
|
103
|
+
|
|
104
|
+
const currentChunkIds = runs[0]?.chunkIds || [];
|
|
105
|
+
const baselineChunkIds = runs[runs.length - 1]?.chunkIds || [];
|
|
106
|
+
|
|
107
|
+
const chunksData = compareChunksData(currentChunkIds, baselineChunkIds);
|
|
108
|
+
|
|
109
|
+
return (
|
|
110
|
+
<Stack space="xxxsmall">
|
|
111
|
+
<div>
|
|
112
|
+
{chunksData.map((chunkData) => {
|
|
113
|
+
const chunk = chunks?.find(({ id }) => id === chunkData.id);
|
|
114
|
+
|
|
115
|
+
if (!chunk) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<EntryInfoMetaLink
|
|
121
|
+
as={CustomComponentLink}
|
|
122
|
+
{...getBundleModulesByChunk(chunkIds, chunkData.id)}
|
|
123
|
+
className={cx(
|
|
124
|
+
css.chunksItem,
|
|
125
|
+
css[`chunksItem-${ChunkDataStateKind[chunkData.state]}`],
|
|
126
|
+
)}
|
|
127
|
+
>
|
|
128
|
+
{chunk.name}
|
|
129
|
+
</EntryInfoMetaLink>
|
|
130
|
+
);
|
|
131
|
+
})}
|
|
132
|
+
</div>
|
|
133
|
+
</Stack>
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
interface ModuleSizeRunInfoProps {
|
|
138
|
+
metric: MetricTypeConfig;
|
|
139
|
+
title: string;
|
|
140
|
+
titleTooltip?: string;
|
|
141
|
+
current?: number;
|
|
142
|
+
baseline?: number;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const ModuleSizeRunInfo = (props: ModuleSizeRunInfoProps) => {
|
|
146
|
+
const { metric, title, titleTooltip, current, baseline } = props;
|
|
147
|
+
|
|
148
|
+
const runInfo = getMetricRunInfo(metric, current || 0, baseline || 0) as MetricRunInfo;
|
|
149
|
+
|
|
150
|
+
return (
|
|
151
|
+
<RunInfo
|
|
152
|
+
title={title}
|
|
153
|
+
titleTooltip={titleTooltip}
|
|
154
|
+
current={runInfo.displayValue}
|
|
155
|
+
delta={runInfo.displayDeltaPercentage}
|
|
156
|
+
deltaPercentage={runInfo.displayDelta}
|
|
157
|
+
deltaType={runInfo.deltaType}
|
|
158
|
+
baseline={metric.formatter(baseline || 0)}
|
|
159
|
+
/>
|
|
160
|
+
);
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const renderRunInfo = (item: ReportMetricRow) => {
|
|
164
|
+
const itemModule = item as ReportMetricModuleRow;
|
|
165
|
+
|
|
166
|
+
const currentRun = itemModule.runs[0];
|
|
167
|
+
const baselineRun =
|
|
168
|
+
itemModule.runs.length > 1 ? itemModule.runs[itemModule.runs.length - 1] : null;
|
|
169
|
+
const metric = METRIC_TYPE_CONFIGS.METRIC_TYPE_FILE_SIZE;
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<FlexStack space="small" className={css.moduleSizes}>
|
|
173
|
+
{itemModule.duplicated && (
|
|
174
|
+
<>
|
|
175
|
+
<ModuleSizeRunInfo
|
|
176
|
+
metric={metric}
|
|
177
|
+
title={ModuleSizeMetrics[ModuleSizeMetric.TOTAL_SIZE].label}
|
|
178
|
+
titleTooltip={ModuleSizeMetrics[ModuleSizeMetric.TOTAL_SIZE].tooltip}
|
|
179
|
+
current={currentRun.sizeTotal}
|
|
180
|
+
baseline={baselineRun.sizeTotal}
|
|
181
|
+
/>
|
|
182
|
+
<ModuleSizeRunInfo
|
|
183
|
+
metric={metric}
|
|
184
|
+
title={ModuleSizeMetrics[ModuleSizeMetric.DUPLICATE_SIZE].label}
|
|
185
|
+
titleTooltip={ModuleSizeMetrics[ModuleSizeMetric.DUPLICATE_SIZE].tooltip}
|
|
186
|
+
current={currentRun.sizeDuplicate}
|
|
187
|
+
baseline={baselineRun.sizeDuplicate}
|
|
188
|
+
/>
|
|
189
|
+
</>
|
|
190
|
+
)}
|
|
191
|
+
<ModuleSizeRunInfo
|
|
192
|
+
metric={metric}
|
|
193
|
+
title={ModuleSizeMetrics[ModuleSizeMetric.SIZE].label}
|
|
194
|
+
titleTooltip={ModuleSizeMetrics[ModuleSizeMetric.SIZE].tooltip}
|
|
195
|
+
current={currentRun.size}
|
|
196
|
+
baseline={baselineRun.size}
|
|
197
|
+
/>
|
|
198
|
+
</FlexStack>
|
|
199
|
+
);
|
|
200
|
+
};
|
|
21
201
|
|
|
22
202
|
interface ModuleInfoProps {
|
|
23
|
-
item:
|
|
24
|
-
label: string;
|
|
25
|
-
changed?: boolean;
|
|
26
|
-
duplicated?: boolean;
|
|
27
|
-
thirdParty?: boolean;
|
|
28
|
-
fileType?: string;
|
|
29
|
-
runs: Array<Module & MetricRunInfo>;
|
|
30
|
-
};
|
|
203
|
+
item: ReportMetricModuleRow;
|
|
31
204
|
chunks?: Array<MetaChunk>;
|
|
32
205
|
chunkIds?: Array<string>;
|
|
33
206
|
labels: Array<string>;
|
|
207
|
+
metricLabel?: string;
|
|
34
208
|
customComponentLink?: React.ElementType;
|
|
35
209
|
onClose: () => void;
|
|
36
210
|
}
|
|
@@ -42,6 +216,7 @@ export const ModuleInfo = (props: ModuleInfoProps & React.ComponentProps<'div'>)
|
|
|
42
216
|
labels,
|
|
43
217
|
chunks = [],
|
|
44
218
|
chunkIds = [],
|
|
219
|
+
metricLabel = '',
|
|
45
220
|
customComponentLink: CustomComponentLink = ComponentLink,
|
|
46
221
|
onClick = noop,
|
|
47
222
|
onClose,
|
|
@@ -69,37 +244,25 @@ export const ModuleInfo = (props: ModuleInfoProps & React.ComponentProps<'div'>)
|
|
|
69
244
|
? MODULE_SOURCE_TYPE_LABELS.THIRD_PARTY
|
|
70
245
|
: MODULE_SOURCE_TYPE_LABELS.FIRST_PARTY;
|
|
71
246
|
|
|
247
|
+
const currentChunkCount = (item.runs[0]?.chunkIds || []).length;
|
|
248
|
+
const baselineChunkCount = (item.runs[item.runs.length - 1]?.chunkIds || []).length;
|
|
249
|
+
const currentDuplicateInstances = currentChunkCount > 0 ? currentChunkCount - 1 : 0;
|
|
250
|
+
const baselineDuplicateInstances = baselineChunkCount > 0 ? baselineChunkCount - 1 : 0;
|
|
251
|
+
const hasDuplicates = currentDuplicateInstances !== 0 || baselineDuplicateInstances !== 0;
|
|
252
|
+
|
|
72
253
|
return (
|
|
73
|
-
<EntryInfo
|
|
254
|
+
<EntryInfo
|
|
255
|
+
item={item as ReportMetricRow}
|
|
256
|
+
labels={labels}
|
|
257
|
+
tags={tags}
|
|
258
|
+
onClose={onClose}
|
|
259
|
+
renderRunInfo={renderRunInfo}
|
|
260
|
+
runSizeLabel={metricLabel}
|
|
261
|
+
className={rootClassName}
|
|
262
|
+
>
|
|
74
263
|
<Stack space="xxxsmall">
|
|
75
|
-
{item.runs?.map(
|
|
76
|
-
(currentRun, index) =>
|
|
77
|
-
!isEmpty(currentRun?.chunkIds) && (
|
|
78
|
-
<EntryInfo.Meta label="Chunks" className={css.chunks}>
|
|
79
|
-
{labels[index]}:
|
|
80
|
-
{currentRun.chunkIds.map((chunkId) => {
|
|
81
|
-
const chunk = chunks?.find(({ id }) => id === chunkId);
|
|
82
|
-
|
|
83
|
-
if (!chunk) {
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<EntryInfoMetaLink
|
|
89
|
-
as={CustomComponentLink}
|
|
90
|
-
{...getBundleModulesByChunk(chunkIds, chunkId)}
|
|
91
|
-
className={css.chunksItem}
|
|
92
|
-
>
|
|
93
|
-
{chunk.name}
|
|
94
|
-
</EntryInfoMetaLink>
|
|
95
|
-
);
|
|
96
|
-
})}
|
|
97
|
-
</EntryInfo.Meta>
|
|
98
|
-
),
|
|
99
|
-
)}
|
|
100
|
-
|
|
101
264
|
{item?.fileType && (
|
|
102
|
-
<EntryInfo.Meta label="File type">
|
|
265
|
+
<EntryInfo.Meta label="File type" tooltip="Module file type: JS, CSS">
|
|
103
266
|
<EntryInfoMetaLink
|
|
104
267
|
as={CustomComponentLink}
|
|
105
268
|
{...getBundleModulesByFileTpe(item.fileType, fileTypeLabel)}
|
|
@@ -109,7 +272,7 @@ export const ModuleInfo = (props: ModuleInfoProps & React.ComponentProps<'div'>)
|
|
|
109
272
|
</EntryInfo.Meta>
|
|
110
273
|
)}
|
|
111
274
|
|
|
112
|
-
<EntryInfo.Meta label="Source">
|
|
275
|
+
<EntryInfo.Meta label="Source" tooltip="Module source type: first party, third party">
|
|
113
276
|
<EntryInfoMetaLink
|
|
114
277
|
as={CustomComponentLink}
|
|
115
278
|
{...getBundleModulesBySource(item.thirdParty || false, sourceTypeLabel)}
|
|
@@ -118,6 +281,24 @@ export const ModuleInfo = (props: ModuleInfoProps & React.ComponentProps<'div'>)
|
|
|
118
281
|
{sourceTypeLabel}
|
|
119
282
|
</EntryInfoMetaLink>
|
|
120
283
|
</EntryInfo.Meta>
|
|
284
|
+
|
|
285
|
+
{hasDuplicates && (
|
|
286
|
+
<EntryInfo.Meta label="Duplicates" tooltip="Module duplicate instances">
|
|
287
|
+
<DuplicateInstances
|
|
288
|
+
current={currentDuplicateInstances}
|
|
289
|
+
baseline={baselineDuplicateInstances}
|
|
290
|
+
/>
|
|
291
|
+
</EntryInfo.Meta>
|
|
292
|
+
)}
|
|
293
|
+
|
|
294
|
+
<EntryInfo.Meta label="Chunks" tooltip="Chunks that contain the module">
|
|
295
|
+
<ChunksDelta
|
|
296
|
+
runs={item.runs}
|
|
297
|
+
chunks={chunks}
|
|
298
|
+
chunkIds={chunkIds}
|
|
299
|
+
customComponentLink={CustomComponentLink}
|
|
300
|
+
/>
|
|
301
|
+
</EntryInfo.Meta>
|
|
121
302
|
</Stack>
|
|
122
303
|
</EntryInfo>
|
|
123
304
|
);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
|
-
import noop from 'lodash/noop';
|
|
3
2
|
import {
|
|
4
3
|
BUNDLE_PACKAGES_DUPLICATE,
|
|
5
4
|
PACKAGES_SEPARATOR,
|
|
@@ -79,14 +78,14 @@ export const PackageInfo = (props: PackageInfoProps & React.ComponentProps<'div'
|
|
|
79
78
|
return (
|
|
80
79
|
<EntryInfo
|
|
81
80
|
itemTitle={packageTitle}
|
|
82
|
-
item={item}
|
|
81
|
+
item={item as any}
|
|
83
82
|
labels={labels}
|
|
84
83
|
tags={tags}
|
|
85
84
|
runNameSelector="path"
|
|
86
85
|
onClose={onClose}
|
|
87
86
|
className={className}
|
|
88
87
|
>
|
|
89
|
-
<EntryInfo.Meta label="Path">
|
|
88
|
+
<EntryInfo.Meta label="Path" tooltip="Package path">
|
|
90
89
|
<FileName name={normalizedPackagePath} className={css.fileName} />
|
|
91
90
|
</EntryInfo.Meta>
|
|
92
91
|
|
package/src/constants.ts
CHANGED
|
@@ -39,9 +39,30 @@ export const METRICS_WEBPACK_PACKAGES = ['webpack.packageCount', 'webpack.duplic
|
|
|
39
39
|
export const SORT = {
|
|
40
40
|
ASC: 'asc',
|
|
41
41
|
DESC: 'desc',
|
|
42
|
-
};
|
|
42
|
+
} as const;
|
|
43
43
|
|
|
44
44
|
export enum MetricsDisplayType {
|
|
45
45
|
TABLE = 'table',
|
|
46
46
|
TREEMAP = 'treemap',
|
|
47
47
|
}
|
|
48
|
+
|
|
49
|
+
export enum ModuleSizeMetric {
|
|
50
|
+
SIZE = 'value',
|
|
51
|
+
DUPLICATE_SIZE = 'duplicateSize',
|
|
52
|
+
TOTAL_SIZE = 'totalSize',
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export const ModuleSizeMetrics = {
|
|
56
|
+
[ModuleSizeMetric.SIZE]: {
|
|
57
|
+
label: 'Size',
|
|
58
|
+
tooltip: 'Module size (excluding duplicate modules)',
|
|
59
|
+
},
|
|
60
|
+
[ModuleSizeMetric.TOTAL_SIZE]: {
|
|
61
|
+
label: 'Total size',
|
|
62
|
+
tooltip: 'Module total size (including duplicate modules)',
|
|
63
|
+
},
|
|
64
|
+
[ModuleSizeMetric.DUPLICATE_SIZE]: {
|
|
65
|
+
label: 'Duplicate size',
|
|
66
|
+
tooltip: 'Module duplicate size',
|
|
67
|
+
},
|
|
68
|
+
};
|