@jsenv/performance-impact 4.1.0 → 4.1.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/package.json +5 -5
- package/src/importMetricFromFiles.js +25 -25
- package/src/internal/assertions.js +15 -15
- package/src/internal/comment/createPerfImpactComment.js +37 -37
- package/src/internal/comment/formatImpact.js +11 -11
- package/src/internal/comment/jsenvCommentParameters.js +4 -4
- package/src/internal/comment/jsenvFormatGroupSummary.js +22 -22
- package/src/internal/comment/jsenvFormatPerformanceImpactCell.js +8 -8
- package/src/internal/comment/jsenvIsPerformanceImpactBig.js +6 -6
- package/src/internal/comment/renderGroupImpactTable.js +37 -37
- package/src/internal/computeMetricsMedian.js +7 -7
- package/src/internal/formatMetricValue.js +4 -4
- package/src/internal/formatMetricsLog.js +6 -6
- package/src/internal/formatRatio.js +4 -4
- package/src/internal/measureMultipleTimes.js +33 -33
- package/src/internal/median.js +14 -14
- package/src/main.js +4 -4
- package/src/reportPerformanceImpact.js +17 -17
- package/src/startMeasures.js +32 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/performance-impact",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.2",
|
|
4
4
|
"description": "Report pull request impacts on performance metrics",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"repository": {
|
|
12
12
|
"type": "git",
|
|
13
13
|
"url": "https://github.com/jsenv/workflow",
|
|
14
|
-
"directory": "packages/
|
|
14
|
+
"directory": "packages/performance-impact"
|
|
15
15
|
},
|
|
16
16
|
"engines": {
|
|
17
17
|
"node": ">=16.13.0"
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
38
|
"@jsenv/dynamic-import-worker": "1.1.0",
|
|
39
|
-
"@jsenv/filesystem": "4.
|
|
40
|
-
"@jsenv/github-pull-request-impact": "1.7.
|
|
41
|
-
"@jsenv/log": "3.
|
|
39
|
+
"@jsenv/filesystem": "4.3.2",
|
|
40
|
+
"@jsenv/github-pull-request-impact": "1.7.2",
|
|
41
|
+
"@jsenv/log": "3.4.0"
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem"
|
|
2
|
-
import { createLogger } from "@jsenv/log"
|
|
3
|
-
import { importOneExportFromFile } from "@jsenv/dynamic-import-worker"
|
|
1
|
+
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem";
|
|
2
|
+
import { createLogger } from "@jsenv/log";
|
|
3
|
+
import { importOneExportFromFile } from "@jsenv/dynamic-import-worker";
|
|
4
4
|
|
|
5
|
-
import { measureMultipleTimes } from "./internal/measureMultipleTimes.js"
|
|
6
|
-
import { assertMetrics } from "./internal/assertions.js"
|
|
7
|
-
import { computeMetricsMedian } from "./internal/computeMetricsMedian.js"
|
|
8
|
-
import { formatMetricsLog } from "./internal/formatMetricsLog.js"
|
|
5
|
+
import { measureMultipleTimes } from "./internal/measureMultipleTimes.js";
|
|
6
|
+
import { assertMetrics } from "./internal/assertions.js";
|
|
7
|
+
import { computeMetricsMedian } from "./internal/computeMetricsMedian.js";
|
|
8
|
+
import { formatMetricsLog } from "./internal/formatMetricsLog.js";
|
|
9
9
|
|
|
10
10
|
export const importMetricFromFiles = async ({
|
|
11
11
|
logLevel,
|
|
12
12
|
directoryUrl,
|
|
13
13
|
metricsDescriptions,
|
|
14
14
|
}) => {
|
|
15
|
-
const logger = createLogger({ logLevel })
|
|
15
|
+
const logger = createLogger({ logLevel });
|
|
16
16
|
|
|
17
|
-
directoryUrl = assertAndNormalizeDirectoryUrl(directoryUrl)
|
|
17
|
+
directoryUrl = assertAndNormalizeDirectoryUrl(directoryUrl);
|
|
18
18
|
|
|
19
|
-
const allMetrics = {}
|
|
19
|
+
const allMetrics = {};
|
|
20
20
|
await Object.keys(metricsDescriptions).reduce(
|
|
21
21
|
async (previous, metricName) => {
|
|
22
|
-
await previous
|
|
22
|
+
await previous;
|
|
23
23
|
|
|
24
|
-
const metricsDescription = metricsDescriptions[metricName]
|
|
24
|
+
const metricsDescription = metricsDescriptions[metricName];
|
|
25
25
|
const {
|
|
26
26
|
file,
|
|
27
27
|
iterations,
|
|
28
28
|
msToWaitBetweenEachIteration = 100,
|
|
29
|
-
} = metricsDescription
|
|
30
|
-
const url = new URL(file, directoryUrl).href
|
|
29
|
+
} = metricsDescription;
|
|
30
|
+
const url = new URL(file, directoryUrl).href;
|
|
31
31
|
|
|
32
32
|
const measure = async () => {
|
|
33
|
-
const metrics = await importOneExportFromFile(url)
|
|
34
|
-
assertMetrics(metrics, `in ${file}`)
|
|
35
|
-
return metrics
|
|
36
|
-
}
|
|
33
|
+
const metrics = await importOneExportFromFile(url);
|
|
34
|
+
assertMetrics(metrics, `in ${file}`);
|
|
35
|
+
return metrics;
|
|
36
|
+
};
|
|
37
37
|
|
|
38
38
|
const metricsWithIterations = await measureMultipleTimes(
|
|
39
39
|
measure,
|
|
@@ -41,15 +41,15 @@ export const importMetricFromFiles = async ({
|
|
|
41
41
|
{
|
|
42
42
|
msToWaitBetweenEachIteration,
|
|
43
43
|
},
|
|
44
|
-
)
|
|
45
|
-
const metrics = computeMetricsMedian(metricsWithIterations)
|
|
44
|
+
);
|
|
45
|
+
const metrics = computeMetricsMedian(metricsWithIterations);
|
|
46
46
|
|
|
47
|
-
logger.info(formatMetricsLog(metrics))
|
|
47
|
+
logger.info(formatMetricsLog(metrics));
|
|
48
48
|
|
|
49
|
-
allMetrics[metricName] = metrics
|
|
49
|
+
allMetrics[metricName] = metrics;
|
|
50
50
|
},
|
|
51
51
|
Promise.resolve(),
|
|
52
|
-
)
|
|
52
|
+
);
|
|
53
53
|
|
|
54
|
-
return allMetrics
|
|
55
|
-
}
|
|
54
|
+
return allMetrics;
|
|
55
|
+
};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { createDetailedMessage } from "@jsenv/log"
|
|
1
|
+
import { createDetailedMessage } from "@jsenv/log";
|
|
2
2
|
|
|
3
3
|
export const assertPerformanceReport = (performanceReport) => {
|
|
4
4
|
if (typeof performanceReport !== "object" || performanceReport === null) {
|
|
5
5
|
throw new TypeError(
|
|
6
6
|
`performanceReport must be an object, received ${performanceReport}.`,
|
|
7
|
-
)
|
|
7
|
+
);
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
Object.keys(performanceReport).forEach((groupName) => {
|
|
11
|
-
const metrics = performanceReport[groupName]
|
|
12
|
-
assertMetrics(metrics)
|
|
13
|
-
})
|
|
14
|
-
}
|
|
11
|
+
const metrics = performanceReport[groupName];
|
|
12
|
+
assertMetrics(metrics);
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
15
|
|
|
16
16
|
export const assertMetrics = (metrics, metricsTrace) => {
|
|
17
17
|
if (typeof metrics !== "object" || metrics === null) {
|
|
@@ -19,29 +19,29 @@ export const assertMetrics = (metrics, metricsTrace) => {
|
|
|
19
19
|
createDetailedMessage(`metrics must be an object, got ${metrics}`, {
|
|
20
20
|
"metrics trace": metricsTrace,
|
|
21
21
|
}),
|
|
22
|
-
)
|
|
22
|
+
);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
Object.keys(metrics).forEach((metricName) => {
|
|
26
|
-
const metric = metrics[metricName]
|
|
26
|
+
const metric = metrics[metricName];
|
|
27
27
|
if (typeof metric !== "object") {
|
|
28
28
|
throw new TypeError(
|
|
29
29
|
`metric must be an object, got ${metric} for ${metricName}`,
|
|
30
|
-
)
|
|
30
|
+
);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
const { value } = metric
|
|
33
|
+
const { value } = metric;
|
|
34
34
|
if (typeof value !== "number") {
|
|
35
35
|
throw new TypeError(
|
|
36
36
|
`metric value must be a number, got ${value} for ${metricName}`,
|
|
37
|
-
)
|
|
37
|
+
);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const { unit } = metric
|
|
40
|
+
const { unit } = metric;
|
|
41
41
|
if (unit !== undefined && unit !== "ms" && unit !== "byte") {
|
|
42
42
|
throw new TypeError(
|
|
43
43
|
`metric type must be undefined, "ms", or "byte", got ${unit} for ${metricName}`,
|
|
44
|
-
)
|
|
44
|
+
);
|
|
45
45
|
}
|
|
46
|
-
})
|
|
47
|
-
}
|
|
46
|
+
});
|
|
47
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { renderGroupImpactTable } from "./renderGroupImpactTable.js"
|
|
1
|
+
import { renderGroupImpactTable } from "./renderGroupImpactTable.js";
|
|
2
2
|
|
|
3
3
|
export const createPerfImpactComment = ({
|
|
4
4
|
pullRequestBase,
|
|
@@ -9,47 +9,47 @@ export const createPerfImpactComment = ({
|
|
|
9
9
|
formatGroupSummary,
|
|
10
10
|
formatPerformanceImpactCell,
|
|
11
11
|
}) => {
|
|
12
|
-
const warnings = []
|
|
12
|
+
const warnings = [];
|
|
13
13
|
|
|
14
14
|
// here we have are sure beforeMergeData and afterMergeData are valid
|
|
15
15
|
// because we assert their shape is correct in reportPerformanceImpact.js#collectInfo()
|
|
16
16
|
// for metrics here before merge but are no longer in afetr merge, we will ignore them
|
|
17
17
|
// for new metrics we'll show them without doing the diff
|
|
18
18
|
|
|
19
|
-
const beforeMergeGroups = beforeMergeData
|
|
20
|
-
const afterMergeGroups = afterMergeData
|
|
19
|
+
const beforeMergeGroups = beforeMergeData;
|
|
20
|
+
const afterMergeGroups = afterMergeData;
|
|
21
21
|
|
|
22
|
-
const groupNames = Object.keys(afterMergeGroups)
|
|
22
|
+
const groupNames = Object.keys(afterMergeGroups);
|
|
23
23
|
const metricCount = groupNames.reduce((previous, groupName) => {
|
|
24
|
-
const afterMergeMetrics = afterMergeGroups[groupName]
|
|
25
|
-
return previous + Object.keys(afterMergeMetrics).length
|
|
26
|
-
}, 0)
|
|
24
|
+
const afterMergeMetrics = afterMergeGroups[groupName];
|
|
25
|
+
return previous + Object.keys(afterMergeMetrics).length;
|
|
26
|
+
}, 0);
|
|
27
27
|
|
|
28
28
|
if (metricCount === 0) {
|
|
29
29
|
const body = `<h4 id="perf-impact">Performance impact</h4>
|
|
30
30
|
|
|
31
|
-
<p>No impact to compute when merging <em>${pullRequestHead}</em> into <em>${pullRequestBase}</em>: there is no performance metric.</p
|
|
31
|
+
<p>No impact to compute when merging <em>${pullRequestHead}</em> into <em>${pullRequestBase}</em>: there is no performance metric.</p>`;
|
|
32
32
|
|
|
33
|
-
return { warnings, body }
|
|
33
|
+
return { warnings, body };
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const groups = []
|
|
36
|
+
const groups = [];
|
|
37
37
|
groupNames.forEach((groupName) => {
|
|
38
|
-
const afterMergeMetrics = afterMergeGroups[groupName]
|
|
39
|
-
const afterMergeMetricCount = Object.keys(afterMergeMetrics).length
|
|
38
|
+
const afterMergeMetrics = afterMergeGroups[groupName];
|
|
39
|
+
const afterMergeMetricCount = Object.keys(afterMergeMetrics).length;
|
|
40
40
|
if (afterMergeMetricCount === 0) {
|
|
41
41
|
// skip empty groups
|
|
42
|
-
return
|
|
42
|
+
return;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
const beforeMergeMetrics = beforeMergeGroups[groupName]
|
|
45
|
+
const beforeMergeMetrics = beforeMergeGroups[groupName];
|
|
46
46
|
const bigImpacts = beforeMergeMetrics
|
|
47
47
|
? getBigImpacts({
|
|
48
48
|
afterMergeMetrics,
|
|
49
49
|
beforeMergeMetrics,
|
|
50
50
|
isPerformanceImpactBig,
|
|
51
51
|
})
|
|
52
|
-
: {}
|
|
52
|
+
: {};
|
|
53
53
|
|
|
54
54
|
groups.push(
|
|
55
55
|
renderDetails({
|
|
@@ -66,38 +66,38 @@ export const createPerfImpactComment = ({
|
|
|
66
66
|
bigImpacts,
|
|
67
67
|
}),
|
|
68
68
|
}),
|
|
69
|
-
)
|
|
70
|
-
})
|
|
69
|
+
);
|
|
70
|
+
});
|
|
71
71
|
|
|
72
72
|
const body = `<h4 id="perf-impact">Performance impact</h4>
|
|
73
73
|
|
|
74
|
-
<p>Impact on ${metricCount} metrics when merging <em>${pullRequestHead}</em> into <em>${pullRequestBase}</em>. Before drawing conclusion, keep in mind <a href="https://github.com/jsenv/workflow/tree/main/packages/
|
|
74
|
+
<p>Impact on ${metricCount} metrics when merging <em>${pullRequestHead}</em> into <em>${pullRequestBase}</em>. Before drawing conclusion, keep in mind <a href="https://github.com/jsenv/workflow/tree/main/packages/performance-impact#performance-variability">performance variability</a>.</p>
|
|
75
75
|
|
|
76
76
|
${groups.join(`
|
|
77
77
|
|
|
78
|
-
`)}
|
|
78
|
+
`)}`;
|
|
79
79
|
|
|
80
|
-
return { warnings, body }
|
|
81
|
-
}
|
|
80
|
+
return { warnings, body };
|
|
81
|
+
};
|
|
82
82
|
|
|
83
83
|
const getBigImpacts = ({
|
|
84
84
|
afterMergeMetrics,
|
|
85
85
|
beforeMergeMetrics,
|
|
86
86
|
isPerformanceImpactBig,
|
|
87
87
|
}) => {
|
|
88
|
-
const bigImpacts = {}
|
|
88
|
+
const bigImpacts = {};
|
|
89
89
|
|
|
90
90
|
Object.keys(afterMergeMetrics).forEach((metricName) => {
|
|
91
|
-
const metricBeforeMerge = beforeMergeMetrics[metricName]
|
|
91
|
+
const metricBeforeMerge = beforeMergeMetrics[metricName];
|
|
92
92
|
if (metricBeforeMerge === undefined) {
|
|
93
|
-
return
|
|
93
|
+
return;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
const metricAfterMerge = afterMergeMetrics[metricName]
|
|
97
|
-
const metricUnit = metricAfterMerge.unit
|
|
98
|
-
const metricValueAfterMerge = metricAfterMerge.value
|
|
99
|
-
const metricValueBeforeMerge = metricBeforeMerge.value
|
|
100
|
-
const metricValueDelta = metricValueAfterMerge - metricValueBeforeMerge
|
|
96
|
+
const metricAfterMerge = afterMergeMetrics[metricName];
|
|
97
|
+
const metricUnit = metricAfterMerge.unit;
|
|
98
|
+
const metricValueAfterMerge = metricAfterMerge.value;
|
|
99
|
+
const metricValueBeforeMerge = metricBeforeMerge.value;
|
|
100
|
+
const metricValueDelta = metricValueAfterMerge - metricValueBeforeMerge;
|
|
101
101
|
if (
|
|
102
102
|
!isPerformanceImpactBig({
|
|
103
103
|
metricName,
|
|
@@ -107,22 +107,22 @@ const getBigImpacts = ({
|
|
|
107
107
|
metricValueDelta,
|
|
108
108
|
})
|
|
109
109
|
) {
|
|
110
|
-
return
|
|
110
|
+
return;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
bigImpacts[metricName] = {
|
|
114
114
|
metricValueAfterMerge,
|
|
115
115
|
metricValueBeforeMerge,
|
|
116
116
|
metricValueDelta,
|
|
117
|
-
}
|
|
118
|
-
})
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
119
|
|
|
120
|
-
return bigImpacts
|
|
121
|
-
}
|
|
120
|
+
return bigImpacts;
|
|
121
|
+
};
|
|
122
122
|
|
|
123
123
|
const renderDetails = ({ summary, content, opened = false, indent = 0 }) => {
|
|
124
124
|
return `${" ".repeat(indent)}<details${opened ? " open" : ""}>
|
|
125
125
|
${" ".repeat(indent + 2)}<summary>${summary}</summary>
|
|
126
126
|
${" ".repeat(indent + 2)}${content}
|
|
127
|
-
${" ".repeat(indent)}</details
|
|
128
|
-
}
|
|
127
|
+
${" ".repeat(indent)}</details>`;
|
|
128
|
+
};
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import { formatMetricValue } from "../formatMetricValue.js"
|
|
2
|
-
import { formatRatioAsPercentage } from "../formatRatio.js"
|
|
1
|
+
import { formatMetricValue } from "../formatMetricValue.js";
|
|
2
|
+
import { formatRatioAsPercentage } from "../formatRatio.js";
|
|
3
3
|
|
|
4
4
|
export const formatImpact = ({
|
|
5
5
|
metricValueBeforeMerge,
|
|
6
6
|
metricValueAfterMerge,
|
|
7
7
|
metricUnit,
|
|
8
8
|
}) => {
|
|
9
|
-
const diff = metricValueAfterMerge - metricValueBeforeMerge
|
|
9
|
+
const diff = metricValueAfterMerge - metricValueBeforeMerge;
|
|
10
10
|
|
|
11
11
|
if (diff === 0) {
|
|
12
|
-
return
|
|
12
|
+
return ``;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
const diffFormatted = `${diff < 0 ? "-" : "+"}${formatMetricValue({
|
|
16
16
|
value: Math.abs(diff),
|
|
17
17
|
unit: metricUnit,
|
|
18
|
-
})}
|
|
18
|
+
})}`;
|
|
19
19
|
|
|
20
|
-
return diffFormatted
|
|
21
|
-
}
|
|
20
|
+
return diffFormatted;
|
|
21
|
+
};
|
|
22
22
|
|
|
23
23
|
export const formatImpactAsPercentage = ({
|
|
24
24
|
metricValueBeforeMerge,
|
|
25
25
|
metricValueAfterMerge,
|
|
26
26
|
}) => {
|
|
27
|
-
const diff = metricValueAfterMerge - metricValueBeforeMerge
|
|
27
|
+
const diff = metricValueAfterMerge - metricValueBeforeMerge;
|
|
28
28
|
const diffRatio =
|
|
29
29
|
metricValueBeforeMerge === 0
|
|
30
30
|
? 1
|
|
31
31
|
: metricValueAfterMerge === 0
|
|
32
32
|
? -1
|
|
33
|
-
: diff / metricValueBeforeMerge
|
|
34
|
-
return formatRatioAsPercentage(diffRatio)
|
|
35
|
-
}
|
|
33
|
+
: diff / metricValueBeforeMerge;
|
|
34
|
+
return formatRatioAsPercentage(diffRatio);
|
|
35
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { jsenvIsPerformanceImpactBig } from "./jsenvIsPerformanceImpactBig.js"
|
|
2
|
-
import { jsenvFormatGroupSummary } from "./jsenvFormatGroupSummary.js"
|
|
3
|
-
import { jsenvFormatPerformanceImpactCell } from "./jsenvFormatPerformanceImpactCell.js"
|
|
1
|
+
import { jsenvIsPerformanceImpactBig } from "./jsenvIsPerformanceImpactBig.js";
|
|
2
|
+
import { jsenvFormatGroupSummary } from "./jsenvFormatGroupSummary.js";
|
|
3
|
+
import { jsenvFormatPerformanceImpactCell } from "./jsenvFormatPerformanceImpactCell.js";
|
|
4
4
|
|
|
5
5
|
export const jsenvCommentParameters = {
|
|
6
6
|
isPerformanceImpactBig: jsenvIsPerformanceImpactBig,
|
|
7
7
|
formatGroupSummary: jsenvFormatGroupSummary,
|
|
8
8
|
formatPerformanceImpactCell: jsenvFormatPerformanceImpactCell,
|
|
9
|
-
}
|
|
9
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { formatRatioAsPercentage } from "../formatRatio.js"
|
|
1
|
+
import { formatRatioAsPercentage } from "../formatRatio.js";
|
|
2
2
|
|
|
3
3
|
export const jsenvFormatGroupSummary = ({
|
|
4
4
|
groupName,
|
|
@@ -6,38 +6,38 @@ export const jsenvFormatGroupSummary = ({
|
|
|
6
6
|
afterMergeMetrics,
|
|
7
7
|
}) => {
|
|
8
8
|
if (!beforeMergeMetrics) {
|
|
9
|
-
return `${groupName} (new)
|
|
9
|
+
return `${groupName} (new)`;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const allRatios = getAllImpactRatios({
|
|
13
13
|
beforeMergeMetrics,
|
|
14
14
|
afterMergeMetrics,
|
|
15
|
-
})
|
|
16
|
-
const ratiosCount = allRatios.length
|
|
15
|
+
});
|
|
16
|
+
const ratiosCount = allRatios.length;
|
|
17
17
|
if (ratiosCount === 0) {
|
|
18
|
-
return `${groupName} (no impact)
|
|
18
|
+
return `${groupName} (no impact)`;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const ratioSum = allRatios.reduce((previous, ratio) => previous + ratio, 0)
|
|
21
|
+
const ratioSum = allRatios.reduce((previous, ratio) => previous + ratio, 0);
|
|
22
22
|
if (ratioSum === 0) {
|
|
23
|
-
return `${groupName} (no impact)
|
|
23
|
+
return `${groupName} (no impact)`;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
return `${groupName} (${formatRatioAsPercentage(ratioSum / ratiosCount)})
|
|
27
|
-
}
|
|
26
|
+
return `${groupName} (${formatRatioAsPercentage(ratioSum / ratiosCount)})`;
|
|
27
|
+
};
|
|
28
28
|
|
|
29
29
|
const getAllImpactRatios = ({ afterMergeMetrics, beforeMergeMetrics }) => {
|
|
30
|
-
let allRatios = []
|
|
30
|
+
let allRatios = [];
|
|
31
31
|
Object.keys(afterMergeMetrics).forEach((metricName) => {
|
|
32
|
-
const metricBeforeMerge = beforeMergeMetrics[metricName]
|
|
32
|
+
const metricBeforeMerge = beforeMergeMetrics[metricName];
|
|
33
33
|
if (!metricBeforeMerge) {
|
|
34
34
|
// it's new, let's ignore
|
|
35
|
-
return
|
|
35
|
+
return;
|
|
36
36
|
}
|
|
37
|
-
const metricAfterMerge = afterMergeMetrics[metricName]
|
|
38
|
-
const metricValueBeforeMerge = metricBeforeMerge.value
|
|
39
|
-
const metricValueAfterMerge = metricAfterMerge.value
|
|
40
|
-
const metricDiff = metricValueAfterMerge - metricValueBeforeMerge
|
|
37
|
+
const metricAfterMerge = afterMergeMetrics[metricName];
|
|
38
|
+
const metricValueBeforeMerge = metricBeforeMerge.value;
|
|
39
|
+
const metricValueAfterMerge = metricAfterMerge.value;
|
|
40
|
+
const metricDiff = metricValueAfterMerge - metricValueBeforeMerge;
|
|
41
41
|
if (
|
|
42
42
|
metricDiff === 0 &&
|
|
43
43
|
metricValueAfterMerge === 0 &&
|
|
@@ -45,7 +45,7 @@ const getAllImpactRatios = ({ afterMergeMetrics, beforeMergeMetrics }) => {
|
|
|
45
45
|
) {
|
|
46
46
|
// there is no impact on a metric that is 0
|
|
47
47
|
// we can ignore this metric
|
|
48
|
-
return
|
|
48
|
+
return;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
const metricDiffRatio =
|
|
@@ -55,8 +55,8 @@ const getAllImpactRatios = ({ afterMergeMetrics, beforeMergeMetrics }) => {
|
|
|
55
55
|
? 1
|
|
56
56
|
: metricValueAfterMerge === 0
|
|
57
57
|
? -1
|
|
58
|
-
: metricDiff / metricValueBeforeMerge
|
|
59
|
-
allRatios.push(metricDiffRatio)
|
|
60
|
-
})
|
|
61
|
-
return allRatios
|
|
62
|
-
}
|
|
58
|
+
: metricDiff / metricValueBeforeMerge;
|
|
59
|
+
allRatios.push(metricDiffRatio);
|
|
60
|
+
});
|
|
61
|
+
return allRatios;
|
|
62
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { formatImpact, formatImpactAsPercentage } from "./formatImpact.js"
|
|
1
|
+
import { formatImpact, formatImpactAsPercentage } from "./formatImpact.js";
|
|
2
2
|
|
|
3
3
|
export const jsenvFormatPerformanceImpactCell = ({
|
|
4
4
|
metricUnit,
|
|
@@ -7,23 +7,23 @@ export const jsenvFormatPerformanceImpactCell = ({
|
|
|
7
7
|
}) => {
|
|
8
8
|
// metric is new
|
|
9
9
|
if (metricValueBeforeMerge === undefined) {
|
|
10
|
-
return
|
|
10
|
+
return ``;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
const diff = metricValueAfterMerge - metricValueBeforeMerge
|
|
13
|
+
const diff = metricValueAfterMerge - metricValueBeforeMerge;
|
|
14
14
|
if (diff === 0) {
|
|
15
|
-
return
|
|
15
|
+
return ``;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
const diffFormatted = formatImpact({
|
|
19
19
|
metricUnit,
|
|
20
20
|
metricValueAfterMerge,
|
|
21
21
|
metricValueBeforeMerge,
|
|
22
|
-
})
|
|
22
|
+
});
|
|
23
23
|
const diffAsPercentageFormatted = formatImpactAsPercentage({
|
|
24
24
|
metricUnit,
|
|
25
25
|
metricValueBeforeMerge,
|
|
26
26
|
metricValueAfterMerge,
|
|
27
|
-
})
|
|
28
|
-
return `${diffFormatted} / ${diffAsPercentageFormatted}
|
|
29
|
-
}
|
|
27
|
+
});
|
|
28
|
+
return `${diffFormatted} / ${diffAsPercentageFormatted}`;
|
|
29
|
+
};
|
|
@@ -2,14 +2,14 @@ export const jsenvIsPerformanceImpactBig = ({
|
|
|
2
2
|
metricValueBeforeMerge,
|
|
3
3
|
metricValueDelta,
|
|
4
4
|
}) => {
|
|
5
|
-
const absoluteDelta = Math.abs(metricValueDelta)
|
|
5
|
+
const absoluteDelta = Math.abs(metricValueDelta);
|
|
6
6
|
const absoluteDeltaAsRatio =
|
|
7
|
-
absoluteDelta === 0 ? 0 : absoluteDelta / metricValueBeforeMerge
|
|
8
|
-
const absoluteDeltaAsPercentage = absoluteDeltaAsRatio * 100
|
|
7
|
+
absoluteDelta === 0 ? 0 : absoluteDelta / metricValueBeforeMerge;
|
|
8
|
+
const absoluteDeltaAsPercentage = absoluteDeltaAsRatio * 100;
|
|
9
9
|
|
|
10
10
|
// absolute diff as percentage is below 5% -> not big
|
|
11
11
|
if (absoluteDeltaAsPercentage < 5) {
|
|
12
|
-
return false
|
|
12
|
+
return false;
|
|
13
13
|
}
|
|
14
|
-
return true
|
|
15
|
-
}
|
|
14
|
+
return true;
|
|
15
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { formatMetricValue } from "../formatMetricValue.js"
|
|
1
|
+
import { formatMetricValue } from "../formatMetricValue.js";
|
|
2
2
|
|
|
3
|
-
const METRIC_NAME_MAX_LENGTH = 50
|
|
4
|
-
const MAX_METRIC_PER_GROUP = 20
|
|
3
|
+
const METRIC_NAME_MAX_LENGTH = 50;
|
|
4
|
+
const MAX_METRIC_PER_GROUP = 20;
|
|
5
5
|
|
|
6
6
|
export const renderGroupImpactTable = ({
|
|
7
7
|
formatPerformanceImpactCell,
|
|
@@ -26,8 +26,8 @@ export const renderGroupImpactTable = ({
|
|
|
26
26
|
bigImpacts,
|
|
27
27
|
})}
|
|
28
28
|
</tbody>
|
|
29
|
-
</table
|
|
30
|
-
}
|
|
29
|
+
</table>`;
|
|
30
|
+
};
|
|
31
31
|
|
|
32
32
|
const renderPerfImpactTableBody = ({
|
|
33
33
|
formatPerformanceImpactCell,
|
|
@@ -35,21 +35,21 @@ const renderPerfImpactTableBody = ({
|
|
|
35
35
|
afterMergeMetrics,
|
|
36
36
|
bigImpacts,
|
|
37
37
|
}) => {
|
|
38
|
-
const lines = []
|
|
39
|
-
const metricAllNames = Object.keys(afterMergeMetrics)
|
|
40
|
-
const metricCount = metricAllNames.length
|
|
38
|
+
const lines = [];
|
|
39
|
+
const metricAllNames = Object.keys(afterMergeMetrics);
|
|
40
|
+
const metricCount = metricAllNames.length;
|
|
41
41
|
const metricNames =
|
|
42
42
|
metricCount > MAX_METRIC_PER_GROUP
|
|
43
43
|
? metricAllNames.slice(0, MAX_METRIC_PER_GROUP)
|
|
44
|
-
: metricAllNames
|
|
44
|
+
: metricAllNames;
|
|
45
45
|
|
|
46
46
|
metricNames.forEach((metricName) => {
|
|
47
|
-
const metricNameDisplayed = truncateMetricName(metricName)
|
|
48
|
-
const metric = afterMergeMetrics[metricName]
|
|
49
|
-
const metricValueAfterMerge = metric.value
|
|
47
|
+
const metricNameDisplayed = truncateMetricName(metricName);
|
|
48
|
+
const metric = afterMergeMetrics[metricName];
|
|
49
|
+
const metricValueAfterMerge = metric.value;
|
|
50
50
|
const metricBeforeMerge = beforeMergeMetrics
|
|
51
51
|
? beforeMergeMetrics[metricName]
|
|
52
|
-
: undefined
|
|
52
|
+
: undefined;
|
|
53
53
|
if (!metricBeforeMerge) {
|
|
54
54
|
lines.push([
|
|
55
55
|
`<td nowrap>${metricNameDisplayed}</td>`,
|
|
@@ -62,12 +62,12 @@ const renderPerfImpactTableBody = ({
|
|
|
62
62
|
isBig: false,
|
|
63
63
|
})}</td>`,
|
|
64
64
|
`<td>:baby:</td>`,
|
|
65
|
-
])
|
|
66
|
-
return
|
|
65
|
+
]);
|
|
66
|
+
return;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
const isBig = Boolean(bigImpacts[metricName])
|
|
70
|
-
const metricValueBeforeMerge = metricBeforeMerge.value
|
|
69
|
+
const isBig = Boolean(bigImpacts[metricName]);
|
|
70
|
+
const metricValueBeforeMerge = metricBeforeMerge.value;
|
|
71
71
|
lines.push([
|
|
72
72
|
`<td nowrap>${metricNameDisplayed}</td>`,
|
|
73
73
|
`<td nowrap>${formatMetricValue(metricBeforeMerge)}</td>`,
|
|
@@ -82,50 +82,50 @@ const renderPerfImpactTableBody = ({
|
|
|
82
82
|
metricValueAfterMerge,
|
|
83
83
|
metricValueBeforeMerge,
|
|
84
84
|
})}</td>`,
|
|
85
|
-
])
|
|
86
|
-
})
|
|
85
|
+
]);
|
|
86
|
+
});
|
|
87
87
|
if (metricNames !== metricAllNames) {
|
|
88
88
|
lines.push([
|
|
89
89
|
`<td colspan="5" align="center">... ${
|
|
90
90
|
metricCount - MAX_METRIC_PER_GROUP
|
|
91
91
|
} more ...</td>`,
|
|
92
|
-
])
|
|
92
|
+
]);
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
return renderTableLines(lines)
|
|
96
|
-
}
|
|
95
|
+
return renderTableLines(lines);
|
|
96
|
+
};
|
|
97
97
|
|
|
98
98
|
const renderEmojiCellContent = ({
|
|
99
99
|
metricValueAfterMerge,
|
|
100
100
|
metricValueBeforeMerge,
|
|
101
101
|
}) => {
|
|
102
|
-
const delta = metricValueAfterMerge - metricValueBeforeMerge
|
|
102
|
+
const delta = metricValueAfterMerge - metricValueBeforeMerge;
|
|
103
103
|
if (delta === 0) {
|
|
104
|
-
return ":ghost:"
|
|
104
|
+
return ":ghost:";
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
if (delta > 0) {
|
|
108
|
-
return ":arrow_upper_right:"
|
|
108
|
+
return ":arrow_upper_right:";
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
return ":arrow_lower_right:"
|
|
112
|
-
}
|
|
111
|
+
return ":arrow_lower_right:";
|
|
112
|
+
};
|
|
113
113
|
|
|
114
114
|
const truncateMetricName = (metricName) => {
|
|
115
|
-
const length = metricName.length
|
|
115
|
+
const length = metricName.length;
|
|
116
116
|
if (length > METRIC_NAME_MAX_LENGTH) {
|
|
117
|
-
return `${metricName.slice(0, METRIC_NAME_MAX_LENGTH - `…`.length)}
|
|
117
|
+
return `${metricName.slice(0, METRIC_NAME_MAX_LENGTH - `…`.length)}…`;
|
|
118
118
|
}
|
|
119
|
-
return metricName
|
|
120
|
-
}
|
|
119
|
+
return metricName;
|
|
120
|
+
};
|
|
121
121
|
|
|
122
122
|
const renderTableLines = (lines, { indentCount = 3, indentSize = 2 } = {}) => {
|
|
123
123
|
if (lines.length === 0) {
|
|
124
|
-
return ""
|
|
124
|
+
return "";
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
const cellLeftSpacing = indent(indentCount + 1, indentSize)
|
|
128
|
-
const lineLeftSpacing = indent(indentCount, indentSize)
|
|
127
|
+
const cellLeftSpacing = indent(indentCount + 1, indentSize);
|
|
128
|
+
const lineLeftSpacing = indent(indentCount, indentSize);
|
|
129
129
|
|
|
130
130
|
return `<tr>${lines.map(
|
|
131
131
|
(cells) => `
|
|
@@ -134,7 +134,7 @@ ${cellLeftSpacing}`)}`,
|
|
|
134
134
|
).join(`
|
|
135
135
|
${lineLeftSpacing}</tr>
|
|
136
136
|
${lineLeftSpacing}<tr>`)}
|
|
137
|
-
${lineLeftSpacing}</tr
|
|
138
|
-
}
|
|
137
|
+
${lineLeftSpacing}</tr>`;
|
|
138
|
+
};
|
|
139
139
|
|
|
140
|
-
const indent = (count, size) => ` `.repeat(size * count)
|
|
140
|
+
const indent = (count, size) => ` `.repeat(size * count);
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { median } from "./median.js"
|
|
1
|
+
import { median } from "./median.js";
|
|
2
2
|
|
|
3
3
|
export const computeMetricsMedian = (metrics) => {
|
|
4
|
-
const metricsMedian = {}
|
|
4
|
+
const metricsMedian = {};
|
|
5
5
|
|
|
6
6
|
Object.keys(metrics).forEach((metricName) => {
|
|
7
|
-
const metricWithIteration = metrics[metricName]
|
|
7
|
+
const metricWithIteration = metrics[metricName];
|
|
8
8
|
metricsMedian[metricName] = {
|
|
9
9
|
value: median(metricWithIteration.values),
|
|
10
10
|
unit: metricWithIteration.unit,
|
|
11
|
-
}
|
|
12
|
-
})
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
13
|
|
|
14
|
-
return metricsMedian
|
|
15
|
-
}
|
|
14
|
+
return metricsMedian;
|
|
15
|
+
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { byteAsFileSize, msAsDuration } from "@jsenv/log"
|
|
1
|
+
import { byteAsFileSize, msAsDuration } from "@jsenv/log";
|
|
2
2
|
|
|
3
3
|
export const formatMetricValue = ({ value, unit }) => {
|
|
4
|
-
return formatters[unit](value)
|
|
5
|
-
}
|
|
4
|
+
return formatters[unit](value);
|
|
5
|
+
};
|
|
6
6
|
|
|
7
7
|
const formatters = {
|
|
8
8
|
ms: msAsDuration,
|
|
9
9
|
byte: byteAsFileSize,
|
|
10
10
|
undefined: (value) => value,
|
|
11
|
-
}
|
|
11
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { formatMetricValue } from "./formatMetricValue.js"
|
|
1
|
+
import { formatMetricValue } from "./formatMetricValue.js";
|
|
2
2
|
|
|
3
3
|
export const formatMetricsLog = (metrics) => {
|
|
4
|
-
const metricsReadable = {}
|
|
4
|
+
const metricsReadable = {};
|
|
5
5
|
Object.keys(metrics).forEach((metricName) => {
|
|
6
|
-
metricsReadable[metricName] = formatMetricValue(metrics[metricName])
|
|
7
|
-
})
|
|
8
|
-
return JSON.stringify(metricsReadable, null, " ")
|
|
9
|
-
}
|
|
6
|
+
metricsReadable[metricName] = formatMetricValue(metrics[metricName]);
|
|
7
|
+
});
|
|
8
|
+
return JSON.stringify(metricsReadable, null, " ");
|
|
9
|
+
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { setRoundedPrecision } from "@jsenv/log"
|
|
1
|
+
import { setRoundedPrecision } from "@jsenv/log";
|
|
2
2
|
|
|
3
3
|
export const formatRatioAsPercentage = (ratio) => {
|
|
4
|
-
const percentage = ratio * 100
|
|
4
|
+
const percentage = ratio * 100;
|
|
5
5
|
return `${percentage < 0 ? `-` : "+"}${setRoundedPrecision(
|
|
6
6
|
Math.abs(percentage),
|
|
7
7
|
{
|
|
8
8
|
decimals: 0,
|
|
9
9
|
decimalsWhenSmall: 1,
|
|
10
10
|
},
|
|
11
|
-
)}
|
|
12
|
-
}
|
|
11
|
+
)}%`;
|
|
12
|
+
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createDetailedMessage } from "@jsenv/log"
|
|
1
|
+
import { createDetailedMessage } from "@jsenv/log";
|
|
2
2
|
|
|
3
|
-
import { assertMetrics } from "./assertions.js"
|
|
3
|
+
import { assertMetrics } from "./assertions.js";
|
|
4
4
|
|
|
5
5
|
export const measureMultipleTimes = async (
|
|
6
6
|
measure,
|
|
@@ -8,53 +8,53 @@ export const measureMultipleTimes = async (
|
|
|
8
8
|
{ msToWaitBetweenEachIteration = 0 } = {},
|
|
9
9
|
) => {
|
|
10
10
|
if (typeof measure !== "function") {
|
|
11
|
-
throw new TypeError(`measure must be a function, received ${measure}`)
|
|
11
|
+
throw new TypeError(`measure must be a function, received ${measure}`);
|
|
12
12
|
}
|
|
13
13
|
if (typeof iterationCount !== "number") {
|
|
14
14
|
throw new TypeError(
|
|
15
15
|
`iterationCount must be a number, received ${iterationCount}`,
|
|
16
|
-
)
|
|
16
|
+
);
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const firstIterationMetrics = await measure()
|
|
20
|
-
assertMetrics(firstIterationMetrics)
|
|
21
|
-
const firstIterationMetricNames = Object.keys(firstIterationMetrics)
|
|
19
|
+
const firstIterationMetrics = await measure();
|
|
20
|
+
assertMetrics(firstIterationMetrics);
|
|
21
|
+
const firstIterationMetricNames = Object.keys(firstIterationMetrics);
|
|
22
22
|
if (firstIterationMetricNames.length === 0) {
|
|
23
23
|
throw new Error(
|
|
24
24
|
`measure must return a non empty object, received an object without key`,
|
|
25
|
-
)
|
|
25
|
+
);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const metricsWithIteration = {}
|
|
28
|
+
const metricsWithIteration = {};
|
|
29
29
|
firstIterationMetricNames.forEach((metricName) => {
|
|
30
|
-
const metric = firstIterationMetrics[metricName]
|
|
30
|
+
const metric = firstIterationMetrics[metricName];
|
|
31
31
|
metricsWithIteration[metricName] = {
|
|
32
32
|
values: [metric.value],
|
|
33
33
|
unit: metric.unit,
|
|
34
|
-
}
|
|
35
|
-
})
|
|
34
|
+
};
|
|
35
|
+
});
|
|
36
36
|
|
|
37
37
|
if (iterationCount === 1) {
|
|
38
|
-
return metricsWithIteration
|
|
38
|
+
return metricsWithIteration;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
const iterationArray = new Array(iterationCount - 1).fill()
|
|
41
|
+
const iterationArray = new Array(iterationCount - 1).fill();
|
|
42
42
|
await iterationArray.reduce(async (previous, _, index) => {
|
|
43
|
-
await previous
|
|
43
|
+
await previous;
|
|
44
44
|
|
|
45
|
-
const currentMetrics = await measure()
|
|
46
|
-
assertMetrics(currentMetrics)
|
|
47
|
-
const currentIterationMetricNames = Object.keys(currentMetrics)
|
|
45
|
+
const currentMetrics = await measure();
|
|
46
|
+
assertMetrics(currentMetrics);
|
|
47
|
+
const currentIterationMetricNames = Object.keys(currentMetrics);
|
|
48
48
|
const missingMetricNamesInCurrentIteration =
|
|
49
49
|
firstIterationMetricNames.filter(
|
|
50
50
|
(firstIterationMetricName) =>
|
|
51
51
|
!currentIterationMetricNames.includes(firstIterationMetricName),
|
|
52
|
-
)
|
|
52
|
+
);
|
|
53
53
|
const extraMetricNamesInCurrentIteration =
|
|
54
54
|
currentIterationMetricNames.filter(
|
|
55
55
|
(currentIterationMetricName) =>
|
|
56
56
|
!firstIterationMetricNames.includes(currentIterationMetricName),
|
|
57
|
-
)
|
|
57
|
+
);
|
|
58
58
|
if (
|
|
59
59
|
missingMetricNamesInCurrentIteration.length ||
|
|
60
60
|
extraMetricNamesInCurrentIteration.length
|
|
@@ -65,12 +65,12 @@ export const measureMultipleTimes = async (
|
|
|
65
65
|
extraMetricNamesInCurrentIteration,
|
|
66
66
|
iterationIndex: index,
|
|
67
67
|
}),
|
|
68
|
-
)
|
|
68
|
+
);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
firstIterationMetricNames.forEach((metricName) => {
|
|
72
|
-
const metricWithIteration = metricsWithIteration[metricName]
|
|
73
|
-
const currentMetric = currentMetrics[metricName]
|
|
72
|
+
const metricWithIteration = metricsWithIteration[metricName];
|
|
73
|
+
const currentMetric = currentMetrics[metricName];
|
|
74
74
|
if (currentMetric.unit !== metricWithIteration.unit) {
|
|
75
75
|
throw new Error(
|
|
76
76
|
createDetailedMessage(
|
|
@@ -81,19 +81,19 @@ export const measureMultipleTimes = async (
|
|
|
81
81
|
"metric name": metricName,
|
|
82
82
|
},
|
|
83
83
|
),
|
|
84
|
-
)
|
|
84
|
+
);
|
|
85
85
|
}
|
|
86
|
-
metricWithIteration.values.push(currentMetric.value)
|
|
87
|
-
})
|
|
86
|
+
metricWithIteration.values.push(currentMetric.value);
|
|
87
|
+
});
|
|
88
88
|
|
|
89
89
|
// await a little bit to let previous execution the time to potentially clean up things
|
|
90
90
|
await new Promise((resolve) => {
|
|
91
|
-
setTimeout(resolve, msToWaitBetweenEachIteration)
|
|
92
|
-
})
|
|
93
|
-
}, Promise.resolve())
|
|
91
|
+
setTimeout(resolve, msToWaitBetweenEachIteration);
|
|
92
|
+
});
|
|
93
|
+
}, Promise.resolve());
|
|
94
94
|
|
|
95
|
-
return metricsWithIteration
|
|
96
|
-
}
|
|
95
|
+
return metricsWithIteration;
|
|
96
|
+
};
|
|
97
97
|
|
|
98
98
|
const createVariableMetricNamesErrorMessage = ({
|
|
99
99
|
missingMetricNamesInCurrentIteration,
|
|
@@ -116,5 +116,5 @@ const createVariableMetricNamesErrorMessage = ({
|
|
|
116
116
|
}
|
|
117
117
|
: {}),
|
|
118
118
|
},
|
|
119
|
-
)
|
|
120
|
-
}
|
|
119
|
+
);
|
|
120
|
+
};
|
package/src/internal/median.js
CHANGED
|
@@ -8,21 +8,21 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
export const median = (numbers) => {
|
|
11
|
-
const numberCount = numbers.length
|
|
12
|
-
const numbersSorted = numbers.slice()
|
|
13
|
-
numbersSorted.sort()
|
|
11
|
+
const numberCount = numbers.length;
|
|
12
|
+
const numbersSorted = numbers.slice();
|
|
13
|
+
numbersSorted.sort();
|
|
14
14
|
|
|
15
|
-
const isEven = numberCount % 2 === 0
|
|
15
|
+
const isEven = numberCount % 2 === 0;
|
|
16
16
|
if (isEven) {
|
|
17
|
-
const rightMiddleNumberIndex = numberCount / 2
|
|
18
|
-
const leftMiddleNumberIndex = rightMiddleNumberIndex - 1
|
|
19
|
-
const leftMiddleNumber = numbersSorted[leftMiddleNumberIndex]
|
|
20
|
-
const rightMiddleNumber = numbersSorted[rightMiddleNumberIndex]
|
|
21
|
-
const medianNumber = (leftMiddleNumber + rightMiddleNumber) / 2
|
|
22
|
-
return medianNumber
|
|
17
|
+
const rightMiddleNumberIndex = numberCount / 2;
|
|
18
|
+
const leftMiddleNumberIndex = rightMiddleNumberIndex - 1;
|
|
19
|
+
const leftMiddleNumber = numbersSorted[leftMiddleNumberIndex];
|
|
20
|
+
const rightMiddleNumber = numbersSorted[rightMiddleNumberIndex];
|
|
21
|
+
const medianNumber = (leftMiddleNumber + rightMiddleNumber) / 2;
|
|
22
|
+
return medianNumber;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const medianNumberIndex = (numberCount - 1) / 2
|
|
26
|
-
const medianNumber = numbersSorted[medianNumberIndex]
|
|
27
|
-
return medianNumber
|
|
28
|
-
}
|
|
25
|
+
const medianNumberIndex = (numberCount - 1) / 2;
|
|
26
|
+
const medianNumber = numbersSorted[medianNumberIndex];
|
|
27
|
+
return medianNumber;
|
|
28
|
+
};
|
package/src/main.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { readGitHubWorkflowEnv } from "@jsenv/github-pull-request-impact"
|
|
1
|
+
export { readGitHubWorkflowEnv } from "@jsenv/github-pull-request-impact";
|
|
2
2
|
|
|
3
|
-
export { startMeasures } from "./startMeasures.js"
|
|
4
|
-
export { importMetricFromFiles } from "./importMetricFromFiles.js"
|
|
5
|
-
export { reportPerformanceImpact } from "./reportPerformanceImpact.js"
|
|
3
|
+
export { startMeasures } from "./startMeasures.js";
|
|
4
|
+
export { importMetricFromFiles } from "./importMetricFromFiles.js";
|
|
5
|
+
export { reportPerformanceImpact } from "./reportPerformanceImpact.js";
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem"
|
|
2
|
-
import { commentGitHubPullRequestImpact } from "@jsenv/github-pull-request-impact"
|
|
3
|
-
import { importOneExportFromFile } from "@jsenv/dynamic-import-worker"
|
|
1
|
+
import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem";
|
|
2
|
+
import { commentGitHubPullRequestImpact } from "@jsenv/github-pull-request-impact";
|
|
3
|
+
import { importOneExportFromFile } from "@jsenv/dynamic-import-worker";
|
|
4
4
|
|
|
5
|
-
import { assertPerformanceReport } from "./internal/assertions.js"
|
|
6
|
-
import { createPerfImpactComment } from "./internal/comment/createPerfImpactComment.js"
|
|
7
|
-
import { jsenvCommentParameters } from "./internal/comment/jsenvCommentParameters.js"
|
|
5
|
+
import { assertPerformanceReport } from "./internal/assertions.js";
|
|
6
|
+
import { createPerfImpactComment } from "./internal/comment/createPerfImpactComment.js";
|
|
7
|
+
import { jsenvCommentParameters } from "./internal/comment/jsenvCommentParameters.js";
|
|
8
8
|
|
|
9
9
|
export const reportPerformanceImpact = async ({
|
|
10
10
|
logLevel,
|
|
@@ -26,14 +26,14 @@ export const reportPerformanceImpact = async ({
|
|
|
26
26
|
runLink,
|
|
27
27
|
commitInGeneratedByInfo,
|
|
28
28
|
}) => {
|
|
29
|
-
rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl)
|
|
29
|
+
rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl);
|
|
30
30
|
if (performanceReportUrl === "string") {
|
|
31
|
-
performanceReportUrl = new URL(performanceReportUrl, rootDirectoryUrl).href
|
|
31
|
+
performanceReportUrl = new URL(performanceReportUrl, rootDirectoryUrl).href;
|
|
32
32
|
} else if (performanceReportUrl instanceof URL) {
|
|
33
33
|
} else {
|
|
34
34
|
throw new TypeError(
|
|
35
35
|
`performanceReportUrl must be a string or an url but received ${performanceReportUrl}`,
|
|
36
|
-
)
|
|
36
|
+
);
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
return commentGitHubPullRequestImpact({
|
|
@@ -48,12 +48,12 @@ export const reportPerformanceImpact = async ({
|
|
|
48
48
|
pullRequestNumber,
|
|
49
49
|
|
|
50
50
|
collectInfo: async ({ execCommandInRootDirectory }) => {
|
|
51
|
-
await execCommandInRootDirectory(installCommand)
|
|
51
|
+
await execCommandInRootDirectory(installCommand);
|
|
52
52
|
const performanceReport = await importOneExportFromFile(
|
|
53
53
|
performanceReportUrl,
|
|
54
|
-
)
|
|
55
|
-
assertPerformanceReport(performanceReport)
|
|
56
|
-
return { version: 1, data: performanceReport }
|
|
54
|
+
);
|
|
55
|
+
assertPerformanceReport(performanceReport);
|
|
56
|
+
return { version: 1, data: performanceReport };
|
|
57
57
|
},
|
|
58
58
|
commentIdentifier: `<!-- Generated by @jsenv/performance-impact -->`,
|
|
59
59
|
createCommentForComparison: ({
|
|
@@ -70,13 +70,13 @@ export const reportPerformanceImpact = async ({
|
|
|
70
70
|
isPerformanceImpactBig,
|
|
71
71
|
formatGroupSummary,
|
|
72
72
|
formatPerformanceImpactCell,
|
|
73
|
-
})
|
|
73
|
+
});
|
|
74
74
|
},
|
|
75
75
|
generatedByLink: {
|
|
76
|
-
url: "https://github.com/jsenv/workflow/tree/main/packages/
|
|
76
|
+
url: "https://github.com/jsenv/workflow/tree/main/packages/performance-impact",
|
|
77
77
|
text: "@jsenv/performance-impact",
|
|
78
78
|
},
|
|
79
79
|
runLink,
|
|
80
80
|
commitInGeneratedByInfo,
|
|
81
|
-
})
|
|
82
|
-
}
|
|
81
|
+
});
|
|
82
|
+
};
|
package/src/startMeasures.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { resourceUsage, memoryUsage, emitWarning } from "node:process"
|
|
2
|
-
import v8 from "node:v8"
|
|
3
|
-
import { runInNewContext } from "node:vm"
|
|
1
|
+
import { resourceUsage, memoryUsage, emitWarning } from "node:process";
|
|
2
|
+
import v8 from "node:v8";
|
|
3
|
+
import { runInNewContext } from "node:vm";
|
|
4
4
|
|
|
5
5
|
export const startMeasures = ({
|
|
6
6
|
duration = true, // will be in milliseconds
|
|
@@ -9,48 +9,49 @@ export const startMeasures = ({
|
|
|
9
9
|
gc = false,
|
|
10
10
|
} = {}) => {
|
|
11
11
|
if (gc && !global.gc) {
|
|
12
|
-
v8.setFlagsFromString("--expose_gc")
|
|
13
|
-
global.gc = runInNewContext("gc")
|
|
12
|
+
v8.setFlagsFromString("--expose_gc");
|
|
13
|
+
global.gc = runInNewContext("gc");
|
|
14
14
|
emitWarning(`node must be started with --expose-gc`, {
|
|
15
15
|
CODE: "EXPOSE_GC_IS_MISSING",
|
|
16
16
|
detail: "global.gc forced with v8.setFlagsFromString",
|
|
17
|
-
})
|
|
17
|
+
});
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
const measures = []
|
|
20
|
+
const measures = [];
|
|
21
21
|
if (duration) {
|
|
22
|
-
const beforeMs = Date.now()
|
|
22
|
+
const beforeMs = Date.now();
|
|
23
23
|
measures.push(() => {
|
|
24
|
-
const afterMs = Date.now()
|
|
25
|
-
const duration = afterMs - beforeMs
|
|
24
|
+
const afterMs = Date.now();
|
|
25
|
+
const duration = afterMs - beforeMs;
|
|
26
26
|
return {
|
|
27
27
|
duration,
|
|
28
|
-
}
|
|
29
|
-
})
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
30
|
}
|
|
31
31
|
if (filesystem) {
|
|
32
|
-
const beforeRessourceUsage = resourceUsage()
|
|
32
|
+
const beforeRessourceUsage = resourceUsage();
|
|
33
33
|
measures.push(() => {
|
|
34
|
-
const afterRessourceUsage = resourceUsage()
|
|
35
|
-
const fsRead = afterRessourceUsage.fsRead - beforeRessourceUsage.fsRead
|
|
36
|
-
const fsWrite =
|
|
34
|
+
const afterRessourceUsage = resourceUsage();
|
|
35
|
+
const fsRead = afterRessourceUsage.fsRead - beforeRessourceUsage.fsRead;
|
|
36
|
+
const fsWrite =
|
|
37
|
+
afterRessourceUsage.fsWrite - beforeRessourceUsage.fsWrite;
|
|
37
38
|
return {
|
|
38
39
|
fsRead,
|
|
39
40
|
fsWrite,
|
|
40
|
-
}
|
|
41
|
-
})
|
|
41
|
+
};
|
|
42
|
+
});
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
if (memoryHeap) {
|
|
45
46
|
if (gc) {
|
|
46
|
-
global.gc()
|
|
47
|
+
global.gc();
|
|
47
48
|
}
|
|
48
|
-
const beforeMemoryUsage = memoryUsage()
|
|
49
|
+
const beforeMemoryUsage = memoryUsage();
|
|
49
50
|
measures.push(() => {
|
|
50
51
|
if (gc) {
|
|
51
|
-
global.gc()
|
|
52
|
+
global.gc();
|
|
52
53
|
}
|
|
53
|
-
const afterMemoryUsage = memoryUsage()
|
|
54
|
+
const afterMemoryUsage = memoryUsage();
|
|
54
55
|
return {
|
|
55
56
|
// total means "allocated"
|
|
56
57
|
memoryHeapTotal:
|
|
@@ -60,20 +61,20 @@ export const startMeasures = ({
|
|
|
60
61
|
rss: afterMemoryUsage.rss - beforeMemoryUsage.rss,
|
|
61
62
|
arrayBuffers:
|
|
62
63
|
afterMemoryUsage.arrayBuffers - beforeMemoryUsage.arrayBuffers,
|
|
63
|
-
}
|
|
64
|
-
})
|
|
64
|
+
};
|
|
65
|
+
});
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
const stop = () => {
|
|
68
|
-
let metrics = {}
|
|
69
|
+
let metrics = {};
|
|
69
70
|
measures.forEach((measure) => {
|
|
70
71
|
metrics = {
|
|
71
72
|
...metrics,
|
|
72
73
|
...measure(),
|
|
73
|
-
}
|
|
74
|
-
})
|
|
75
|
-
return metrics
|
|
76
|
-
}
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
return metrics;
|
|
77
|
+
};
|
|
77
78
|
|
|
78
|
-
return { stop }
|
|
79
|
-
}
|
|
79
|
+
return { stop };
|
|
80
|
+
};
|