Linting: Speed up betterer by sharing eslint instances by groups (#60019)

pull/59782/head
Esteban Beltran 3 years ago committed by GitHub
parent a6e8f651ac
commit a7a2c0f373
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 104
      .betterer.ts
  2. 1
      package.json
  3. 11
      yarn.lock

@ -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}`);
});
})
);
});
}
});
}

@ -120,6 +120,7 @@
"@types/enzyme-adapter-react-16": "1.0.6",
"@types/eslint": "8.4.9",
"@types/file-saver": "2.0.5",
"@types/glob": "^8.0.0",
"@types/google.analytics": "^0.0.42",
"@types/gtag.js": "^0.0.12",
"@types/history": "4.7.11",

@ -10781,6 +10781,16 @@ __metadata:
languageName: node
linkType: hard
"@types/glob@npm:^8.0.0":
version: 8.0.0
resolution: "@types/glob@npm:8.0.0"
dependencies:
"@types/minimatch": "*"
"@types/node": "*"
checksum: 1817b05f5a8aed851d102a65b5e926d5c777bef927ea62b36d635860eef5364f2046bb5a692d135b6f2b28f34e4a9d44ade9396122c0845bcc7636d35f624747
languageName: node
linkType: hard
"@types/google.analytics@npm:^0.0.42":
version: 0.0.42
resolution: "@types/google.analytics@npm:0.0.42"
@ -21518,6 +21528,7 @@ __metadata:
"@types/enzyme-adapter-react-16": 1.0.6
"@types/eslint": 8.4.9
"@types/file-saver": 2.0.5
"@types/glob": ^8.0.0
"@types/google.analytics": ^0.0.42
"@types/gtag.js": ^0.0.12
"@types/history": 4.7.11

Loading…
Cancel
Save