@bundle-stats/utils 4.2.2 → 4.2.3-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/README.md +15 -10
- package/lib/browsertime/metrics.js +22 -22
- package/lib/browsertime/metrics.js.map +1 -1
- package/lib/config/component-links.js +7 -1
- package/lib/config/component-links.js.map +1 -1
- package/lib/config/file-types.js +3 -3
- package/lib/config/file-types.js.map +1 -1
- package/lib/config/index.js +1 -2
- package/lib/config/index.js.map +1 -1
- package/lib/config/metric-types.js +28 -0
- package/lib/config/metric-types.js.map +1 -0
- package/lib/constants.js +23 -22
- package/lib/constants.js.map +1 -1
- package/lib/i18n.js +15 -10
- package/lib/i18n.js.map +1 -1
- package/lib/jobs/create-job.js +17 -7
- package/lib/jobs/create-job.js.map +1 -1
- package/lib/jobs/create-jobs.js.map +1 -1
- package/lib/lighthouse/metrics.js +13 -13
- package/lib/lighthouse/metrics.js.map +1 -1
- package/lib/report/add-row-metric-data.js +4 -4
- package/lib/report/add-row-metric-data.js.map +1 -1
- package/lib/report/compare-metrics.js +4 -1
- package/lib/report/compare-metrics.js.map +1 -1
- package/lib/report/create.js +17 -7
- package/lib/report/create.js.map +1 -1
- package/lib/report/get-metric-changed.js.map +1 -1
- package/lib/report/index.js +1 -0
- package/lib/report/index.js.map +1 -1
- package/lib/report/merge-metrics-by-key.js +1 -1
- package/lib/report/merge-metrics-by-key.js.map +1 -1
- package/lib/utils/component-links.js +191 -27
- package/lib/utils/component-links.js.map +1 -1
- package/lib/utils/date.js.map +1 -1
- package/lib/utils/delta.js +35 -30
- package/lib/utils/delta.js.map +1 -1
- package/lib/utils/format.js +5 -6
- package/lib/utils/format.js.map +1 -1
- package/lib/utils/insights.js.map +1 -1
- package/lib/utils/metrics.js +49 -32
- package/lib/utils/metrics.js.map +1 -1
- package/lib/webpack/compare.js +16 -12
- package/lib/webpack/compare.js.map +1 -1
- package/lib/webpack/extract/assets.js +7 -1
- package/lib/webpack/extract/assets.js.map +1 -1
- package/lib/webpack/extract/meta.js.map +1 -1
- package/lib/webpack/extract/modules-packages-duplicate.js +13 -8
- package/lib/webpack/extract/modules-packages-duplicate.js.map +1 -1
- package/lib/webpack/extract/modules-packages-new-insight.js.map +1 -1
- package/lib/webpack/extract/modules-packages.js +8 -1
- package/lib/webpack/extract/modules-packages.js.map +1 -1
- package/lib/webpack/extract/modules.js +45 -20
- package/lib/webpack/extract/modules.js.map +1 -1
- package/lib/webpack/metrics.js +35 -34
- package/lib/webpack/metrics.js.map +1 -1
- package/lib/webpack/selectors.js +40 -3
- package/lib/webpack/selectors.js.map +1 -1
- package/lib/webpack/types.js +1 -1
- package/lib/webpack/types.js.map +1 -1
- package/lib/webpack/utils.js +37 -25
- package/lib/webpack/utils.js.map +1 -1
- package/lib-esm/browsertime/metrics.js +22 -22
- package/lib-esm/browsertime/metrics.js.map +1 -1
- package/lib-esm/config/component-links.js +6 -0
- package/lib-esm/config/component-links.js.map +1 -1
- package/lib-esm/config/file-types.js +3 -3
- package/lib-esm/config/file-types.js.map +1 -1
- package/lib-esm/config/index.js +1 -2
- package/lib-esm/config/index.js.map +1 -1
- package/lib-esm/config/metric-types.js +25 -0
- package/lib-esm/config/metric-types.js.map +1 -0
- package/lib-esm/constants.js +19 -18
- package/lib-esm/constants.js.map +1 -1
- package/lib-esm/i18n.js +15 -10
- package/lib-esm/i18n.js.map +1 -1
- package/lib-esm/jobs/create-job.js.map +1 -1
- package/lib-esm/jobs/create-jobs.js.map +1 -1
- package/lib-esm/lighthouse/metrics.js +13 -13
- package/lib-esm/lighthouse/metrics.js.map +1 -1
- package/lib-esm/report/add-row-metric-data.js +4 -4
- package/lib-esm/report/add-row-metric-data.js.map +1 -1
- package/lib-esm/report/compare-metrics.js +4 -1
- package/lib-esm/report/compare-metrics.js.map +1 -1
- package/lib-esm/report/create.js.map +1 -1
- package/lib-esm/report/get-metric-changed.js.map +1 -1
- package/lib-esm/report/index.js +1 -0
- package/lib-esm/report/index.js.map +1 -1
- package/lib-esm/report/merge-metrics-by-key.js +1 -1
- package/lib-esm/report/merge-metrics-by-key.js.map +1 -1
- package/lib-esm/utils/component-links.js +185 -27
- package/lib-esm/utils/component-links.js.map +1 -1
- package/lib-esm/utils/date.js.map +1 -1
- package/lib-esm/utils/delta.js +32 -26
- package/lib-esm/utils/delta.js.map +1 -1
- package/lib-esm/utils/format.js.map +1 -1
- package/lib-esm/utils/insights.js.map +1 -1
- package/lib-esm/utils/metrics.js +44 -27
- package/lib-esm/utils/metrics.js.map +1 -1
- package/lib-esm/webpack/compare.js +7 -5
- package/lib-esm/webpack/compare.js.map +1 -1
- package/lib-esm/webpack/extract/assets.js +7 -1
- package/lib-esm/webpack/extract/assets.js.map +1 -1
- package/lib-esm/webpack/extract/meta.js.map +1 -1
- package/lib-esm/webpack/extract/modules-packages-duplicate.js +13 -8
- package/lib-esm/webpack/extract/modules-packages-duplicate.js.map +1 -1
- package/lib-esm/webpack/extract/modules-packages-new-insight.js.map +1 -1
- package/lib-esm/webpack/extract/modules-packages.js +8 -1
- package/lib-esm/webpack/extract/modules-packages.js.map +1 -1
- package/lib-esm/webpack/extract/modules.js +45 -20
- package/lib-esm/webpack/extract/modules.js.map +1 -1
- package/lib-esm/webpack/metrics.js +35 -34
- package/lib-esm/webpack/metrics.js.map +1 -1
- package/lib-esm/webpack/selectors.js +35 -1
- package/lib-esm/webpack/selectors.js.map +1 -1
- package/lib-esm/webpack/utils.js +37 -25
- package/lib-esm/webpack/utils.js.map +1 -1
- package/package.json +20 -22
- package/src/browsertime/metrics.js +22 -22
- package/src/config/component-links.js +7 -0
- package/src/config/file-types.js +3 -3
- package/src/config/index.js +1 -2
- package/src/config/metric-types.ts +25 -0
- package/src/constants.ts +94 -34
- package/src/i18n.js +15 -10
- package/src/lighthouse/metrics.js +13 -15
- package/src/report/add-row-metric-data.ts +42 -38
- package/src/report/compare-metrics.ts +7 -4
- package/src/report/create.ts +1 -1
- package/src/report/index.js +1 -0
- package/src/report/merge-metrics-by-key.ts +7 -5
- package/src/report/types.ts +17 -0
- package/src/utils/component-links.ts +276 -35
- package/src/utils/delta.ts +98 -0
- package/src/utils/metrics.ts +88 -0
- package/src/webpack/compare.ts +62 -0
- package/src/webpack/extract/assets.ts +13 -7
- package/src/webpack/extract/modules-packages-duplicate.ts +23 -15
- package/src/webpack/extract/modules-packages.ts +74 -61
- package/src/webpack/extract/modules.ts +66 -33
- package/src/webpack/{metrics.js → metrics.ts} +41 -39
- package/src/webpack/selectors.js +82 -9
- package/src/webpack/types.ts +13 -4
- package/src/webpack/utils.ts +173 -0
- package/types/browsertime/compare.d.ts +1 -1
- package/types/browsertime/metrics.d.ts +63 -43
- package/types/config/component-links.d.ts +26 -18
- package/types/config/file-types.d.ts +17 -17
- package/types/config/index.d.ts +1 -2
- package/types/config/metric-types.d.ts +2 -0
- package/types/constants.d.ts +52 -29
- package/types/i18n.d.ts +19 -14
- package/types/lighthouse/compare.d.ts +1 -1
- package/types/lighthouse/filter.d.ts +1 -1
- package/types/lighthouse/metrics.d.ts +36 -28
- package/types/report/add-row-metric-data.d.ts +1 -16
- package/types/report/compare-metrics.d.ts +1 -1
- package/types/report/index.d.ts +1 -0
- package/types/report/merge-metrics-by-key.d.ts +1 -1
- package/types/report/types.d.ts +12 -0
- package/types/utils/component-links.d.ts +24 -8
- package/types/utils/delta.d.ts +4 -6
- package/types/utils/metrics.d.ts +4 -3
- package/types/webpack/compare.d.ts +11 -14
- package/types/webpack/constants.d.ts +1 -1
- package/types/webpack/extract/assets-size-total-insight.d.ts +2 -2
- package/types/webpack/extract/modules-packages-duplicate.d.ts +3 -3
- package/types/webpack/extract/modules.d.ts +1 -1
- package/types/webpack/metrics.d.ts +7 -153
- package/types/webpack/selectors.d.ts +21 -7
- package/types/webpack/types.d.ts +4 -4
- package/types/webpack/utils.d.ts +8 -8
- package/CHANGELOG.md +0 -333
- package/lib/config/delta.js +0 -13
- package/lib/config/delta.js.map +0 -1
- package/lib/config/metrics.js +0 -32
- package/lib/config/metrics.js.map +0 -1
- package/lib-esm/config/delta.js +0 -10
- package/lib-esm/config/delta.js.map +0 -1
- package/lib-esm/config/metrics.js +0 -29
- package/lib-esm/config/metrics.js.map +0 -1
- package/src/config/delta.js +0 -11
- package/src/config/metrics.js +0 -35
- package/src/utils/delta.js +0 -94
- package/src/utils/metrics.js +0 -92
- package/src/webpack/compare.js +0 -81
- package/src/webpack/utils.js +0 -155
- package/types/config/delta.d.ts +0 -9
- package/types/config/metrics.d.ts +0 -35
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import round from 'lodash/round';
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
MetricRun,
|
|
5
|
+
MetricRunDelta,
|
|
6
|
+
MetricRunInfoDeltaType,
|
|
7
|
+
MetricTypeConfig,
|
|
8
|
+
} from '../constants';
|
|
9
|
+
|
|
10
|
+
export function getDelta(baseline?: MetricRun | null, current?: MetricRun | null): MetricRunDelta {
|
|
11
|
+
const baselineValue = baseline?.value || 0;
|
|
12
|
+
const currentValue = current?.value || 0;
|
|
13
|
+
|
|
14
|
+
if (baselineValue === currentValue) {
|
|
15
|
+
return {
|
|
16
|
+
delta: 0,
|
|
17
|
+
deltaPercentage: 0,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (baselineValue === 0) {
|
|
22
|
+
return {
|
|
23
|
+
delta: currentValue,
|
|
24
|
+
deltaPercentage: 100,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
delta: currentValue - baselineValue,
|
|
30
|
+
deltaPercentage: round((currentValue / baselineValue) * 100 - 100, 8),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Format delta output
|
|
36
|
+
*/
|
|
37
|
+
export function formatDelta(value: number, formatter: MetricTypeConfig['formatter']): string {
|
|
38
|
+
let sign = '';
|
|
39
|
+
|
|
40
|
+
if (value > 0) {
|
|
41
|
+
sign = '+';
|
|
42
|
+
} else if (value < 0) {
|
|
43
|
+
sign = '-';
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let absValue = Math.abs(value);
|
|
47
|
+
|
|
48
|
+
if (absValue > 0 && absValue < 0.01) {
|
|
49
|
+
sign = `~${sign}`;
|
|
50
|
+
absValue = 0.01;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (absValue > 0.01) {
|
|
54
|
+
absValue = round(absValue, 2);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return `${sign}${formatter(absValue)}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get delta type
|
|
62
|
+
*/
|
|
63
|
+
export function getDeltaType(
|
|
64
|
+
deltaPercentageValue: number,
|
|
65
|
+
biggerIsBetter?: MetricTypeConfig['biggerIsBetter'],
|
|
66
|
+
): MetricRunInfoDeltaType {
|
|
67
|
+
// No change
|
|
68
|
+
if (deltaPercentageValue === 0) {
|
|
69
|
+
return 'NO_CHANGE';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Change + no regression/improvement
|
|
73
|
+
if (biggerIsBetter === null) {
|
|
74
|
+
return 'CHANGE';
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (deltaPercentageValue < -50) {
|
|
78
|
+
return biggerIsBetter ? 'HIGH_NEGATIVE' : 'HIGH_POSITIVE';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (deltaPercentageValue < -5) {
|
|
82
|
+
return biggerIsBetter ? 'NEGATIVE' : 'POSITIVE';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (deltaPercentageValue < 0) {
|
|
86
|
+
return biggerIsBetter ? 'LOW_NEGATIVE' : 'LOW_POSITIVE';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (deltaPercentageValue <= 5) {
|
|
90
|
+
return biggerIsBetter ? 'LOW_POSITIVE' : 'LOW_NEGATIVE';
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (deltaPercentageValue <= 50) {
|
|
94
|
+
return biggerIsBetter ? 'POSITIVE' : 'NEGATIVE';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return biggerIsBetter ? 'HIGH_POSITIVE' : 'HIGH_NEGATIVE';
|
|
98
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import get from 'lodash/get';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
MetricRunInfo,
|
|
5
|
+
MetricRunInfoBaseline,
|
|
6
|
+
Metric,
|
|
7
|
+
MetricTypes,
|
|
8
|
+
MetricConfig,
|
|
9
|
+
MetricTypeConfig,
|
|
10
|
+
} from '../constants';
|
|
11
|
+
import { METRIC_TYPE_CONFIGS } from '../config';
|
|
12
|
+
import { metrics as webpackMetricTypes } from '../webpack/metrics';
|
|
13
|
+
import { metrics as lighthouseMetricTypes } from '../lighthouse/metrics';
|
|
14
|
+
import { metrics as browsertimeMeticTypes } from '../browsertime/metrics';
|
|
15
|
+
import { formatPercentage } from './format';
|
|
16
|
+
import { formatDelta, getDelta, getDeltaType } from './delta';
|
|
17
|
+
|
|
18
|
+
export function createGetMetricType(metrics: Record<string, unknown>) {
|
|
19
|
+
return function getMetricType(key: string, type?: MetricTypes): Metric {
|
|
20
|
+
const metric = get(metrics, key) as MetricConfig;
|
|
21
|
+
|
|
22
|
+
if (metric?.type) {
|
|
23
|
+
return {
|
|
24
|
+
...METRIC_TYPE_CONFIGS[metric.type],
|
|
25
|
+
...metric,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const resolvedType = type || MetricTypes.Numeric;
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
...METRIC_TYPE_CONFIGS[resolvedType],
|
|
33
|
+
type: resolvedType,
|
|
34
|
+
label: key,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function getGlobalMetricType(key: string, type?: MetricTypes): Metric {
|
|
40
|
+
const getMetricType = createGetMetricType({
|
|
41
|
+
webpack: webpackMetricTypes,
|
|
42
|
+
lighthouse: lighthouseMetricTypes,
|
|
43
|
+
browsertime: browsertimeMeticTypes,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return getMetricType(key, type);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function getMetricRunInfo(
|
|
50
|
+
metricType: MetricTypeConfig,
|
|
51
|
+
current: number,
|
|
52
|
+
baseline?: number,
|
|
53
|
+
): MetricRunInfo | MetricRunInfoBaseline {
|
|
54
|
+
const { formatter, biggerIsBetter } = metricType;
|
|
55
|
+
|
|
56
|
+
const runInfo = {
|
|
57
|
+
value: current,
|
|
58
|
+
displayValue: formatter(current),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Skip computing delta fields when the baseline is missing
|
|
62
|
+
if (typeof baseline === 'undefined') {
|
|
63
|
+
return runInfo;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const { delta, deltaPercentage } = getDelta({ value: baseline }, { value: current });
|
|
67
|
+
|
|
68
|
+
// Resolve regression/improvement flag
|
|
69
|
+
let regression;
|
|
70
|
+
|
|
71
|
+
if (biggerIsBetter === null) {
|
|
72
|
+
regression = null;
|
|
73
|
+
} else if ((biggerIsBetter === true && delta > 0) || (biggerIsBetter === false && delta < 0)) {
|
|
74
|
+
regression = false;
|
|
75
|
+
} else if ((biggerIsBetter === false && delta > 0) || (biggerIsBetter === true && delta < 0)) {
|
|
76
|
+
regression = true;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
...runInfo,
|
|
81
|
+
delta,
|
|
82
|
+
deltaPercentage,
|
|
83
|
+
displayDelta: formatDelta(delta, formatter),
|
|
84
|
+
displayDeltaPercentage: formatDelta(deltaPercentage, formatPercentage),
|
|
85
|
+
deltaType: getDeltaType(deltaPercentage, biggerIsBetter),
|
|
86
|
+
...(typeof regression !== 'undefined' && { regression }),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { MetricTypes } from '../constants';
|
|
2
|
+
import { compareMetrics } from '../report/compare-metrics';
|
|
3
|
+
import { MetricReportRowTransformFn } from '../report/types';
|
|
4
|
+
import {
|
|
5
|
+
SECTION_WEBPACK_STATS,
|
|
6
|
+
SECTION_WEBPACK_SIZES,
|
|
7
|
+
SECTION_WEBPACK_ASSETS,
|
|
8
|
+
SECTION_WEBPACK_MODULES,
|
|
9
|
+
SECTION_WEBPACK_PACKAGES,
|
|
10
|
+
SECTIONS,
|
|
11
|
+
} from './constants';
|
|
12
|
+
import { selectors, getModulesDuplicateSizeMetrics, getModulesTotalSizeMetrics } from './selectors';
|
|
13
|
+
|
|
14
|
+
const compareStats = (jobs: Array<unknown>, rowTransformers?: Array<MetricReportRowTransformFn>) =>
|
|
15
|
+
compareMetrics(jobs, selectors.stats, undefined, rowTransformers);
|
|
16
|
+
|
|
17
|
+
const compareSizes = (jobs: Array<unknown>, rowTransformers?: Array<MetricReportRowTransformFn>) =>
|
|
18
|
+
compareMetrics(jobs, selectors.sizes, undefined, rowTransformers);
|
|
19
|
+
|
|
20
|
+
const compareAssets = (jobs: Array<unknown>, rowTransformers?: Array<MetricReportRowTransformFn>) =>
|
|
21
|
+
compareMetrics(jobs, selectors.assets, MetricTypes.FileSize, rowTransformers);
|
|
22
|
+
|
|
23
|
+
const compareModules = (
|
|
24
|
+
jobs: Array<unknown>,
|
|
25
|
+
rowTransformers?: Array<MetricReportRowTransformFn>,
|
|
26
|
+
) => compareMetrics(jobs, selectors.modules, MetricTypes.FileSize, rowTransformers);
|
|
27
|
+
|
|
28
|
+
export const compareModuleDuplicateSize = (
|
|
29
|
+
jobs: Array<unknown>,
|
|
30
|
+
rowTransformers?: Array<MetricReportRowTransformFn>,
|
|
31
|
+
) => compareMetrics(jobs, getModulesDuplicateSizeMetrics, MetricTypes.FileSize, rowTransformers);
|
|
32
|
+
|
|
33
|
+
export const compareModuleTotalSize = (
|
|
34
|
+
jobs: Array<unknown>,
|
|
35
|
+
rowTransformers?: Array<MetricReportRowTransformFn>,
|
|
36
|
+
) => compareMetrics(jobs, getModulesTotalSizeMetrics, MetricTypes.FileSize, rowTransformers);
|
|
37
|
+
|
|
38
|
+
const comparePackages = (
|
|
39
|
+
jobs: Array<unknown>,
|
|
40
|
+
rowTransformers?: Array<MetricReportRowTransformFn>,
|
|
41
|
+
) => compareMetrics(jobs, selectors.packages, MetricTypes.FileSize, rowTransformers);
|
|
42
|
+
|
|
43
|
+
export const compareBySection = {
|
|
44
|
+
[SECTION_WEBPACK_STATS]: compareStats,
|
|
45
|
+
[SECTION_WEBPACK_SIZES]: compareSizes,
|
|
46
|
+
[SECTION_WEBPACK_ASSETS]: compareAssets,
|
|
47
|
+
[SECTION_WEBPACK_MODULES]: compareModules,
|
|
48
|
+
[SECTION_WEBPACK_PACKAGES]: comparePackages,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/* eslint-disable prettier/prettier */
|
|
52
|
+
/**
|
|
53
|
+
* Compare webpack sections
|
|
54
|
+
*/
|
|
55
|
+
export const compare = (jobs: Array<unknown>) => SECTIONS.reduce(
|
|
56
|
+
(agg, sectionId) => ({
|
|
57
|
+
...agg,
|
|
58
|
+
[sectionId]: compareBySection[sectionId as keyof typeof compareBySection](jobs),
|
|
59
|
+
}),
|
|
60
|
+
{},
|
|
61
|
+
);
|
|
62
|
+
/* eslint-enable prettier/prettier */
|
|
@@ -43,9 +43,7 @@ export const extractAssets = (webpackStats: WebpackStatsFiltered): MetricsAssets
|
|
|
43
43
|
const normalizedName = getAssetName(baseName);
|
|
44
44
|
const { size, name } = asset;
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
// eslint-disable-next-line no-param-reassign
|
|
48
|
-
agg[normalizedName] = {
|
|
46
|
+
const entry = {
|
|
49
47
|
name: baseName,
|
|
50
48
|
value: size || 0,
|
|
51
49
|
isEntry: entryAssetNames.includes(name),
|
|
@@ -54,6 +52,14 @@ export const extractAssets = (webpackStats: WebpackStatsFiltered): MetricsAssets
|
|
|
54
52
|
...(assetChunk && { chunkId: assetChunk.id }),
|
|
55
53
|
};
|
|
56
54
|
|
|
55
|
+
if (agg[normalizedName]) {
|
|
56
|
+
// eslint-disable-next-line no-param-reassign
|
|
57
|
+
agg[baseName] = entry;
|
|
58
|
+
} else {
|
|
59
|
+
// eslint-disable-next-line no-param-reassign
|
|
60
|
+
agg[normalizedName] = entry;
|
|
61
|
+
}
|
|
62
|
+
|
|
57
63
|
return agg;
|
|
58
64
|
}, {} as Assets) || {};
|
|
59
65
|
|
|
@@ -63,10 +69,10 @@ export const extractAssets = (webpackStats: WebpackStatsFiltered): MetricsAssets
|
|
|
63
69
|
},
|
|
64
70
|
...(!isEmpty(normalizedChunks)
|
|
65
71
|
? {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
meta: {
|
|
73
|
+
chunks: normalizedChunks?.map(({ id, name }) => ({ id, name })) || [],
|
|
74
|
+
},
|
|
75
|
+
}
|
|
70
76
|
: {}),
|
|
71
77
|
};
|
|
72
78
|
};
|
|
@@ -26,9 +26,9 @@ interface DuplicatePackageGroup {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
interface ModulesPackagesDuplicateData {
|
|
29
|
-
insights
|
|
29
|
+
insights?: {
|
|
30
30
|
duplicatePackages?: JobInsights['webpack']['duplicatePackages'];
|
|
31
|
-
duplicatePackagesV3
|
|
31
|
+
duplicatePackagesV3?: JobInsights['webpack']['duplicatePackagesV3'];
|
|
32
32
|
};
|
|
33
33
|
metrics: {
|
|
34
34
|
duplicatePackagesCount: MetricRun;
|
|
@@ -38,7 +38,7 @@ interface ModulesPackagesDuplicateData {
|
|
|
38
38
|
export const getDuplicatePackagesInsight = (
|
|
39
39
|
duplicatePackagesMap: Record<string, Array<string>>,
|
|
40
40
|
baselineDuplicatePackagesMap?: Record<string, Array<string>>,
|
|
41
|
-
): JobInsight<JobInsightDuplicatePackagesV3Data> => {
|
|
41
|
+
): JobInsight<JobInsightDuplicatePackagesV3Data> | null => {
|
|
42
42
|
const duplicateInstances: Array<string> = [];
|
|
43
43
|
const baselineDuplicateInstances: Array<string> = [];
|
|
44
44
|
const newDuplicateInstances: Array<string> = [];
|
|
@@ -82,8 +82,8 @@ export const getDuplicatePackagesInsight = (
|
|
|
82
82
|
const newDuplicateInstancesCount = newDuplicateInstances.length;
|
|
83
83
|
const removedDuplicateInstancesCount = removedDuplicateInstances.length;
|
|
84
84
|
|
|
85
|
-
let text
|
|
86
|
-
let insightType: InsightType;
|
|
85
|
+
let text = '';
|
|
86
|
+
let insightType: InsightType | null = null;
|
|
87
87
|
|
|
88
88
|
if (newDuplicateInstancesCount > 0 && removedDuplicateInstancesCount > 0) {
|
|
89
89
|
// New duplicate packages and removed duplicates
|
|
@@ -122,9 +122,11 @@ export const getDuplicatePackagesInsight = (
|
|
|
122
122
|
text = `Bundle contains ${duplicateInstancesCount} duplicate ${item}`;
|
|
123
123
|
insightType = InsightType.WARNING;
|
|
124
124
|
} else {
|
|
125
|
-
// No change
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
// No change - noop
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (!text || !insightType) {
|
|
129
|
+
return null;
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
return {
|
|
@@ -187,11 +189,15 @@ export const extractModulesPackagesDuplicate = (
|
|
|
187
189
|
baselineJob?.insights?.webpack?.duplicatePackages?.data ||
|
|
188
190
|
{};
|
|
189
191
|
|
|
192
|
+
const noDuplicatesDuplicatePackagesV3 = getDuplicatePackagesInsight({}, baselineDuplicatePackages);
|
|
193
|
+
|
|
190
194
|
if (!count) {
|
|
191
195
|
return {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
196
|
+
...(noDuplicatesDuplicatePackagesV3 && {
|
|
197
|
+
insights: {
|
|
198
|
+
duplicatePackagesV3: noDuplicatesDuplicatePackagesV3,
|
|
199
|
+
},
|
|
200
|
+
}),
|
|
195
201
|
metrics: {
|
|
196
202
|
duplicatePackagesCount: {
|
|
197
203
|
value: count,
|
|
@@ -228,16 +234,18 @@ export const extractModulesPackagesDuplicate = (
|
|
|
228
234
|
duplicatePackagesData[packageName] = packageData.children.map(({ id }) => id);
|
|
229
235
|
});
|
|
230
236
|
|
|
237
|
+
const duplicatePackagesV3 = getDuplicatePackagesInsight(
|
|
238
|
+
duplicatePackagesData,
|
|
239
|
+
baselineDuplicatePackages,
|
|
240
|
+
);
|
|
241
|
+
|
|
231
242
|
return {
|
|
232
243
|
insights: {
|
|
233
244
|
duplicatePackages: {
|
|
234
245
|
type: InsightType.WARNING,
|
|
235
246
|
data: duplicatePackagesData,
|
|
236
247
|
},
|
|
237
|
-
duplicatePackagesV3
|
|
238
|
-
duplicatePackagesData,
|
|
239
|
-
baselineDuplicatePackages,
|
|
240
|
-
),
|
|
248
|
+
...(duplicatePackagesV3 && { duplicatePackagesV3 }),
|
|
241
249
|
},
|
|
242
250
|
metrics: {
|
|
243
251
|
duplicatePackagesCount: {
|
|
@@ -22,6 +22,11 @@ const uniqLast = (data: Array<unknown>) => {
|
|
|
22
22
|
* Heuristics to extract package id, name, and path from a module path
|
|
23
23
|
*/
|
|
24
24
|
export const getPackageMetaFromModulePath = (modulePath: string) => {
|
|
25
|
+
// Skip module federation entries
|
|
26
|
+
if (modulePath.match(/^(provide|consume) shared module/)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
const paths = modulePath.match(MODULE_PATH_PACKAGES);
|
|
26
31
|
|
|
27
32
|
// No package paths found, skip
|
|
@@ -41,7 +46,11 @@ export const getPackageMetaFromModulePath = (modulePath: string) => {
|
|
|
41
46
|
return [];
|
|
42
47
|
}
|
|
43
48
|
|
|
44
|
-
return [...found]
|
|
49
|
+
return [...found]
|
|
50
|
+
.flat()
|
|
51
|
+
.slice(1)
|
|
52
|
+
.filter(Boolean)
|
|
53
|
+
.map((name) => name.replace(/\+/g, '/'));
|
|
45
54
|
})
|
|
46
55
|
.flat(),
|
|
47
56
|
);
|
|
@@ -71,74 +80,78 @@ export const extractModulesPackages = (
|
|
|
71
80
|
): MetricsPackages => {
|
|
72
81
|
const modules = Object.entries(currentExtractedData?.metrics?.modules || {});
|
|
73
82
|
|
|
74
|
-
const packages = modules.reduce(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
const packages = modules.reduce(
|
|
84
|
+
(agg, [modulePath, { value }]) => {
|
|
85
|
+
const packageMeta = getPackageMetaFromModulePath(modulePath);
|
|
86
|
+
|
|
87
|
+
if (!packageMeta) {
|
|
88
|
+
return agg;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const existingPackageData = agg[packageMeta.id];
|
|
92
|
+
|
|
93
|
+
// New package data
|
|
94
|
+
if (!existingPackageData) {
|
|
95
|
+
return {
|
|
96
|
+
...agg,
|
|
97
|
+
[packageMeta.id]: {
|
|
98
|
+
name: packageMeta.name,
|
|
99
|
+
path: packageMeta.path,
|
|
100
|
+
value,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Existing package info
|
|
106
|
+
if (existingPackageData.path === packageMeta.path) {
|
|
107
|
+
return {
|
|
108
|
+
...agg,
|
|
109
|
+
[packageMeta.id]: {
|
|
110
|
+
...existingPackageData,
|
|
111
|
+
value: existingPackageData.value + value,
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Same package name, but different paths (eg: symlinks)
|
|
117
|
+
const existingPackageWithEqualPath = Object.entries(agg).find(
|
|
118
|
+
([__, packageData]) => packageData.path === packageMeta.path,
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
if (existingPackageWithEqualPath) {
|
|
122
|
+
const [name, data] = existingPackageWithEqualPath;
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
...agg,
|
|
126
|
+
[name]: {
|
|
127
|
+
...data,
|
|
128
|
+
value: data.value + value,
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// New package name & data
|
|
134
|
+
const lastIndex =
|
|
135
|
+
max(
|
|
136
|
+
Object.keys(agg)
|
|
137
|
+
.map((id) => id.split('~'))
|
|
138
|
+
.filter(([id]) => id === packageMeta.id)
|
|
139
|
+
.map(([__, index]) => parseInt(index, 10)),
|
|
140
|
+
) || 0;
|
|
141
|
+
|
|
142
|
+
const packageName = [packageMeta.id, lastIndex + 1].join(PACKAGE_ID_SEPARATOR);
|
|
82
143
|
|
|
83
|
-
// New package data
|
|
84
|
-
if (!existingPackageData) {
|
|
85
144
|
return {
|
|
86
145
|
...agg,
|
|
87
|
-
[
|
|
146
|
+
[packageName]: {
|
|
88
147
|
name: packageMeta.name,
|
|
89
148
|
path: packageMeta.path,
|
|
90
149
|
value,
|
|
91
150
|
},
|
|
92
151
|
};
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (existingPackageData.path === packageMeta.path) {
|
|
97
|
-
return {
|
|
98
|
-
...agg,
|
|
99
|
-
[packageMeta.id]: {
|
|
100
|
-
...existingPackageData,
|
|
101
|
-
value: existingPackageData.value + value,
|
|
102
|
-
},
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Same package name, but different paths (eg: symlinks)
|
|
107
|
-
const existingPackageWithEqualPath = Object.entries(agg).find(
|
|
108
|
-
([__, packageData]) => packageData.path === packageMeta.path,
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
if (existingPackageWithEqualPath) {
|
|
112
|
-
const [name, data] = existingPackageWithEqualPath;
|
|
113
|
-
|
|
114
|
-
return {
|
|
115
|
-
...agg,
|
|
116
|
-
[name]: {
|
|
117
|
-
...data,
|
|
118
|
-
value: data.value + value,
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// New package name & data
|
|
124
|
-
const lastIndex = max(
|
|
125
|
-
Object.keys(agg)
|
|
126
|
-
.map((id) => id.split('~'))
|
|
127
|
-
.filter(([id]) => id === packageMeta.id)
|
|
128
|
-
.map(([__, index]) => parseInt(index, 10)),
|
|
129
|
-
) || 0;
|
|
130
|
-
|
|
131
|
-
const packageName = [packageMeta.id, lastIndex + 1].join(PACKAGE_ID_SEPARATOR);
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
...agg,
|
|
135
|
-
[packageName]: {
|
|
136
|
-
name: packageMeta.name,
|
|
137
|
-
path: packageMeta.path,
|
|
138
|
-
value,
|
|
139
|
-
},
|
|
140
|
-
};
|
|
141
|
-
}, {} as Record<string, Package>);
|
|
152
|
+
},
|
|
153
|
+
{} as Record<string, Package>,
|
|
154
|
+
);
|
|
142
155
|
|
|
143
156
|
return { metrics: { packages } };
|
|
144
157
|
};
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import round from 'lodash/round';
|
|
2
|
-
import
|
|
2
|
+
import union from 'lodash/union';
|
|
3
|
+
import {
|
|
4
|
+
WebpackStatsFiltered,
|
|
5
|
+
WebpackStatsFilteredModule,
|
|
6
|
+
} from '@bundle-stats/plugin-webpack-filter';
|
|
3
7
|
|
|
4
|
-
import {
|
|
8
|
+
import type { MetricsModules, Modules } from '../types';
|
|
5
9
|
import { getModuleName, normalizeChunkId } from '../utils';
|
|
6
10
|
|
|
7
11
|
/*
|
|
8
|
-
* Extract webpack modules
|
|
12
|
+
* Extract webpack modules metrics and stats
|
|
9
13
|
*/
|
|
10
14
|
export const extractModules = (webpackStats?: WebpackStatsFiltered): MetricsModules => {
|
|
11
15
|
const modulesSource = webpackStats?.modules;
|
|
@@ -22,58 +26,87 @@ export const extractModules = (webpackStats?: WebpackStatsFiltered): MetricsModu
|
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
// Flatten concatenated modules
|
|
25
|
-
const
|
|
26
|
-
if (!moduleEntry.modules) {
|
|
27
|
-
agg.push(moduleEntry);
|
|
29
|
+
const modulesByName: Map<string, WebpackStatsFilteredModule> = new Map();
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
modulesSource.forEach((rootModule) => {
|
|
32
|
+
// skip modules that do not belong to any chunk
|
|
33
|
+
if (!rootModule.chunks || rootModule.chunks.length === 0) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// flatten concatenated modules
|
|
38
|
+
if (rootModule.modules) {
|
|
39
|
+
rootModule.modules.forEach((concatenatedModule) => {
|
|
40
|
+
const concatenatedModuleName = getModuleName(concatenatedModule.name);
|
|
41
|
+
const existingModule = modulesByName.get(concatenatedModuleName);
|
|
42
|
+
|
|
43
|
+
// Merge if the module is already available
|
|
44
|
+
// webpack adds a module entry for every concatenated module
|
|
45
|
+
if (existingModule) {
|
|
46
|
+
modulesByName.set(concatenatedModuleName, {
|
|
47
|
+
...existingModule,
|
|
48
|
+
chunks: union(existingModule.chunks, rootModule.chunks),
|
|
49
|
+
});
|
|
50
|
+
} else {
|
|
51
|
+
modulesByName.set(concatenatedModuleName, {
|
|
52
|
+
...concatenatedModule,
|
|
53
|
+
chunks: rootModule.chunks,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const normalizedName = getModuleName(rootModule.name);
|
|
62
|
+
const savedRootModule = modulesByName.get(normalizedName);
|
|
63
|
+
|
|
64
|
+
// Merge module data
|
|
65
|
+
// @NOTE some webpack plugins (mini-css-extract-plugin) might add a root module entry
|
|
66
|
+
// even if thee module was already concantenated in another module
|
|
67
|
+
if (savedRootModule) {
|
|
68
|
+
modulesByName.set(normalizedName, {
|
|
69
|
+
...savedRootModule,
|
|
70
|
+
chunks: union(savedRootModule.chunks, rootModule.chunks),
|
|
71
|
+
size: rootModule.size,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return;
|
|
30
75
|
}
|
|
31
76
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
moduleEntry.modules.map((concatenatedModule) => ({
|
|
35
|
-
...concatenatedModule,
|
|
36
|
-
// Add parent chunks
|
|
37
|
-
chunks: moduleEntry.chunks,
|
|
38
|
-
})),
|
|
39
|
-
);
|
|
77
|
+
modulesByName.set(normalizedName, rootModule);
|
|
78
|
+
});
|
|
40
79
|
|
|
41
|
-
|
|
42
|
-
|
|
80
|
+
// Normalize module entries and extract metrics
|
|
81
|
+
const modules: Modules = {};
|
|
43
82
|
|
|
44
83
|
let moduleCount = 0;
|
|
45
84
|
let totalCodeSize = 0;
|
|
46
85
|
let duplicateCodeSize = 0;
|
|
47
86
|
let duplicateModulesCount = 0;
|
|
48
87
|
|
|
49
|
-
|
|
50
|
-
const modules = allModules.reduce((agg, moduleEntry) => {
|
|
88
|
+
modulesByName.forEach((moduleEntry, normalizedName) => {
|
|
51
89
|
const { name, size = 0, chunks } = moduleEntry;
|
|
52
|
-
const normalizedName = getModuleName(name);
|
|
53
|
-
|
|
54
|
-
// skip modules that do not belong to any chunk
|
|
55
|
-
if (!chunks || chunks?.length === 0) {
|
|
56
|
-
return agg;
|
|
57
|
-
}
|
|
58
90
|
|
|
59
91
|
const instances = chunks.length;
|
|
60
92
|
const duplicateInstances = instances - 1;
|
|
93
|
+
const duplicated = duplicateInstances > 0;
|
|
61
94
|
|
|
62
95
|
moduleCount += instances;
|
|
63
|
-
duplicateModulesCount += duplicateInstances;
|
|
64
|
-
duplicateCodeSize += duplicateInstances * size;
|
|
65
96
|
totalCodeSize += instances * size;
|
|
66
97
|
|
|
67
|
-
|
|
68
|
-
|
|
98
|
+
if (duplicated) {
|
|
99
|
+
duplicateModulesCount += duplicateInstances;
|
|
100
|
+
duplicateCodeSize += duplicateInstances * size;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
modules[normalizedName] = {
|
|
69
104
|
name,
|
|
70
105
|
value: size,
|
|
71
106
|
chunkIds: chunks.map(normalizeChunkId),
|
|
72
|
-
duplicated
|
|
107
|
+
...(duplicated && { duplicated }),
|
|
73
108
|
};
|
|
74
|
-
|
|
75
|
-
return agg;
|
|
76
|
-
}, {} as Record<string, Module>);
|
|
109
|
+
}, {} as Modules);
|
|
77
110
|
|
|
78
111
|
const duplicateCode = totalCodeSize ? round((duplicateCodeSize / totalCodeSize) * 100, 2) : 0;
|
|
79
112
|
|