mirror of
https://github.com/bitwarden/browser
synced 2026-02-28 02:23:25 +00:00
Fix backup
This commit is contained in:
230
scripts/migration/i18n/BACKUP-SYSTEM.md
Normal file
230
scripts/migration/i18n/BACKUP-SYSTEM.md
Normal file
@@ -0,0 +1,230 @@
|
||||
# Improved Backup System
|
||||
|
||||
## Overview
|
||||
|
||||
The migration tools now include an improved backup system that preserves full file paths, enabling safe rollback operations even for files in nested directory structures.
|
||||
|
||||
## Problem Solved
|
||||
|
||||
**Previous Issue**: The original backup system only stored filenames without paths:
|
||||
|
||||
```
|
||||
backups/
|
||||
├── component.html.backup # Lost path info!
|
||||
├── template.html.backup # Could be from anywhere!
|
||||
└── form.html.backup # No way to restore correctly!
|
||||
```
|
||||
|
||||
**New Solution**: Path-preserving backup system:
|
||||
|
||||
```
|
||||
backups/
|
||||
├── path-mapping.json # Maps backup files to original paths
|
||||
├── src_app_components_login.html.backup # Unique filename with path info
|
||||
├── src_shared_templates_form.html.backup # No naming conflicts
|
||||
└── libs_ui_components_button.html.backup # Safe restoration
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### 1. Backup Creation
|
||||
|
||||
When creating backups, the system:
|
||||
|
||||
1. **Generates unique backup filenames** by replacing path separators with underscores:
|
||||
|
||||
```typescript
|
||||
const relativePath = path.relative(process.cwd(), filePath);
|
||||
const backupFileName = relativePath.replace(/[/\\]/g, "_") + ".backup";
|
||||
```
|
||||
|
||||
2. **Creates a path mapping file** that tracks original locations:
|
||||
|
||||
```json
|
||||
{
|
||||
"src_app_login.html.backup": "/full/path/to/src/app/login.html",
|
||||
"libs_ui_button.html.backup": "/full/path/to/libs/ui/button.html"
|
||||
}
|
||||
```
|
||||
|
||||
3. **Copies files to backup directory** with the unique names.
|
||||
|
||||
### 2. Backup Restoration
|
||||
|
||||
When restoring backups, the system:
|
||||
|
||||
1. **Reads the path mapping file** to get original locations
|
||||
2. **Creates missing directories** if they don't exist
|
||||
3. **Restores files to their exact original paths**
|
||||
4. **Validates each restoration** before proceeding
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### TypeScript Migration with Backup
|
||||
|
||||
```bash
|
||||
# Create backups and migrate
|
||||
npm run migrate -- --backup --output ./migration-reports
|
||||
|
||||
# If something goes wrong, rollback
|
||||
npm run cli -- rollback --backup-dir ./migration-reports/backups
|
||||
```
|
||||
|
||||
### Template Migration with Backup
|
||||
|
||||
```bash
|
||||
# Create backups and migrate templates
|
||||
npm run template-migrate -- --pattern "src/**/*.html" --backup --output ./reports
|
||||
|
||||
# Rollback if needed
|
||||
npm run template-cli -- rollback --backup-dir ./reports/backups
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
### Backup Directory Structure
|
||||
|
||||
```
|
||||
migration-reports/
|
||||
└── backups/
|
||||
├── path-mapping.json # Critical: Maps backup files to originals
|
||||
├── src_app_login_login.component.html.backup
|
||||
├── src_shared_ui_button.component.html.backup
|
||||
├── libs_forms_input.component.html.backup
|
||||
└── apps_web_dashboard_main.component.html.backup
|
||||
```
|
||||
|
||||
### Path Mapping Format
|
||||
|
||||
```json
|
||||
{
|
||||
"src_app_login_login.component.html.backup": "/project/src/app/login/login.component.html",
|
||||
"src_shared_ui_button.component.html.backup": "/project/src/shared/ui/button.component.html",
|
||||
"libs_forms_input.component.html.backup": "/project/libs/forms/input.component.html"
|
||||
}
|
||||
```
|
||||
|
||||
## Safety Features
|
||||
|
||||
### 1. Path Validation
|
||||
|
||||
- Verifies path mapping file exists before restoration
|
||||
- Warns about orphaned backup files without mappings
|
||||
- Creates missing directories during restoration
|
||||
|
||||
### 2. Error Handling
|
||||
|
||||
- Graceful handling of missing mapping files
|
||||
- Clear error messages for corrupted backups
|
||||
- Verbose logging for troubleshooting
|
||||
|
||||
### 3. Backward Compatibility Detection
|
||||
|
||||
```bash
|
||||
❌ Path mapping file not found. Cannot restore files safely.
|
||||
This backup was created with an older version that doesn't preserve paths.
|
||||
```
|
||||
|
||||
## Migration from Old Backup System
|
||||
|
||||
If you have backups created with the old system (without path mapping):
|
||||
|
||||
1. **Manual Restoration Required**: The old backups cannot be automatically restored
|
||||
2. **Identify Original Locations**: You'll need to manually determine where files belong
|
||||
3. **Create New Backups**: Re-run migrations with `--backup` to create proper backups
|
||||
|
||||
## Testing
|
||||
|
||||
The backup system includes comprehensive tests covering:
|
||||
|
||||
- Path preservation across nested directories
|
||||
- Restoration accuracy
|
||||
- Missing directory creation
|
||||
- Error handling scenarios
|
||||
- Orphaned file detection
|
||||
|
||||
Run tests:
|
||||
|
||||
```bash
|
||||
npm test -- templates/backup-system.spec.ts
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Always Use Backups for Production
|
||||
|
||||
```bash
|
||||
# Good: Creates backups before migration
|
||||
npm run template-migrate -- --pattern "src/**/*.html" --backup
|
||||
|
||||
# Risky: No backup created
|
||||
npm run template-migrate -- --pattern "src/**/*.html"
|
||||
```
|
||||
|
||||
### 2. Verify Backup Creation
|
||||
|
||||
```bash
|
||||
# Check that path-mapping.json exists
|
||||
ls -la migration-reports/backups/path-mapping.json
|
||||
|
||||
# Verify backup count matches expected files
|
||||
cat migration-reports/backups/path-mapping.json | jq 'keys | length'
|
||||
```
|
||||
|
||||
### 3. Test Rollback on Small Set First
|
||||
|
||||
```bash
|
||||
# Test rollback on a few files first
|
||||
npm run template-migrate -- --file "src/app/test.html" --backup
|
||||
npm run template-cli -- rollback --backup-dir ./migration-reports/backups
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Issue: "Path mapping file not found"
|
||||
|
||||
**Cause**: Backup was created with old version or mapping file was deleted
|
||||
**Solution**: Cannot auto-restore; manual restoration required
|
||||
|
||||
### Issue: "No mapping found for backup file"
|
||||
|
||||
**Cause**: Backup file exists but not in mapping (corrupted backup)
|
||||
**Solution**: Check backup integrity; may need to recreate
|
||||
|
||||
### Issue: Restoration fails with permission errors
|
||||
|
||||
**Cause**: Insufficient permissions to create directories or write files
|
||||
**Solution**: Check file permissions and disk space
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Filename Sanitization
|
||||
|
||||
```typescript
|
||||
// Convert paths to safe filenames
|
||||
const backupFileName = relativePath.replace(/[/\\]/g, "_") + ".backup";
|
||||
|
||||
// Examples:
|
||||
// "src/app/login.html" → "src_app_login.html.backup"
|
||||
// "libs\\ui\\button.html" → "libs_ui_button.html.backup"
|
||||
```
|
||||
|
||||
### Directory Creation
|
||||
|
||||
```typescript
|
||||
// Ensure target directory exists during restoration
|
||||
const originalDir = path.dirname(originalPath);
|
||||
if (!fs.existsSync(originalDir)) {
|
||||
fs.mkdirSync(originalDir, { recursive: true });
|
||||
}
|
||||
```
|
||||
|
||||
### Path Mapping Storage
|
||||
|
||||
```typescript
|
||||
// Save mapping as JSON for easy parsing
|
||||
const mappingPath = path.join(backupDir, "path-mapping.json");
|
||||
fs.writeFileSync(mappingPath, JSON.stringify(pathMapping, null, 2));
|
||||
```
|
||||
|
||||
This improved backup system ensures that migration operations can be safely reversed, even in complex project structures with deeply nested files.
|
||||
222
scripts/migration/i18n/templates/backup-system.spec.ts
Normal file
222
scripts/migration/i18n/templates/backup-system.spec.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
describe("Backup System", () => {
|
||||
const testDir = path.join(__dirname, "test-backup-system");
|
||||
const backupDir = path.join(testDir, "backups");
|
||||
|
||||
const originalTemplate = `<div>
|
||||
<h1>{{ 'title' | i18n }}</h1>
|
||||
<p>{{ 'description' | i18n }}</p>
|
||||
</div>`;
|
||||
|
||||
const modifiedTemplate = `<div>
|
||||
<h1><span i18n="@@title">Title</span></h1>
|
||||
<p><span i18n="@@description">Description</span></p>
|
||||
</div>`;
|
||||
|
||||
beforeEach(() => {
|
||||
// Create test directory structure
|
||||
if (fs.existsSync(testDir)) {
|
||||
fs.rmSync(testDir, { recursive: true });
|
||||
}
|
||||
fs.mkdirSync(testDir, { recursive: true });
|
||||
|
||||
// Create nested directory structure to test path preservation
|
||||
const nestedDir = path.join(testDir, "nested", "deep");
|
||||
fs.mkdirSync(nestedDir, { recursive: true });
|
||||
|
||||
// Create test files
|
||||
fs.writeFileSync(path.join(testDir, "test1.html"), originalTemplate);
|
||||
fs.writeFileSync(path.join(nestedDir, "test2.html"), originalTemplate);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (fs.existsSync(testDir)) {
|
||||
fs.rmSync(testDir, { recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
describe("backup creation", () => {
|
||||
it("should create backups with path mapping", () => {
|
||||
const templateFiles = [
|
||||
path.join(testDir, "test1.html"),
|
||||
path.join(testDir, "nested", "deep", "test2.html"),
|
||||
];
|
||||
|
||||
// Simulate the backup creation logic
|
||||
const pathMapping: Record<string, string> = {};
|
||||
|
||||
if (!fs.existsSync(backupDir)) {
|
||||
fs.mkdirSync(backupDir, { recursive: true });
|
||||
}
|
||||
|
||||
for (const filePath of templateFiles) {
|
||||
const relativePath = path.relative(process.cwd(), filePath);
|
||||
const backupFileName = relativePath.replace(/[/\\]/g, "_") + ".backup";
|
||||
const backupPath = path.join(backupDir, backupFileName);
|
||||
|
||||
fs.copyFileSync(filePath, backupPath);
|
||||
pathMapping[backupFileName] = filePath;
|
||||
}
|
||||
|
||||
// Save path mapping
|
||||
const mappingPath = path.join(backupDir, "path-mapping.json");
|
||||
fs.writeFileSync(mappingPath, JSON.stringify(pathMapping, null, 2));
|
||||
|
||||
// Verify backup files exist
|
||||
expect(fs.existsSync(mappingPath)).toBe(true);
|
||||
expect(Object.keys(pathMapping)).toHaveLength(2);
|
||||
|
||||
// Verify backup files contain original content
|
||||
for (const [backupFileName, originalPath] of Object.entries(pathMapping)) {
|
||||
const backupPath = path.join(backupDir, backupFileName);
|
||||
expect(fs.existsSync(backupPath)).toBe(true);
|
||||
|
||||
const backupContent = fs.readFileSync(backupPath, "utf-8");
|
||||
const originalContent = fs.readFileSync(originalPath, "utf-8");
|
||||
expect(backupContent).toBe(originalContent);
|
||||
}
|
||||
});
|
||||
|
||||
it("should handle nested directory paths correctly", () => {
|
||||
const filePath = path.join(testDir, "nested", "deep", "test2.html");
|
||||
const relativePath = path.relative(process.cwd(), filePath);
|
||||
const backupFileName = relativePath.replace(/[/\\]/g, "_") + ".backup";
|
||||
|
||||
// Should convert slashes/backslashes to underscores
|
||||
expect(backupFileName).toContain("_");
|
||||
expect(backupFileName).not.toContain("/");
|
||||
expect(backupFileName).not.toContain("\\");
|
||||
expect(backupFileName.endsWith(".backup")).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("backup restoration", () => {
|
||||
it("should restore files to original locations", () => {
|
||||
const templateFiles = [
|
||||
path.join(testDir, "test1.html"),
|
||||
path.join(testDir, "nested", "deep", "test2.html"),
|
||||
];
|
||||
|
||||
// Create backups
|
||||
const pathMapping: Record<string, string> = {};
|
||||
|
||||
if (!fs.existsSync(backupDir)) {
|
||||
fs.mkdirSync(backupDir, { recursive: true });
|
||||
}
|
||||
|
||||
for (const filePath of templateFiles) {
|
||||
const relativePath = path.relative(process.cwd(), filePath);
|
||||
const backupFileName = relativePath.replace(/[/\\]/g, "_") + ".backup";
|
||||
const backupPath = path.join(backupDir, backupFileName);
|
||||
|
||||
fs.copyFileSync(filePath, backupPath);
|
||||
pathMapping[backupFileName] = filePath;
|
||||
}
|
||||
|
||||
const mappingPath = path.join(backupDir, "path-mapping.json");
|
||||
fs.writeFileSync(mappingPath, JSON.stringify(pathMapping, null, 2));
|
||||
|
||||
// Modify original files
|
||||
for (const filePath of templateFiles) {
|
||||
fs.writeFileSync(filePath, modifiedTemplate);
|
||||
}
|
||||
|
||||
// Verify files are modified
|
||||
for (const filePath of templateFiles) {
|
||||
const content = fs.readFileSync(filePath, "utf-8");
|
||||
expect(content).toBe(modifiedTemplate);
|
||||
}
|
||||
|
||||
// Restore from backups
|
||||
const loadedMapping = JSON.parse(fs.readFileSync(mappingPath, "utf-8"));
|
||||
const backupFiles = fs.readdirSync(backupDir).filter((f) => f.endsWith(".backup"));
|
||||
|
||||
for (const backupFile of backupFiles) {
|
||||
const backupPath = path.join(backupDir, backupFile);
|
||||
const originalPath = loadedMapping[backupFile];
|
||||
|
||||
if (originalPath) {
|
||||
// Ensure directory exists
|
||||
const originalDir = path.dirname(originalPath);
|
||||
if (!fs.existsSync(originalDir)) {
|
||||
fs.mkdirSync(originalDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.copyFileSync(backupPath, originalPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify files are restored
|
||||
for (const filePath of templateFiles) {
|
||||
const content = fs.readFileSync(filePath, "utf-8");
|
||||
expect(content).toBe(originalTemplate);
|
||||
}
|
||||
});
|
||||
|
||||
it("should handle missing directories during restoration", () => {
|
||||
const filePath = path.join(testDir, "new", "nested", "path", "test.html");
|
||||
const backupFileName = "test_new_nested_path_test.html.backup";
|
||||
const pathMapping = { [backupFileName]: filePath };
|
||||
|
||||
// Create backup directory and mapping
|
||||
if (!fs.existsSync(backupDir)) {
|
||||
fs.mkdirSync(backupDir, { recursive: true });
|
||||
}
|
||||
|
||||
const backupPath = path.join(backupDir, backupFileName);
|
||||
fs.writeFileSync(backupPath, originalTemplate);
|
||||
|
||||
const mappingPath = path.join(backupDir, "path-mapping.json");
|
||||
fs.writeFileSync(mappingPath, JSON.stringify(pathMapping, null, 2));
|
||||
|
||||
// Restore (should create missing directories)
|
||||
const originalDir = path.dirname(filePath);
|
||||
if (!fs.existsSync(originalDir)) {
|
||||
fs.mkdirSync(originalDir, { recursive: true });
|
||||
}
|
||||
fs.copyFileSync(backupPath, filePath);
|
||||
|
||||
// Verify file was restored and directories were created
|
||||
expect(fs.existsSync(filePath)).toBe(true);
|
||||
expect(fs.readFileSync(filePath, "utf-8")).toBe(originalTemplate);
|
||||
});
|
||||
});
|
||||
|
||||
describe("error handling", () => {
|
||||
it("should handle missing path mapping file", () => {
|
||||
// Create backup files without mapping
|
||||
if (!fs.existsSync(backupDir)) {
|
||||
fs.mkdirSync(backupDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.writeFileSync(path.join(backupDir, "test.html.backup"), originalTemplate);
|
||||
|
||||
const mappingPath = path.join(backupDir, "path-mapping.json");
|
||||
|
||||
// Should detect missing mapping file
|
||||
expect(fs.existsSync(mappingPath)).toBe(false);
|
||||
});
|
||||
|
||||
it("should handle backup files without mapping entries", () => {
|
||||
if (!fs.existsSync(backupDir)) {
|
||||
fs.mkdirSync(backupDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Create backup file
|
||||
const backupFileName = "orphaned.html.backup";
|
||||
fs.writeFileSync(path.join(backupDir, backupFileName), originalTemplate);
|
||||
|
||||
// Create mapping without this file
|
||||
const pathMapping = { "other.html.backup": "/some/other/path.html" };
|
||||
const mappingPath = path.join(backupDir, "path-mapping.json");
|
||||
fs.writeFileSync(mappingPath, JSON.stringify(pathMapping, null, 2));
|
||||
|
||||
const loadedMapping = JSON.parse(fs.readFileSync(mappingPath, "utf-8"));
|
||||
|
||||
// Should not find mapping for orphaned file
|
||||
expect(loadedMapping[backupFileName]).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -306,12 +306,23 @@ program
|
||||
try {
|
||||
console.log(chalk.blue("🔄 Rolling back template migration..."));
|
||||
|
||||
const backupDir = options.backup_dir;
|
||||
const backupDir = options.backupDir;
|
||||
if (!fs.existsSync(backupDir)) {
|
||||
console.error(chalk.red(`❌ Backup directory not found: ${backupDir}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Check for path mapping file
|
||||
const mappingPath = path.join(backupDir, "path-mapping.json");
|
||||
if (!fs.existsSync(mappingPath)) {
|
||||
console.error(chalk.red("❌ Path mapping file not found. Cannot restore files safely."));
|
||||
console.log(
|
||||
chalk.gray("This backup was created with an older version that doesn't preserve paths."),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const pathMapping = JSON.parse(fs.readFileSync(mappingPath, "utf-8"));
|
||||
const backupFiles = fs.readdirSync(backupDir).filter((f) => f.endsWith(".backup"));
|
||||
|
||||
if (backupFiles.length === 0) {
|
||||
@@ -322,15 +333,24 @@ program
|
||||
let restoredCount = 0;
|
||||
for (const backupFile of backupFiles) {
|
||||
const backupPath = path.join(backupDir, backupFile);
|
||||
const originalPath = backupFile.replace(".backup", "");
|
||||
const originalPath = pathMapping[backupFile];
|
||||
|
||||
if (fs.existsSync(originalPath)) {
|
||||
fs.copyFileSync(backupPath, originalPath);
|
||||
restoredCount++;
|
||||
if (!originalPath) {
|
||||
console.warn(chalk.yellow(`⚠️ No mapping found for backup file: ${backupFile}`));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (options.verbose) {
|
||||
console.log(chalk.gray(`Restored: ${originalPath}`));
|
||||
}
|
||||
// Ensure the directory exists
|
||||
const originalDir = path.dirname(originalPath);
|
||||
if (!fs.existsSync(originalDir)) {
|
||||
fs.mkdirSync(originalDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.copyFileSync(backupPath, originalPath);
|
||||
restoredCount++;
|
||||
|
||||
if (options.verbose) {
|
||||
console.log(chalk.gray(`Restored: ${originalPath}`));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,13 +446,25 @@ async function createBackups(templateFiles: string[], outputDir: string): Promis
|
||||
fs.mkdirSync(backupDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Create a mapping file to track original paths
|
||||
const pathMapping: Record<string, string> = {};
|
||||
|
||||
for (const filePath of templateFiles) {
|
||||
if (fs.existsSync(filePath)) {
|
||||
const backupPath = path.join(backupDir, path.basename(filePath) + ".backup");
|
||||
// Create a unique backup filename that preserves path info
|
||||
const relativePath = path.relative(process.cwd(), filePath);
|
||||
const backupFileName = relativePath.replace(/[/\\]/g, "_") + ".backup";
|
||||
const backupPath = path.join(backupDir, backupFileName);
|
||||
|
||||
fs.copyFileSync(filePath, backupPath);
|
||||
pathMapping[backupFileName] = filePath;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the path mapping for restoration
|
||||
const mappingPath = path.join(backupDir, "path-mapping.json");
|
||||
fs.writeFileSync(mappingPath, JSON.stringify(pathMapping, null, 2));
|
||||
|
||||
console.log(chalk.green(`📦 Created backups for ${templateFiles.length} files`));
|
||||
}
|
||||
|
||||
|
||||
@@ -189,12 +189,23 @@ program
|
||||
try {
|
||||
console.log(chalk.blue("🔄 Rolling back migration..."));
|
||||
|
||||
const backupDir = options.backup_dir;
|
||||
const backupDir = options.backupDir;
|
||||
if (!fs.existsSync(backupDir)) {
|
||||
console.error(chalk.red(`❌ Backup directory not found: ${backupDir}`));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Check for path mapping file
|
||||
const mappingPath = path.join(backupDir, "path-mapping.json");
|
||||
if (!fs.existsSync(mappingPath)) {
|
||||
console.error(chalk.red("❌ Path mapping file not found. Cannot restore files safely."));
|
||||
console.log(
|
||||
chalk.gray("This backup was created with an older version that doesn't preserve paths."),
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const pathMapping = JSON.parse(fs.readFileSync(mappingPath, "utf-8"));
|
||||
const backupFiles = fs.readdirSync(backupDir).filter((f) => f.endsWith(".backup"));
|
||||
|
||||
if (backupFiles.length === 0) {
|
||||
@@ -205,15 +216,24 @@ program
|
||||
let restoredCount = 0;
|
||||
for (const backupFile of backupFiles) {
|
||||
const backupPath = path.join(backupDir, backupFile);
|
||||
const originalPath = backupFile.replace(".backup", "");
|
||||
const originalPath = pathMapping[backupFile];
|
||||
|
||||
if (fs.existsSync(originalPath)) {
|
||||
fs.copyFileSync(backupPath, originalPath);
|
||||
restoredCount++;
|
||||
if (!originalPath) {
|
||||
console.warn(chalk.yellow(`⚠️ No mapping found for backup file: ${backupFile}`));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (options.verbose) {
|
||||
console.log(chalk.gray(`Restored: ${originalPath}`));
|
||||
}
|
||||
// Ensure the directory exists
|
||||
const originalDir = path.dirname(originalPath);
|
||||
if (!fs.existsSync(originalDir)) {
|
||||
fs.mkdirSync(originalDir, { recursive: true });
|
||||
}
|
||||
|
||||
fs.copyFileSync(backupPath, originalPath);
|
||||
restoredCount++;
|
||||
|
||||
if (options.verbose) {
|
||||
console.log(chalk.gray(`Restored: ${originalPath}`));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,13 +254,25 @@ async function createBackups(migrator: TypeScriptMigrator, outputDir: string): P
|
||||
const usages = migrator.analyzeUsage();
|
||||
const filesToBackup = new Set(usages.map((u) => u.filePath));
|
||||
|
||||
// Create a mapping file to track original paths
|
||||
const pathMapping: Record<string, string> = {};
|
||||
|
||||
for (const filePath of filesToBackup) {
|
||||
if (fs.existsSync(filePath)) {
|
||||
const backupPath = path.join(backupDir, path.basename(filePath) + ".backup");
|
||||
// Create a unique backup filename that preserves path info
|
||||
const relativePath = path.relative(process.cwd(), filePath);
|
||||
const backupFileName = relativePath.replace(/[/\\]/g, "_") + ".backup";
|
||||
const backupPath = path.join(backupDir, backupFileName);
|
||||
|
||||
fs.copyFileSync(filePath, backupPath);
|
||||
pathMapping[backupFileName] = filePath;
|
||||
}
|
||||
}
|
||||
|
||||
// Save the path mapping for restoration
|
||||
const mappingPath = path.join(backupDir, "path-mapping.json");
|
||||
fs.writeFileSync(mappingPath, JSON.stringify(pathMapping, null, 2));
|
||||
|
||||
console.log(chalk.green(`📦 Created backups for ${filesToBackup.size} files`));
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user