| 13 | * found. |
| 14 | */ |
| 15 | export function detectImportFileExtension(fromDir: string): string | undefined { |
| 16 | const configPath = ts.findConfigFile(fromDir, ts.sys.fileExists, 'tsconfig.json'); |
| 17 | if (!configPath) { |
| 18 | return undefined; |
| 19 | } |
| 20 | |
| 21 | const configFile = ts.readConfigFile(configPath, ts.sys.readFile); |
| 22 | if (configFile.error) { |
| 23 | return undefined; |
| 24 | } |
| 25 | |
| 26 | // resolve `extends` chains and module/moduleResolution defaults |
| 27 | const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, path.dirname(configPath)); |
| 28 | |
| 29 | // Prefer an explicit `moduleResolution`; when it's omitted, TypeScript derives |
| 30 | // it from `module`, so fall back to inspecting the module kind ourselves. |
| 31 | let moduleResolution = parsed.options.moduleResolution; |
| 32 | if (moduleResolution === undefined) { |
| 33 | switch (parsed.options.module) { |
| 34 | case ts.ModuleKind.Node16: |
| 35 | case ts.ModuleKind.Node18: |
| 36 | case ts.ModuleKind.Node20: |
| 37 | case ts.ModuleKind.NodeNext: |
| 38 | moduleResolution = ts.ModuleResolutionKind.NodeNext; |
| 39 | break; |
| 40 | default: |
| 41 | break; |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | // node16/nodenext resolution requires explicit extensions on relative imports |
| 46 | if ( |
| 47 | moduleResolution === ts.ModuleResolutionKind.Node16 || |
| 48 | moduleResolution === ts.ModuleResolutionKind.NodeNext |
| 49 | ) { |
| 50 | return '.js'; |
| 51 | } |
| 52 | |
| 53 | return undefined; |
| 54 | } |