Skip to content

Commit 3aac581

Browse files
committed
feat(plugin-eslint): register audit metadata based on eslintrc and file patterns
1 parent a9dc323 commit 3aac581

File tree

7 files changed

+118
-26
lines changed

7 files changed

+118
-26
lines changed

packages/plugin-eslint/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
import { eslintPlugin } from './lib/eslint-plugin';
22

33
export default eslintPlugin;
4+
5+
export type { ESLintPluginConfig } from './lib/eslint-plugin';
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import { eslintPlugin } from './eslint-plugin';
22

33
describe('eslintPlugin', () => {
4-
it('should initialize ESLint plugin', () => {
5-
expect(eslintPlugin({ config: '.eslintrc.json' }).slug).toBe('eslint');
4+
it('should initialize ESLint plugin', async () => {
5+
const plugin = await eslintPlugin({
6+
eslintrc: '.eslintrc.json',
7+
patterns: ['**/*.ts', '**/*.js', '**/*.json'],
8+
});
9+
console.log(plugin);
10+
expect(plugin.slug).toBe('eslint');
611
});
712
});
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,31 @@
11
import { AuditOutputs, PluginConfig } from '@quality-metrics/models';
22
import { objectToCliArgs } from '@quality-metrics/utils';
3-
import * as eslint from 'eslint';
3+
import { ESLint } from 'eslint';
4+
import { listAudits } from './meta/audits';
45

5-
type ESLintPluginConfig = {
6-
config: string;
6+
export type ESLintPluginConfig = {
7+
eslintrc: string;
8+
patterns: string | string[];
79
};
810

9-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
10-
export function eslintPlugin(_: ESLintPluginConfig): PluginConfig {
11-
// This line is here to keep errors related to imports and engines
12-
eslint;
11+
export async function eslintPlugin({
12+
eslintrc,
13+
patterns,
14+
}: ESLintPluginConfig): Promise<PluginConfig> {
15+
const eslint = new ESLint({
16+
useEslintrc: false,
17+
baseConfig: { extends: eslintrc },
18+
});
19+
20+
const audits = await listAudits(eslint, patterns);
21+
1322
return {
14-
audits: [
15-
{
16-
slug: 'no-any',
17-
title: 'No any type',
18-
},
19-
],
23+
slug: 'eslint',
24+
title: 'ESLint',
25+
icon: 'eslint',
26+
description: 'Official Code PushUp ESLint plugin',
27+
// TODO: docsUrl
28+
audits,
2029
runner: {
2130
command: 'node',
2231
args: objectToCliArgs({
@@ -31,8 +40,5 @@ export function eslintPlugin(_: ESLintPluginConfig): PluginConfig {
3140
}),
3241
outputPath: 'tmp/out.json',
3342
},
34-
slug: 'eslint',
35-
title: 'execute plugin',
36-
icon: 'eslint',
3743
};
3844
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { Audit } from '@quality-metrics/models';
2+
import { distinct, toArray } from '@quality-metrics/utils';
3+
import type { ESLint, Linter, Rule } from 'eslint';
4+
5+
export async function listAudits(
6+
eslint: ESLint,
7+
patterns: string | string[],
8+
): Promise<Audit[]> {
9+
const configs = await toArray(patterns).reduce(
10+
async (acc, pattern) => [
11+
...(await acc),
12+
await eslint.calculateConfigForFile(pattern),
13+
],
14+
Promise.resolve<Linter.Config[]>([]),
15+
);
16+
17+
const rulesIds = distinct(
18+
configs.flatMap(config => Object.keys(config.rules ?? {})),
19+
);
20+
const rulesMeta = eslint.getRulesMetaForResults([
21+
{
22+
messages: rulesIds.map(ruleId => ({ ruleId })),
23+
suppressedMessages: [] as Linter.SuppressedLintMessage[],
24+
} as ESLint.LintResult,
25+
]);
26+
27+
return Object.entries(rulesMeta).map(args => ruleToAudit(...args));
28+
}
29+
30+
function ruleToAudit(ruleId: string, meta: Rule.RuleMetaData): Audit {
31+
return {
32+
slug: ruleId, // TODO: slugify
33+
title: meta.docs?.description ?? ruleId,
34+
docsUrl: meta.docs?.url,
35+
};
36+
}

packages/utils/src/index.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
export {
2-
executeProcess,
32
ProcessConfig,
4-
ProcessResult,
5-
ProcessObserver,
63
ProcessError,
4+
ProcessObserver,
5+
ProcessResult,
6+
executeProcess,
77
objectToCliArgs,
88
} from './lib/execute-process';
9-
export { calcDuration, formatBytes } from './lib/utils';
10-
export { reportToStdout } from './lib/report-to-stdout';
11-
export { reportToMd } from './lib/report-to-md';
129
export { importModule } from './lib/load-file';
10+
export { reportToMd } from './lib/report-to-md';
11+
export { reportToStdout } from './lib/report-to-stdout';
12+
export { calcDuration, formatBytes, toArray, distinct } from './lib/utils';

packages/utils/src/lib/utils.spec.ts

+37-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1-
import { describe, expect } from 'vitest';
2-
import { calcDuration, formatBytes, countWeightedRefs, sumRefs } from './utils';
31
import { CategoryConfig } from '@quality-metrics/models';
2+
import { describe, expect } from 'vitest';
3+
import {
4+
calcDuration,
5+
countWeightedRefs,
6+
distinct,
7+
formatBytes,
8+
sumRefs,
9+
toArray,
10+
} from './utils';
411

512
describe('calcDuration', () => {
613
it('should calc the duration correctly if start and stop are given', () => {
@@ -106,3 +113,31 @@ describe('formatBytes', () => {
106113
expect(formatBytes(0)).toBe('0 B');
107114
});
108115
});
116+
117+
describe('distinct', () => {
118+
it('should remove duplicate strings from array', () => {
119+
expect(
120+
distinct([
121+
'no-unused-vars',
122+
'no-invalid-regexp',
123+
'no-unused-vars',
124+
'no-invalid-regexp',
125+
'@typescript-eslint/no-unused-vars',
126+
]),
127+
).toEqual([
128+
'no-unused-vars',
129+
'no-invalid-regexp',
130+
'@typescript-eslint/no-unused-vars',
131+
]);
132+
});
133+
});
134+
135+
describe('toArray', () => {
136+
it('should transform non-array value into array with single value', () => {
137+
expect(toArray('src/**/*.ts')).toEqual(['src/**/*.ts']);
138+
});
139+
140+
it('should leave array value unchanged', () => {
141+
expect(toArray(['*.ts', '*.js'])).toEqual(['*.ts', '*.js']);
142+
});
143+
});

packages/utils/src/lib/utils.ts

+8
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,11 @@ export function calcDuration(start: number, stop?: number): number {
3030
stop = stop !== undefined ? stop : performance.now();
3131
return Math.floor(stop - start);
3232
}
33+
34+
export function distinct<T extends string | number | boolean>(array: T[]): T[] {
35+
return Array.from(new Set(array));
36+
}
37+
38+
export function toArray<T>(val: T | T[]): T[] {
39+
return Array.isArray(val) ? val : [val];
40+
}

0 commit comments

Comments
 (0)