import { existsSync, realpathSync, statSync } from "node:fs"; import { dirname, extname, join, resolve } from "node:path"; import type { StructuredImportDecl } from "@wrnexus/syntax"; export type ImportMode = "legacy" | "compatible" | "explicit"; export interface ImportResolverOptions { appRoot: string; mode?: ImportMode; aliases?: Record; } export interface ResolvedImport { declaration: StructuredImportDecl; resolved?: string; /** `.tsx` imports are React islands, not `.wrn` components. */ kind?: "island"; diagnostic?: { code: string; message: string; severity: "error" | "warning" }; } function candidates(path: string): string[] { return extname(path) ? [path] : [ path, `${path}.wrn`, `${path}.ts`, `${path}.tsx`, `${path}.d.ts`, join(path, "index.wrn"), join(path, "index.ts"), join(path, "index.tsx"), ]; } export function resolveWrnImport( declaration: StructuredImportDecl, importer: string, options: ImportResolverOptions, ): ResolvedImport { const source = declaration.source; const aliases: Record = { "@": "./app", ...(options.aliases ?? {}), }; const alias = Object.keys(aliases) .filter((key) => key.length > 0 && (source === key || source.startsWith(`${key}/`))) .sort((left, right) => right.length - left.length)[0]; if (!source.startsWith(".") && !alias) return { declaration, resolved: source }; const base = alias ? resolve( options.appRoot, aliases[alias]!, source === alias ? "" : source.slice(alias.length + 1), ) : resolve(dirname(importer), source); const found = candidates(base).find((candidate) => { if (!existsSync(candidate)) return false; try { return statSync(candidate).isFile(); } catch { return false; } }); if (found) { const resolved = realpathSync(found); return resolved.endsWith(".tsx") ? { declaration, resolved, kind: "island" as const } : { declaration, resolved }; } const severity = (options.mode ?? "compatible") === "explicit" ? "error" : "warning"; return { declaration, diagnostic: { code: "WRN-IMPORT-NOT-FOUND", message: `Cannot resolve import '${source}' from ${importer}`, severity, }, }; } export function resolveWrnImports( declarations: StructuredImportDecl[], importer: string, options: ImportResolverOptions, ): ResolvedImport[] { return declarations.map((declaration) => resolveWrnImport(declaration, importer, options)); }