UNPKG

deprecopilot

Version:

Automated dependency management with AI-powered codemods

166 lines (165 loc) โ€ข 7.63 kB
import { detectPackageManager, getPackageJson, getCurrentVersion, stubDetectBreaking, findSourceFilesUsing } from '../lib/upgradeUtils.js'; import { runCodemod } from '../lib/codemodEngine.js'; import { logger } from '../lib/logger.js'; import { safeInvokePluginHook } from '../plugins/safeInvokePluginHook.js'; import path from 'path'; export async function fix(flags = {}, context = {}) { // Only print debug logs if not in JSON mode and not in test environment if (!flags.json && process.env.NODE_ENV !== 'test') { console.error('FLAGS:', JSON.stringify(flags)); } function log(...args) { if (process.env.NODE_ENV !== 'test' && !flags.json) { console.log(...args); } } const pkg = await getPackageJson(); const manager = detectPackageManager(); const upgrades = []; for (const depType of ['dependencies', 'devDependencies', 'peerDependencies']) { const deps = pkg[depType] || {}; for (const name of Object.keys(deps)) { const current = await getCurrentVersion(name); const to = current ? (parseInt(current.split('.')[0]) + 1) + '.0.0' : null; const safeCurrent = current ?? ''; const safeTo = to ?? ''; if (flags.all || flags.breakingChanges || stubDetectBreaking(name, safeCurrent, safeTo).length) { upgrades.push({ name, from: safeCurrent, to: safeTo }); } } } const results = []; let filesTouched = 0; let filesSkipped = 0; let previewDiffs = []; await Promise.all((context.plugins ?? []).map(p => safeInvokePluginHook(p, 'beforeFix', context))); for (const up of upgrades) { let files = await findSourceFilesUsing(up.name); files = files.filter(f => !f.includes('.deprecopilot-preview-')); files = files.filter(f => f.endsWith('.js')); if (!flags.json) { console.error('DEBUG: files found for', up.name, ':', files); } let codemodResult = null; if (flags.dryRun) { // In dry-run mode, always show what would be done, even if no files found if (!flags.json) { console.log(`๐Ÿ‘€ Would apply codemod for ${up.name} ${up.from} โ†’ ${up.to} to ${files.length} files`); console.log(`๐Ÿ‘€ Files found for ${up.name}:`, files); } if (files.length > 0) { codemodResult = { applied: false, files, diff: 'stub-diff' }; } results.push({ ...up, files, codemodResult }); continue; } if (!files.length) continue; let codemodPath = undefined; if (pkg.deprecopilot && pkg.deprecopilot.codemods && pkg.deprecopilot.codemods[up.name]) { codemodPath = pkg.deprecopilot.codemods[up.name]; } else { codemodPath = `codemods/${up.name}-major.js`; } codemodPath = path.isAbsolute(codemodPath) ? codemodPath : path.resolve(codemodPath); try { if (flags.previewOnly) { if (!flags.json && process.env.NODE_ENV !== 'test') { console.error('ENTERING PREVIEWONLY BLOCK'); console.error('DEBUG: codemodPath:', codemodPath); console.error('DEBUG: files:', files); console.error('DEBUG: up.name:', up.name); console.error('DEBUG: up.from:', up.from); console.error('DEBUG: up.to:', up.to); } try { const diff = await runCodemod({ codemodPath, files, llmPromptContext: { fromVersion: up.from, toVersion: up.to, packageName: up.name, changelog: '', codeContext: '' }, ai: flags.ai, previewOnly: true, llmProvider: flags.llmProvider, }); if (!flags.json && process.env.NODE_ENV !== 'test') { console.error('AFTER RUNCODEMOD:', typeof diff, diff); } codemodResult = { applied: false, files, diff }; if (flags.previewOnly) { if (!flags.json && process.env.NODE_ENV !== 'test') { console.error('DIFF OUTPUT:', JSON.stringify(codemodResult.diff)); } previewDiffs.push({ name: up.name, diff }); } if (!flags.json) { process.stdout.write(diff + '\n'); } } catch (error) { if (!flags.json && process.env.NODE_ENV !== 'test') { console.error('DEBUG: Exception in runCodemod:', error); if (error instanceof Error) { console.error('DEBUG: Exception stack:', error.stack); } } throw error; } } else { if (!flags.json && process.env.NODE_ENV !== 'test') { console.error('ENTERING ELSE BLOCK (not previewOnly)'); } if (process.env.NODE_ENV === 'test') { await runCodemod({ codemodPath, files, llmPromptContext: { fromVersion: up.from, toVersion: up.to, packageName: up.name, changelog: '', codeContext: '' }, ai: flags.ai, llmProvider: flags.llmProvider, }); codemodResult = { applied: true, files }; } else { await runCodemod({ codemodPath, files, llmPromptContext: { fromVersion: up.from, toVersion: up.to, packageName: up.name, changelog: '', codeContext: '' }, ai: flags.ai, llmProvider: flags.llmProvider, }); codemodResult = { applied: true, files }; } if (!flags.json) logger.info(`โœ” ${up.name} โ†’ ${up.to}: codemod applied to ${files.length} files`); } filesTouched += files.length; } catch (error) { if (!flags.json && process.env.NODE_ENV !== 'test') { console.error('DEBUG: Exception caught in fix command:', error); if (error instanceof Error) { console.error('DEBUG: Exception stack:', error.stack); } } filesSkipped += files.length; if (!flags.json) logger.error(`โ— ${files.length} file(s) skipped for ${up.name}`); } results.push({ ...up, files, codemodResult }); } if (flags.json) { const output = { results, filesTouched, filesSkipped }; if (flags.previewOnly) { output.preview = { diffs: previewDiffs }; } process.stdout.write(JSON.stringify(output) + '\n'); } else { logger.info(`๐Ÿงช Codemods applied to ${filesTouched} files`); if (filesSkipped) logger.error(`โ— ${filesSkipped} file(s) skipped`); logger.info('โœ… All fixes complete'); } await Promise.all((context.plugins ?? []).map(p => safeInvokePluginHook(p, 'afterFix', context))); }