* Resolve a relative import
( importPath: string, fromDir: string, language: Language, context: ResolutionContext )
| 206 | * Resolve a relative import |
| 207 | */ |
| 208 | function resolveRelativeImport( |
| 209 | importPath: string, |
| 210 | fromDir: string, |
| 211 | language: Language, |
| 212 | context: ResolutionContext |
| 213 | ): string | null { |
| 214 | const projectRoot = context.getProjectRoot(); |
| 215 | const extensions = EXTENSION_RESOLUTION[language] || []; |
| 216 | |
| 217 | // Python dotted-relative imports (`from .certs import x`, `from ..pkg.mod |
| 218 | // import y`): leading dots are PACKAGE levels (1 = current package), and the |
| 219 | // remainder is a dotted submodule path. `path.resolve(dir, '.certs')` would |
| 220 | // treat `.certs` as a literal hidden filename, so translate the Python form |
| 221 | // to a real filesystem-relative path before resolving. |
| 222 | if (language === 'python' && importPath.startsWith('.')) { |
| 223 | const dots = importPath.length - importPath.replace(/^\.+/, '').length; |
| 224 | const up = '../'.repeat(Math.max(0, dots - 1)); // 1 dot = current dir |
| 225 | const rest = importPath.slice(dots).replace(/\./g, '/'); // 'sub.mod' -> 'sub/mod' |
| 226 | const pyBase = path.resolve(fromDir, up + rest); |
| 227 | const pyRel = path.relative(projectRoot, pyBase).replace(/\\/g, '/'); |
| 228 | for (const ext of extensions) { |
| 229 | if (context.fileExists(pyRel + ext)) return pyRel + ext; |
| 230 | } |
| 231 | if (pyRel && context.fileExists(pyRel)) return pyRel; |
| 232 | return null; |
| 233 | } |
| 234 | |
| 235 | // Try the path as-is first |
| 236 | const basePath = path.resolve(fromDir, importPath); |
| 237 | const relativePath = path.relative(projectRoot, basePath).replace(/\\/g, '/'); |
| 238 | |
| 239 | // Try each extension |
| 240 | for (const ext of extensions) { |
| 241 | const candidatePath = relativePath + ext; |
| 242 | if (context.fileExists(candidatePath)) { |
| 243 | return candidatePath; |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | // Try without extension (might already have one) |
| 248 | if (context.fileExists(relativePath)) { |
| 249 | return relativePath; |
| 250 | } |
| 251 | |
| 252 | return null; |
| 253 | } |
| 254 | |
| 255 | /** |
| 256 | * Resolve an aliased/absolute import. |
no test coverage detected