@bundle-stats/utils 4.2.0-beta.0 → 4.2.0-beta.2
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/config/index.js +1 -9
- package/lib/config/index.js.map +1 -1
- package/lib/config/insights.js +7 -5
- package/lib/config/insights.js.map +1 -1
- package/lib/constants.js +27 -13
- package/lib/constants.js.map +1 -1
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/jobs/create-job.js +23 -12
- package/lib/jobs/create-job.js.map +1 -1
- package/lib/jobs/create-jobs.js +13 -17
- package/lib/jobs/create-jobs.js.map +1 -1
- package/lib/jobs/create-summary.js +10 -11
- package/lib/jobs/create-summary.js.map +1 -1
- package/lib/jobs/index.js.map +1 -1
- package/lib/lighthouse/filter.js.map +1 -1
- package/lib/report/create.js +11 -16
- package/lib/report/create.js.map +1 -1
- package/lib/utils/component-links.js +3 -3
- package/lib/utils/metrics.js.map +1 -1
- package/lib/webpack/constants.js +12 -11
- package/lib/webpack/constants.js.map +1 -1
- package/lib/webpack/extract/assets-size-total-insight.js +5 -5
- package/lib/webpack/extract/assets-size-total-insight.js.map +1 -1
- package/lib/webpack/extract/assets-size.js +3 -4
- package/lib/webpack/extract/assets-size.js.map +1 -1
- package/lib/webpack/extract/modules-packages-duplicate.js +117 -41
- package/lib/webpack/extract/modules-packages-duplicate.js.map +1 -1
- package/lib/webpack/extract/modules-packages.js.map +1 -1
- package/lib/webpack/extract/modules.js +0 -2
- package/lib/webpack/extract/modules.js.map +1 -1
- package/lib/webpack/metrics.js +27 -25
- package/lib/webpack/metrics.js.map +1 -1
- package/lib/webpack/types.js +24 -0
- package/lib/webpack/types.js.map +1 -1
- package/lib-esm/config/index.js +0 -8
- package/lib-esm/config/index.js.map +1 -1
- package/lib-esm/config/insights.js +6 -4
- package/lib-esm/config/insights.js.map +1 -1
- package/lib-esm/constants.js +26 -12
- package/lib-esm/constants.js.map +1 -1
- package/lib-esm/index.js +1 -0
- package/lib-esm/index.js.map +1 -1
- package/lib-esm/jobs/create-job.js +24 -12
- package/lib-esm/jobs/create-job.js.map +1 -1
- package/lib-esm/jobs/create-jobs.js +13 -14
- package/lib-esm/jobs/create-jobs.js.map +1 -1
- package/lib-esm/jobs/create-summary.js +10 -11
- package/lib-esm/jobs/create-summary.js.map +1 -1
- package/lib-esm/jobs/index.js.map +1 -1
- package/lib-esm/lighthouse/filter.js.map +1 -1
- package/lib-esm/report/create.js +11 -16
- package/lib-esm/report/create.js.map +1 -1
- package/lib-esm/utils/component-links.js +2 -2
- package/lib-esm/utils/metrics.js.map +1 -1
- package/lib-esm/webpack/constants.js +12 -11
- package/lib-esm/webpack/constants.js.map +1 -1
- package/lib-esm/webpack/extract/assets-size-total-insight.js +6 -6
- package/lib-esm/webpack/extract/assets-size-total-insight.js.map +1 -1
- package/lib-esm/webpack/extract/assets-size.js +3 -4
- package/lib-esm/webpack/extract/assets-size.js.map +1 -1
- package/lib-esm/webpack/extract/modules-packages-duplicate.js +119 -41
- package/lib-esm/webpack/extract/modules-packages-duplicate.js.map +1 -1
- package/lib-esm/webpack/extract/modules-packages.js.map +1 -1
- package/lib-esm/webpack/extract/modules.js +0 -3
- package/lib-esm/webpack/extract/modules.js.map +1 -1
- package/lib-esm/webpack/metrics.js +27 -25
- package/lib-esm/webpack/metrics.js.map +1 -1
- package/lib-esm/webpack/types.js +23 -1
- package/lib-esm/webpack/types.js.map +1 -1
- package/package.json +13 -12
- package/src/config/index.js +0 -9
- package/src/config/insights.ts +5 -0
- package/src/constants.ts +82 -15
- package/src/index.js +1 -0
- package/src/jobs/create-job.ts +61 -0
- package/src/jobs/create-jobs.ts +28 -0
- package/src/jobs/create-summary.ts +22 -0
- package/src/jobs/{index.js → index.ts} +0 -0
- package/src/lighthouse/filter.js +8 -5
- package/src/report/create.ts +31 -24
- package/src/utils/component-links.ts +2 -2
- package/src/utils/metrics.js +8 -7
- package/src/webpack/constants.js +13 -11
- package/src/webpack/extract/assets-size-total-insight.js +6 -7
- package/src/webpack/extract/assets-size.js +29 -24
- package/src/webpack/extract/modules-packages-duplicate.ts +241 -0
- package/src/webpack/extract/modules-packages.ts +1 -1
- package/src/webpack/extract/modules.ts +1 -4
- package/src/webpack/metrics.js +27 -25
- package/src/webpack/types.ts +31 -7
- package/types/config/index.d.ts +0 -4
- package/types/config/insights.d.ts +5 -4
- package/types/constants.d.ts +68 -15
- package/types/index.d.ts +1 -0
- package/types/jobs/create-job.d.ts +2 -1
- package/types/jobs/create-jobs.d.ts +2 -1
- package/types/jobs/create-summary.d.ts +4 -1
- package/types/jobs/index.d.ts +2 -2
- package/types/lighthouse/filter.d.ts +1 -1
- package/types/report/add-row-metric-data.d.ts +10 -1
- package/types/report/create.d.ts +14 -12
- package/types/utils/component-links.d.ts +1 -1
- package/types/utils/metrics.d.ts +1 -1
- package/types/webpack/constants.d.ts +2 -1
- package/types/webpack/extract/assets-size-total-insight.d.ts +3 -2
- package/types/webpack/extract/modules-packages-duplicate.d.ts +9 -16
- package/types/webpack/extract/modules-packages.d.ts +1 -1
- package/types/webpack/types.d.ts +29 -8
- package/src/config/insights.js +0 -5
- package/src/jobs/create-job.js +0 -46
- package/src/jobs/create-jobs.js +0 -26
- package/src/jobs/create-summary.js +0 -16
- package/src/webpack/extract/modules-packages-duplicate.js +0 -102
package/src/utils/metrics.js
CHANGED
|
@@ -14,17 +14,18 @@ import { metrics as browsertimeMeticTypes } from '../browsertime/metrics';
|
|
|
14
14
|
* @param {Object} metrics
|
|
15
15
|
* @return {Function}
|
|
16
16
|
*/
|
|
17
|
-
export const createGetMetricType =
|
|
17
|
+
export const createGetMetricType =
|
|
18
|
+
(metrics) =>
|
|
18
19
|
/**
|
|
19
20
|
* Get metric type data
|
|
20
21
|
*
|
|
21
22
|
* @param {String} key Metric key
|
|
22
23
|
* @param {String} [type] Default metric type
|
|
23
|
-
* @return {import('
|
|
24
|
+
* @return {import('../constants').MetricType}
|
|
24
25
|
*/
|
|
25
26
|
// eslint-disable-next-line implicit-arrow-linebreak
|
|
26
27
|
(key, type) => {
|
|
27
|
-
/** @type {import('
|
|
28
|
+
/** @type {import('../constants').MetricTypeConfig} */
|
|
28
29
|
const metric = get(metrics, key);
|
|
29
30
|
|
|
30
31
|
if (metric && metric.type) {
|
|
@@ -47,8 +48,8 @@ export const createGetMetricType = (metrics) =>
|
|
|
47
48
|
* Get global metric type
|
|
48
49
|
*
|
|
49
50
|
* @param {String} key Metric key
|
|
50
|
-
* @param {import('
|
|
51
|
-
* @return {import('
|
|
51
|
+
* @param {import('../constants').MetricTypeType} [type] Default metric type
|
|
52
|
+
* @return {import('../constants').MetricType}
|
|
52
53
|
*/
|
|
53
54
|
export const getGlobalMetricType = createGetMetricType({
|
|
54
55
|
webpack: webpackMetricTypes,
|
|
@@ -60,11 +61,11 @@ export const getGlobalMetricType = createGetMetricType({
|
|
|
60
61
|
*
|
|
61
62
|
* Get metric information
|
|
62
63
|
*
|
|
63
|
-
* @param {import('
|
|
64
|
+
* @param {import('../constants').MetricType} metric Metric data
|
|
64
65
|
* @param {number} currentValue Current value
|
|
65
66
|
* @param {number} baselineValue Baseline value
|
|
66
67
|
*
|
|
67
|
-
* @return {import('
|
|
68
|
+
* @return {import('../constants').MetricRunInfo}
|
|
68
69
|
*/
|
|
69
70
|
export const getMetricRunInfo = (metric, currentValue, baselineValue) => {
|
|
70
71
|
const { formatter, biggerIsBetter } = metric;
|
package/src/webpack/constants.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Metric } from './types';
|
|
2
|
+
|
|
1
3
|
export const SECTION_WEBPACK_STATS = 'stats';
|
|
2
4
|
export const SECTION_WEBPACK_SIZES = 'sizes';
|
|
3
5
|
export const SECTION_WEBPACK_ASSETS = 'assets';
|
|
@@ -13,17 +15,17 @@ export const SECTIONS = [
|
|
|
13
15
|
];
|
|
14
16
|
|
|
15
17
|
export const SUMMARY_METRIC_PATHS = [
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
18
|
+
Metric.BUNDLE_SIZE,
|
|
19
|
+
Metric.INITIAL_SIZE_JS,
|
|
20
|
+
Metric.INITIAL_SIZE_CSS,
|
|
21
|
+
Metric.CACHE_INVALIDATION,
|
|
22
|
+
Metric.MODULE_COUNT,
|
|
23
|
+
Metric.DUPLICATE_MODULES_COUNT,
|
|
24
|
+
Metric.DUPLICATE_CODE,
|
|
25
|
+
Metric.CHUNK_COUNT,
|
|
26
|
+
Metric.ASSET_COUNT,
|
|
27
|
+
Metric.PACKAGE_COUNT,
|
|
28
|
+
Metric.DUPLICATE_PACKAGES_COUNT,
|
|
27
29
|
];
|
|
28
30
|
|
|
29
31
|
const PACKAGE_PREFIX = /(?:node_modules|~)(?:\/\.pnpm)?/;
|
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { InsightType } from '../../config/insights';
|
|
2
2
|
import { getMetricRunInfo } from '../../utils/metrics';
|
|
3
3
|
import { getMetricType } from '../utils';
|
|
4
|
-
|
|
5
|
-
const METRIC_NAME = 'totalSizeByTypeALL';
|
|
4
|
+
import { Metric } from '../types';
|
|
6
5
|
|
|
7
6
|
const getText = ({ metric, displayValue, displayDeltaPercentage }) =>
|
|
8
7
|
`${metric} — ${displayValue} (${displayDeltaPercentage}).`;
|
|
9
8
|
|
|
10
9
|
export const extractAssetsSizeTotalInsight = (_, currentExtractedData, baselineBundleStats) => {
|
|
11
|
-
const currentValue = currentExtractedData?.metrics?.[
|
|
12
|
-
const baselineValue = baselineBundleStats?.metrics?.webpack?.[
|
|
10
|
+
const currentValue = currentExtractedData?.metrics?.[Metric.BUNDLE_SIZE]?.value || 0;
|
|
11
|
+
const baselineValue = baselineBundleStats?.metrics?.webpack?.[Metric.BUNDLE_SIZE]?.value || 0;
|
|
13
12
|
|
|
14
|
-
const metric = getMetricType(
|
|
13
|
+
const metric = getMetricType(Metric.BUNDLE_SIZE);
|
|
15
14
|
const info = getMetricRunInfo(metric, currentValue, baselineValue);
|
|
16
15
|
const { displayDeltaPercentage, displayValue } = info;
|
|
17
16
|
|
|
18
17
|
return {
|
|
19
18
|
insights: {
|
|
20
19
|
assetsSizeTotal: {
|
|
21
|
-
type:
|
|
20
|
+
type: InsightType.INFO,
|
|
22
21
|
data: {
|
|
23
22
|
text: getText({ metric: metric.label, displayValue, displayDeltaPercentage }),
|
|
24
23
|
md: getText({
|
|
@@ -4,34 +4,39 @@ import sum from 'lodash/sum';
|
|
|
4
4
|
|
|
5
5
|
import { FILE_TYPE_CSS, FILE_TYPE_JS, FILE_TYPES } from '../../config/file-types';
|
|
6
6
|
import { getFileType } from '../../utils/file-types';
|
|
7
|
+
import { METRIC_TOTALS_PREFIX, METRIC_TOTALS_SUFIX_ALL } from '../types';
|
|
7
8
|
|
|
8
|
-
const
|
|
9
|
-
const METRIC_NAME_PREFIX = 'totalSizeByType';
|
|
9
|
+
const getMetricName = (fileType) => `${METRIC_TOTALS_PREFIX}${fileType}`;
|
|
10
10
|
|
|
11
|
-
const
|
|
11
|
+
const generateInitialSizeByType = () =>
|
|
12
|
+
FILE_TYPES.reduce(
|
|
13
|
+
(accumulator, fileType) => ({
|
|
14
|
+
...accumulator,
|
|
15
|
+
[getMetricName(fileType)]: {
|
|
16
|
+
value: 0,
|
|
17
|
+
},
|
|
18
|
+
}),
|
|
19
|
+
{},
|
|
20
|
+
);
|
|
12
21
|
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}), {});
|
|
22
|
+
const calculateTotalByType = (assets) =>
|
|
23
|
+
assets.reduce((accumulator, current) => {
|
|
24
|
+
const fileType = getFileType(current.name);
|
|
25
|
+
const statName = getMetricName(fileType);
|
|
26
|
+
const value = accumulator[statName].value + current.value;
|
|
19
27
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
return {
|
|
29
|
+
...accumulator,
|
|
30
|
+
[statName]: {
|
|
31
|
+
value,
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}, generateInitialSizeByType());
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}, generateInitialSizeByType());
|
|
32
|
-
|
|
33
|
-
const getFilterInitialAssetsByType = (fileType) => ({ name, isInitial }) =>
|
|
34
|
-
getFileType(name) === fileType && isInitial; // eslint-disable-line implicit-arrow-linebreak
|
|
36
|
+
const getFilterInitialAssetsByType =
|
|
37
|
+
(fileType) =>
|
|
38
|
+
({ name, isInitial }) =>
|
|
39
|
+
getFileType(name) === fileType && isInitial; // eslint-disable-line implicit-arrow-linebreak
|
|
35
40
|
|
|
36
41
|
const calculateInitialTotals = (assets) => {
|
|
37
42
|
const cssAssets = assets.filter(getFilterInitialAssetsByType(FILE_TYPE_CSS));
|
|
@@ -52,7 +57,7 @@ export const extractAssetsSize = (webpackStats, currentExtractedData) => {
|
|
|
52
57
|
|
|
53
58
|
const sizes = calculateTotalByType(bundleAssets);
|
|
54
59
|
const generic = {
|
|
55
|
-
[getMetricName(
|
|
60
|
+
[getMetricName(METRIC_TOTALS_SUFIX_ALL)]: {
|
|
56
61
|
value: sum(map(bundleAssets, 'value')),
|
|
57
62
|
},
|
|
58
63
|
...calculateInitialTotals(bundleAssets),
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import map from 'lodash/map';
|
|
2
|
+
import orderBy from 'lodash/orderBy';
|
|
3
|
+
import sum from 'lodash/sum';
|
|
4
|
+
|
|
5
|
+
import { InsightType } from '../../config';
|
|
6
|
+
import {
|
|
7
|
+
Job,
|
|
8
|
+
JobInsight,
|
|
9
|
+
JobInsightDuplicatePackagesData,
|
|
10
|
+
JobInsightDuplicatePackagesV3Data,
|
|
11
|
+
JobInsights,
|
|
12
|
+
MetricRun,
|
|
13
|
+
} from '../../constants';
|
|
14
|
+
import { Packages } from '../types';
|
|
15
|
+
|
|
16
|
+
interface DuplicatePackage {
|
|
17
|
+
id: string;
|
|
18
|
+
value: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface DuplicatePackageGroup {
|
|
22
|
+
name: string;
|
|
23
|
+
value: number;
|
|
24
|
+
children: Array<DuplicatePackage>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface ModulesPackagesDuplicateData {
|
|
28
|
+
insights: {
|
|
29
|
+
duplicatePackages?: JobInsights['webpack']['duplicatePackages'];
|
|
30
|
+
duplicatePackagesV3: JobInsights['webpack']['duplicatePackagesV3'];
|
|
31
|
+
};
|
|
32
|
+
metrics: {
|
|
33
|
+
duplicatePackagesCount: MetricRun;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export const getDuplicatePackagesInsight = (
|
|
38
|
+
duplicatePackagesMap: Record<string, Array<string>>,
|
|
39
|
+
baselineDuplicatePackagesMap?: Record<string, Array<string>>,
|
|
40
|
+
): JobInsight<JobInsightDuplicatePackagesV3Data> => {
|
|
41
|
+
const duplicateInstances: Array<string> = [];
|
|
42
|
+
const baselineDuplicateInstances: Array<string> = [];
|
|
43
|
+
const newDuplicateInstances: Array<string> = [];
|
|
44
|
+
const removedDuplicateInstances: Array<string> = [];
|
|
45
|
+
|
|
46
|
+
Object.entries(duplicatePackagesMap).forEach(([id, instances]) => {
|
|
47
|
+
instances.forEach((duplicateInstance) => {
|
|
48
|
+
if (duplicateInstance === id) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
duplicateInstances.push(duplicateInstance);
|
|
53
|
+
|
|
54
|
+
if (baselineDuplicatePackagesMap?.[id]?.includes(duplicateInstance)) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
newDuplicateInstances.push(duplicateInstance);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (baselineDuplicatePackagesMap) {
|
|
63
|
+
Object.entries(baselineDuplicatePackagesMap).forEach(([id, instances]) => {
|
|
64
|
+
instances.forEach((duplicateInstance) => {
|
|
65
|
+
if (duplicateInstance === id) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
baselineDuplicateInstances.push(duplicateInstance);
|
|
70
|
+
|
|
71
|
+
if (duplicatePackagesMap?.[id]?.includes(duplicateInstance)) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
removedDuplicateInstances.push(duplicateInstance);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const duplicateInstancesCount = duplicateInstances.length;
|
|
81
|
+
const newDuplicateInstancesCount = newDuplicateInstances.length;
|
|
82
|
+
const removedDuplicateInstancesCount = removedDuplicateInstances.length;
|
|
83
|
+
|
|
84
|
+
let text: string;
|
|
85
|
+
let insightType: InsightType;
|
|
86
|
+
|
|
87
|
+
if (newDuplicateInstancesCount > 0 && removedDuplicateInstancesCount > 0) {
|
|
88
|
+
// New duplicate packages and removed duplicates
|
|
89
|
+
const item =
|
|
90
|
+
newDuplicateInstancesCount > 1 || removedDuplicateInstancesCount > 1 ? 'packages' : 'package';
|
|
91
|
+
text = `Bundle introduced ${newDuplicateInstancesCount} and removed ${removedDuplicateInstancesCount} duplicate ${item}`;
|
|
92
|
+
insightType = InsightType.ERROR;
|
|
93
|
+
} else if (newDuplicateInstancesCount > 0 && baselineDuplicatePackagesMap) {
|
|
94
|
+
// New duplicate packages and baseline comparison
|
|
95
|
+
const item = newDuplicateInstancesCount > 1 ? 'packages' : 'package';
|
|
96
|
+
text = `Bundle introduced ${newDuplicateInstancesCount} duplicate ${item}`;
|
|
97
|
+
insightType = InsightType.ERROR;
|
|
98
|
+
} else if (newDuplicateInstancesCount > 0) {
|
|
99
|
+
// New duplicate packages and no baseline
|
|
100
|
+
const item = newDuplicateInstancesCount > 1 ? 'packages' : 'package';
|
|
101
|
+
text = `Bundle contains ${newDuplicateInstancesCount} duplicate ${item}`;
|
|
102
|
+
insightType = InsightType.WARNING;
|
|
103
|
+
} else if (removedDuplicateInstancesCount > 0) {
|
|
104
|
+
// Removed duplicate packages
|
|
105
|
+
const item = removedDuplicateInstancesCount > 1 ? 'packages' : 'package';
|
|
106
|
+
text = `Bundle removed ${removedDuplicateInstancesCount} duplicate ${item}`;
|
|
107
|
+
insightType = InsightType.INFO;
|
|
108
|
+
} else if (
|
|
109
|
+
newDuplicateInstancesCount === 0 &&
|
|
110
|
+
removedDuplicateInstancesCount === 0 &&
|
|
111
|
+
duplicateInstancesCount > 0
|
|
112
|
+
) {
|
|
113
|
+
// No change comparred with the baseline
|
|
114
|
+
const item = duplicateInstancesCount > 1 ? 'packages' : 'package';
|
|
115
|
+
text = `Bundle contains ${duplicateInstancesCount} duplicate ${item}`;
|
|
116
|
+
insightType = InsightType.WARNING;
|
|
117
|
+
} else {
|
|
118
|
+
// No change
|
|
119
|
+
text = 'Bundle does not contain duplicate packages';
|
|
120
|
+
insightType = InsightType.INFO;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
type: insightType,
|
|
125
|
+
data: {
|
|
126
|
+
text,
|
|
127
|
+
packages: duplicatePackagesMap,
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export const extractModulesPackagesDuplicate = (
|
|
133
|
+
_: any,
|
|
134
|
+
currentExtractedData: any,
|
|
135
|
+
baselineJob?: Job,
|
|
136
|
+
): ModulesPackagesDuplicateData => {
|
|
137
|
+
const source: Packages = currentExtractedData?.metrics?.packages || {};
|
|
138
|
+
|
|
139
|
+
// Group packages by name(uniq)
|
|
140
|
+
const packagesByName: Record<string, DuplicatePackageGroup> = {};
|
|
141
|
+
|
|
142
|
+
Object.entries(source).forEach(([packageId, packageData]) => {
|
|
143
|
+
const { name, value } = packageData;
|
|
144
|
+
const existingPackageData: DuplicatePackageGroup = packagesByName[name] || {
|
|
145
|
+
name,
|
|
146
|
+
value,
|
|
147
|
+
children: [],
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
existingPackageData.children.push({ id: packageId, value });
|
|
151
|
+
|
|
152
|
+
packagesByName[name] = existingPackageData;
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// Filter, count and sum the duplicate packages
|
|
156
|
+
let count = 0;
|
|
157
|
+
const duplicatePackages: Array<DuplicatePackageGroup> = [];
|
|
158
|
+
|
|
159
|
+
Object.entries(packagesByName).forEach(([packageGroupName, packageGroupData]) => {
|
|
160
|
+
// Count duplicate instances (substract the initial instance)
|
|
161
|
+
// example: package-a, package-b:package-a -> duplicateInstances = 1
|
|
162
|
+
const duplicateInstances = packageGroupData.children.length - 1;
|
|
163
|
+
|
|
164
|
+
// No duplicates, skip
|
|
165
|
+
if (duplicateInstances === 0) {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
count += duplicateInstances;
|
|
170
|
+
|
|
171
|
+
duplicatePackages.push({
|
|
172
|
+
name: packageGroupName,
|
|
173
|
+
value: sum(map(packageGroupData.children, 'value')),
|
|
174
|
+
children: packageGroupData.children,
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
const baselineDuplicatePackages =
|
|
179
|
+
baselineJob?.insights?.webpack?.duplicatePackagesV3?.data?.packages ||
|
|
180
|
+
baselineJob?.insights?.webpack?.duplicatePackages?.data ||
|
|
181
|
+
{};
|
|
182
|
+
|
|
183
|
+
if (!count) {
|
|
184
|
+
return {
|
|
185
|
+
insights: {
|
|
186
|
+
duplicatePackagesV3: getDuplicatePackagesInsight({}, baselineDuplicatePackages),
|
|
187
|
+
},
|
|
188
|
+
metrics: {
|
|
189
|
+
duplicatePackagesCount: {
|
|
190
|
+
value: count,
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Group duplicate packages by name and order children by value
|
|
197
|
+
const duplicatePackagesByName: Record<string, DuplicatePackageGroup> = orderBy(
|
|
198
|
+
duplicatePackages,
|
|
199
|
+
'value',
|
|
200
|
+
'desc',
|
|
201
|
+
).reduce(
|
|
202
|
+
(agg, { name, ...duplicatePackageData }: DuplicatePackageGroup) => ({
|
|
203
|
+
...agg,
|
|
204
|
+
[name]: {
|
|
205
|
+
...duplicatePackageData,
|
|
206
|
+
children: orderBy(duplicatePackageData.children, 'value', 'desc'),
|
|
207
|
+
},
|
|
208
|
+
}),
|
|
209
|
+
{},
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// Generate v2 data structure
|
|
213
|
+
// @TODO remove in v5.0
|
|
214
|
+
const duplicatePackagesData: JobInsightDuplicatePackagesData = {};
|
|
215
|
+
|
|
216
|
+
Object.entries(duplicatePackagesByName).forEach(([packageName, packageData]) => {
|
|
217
|
+
if (!duplicatePackagesData[packageName]) {
|
|
218
|
+
duplicatePackagesData[packageName] = [];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
duplicatePackagesData[packageName] = packageData.children.map(({ id }) => id);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
insights: {
|
|
226
|
+
duplicatePackages: {
|
|
227
|
+
type: InsightType.WARNING,
|
|
228
|
+
data: duplicatePackagesData,
|
|
229
|
+
},
|
|
230
|
+
duplicatePackagesV3: getDuplicatePackagesInsight(
|
|
231
|
+
duplicatePackagesData,
|
|
232
|
+
baselineDuplicatePackages,
|
|
233
|
+
),
|
|
234
|
+
},
|
|
235
|
+
metrics: {
|
|
236
|
+
duplicatePackagesCount: {
|
|
237
|
+
value: count,
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
};
|
|
@@ -51,7 +51,7 @@ export const getPackageMetaFromModulePath = (modulePath: string) => {
|
|
|
51
51
|
return null;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
const name = last(names);
|
|
54
|
+
const name = last(names) as string;
|
|
55
55
|
|
|
56
56
|
// Get package full path
|
|
57
57
|
// @NOTE use the last path to prevent getting incorrect results on packages with similar names
|
|
@@ -51,7 +51,7 @@ export const extractModules = (webpackStats?: WebpackStatsFiltered): MetricsModu
|
|
|
51
51
|
const { name, size = 0, chunks } = moduleEntry;
|
|
52
52
|
const normalizedName = getModuleName(name);
|
|
53
53
|
|
|
54
|
-
// skip modules that
|
|
54
|
+
// skip modules that do not belong to any chunk
|
|
55
55
|
if (!chunks || chunks?.length === 0) {
|
|
56
56
|
return agg;
|
|
57
57
|
}
|
|
@@ -64,15 +64,12 @@ export const extractModules = (webpackStats?: WebpackStatsFiltered): MetricsModu
|
|
|
64
64
|
duplicateCodeSize += duplicateInstances * size;
|
|
65
65
|
totalCodeSize += instances * size;
|
|
66
66
|
|
|
67
|
-
const reasons = moduleEntry.reasons?.map((reason) => getModuleName(reason.module));
|
|
68
|
-
|
|
69
67
|
// eslint-disable-next-line no-param-reassign
|
|
70
68
|
agg[normalizedName] = {
|
|
71
69
|
name,
|
|
72
70
|
value: size,
|
|
73
71
|
chunkIds: chunks.map(normalizeChunkId),
|
|
74
72
|
duplicated: Boolean(duplicateInstances),
|
|
75
|
-
...(reasons && { reasons }),
|
|
76
73
|
};
|
|
77
74
|
|
|
78
75
|
return agg;
|
package/src/webpack/metrics.js
CHANGED
|
@@ -3,76 +3,78 @@ import {
|
|
|
3
3
|
METRIC_TYPE_NUMERIC,
|
|
4
4
|
METRIC_TYPE_PERCENTAGE,
|
|
5
5
|
} from '../config/metrics';
|
|
6
|
+
import { FILE_TYPE_LABELS } from '../config/file-types';
|
|
7
|
+
import { Metric } from './types';
|
|
6
8
|
|
|
7
9
|
export const metrics = {
|
|
8
|
-
|
|
10
|
+
[Metric.BUNDLE_SIZE]: {
|
|
9
11
|
label: 'Bundle Size',
|
|
10
12
|
description: 'The total size of the assets produced by webpack',
|
|
11
13
|
url: 'https://relative-ci.com/documentation/metrics-and-data#assets',
|
|
12
14
|
type: METRIC_TYPE_FILE_SIZE,
|
|
13
15
|
},
|
|
14
|
-
|
|
16
|
+
[Metric.INITIAL_SIZE_JS]: {
|
|
15
17
|
label: 'Initial JS',
|
|
16
18
|
description: 'The total size of JavaScript initial chunks',
|
|
17
19
|
url: 'https://relative-ci.com/documentation/metrics-and-data#assets',
|
|
18
20
|
type: METRIC_TYPE_FILE_SIZE,
|
|
19
21
|
},
|
|
20
|
-
|
|
22
|
+
[Metric.INITIAL_SIZE_CSS]: {
|
|
21
23
|
label: 'Initial CSS',
|
|
22
24
|
description: 'The total size of CSS initial chunks',
|
|
23
25
|
url: 'https://relative-ci.com/documentation/metrics-and-data#assets',
|
|
24
26
|
type: METRIC_TYPE_FILE_SIZE,
|
|
25
27
|
},
|
|
26
|
-
|
|
28
|
+
[Metric.CACHE_INVALIDATION]: {
|
|
27
29
|
label: 'Cache Invalidation',
|
|
28
30
|
description: 'The ratio between the total changed assets and total assets size(Bundle Size)',
|
|
29
31
|
url: 'https://relative-ci.com/documentation/metrics-and-data#assets',
|
|
30
32
|
type: METRIC_TYPE_PERCENTAGE,
|
|
31
33
|
biggerIsBetter: false,
|
|
32
34
|
},
|
|
33
|
-
|
|
35
|
+
[Metric.CHUNK_COUNT]: {
|
|
34
36
|
label: 'Chunks',
|
|
35
37
|
description: 'The total number of chunks',
|
|
36
38
|
url: 'https://relative-ci.com/documentation/metrics-and-data#assets',
|
|
37
39
|
type: METRIC_TYPE_NUMERIC,
|
|
38
40
|
biggerIsBetter: false,
|
|
39
41
|
},
|
|
40
|
-
|
|
42
|
+
[Metric.ASSET_COUNT]: {
|
|
41
43
|
label: 'Assets',
|
|
42
44
|
description: 'Total number of assets',
|
|
43
45
|
url: 'https://relative-ci.com/documentation/metrics-and-data#assets',
|
|
44
46
|
type: METRIC_TYPE_NUMERIC,
|
|
45
47
|
biggerIsBetter: false,
|
|
46
48
|
},
|
|
47
|
-
|
|
49
|
+
[Metric.MODULE_COUNT]: {
|
|
48
50
|
label: 'Modules',
|
|
49
51
|
description: 'The total number of modules bundled by webpack',
|
|
50
52
|
url: 'https://relative-ci.com/documentation/metrics-and-data#modules-1',
|
|
51
53
|
type: METRIC_TYPE_NUMERIC,
|
|
52
54
|
biggerIsBetter: false,
|
|
53
55
|
},
|
|
54
|
-
|
|
56
|
+
[Metric.DUPLICATE_MODULES_COUNT]: {
|
|
55
57
|
label: 'Duplicate Modules',
|
|
56
58
|
description: 'The total number of modules that belong to more than one chunk',
|
|
57
59
|
url: 'https://relative-ci.com/documentation/metrics-and-data#modules-1',
|
|
58
60
|
type: METRIC_TYPE_NUMERIC,
|
|
59
61
|
biggerIsBetter: false,
|
|
60
62
|
},
|
|
61
|
-
|
|
63
|
+
[Metric.DUPLICATE_CODE]: {
|
|
62
64
|
label: 'Duplicate Code',
|
|
63
65
|
description: 'The percentage of total duplicate module size from the total module size',
|
|
64
66
|
url: 'https://relative-ci.com/documentation/metrics-and-data#modules-1',
|
|
65
67
|
type: METRIC_TYPE_PERCENTAGE,
|
|
66
68
|
biggerIsBetter: false,
|
|
67
69
|
},
|
|
68
|
-
|
|
70
|
+
[Metric.PACKAGE_COUNT]: {
|
|
69
71
|
label: 'Packages',
|
|
70
72
|
description: 'The total number of packages bundled by webpack',
|
|
71
73
|
url: 'https://relative-ci.com/documentation/metrics-and-data#packages',
|
|
72
74
|
type: METRIC_TYPE_NUMERIC,
|
|
73
75
|
biggerIsBetter: false,
|
|
74
76
|
},
|
|
75
|
-
|
|
77
|
+
[Metric.DUPLICATE_PACKAGES_COUNT]: {
|
|
76
78
|
label: 'Duplicate Packages',
|
|
77
79
|
description: 'The total number of duplicate packages',
|
|
78
80
|
url: 'https://relative-ci.com/documentation/metrics-and-data#packages',
|
|
@@ -80,32 +82,32 @@ export const metrics = {
|
|
|
80
82
|
biggerIsBetter: false,
|
|
81
83
|
},
|
|
82
84
|
sizes: {
|
|
83
|
-
|
|
84
|
-
label:
|
|
85
|
+
[Metric.TOTAL_SIZE_JS]: {
|
|
86
|
+
label: FILE_TYPE_LABELS.JS,
|
|
85
87
|
type: METRIC_TYPE_FILE_SIZE,
|
|
86
88
|
},
|
|
87
|
-
|
|
88
|
-
label:
|
|
89
|
+
[Metric.TOTAL_SIZE_CSS]: {
|
|
90
|
+
label: FILE_TYPE_LABELS.CSS,
|
|
89
91
|
type: METRIC_TYPE_FILE_SIZE,
|
|
90
92
|
},
|
|
91
|
-
|
|
92
|
-
label:
|
|
93
|
+
[Metric.TOTAL_SIZE_IMG]: {
|
|
94
|
+
label: FILE_TYPE_LABELS.IMG,
|
|
93
95
|
type: METRIC_TYPE_FILE_SIZE,
|
|
94
96
|
},
|
|
95
|
-
|
|
96
|
-
label:
|
|
97
|
+
[Metric.TOTAL_SIZE_MEDIA]: {
|
|
98
|
+
label: FILE_TYPE_LABELS.MEDIA,
|
|
97
99
|
type: METRIC_TYPE_FILE_SIZE,
|
|
98
100
|
},
|
|
99
|
-
|
|
100
|
-
label:
|
|
101
|
+
[Metric.TOTAL_SIZE_FONT]: {
|
|
102
|
+
label: FILE_TYPE_LABELS.FONT,
|
|
101
103
|
type: METRIC_TYPE_FILE_SIZE,
|
|
102
104
|
},
|
|
103
|
-
|
|
104
|
-
label:
|
|
105
|
+
[Metric.TOTAL_SIZE_HTML]: {
|
|
106
|
+
label: FILE_TYPE_LABELS.HTML,
|
|
105
107
|
type: METRIC_TYPE_FILE_SIZE,
|
|
106
108
|
},
|
|
107
|
-
|
|
108
|
-
label:
|
|
109
|
+
[Metric.TOTAL_SIZE_OTHER]: {
|
|
110
|
+
label: FILE_TYPE_LABELS.OTHER,
|
|
109
111
|
type: METRIC_TYPE_FILE_SIZE,
|
|
110
112
|
},
|
|
111
113
|
},
|
package/src/webpack/types.ts
CHANGED
|
@@ -1,11 +1,35 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MetricRun } from '../constants';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
export const METRIC_TOTALS_SUFIX_ALL = 'ALL';
|
|
4
|
+
export const METRIC_TOTALS_PREFIX = 'totalSizeByType';
|
|
5
|
+
|
|
6
|
+
export enum Metric {
|
|
7
|
+
BUNDLE_SIZE = 'totalSizeByTypeALL',
|
|
8
|
+
INITIAL_SIZE_JS = 'totalInitialSizeJS',
|
|
9
|
+
INITIAL_SIZE_CSS = 'totalInitialSizeCSS',
|
|
10
|
+
CACHE_INVALIDATION = 'cacheInvalidation',
|
|
11
|
+
ASSET_COUNT = 'assetCount',
|
|
12
|
+
CHUNK_COUNT = 'chunkCount',
|
|
13
|
+
MODULE_COUNT = 'moduleCount',
|
|
14
|
+
DUPLICATE_MODULES_COUNT = 'duplicateModulesCount',
|
|
15
|
+
DUPLICATE_CODE = 'duplicateCode',
|
|
16
|
+
PACKAGE_COUNT = 'packageCount',
|
|
17
|
+
DUPLICATE_PACKAGES_COUNT = 'duplicatePackagesCount',
|
|
18
|
+
TOTAL_SIZE_JS = 'totalSizeByTypeJS',
|
|
19
|
+
TOTAL_SIZE_CSS = 'totalSizeByTypeCSS',
|
|
20
|
+
TOTAL_SIZE_IMG = 'totalSizeByTypeIMG',
|
|
21
|
+
TOTAL_SIZE_MEDIA = 'totalSizeByTypeMEDIA',
|
|
22
|
+
TOTAL_SIZE_FONT = 'totalSizeByTypeFONT',
|
|
23
|
+
TOTAL_SIZE_HTML = 'totalSizeByTypeHTML',
|
|
24
|
+
TOTAL_SIZE_OTHER = 'totalSizeByTypeOTHER',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface MetaChunk {
|
|
4
28
|
id: string;
|
|
5
29
|
name: string;
|
|
6
30
|
}
|
|
7
31
|
|
|
8
|
-
export interface Asset extends
|
|
32
|
+
export interface Asset extends MetricRun {
|
|
9
33
|
name: string;
|
|
10
34
|
isEntry: boolean;
|
|
11
35
|
isInitial: boolean;
|
|
@@ -24,11 +48,10 @@ export interface MetricsAssets {
|
|
|
24
48
|
};
|
|
25
49
|
}
|
|
26
50
|
|
|
27
|
-
export interface Module extends
|
|
51
|
+
export interface Module extends MetricRun {
|
|
28
52
|
name: string;
|
|
29
|
-
chunkIds: Array<
|
|
53
|
+
chunkIds: Array<String>;
|
|
30
54
|
duplicated: boolean;
|
|
31
|
-
reasons?: Array<string>;
|
|
32
55
|
}
|
|
33
56
|
|
|
34
57
|
export type Modules = Record<string, Module>;
|
|
@@ -42,7 +65,8 @@ export interface MetricsModules {
|
|
|
42
65
|
};
|
|
43
66
|
}
|
|
44
67
|
|
|
45
|
-
export interface Package extends
|
|
68
|
+
export interface Package extends MetricRun {
|
|
69
|
+
name: string;
|
|
46
70
|
path: string;
|
|
47
71
|
}
|
|
48
72
|
|
package/types/config/index.d.ts
CHANGED
|
@@ -5,7 +5,3 @@ export * from "./metrics";
|
|
|
5
5
|
export * from "./delta";
|
|
6
6
|
export const PACKAGES_SEPARATOR: ":";
|
|
7
7
|
export const PACKAGE_ID_SEPARATOR: "~";
|
|
8
|
-
export const SOURCE_PATH_WEBPACK_STATS: "webpack";
|
|
9
|
-
export const SOURCE_PATH_LIGHTHOUSE: "lighthouse";
|
|
10
|
-
export const SOURCE_PATH_BROWSERTIME: "browsertime";
|
|
11
|
-
export const SOURCE_PATHS: string[];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
export declare enum InsightType {
|
|
2
|
+
ERROR = "ERROR",
|
|
3
|
+
INFO = "INFO",
|
|
4
|
+
WARNING = "WARNING"
|
|
5
|
+
}
|