Skip to content

Commit 35ec495

Browse files
committed
feat(plugin-typescript): support multiple tsconfigs
1 parent d3230f1 commit 35ec495

File tree

6 files changed

+81
-16
lines changed

6 files changed

+81
-16
lines changed

packages/plugin-typescript/src/lib/runner/runner.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,22 @@ import {
1111
toSentenceCase,
1212
} from '@code-pushup/utils';
1313
import type { AuditSlug } from '../types.js';
14-
import {
15-
type DiagnosticsOptions,
16-
getTypeScriptDiagnostics,
17-
} from './ts-runner.js';
14+
import { getTypeScriptDiagnostics } from './ts-runner.js';
1815
import type { CodeRangeName } from './types.js';
1916
import { getIssueFromDiagnostic, tsCodeToAuditSlug } from './utils.js';
2017

21-
export type RunnerOptions = DiagnosticsOptions & {
18+
export type RunnerOptions = {
19+
tsconfig: string[];
2220
expectedAudits: { slug: AuditSlug }[];
2321
};
2422

2523
export function createRunnerFunction(options: RunnerOptions): RunnerFunction {
2624
const { tsconfig, expectedAudits } = options;
2725

2826
return (): AuditOutputs => {
29-
const diagnostics = getTypeScriptDiagnostics({ tsconfig });
27+
const diagnostics = tsconfig.flatMap(config => [
28+
...getTypeScriptDiagnostics({ tsconfig: config }),
29+
]);
3030

3131
const result = diagnostics.reduce<
3232
Partial<Record<CodeRangeName, Pick<AuditOutput, 'slug' | 'details'>>>

packages/plugin-typescript/src/lib/runner/runner.unit.test.ts

Lines changed: 51 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('createRunnerFunction', () => {
5656
it('should return empty array if no diagnostics are found', () => {
5757
getTypeScriptDiagnosticsSpy.mockReturnValue([]);
5858
const runner = createRunnerFunction({
59-
tsconfig: 'tsconfig.json',
59+
tsconfig: ['tsconfig.json'],
6060
expectedAudits: [],
6161
});
6262
expect(runner(runnerArgs)).toStrictEqual([]);
@@ -65,7 +65,7 @@ describe('createRunnerFunction', () => {
6565
it('should return empty array if no supported diagnostics are found', () => {
6666
getTypeScriptDiagnosticsSpy.mockReturnValue([mockSemanticDiagnostic]);
6767
const runner = createRunnerFunction({
68-
tsconfig: 'tsconfig.json',
68+
tsconfig: ['tsconfig.json'],
6969
expectedAudits: [],
7070
});
7171
expect(runner(runnerArgs)).toStrictEqual([]);
@@ -74,7 +74,7 @@ describe('createRunnerFunction', () => {
7474
it('should pass the diagnostic code to tsCodeToSlug', () => {
7575
getTypeScriptDiagnosticsSpy.mockReturnValue([mockSemanticDiagnostic]);
7676
const runner = createRunnerFunction({
77-
tsconfig: 'tsconfig.json',
77+
tsconfig: ['tsconfig.json'],
7878
expectedAudits: [],
7979
});
8080
expect(runner(runnerArgs)).toStrictEqual([]);
@@ -85,7 +85,7 @@ describe('createRunnerFunction', () => {
8585
it('should pass the diagnostic to getIssueFromDiagnostic', () => {
8686
getTypeScriptDiagnosticsSpy.mockReturnValue([mockSemanticDiagnostic]);
8787
const runner = createRunnerFunction({
88-
tsconfig: 'tsconfig.json',
88+
tsconfig: ['tsconfig.json'],
8989
expectedAudits: [],
9090
});
9191
expect(runner(runnerArgs)).toStrictEqual([]);
@@ -106,7 +106,7 @@ describe('createRunnerFunction', () => {
106106
},
107107
]);
108108
const runner = createRunnerFunction({
109-
tsconfig: 'tsconfig.json',
109+
tsconfig: ['tsconfig.json'],
110110
expectedAudits: [{ slug: 'semantic-errors' }],
111111
});
112112

@@ -138,7 +138,7 @@ describe('createRunnerFunction', () => {
138138
mockSemanticDiagnostic,
139139
]);
140140
const runner = createRunnerFunction({
141-
tsconfig: 'tsconfig.json',
141+
tsconfig: ['tsconfig.json'],
142142
expectedAudits: [{ slug: 'semantic-errors' }, { slug: 'syntax-errors' }],
143143
});
144144

@@ -181,10 +181,54 @@ describe('createRunnerFunction', () => {
181181
},
182182
]);
183183
const runner = createRunnerFunction({
184-
tsconfig: 'tsconfig.json',
184+
tsconfig: ['tsconfig.json'],
185185
expectedAudits: [{ slug: 'semantic-errors' }, { slug: 'syntax-errors' }],
186186
});
187187
const auditOutputs = runner(runnerArgs);
188188
expect(() => auditOutputsSchema.parse(auditOutputs)).not.toThrow();
189189
});
190+
191+
it('should aggregate diagnostics from multiple tsconfigs', () => {
192+
getTypeScriptDiagnosticsSpy
193+
.mockReturnValueOnce([mockSemanticDiagnostic])
194+
.mockReturnValueOnce([mockSyntacticDiagnostic]);
195+
196+
const runner = createRunnerFunction({
197+
tsconfig: ['tsconfig.lib.json', 'tsconfig.spec.json'],
198+
expectedAudits: [{ slug: 'semantic-errors' }, { slug: 'syntax-errors' }],
199+
});
200+
201+
const auditOutputs = runner(runnerArgs);
202+
203+
expect(getTypeScriptDiagnosticsSpy).toHaveBeenCalledTimes(2);
204+
expect(getTypeScriptDiagnosticsSpy).toHaveBeenNthCalledWith(1, {
205+
tsconfig: 'tsconfig.lib.json',
206+
});
207+
expect(getTypeScriptDiagnosticsSpy).toHaveBeenNthCalledWith(2, {
208+
tsconfig: 'tsconfig.spec.json',
209+
});
210+
211+
expect(auditOutputs).toStrictEqual([
212+
expect.objectContaining({
213+
slug: 'semantic-errors',
214+
value: 1,
215+
details: {
216+
issues: [
217+
expect.objectContaining({
218+
message: `TS2322: Type 'string' is not assignable to type 'number'.`,
219+
}),
220+
],
221+
},
222+
}),
223+
expect.objectContaining({
224+
slug: 'syntax-errors',
225+
value: 1,
226+
details: {
227+
issues: [
228+
expect.objectContaining({ message: "TS1005: ';' expected." }),
229+
],
230+
},
231+
}),
232+
]);
233+
});
190234
});

packages/plugin-typescript/src/lib/schema.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { z } from 'zod';
22
import { pluginScoreTargetsSchema } from '@code-pushup/models';
3+
import { toArray } from '@code-pushup/utils';
34
import { AUDITS, DEFAULT_TS_CONFIG } from './constants.js';
45
import type { AuditSlug } from './types.js';
56

@@ -10,8 +11,9 @@ const auditSlugs = AUDITS.map(({ slug }) => slug) as [
1011
export const typescriptPluginConfigSchema = z
1112
.object({
1213
tsconfig: z
13-
.string()
14+
.union([z.string(), z.array(z.string()).min(1)])
1415
.default(DEFAULT_TS_CONFIG)
16+
.transform(toArray)
1517
.meta({
1618
description: `Path to a tsconfig file (default is ${DEFAULT_TS_CONFIG})`,
1719
}),

packages/plugin-typescript/src/lib/schema.unit.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,25 @@ describe('typescriptPluginConfigSchema', () => {
1919
).not.toThrow();
2020
});
2121

22+
it('transforms a single tsconfig string to an array', () => {
23+
expect(
24+
typescriptPluginConfigSchema.parse({ tsconfig }).tsconfig,
25+
).toStrictEqual([tsconfig]);
26+
});
27+
28+
it('accepts an array of tsconfig paths', () => {
29+
const tsconfigs = ['tsconfig.lib.json', 'tsconfig.spec.json'];
30+
expect(
31+
typescriptPluginConfigSchema.parse({ tsconfig: tsconfigs }).tsconfig,
32+
).toStrictEqual(tsconfigs);
33+
});
34+
35+
it('throws for empty tsconfig array', () => {
36+
expect(() => typescriptPluginConfigSchema.parse({ tsconfig: [] })).toThrow(
37+
'too_small',
38+
);
39+
});
40+
2241
it('accepts a configuration with tsconfig and empty onlyAudits', () => {
2342
expect(() =>
2443
typescriptPluginConfigSchema.parse({

packages/plugin-typescript/src/lib/typescript-plugin.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function typescriptPlugin(
2222
options?: TypescriptPluginOptions,
2323
): PluginConfig {
2424
const {
25-
tsconfig = DEFAULT_TS_CONFIG,
25+
tsconfig = [DEFAULT_TS_CONFIG],
2626
onlyAudits,
2727
scoreTargets,
2828
} = parseOptions(options ?? {});

packages/plugin-typescript/src/lib/typescript-plugin.unit.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe('typescriptPlugin', () => {
3838
}),
3939
)
4040
.toThrow(`Error parsing TypeScript Plugin options: SchemaValidationError: Invalid ${ansis.bold('TypescriptPluginConfig')}
41-
✖ Invalid input: expected string, received number
41+
✖ Invalid input
4242
→ at tsconfig
4343
`);
4444
});

0 commit comments

Comments
 (0)