Skip to content

Commit cee894f

Browse files
committed
wip
1 parent 1b4d4bb commit cee894f

File tree

9 files changed

+131
-50
lines changed

9 files changed

+131
-50
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: [typescriptPlugin(opt)],
146+
plugins: [await typescriptPlugin(opt)],
147147
categories: [
148148
{
149149
slug: 'typescript',

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export const GROUPS_DESCRIPTIONS = {
2525
* It's divided into: category -> compiler option -> error codes (that might trigger)
2626
*/
2727
export const TS_ERROR_CODES = {
28-
languageAndEnvironment: {
28+
languageAndEnvivronment: {
2929
experimentalDecorators: [1240, 1241, 1242, 1243, 1244, 1270, 1271, 1272],
3030
emitDecoratorMetadata: [1240, 1241, 1272],
3131
jsx: [1341, 18007, 18034, 18035, 18053],

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ import { createRunnerFunction } from './runner/runner.js';
1010
import type { TypescriptPluginOptions } from './types.js';
1111
import { filterAuditsBySlug, filterGroupsByAuditSlug } from './utils.js';
1212

13-
export function typescriptPlugin(
13+
export async function typescriptPlugin(
1414
options?: TypescriptPluginOptions,
15-
): PluginConfig {
15+
): Promise<PluginConfig> {
1616
const { tsConfigPath = DEFAULT_TS_CONFIG, onlyAudits } = options ?? {};
17+
// const defaultConfig = loadDefaultTsConfig(await getCurrentTsVersion());
18+
// console.log(defaultConfig);
1719
const filteredAudits = AUDITS.filter(filterAuditsBySlug(onlyAudits));
1820
const filteredGroups = GROUPS.filter(filterGroupsByAuditSlug(onlyAudits));
1921
return {

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import type { CompilerOptions } from 'typescript';
12
import type { Audit, Group } from '@code-pushup/models';
3+
import { executeProcess } from '@code-pushup/utils';
4+
import type { SemVerString } from '../../tools/generate-ts-config.js';
25

36
export function filterAuditsBySlug(slugs?: string[]) {
47
return ({ slug }: Audit) => {
@@ -17,3 +20,23 @@ export function filterGroupsByAuditSlug(slugs?: string[]) {
1720
return true;
1821
};
1922
}
23+
24+
export async function getCurrentTsVersion(): Promise<SemVerString> {
25+
const { stdout } = await executeProcess({
26+
command: 'npx',
27+
args: ['tsc', '--version'],
28+
});
29+
return stdout.trim() as SemVerString;
30+
}
31+
32+
export async function loadDefaultTsConfig(version: SemVerString) {
33+
try {
34+
const module = await import(`./${version}.ts`);
35+
//const module = await import(`.packages/plugin-typescript/src/lib/default-ts-configs/1.6.2.ts`);
36+
return module.default as CompilerOptions;
37+
} catch (error) {
38+
throw new Error(
39+
`Could not find default TS config for version ${version}. /n ${(error as Error).message}`,
40+
);
41+
}
42+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`prepareTsConfigFileContent > should parse tsconfig.json created from init command 1`] = `
4+
"{
5+
"compilerOptions": {"incremental": true"composite": true"tsBuildInfoFile": "./.tsbuildinfo""disableSourceOfProjectReferenceRedirect": true"disableSolutionSearching": true"disableReferencedProjectLoad": true"target": "es2016""lib": []"jsx": "preserve""experimentalDecorators": true"emitDecoratorMetadata": true"jsxFactory": """jsxFragmentFactory": """jsxImportSource": """reactNamespace": """noLib": true"useDefineForClassFields": true"moduleDetection": "auto""module": "commonjs""rootDir": "./""moduleResolution": "node""baseUrl": "./""paths": {}"rootDirs": []"typeRoots": []"types": []"allowUmdGlobalAccess": true"moduleSuffixes": []"resolveJsonModule": true"noResolve": true"allowJs": true"checkJs": true"maxNodeModuleJsDepth": 1"declaration": true"declarationMap": true"emitDeclarationOnly": true"sourceMap": true"outFile": "./""outDir": "./""removeComments": true"noEmit": true"importHelpers": true"importsNotUsedAsValues": "remove""downlevelIteration": true"sourceRoot": """mapRoot": """inlineSourceMap": true"inlineSources": true"emitBOM": true"newLine": "crlf""stripInternal": true"noEmitHelpers": true"noEmitOnError": true"preserveConstEnums": true"declarationDir": "./""preserveValueImports": true"isolatedModules": true"allowSyntheticDefaultImports": true"esModuleInterop": true"preserveSymlinks": true"forceConsistentCasingInFileNames": true"strict": true"noImplicitAny": true"strictNullChecks": true"strictFunctionTypes": true"strictBindCallApply": true"strictPropertyInitialization": true"noImplicitThis": true"useUnknownInCatchVariables": true"alwaysStrict": true"noUnusedLocals": true"noUnusedParameters": true"exactOptionalPropertyTypes": true"noImplicitReturns": true"noFallthroughCasesInSwitch": true"noUncheckedIndexedAccess": true"noImplicitOverride": true"noPropertyAccessFromIndexSignature": true"allowUnusedLabels": true"allowUnreachableCode": true"skipDefaultLibCheck": true"skipLibCheck": true}
6+
}"
7+
`;
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1-
import { updateKnownConfigMap } from './generate-ts-config';
1+
import {updateKnownConfigMap,} from './generate-ts-config.js';
22

3-
(async () => await updateKnownConfigMap())();
3+
// eslint-disable-next-line unicorn/prefer-top-level-await
4+
(async () => {
5+
await updateKnownConfigMap();
6+
})();

packages/plugin-typescript/tools/generate-ts-config.integration.test.ts

Lines changed: 0 additions & 13 deletions
This file was deleted.

packages/plugin-typescript/tools/generate-ts-config.ts

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@
2020
import { executeProcess } from '@push-based/nx-verdaccio/src/internal/execute-process';
2121
import { ensureDirectoryExists } from '@push-based/nx-verdaccio/src/internal/file-system';
2222
import { readdir, writeFile } from 'node:fs/promises';
23+
// eslint-disable-next-line unicorn/import-style
2324
import { basename, join } from 'node:path';
2425
import * as process from 'node:process';
2526
import type { CompilerOptions } from 'typescript';
26-
import { readJsonFile, readTextFile } from '@code-pushup/utils';
27+
import { readTextFile } from '@code-pushup/utils';
2728

2829
export type SemVerString = `${number}.${number}.${number}`;
2930

@@ -81,9 +82,10 @@ export async function updateKnownConfigMap() {
8182
version => !knownVersions.includes(version),
8283
);
8384

84-
console.log(
85-
`generate TS config defaults for ${versionsToGenerate.length} versions`,
85+
console.info(
86+
`Generate TS config defaults for ${versionsToGenerate.length} versions: `,
8687
);
88+
console.info(versionsToGenerate);
8789

8890
await Promise.all(versionsToGenerate.map(saveDefaultTsConfig));
8991
}
@@ -94,7 +96,10 @@ export async function saveDefaultTsConfig(version: SemVerString) {
9496
await cleanupNpmCache(version);
9597
return writeFile(
9698
join(TS_CONFIG_DIR, `${version}.ts`),
97-
`export default ${JSON.stringify(config, null, 2)}`,
99+
[
100+
`const config = ${JSON.stringify(config, null, 2)}`,
101+
`export default config;`,
102+
].join('\n'),
98103
);
99104
}
100105

@@ -118,18 +123,10 @@ export async function extractTsConfig(
118123
const dir = join(TMP_TS_CONFIG_DIR, version);
119124
await ensureDirectoryExists(dir);
120125
try {
121-
const fileContent = await readTextFile(join(dir, 'tsconfig.json'));
122-
123-
await writeFile(
124-
join(TMP_TS_CONFIG_DIR, version, `tsconfig.clean.json`),
125-
prepareTsConfigFileContent(fileContent),
126-
);
127-
const stConfigJson = JSON.parse(prepareTsConfigFileContent(fileContent));
128-
129-
return stConfigJson;
130-
} catch (e) {
126+
return parseTsConfigJson(await readTextFile(join(dir, 'tsconfig.json')));
127+
} catch (error) {
131128
throw new Error(
132-
`Failed to extract tsconfig.json for version ${version}. \n ${(e as Error).message}`,
129+
`Failed to extract tsconfig.json for version ${version}. \n ${(error as Error).message}`,
133130
);
134131
}
135132
}
@@ -170,9 +167,10 @@ export async function getRelevantVersions() {
170167
});
171168
const allVersions: SemVerString[] = JSON.parse(stdout);
172169
return allVersions.filter(version => {
173-
const [major, minor, patch] = version.split('.').map(Number);
170+
const [major = 0, minor = 0, patch = 0] = version.split('.').map(Number);
174171
return (
175172
major >= 1 &&
173+
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
176174
minor >= 6 &&
177175
patch >= 2 &&
178176
!version.includes('rc') &&
@@ -182,13 +180,19 @@ export async function getRelevantVersions() {
182180
});
183181
}
184182

185-
export function prepareTsConfigFileContent(fileContent: string) {
183+
/**
184+
* Parse the tsconfig.json file content into a CompilerOptions object.
185+
* tsconfig.json files can have comments and trailing commas, which are not valid JSON.
186+
* This function removes comments and trailing commas and parses the JSON.
187+
* @param fileContent
188+
*/
189+
export function parseTsConfigJson(fileContent: string) {
186190
const parsedFileContent = fileContent
191+
.trim()
187192
.split('\n')
188193
.map(line =>
189194
line
190195
// replace all /**/ comments with empty string
191-
.replace(/\r/g, '')
192196
.replace(/\/\*.*\*\//g, '')
193197
// replace all // strings with empty string
194198
.replace(/\/\//g, '')
@@ -197,16 +201,17 @@ export function prepareTsConfigFileContent(fileContent: string) {
197201
.trim(),
198202
)
199203
.filter(s => s !== '')
204+
// missing comma dua to newly uncommented lines
200205
.map(s => {
201-
// if it is a property with a value, check if there is a comma at the end
202-
if (s.match(/:\s*[^,\n\r]*$/)) {
206+
// if is si noa a opening or closing object bracket "{" or "}"
207+
if (!/[{}[]$/.test(s)) {
208+
// add a comma at the end it is missing
203209
return s.replace(/:\s*([^,]*)$/, ': $1,');
204210
}
205211
return s;
206212
})
207213
.join('')
208-
// last camma is not allowed
214+
// remove dangling commas
209215
.replace(/,\s*}/gm, '}');
210-
211-
return parsedFileContent;
216+
return JSON.parse(parsedFileContent) as CompilerOptions;
212217
}
Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,54 @@
11
import { describe, expect, it } from 'vitest';
2-
import { prepareTsConfigFileContent } from './generate-ts-config.js';
2+
import { parseTsConfigJson } from './generate-ts-config.js';
3+
4+
describe('parseTsConfigJson', () => {
5+
it('should work', async () => {
6+
const testContent = `{
7+
"compilerOptions": {
8+
/* Visit https://aka.ms/tsconfig to read more about this file */
9+
10+
/* Projects */
11+
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
12+
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
13+
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
14+
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
15+
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
16+
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
17+
18+
/* Type Checking */
19+
"strict": true, /* Enable all strict type-checking options. */
20+
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
21+
22+
/* Completeness */
23+
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
24+
"skipLibCheck": true /* Skip type checking all .d.ts files. */
25+
// "preserveConstEnums": true, /* ... */
26+
}
27+
}`;
28+
expect(parseTsConfigJson(testContent)).toStrictEqual({
29+
compilerOptions: {
30+
incremental: true,
31+
composite: true,
32+
tsBuildInfoFile: './.tsbuildinfo',
33+
disableSourceOfProjectReferenceRedirect: true,
34+
disableSolutionSearching: true,
35+
disableReferencedProjectLoad: true,
36+
strict: true,
37+
noImplicitAny: true,
38+
skipDefaultLibCheck: true,
39+
skipLibCheck: true,
40+
preserveConstEnums: true,
41+
},
42+
});
43+
});
344

4-
describe('prepareTsConfigFileContent', () => {
545
it('should remove empty lines', async () => {
646
const testContent = `
747
{
848
949
}
1050
`;
11-
expect(prepareTsConfigFileContent(testContent)).toBe(`{}`);
51+
expect(parseTsConfigJson(testContent)).toStrictEqual({});
1252
});
1353

1454
it('should remove block comments', async () => {
@@ -17,23 +57,37 @@ describe('prepareTsConfigFileContent', () => {
1757
/* property block comment */
1858
"prop": 42, /* value block comment */
1959
}`;
20-
expect(prepareTsConfigFileContent(testContent)).toBe(`{"prop": 42}`);
60+
expect(parseTsConfigJson(testContent)).toStrictEqual({ prop: 42 });
2161
});
2262

2363
it('should remove line comments characters', async () => {
2464
const testContent = `{
2565
// "prop": 42,
2666
}`;
27-
expect(prepareTsConfigFileContent(testContent)).toBe(`{"prop": 42}`);
67+
expect(parseTsConfigJson(testContent)).toStrictEqual({ prop: 42 });
2868
});
2969

3070
it('should add missing comma for existing properties before a inline comment property', async () => {
3171
const testContent = `{
3272
"pro1": 42
3373
// "prop2": "value"
3474
}`;
35-
expect(prepareTsConfigFileContent(testContent)).toBe(
36-
`{"pro1": 42,"prop2": "value"}`,
37-
);
75+
expect(parseTsConfigJson(testContent)).toStrictEqual({
76+
pro1: 42,
77+
prop2: 'value',
78+
});
79+
});
80+
81+
it('should not comma for opening objects "{"', async () => {
82+
const testContent = `{
83+
"compilerOptions": {
84+
// "prop2": [
85+
"value"
86+
]
87+
}
88+
}`;
89+
expect(parseTsConfigJson(testContent)).toStrictEqual({
90+
compilerOptions: { prop2: ['value'] },
91+
});
3892
});
3993
});

0 commit comments

Comments
 (0)