eslint-rule-benchmark
Version:
Benchmark ESLint rules with detailed performance metrics for CI and plugin development
138 lines (137 loc) • 4.76 kB
JavaScript
import fs from 'node:fs/promises'
import path from 'node:path'
async function validateConfig(config, configDirectory) {
let errors = []
if (
!config.tests ||
!Array.isArray(config.tests) ||
config.tests.length === 0
) {
errors.push(
'Configuration must include at least one test in the "tests" array.',
)
return errors
}
errors.push(...validateBaseBenchmarkSettings(config))
let validationPromises = config.tests.map(async (testSpec, testIndex) => {
let testSpecErrors = []
let testPrefix = `Test "${testSpec.name || `at index ${testIndex}`}"`
if (!testSpec.name) {
testSpecErrors.push(`Test at index ${testIndex}: "name" is required.`)
}
if (!testSpec.ruleId) {
testSpecErrors.push(`${testPrefix}: "ruleId" is required.`)
}
if (testSpec.rulePath) {
try {
let rulePath = path.resolve(configDirectory, testSpec.rulePath)
await fs.access(rulePath)
} catch {
testSpecErrors.push(
`${testPrefix}: Rule file not found at "${testSpec.rulePath}".`,
)
}
} else {
testSpecErrors.push(`${testPrefix}: "rulePath" is required.`)
}
let baseSettingsErrors = validateBaseBenchmarkSettings(testSpec)
if (baseSettingsErrors.length > 0) {
testSpecErrors.push(
...baseSettingsErrors.map(error => `${testPrefix}: ${error}`),
)
}
if (!Array.isArray(testSpec.cases) || testSpec.cases.length === 0) {
testSpecErrors.push(
`${testPrefix}: must include at least one case in the "cases" array.`,
)
} else {
let caseValidationPromises = testSpec.cases.map(
async (caseItem, caseIndex) => {
let caseErrors = []
let casePrefix = `${testPrefix}, Case ${caseIndex + 1}`
if (caseItem.testPath) {
let pathsToTest = Array.isArray(caseItem.testPath)
? caseItem.testPath
: [caseItem.testPath]
if (pathsToTest.length === 0) {
caseErrors.push(
`${casePrefix}: "testPath" array cannot be empty.`,
)
}
let pathCheckPromises = pathsToTest.map(async pathToTest => {
if (typeof pathToTest !== 'string') {
return `${casePrefix}: each item in "testPath" must be a string.`
}
try {
let testFilePath = path.resolve(configDirectory, pathToTest)
await fs.access(testFilePath)
return null
} catch {
return `${casePrefix}: Test file/directory not found at "${pathToTest}".`
}
})
let pathErrors = (await Promise.all(pathCheckPromises)).filter(
Boolean,
)
caseErrors.push(...pathErrors)
} else {
caseErrors.push(`${casePrefix}: "testPath" is required.`)
}
if (
caseItem.severity !== void 0 &&
(typeof caseItem.severity !== 'number' ||
![0, 1, 2].includes(caseItem.severity))
) {
caseErrors.push(`${casePrefix}: "severity" must be 0, 1, or 2.`)
}
if (caseItem.options !== void 0 && !Array.isArray(caseItem.options)) {
caseErrors.push(`${casePrefix}: "options" must be an array.`)
}
return caseErrors
},
)
let allCaseErrors = await Promise.all(caseValidationPromises)
testSpecErrors.push(...allCaseErrors.flat())
}
return testSpecErrors
})
let allTestErrors = await Promise.all(validationPromises)
errors.push(...allTestErrors.flat())
return errors
}
function validateBaseBenchmarkSettings(settings = {}) {
let errors = []
if (
settings.iterations !== void 0 &&
(typeof settings.iterations !== 'number' || settings.iterations <= 0)
) {
errors.push(`"iterations" must be a positive number`)
}
if (
settings.timeout !== void 0 &&
(typeof settings.timeout !== 'number' || settings.timeout <= 0)
) {
errors.push(`"timeout" must be a positive number`)
}
if (settings.warmup !== void 0) {
if (typeof settings.warmup === 'object') {
if (
settings.warmup.iterations !== void 0 &&
(typeof settings.warmup.iterations !== 'number' ||
settings.warmup.iterations < 0)
) {
errors.push(`"warmup.iterations" must be a non-negative number`)
}
if (
settings.warmup.enabled !== void 0 &&
typeof settings.warmup.enabled !== 'boolean'
) {
errors.push(`"warmup.enabled" must be a boolean`)
}
} else {
errors.push(`"warmup" must be an object`)
}
}
return errors
}
export { validateConfig }