UNPKG

@re-shell/cli

Version:

Full-stack development platform uniting microservices and microfrontends. Build complete applications with .NET (ASP.NET Core Web API, Minimal API), Java (Spring Boot, Quarkus, Micronaut, Vert.x), Rust (Actix-Web, Warp, Rocket, Axum), Python (FastAPI, Dja

668 lines (667 loc) 28.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.WorkspaceConflictManager = void 0; exports.createWorkspaceConflictManager = createWorkspaceConflictManager; exports.detectWorkspaceConflicts = detectWorkspaceConflicts; exports.autoResolveConflicts = autoResolveConflicts; const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); const yaml = __importStar(require("yaml")); const error_handler_1 = require("./error-handler"); // Workspace conflict detector and resolver class WorkspaceConflictManager { constructor(rootPath = process.cwd()) { this.conflicts = new Map(); this.rootPath = rootPath; } // Detect all conflicts in workspace definition async detectConflicts(definition, options = {}) { this.conflicts.clear(); const defaultOptions = { includeWarnings: true, checkDependencies: true, checkPorts: true, checkPaths: true, checkTypes: true, enableResolution: true, ...options }; // Check for naming conflicts await this.detectNamingConflicts(definition); // Check for dependency conflicts if (defaultOptions.checkDependencies) { await this.detectDependencyConflicts(definition); } // Check for port collisions if (defaultOptions.checkPorts) { await this.detectPortConflicts(definition); } // Check for path collisions if (defaultOptions.checkPaths) { await this.detectPathConflicts(definition); } // Check for type mismatches if (defaultOptions.checkTypes) { await this.detectTypeConflicts(definition); } // Check for configuration conflicts await this.detectConfigurationConflicts(definition); // Generate resolutions if enabled if (defaultOptions.enableResolution) { await this.generateResolutions(definition); } return Array.from(this.conflicts.values()); } // Resolve conflicts automatically or with guidance async resolveConflicts(definition, conflicts, autoResolve = false) { const result = { resolved: [], unresolved: [], changes: [], warnings: [] }; for (const conflict of conflicts) { try { const resolution = await this.resolveConflict(definition, conflict, autoResolve); if (resolution) { result.resolved.push(conflict); result.changes.push(...resolution.changes); result.warnings.push(...resolution.warnings); } else { result.unresolved.push(conflict); } } catch (error) { result.unresolved.push(conflict); result.warnings.push(`Failed to resolve conflict ${conflict.id}: ${error.message}`); } } return result; } // Validate resolution preview async previewResolution(definition, conflict, resolutionId) { const resolution = conflict.suggestions.find(r => r.id === resolutionId); if (!resolution) { throw new error_handler_1.ValidationError(`Resolution ${resolutionId} not found for conflict ${conflict.id}`); } // Create a deep copy for preview const preview = JSON.parse(JSON.stringify(definition)); const changes = []; const warnings = []; try { await this.applyResolution(preview, conflict, resolution, changes, true); return { success: true, changes, warnings, preview }; } catch (error) { return { success: false, changes: [], warnings: [error.message], preview: definition }; } } // Detect naming conflicts async detectNamingConflicts(definition) { const workspaceNames = Object.keys(definition.workspaces); const duplicates = workspaceNames.filter((name, index) => workspaceNames.indexOf(name) !== index); for (const duplicate of duplicates) { this.conflicts.set(`naming-${duplicate}`, { id: `naming-${duplicate}`, type: 'naming', severity: 'error', description: `Duplicate workspace name: ${duplicate}`, details: `Multiple workspaces are using the same name '${duplicate}'. Each workspace must have a unique name.`, affectedWorkspaces: [duplicate], suggestions: [ { id: 'rename-workspace', description: `Rename one of the duplicate workspaces`, action: 'rename-workspace', automatic: false, riskLevel: 'low', preview: `Rename '${duplicate}' to '${duplicate}-2'` } ] }); } // Check for reserved names const reservedNames = ['build', 'test', 'dev', 'prod', 'staging', 'config']; for (const [name, workspace] of Object.entries(definition.workspaces)) { if (reservedNames.includes(name.toLowerCase())) { this.conflicts.set(`reserved-${name}`, { id: `reserved-${name}`, type: 'naming', severity: 'warning', description: `Workspace uses reserved name: ${name}`, details: `The workspace name '${name}' conflicts with reserved system names.`, affectedWorkspaces: [name], suggestions: [ { id: 'rename-workspace', description: `Rename workspace to avoid reserved name`, action: 'rename-workspace', automatic: false, riskLevel: 'low', preview: `Rename '${name}' to '${name}-app'` } ] }); } } } // Detect dependency conflicts async detectDependencyConflicts(definition) { // Check for circular dependencies const cycles = this.findDependencyCycles(definition); for (const cycle of cycles) { this.conflicts.set(`cycle-${cycle.join('-')}`, { id: `cycle-${cycle.join('-')}`, type: 'dependency-cycle', severity: 'error', description: `Circular dependency detected: ${cycle.join(' → ')}`, details: `A circular dependency exists between workspaces: ${cycle.join(' → ')} → ${cycle[0]}`, affectedWorkspaces: cycle, suggestions: [ { id: 'remove-dependency', description: `Remove dependency from ${cycle[cycle.length - 1]} to ${cycle[0]}`, action: 'remove-dependency', automatic: false, riskLevel: 'medium', preview: `Remove '${cycle[0]}' from ${cycle[cycle.length - 1]} dependencies` }, { id: 'extract-common', description: `Extract common functionality to a shared library`, action: 'split-workspace', automatic: false, riskLevel: 'high' } ] }); } // Check for missing dependencies for (const [workspaceName, deps] of Object.entries(definition.dependencies || {})) { if (!definition.workspaces[workspaceName]) { this.conflicts.set(`missing-workspace-${workspaceName}`, { id: `missing-workspace-${workspaceName}`, type: 'dependency-missing', severity: 'error', description: `Dependencies defined for non-existent workspace: ${workspaceName}`, details: `Dependencies are defined for workspace '${workspaceName}' but this workspace doesn't exist.`, affectedWorkspaces: [workspaceName], suggestions: [ { id: 'remove-dependency', description: `Remove dependencies for non-existent workspace`, action: 'remove-dependency', automatic: true, riskLevel: 'low' } ] }); } for (const dep of deps) { if (!definition.workspaces[dep.name]) { this.conflicts.set(`missing-dep-${workspaceName}-${dep.name}`, { id: `missing-dep-${workspaceName}-${dep.name}`, type: 'dependency-missing', severity: 'error', description: `Missing dependency: ${dep.name}`, details: `Workspace '${workspaceName}' depends on '${dep.name}' which doesn't exist.`, affectedWorkspaces: [workspaceName, dep.name], suggestions: [ { id: 'remove-dependency', description: `Remove dependency on non-existent workspace`, action: 'remove-dependency', automatic: true, riskLevel: 'low' } ] }); } } } } // Detect port conflicts async detectPortConflicts(definition) { const portMap = new Map(); for (const [name, workspace] of Object.entries(definition.workspaces)) { if (workspace.dev?.port) { const port = workspace.dev.port; if (!portMap.has(port)) { portMap.set(port, []); } portMap.get(port).push(name); } } for (const [port, workspaces] of portMap.entries()) { if (workspaces.length > 1) { this.conflicts.set(`port-${port}`, { id: `port-${port}`, type: 'port-collision', severity: 'error', description: `Port collision on ${port}`, details: `Multiple workspaces are configured to use port ${port}: ${workspaces.join(', ')}`, affectedWorkspaces: workspaces, suggestions: [ { id: 'auto-assign-ports', description: `Automatically assign different ports`, action: 'change-port', automatic: true, riskLevel: 'low', preview: `Assign sequential ports starting from ${port + 1}` } ] }); } } } // Detect path conflicts async detectPathConflicts(definition) { const pathMap = new Map(); for (const [name, workspace] of Object.entries(definition.workspaces)) { if (workspace.path) { const normalizedPath = path.normalize(workspace.path); if (!pathMap.has(normalizedPath)) { pathMap.set(normalizedPath, []); } pathMap.get(normalizedPath).push(name); } } for (const [workspacePath, workspaces] of pathMap.entries()) { if (workspaces.length > 1) { this.conflicts.set(`path-${workspacePath}`, { id: `path-${workspacePath}`, type: 'path-collision', severity: 'error', description: `Path collision: ${workspacePath}`, details: `Multiple workspaces are using the same path '${workspacePath}': ${workspaces.join(', ')}`, affectedWorkspaces: workspaces, suggestions: [ { id: 'change-paths', description: `Assign unique paths to each workspace`, action: 'change-path', automatic: false, riskLevel: 'medium', preview: `Move workspaces to subdirectories` } ] }); } } } // Detect type conflicts async detectTypeConflicts(definition) { for (const [name, workspace] of Object.entries(definition.workspaces)) { if (!definition.types[workspace.type]) { this.conflicts.set(`type-${name}`, { id: `type-${name}`, type: 'type-mismatch', severity: 'error', description: `Undefined workspace type: ${workspace.type}`, details: `Workspace '${name}' references type '${workspace.type}' which is not defined.`, affectedWorkspaces: [name], suggestions: [ { id: 'define-type', description: `Define the missing workspace type`, action: 'auto-resolve', automatic: true, riskLevel: 'low', preview: `Create type definition for '${workspace.type}'` }, { id: 'change-type', description: `Change to an existing type`, action: 'update-dependency', automatic: false, riskLevel: 'low' } ] }); } } } // Detect configuration conflicts async detectConfigurationConflicts(definition) { // Check for conflicting build commands const buildCommands = new Set(); for (const [name, workspace] of Object.entries(definition.workspaces)) { const type = definition.types[workspace.type]; if (type?.build?.command && buildCommands.has(type.build.command)) { this.conflicts.set(`build-command-${name}`, { id: `build-command-${name}`, type: 'build-target', severity: 'warning', description: `Conflicting build command: ${type.build.command}`, details: `Multiple workspaces are using the same build command '${type.build.command}'`, affectedWorkspaces: [name], suggestions: [ { id: 'unique-build-command', description: `Use unique build commands`, action: 'auto-resolve', automatic: true, riskLevel: 'low' } ] }); } if (type?.build?.command) { buildCommands.add(type.build.command); } } } // Generate resolution suggestions async generateResolutions(definition) { // Enhanced resolution generation based on conflict analysis for (const conflict of this.conflicts.values()) { if (conflict.suggestions.length === 0) { this.generateDefaultResolutions(conflict); } } } // Generate default resolutions for conflicts without suggestions generateDefaultResolutions(conflict) { switch (conflict.type) { case 'naming': conflict.suggestions.push({ id: 'auto-rename', description: 'Automatically generate unique names', action: 'rename-workspace', automatic: true, riskLevel: 'low' }); break; case 'port-collision': conflict.suggestions.push({ id: 'sequential-ports', description: 'Assign sequential ports', action: 'change-port', automatic: true, riskLevel: 'low' }); break; case 'dependency-missing': conflict.suggestions.push({ id: 'remove-missing', description: 'Remove references to missing dependencies', action: 'remove-dependency', automatic: true, riskLevel: 'low' }); break; } } // Find dependency cycles using DFS findDependencyCycles(definition) { const cycles = []; const visited = new Set(); const recursionStack = new Set(); const path = []; const dfs = (workspace) => { if (recursionStack.has(workspace)) { // Found a cycle const cycleStart = path.indexOf(workspace); if (cycleStart !== -1) { cycles.push(path.slice(cycleStart)); } return; } if (visited.has(workspace)) { return; } visited.add(workspace); recursionStack.add(workspace); path.push(workspace); const dependencies = definition.dependencies?.[workspace] || []; for (const dep of dependencies) { if (definition.workspaces[dep.name]) { dfs(dep.name); } } recursionStack.delete(workspace); path.pop(); }; for (const workspace of Object.keys(definition.workspaces)) { if (!visited.has(workspace)) { dfs(workspace); } } return cycles; } // Resolve individual conflict async resolveConflict(definition, conflict, autoResolve) { const changes = []; const warnings = []; // Find automatic resolution if auto-resolve is enabled let resolution = conflict.suggestions.find(r => r.automatic && autoResolve); // Otherwise, use the first low-risk resolution if (!resolution) { resolution = conflict.suggestions.find(r => r.riskLevel === 'low'); } if (!resolution) { return null; } await this.applyResolution(definition, conflict, resolution, changes); return { changes, warnings }; } // Apply a specific resolution async applyResolution(definition, conflict, resolution, changes, preview = false) { switch (resolution.action) { case 'rename-workspace': await this.applyWorkspaceRename(definition, conflict, changes, preview); break; case 'change-port': await this.applyPortChange(definition, conflict, changes, preview); break; case 'change-path': await this.applyPathChange(definition, conflict, changes, preview); break; case 'remove-dependency': await this.applyDependencyRemoval(definition, conflict, changes, preview); break; case 'auto-resolve': await this.applyAutoResolve(definition, conflict, changes, preview); break; } } // Apply workspace rename resolution async applyWorkspaceRename(definition, conflict, changes, preview = false) { const affected = conflict.affectedWorkspaces; for (let i = 1; i < affected.length; i++) { const oldName = affected[i]; const newName = `${oldName}-${i + 1}`; if (!preview) { // Rename workspace definition.workspaces[newName] = definition.workspaces[oldName]; delete definition.workspaces[oldName]; // Update dependencies for (const [workspace, deps] of Object.entries(definition.dependencies || {})) { for (const dep of deps) { if (dep.name === oldName) { dep.name = newName; } } } } changes.push({ type: 'workspace', target: oldName, property: 'name', oldValue: oldName, newValue: newName, reason: `Resolved naming conflict` }); } } // Apply port change resolution async applyPortChange(definition, conflict, changes, preview = false) { const basePort = parseInt(conflict.id.split('-')[1]); let currentPort = basePort + 1; for (let i = 1; i < conflict.affectedWorkspaces.length; i++) { const workspaceName = conflict.affectedWorkspaces[i]; const workspace = definition.workspaces[workspaceName]; if (workspace.dev) { const oldPort = workspace.dev.port; if (!preview) { workspace.dev.port = currentPort; } changes.push({ type: 'workspace', target: workspaceName, property: 'dev.port', oldValue: oldPort, newValue: currentPort, reason: `Resolved port collision` }); currentPort++; } } } // Apply path change resolution async applyPathChange(definition, conflict, changes, preview = false) { const basePath = conflict.id.replace('path-', ''); for (let i = 1; i < conflict.affectedWorkspaces.length; i++) { const workspaceName = conflict.affectedWorkspaces[i]; const workspace = definition.workspaces[workspaceName]; const oldPath = workspace.path; const newPath = path.join(basePath, workspaceName); if (!preview) { workspace.path = newPath; } changes.push({ type: 'workspace', target: workspaceName, property: 'path', oldValue: oldPath, newValue: newPath, reason: `Resolved path collision` }); } } // Apply dependency removal resolution async applyDependencyRemoval(definition, conflict, changes, preview = false) { if (conflict.type === 'dependency-missing') { const [, , workspace, depName] = conflict.id.split('-'); if (definition.dependencies?.[workspace]) { const deps = definition.dependencies[workspace]; const depIndex = deps.findIndex(d => d.name === depName); if (depIndex !== -1) { if (!preview) { deps.splice(depIndex, 1); } changes.push({ type: 'dependency', target: workspace, property: 'dependencies', oldValue: depName, newValue: null, reason: `Removed missing dependency` }); } } } } // Apply auto-resolve resolution async applyAutoResolve(definition, conflict, changes, preview = false) { if (conflict.type === 'type-mismatch') { const workspaceName = conflict.affectedWorkspaces[0]; const workspace = definition.workspaces[workspaceName]; const typeName = workspace.type; if (!definition.types[typeName] && !preview) { definition.types[typeName] = { name: typeName, description: `Auto-generated type for ${typeName}`, framework: 'react' }; } changes.push({ type: 'configuration', target: 'types', property: typeName, oldValue: undefined, newValue: definition.types[typeName], reason: `Created missing type definition` }); } } // Helper methods async loadWorkspaceDefinition(filePath) { if (!(await fs.pathExists(filePath))) { throw new error_handler_1.ValidationError(`Workspace file not found: ${filePath}`); } const content = await fs.readFile(filePath, 'utf8'); return yaml.parse(content); } async saveWorkspaceDefinition(filePath, definition) { const content = yaml.stringify(definition); await fs.writeFile(filePath, content, 'utf8'); } } exports.WorkspaceConflictManager = WorkspaceConflictManager; // Utility functions async function createWorkspaceConflictManager(rootPath) { return new WorkspaceConflictManager(rootPath); } // Quick conflict detection async function detectWorkspaceConflicts(workspaceFile, options) { const manager = new WorkspaceConflictManager(); const definition = await manager['loadWorkspaceDefinition'](workspaceFile); return await manager.detectConflicts(definition, options); } // Auto-resolve conflicts async function autoResolveConflicts(workspaceFile, conflicts) { const manager = new WorkspaceConflictManager(); const definition = await manager['loadWorkspaceDefinition'](workspaceFile); const conflictsToResolve = conflicts || await manager.detectConflicts(definition); const result = await manager.resolveConflicts(definition, conflictsToResolve, true); if (result.resolved.length > 0) { await manager['saveWorkspaceDefinition'](workspaceFile, definition); } return result; }