markmv
Version:
TypeScript CLI for markdown file operations with intelligent link refactoring
260 lines (209 loc) โข 8.01 kB
text/typescript
/**
* TypeScript example demonstrating programmatic usage of markmv
*
* This example shows how to use markmv as a library with full TypeScript support.
*
* To run: npx tsx examples/typescript-usage.ts
* Or compile: tsc examples/typescript-usage.ts && node examples/typescript-usage.js
*/
import {
createMarkMv,
moveFile,
moveFiles,
validateOperation,
FileOperations,
type MoveOperationOptions,
type OperationResult,
type OperationChange
} from '../dist/index.js';
import { join } from 'node:path';
import { writeFileSync, mkdirSync, rmSync, existsSync } from 'node:fs';
/**
* Type-safe wrapper for file operations with enhanced error handling
*/
class SafeMarkMv {
private fileOps: FileOperations;
constructor() {
this.fileOps = new FileOperations();
}
/**
* Move a file with comprehensive result handling
*/
async moveWithValidation(
source: string,
destination: string,
options: MoveOperationOptions = {}
): Promise<{
result: OperationResult;
validation: { valid: boolean; brokenLinks: number; errors: string[] }
}> {
const result = await this.fileOps.moveFile(source, destination, options);
const validation = await this.fileOps.validateOperation(result);
return { result, validation };
}
/**
* Get detailed statistics about an operation
*/
getOperationStats(result: OperationResult): {
filesAffected: number;
linksUpdated: number;
changesByType: Record<string, number>;
} {
const linkUpdates = result.changes.filter(c => c.type === 'link-updated');
const changesByType = result.changes.reduce((acc, change) => {
acc[change.type] = (acc[change.type] || 0) + 1;
return acc;
}, {} as Record<string, number>);
return {
filesAffected: result.modifiedFiles.length + result.createdFiles.length + result.deletedFiles.length,
linksUpdated: linkUpdates.length,
changesByType
};
}
}
async function main(): Promise<void> {
const exampleDir = join(__dirname, 'temp-ts-example');
// Clean up and create example directory
if (existsSync(exampleDir)) {
rmSync(exampleDir, { recursive: true });
}
mkdirSync(exampleDir, { recursive: true });
console.log('๐ TypeScript Markmv API Examples\n');
try {
// Example 1: Type-safe options and results
console.log('๐ Example 1: Type-safe configuration');
const sourceFile = join(exampleDir, 'source.md');
const destFile = join(exampleDir, 'destination.md');
writeFileSync(sourceFile, `# Source Document
This document will be moved.
## Links
- [Related](./related.md)
- [Assets](./assets/image.png)
## Code
\`\`\`typescript
const example = "TypeScript code";
\`\`\`
`);
writeFileSync(join(exampleDir, 'related.md'), '# Related Document\n\nThis is related content.');
// Type-safe options
const options: MoveOperationOptions = {
dryRun: true,
verbose: true,
createDirectories: true
};
const result: OperationResult = await moveFile(sourceFile, destFile, options);
console.log(`โ
Move operation: ${result.success ? 'SUCCESS' : 'FAILED'}`);
console.log(`๐ Summary: ${result.modifiedFiles.length} modified, ${result.createdFiles.length} created, ${result.deletedFiles.length} deleted`);
// Type-safe change analysis
const linkChanges: OperationChange[] = result.changes.filter(c => c.type === 'link-updated');
console.log(`๐ Link updates: ${linkChanges.length}`);
linkChanges.forEach(change => {
console.log(` ๐ ${change.filePath}`);
console.log(` "${change.oldValue}" โ "${change.newValue}"`);
});
console.log('');
// Example 2: Using the enhanced wrapper class
console.log('๐ Example 2: Enhanced wrapper with validation');
const safeMarkMv = new SafeMarkMv();
const { result: enhancedResult, validation } = await safeMarkMv.moveWithValidation(
sourceFile,
destFile,
{ dryRun: true }
);
const stats = safeMarkMv.getOperationStats(enhancedResult);
console.log(`โ
Enhanced operation: ${enhancedResult.success ? 'SUCCESS' : 'FAILED'}`);
console.log(`๐ Files affected: ${stats.filesAffected}`);
console.log(`๐ Links updated: ${stats.linksUpdated}`);
console.log(`๐ Validation: ${validation.valid ? 'PASSED' : 'FAILED'}`);
console.log(`โ ๏ธ Broken links: ${validation.brokenLinks}`);
console.log('๐ Changes by type:');
Object.entries(stats.changesByType).forEach(([type, count]) => {
console.log(` ${type}: ${count}`);
});
console.log('');
// Example 3: Batch operations with type safety
console.log('๐ Example 3: Batch operations');
const files = ['doc1.md', 'doc2.md', 'doc3.md'].map(name => join(exampleDir, name));
const destinations = ['new-doc1.md', 'new-doc2.md', 'new-doc3.md'].map(name => join(exampleDir, name));
// Create source files
files.forEach((file, index) => {
writeFileSync(file, `# Document ${index + 1}
Content for document ${index + 1}.
[Link to doc ${((index + 1) % 3) + 1}](./doc${((index + 1) % 3) + 1}.md)
`);
});
const moves = files.map((source, index) => ({
source,
destination: destinations[index]
}));
const batchResult: OperationResult = await moveFiles(moves, { dryRun: true });
console.log(`โ
Batch operation: ${batchResult.success ? 'SUCCESS' : 'FAILED'}`);
console.log(`๐ Files in batch: ${moves.length}`);
console.log(`๐ Files to be created: ${batchResult.createdFiles.length}`);
console.log(`๐๏ธ Files to be deleted: ${batchResult.deletedFiles.length}`);
console.log(`๐ Files to be modified: ${batchResult.modifiedFiles.length}`);
// Detailed change analysis
const changeTypes = new Set(batchResult.changes.map(c => c.type));
console.log(`๐ Change types: ${Array.from(changeTypes).join(', ')}`);
console.log('');
// Example 4: Error handling with types
console.log('๐ Example 4: Error handling');
try {
const invalidResult = await moveFile(
join(exampleDir, 'nonexistent.md'),
join(exampleDir, 'destination.md'),
{ dryRun: true }
);
if (!invalidResult.success) {
console.log('โ Operation failed as expected:');
invalidResult.errors.forEach(error => console.log(` ${error}`));
}
} catch (error) {
console.log('โ Caught exception:', (error as Error).message);
}
} catch (error) {
console.error('โ Error running TypeScript examples:', (error as Error).message);
} finally {
// Clean up
if (existsSync(exampleDir)) {
rmSync(exampleDir, { recursive: true });
}
console.log('๐งน Cleaned up temporary files');
}
}
// Advanced usage patterns
interface ProjectConfig {
sourceDir: string;
outputDir: string;
moveOptions: MoveOperationOptions;
}
/**
* Example of a more complex integration pattern
*/
class ProjectReorganizer {
private config: ProjectConfig;
private markmv: FileOperations;
constructor(config: ProjectConfig) {
this.config = config;
this.markmv = createMarkMv();
}
async reorganizeProject(): Promise<OperationResult[]> {
// This would contain your project-specific logic
console.log('๐ Project reorganization would happen here...');
// Example: Move all docs to a new structure
const results: OperationResult[] = [];
// Simulate some moves
const exampleMoves = [
{ source: join(this.config.sourceDir, 'readme.md'), destination: join(this.config.outputDir, 'README.md') }
];
for (const move of exampleMoves) {
const result = await this.markmv.moveFile(move.source, move.destination, this.config.moveOptions);
results.push(result);
}
return results;
}
}
// Run examples if this file is executed directly
if (require.main === module) {
main().catch(console.error);
}