Skip to content

Commit

Permalink
feat(plugin-eslint): register audit metadata based on eslintrc and fi…
Browse files Browse the repository at this point in the history
…le patterns
  • Loading branch information
matejchalk committed Oct 9, 2023
1 parent a9dc323 commit 3aac581
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 26 deletions.
2 changes: 2 additions & 0 deletions packages/plugin-eslint/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { eslintPlugin } from './lib/eslint-plugin';

export default eslintPlugin;

export type { ESLintPluginConfig } from './lib/eslint-plugin';
9 changes: 7 additions & 2 deletions packages/plugin-eslint/src/lib/eslint-plugin.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { eslintPlugin } from './eslint-plugin';

describe('eslintPlugin', () => {
it('should initialize ESLint plugin', () => {
expect(eslintPlugin({ config: '.eslintrc.json' }).slug).toBe('eslint');
it('should initialize ESLint plugin', async () => {
const plugin = await eslintPlugin({
eslintrc: '.eslintrc.json',
patterns: ['**/*.ts', '**/*.js', '**/*.json'],
});
console.log(plugin);
expect(plugin.slug).toBe('eslint');
});
});
38 changes: 22 additions & 16 deletions packages/plugin-eslint/src/lib/eslint-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
import { AuditOutputs, PluginConfig } from '@quality-metrics/models';
import { objectToCliArgs } from '@quality-metrics/utils';
import * as eslint from 'eslint';
import { ESLint } from 'eslint';
import { listAudits } from './meta/audits';

type ESLintPluginConfig = {
config: string;
export type ESLintPluginConfig = {
eslintrc: string;
patterns: string | string[];
};

// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function eslintPlugin(_: ESLintPluginConfig): PluginConfig {
// This line is here to keep errors related to imports and engines
eslint;
export async function eslintPlugin({
eslintrc,
patterns,
}: ESLintPluginConfig): Promise<PluginConfig> {
const eslint = new ESLint({
useEslintrc: false,
baseConfig: { extends: eslintrc },
});

const audits = await listAudits(eslint, patterns);

return {
audits: [
{
slug: 'no-any',
title: 'No any type',
},
],
slug: 'eslint',
title: 'ESLint',
icon: 'eslint',
description: 'Official Code PushUp ESLint plugin',
// TODO: docsUrl
audits,
runner: {
command: 'node',
args: objectToCliArgs({
Expand All @@ -31,8 +40,5 @@ export function eslintPlugin(_: ESLintPluginConfig): PluginConfig {
}),
outputPath: 'tmp/out.json',
},
slug: 'eslint',
title: 'execute plugin',
icon: 'eslint',
};
}
36 changes: 36 additions & 0 deletions packages/plugin-eslint/src/lib/meta/audits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { Audit } from '@quality-metrics/models';
import { distinct, toArray } from '@quality-metrics/utils';
import type { ESLint, Linter, Rule } from 'eslint';

export async function listAudits(
eslint: ESLint,
patterns: string | string[],
): Promise<Audit[]> {
const configs = await toArray(patterns).reduce(
async (acc, pattern) => [
...(await acc),
await eslint.calculateConfigForFile(pattern),
],
Promise.resolve<Linter.Config[]>([]),
);

const rulesIds = distinct(
configs.flatMap(config => Object.keys(config.rules ?? {})),
);
const rulesMeta = eslint.getRulesMetaForResults([
{
messages: rulesIds.map(ruleId => ({ ruleId })),
suppressedMessages: [] as Linter.SuppressedLintMessage[],
} as ESLint.LintResult,
]);

return Object.entries(rulesMeta).map(args => ruleToAudit(...args));
}

function ruleToAudit(ruleId: string, meta: Rule.RuleMetaData): Audit {
return {
slug: ruleId, // TODO: slugify
title: meta.docs?.description ?? ruleId,
docsUrl: meta.docs?.url,
};
}
12 changes: 6 additions & 6 deletions packages/utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
export {
executeProcess,
ProcessConfig,
ProcessResult,
ProcessObserver,
ProcessError,
ProcessObserver,
ProcessResult,
executeProcess,
objectToCliArgs,
} from './lib/execute-process';
export { calcDuration, formatBytes } from './lib/utils';
export { reportToStdout } from './lib/report-to-stdout';
export { reportToMd } from './lib/report-to-md';
export { importModule } from './lib/load-file';
export { reportToMd } from './lib/report-to-md';
export { reportToStdout } from './lib/report-to-stdout';
export { calcDuration, formatBytes, toArray, distinct } from './lib/utils';
39 changes: 37 additions & 2 deletions packages/utils/src/lib/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { describe, expect } from 'vitest';
import { calcDuration, formatBytes, countWeightedRefs, sumRefs } from './utils';
import { CategoryConfig } from '@quality-metrics/models';
import { describe, expect } from 'vitest';
import {
calcDuration,
countWeightedRefs,
distinct,
formatBytes,
sumRefs,
toArray,
} from './utils';

describe('calcDuration', () => {
it('should calc the duration correctly if start and stop are given', () => {
Expand Down Expand Up @@ -106,3 +113,31 @@ describe('formatBytes', () => {
expect(formatBytes(0)).toBe('0 B');
});
});

describe('distinct', () => {
it('should remove duplicate strings from array', () => {
expect(
distinct([
'no-unused-vars',
'no-invalid-regexp',
'no-unused-vars',
'no-invalid-regexp',
'@typescript-eslint/no-unused-vars',
]),
).toEqual([
'no-unused-vars',
'no-invalid-regexp',
'@typescript-eslint/no-unused-vars',
]);
});
});

describe('toArray', () => {
it('should transform non-array value into array with single value', () => {
expect(toArray('src/**/*.ts')).toEqual(['src/**/*.ts']);
});

it('should leave array value unchanged', () => {
expect(toArray(['*.ts', '*.js'])).toEqual(['*.ts', '*.js']);
});
});
8 changes: 8 additions & 0 deletions packages/utils/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,11 @@ export function calcDuration(start: number, stop?: number): number {
stop = stop !== undefined ? stop : performance.now();
return Math.floor(stop - start);
}

export function distinct<T extends string | number | boolean>(array: T[]): T[] {
return Array.from(new Set(array));
}

export function toArray<T>(val: T | T[]): T[] {
return Array.isArray(val) ? val : [val];
}

0 comments on commit 3aac581

Please sign in to comment.