|
|
|
@ -2,6 +2,9 @@ import { regexp } from '@betterer/regexp'; |
|
|
|
|
import { BettererFileTest } from '@betterer/betterer'; |
|
|
|
|
import { ESLint, Linter } from 'eslint'; |
|
|
|
|
import { existsSync } from 'fs'; |
|
|
|
|
import { exec } from 'child_process'; |
|
|
|
|
import path from 'path'; |
|
|
|
|
import glob from 'glob'; |
|
|
|
|
|
|
|
|
|
export default { |
|
|
|
|
'no enzyme tests': () => regexp(/from 'enzyme'/g).include('**/*.test.*'), |
|
|
|
@ -22,54 +25,79 @@ function countUndocumentedStories() { |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async function findEslintConfigFiles(): Promise<string[]> { |
|
|
|
|
return new Promise((resolve, reject) => { |
|
|
|
|
glob('**/.eslintrc', (err, files) => { |
|
|
|
|
if (err) { |
|
|
|
|
reject(err); |
|
|
|
|
} |
|
|
|
|
resolve(files); |
|
|
|
|
}); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function countEslintErrors() { |
|
|
|
|
return new BettererFileTest(async (filePaths, fileTestResult, resolver) => { |
|
|
|
|
const { baseDirectory } = resolver; |
|
|
|
|
const cli = new ESLint({ cwd: baseDirectory }); |
|
|
|
|
|
|
|
|
|
await Promise.all( |
|
|
|
|
filePaths.map(async (filePath) => { |
|
|
|
|
const linterOptions = (await cli.calculateConfigForFile(filePath)) as Linter.Config; |
|
|
|
|
const eslintConfigFiles = await findEslintConfigFiles(); |
|
|
|
|
const eslintConfigMainPaths = eslintConfigFiles.map((file) => path.resolve(path.dirname(file))); |
|
|
|
|
|
|
|
|
|
const rules: Partial<Linter.RulesRecord> = { |
|
|
|
|
'@typescript-eslint/no-explicit-any': 'error', |
|
|
|
|
}; |
|
|
|
|
const baseRules: Partial<Linter.RulesRecord> = { |
|
|
|
|
'@typescript-eslint/no-explicit-any': 'error', |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
const isTestFile = |
|
|
|
|
filePath.endsWith('.test.tsx') || |
|
|
|
|
filePath.endsWith('.test.ts') || |
|
|
|
|
filePath.includes('__mocks__') || |
|
|
|
|
filePath.includes('public/test/'); |
|
|
|
|
const nonTestFilesRules: Partial<Linter.RulesRecord> = { |
|
|
|
|
...baseRules, |
|
|
|
|
'@typescript-eslint/consistent-type-assertions': ['error', { assertionStyle: 'never' }], |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if (!isTestFile) { |
|
|
|
|
rules['@typescript-eslint/consistent-type-assertions'] = [ |
|
|
|
|
'error', |
|
|
|
|
{ |
|
|
|
|
assertionStyle: 'never', |
|
|
|
|
}, |
|
|
|
|
]; |
|
|
|
|
} |
|
|
|
|
// group files by eslint config file
|
|
|
|
|
// this will create two file groups for each eslint config file
|
|
|
|
|
// one for test files and one for non-test files
|
|
|
|
|
const fileGroups: Record<string, string[]> = {}; |
|
|
|
|
|
|
|
|
|
const runner = new ESLint({ |
|
|
|
|
baseConfig: { |
|
|
|
|
...linterOptions, |
|
|
|
|
rules, |
|
|
|
|
}, |
|
|
|
|
useEslintrc: false, |
|
|
|
|
cwd: baseDirectory, |
|
|
|
|
}); |
|
|
|
|
for (const filePath of filePaths) { |
|
|
|
|
let configPath = eslintConfigMainPaths.find((configPath) => filePath.startsWith(configPath)) ?? ''; |
|
|
|
|
const isTestFile = |
|
|
|
|
filePath.endsWith('.test.tsx') || |
|
|
|
|
filePath.endsWith('.test.ts') || |
|
|
|
|
filePath.includes('__mocks__') || |
|
|
|
|
filePath.includes('public/test/'); |
|
|
|
|
|
|
|
|
|
const lintResults = await runner.lintFiles([filePath]); |
|
|
|
|
lintResults |
|
|
|
|
.filter((lintResult) => lintResult.source) |
|
|
|
|
.forEach((lintResult) => { |
|
|
|
|
const { messages } = lintResult; |
|
|
|
|
const file = fileTestResult.addFile(filePath, ''); |
|
|
|
|
messages.forEach((message, index) => { |
|
|
|
|
file.addIssue(0, 0, message.message, `${index}`); |
|
|
|
|
}); |
|
|
|
|
if (isTestFile) { |
|
|
|
|
configPath += '-test'; |
|
|
|
|
} |
|
|
|
|
if (!fileGroups[configPath]) { |
|
|
|
|
fileGroups[configPath] = []; |
|
|
|
|
} |
|
|
|
|
fileGroups[configPath].push(filePath); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (const configPath of Object.keys(fileGroups)) { |
|
|
|
|
const rules = configPath.endsWith('-test') ? baseRules : nonTestFilesRules; |
|
|
|
|
// this is by far the slowest part of this code. It takes eslint about 2 seconds just to find the config
|
|
|
|
|
const linterOptions = (await cli.calculateConfigForFile(fileGroups[configPath][0])) as Linter.Config; |
|
|
|
|
const runner = new ESLint({ |
|
|
|
|
baseConfig: { |
|
|
|
|
...linterOptions, |
|
|
|
|
rules: rules, |
|
|
|
|
}, |
|
|
|
|
useEslintrc: false, |
|
|
|
|
cwd: baseDirectory, |
|
|
|
|
}); |
|
|
|
|
const lintResults = await runner.lintFiles(fileGroups[configPath]); |
|
|
|
|
lintResults |
|
|
|
|
.filter((lintResult) => lintResult.source) |
|
|
|
|
.forEach((lintResult) => { |
|
|
|
|
const { messages } = lintResult; |
|
|
|
|
const filePath = lintResult.filePath; |
|
|
|
|
const file = fileTestResult.addFile(filePath, ''); |
|
|
|
|
messages.forEach((message, index) => { |
|
|
|
|
file.addIssue(0, 0, message.message, `${index}`); |
|
|
|
|
}); |
|
|
|
|
}) |
|
|
|
|
); |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|