diff --git a/apps/coder/src/services/edit-guards-imports.ts b/apps/coder/src/services/edit-guards-imports.ts new file mode 100644 index 0000000..e10e4df --- /dev/null +++ b/apps/coder/src/services/edit-guards-imports.ts @@ -0,0 +1,47 @@ +// edit-guards-imports — detects dropped imports in edited files. +// Ported from opencode-morph-fast-apply (MIT). + +export interface ImportCheckResult { + ok: boolean; + missingImports: string[]; + reason?: string; +} + +const IMPORT_PATTERNS = [ + /^import\s+(?:\{[^}]*\}|\*\s+as\s+\w+|\w+)\s+from\s+['"][^'"]+['"]\s*;?$/m, + /^import\s+['"][^'"]+['"]\s*;?$/m, + /^export\s+.*\s+from\s+['"][^'"]+['"]\s*;?$/m, + /^require\s*\(\s*['"][^'"]+['"]\s*\)\s*;?$/m, + /^import\s+type\s+\{[^}]*\}\s+from\s+['"][^'"]+['"]\s*;?$/m, +]; + +function extractImportLines(content: string): string[] { + return content.split('\n').filter((line) => + IMPORT_PATTERNS.some((p) => p.test(line.trim())), + ); +} + +export function checkDroppedImports( + original: string, + updated: string, + filePath: string, +): ImportCheckResult { + const originalImports = extractImportLines(original); + const updatedImports = extractImportLines(updated); + + if (originalImports.length === 0) { + return { ok: true, missingImports: [] }; + } + + const missing = originalImports.filter((imp) => !updatedImports.includes(imp)); + + if (missing.length > 0 && originalImports.length > 0) { + return { + ok: false, + missingImports: missing, + reason: `Edit would drop ${missing.length} import(s) from ${filePath}`, + }; + } + + return { ok: true, missingImports: [] }; +}