From 90ef5e5d31eb5a4763ab54b2261231f7f883265f Mon Sep 17 00:00:00 2001 From: rubiesonthesky <2591240+rubiesonthesky@users.noreply.github.com> Date: Fri, 17 Jan 2025 16:02:01 +0200 Subject: [PATCH] fix: writing TypeScript config file (#2114) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## PR Checklist - [x] Addresses an existing open issue: fixes #2113 - [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/TypeStat/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) - [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/TypeStat/blob/main/.github/CONTRIBUTING.md) were taken ## Overview - Fixes issue where `null` was inserted to typestat.json `includes` list - Fixes issue where option was `inferableTypes` when it should be `noInferableTypes` - Added basic testing for config generation For future: - Config generation is still little bit broken. If I want to select just "Remove type annotations that don't change the meaning of code" option, the generated config file does not have it but instead has just `"incompleteTypes": true`. -> Needs new issue created. 🐙 --- .../writeMultiTypeScriptConfig.test.ts | 58 +++++++++++ .../writeMultiTypeScriptConfig.ts | 99 ++++++++++--------- .../writeSingleTypeScriptConfig.test.ts | 27 +++++ .../writeSingleTypeScriptConfig.ts | 48 ++++----- 4 files changed, 160 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..675675b5b --- /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, + }, + }, + { + fixes: { + incompleteTypes: true, + noImplicitAny: true, + noImplicitThis: true, + noInferableTypes: true, + }, + include: undefined, + 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..d5aba90bf 100644 --- a/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.ts +++ b/src/initialization/initializeTypeScript/writeMultiTypeScriptConfig.ts @@ -1,5 +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"; @@ -11,61 +13,62 @@ 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, + }, + include: testFiles ? [testFiles] : undefined, + projectPath: project.filePath, + types: { + strictNullChecks: true, + }, + }; + + 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, + }; + + 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..0270a9de0 --- /dev/null +++ b/src/initialization/initializeTypeScript/writeSingleTypeScriptConfig.test.ts @@ -0,0 +1,27 @@ +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, + }, + 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 c4a0febb1..da351ff65 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,35 @@ 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), + include: sourceFiles ? [sourceFiles] : undefined, + projectPath: project.filePath, + }; + 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; };