@jsenv/performance-impact 2.3.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsenv/performance-impact",
3
- "version": "2.3.0",
3
+ "version": "3.0.0",
4
4
  "description": "Report pull request impacts on performance metrics",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -23,28 +23,22 @@
23
23
  "type": "module",
24
24
  "exports": {
25
25
  ".": {
26
- "import": "./main.js"
26
+ "import": "./src/main.js"
27
27
  },
28
28
  "./*": "./*"
29
29
  },
30
- "main": "./main.js",
30
+ "main": "./src/main.js",
31
31
  "files": [
32
- "/src/",
33
- "/main.js"
32
+ "/src/"
34
33
  ],
35
34
  "scripts": {
36
- "eslint": "npx eslint . --ext=.js,.mjs",
37
- "snapshot": "node ./test/comment_snapshot/generate_comment_snapshot_file.mjs",
38
- "test": "node ./script/test/test.mjs",
39
- "test-with-coverage": "npm run test -- --coverage",
40
- "prettier": "prettier --write ."
35
+ "snapshot": "node ./tests/comment_snapshot/generate_comment_snapshot_file.mjs",
36
+ "test": "node ./scripts/test.mjs"
41
37
  },
42
38
  "dependencies": {
43
- "@jsenv/dynamic-import-worker": "1.0.1",
44
- "@jsenv/filesystem": "3.1.0",
45
- "@jsenv/github-pull-request-impact": "1.6.11",
46
- "@jsenv/logger": "4.0.1",
47
- "bytes": "3.1.1",
48
- "humanize-duration": "3.27.1"
39
+ "@jsenv/dynamic-import-worker": "1.1.0",
40
+ "@jsenv/filesystem": "4.1.0",
41
+ "@jsenv/github-pull-request-impact": "1.7.0",
42
+ "@jsenv/log": "2.1.0"
49
43
  }
50
44
  }
@@ -1,5 +1,5 @@
1
- import { assertAndNormalizeDirectoryUrl, resolveUrl } from "@jsenv/filesystem"
2
- import { createLogger } from "@jsenv/logger"
1
+ import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem"
2
+ import { createLogger } from "@jsenv/log"
3
3
  import { importOneExportFromFile } from "@jsenv/dynamic-import-worker"
4
4
 
5
5
  import { measureMultipleTimes } from "./internal/measureMultipleTimes.js"
