Skip to content

Commit 7c67433

Browse files
committed
merge PR
1 parent 68f56c8 commit 7c67433

10 files changed

Lines changed: 79 additions & 55 deletions

File tree

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { typescriptPlugin } from './lib/typescript-plugin.js';
22

3-
export type { TypescriptPluginOptions } from './lib/config.js';
3+
export { TYPESCRIPT_PLUGIN_SLUG } from './lib/constants.js';
4+
export type { TypescriptPluginOptions } from './lib/types.js';
45
export { typescriptPlugin } from './lib/typescript-plugin.js';
56
export default typescriptPlugin;

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

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { DiagnosticCategory } from 'typescript';
21
import type {
32
Audit,
43
AuditOutput,
@@ -7,48 +6,43 @@ import type {
76
Issue,
87
RunnerFunction,
98
} from '@code-pushup/models';
10-
import type { TypescriptPluginOptions } from '../config.js';
11-
import { AUDITS } from '../constants.js';
129
import type { AuditSlug } from '../types.js';
13-
import { filterAuditsBySlug } from '../utils.js';
14-
import { getDiagnostics } from './typescript-runner.js';
10+
import {
11+
type DiagnosticsOptions,
12+
getDiagnostics,
13+
} from './typescript-runner.js';
1514
import {
1615
AUDIT_LOOKUP,
1716
getIssueFromDiagnostic,
18-
transformTSErrorCodeToAuditSlug,
17+
tSCodeToAuditSlug,
1918
} from './utils.js';
2019

21-
export function createRunnerFunction(
22-
options: TypescriptPluginOptions & { audits: Audit[] },
23-
): RunnerFunction {
20+
export type RunnerOptions = DiagnosticsOptions & {
21+
filteredAudits: Audit[];
22+
};
23+
24+
export function createRunnerFunction(options: RunnerOptions): RunnerFunction {
2425
return async (): Promise<AuditOutputs> => {
25-
const diagnostics = await getDiagnostics(options);
26+
const { filteredAudits, tsConfigPath } = options;
27+
const diagnostics = await getDiagnostics({ tsConfigPath });
2628

2729
const result: Record<
2830
AuditSlug,
2931
Pick<AuditReport, 'slug' | 'details'>
3032
> = diagnostics
31-
.filter(
32-
({ category }) =>
33-
category === DiagnosticCategory.Warning ||
34-
category === DiagnosticCategory.Error,
35-
)
3633
// filter out unsupported errors
3734
.filter(({ code }) => AUDIT_LOOKUP.get(code) !== undefined)
3835
.reduce(
3936
(acc, diag) => {
40-
const slug = transformTSErrorCodeToAuditSlug(diag.code);
41-
const issue = getIssueFromDiagnostic(diag);
42-
37+
const slug = tSCodeToAuditSlug(diag.code);
4338
const existingIssues: Issue[] =
4439
(acc[slug] && acc[slug].details?.issues) || ([] as Issue[]);
45-
4640
return {
4741
...acc,
4842
[slug]: {
4943
slug,
5044
details: {
51-
issues: [...existingIssues, issue],
45+
issues: [...existingIssues, getIssueFromDiagnostic(diag)],
5246
},
5347
},
5448
};
@@ -59,7 +53,7 @@ export function createRunnerFunction(
5953
>,
6054
);
6155

62-
return AUDITS.filter(filterAuditsBySlug(options.onlyAudits)).map(audit => {
56+
return filteredAudits.map(audit => {
6357
const { details } = result[audit.slug as AuditSlug] ?? {};
6458
const issues = details?.issues ?? [];
6559
return {

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

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,18 @@ import {
1111
} from 'typescript';
1212

1313
export type DiagnosticsOptions = { tsConfigPath: string };
14+
1415
export async function getDiagnostics(
1516
options: DiagnosticsOptions,
1617
): Promise<readonly Diagnostic[]> {
18+
const { fileNames, options: parsedOptions } =
19+
await getTsConfiguration(options);
20+
21+
const program = createProgram(fileNames, parsedOptions);
22+
return getPreEmitDiagnostics(program);
23+
}
24+
25+
export async function getTsConfiguration(options: DiagnosticsOptions) {
1726
const { tsConfigPath = 'tsconfig.json' } = options;
1827
const configPath = resolve(process.cwd(), tsConfigPath);
1928
const basePath = dirname(configPath);
@@ -26,18 +35,18 @@ export async function getDiagnostics(
2635

2736
const configFile = (await readFile(configPath)).toString();
2837

29-
const { config: strictConfig } = parseConfigFileTextToJson(
30-
configPath,
31-
configFile,
32-
);
33-
const parsed = parseJsonConfigFileContent(strictConfig, sys, basePath);
38+
const { config } = parseConfigFileTextToJson(configPath, configFile);
39+
const parsed = parseJsonConfigFileContent(config, sys, basePath);
3440

3541
const { options: opt, fileNames } = parsed;
3642
if (fileNames.length === 0) {
3743
throw new Error(
3844
'No files matched by the TypeScript configuration. Check your "include", "exclude" or "files" settings.',
3945
);
4046
}
41-
const program = createProgram(fileNames, opt);
42-
return getPreEmitDiagnostics(program);
47+
48+
return {
49+
options: opt,
50+
fileNames,
51+
};
4352
}

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,16 @@ import {
55
} from 'typescript';
66
import type { Issue } from '@code-pushup/models';
77
import type { AuditSlug } from '../types.js';
8+
import { camelCaseToKebabCase } from '../utils.js';
89
import { TS_ERROR_CODES } from './ts-error-codes.js';
910

1011
/** Build Reverse Lookup Map. It will a map with key as the error code and value as the audit slug. */
1112
export const AUDIT_LOOKUP = Object.values(TS_ERROR_CODES)
1213
.flatMap(v => Object.entries(v))
13-
.reduce<Map<number, AuditSlug>>((lookup, [slug, codes]) => {
14-
codes.forEach(code => lookup.set(code, slug as AuditSlug));
14+
.reduce<Map<number, AuditSlug>>((lookup, [name, codes]) => {
15+
codes.forEach(code =>
16+
lookup.set(code, camelCaseToKebabCase(name) as AuditSlug),
17+
);
1518
return lookup;
1619
}, new Map<number, AuditSlug>());
1720

@@ -21,7 +24,7 @@ export const AUDIT_LOOKUP = Object.values(TS_ERROR_CODES)
2124
* @returns The audit slug.
2225
* @throws Error if the code is not supported.
2326
*/
24-
export function transformTSErrorCodeToAuditSlug(code: number): AuditSlug {
27+
export function tSCodeToAuditSlug(code: number): AuditSlug {
2528
const knownCode = AUDIT_LOOKUP.get(code);
2629
if (knownCode === undefined) {
2730
throw new Error(`Code ${code} not supported.`);

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

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,20 +39,16 @@ describe('getSeverity', () => {
3939
});
4040

4141
describe('getIssueFromDiagnostic', () => {
42-
let diagnosticMock: Diagnostic;
43-
44-
beforeEach(() => {
45-
diagnosticMock = {
46-
code: 222,
47-
category: DiagnosticCategory.Error,
48-
messageText: "Type 'number' is not assignable to type 'string'.",
49-
file: {
50-
fileName: 'file.ts',
51-
getLineAndCharacterOfPosition: () => ({ line: 99 }),
52-
},
53-
start: 4,
54-
} as any;
55-
});
42+
const diagnositcMock = {
43+
code: 222,
44+
category: DiagnosticCategory.Error,
45+
messageText: "Type 'number' is not assignable to type 'string'.",
46+
file: {
47+
fileName: 'file.ts',
48+
getLineAndCharacterOfPosition: () => ({ line: 99 }),
49+
},
50+
start: 4,
51+
} as any;
5652

5753
it('should return valid issue', () => {
5854
expect(getIssueFromDiagnostic(diagnosticMock)).toStrictEqual({
File renamed without changes.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest';
22
import {
33
type TypescriptPluginOptions,
44
typescriptPluginConfigSchema,
5-
} from './config.js';
5+
} from './schema.js';
66

77
describe('TypescriptPlugin Configuration', () => {
88
const tsConfigPath = 'tsconfig.json';
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
import { z } from 'zod';
12
import type { AUDITS } from './constants.js';
3+
import { typescriptPluginConfigSchema } from './schema.js';
24

35
export type AuditSlug = (typeof AUDITS)[number]['slug'];
6+
7+
export type TypescriptPluginOptions = z.infer<
8+
typeof typescriptPluginConfigSchema
9+
> & { onlyAudits?: (string | AuditSlug)[] | undefined };
Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
import type { PluginConfig } from '@code-pushup/models';
2-
import packageJson from '../../package.json';
3-
import type { TypescriptPluginOptions } from './config.js';
2+
import { name as packageName, version } from '../../package.json';
43
import { AUDITS, GROUPS, TYPESCRIPT_PLUGIN_SLUG } from './constants.js';
54
import { createRunnerFunction } from './runner/runner.js';
5+
import type { TypescriptPluginOptions } from './types.js';
66
import { filterAuditsBySlug, filterGroupsByAuditSlug } from './utils.js';
77

88
export function typescriptPlugin(
99
options: TypescriptPluginOptions,
1010
): PluginConfig {
11-
const audits = AUDITS.filter(filterAuditsBySlug(options.onlyAudits));
11+
const { tsConfigPath, onlyAudits } = options;
12+
const filteredAudits = AUDITS.filter(filterAuditsBySlug(onlyAudits));
13+
const filteredGroups = GROUPS.filter(filterGroupsByAuditSlug(onlyAudits));
1214
return {
1315
slug: TYPESCRIPT_PLUGIN_SLUG,
14-
packageName: packageJson.name,
15-
version: packageJson.version,
16+
packageName,
17+
version,
1618
title: 'Typescript',
1719
description: 'Official Code PushUp typescript plugin.',
1820
docsUrl: 'https://www.npmjs.com/package/@code-pushup/typescript-plugin/',
1921
icon: 'typescript',
20-
audits,
21-
groups: GROUPS.filter(filterGroupsByAuditSlug(options.onlyAudits)),
22-
runner: createRunnerFunction({ ...options, audits }),
22+
audits: filteredAudits,
23+
groups: filteredGroups,
24+
runner: createRunnerFunction({ tsConfigPath, filteredAudits }),
2325
};
2426
}

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,16 @@ export function filterGroupsByAuditSlug(slugs?: string[]) {
1717
return true;
1818
};
1919
}
20+
21+
export function camelCaseToKebabCase(string: string): string {
22+
return string
23+
.replace(/([a-z])([A-Z])/g, '$1-$2')
24+
.replace(/[\s_]+/g, '-')
25+
.toLowerCase();
26+
}
27+
28+
export function formatTitle(description: string = '') {
29+
return description
30+
.replace(/-/g, ' ')
31+
.replace(/\b\w/g, letter => letter.toUpperCase());
32+
}

0 commit comments

Comments
 (0)