#!/usr/bin/env bun /** * Fixes `../.js` imports that now point to folders instead of files * (since migrate-feature-layout moved X.ts → X/X.ts). Rewrites to the * appropriate file path. */ import { existsSync, readdirSync, readFileSync, statSync, writeFileSync, } from "node:fs"; import { dirname, join, relative } from "node:path"; const PACKAGES_DIR = "packages"; function walkTs(dir: string): string[] { const out: string[] = []; for (const name of readdirSync(dir)) { const full = join(dir, name); const s = statSync(full); if (s.isDirectory()) out.push(...walkTs(full)); else if ( (name.endsWith(".ts") || name.endsWith(".tsx")) && !name.endsWith(".d.ts") ) { out.push(full); } } return out; } function listPackages(): string[] { const out: string[] = []; for (const cat of readdirSync(PACKAGES_DIR)) { const catDir = join(PACKAGES_DIR, cat); if (!statSync(catDir).isDirectory()) continue; for (const plugin of readdirSync(catDir)) { const pluginDir = join(catDir, plugin); if (!statSync(pluginDir).isDirectory()) continue; for (const pkg of readdirSync(pluginDir)) { const pkgDir = join(pluginDir, pkg); if (!statSync(pkgDir).isDirectory()) continue; if (existsSync(join(pkgDir, "src"))) out.push(pkgDir); } } } return out; } function fileExists(p: string): boolean { try { statSync(p); return true; } catch { return false; } } function isDirectory(p: string): boolean { try { return statSync(p).isDirectory(); } catch { return false; } } function fixFile(file: string, srcDir: string): number { const src = readFileSync(file, "utf8"); let updated = src; let fixes = 0; const re = /"((?:\.\.?\/)+[^"]+\.js)"/g; const seen = new Set(); for (const m of src.matchAll(re)) seen.add(m[1] as string); for (const importPath of seen) { const fileDir = dirname(file); const stripped = importPath.replace(/\.js$/, ""); const resolved = join(fileDir, stripped); // Skip if outside src/ const relFromSrc = relative(srcDir, resolved); if (relFromSrc.startsWith("..")) continue; // If the .ts file exists at this exact path, resolution is fine. if (fileExists(`${resolved}.ts`) || fileExists(`${resolved}.tsx`)) continue; // If a directory exists at this path, the import is broken — must rewrite. if (!isDirectory(resolved)) continue; const baseName = relative(srcDir, resolved).split("/").pop() as string; const innerImpl = join(resolved, `${baseName}.ts`); const innerIndex = join(resolved, "index.ts"); // Prefer the impl file (matches migrate-feature-layout's sibling pattern). let newImportPath: string | null = null; if (fileExists(innerImpl)) { newImportPath = importPath.replace(/\.js$/, `/${baseName}.js`); } else if (fileExists(innerIndex)) { newImportPath = importPath.replace(/\.js$/, "/index.js"); } if (newImportPath === null) continue; updated = updated.split(`"${importPath}"`).join(`"${newImportPath}"`); fixes++; } if (updated !== src) writeFileSync(file, updated); return fixes; } function main(): void { let totalFiles = 0; let totalFixes = 0; for (const pkg of listPackages()) { const srcDir = join(pkg, "src"); const files = walkTs(srcDir); for (const f of files) { const fixes = fixFile(f, srcDir); if (fixes > 0) { totalFiles++; totalFixes += fixes; console.log(`FIX ${f} (${fixes})`); } } } console.log(`\nFixed ${totalFixes} imports across ${totalFiles} files.`); } main();