158 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
#!/usr/bin/env node
 | 
						|
 | 
						|
/**
 | 
						|
 * Migration script for converting old flat JSON Excalidraw files to Obsidian format
 | 
						|
 * Usage: node server/migrate-excalidraw.mjs [--dry-run] [--vault-path=./vault]
 | 
						|
 */
 | 
						|
 | 
						|
import fs from 'fs';
 | 
						|
import path from 'path';
 | 
						|
import { fileURLToPath } from 'url';
 | 
						|
import { parseFlatJson, toObsidianExcalidrawMd, isValidExcalidrawScene } from './excalidraw-obsidian.mjs';
 | 
						|
 | 
						|
const __filename = fileURLToPath(import.meta.url);
 | 
						|
const __dirname = path.dirname(__filename);
 | 
						|
 | 
						|
// Parse command line arguments
 | 
						|
const args = process.argv.slice(2);
 | 
						|
const dryRun = args.includes('--dry-run');
 | 
						|
const vaultPathArg = args.find(arg => arg.startsWith('--vault-path='));
 | 
						|
const vaultPath = vaultPathArg 
 | 
						|
  ? path.resolve(vaultPathArg.split('=')[1])
 | 
						|
  : path.resolve(__dirname, '..', 'vault');
 | 
						|
 | 
						|
console.log('🔄 Excalidraw Migration Tool');
 | 
						|
console.log('━'.repeat(50));
 | 
						|
console.log(`Vault path: ${vaultPath}`);
 | 
						|
console.log(`Mode: ${dryRun ? 'DRY RUN (no changes)' : 'LIVE (will modify files)'}`);
 | 
						|
console.log('━'.repeat(50));
 | 
						|
console.log();
 | 
						|
 | 
						|
if (!fs.existsSync(vaultPath)) {
 | 
						|
  console.error(`❌ Vault directory not found: ${vaultPath}`);
 | 
						|
  process.exit(1);
 | 
						|
}
 | 
						|
 | 
						|
let filesScanned = 0;
 | 
						|
let filesConverted = 0;
 | 
						|
let filesSkipped = 0;
 | 
						|
let filesErrored = 0;
 | 
						|
 | 
						|
/**
 | 
						|
 * Recursively scan directory for Excalidraw files
 | 
						|
 */
 | 
						|
function scanDirectory(dir) {
 | 
						|
  const entries = fs.readdirSync(dir, { withFileTypes: true });
 | 
						|
  
 | 
						|
  for (const entry of entries) {
 | 
						|
    const fullPath = path.join(dir, entry.name);
 | 
						|
    
 | 
						|
    if (entry.isDirectory()) {
 | 
						|
      // Skip hidden directories
 | 
						|
      if (entry.name.startsWith('.')) continue;
 | 
						|
      scanDirectory(fullPath);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    
 | 
						|
    if (!entry.isFile()) continue;
 | 
						|
    
 | 
						|
    const lower = entry.name.toLowerCase();
 | 
						|
    
 | 
						|
    // Look for .excalidraw or .json files (but not .excalidraw.md)
 | 
						|
    if (lower.endsWith('.excalidraw.md')) {
 | 
						|
      // Already in Obsidian format, skip
 | 
						|
      filesSkipped++;
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    
 | 
						|
    if (!lower.endsWith('.excalidraw') && !lower.endsWith('.json')) {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
    
 | 
						|
    filesScanned++;
 | 
						|
    processFile(fullPath);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Process a single file
 | 
						|
 */
 | 
						|
function processFile(filePath) {
 | 
						|
  const relativePath = path.relative(vaultPath, filePath);
 | 
						|
  
 | 
						|
  try {
 | 
						|
    const content = fs.readFileSync(filePath, 'utf-8');
 | 
						|
    
 | 
						|
    // Try to parse as flat JSON
 | 
						|
    const scene = parseFlatJson(content);
 | 
						|
    
 | 
						|
    if (!scene || !isValidExcalidrawScene(scene)) {
 | 
						|
      console.log(`⏭️  Skipped (not valid Excalidraw): ${relativePath}`);
 | 
						|
      filesSkipped++;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Convert to Obsidian format
 | 
						|
    const obsidianMd = toObsidianExcalidrawMd(scene);
 | 
						|
    
 | 
						|
    // Determine new file path
 | 
						|
    const dir = path.dirname(filePath);
 | 
						|
    const baseName = path.basename(filePath, path.extname(filePath));
 | 
						|
    const newPath = path.join(dir, `${baseName}.excalidraw.md`);
 | 
						|
    
 | 
						|
    if (dryRun) {
 | 
						|
      console.log(`✅ Would convert: ${relativePath} → ${path.basename(newPath)}`);
 | 
						|
      filesConverted++;
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Create backup of original
 | 
						|
    const backupPath = filePath + '.bak';
 | 
						|
    fs.copyFileSync(filePath, backupPath);
 | 
						|
    
 | 
						|
    // Write new file
 | 
						|
    fs.writeFileSync(newPath, obsidianMd, 'utf-8');
 | 
						|
    
 | 
						|
    // Remove original if new file is different
 | 
						|
    if (newPath !== filePath) {
 | 
						|
      fs.unlinkSync(filePath);
 | 
						|
    }
 | 
						|
    
 | 
						|
    console.log(`✅ Converted: ${relativePath} → ${path.basename(newPath)}`);
 | 
						|
    filesConverted++;
 | 
						|
    
 | 
						|
  } catch (error) {
 | 
						|
    console.error(`❌ Error processing ${relativePath}:`, error.message);
 | 
						|
    filesErrored++;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
// Run migration
 | 
						|
try {
 | 
						|
  scanDirectory(vaultPath);
 | 
						|
  
 | 
						|
  console.log();
 | 
						|
  console.log('━'.repeat(50));
 | 
						|
  console.log('📊 Migration Summary');
 | 
						|
  console.log('━'.repeat(50));
 | 
						|
  console.log(`Files scanned:   ${filesScanned}`);
 | 
						|
  console.log(`Files converted: ${filesConverted}`);
 | 
						|
  console.log(`Files skipped:   ${filesSkipped}`);
 | 
						|
  console.log(`Files errored:   ${filesErrored}`);
 | 
						|
  console.log('━'.repeat(50));
 | 
						|
  
 | 
						|
  if (dryRun) {
 | 
						|
    console.log();
 | 
						|
    console.log('💡 This was a dry run. Run without --dry-run to apply changes.');
 | 
						|
  } else if (filesConverted > 0) {
 | 
						|
    console.log();
 | 
						|
    console.log('✅ Migration complete! Backup files (.bak) were created.');
 | 
						|
  }
 | 
						|
  
 | 
						|
  process.exit(filesErrored > 0 ? 1 : 0);
 | 
						|
  
 | 
						|
} catch (error) {
 | 
						|
  console.error('❌ Migration failed:', error);
 | 
						|
  process.exit(1);
 | 
						|
}
 |