From d05243b3f62b031a1ccbf722a3455ad4256b2ffa Mon Sep 17 00:00:00 2001 From: rubiesonthesky <2591240+rubiesonthesky@users.noreply.github.com> Date: Wed, 8 Jan 2025 00:24:45 +0200 Subject: [PATCH 1/2] fix: writing TypeScript config file --- .../writeMultiTypeScriptConfig.test.ts | 58 +++++++++ .../writeMultiTypeScriptConfig.ts | 110 ++++++++++-------- .../writeSingleTypeScriptConfig.test.ts | 22 ++++ .../writeSingleTypeScriptConfig.ts | 52 +++++---- 4 files changed, 170 insertions(+), 72 deletions(-) create mode 100644 src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.test.ts create mode 100644 src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.test.ts diff --git a/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.test.ts b/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.test.ts new file mode 100644 index 000000000..4981e68ca --- /dev/null +++ b/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.test.ts @@ -0,0 +1,58 @@ +import { describe, expect, it } from "vitest"; + +import { InitializationImprovement } from "./improvements.js"; +import { generateMultiTypeScriptConfig } from "./writeMultiTypeScriptConfig.js"; + +describe("writeMultiTypeScriptConfig", () => { + it("creates multi TypeScript config", () => { + const config = generateMultiTypeScriptConfig({ + fileName: "typestat.json", + improvements: new Set([ + InitializationImprovement.MissingProperties, + InitializationImprovement.NoImplicitAny, + InitializationImprovement.NoImplicitThis, + InitializationImprovement.NoInferableTypes, + InitializationImprovement.StrictNullChecks, + ]), + project: { filePath: "./tsconfig.json" }, + testFiles: "test/**/*.{ts,tsx}", + }); + + expect(config).toStrictEqual([ + { + fixes: { + incompleteTypes: true, + noImplicitAny: true, + noImplicitThis: true, + noInferableTypes: true, + strictNonNullAssertions: true, + }, + include: ["test/**/*.{ts,tsx}"], + projectPath: "./tsconfig.json", + types: { + strictNullChecks: true, + }, + }, + { + exclude: ["test/**/*.{ts,tsx}"], + fixes: { + incompleteTypes: true, + noImplicitAny: true, + noImplicitThis: true, + noInferableTypes: true, + }, + projectPath: "./tsconfig.json", + }, + { + fixes: { + incompleteTypes: true, + noImplicitAny: true, + noImplicitThis: true, + noInferableTypes: true, + }, + include: ["test/**/*.{ts,tsx}"], + projectPath: "./tsconfig.json", + }, + ]); + }); +}); diff --git a/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.ts b/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.ts index 9350cb3a0..cc4cf6732 100644 --- a/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.ts +++ b/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.ts @@ -1,5 +1,6 @@ import * as fs from "node:fs/promises"; +import { Fixes, RawTypeStatOptions } from "../../options/types.js"; import { ProjectDescription } from "../initializeProject/shared.js"; import { InitializationImprovement } from "./improvements.js"; @@ -11,61 +12,74 @@ export interface MultiTypeScriptConfigSettings { testFiles?: string; } -export const writeMultiTypeScriptConfig = async ({ - fileName, +export const writeMultiTypeScriptConfig = async ( + settings: MultiTypeScriptConfigSettings, +) => { + const config = generateMultiTypeScriptConfig(settings); + await fs.writeFile(settings.fileName, JSON.stringify(config, undefined, 4)); +}; + +export const generateMultiTypeScriptConfig = ({ improvements, project, sourceFiles, testFiles, }: MultiTypeScriptConfigSettings) => { - await fs.writeFile( - fileName, - JSON.stringify( - [ - { - fixes: { - ...printImprovements(improvements), - strictNonNullAssertions: true, - }, - ...(testFiles && { include: [testFiles] }), - projectPath: project.filePath, - types: { - strictNullChecks: true, - }, - }, - { - ...(testFiles && { exclude: [testFiles] }), - fixes: printImprovements(improvements), - ...(sourceFiles && { include: [sourceFiles] }), - projectPath: project.filePath, - }, - { - fixes: printImprovements(improvements), - ...(testFiles - ? { include: [testFiles, sourceFiles] } - : { include: [sourceFiles] }), - projectPath: project.filePath, - }, - ], - undefined, - 4, - ), - ); + const fixes = printImprovements(improvements); + + const stage1: Partial = { + fixes: { + ...fixes, + strictNonNullAssertions: true, + }, + projectPath: project.filePath, + types: { + strictNullChecks: true, + }, + }; + + const stage2: Partial = { + fixes, + projectPath: project.filePath, + }; + + const stage3: Partial = { + fixes, + projectPath: project.filePath, + }; + + if (testFiles) { + // @ts-expect-error Cannot assign to 'include' because it is a read-only property. + stage1.include = [testFiles]; + // @ts-expect-error Property 'exclude' does not exist on type 'Partial'. + stage2.exclude = [testFiles]; + // @ts-expect-error Cannot assign to 'include' because it is a read-only property. + stage3.include = [testFiles, sourceFiles].filter(Boolean); + } else { + // @ts-expect-error Cannot assign to 'include' because it is a read-only property. + stage3.include = [sourceFiles].filter(Boolean); + } + + if (sourceFiles) { + // @ts-expect-error Cannot assign to 'include' because it is a read-only property. + stage2.include = [sourceFiles]; + } + + return [stage1, stage2, stage3]; }; const printImprovements = ( improvements: ReadonlySet, -) => { - return { - incompleteTypes: true, - ...(improvements.has(InitializationImprovement.NoImplicitAny) && { - noImplicitAny: true, - }), - ...(improvements.has(InitializationImprovement.NoInferableTypes) && { - inferableTypes: true, - }), - ...(improvements.has(InitializationImprovement.NoImplicitThis) && { - noImplicitThis: true, - }), - }; +): Partial => { + const fixes: Partial = { incompleteTypes: true }; + if (improvements.has(InitializationImprovement.NoImplicitAny)) { + fixes.noImplicitAny = true; + } + if (improvements.has(InitializationImprovement.NoInferableTypes)) { + fixes.noInferableTypes = true; + } + if (improvements.has(InitializationImprovement.NoImplicitThis)) { + fixes.noImplicitThis = true; + } + return fixes; }; diff --git a/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.test.ts b/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.test.ts new file mode 100644 index 000000000..dbffed0c3 --- /dev/null +++ b/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, it } from "vitest"; + +import { InitializationImprovement } from "./improvements.js"; +import { generateSingleTypeScriptConfig } from "./writeSingleTypeScriptConfig.js"; + +describe("writeMultiTypeScriptConfig", () => { + it("creates multi TypeScript config", () => { + const config = generateSingleTypeScriptConfig({ + fileName: "typestat.json", + improvements: new Set([InitializationImprovement.NoImplicitAny]), + project: { filePath: "./tsconfig.json" }, + }); + + expect(config).toStrictEqual({ + fixes: { + incompleteTypes: true, + noImplicitAny: true, + }, + projectPath: "./tsconfig.json", + }); + }); +}); diff --git a/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.ts b/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.ts index c4a0febb1..a7210ab2e 100644 --- a/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.ts +++ b/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.ts @@ -1,5 +1,6 @@ import * as fs from "node:fs/promises"; +import { Fixes, RawTypeStatOptions } from "../../options/types.js"; import { ProjectDescription } from "../initializeProject/shared.js"; import { InitializationImprovement } from "./improvements.js"; @@ -10,36 +11,39 @@ export interface SingleTypeScriptConfigSettings { sourceFiles?: string; } -export const writeSingleTypeScriptConfig = async ({ - fileName, +export const writeSingleTypeScriptConfig = async ( + settings: SingleTypeScriptConfigSettings, +) => { + const config = generateSingleTypeScriptConfig(settings); + await fs.writeFile(settings.fileName, JSON.stringify(config, undefined, 4)); +}; + +export const generateSingleTypeScriptConfig = ({ improvements, project, sourceFiles, }: SingleTypeScriptConfigSettings) => { - await fs.writeFile( - fileName, - JSON.stringify( - { - fixes: printImprovements(improvements), - ...(sourceFiles && { include: [sourceFiles] }), - projectPath: project.filePath, - }, - undefined, - 4, - ), - ); + const config: Partial = { + fixes: printImprovements(improvements), + projectPath: project.filePath, + }; + + if (sourceFiles) { + // @ts-expect-error Cannot assign to 'include' because it is a read-only property. + config.include = [sourceFiles]; + } + return config; }; const printImprovements = ( improvements: ReadonlySet, -) => { - return { - incompleteTypes: true, - ...(improvements.has(InitializationImprovement.NoImplicitAny) && { - noImplicitAny: true, - }), - ...(improvements.has(InitializationImprovement.NoImplicitThis) && { - noImplicitThis: true, - }), - }; +): Partial => { + const fixes: Partial = { incompleteTypes: true }; + if (improvements.has(InitializationImprovement.NoImplicitAny)) { + fixes.noImplicitAny = true; + } + if (improvements.has(InitializationImprovement.NoImplicitThis)) { + fixes.noImplicitThis = true; + } + return fixes; }; From 27560ecf29254ff09f6c607f71f255562c4d77d4 Mon Sep 17 00:00:00 2001 From: rubiesonthesky <2591240+rubiesonthesky@users.noreply.github.com> Date: Mon, 13 Jan 2025 10:40:07 +0200 Subject: [PATCH 2/2] improve code quality based on PR comments --- .../writeMultiTypeScriptConfig.test.ts | 2 +- .../writeMultiTypeScriptConfig.ts | 23 +++++-------------- .../writeSingleTypeScriptConfig.test.ts | 5 ++++ .../writeSingleTypeScriptConfig.ts | 6 +---- 4 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.test.ts b/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.test.ts index 4981e68ca..675675b5b 100644 --- a/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.test.ts +++ b/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.test.ts @@ -34,13 +34,13 @@ describe("writeMultiTypeScriptConfig", () => { }, }, { - exclude: ["test/**/*.{ts,tsx}"], fixes: { incompleteTypes: true, noImplicitAny: true, noImplicitThis: true, noInferableTypes: true, }, + include: undefined, projectPath: "./tsconfig.json", }, { diff --git a/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.ts b/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.ts index cc4cf6732..d5aba90bf 100644 --- a/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.ts +++ b/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.ts @@ -1,6 +1,7 @@ import * as fs from "node:fs/promises"; import { Fixes, RawTypeStatOptions } from "../../options/types.js"; +import { isNotUndefined } from "../../shared/arrays.js"; import { ProjectDescription } from "../initializeProject/shared.js"; import { InitializationImprovement } from "./improvements.js"; @@ -32,6 +33,7 @@ export const generateMultiTypeScriptConfig = ({ ...fixes, strictNonNullAssertions: true, }, + include: testFiles ? [testFiles] : undefined, projectPath: project.filePath, types: { strictNullChecks: true, @@ -40,31 +42,18 @@ export const generateMultiTypeScriptConfig = ({ const stage2: Partial = { fixes, + include: sourceFiles ? [sourceFiles] : undefined, projectPath: project.filePath, }; + const stage3Include = [testFiles, sourceFiles].filter(isNotUndefined); + const stage3: Partial = { fixes, + include: stage3Include.length ? stage3Include : undefined, projectPath: project.filePath, }; - if (testFiles) { - // @ts-expect-error Cannot assign to 'include' because it is a read-only property. - stage1.include = [testFiles]; - // @ts-expect-error Property 'exclude' does not exist on type 'Partial'. - stage2.exclude = [testFiles]; - // @ts-expect-error Cannot assign to 'include' because it is a read-only property. - stage3.include = [testFiles, sourceFiles].filter(Boolean); - } else { - // @ts-expect-error Cannot assign to 'include' because it is a read-only property. - stage3.include = [sourceFiles].filter(Boolean); - } - - if (sourceFiles) { - // @ts-expect-error Cannot assign to 'include' because it is a read-only property. - stage2.include = [sourceFiles]; - } - return [stage1, stage2, stage3]; }; diff --git a/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.test.ts b/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.test.ts index dbffed0c3..0270a9de0 100644 --- a/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.test.ts +++ b/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.test.ts @@ -16,7 +16,12 @@ describe("writeMultiTypeScriptConfig", () => { incompleteTypes: true, noImplicitAny: true, }, + include: undefined, projectPath: "./tsconfig.json", }); + + expect(JSON.stringify(config)).toBe( + '{"fixes":{"incompleteTypes":true,"noImplicitAny":true},"projectPath":"./tsconfig.json"}', + ); }); }); diff --git a/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.ts b/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.ts index a7210ab2e..da351ff65 100644 --- a/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.ts +++ b/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.ts @@ -25,13 +25,9 @@ export const generateSingleTypeScriptConfig = ({ }: SingleTypeScriptConfigSettings) => { const config: Partial = { fixes: printImprovements(improvements), + include: sourceFiles ? [sourceFiles] : undefined, projectPath: project.filePath, }; - - if (sourceFiles) { - // @ts-expect-error Cannot assign to 'include' because it is a read-only property. - config.include = [sourceFiles]; - } return config; };