1- import fs from 'fs-extra'
21import path from 'pathe'
32import { pkgName , pkgVersion } from '../constants'
3+ import { executeMigrationFile , restoreConfigEntries } from './migration-file-executor'
4+ import type { MigrationWrittenEntry } from './migration-file-executor'
5+ import { loadMigrationReportForRestore } from './migration-report-loader'
46import {
57 collectWorkspaceConfigFiles ,
68 DEFAULT_WORKSPACE_MAX_DEPTH ,
79 filterTargetFiles ,
8- resolveBackupRelativePath ,
910 resolveTargetFiles ,
1011} from './migration-target-files'
1112import {
12- assertMigrationReportCompatibility ,
1313 MIGRATION_REPORT_KIND ,
1414 MIGRATION_REPORT_SCHEMA_VERSION ,
1515} from './migration-report'
1616import { migrateConfigSource } from './migration-source'
17+ import type {
18+ ConfigFileMigrationEntry ,
19+ ConfigFileMigrationReport ,
20+ MigrateConfigFilesOptions ,
21+ RestoreConfigFilesOptions ,
22+ RestoreConfigFilesResult ,
23+ } from './migration-types'
1724export { DEFAULT_CONFIG_FILENAMES } from './migration-target-files'
1825export { MIGRATION_REPORT_KIND , MIGRATION_REPORT_SCHEMA_VERSION } from './migration-report'
1926export { migrateConfigSource } from './migration-source'
2027export type { ConfigSourceMigrationResult } from './migration-source'
21-
22- export interface ConfigFileMigrationEntry {
23- file : string
24- changed : boolean
25- written : boolean
26- rolledBack : boolean
27- backupFile ?: string
28- changes : string [ ]
29- }
30-
31- export interface ConfigFileMigrationReport {
32- reportKind : typeof MIGRATION_REPORT_KIND
33- schemaVersion : typeof MIGRATION_REPORT_SCHEMA_VERSION
34- generatedAt : string
35- tool : {
36- name : string
37- version : string
38- }
39- cwd : string
40- dryRun : boolean
41- rollbackOnError : boolean
42- backupDirectory ?: string
43- scannedFiles : number
44- changedFiles : number
45- writtenFiles : number
46- backupsWritten : number
47- unchangedFiles : number
48- missingFiles : number
49- entries : ConfigFileMigrationEntry [ ]
50- }
51-
52- export interface MigrateConfigFilesOptions {
53- cwd : string
54- files ?: string [ ]
55- dryRun ?: boolean
56- workspace ?: boolean
57- maxDepth ?: number
58- rollbackOnError ?: boolean
59- backupDir ?: string
60- include ?: string [ ]
61- exclude ?: string [ ]
62- }
63-
64- export interface RestoreConfigFilesOptions {
65- cwd : string
66- reportFile : string
67- dryRun ?: boolean
68- strict ?: boolean
69- }
70-
71- export interface RestoreConfigFilesResult {
72- cwd : string
73- reportFile : string
74- reportKind ?: string
75- reportSchemaVersion ?: number
76- dryRun : boolean
77- strict : boolean
78- scannedEntries : number
79- restorableEntries : number
80- restoredFiles : number
81- missingBackups : number
82- skippedEntries : number
83- restored : string [ ]
84- }
28+ export type {
29+ ConfigFileMigrationEntry ,
30+ ConfigFileMigrationReport ,
31+ MigrateConfigFilesOptions ,
32+ RestoreConfigFilesOptions ,
33+ RestoreConfigFilesResult ,
34+ } from './migration-types'
8535
8636export async function migrateConfigFiles ( options : MigrateConfigFilesOptions ) : Promise < ConfigFileMigrationReport > {
8737 const cwd = path . resolve ( options . cwd )
@@ -103,67 +53,33 @@ export async function migrateConfigFiles(options: MigrateConfigFilesOptions): Pr
10353 let backupsWritten = 0
10454 let unchangedFiles = 0
10555 let missingFiles = 0
106- const wroteEntries : Array < { file : string , source : string , entry : ConfigFileMigrationEntry } > = [ ]
56+ const wroteEntries : MigrationWrittenEntry [ ] = [ ]
10757
10858 for ( const file of targetFiles ) {
109- const exists = await fs . pathExists ( file )
110- if ( ! exists ) {
59+ const result = await executeMigrationFile ( {
60+ cwd,
61+ file,
62+ dryRun,
63+ rollbackOnError,
64+ wroteEntries,
65+ ...( backupDirectory ? { backupDirectory } : { } ) ,
66+ } )
67+
68+ if ( result . missing ) {
11169 missingFiles += 1
11270 continue
11371 }
11472
11573 scannedFiles += 1
116- const source = await fs . readFile ( file , 'utf8' )
117- const migrated = migrateConfigSource ( source )
74+ entries . push ( result . entry )
11875
119- const entry : ConfigFileMigrationEntry = {
120- file,
121- changed : migrated . changed ,
122- written : false ,
123- rolledBack : false ,
124- changes : migrated . changes ,
125- }
126- entries . push ( entry )
127-
128- if ( migrated . changed ) {
76+ if ( result . changed ) {
12977 changedFiles += 1
130- if ( ! dryRun ) {
131- try {
132- if ( backupDirectory ) {
133- const backupRelativePath = resolveBackupRelativePath ( cwd , file )
134- const backupFile = path . resolve ( backupDirectory , backupRelativePath )
135- await fs . ensureDir ( path . dirname ( backupFile ) )
136- await fs . writeFile ( backupFile , source , 'utf8' )
137- entry . backupFile = backupFile
138- backupsWritten += 1
139- }
140- await fs . writeFile ( file , migrated . code , 'utf8' )
141- entry . written = true
142- wroteEntries . push ( { file, source, entry } )
143- writtenFiles += 1
144- }
145- catch ( error ) {
146- let rollbackCount = 0
147- if ( rollbackOnError && wroteEntries . length > 0 ) {
148- for ( const written of [ ...wroteEntries ] . reverse ( ) ) {
149- try {
150- await fs . writeFile ( written . file , written . source , 'utf8' )
151- written . entry . written = false
152- written . entry . rolledBack = true
153- rollbackCount += 1
154- }
155- catch {
156- // Continue best-effort rollback to avoid leaving even more partial state.
157- }
158- }
159- writtenFiles = Math . max ( 0 , writtenFiles - rollbackCount )
160- }
161- const reason = error instanceof Error ? error . message : String ( error )
162- const rollbackHint = rollbackOnError && rollbackCount > 0
163- ? ` Rolled back ${ rollbackCount } previously written file(s).`
164- : ''
165- throw new Error ( `Failed to write migrated config "${ file } ": ${ reason } .${ rollbackHint } ` )
166- }
78+ if ( result . wrote ) {
79+ writtenFiles += 1
80+ }
81+ if ( result . backupWritten ) {
82+ backupsWritten += 1
16783 }
16884 }
16985 else {
@@ -199,48 +115,15 @@ export async function restoreConfigFiles(options: RestoreConfigFilesOptions): Pr
199115 const strict = options . strict ?? false
200116 const reportFile = path . resolve ( cwd , options . reportFile )
201117
202- const report = await fs . readJSON ( reportFile ) as {
203- reportKind ?: string
204- schemaVersion ?: number
205- entries ?: Array < { file ?: string , backupFile ?: string } >
206- }
207- assertMigrationReportCompatibility ( report , reportFile )
208- const entries = Array . isArray ( report . entries ) ? report . entries : [ ]
209-
210- let scannedEntries = 0
211- let restorableEntries = 0
212- let restoredFiles = 0
213- let missingBackups = 0
214- let skippedEntries = 0
215- const restored : string [ ] = [ ]
216-
217- for ( const entry of entries ) {
218- scannedEntries += 1
219- const targetFile = entry . file ? path . resolve ( entry . file ) : undefined
220- const backupFile = entry . backupFile ? path . resolve ( entry . backupFile ) : undefined
221-
222- if ( ! targetFile || ! backupFile ) {
223- skippedEntries += 1
224- continue
225- }
226-
227- restorableEntries += 1
228-
229- const backupExists = await fs . pathExists ( backupFile )
230- if ( ! backupExists ) {
231- missingBackups += 1
232- continue
233- }
234-
235- if ( ! dryRun ) {
236- const backupContent = await fs . readFile ( backupFile , 'utf8' )
237- await fs . ensureDir ( path . dirname ( targetFile ) )
238- await fs . writeFile ( targetFile , backupContent , 'utf8' )
239- }
240-
241- restoredFiles += 1
242- restored . push ( targetFile )
243- }
118+ const report = await loadMigrationReportForRestore ( reportFile )
119+ const {
120+ scannedEntries,
121+ restorableEntries,
122+ restoredFiles,
123+ missingBackups,
124+ skippedEntries,
125+ restored,
126+ } = await restoreConfigEntries ( report . entries , dryRun )
244127
245128 if ( strict && missingBackups > 0 ) {
246129 throw new Error ( `Restore failed: ${ missingBackups } backup file(s) missing in report ${ reportFile } .` )
0 commit comments