Skip to content

Commit 31072f8

Browse files
committed
feat(plugin-eslint): use loadArtifacts helper
1 parent 577d719 commit 31072f8

File tree

2 files changed

+202
-5
lines changed

2 files changed

+202
-5
lines changed

packages/plugin-eslint/src/lib/runner/index.ts

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { writeFile } from 'node:fs/promises';
22
import path from 'node:path';
3-
import type {
4-
Audit,
5-
AuditOutput,
6-
RunnerConfig,
7-
RunnerFilesPaths,
3+
import {
4+
type Audit,
5+
type AuditOutput,
6+
type AuditOutputs,
7+
DEFAULT_PERSIST_OUTPUT_DIR,
8+
type PluginArtifactOptions,
9+
type RunnerConfig,
10+
type RunnerFilesPaths,
811
} from '@code-pushup/models';
912
import {
1013
asyncSequential,
@@ -18,6 +21,7 @@ import {
1821
import type { ESLintPluginRunnerConfig, ESLintTarget } from '../config.js';
1922
import { lint } from './lint.js';
2023
import { lintResultsToAudits, mergeLinterOutputs } from './transform.js';
24+
import { loadArtifacts } from './utils.js';
2125

2226
export async function executeRunner({
2327
runnerConfigPath,
@@ -71,3 +75,46 @@ export async function createRunnerConfig(
7175
outputFile: runnerOutputPath,
7276
};
7377
}
78+
79+
export async function generateAuditOutputs(options: {
80+
audits: Audit[];
81+
targets: ESLintTarget[];
82+
artifacts?: PluginArtifactOptions;
83+
outputDir?: string;
84+
}): Promise<AuditOutputs> {
85+
const {
86+
audits,
87+
targets,
88+
artifacts,
89+
outputDir = DEFAULT_PERSIST_OUTPUT_DIR,
90+
} = options;
91+
const config: ESLintPluginRunnerConfig = {
92+
targets,
93+
slugs: audits.map(audit => audit.slug),
94+
};
95+
96+
ui().logger.log(`ESLint plugin executing ${targets.length} lint targets`);
97+
98+
const linterOutputs = artifacts
99+
? await loadArtifacts(artifacts)
100+
: await asyncSequential(
101+
targets.map(target => ({
102+
...target,
103+
outputDir,
104+
})),
105+
lint,
106+
);
107+
const lintResults = mergeLinterOutputs(linterOutputs);
108+
const failedAudits = lintResultsToAudits(lintResults);
109+
110+
return config.slugs.map(
111+
(slug): AuditOutput =>
112+
failedAudits.find(audit => audit.slug === slug) ?? {
113+
slug,
114+
score: 1,
115+
value: 0,
116+
displayValue: 'passed',
117+
details: { issues: [] },
118+
},
119+
);
120+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest';
2+
import type { z } from 'zod';
3+
import type {
4+
Audit,
5+
AuditOutput,
6+
pluginArtifactOptionsSchema,
7+
} from '@code-pushup/models';
8+
import { ui } from '@code-pushup/utils';
9+
import type { ESLintTarget } from '../config.js';
10+
import { generateAuditOutputs } from './index.js';
11+
import * as lintModule from './lint.js';
12+
import type { LinterOutput } from './types.js';
13+
import * as utilsFileModule from './utils.js';
14+
15+
describe('generateAuditOutputs', () => {
16+
const loadArtifactsSpy = vi.spyOn(utilsFileModule, 'loadArtifacts');
17+
const lintSpy = vi.spyOn(lintModule, 'lint');
18+
19+
const mockAudits: Audit[] = [
20+
{ slug: 'max-lines', title: 'Max lines', description: 'Test' },
21+
{ slug: 'no-unused-vars', title: 'No unused vars', description: 'Test' },
22+
];
23+
const mockTargetPatterns = { patterns: ['src/**/*.ts'] };
24+
const mockTargetPatternsAndConfigs = {
25+
patterns: ['lib/**/*.js'],
26+
eslintrc: '.eslintrc.js',
27+
};
28+
const mockTargets: ESLintTarget[] = [
29+
mockTargetPatterns,
30+
mockTargetPatternsAndConfigs,
31+
];
32+
33+
const mockLinterOutputs: LinterOutput[] = [
34+
{
35+
results: [
36+
{
37+
filePath: 'test.ts',
38+
messages: [
39+
{
40+
ruleId: 'max-lines',
41+
severity: 1,
42+
message: 'File has too many lines',
43+
line: 1,
44+
column: 1,
45+
},
46+
],
47+
} as any,
48+
],
49+
ruleOptionsPerFile: { 'test.ts': { 'max-lines': [] } },
50+
},
51+
{
52+
results: [
53+
{
54+
filePath: 'test.ts',
55+
messages: [
56+
{
57+
ruleId: 'max-lines',
58+
severity: 1,
59+
message: 'File has too many lines',
60+
line: 1,
61+
column: 1,
62+
},
63+
],
64+
} as any,
65+
],
66+
ruleOptionsPerFile: { 'test.ts': { 'max-lines': [] } },
67+
},
68+
];
69+
70+
const mockedAuditOutputs: AuditOutput[] = [
71+
{
72+
slug: 'max-lines',
73+
score: 0,
74+
value: 2,
75+
displayValue: '2 warnings',
76+
details: {
77+
issues: [
78+
{
79+
message: 'File has too many lines',
80+
severity: 'warning',
81+
source: {
82+
file: 'test.ts',
83+
position: {
84+
startLine: 1,
85+
startColumn: 1,
86+
},
87+
},
88+
},
89+
{
90+
message: 'File has too many lines',
91+
severity: 'warning',
92+
source: {
93+
file: 'test.ts',
94+
position: {
95+
startLine: 1,
96+
startColumn: 1,
97+
},
98+
},
99+
},
100+
],
101+
},
102+
},
103+
{
104+
slug: 'no-unused-vars',
105+
score: 1,
106+
value: 0,
107+
displayValue: 'passed',
108+
details: { issues: [] },
109+
},
110+
];
111+
112+
beforeEach(() => {
113+
vi.clearAllMocks();
114+
});
115+
116+
it('should use loadArtifacts when artifacts are provided', async () => {
117+
const artifacts: z.infer<typeof pluginArtifactOptionsSchema> = {
118+
artifactsPaths: ['path/to/artifacts.json'],
119+
};
120+
loadArtifactsSpy.mockResolvedValue(mockLinterOutputs);
121+
122+
await expect(
123+
generateAuditOutputs({
124+
audits: mockAudits,
125+
targets: mockTargets,
126+
artifacts,
127+
}),
128+
).resolves.toStrictEqual(mockedAuditOutputs);
129+
130+
expect(loadArtifactsSpy).toHaveBeenCalledWith(artifacts);
131+
expect(lintSpy).not.toHaveBeenCalled();
132+
expect(ui()).toHaveLogged('log', 'ESLint plugin executing 2 lint targets');
133+
});
134+
135+
it('should use internal linting logic when artifacts are not provided', async () => {
136+
lintSpy.mockResolvedValueOnce(mockLinterOutputs.at(0)!);
137+
lintSpy.mockResolvedValueOnce(mockLinterOutputs.at(0)!);
138+
139+
await expect(
140+
generateAuditOutputs({
141+
audits: mockAudits,
142+
targets: mockTargets,
143+
outputDir: 'custom-output',
144+
}),
145+
).resolves.toStrictEqual(mockedAuditOutputs);
146+
147+
expect(loadArtifactsSpy).not.toHaveBeenCalled();
148+
expect(lintSpy).toHaveBeenCalledTimes(2);
149+
});
150+
});

0 commit comments

Comments
 (0)