Skip to content

Commit 68f56c8

Browse files
authored
feat(plugin-typescript): new tests, docs, readme, remove unused async, etc (#904)
1 parent ce4daa6 commit 68f56c8

File tree

16 files changed

+494
-106
lines changed

16 files changed

+494
-106
lines changed

code-pushup.preset.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ export const typescriptPluginConfigNx = async (
143143
};
144144

145145
return {
146-
plugins: [await typescriptPlugin(opt)],
146+
plugins: [typescriptPlugin(opt)],
147147
categories: [
148148
{
149149
slug: 'typescript',

packages/plugin-typescript/README.md

Lines changed: 141 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,18 @@
66

77
🕵️ **Code PushUp plugin for measuring TypeScript quality with compiler diagnostics.** 🔥
88

9-
---
9+
This plugin allows you to measure and track TypeScript compiler diagnostics in your TypeScript/JavaScript project.
10+
It analyzes your codebase using the TypeScript compiler to detect potential issues and configuration problems.
1011

11-
The plugin parses your TypeScript and JavaScript code and lints all audits of the official [TypeScript Compiler]().
12+
TypeScript compiler diagnostics are mapped to Code PushUp audits in the following way:
1213

13-
For more infos visit the [official docs](https://developer.chrome.com/docs/typescript/overview).
14+
- `value`: The number of issues found for a specific TypeScript configuration option -> 3
15+
- `displayValue`: The number of issues found -> 3 issues
16+
- `score`: Binary scoring - 1 if no issues are found, 0 if any issues exist
17+
- Issues are mapped to audit details, containing:
18+
- Source file location
19+
- Error message from TypeScript compiler
20+
- Code reference where the issue was found
1421

1522
## Getting started
1623

@@ -50,41 +57,148 @@ For more infos visit the [official docs](https://developer.chrome.com/docs/types
5057

5158
4. Run the CLI with `npx code-pushup collect` and view or upload the report (refer to [CLI docs](../cli/README.md)).
5259

53-
### Optionally set up categories
60+
## About documentation coverage
5461

55-
Reference audits (or groups) which you wish to include in custom categories (use `npx code-pushup print-config --onlyPlugins=typescript` to list audits and groups).
62+
The TypeScript plugin analyzes your codebase using the TypeScript compiler to identify potential issues and enforce best practices. It helps ensure type safety and maintainability of your TypeScript code.
5663

57-
Assign weights based on what influence each Lighthouse audit has on the overall category score (assign weight 0 to only include as extra info, without influencing category score).
58-
The plugin exports the helper `typescriptAuditRef` and `typescriptGroupRef` to reference Lighthouse category references for audits and groups.
64+
The plugin provides multiple audits grouped into different categories like:
5965

60-
#### Reference audits directly with `typescriptGroupRef`
66+
- Language and Environment - Checks configuration for TypeScript features like decorators, JSX, target version
67+
- Type Checking - Validates strict null checks, implicit any/this, function types
68+
- Module Resolution - Verifies module imports/exports and resolution settings
69+
- Build/Emit Options - Checks output generation and optimization settings
70+
- Control Flow - Analyzes code flow, unreachable code, switch statements
6171

62-
```ts
63-
import { typescriptGroupRef } from './utils';
72+
Each audit:
73+
74+
- Checks for specific TypeScript compiler errors and warnings
75+
- Provides a score based on the number of issues found
76+
- Includes detailed error messages and locations
77+
78+
The audits are organized into logical groups to give you a comprehensive view of your TypeScript configuration and code quality. You can:
79+
80+
- Use all groups for complete TypeScript analysis
81+
- Focus on specific groups or individual audits based on your needs
82+
83+
## Plugin architecture
84+
85+
### Plugin configuration specification
86+
87+
The plugin accepts the following parameters:
6488

65-
export default {
66-
// ...
67-
categories: [],
68-
};
89+
#### TsConfigPath
90+
91+
Required parameter. The `tsConfigPath` option accepts a string that defines the path to your `tsconfig.json` file.
92+
93+
```js
94+
typescriptPlugin({
95+
tsConfigPath: './tsconfig.json',
96+
}),
6997
```
7098

71-
#### Reference groups with `typescriptAuditRef`
99+
#### OnlyAudits
100+
101+
Optional parameter. The `onlyAudits` option allows you to specify which documentation types you want to measure. Only the specified audits will be included in the results. Example:
102+
103+
```js
104+
typescriptPlugin({
105+
tsConfigPath: './tsconfig.json',
106+
onlyAudits: [
107+
'no-implicit-any'
108+
] // Only measure documentation for classes and functions
109+
}),
110+
```
111+
112+
### Audits and group
113+
114+
This plugin provides a list of groups to cover different TypeScript configuration options and their areas of responsibility.
72115

73-
The TypeScript categories are reflected as groups.
74-
Referencing individual audits offers more granularity. However, keep maintenance costs of a higher number of audits in mind as well.
116+
```ts
117+
// ...
118+
categories: [
119+
{
120+
slug: 'typescript',
121+
title: 'TypeScript',
122+
refs: [
123+
{
124+
slug: 'language-and-environment',
125+
weight: 1,
126+
type: 'group',
127+
plugin: 'typescript'
128+
},
129+
// ...
130+
],
131+
},
132+
// ...
133+
],
134+
```
135+
136+
Each TypeScript configuration option still has its own audit. So when you want to include a subset of configuration options or assign different weights to them, you can do so in the following way:
75137

76138
```ts
77-
import { typescriptAuditRef } from './utils';
139+
// ...
140+
categories: [
141+
{
142+
slug: 'typescript',
143+
title: 'TypeScript',
144+
refs: [
145+
{
146+
type: 'audit',
147+
plugin: 'typescript',
148+
slug: 'no-implicit-any',
149+
weight: 2,
150+
},
151+
{
152+
type: 'audit',
153+
plugin: 'typescript',
154+
slug: 'no-explicit-any',
155+
weight: 1,
156+
},
157+
// ...
158+
],
159+
},
160+
// ...
161+
],
162+
```
163+
164+
### Audit output
78165

79-
export default {
80-
// ...
81-
categories: [
166+
The plugin outputs a single audit that measures the overall documentation coverage percentage of your codebase.
167+
168+
For instance, this is an example of the plugin output:
169+
170+
```json
171+
{
172+
"packageName": "@code-pushup/typescript-plugin",
173+
"version": "0.57.0",
174+
"title": "Typescript",
175+
"slug": "typescript",
176+
"icon": "typescript",
177+
"date": "2024-12-25T11:10:22.646Z",
178+
"duration": 2059,
179+
"audits": [
82180
{
83-
slug: 'pwa',
84-
title: 'PWA',
85-
isBinary: true,
86-
refs: [typescriptAuditRef('installable-manifest', 2), typescriptAuditRef('splash-screen', 1), typescriptAuditRef('themed-omnibox', 1), typescriptAuditRef('content-width', 1), typescriptAuditRef('themed-omnibox', 2), typescriptAuditRef('viewport', 2), typescriptAuditRef('maskable-icon', 1), typescriptAuditRef('pwa-cross-browser', 0), typescriptAuditRef('pwa-page-transitions', 0), typescriptAuditRef('pwa-each-page-has-url', 0)],
87-
},
181+
"slug": "experimental-decorators",
182+
"value": 0,
183+
"score": 1,
184+
"title": "ExperimentalDecorators",
185+
"docsUrl": "https://www.typescriptlang.org/tsconfig/#experimentalDecorators"
186+
}
88187
],
89-
};
188+
"description": "Official Code PushUp typescript plugin.",
189+
"docsUrl": "https://www.npmjs.com/package/@code-pushup/typescript-plugin/",
190+
"groups": [
191+
{
192+
"slug": "language-and-environment",
193+
"refs": [
194+
{
195+
"slug": "experimental-decorators",
196+
"weight": 1
197+
}
198+
],
199+
"title": "LanguageAndEnvironment",
200+
"description": "Configuration options for TypeScript language features and runtime environment"
201+
}
202+
]
203+
}
90204
```
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"compilerOptions": {
3+
"rootDir": "./src",
4+
"strict": true,
5+
"target": "ES6",
6+
"module": "CommonJS",
7+
"noImplicitAny": true,
8+
"strictNullChecks": true,
9+
"strictFunctionTypes": true,
10+
"strictBindCallApply": true,
11+
"strictPropertyInitialization": true,
12+
"alwaysStrict": true
13+
},
14+
"exclude": ["src/**/*.ts", "src/**/*.js"]
15+
}

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ describe('TypescriptPlugin Configuration', () => {
1212
expect(() =>
1313
typescriptPluginConfigSchema.parse({
1414
tsConfigPath,
15-
onlyAudits: ['ts-code-1065', 'ts-code-2354'],
15+
onlyAudits: ['no-implicit-any', 'module-resolution-node'],
1616
} satisfies TypescriptPluginOptions),
1717
).not.toThrow();
1818
});
@@ -41,7 +41,7 @@ describe('TypescriptPlugin Configuration', () => {
4141
expect(() =>
4242
typescriptPluginConfigSchema.parse({
4343
tsConfigPath,
44-
onlyAudits: ['ts-code-1065', 'argument-expected-1011'],
44+
onlyAudits: ['no-implicit-any', 'module-resolution-node'],
4545
} satisfies TypescriptPluginOptions),
4646
).not.toThrow();
4747
});
@@ -71,5 +71,17 @@ describe('TypescriptPlugin Configuration', () => {
7171
}),
7272
).toThrow('invalid_type');
7373
});
74+
75+
it('throws for unknown audit slug', () => {
76+
expect(
77+
() =>
78+
typescriptPluginConfigSchema.parse({
79+
tsConfigPath,
80+
onlyAudits: ['unknown-audit'],
81+
}),
82+
// Message too large because enums validation
83+
// eslint-disable-next-line vitest/require-to-throw-message
84+
).toThrow();
85+
});
7486
});
7587
});

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import type { Audit, Group } from '@code-pushup/models';
2-
import { TS_ERROR_CODES } from './runner/ts-error-codes.js';
3-
import { camelCaseToKebabCase, formatTitle } from './utils.js';
2+
import { camelCaseToKebabCase, formatSlugToTitle } from '@code-pushup/utils';
3+
import {
4+
GROUPS_DESCRIPTIONS,
5+
TS_ERROR_CODES,
6+
} from './runner/ts-error-codes.js';
47

58
export const TYPESCRIPT_PLUGIN_SLUG = 'typescript';
69

710
export const AUDITS = Object.values(TS_ERROR_CODES)
811
.flatMap(i => Object.entries(i))
912
.reduce<Audit[]>((audits, [name]) => {
1013
const slug = camelCaseToKebabCase(name);
11-
const title = formatTitle(name);
14+
const title = formatSlugToTitle(name);
1215
return [
1316
...audits,
1417
{
@@ -29,7 +32,9 @@ const weights = {
2932
export const GROUPS: Group[] = Object.entries(TS_ERROR_CODES).map(
3033
([groupSlug, auditMap]) => ({
3134
slug: camelCaseToKebabCase(groupSlug),
32-
title: formatTitle(groupSlug),
35+
title: formatSlugToTitle(groupSlug),
36+
description:
37+
GROUPS_DESCRIPTIONS[groupSlug as keyof typeof GROUPS_DESCRIPTIONS],
3338
refs: Object.keys(auditMap).map(audit => ({
3439
slug: camelCaseToKebabCase(audit),
3540
weight: weights[audit as keyof typeof weights] ?? 1,

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,29 @@
11
/* eslint-disable @typescript-eslint/no-magic-numbers, unicorn/numeric-separators-style */
22

3+
export const GROUPS_DESCRIPTIONS = {
4+
languageAndEnvironment:
5+
'Configuration options for TypeScript language features and runtime environment, including decorators, JSX support, target ECMAScript version, and class field behaviors',
6+
interopConstraints:
7+
'Settings that control how TypeScript interoperates with other JavaScript code, including module imports/exports and case sensitivity rules',
8+
watchOptions:
9+
'Configuration for TypeScript watch mode behavior, including file watching strategies and dependency tracking',
10+
projectReferences:
11+
'Options for managing TypeScript project references, composite projects, and build optimization settings',
12+
moduleResolution:
13+
'Settings that control how TypeScript finds and resolves module imports, including Node.js resolution, package.json exports/imports, and module syntax handling',
14+
typeCheckingBehavior:
15+
'Configuration for TypeScript type checking strictness and error reporting, including property access rules and method override checking',
16+
controlFlowOptions:
17+
'Settings that affect code flow analysis, including handling of unreachable code, unused labels, switch statements, and async/generator functions',
18+
strictChecks:
19+
'Strict type checking options that enable additional compile-time verifications, including null checks, implicit any/this, and function type checking',
20+
buildEmitOptions:
21+
'Configuration options that control TypeScript output generation, including whether to emit files, how to handle comments and declarations, and settings for output optimization and compatibility helpers',
22+
};
23+
24+
/** This is the list of error codes that can be triggered by the TypeScript compiler.
25+
* It's divided into: category -> compiler option -> error codes (that might trigger)
26+
*/
327
export const TS_ERROR_CODES = {
428
languageAndEnvironment: {
529
experimentalDecorators: [1240, 1241, 1242, 1243, 1244, 1270, 1271, 1272],

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,23 @@ describe('getDiagnostics', () => {
1919
expect(res).toHaveLength(4);
2020
expect(res.at(0)?.code).toBe(2307);
2121
});
22+
23+
it('should throw if missing tsconfig path', async () => {
24+
await expect(
25+
getDiagnostics({
26+
tsConfigPath: 'missing-tsconfig.json',
27+
}),
28+
).rejects.toThrow('tsconfig not found at: missing-tsconfig.json');
29+
});
30+
31+
it('should throw if no files matched by the TypeScript configuration', async () => {
32+
await expect(
33+
getDiagnostics({
34+
tsConfigPath:
35+
'packages/plugin-typescript/mocks/fixtures/basic-setup/tsconfig-with-exclude.json',
36+
}),
37+
).rejects.toThrow(
38+
'No files matched by the TypeScript configuration. Check your "include", "exclude" or "files" settings.',
39+
);
40+
});
2241
});

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ export async function getDiagnostics(
2121
try {
2222
await access(configPath);
2323
} catch {
24-
throw new Error(`tsconfig not found at: ${configPath}`);
24+
throw new Error(`tsconfig not found at: ${tsConfigPath}`);
2525
}
2626

2727
const configFile = (await readFile(configPath)).toString();
28+
2829
const { config: strictConfig } = parseConfigFileTextToJson(
2930
configPath,
3031
configFile,

0 commit comments

Comments
 (0)