@@ -4,7 +4,6 @@ import { access, readFile, unlink, writeFile } from 'fs/promises';
44import { basename , dirname , join } from 'path' ;
55
66import { applyPatch , parsePatch , reversePatch } from 'diff' ;
7- import { patch } from 'semver' ;
87
98export interface ShouldApplyWithReason {
109 shouldApply : boolean ;
@@ -28,11 +27,16 @@ export abstract class FileModification {
2827 }
2928
3029 private async savePatch ( patchResult : string ) : Promise < void > {
31- const patchFile = this . getPatchFilePath ( this . filePath ) ;
32- await writeFile ( patchFile , patchResult , 'utf8' ) ;
30+ const patchFilePath = this . getPatchFilePath ( this . filePath ) ;
31+ await writeFile ( patchFilePath , patchResult , 'utf8' ) ;
3332 }
3433
35- private async loadSavedPatch ( targetFile : string ) : Promise < string | null > {
34+ /**
35+ * Loads the applied patch for the target file if it exists
36+ * @param targetFile - The path to the file to be patched
37+ * @returns The patch contents if it exists (targetFile.patch), null otherwise
38+ */
39+ private async loadPatchedFilePatch ( targetFile : string ) : Promise < string | null > {
3640 const patchFile = this . getPatchFilePath ( targetFile ) ;
3741 try {
3842 await access ( patchFile , constants . R_OK ) ;
@@ -80,9 +84,13 @@ export abstract class FileModification {
8084 await writeFile ( this . filePath , results ) ;
8185 }
8286
83- // Default implementation of apply that uses the patch
8487 async apply ( ) : Promise < void > {
8588 try {
89+ const savedPatch = await this . loadPatchedFilePatch ( this . filePath ) ;
90+ if ( savedPatch ) {
91+ // Rollback the saved patch before applying the new patch
92+ await this . rollback ( ) ;
93+ }
8694 // First attempt to apply the patch that was generated
8795 const staticPatch = await this . getPregeneratedPatch ( ) ;
8896 if ( staticPatch ) {
@@ -104,12 +112,11 @@ export abstract class FileModification {
104112 }
105113 }
106114
107- // Update rollback to use the shared utility
108115 async rollback ( ) : Promise < void > {
109116 let patch : string ;
110117
111118 // Try to load saved patch first
112- const savedPatch = await this . loadSavedPatch ( this . filePath ) ;
119+ const savedPatch = await this . loadPatchedFilePatch ( this . filePath ) ;
113120 if ( savedPatch ) {
114121 this . logger . debug ( `Using saved patch file for ${ this . id } ` ) ;
115122 patch = savedPatch ;
@@ -141,15 +148,37 @@ export abstract class FileModification {
141148 await access ( patchFile , constants . W_OK ) ;
142149 await unlink ( patchFile ) ;
143150 } catch {
144- // Ignore errors when trying to delete the patch file
151+ this . logger . warn ( `Failed to delete patch file for ${ this . id } ` ) ;
145152 }
146153 }
147154
148155 // Default implementation that can be overridden if needed
149156 async shouldApply ( ) : Promise < ShouldApplyWithReason > {
150- return {
151- shouldApply : true ,
152- reason : 'Default behavior is to always apply modifications' ,
153- } ;
157+ try {
158+ if ( ! this . filePath || ! this . id ) {
159+ throw new Error ( 'Invalid file modification configuration' ) ;
160+ }
161+
162+ const fileExists = await access ( this . filePath , constants . R_OK | constants . W_OK )
163+ . then ( ( ) => true )
164+ . catch ( ( ) => false ) ;
165+
166+ if ( ! fileExists ) {
167+ return {
168+ shouldApply : false ,
169+ reason : `Target file ${ this . filePath } does not exist or is not accessible` ,
170+ } ;
171+ }
172+ return {
173+ shouldApply : true ,
174+ reason : 'Default behavior is to apply modifications if the file exists' ,
175+ } ;
176+ } catch ( err ) {
177+ this . logger . error ( `Failed to check if file ${ this . filePath } should be applied: ${ err } ` ) ;
178+ return {
179+ shouldApply : false ,
180+ reason : `Failed to check if file ${ this . filePath } should be applied: ${ err } ` ,
181+ } ;
182+ }
154183 }
155184}
0 commit comments