eslint-rule-benchmark
Version:
Benchmark ESLint rules with detailed performance metrics for CI and plugin development
176 lines (175 loc) • 5.49 kB
JavaScript
import { collectSystemInfo } from './collect-system-info.js'
import { formatDeviation } from './format-deviation.js'
import { formatHz } from './format-hz.js'
import { formatMs } from './format-ms.js'
const MIN_COLUMN_WIDTH = 5
const CELL_PADDING = 1
const TABLE_HEADERS = [
'Sample',
'Ops/sec',
'Avg Time',
'Median',
'Min',
'Max',
'StdDev',
]
const EMPTY_ROW_VALUES = [
'No samples',
'N/A',
'N/A',
'N/A',
'N/A',
'N/A',
'N/A',
]
async function useConsoleReport(results, _userConfig) {
let outputLines = []
if (results.length === 0) {
return 'No benchmark results available.'
}
let uniformColumnWidths = calculateUniformColumnWidths(results)
for (let testSpecResult of results) {
if (testSpecResult.testCaseResults.length === 0) {
outputLines.push(
' No test cases found or all failed for this specification.',
)
continue
}
let tableRows = [[testSpecResult.name], TABLE_HEADERS]
let alignments = Array.from({ length: TABLE_HEADERS.length }).fill('left')
for (let testCaseResult of testSpecResult.testCaseResults) {
if (testCaseResult.samplesResults.length === 0) {
tableRows.push(EMPTY_ROW_VALUES)
continue
}
for (let sampleResult of testCaseResult.samplesResults) {
let sampleName = extractSampleName(
sampleResult.name,
testCaseResult.name,
)
tableRows.push(formatMetricsRow(sampleName, sampleResult))
}
}
outputLines.push(renderTable(tableRows, uniformColumnWidths, alignments))
}
let systemInfo = await collectSystemInfo()
outputLines.unshift('')
outputLines.push('', formatSystemInfo(systemInfo), '')
return outputLines.join('\n')
}
function renderTable(rows, fixedColumnWidths, columnAlignments) {
let columnCount = Math.max(...rows.map(row => row.length))
let leftPads = fixedColumnWidths.map((_, i) => (i === 0 ? 0 : CELL_PADDING))
let rightPads = fixedColumnWidths.map((_, i) =>
i === columnCount - 1 ? 0 : CELL_PADDING,
)
let separator = buildSeparator(fixedColumnWidths, leftPads, rightPads)
let tableWidth = separator.length
let lines = []
let processedRows = rows
if (processedRows[0] && processedRows[0].length === 1) {
lines.push(
separator,
padCell(processedRows[0][0], tableWidth, 'center'),
separator,
)
processedRows = processedRows.slice(1)
}
for (let row of processedRows) {
let rendered = row
.map((cell, col) => {
let alignment = columnAlignments?.[col]
let content = padCell(cell, fixedColumnWidths[col], alignment)
let leftSpace = ' '.repeat(leftPads[col])
let rightSpace = ' '.repeat(rightPads[col])
return leftSpace + content + rightSpace
})
.join('|')
lines.push(rendered)
}
if (processedRows.length > 0) {
lines.push(separator)
}
return lines.join('\n')
}
function calculateUniformColumnWidths(results) {
let columnWidths = TABLE_HEADERS.map(header => header.length)
for (let testSpecResult of results) {
for (let testCaseResult of testSpecResult.testCaseResults) {
if (testCaseResult.samplesResults.length === 0) {
for (let [i, value] of EMPTY_ROW_VALUES.entries()) {
columnWidths[i] = Math.max(columnWidths[i], value.length)
}
continue
}
for (let sampleResult of testCaseResult.samplesResults) {
let sampleName = extractSampleName(
sampleResult.name,
testCaseResult.name,
)
let rowValues = formatMetricsRow(sampleName, sampleResult)
for (let [i, value] of rowValues.entries()) {
columnWidths[i] = Math.max(columnWidths[i], value.length)
}
}
}
}
return columnWidths.map(width => Math.max(width, MIN_COLUMN_WIDTH))
}
function formatSystemInfo(systemInfo) {
let runTime = [
`Node.js ${systemInfo.nodeVersion}`,
`V8 ${systemInfo.v8Version}`,
`ESLint ${systemInfo.eslintVersion}`,
]
let platform = [
`${systemInfo.platform} ${systemInfo.arch} (${systemInfo.osRelease})`,
]
let hardware = [
`${systemInfo.cpuModel} (${systemInfo.cpuCount} cores, ${systemInfo.cpuSpeedMHz} MHz)`,
`${systemInfo.totalMemoryGb} GB RAM`,
]
let formatList = new Intl.ListFormat('en-US', {
type: 'conjunction',
style: 'narrow',
})
return [
'System Information:',
'',
`Runtime: ${formatList.format(runTime)}`,
`Platform: ${formatList.format(platform)}`,
`Hardware: ${formatList.format(hardware)}`,
].join('\n')
}
function padCell(value, targetWidth, alignment) {
if (value.length >= targetWidth) {
return value
}
let gap = targetWidth - value.length
if (alignment === 'left') {
return value + ' '.repeat(gap)
}
let left = Math.floor(gap / 2)
let right = gap - left
return ' '.repeat(left) + value + ' '.repeat(right)
}
function formatMetricsRow(sampleName, sample) {
return [
sampleName,
formatHz(sample.metrics.hz),
formatMs(sample.metrics.mean),
formatMs(sample.metrics.median),
formatMs(sample.metrics.min),
formatMs(sample.metrics.max),
formatDeviation(sample.metrics.stdDev),
]
}
function buildSeparator(columnWidths, leftPads, rightPads) {
return columnWidths
.map((width, i) => '-'.repeat(width + leftPads[i] + rightPads[i]))
.join('-')
}
function extractSampleName(fullName, testCaseName) {
return fullName.replace(`${testCaseName} on `, '')
}
export { useConsoleReport }