@@ -27,7 +27,7 @@ export const importMetricFromFiles = async ({
27
27
  iterations,
28
28
  msToWaitBetweenEachIteration = 100,
29
29
  } = metricsDescription
30
- const url = resolveUrl(file, directoryUrl)
30
+ const url = new URL(file, directoryUrl).href
31
31
 
32
32
  const measure = async () => {
33
33
  const metrics = await importOneExportFromFile(url)
@@ -1,8 +1,10 @@
1
- import { createDetailedMessage } from "@jsenv/logger"
1
+ import { createDetailedMessage } from "@jsenv/log"
2
2
 
3
3
  export const assertPerformanceReport = (performanceReport) => {
4
4
  if (typeof performanceReport !== "object" || performanceReport === null) {
5
- throw new TypeError(`performanceReport must be an object, received ${performanceReport}.`)
5
+ throw new TypeError(
6
+ `performanceReport must be an object, received ${performanceReport}.`,
7
+ )
6
8
  }
7
9
 
8
10
  Object.keys(performanceReport).forEach((groupName) => {
@@ -23,12 +25,16 @@ export const assertMetrics = (metrics, metricsTrace) => {
23
25
  Object.keys(metrics).forEach((metricName) => {
24
26
  const metric = metrics[metricName]
25
27
  if (typeof metric !== "object") {
26
- throw new TypeError(`metric must be an object, got ${metric} for ${metricName}`)
28
+ throw new TypeError(
29
+ `metric must be an object, got ${metric} for ${metricName}`,
30
+ )
27
31
  }
28
32
 
29
33
  const { value } = metric
30
34
  if (typeof value !== "number") {
31
- throw new TypeError(`metric value must be a number, got ${value} for ${metricName}`)
35
+ throw new TypeError(
36
+ `metric value must be a number, got ${value} for ${metricName}`,
37
+ )
32
38
  }
33
39
 
34
40
  const { unit } = metric
@@ -1,7 +1,11 @@
1
1
  import { formatMetricValue } from "../formatMetricValue.js"
2
2
  import { formatRatioAsPercentage } from "../formatRatio.js"
3
3
 
4
- export const formatImpact = ({ metricValueBeforeMerge, metricValueAfterMerge, metricUnit }) => {
4
+ export const formatImpact = ({
5
+ metricValueBeforeMerge,
6
+ metricValueAfterMerge,
7
+ metricUnit,
8
+ }) => {
5
9
  const diff = metricValueAfterMerge - metricValueBeforeMerge
6
10
 
7
11
  if (diff === 0) {
@@ -16,7 +20,10 @@ export const formatImpact = ({ metricValueBeforeMerge, metricValueAfterMerge, me
16
20
  return diffFormatted
17
21
  }
18
22
 
19
- export const formatImpactAsPercentage = ({ metricValueBeforeMerge, metricValueAfterMerge }) => {
23
+ export const formatImpactAsPercentage = ({
24
+ metricValueBeforeMerge,
25
+ metricValueAfterMerge,
26
+ }) => {
20
27
  const diff = metricValueAfterMerge - metricValueBeforeMerge
21
28
  const diffRatio =
22
29
  metricValueBeforeMerge === 0
@@ -1,11 +1,18 @@
1
1
  import { formatRatioAsPercentage } from "../formatRatio.js"
2
2
 
3
- export const jsenvFormatGroupSummary = ({ groupName, beforeMergeMetrics, afterMergeMetrics }) => {
3
+ export const jsenvFormatGroupSummary = ({
4
+ groupName,
5
+ beforeMergeMetrics,
6
+ afterMergeMetrics,
7
+ }) => {
4
8
  if (!beforeMergeMetrics) {
5
9
  return `${groupName} (new)`
6
10
  }
7
11
 
8
- const allRatios = getAllImpactRatios({ beforeMergeMetrics, afterMergeMetrics })
12
+ const allRatios = getAllImpactRatios({
13
+ beforeMergeMetrics,
14
+ afterMergeMetrics,
15
+ })
9
16
  const ratiosCount = allRatios.length
10
17
  if (ratiosCount === 0) {
11
18
  return `${groupName} (no impact)`
@@ -31,7 +38,11 @@ const getAllImpactRatios = ({ afterMergeMetrics, beforeMergeMetrics }) => {
31
38
  const metricValueBeforeMerge = metricBeforeMerge.value
32
39
  const metricValueAfterMerge = metricAfterMerge.value
33
40
  const metricDiff = metricValueAfterMerge - metricValueBeforeMerge
34
- if (metricDiff === 0 && metricValueAfterMerge === 0 && metricValueBeforeMerge === 0) {
41
+ if (
42
+ metricDiff === 0 &&
43
+ metricValueAfterMerge === 0 &&
44
+ metricValueBeforeMerge === 0
45
+ ) {
35
46
  // there is no impact on a metric that is 0
36
47
  // we can ignore this metric
37
48
  return
@@ -1,6 +1,10 @@
1
- export const jsenvIsPerformanceImpactBig = ({ metricValueBeforeMerge, metricValueDelta }) => {
1
+ export const jsenvIsPerformanceImpactBig = ({
2
+ metricValueBeforeMerge,
3
+ metricValueDelta,
4
+ }) => {
2
5
  const absoluteDelta = Math.abs(metricValueDelta)
3
- const absoluteDeltaAsRatio = absoluteDelta === 0 ? 0 : absoluteDelta / metricValueBeforeMerge
6
+ const absoluteDeltaAsRatio =
7
+ absoluteDelta === 0 ? 0 : absoluteDelta / metricValueBeforeMerge
4
8
  const absoluteDeltaAsPercentage = absoluteDeltaAsRatio * 100
5
9
 
6
10
  // absolute diff as percentage is below 5% -> not big
@@ -47,7 +47,9 @@ const renderPerfImpactTableBody = ({
47
47
  const metricNameDisplayed = truncateMetricName(metricName)
48
48
  const metric = afterMergeMetrics[metricName]
49
49
  const metricValueAfterMerge = metric.value
50
- const metricBeforeMerge = beforeMergeMetrics ? beforeMergeMetrics[metricName] : undefined
50
+ const metricBeforeMerge = beforeMergeMetrics
51
+ ? beforeMergeMetrics[metricName]
52
+ : undefined
51
53
  if (!metricBeforeMerge) {
52
54
  lines.push([
53
55
  `<td nowrap>${metricNameDisplayed}</td>`,
@@ -84,14 +86,19 @@ const renderPerfImpactTableBody = ({
84
86
  })
85
87
  if (metricNames !== metricAllNames) {
86
88
  lines.push([
87
- `<td colspan="5" align="center">... ${metricCount - MAX_METRIC_PER_GROUP} more ...</td>`,
89
+ `<td colspan="5" align="center">... ${
90
+ metricCount - MAX_METRIC_PER_GROUP
91
+ } more ...</td>`,
88
92
  ])
89
93
  }
90
94
 
91
95
  return renderTableLines(lines)
92
96
  }
93
97
 
94
- const renderEmojiCellContent = ({ metricValueAfterMerge, metricValueBeforeMerge }) => {
98
+ const renderEmojiCellContent = ({
99
+ metricValueAfterMerge,
100
+ metricValueBeforeMerge,
101
+ }) => {
95
102
  const delta = metricValueAfterMerge - metricValueBeforeMerge
96
103
  if (delta === 0) {
97
104
  return ":ghost:"
@@ -1,12 +1,11 @@
1
- import { formatMs } from "./formatMs.js"
2
- import { formatByte } from "./formatByte.js"
1
+ import { byteAsFileSize, msAsDuration } from "@jsenv/log"
3
2
 
4
3
  export const formatMetricValue = ({ value, unit }) => {
5
4
  return formatters[unit](value)
6
5
  }
7
6
 
8
7
  const formatters = {
9
- ms: formatMs,
10
- byte: formatByte,
8
+ ms: msAsDuration,
9
+ byte: byteAsFileSize,
11
10
  undefined: (value) => value,
12
11
  }
@@ -1,6 +1,9 @@
1
- import { formatPercentage } from "./format_percentage.js"
1
+ import { setRoundedPrecision } from "@jsenv/log"
2
2
 
3
3
  export const formatRatioAsPercentage = (ratio) => {
4
- const asPercentageFormatted = formatPercentage(ratio * 100)
5
- return asPercentageFormatted
4
+ const percentage = ratio * 100
5
+ return `${percentage < 0 ? `-` : "+"}${setRoundedPrecision(percentage, {
6
+ decimals: 0,
7
+ decimalsWhenSmall: 1,
8
+ })}%`
6
9
  }
@@ -1,4 +1,4 @@
1
- import { createDetailedMessage } from "@jsenv/logger"
1
+ import { createDetailedMessage } from "@jsenv/log"
2
2
 
3
3
  import { assertMetrics } from "./assertions.js"
4
4
 
@@ -11,14 +11,18 @@ export const measureMultipleTimes = async (
11
11
  throw new TypeError(`measure must be a function, received ${measure}`)
12
12
  }
13
13
  if (typeof iterationCount !== "number") {
14
- throw new TypeError(`iterationCount must be a number, received ${iterationCount}`)
14
+ throw new TypeError(
15
+ `iterationCount must be a number, received ${iterationCount}`,
16
+ )
15
17
  }
16
18
 
17
19
  const firstIterationMetrics = await measure()
18
20
  assertMetrics(firstIterationMetrics)
19
21
  const firstIterationMetricNames = Object.keys(firstIterationMetrics)
20
22
  if (firstIterationMetricNames.length === 0) {
21
- throw new Error(`measure must return a non empty object, received an object without key`)
23
+ throw new Error(
24
+ `measure must return a non empty object, received an object without key`,
25
+ )
22
26
  }
23
27
 
24
28
  const metricsWithIteration = {}
@@ -41,14 +45,20 @@ export const measureMultipleTimes = async (
41
45
  const currentMetrics = await measure()
42
46
  assertMetrics(currentMetrics)
43
47
  const currentIterationMetricNames = Object.keys(currentMetrics)
44
- const missingMetricNamesInCurrentIteration = firstIterationMetricNames.filter(
45
- (firstIterationMetricName) => !currentIterationMetricNames.includes(firstIterationMetricName),
46
- )
47
- const extraMetricNamesInCurrentIteration = currentIterationMetricNames.filter(
48
- (currentIterationMetricName) =>
49
- !firstIterationMetricNames.includes(currentIterationMetricName),
50
- )
51
- if (missingMetricNamesInCurrentIteration.length || extraMetricNamesInCurrentIteration.length) {
48
+ const missingMetricNamesInCurrentIteration =
49
+ firstIterationMetricNames.filter(
50
+ (firstIterationMetricName) =>
51
+ !currentIterationMetricNames.includes(firstIterationMetricName),
52
+ )
53
+ const extraMetricNamesInCurrentIteration =
54
+ currentIterationMetricNames.filter(
55
+ (currentIterationMetricName) =>
56
+ !firstIterationMetricNames.includes(currentIterationMetricName),
57
+ )
58
+ if (
59
+ missingMetricNamesInCurrentIteration.length ||
60
+ extraMetricNamesInCurrentIteration.length
61
+ ) {
52
62
  throw new Error(
53
63
  createVariableMetricNamesErrorMessage({
54
64
  missingMetricNamesInCurrentIteration,
@@ -63,11 +73,14 @@ export const measureMultipleTimes = async (
63
73
  const currentMetric = currentMetrics[metricName]
64
74
  if (currentMetric.unit !== metricWithIteration.unit) {
65
75
  throw new Error(
66
- createDetailedMessage(`A metric unit has changed between iterations.`, {
67
- "metric unit on first iteration": metricWithIteration.unit,
68
- [`metric unit on iteration ${index + 1}`]: currentMetric.unit,
69
- "metric name": metricName,
70
- }),
76
+ createDetailedMessage(
77
+ `A metric unit has changed between iterations.`,
78
+ {
79
+ "metric unit on first iteration": metricWithIteration.unit,
80
+ [`metric unit on iteration ${index + 1}`]: currentMetric.unit,
81
+ "metric name": metricName,
82
+ },
83
+ ),
71
84
  )
72
85
  }
73
86
  metricWithIteration.values.push(currentMetric.value)
package/src/main.js ADDED
@@ -0,0 +1,5 @@
1
+ export { readGitHubWorkflowEnv } from "@jsenv/github-pull-request-impact"
2
+
3
+ export { startMeasures } from "./startMeasures.js"
4
+ export { importMetricFromFiles } from "./importMetricFromFiles.js"
5
+ export { reportPerformanceImpact } from "./reportPerformanceImpact.js"
@@ -18,7 +18,7 @@ export const reportPerformanceImpact = async ({
18
18
  pullRequestNumber,
19
19
 
20
20
  installCommand = "npm install",
21
- performanceReportPath,
21
+ performanceReportUrl,
22
22
  isPerformanceImpactBig = jsenvCommentParameters.isPerformanceImpactBig,
23
23
  formatGroupSummary = jsenvCommentParameters.formatGroupSummary,
24
24
  formatPerformanceImpactCell = jsenvCommentParameters.formatPerformanceImpactCell,
@@ -27,14 +27,12 @@ export const reportPerformanceImpact = async ({
27
27
  commitInGeneratedByInfo,
28
28
  }) => {
29
29
  rootDirectoryUrl = assertAndNormalizeDirectoryUrl(rootDirectoryUrl)
30
- let performanceReportUrl
31
- if (performanceReportPath === "string") {
32
- performanceReportUrl = new URL(performanceReportPath, rootDirectoryUrl).href
33
- } else if (performanceReportPath instanceof URL) {
34
- performanceReportUrl = performanceReportPath.href
30
+ if (performanceReportUrl === "string") {
31
+ performanceReportUrl = new URL(performanceReportUrl, rootDirectoryUrl).href
32
+ } else if (performanceReportUrl instanceof URL) {
35
33
  } else {
36
34
  throw new TypeError(
37
- `performanceReportPath must be a string or an url but received ${performanceReportPath}`,
35
+ `performanceReportUrl must be a string or an url but received ${performanceReportUrl}`,
38
36
  )
39
37
  }
40
38
 
@@ -34,7 +34,8 @@ export const startMeasures = ({
34
34
  const beforeRessourceUsage = resourceUsage()
35
35
  measures.push(() => {
36
36
  const afterRessourceUsage = resourceUsage()
37
- const fileSystemReadOperationCount = afterRessourceUsage.fsRead - beforeRessourceUsage.fsRead
37
+ const fileSystemReadOperationCount =
38
+ afterRessourceUsage.fsRead - beforeRessourceUsage.fsRead
38
39
  const fileSystemWriteOperationCount =
39
40
  afterRessourceUsage.fsWrite - beforeRessourceUsage.fsWrite
40
41
  return {
package/main.js DELETED
@@ -1,5 +0,0 @@
1
- export { startMeasures } from "./src/startMeasures.js"
2
- export { importMetricFromFiles } from "./src/importMetricFromFiles.js"
3
-
4
- export { readGitHubWorkflowEnv } from "@jsenv/github-pull-request-impact"
5
- export { reportPerformanceImpact } from "./src/reportPerformanceImpact.js"
@@ -1,10 +0,0 @@
1
- import { createRequire } from "node:module"
2
-
3
- const require = createRequire(import.meta.url)
4
-
5
- // https://github.com/visionmedia/bytes.js/
6
- const bytes = require("bytes")
7
-
8
- export const formatByte = (metricValue) => {
9
- return bytes(metricValue, { decimalPlaces: 2, unitSeparator: " " })
10
- }
@@ -1,13 +0,0 @@
1
- import { createRequire } from "node:module"
2
-
3
- const require = createRequire(import.meta.url)
4
-
5
- const humanizeDuration = require("humanize-duration")
6
-
7
- export const formatMs = (metricValue) => {
8
- return humanizeDuration(metricValue, {
9
- largest: 2,
10
- maxDecimalPoints: metricValue < 1 ? 4 : metricValue < 1000 ? 3 : 2,
11
- // units: ["s"]
12
- })
13
- }
@@ -1,30 +0,0 @@
1
- export const formatPercentage = (number) => {
2
- const absoluteValue = Math.abs(number)
3
- const valueRounded = roundNumber(
4
- absoluteValue,
5
- determineDecimals(absoluteValue),
6
- )
7
- return `${number < 0 ? `-` : "+"}${valueRounded}%`
8
- }
9
- const determineDecimals = (number) => {
10
- if (number < 0.001) {
11
- return 4
12
- }
13
- if (number < 0.01) {
14
- return 3
15
- }
16
- if (number < 0.1) {
17
- return 2
18
- }
19
- if (number < 1) {
20
- return 1
21
- }
22
- return 0
23
- }
24
-
25
- const roundNumber = (value, maxDecimals) => {
26
- const expValue = Math.pow(10, maxDecimals)
27
- const roundedValue = Math.round(expValue * value) / expValue
28
- const valueAsString = roundedValue.toFixed(maxDecimals)
29
- return parseFloat(valueAsString)
30
- }