fix(format): Improve concurrency control in cache and rollback management with mutex locking and refine formatting details
This commit is contained in:
@@ -10,7 +10,7 @@ export class FormatPlanner {
|
||||
private plannedChanges: Map<string, IPlannedChange[]> = new Map();
|
||||
private dependencyAnalyzer = new DependencyAnalyzer();
|
||||
private diffReporter = new DiffReporter();
|
||||
|
||||
|
||||
async planFormat(modules: BaseFormatter[]): Promise<IFormatPlan> {
|
||||
const plan: IFormatPlan = {
|
||||
summary: {
|
||||
@@ -18,20 +18,20 @@ export class FormatPlanner {
|
||||
filesAdded: 0,
|
||||
filesModified: 0,
|
||||
filesRemoved: 0,
|
||||
estimatedTime: 0
|
||||
estimatedTime: 0,
|
||||
},
|
||||
changes: [],
|
||||
warnings: []
|
||||
warnings: [],
|
||||
};
|
||||
|
||||
|
||||
for (const module of modules) {
|
||||
try {
|
||||
const changes = await module.analyze();
|
||||
this.plannedChanges.set(module.name, changes);
|
||||
|
||||
|
||||
for (const change of changes) {
|
||||
plan.changes.push(change);
|
||||
|
||||
|
||||
// Update summary
|
||||
switch (change.type) {
|
||||
case 'create':
|
||||
@@ -49,67 +49,51 @@ export class FormatPlanner {
|
||||
plan.warnings.push({
|
||||
level: 'error',
|
||||
message: `Failed to analyze module ${module.name}: ${error.message}`,
|
||||
module: module.name
|
||||
module: module.name,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
plan.summary.totalFiles = plan.summary.filesAdded + plan.summary.filesModified + plan.summary.filesRemoved;
|
||||
|
||||
plan.summary.totalFiles =
|
||||
plan.summary.filesAdded +
|
||||
plan.summary.filesModified +
|
||||
plan.summary.filesRemoved;
|
||||
plan.summary.estimatedTime = plan.summary.totalFiles * 100; // 100ms per file estimate
|
||||
|
||||
|
||||
return plan;
|
||||
}
|
||||
|
||||
async executePlan(plan: IFormatPlan, modules: BaseFormatter[], context: FormatContext, parallel: boolean = true): Promise<void> {
|
||||
await context.beginOperation();
|
||||
|
||||
async executePlan(
|
||||
plan: IFormatPlan,
|
||||
modules: BaseFormatter[],
|
||||
context: FormatContext,
|
||||
parallel: boolean = false,
|
||||
): Promise<void> {
|
||||
const startTime = Date.now();
|
||||
|
||||
|
||||
try {
|
||||
if (parallel) {
|
||||
// Get execution groups based on dependencies
|
||||
const executionGroups = this.dependencyAnalyzer.getExecutionGroups(modules);
|
||||
|
||||
logger.log('info', `Executing formatters in ${executionGroups.length} groups...`);
|
||||
|
||||
for (let i = 0; i < executionGroups.length; i++) {
|
||||
const group = executionGroups[i];
|
||||
logger.log('info', `Executing group ${i + 1}: ${group.map(m => m.name).join(', ')}`);
|
||||
|
||||
// Execute modules in this group in parallel
|
||||
const promises = group.map(async (module) => {
|
||||
const changes = this.plannedChanges.get(module.name) || [];
|
||||
if (changes.length > 0) {
|
||||
logger.log('info', `Executing ${module.name} formatter...`);
|
||||
await module.execute(changes);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
} else {
|
||||
// Sequential execution (original implementation)
|
||||
for (const module of modules) {
|
||||
const changes = this.plannedChanges.get(module.name) || [];
|
||||
|
||||
if (changes.length > 0) {
|
||||
logger.log('info', `Executing ${module.name} formatter...`);
|
||||
await module.execute(changes);
|
||||
}
|
||||
// Always use sequential execution to avoid race conditions
|
||||
for (const module of modules) {
|
||||
const changes = this.plannedChanges.get(module.name) || [];
|
||||
|
||||
if (changes.length > 0) {
|
||||
logger.log('info', `Executing ${module.name} formatter...`);
|
||||
await module.execute(changes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const endTime = Date.now();
|
||||
const duration = endTime - startTime;
|
||||
logger.log('info', `Format operations completed in ${duration}ms`);
|
||||
|
||||
await context.commitOperation();
|
||||
} catch (error) {
|
||||
await context.rollbackOperation();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async displayPlan(plan: IFormatPlan, detailed: boolean = false): Promise<void> {
|
||||
|
||||
async displayPlan(
|
||||
plan: IFormatPlan,
|
||||
detailed: boolean = false,
|
||||
): Promise<void> {
|
||||
console.log('\nFormat Plan:');
|
||||
console.log('━'.repeat(50));
|
||||
console.log(`Summary: ${plan.summary.totalFiles} files will be changed`);
|
||||
@@ -118,7 +102,7 @@ export class FormatPlanner {
|
||||
console.log(` • ${plan.summary.filesRemoved} deleted files`);
|
||||
console.log('');
|
||||
console.log('Changes by module:');
|
||||
|
||||
|
||||
// Group changes by module
|
||||
const changesByModule = new Map<string, IPlannedChange[]>();
|
||||
for (const change of plan.changes) {
|
||||
@@ -126,14 +110,16 @@ export class FormatPlanner {
|
||||
moduleChanges.push(change);
|
||||
changesByModule.set(change.module, moduleChanges);
|
||||
}
|
||||
|
||||
|
||||
for (const [module, changes] of changesByModule) {
|
||||
console.log(`\n${this.getModuleIcon(module)} ${module} (${changes.length} ${changes.length === 1 ? 'file' : 'files'})`);
|
||||
|
||||
console.log(
|
||||
`\n${this.getModuleIcon(module)} ${module} (${changes.length} ${changes.length === 1 ? 'file' : 'files'})`,
|
||||
);
|
||||
|
||||
for (const change of changes) {
|
||||
const icon = this.getChangeIcon(change.type);
|
||||
console.log(` ${icon} ${change.path} - ${change.description}`);
|
||||
|
||||
|
||||
// Show diff for modified files if detailed view is requested
|
||||
if (detailed && change.type === 'modify') {
|
||||
const diff = await this.diffReporter.generateDiffForChange(change);
|
||||
@@ -143,7 +129,7 @@ export class FormatPlanner {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (plan.warnings.length > 0) {
|
||||
console.log('\nWarnings:');
|
||||
for (const warning of plan.warnings) {
|
||||
@@ -151,26 +137,26 @@ export class FormatPlanner {
|
||||
console.log(` ${icon} ${warning.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
console.log('\n' + '━'.repeat(50));
|
||||
}
|
||||
|
||||
|
||||
private getModuleIcon(module: string): string {
|
||||
const icons: Record<string, string> = {
|
||||
'packagejson': '📦',
|
||||
'license': '📝',
|
||||
'tsconfig': '🔧',
|
||||
'cleanup': '🚮',
|
||||
'gitignore': '🔒',
|
||||
'prettier': '✨',
|
||||
'readme': '📖',
|
||||
'templates': '📄',
|
||||
'npmextra': '⚙️',
|
||||
'copy': '📋'
|
||||
packagejson: '📦',
|
||||
license: '📝',
|
||||
tsconfig: '🔧',
|
||||
cleanup: '🚮',
|
||||
gitignore: '🔒',
|
||||
prettier: '✨',
|
||||
readme: '📖',
|
||||
templates: '📄',
|
||||
npmextra: '⚙️',
|
||||
copy: '📋',
|
||||
};
|
||||
return icons[module] || '📁';
|
||||
}
|
||||
|
||||
|
||||
private getChangeIcon(type: 'create' | 'modify' | 'delete'): string {
|
||||
switch (type) {
|
||||
case 'create':
|
||||
@@ -181,4 +167,4 @@ export class FormatPlanner {
|
||||
return '❌';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user