diff --git a/src/parameter-properties/parameter-properties.test.ts b/src/parameter-properties/parameter-properties.test.ts index 0ad221d..519105f 100644 --- a/src/parameter-properties/parameter-properties.test.ts +++ b/src/parameter-properties/parameter-properties.test.ts @@ -26,6 +26,25 @@ async function checkTsFile(filePath: string): Promise { }); } +async function denoFmt(filePath: string): Promise { + await new Promise((resolve, reject) => { + const tsc = spawn("deno", ["fmt", filePath]); + + tsc.stderr.on("data", (data) => { + console.error(data.toString()); + }); + + tsc.on("close", (code: number) => { + if (code === 0) { + console.log("Typecheck succeeded 🎉"); + resolve(undefined); + } else { + reject(new Error("Typecheck failed")); + } + }); + }); +} + /** * Test utilities for codemod testing with proper cleanup * @example @@ -46,6 +65,7 @@ class TestUtils { async runAndReadCodemod(filePath: string): Promise { parameterPropertiesCodemod(filePath); + await denoFmt(filePath); return await this.readTestFile(filePath); } @@ -77,7 +97,7 @@ console.log(user.name, user.age);`; const expectedOutput = `class User { public name: string; public age: number; - + constructor(name: string, age: number) { this.name = name; this.age = age; @@ -85,7 +105,8 @@ console.log(user.name, user.age);`; } const user = new User("John", 30); -console.log(user.name, user.age);`; +console.log(user.name, user.age); +`; await utils.createTestFile(testFile, originalContent); const result = await utils.runAndReadCodemod(testFile); @@ -504,7 +525,7 @@ Deno.test("preserves existing class properties", async () => { const expectedOutput = `class Employee { public name: string; public id: number; - + private department: string = "Engineering"; constructor(name: string, id: number) { @@ -712,7 +733,7 @@ class Pair { } }); -Deno.test.only("handles abstract classes", async () => { +Deno.test("handles abstract classes", async () => { const utils = new TestUtils(); const testFile = "tmp/abstract_classes_test.ts"; @@ -746,9 +767,9 @@ class Dog extends Animal { }`; const expectedOutput = `abstract class Animal { - public name: string; - protected species: string; - + public name: string; + protected species: string; + constructor(name: string, species: string) { this.name = name; this.species = species; @@ -766,8 +787,8 @@ class Dog extends Animal { } class Dog extends Animal { - public breed: string; - + public breed: string; + constructor(name: string, breed: string) { super(name, "Canis"); this.breed = breed; diff --git a/src/parameter-properties/parameter-properties.ts b/src/parameter-properties/parameter-properties.ts index bee1d69..0a837ee 100644 --- a/src/parameter-properties/parameter-properties.ts +++ b/src/parameter-properties/parameter-properties.ts @@ -1,8 +1,8 @@ import { ClassDeclaration, - ConstructorDeclaration, - Node, + OptionalKind, Project, + PropertyDeclarationStructure, SyntaxKind, } from "ts-morph"; @@ -14,6 +14,8 @@ function convertParameterProperties(classDeclaration: ClassDeclaration): void { const constructor = constructors[0]; const parameters = constructor.getParameters(); + const constructorContent = constructor.getBody()?.getText().slice(1, -1) + .trim(); const parameterProperties = parameters.filter((param) => param.hasModifier(SyntaxKind.ReadonlyKeyword) || param.hasModifier(SyntaxKind.PublicKeyword) || @@ -30,7 +32,7 @@ function convertParameterProperties(classDeclaration: ClassDeclaration): void { .getProperties() .map((property) => property.getName()); - const propertyDeclarations: string[] = []; + const propertyDeclarations: OptionalKind[] = []; const constructorAssignments: string[] = []; parameterProperties.forEach((param) => { @@ -39,11 +41,13 @@ function convertParameterProperties(classDeclaration: ClassDeclaration): void { const modifiers = param.getModifiers().map((mod) => mod.getText()).join( " ", ); - const initializer = param.getInitializer()?.getText(); if (!existingProperties.includes(paramName)) { - const propertyDeclaration = `${modifiers} ${paramName}: ${paramType};`; - propertyDeclarations.push(propertyDeclaration); + // const propertyDeclaration = `${modifiers} ${paramName}: ${paramType};`; + propertyDeclarations.push({ + name: `${modifiers} ${paramName}`, + type: paramType, + }); const assignment = `this.${paramName} = ${paramName};`; constructorAssignments.push(assignment); @@ -51,19 +55,9 @@ function convertParameterProperties(classDeclaration: ClassDeclaration): void { }); if (propertyDeclarations.length > 0) { - const classComment = classDeclaration.getLeadingCommentRanges()[0] - ?.getText(); - // Get the class text and find the constructor - const classText = classDeclaration.getText(); - const constructorMatch = classText.match( - /constructor\s*\([^)]*\)\s*\{?\s*\}?/, - ); + const constructorMatch = constructor.getText(); if (constructorMatch) { - const constructorText = constructorMatch[0]; - const constructorStart = classText.indexOf(constructorText); - const constructorEnd = constructorStart + constructorText.length; - // Create new constructor with assignments const newConstructorParams = constructor.getParameters() .map((param) => { @@ -76,29 +70,18 @@ function convertParameterProperties(classDeclaration: ClassDeclaration): void { }) .join(", "); + if (constructorContent) { + constructorAssignments.unshift(constructorContent); + } + const newConstructor = `constructor(${newConstructorParams}) {\n ${ - constructorAssignments.join("\n ") + constructorAssignments.join("\n") }\n }`; - // Replace constructor in class text - const newClassText = classText.substring(0, constructorStart) + - newConstructor + - classText.substring(constructorEnd); + constructor.replaceWithText(newConstructor); + classDeclaration.setOrder; - // Add property declarations after class opening brace - const classBraceIndex = newClassText.indexOf("{"); - if (classBraceIndex !== -1) { - const propertiesText = propertyDeclarations.join("\n "); - let finalText = newClassText.substring(0, classBraceIndex + 1) + - "\n " + propertiesText + "\n " + - newClassText.substring(classBraceIndex + 1); - - if (classComment) { - finalText = classComment + "\n" + finalText; - } - - classDeclaration.replaceWithText(finalText); - } + classDeclaration.insertProperties(0, propertyDeclarations); } } }