1*d8009c4bSEvan Baconimport { Resolution } from 'metro-resolver'; 2*d8009c4bSEvan Baconimport path from 'path'; 3*d8009c4bSEvan Bacon 4*d8009c4bSEvan Baconimport { matchTsConfigPathAlias } from './matchTsConfigPathAlias'; 5*d8009c4bSEvan Bacon 6*d8009c4bSEvan Bacontype Paths = { [match: string]: string[] }; 7*d8009c4bSEvan Bacon 8*d8009c4bSEvan Baconconst debug = require('debug')('expo:metro:tsconfig-paths') as typeof console.log; 9*d8009c4bSEvan Bacon 10*d8009c4bSEvan Baconconst isAbsolute = process.platform === 'win32' ? path.win32.isAbsolute : path.posix.isAbsolute; 11*d8009c4bSEvan Bacon 12*d8009c4bSEvan Baconexport function resolveWithTsConfigPaths( 13*d8009c4bSEvan Bacon config: { paths: Paths; baseUrl: string }, 14*d8009c4bSEvan Bacon request: { 15*d8009c4bSEvan Bacon /** Import request */ 16*d8009c4bSEvan Bacon moduleName: string; 17*d8009c4bSEvan Bacon /** Originating file path */ 18*d8009c4bSEvan Bacon originModulePath: string; 19*d8009c4bSEvan Bacon }, 20*d8009c4bSEvan Bacon resolve: (moduleName: string) => Resolution | null 21*d8009c4bSEvan Bacon): Resolution | null { 22*d8009c4bSEvan Bacon const aliases = Object.keys(config.paths); 23*d8009c4bSEvan Bacon 24*d8009c4bSEvan Bacon if ( 25*d8009c4bSEvan Bacon // If no aliases are added bail out 26*d8009c4bSEvan Bacon !aliases.length || 27*d8009c4bSEvan Bacon // Library authors cannot utilize this feature in userspace. 28*d8009c4bSEvan Bacon /node_modules/.test(request.originModulePath) || 29*d8009c4bSEvan Bacon // Absolute paths are not supported 30*d8009c4bSEvan Bacon isAbsolute(request.moduleName) || 31*d8009c4bSEvan Bacon // Relative paths are not supported 32*d8009c4bSEvan Bacon /^\.\.?($|[\\/])/.test(request.moduleName) 33*d8009c4bSEvan Bacon ) { 34*d8009c4bSEvan Bacon return null; 35*d8009c4bSEvan Bacon } 36*d8009c4bSEvan Bacon 37*d8009c4bSEvan Bacon const matched = matchTsConfigPathAlias(aliases, request.moduleName); 38*d8009c4bSEvan Bacon if (!matched) { 39*d8009c4bSEvan Bacon return null; 40*d8009c4bSEvan Bacon } 41*d8009c4bSEvan Bacon 42*d8009c4bSEvan Bacon for (const alias of config.paths[matched.text]) { 43*d8009c4bSEvan Bacon const nextModuleName = matched.star ? alias.replace('*', matched.star) : alias; 44*d8009c4bSEvan Bacon 45*d8009c4bSEvan Bacon if (/\.d\.ts$/.test(nextModuleName)) continue; 46*d8009c4bSEvan Bacon 47*d8009c4bSEvan Bacon const possibleResult = path.join(config.baseUrl, nextModuleName); 48*d8009c4bSEvan Bacon 49*d8009c4bSEvan Bacon const result = resolve(possibleResult); 50*d8009c4bSEvan Bacon if (result) { 51*d8009c4bSEvan Bacon debug(`${request.moduleName} -> ${possibleResult}`); 52*d8009c4bSEvan Bacon return result; 53*d8009c4bSEvan Bacon } 54*d8009c4bSEvan Bacon } 55*d8009c4bSEvan Bacon return null; 56*d8009c4bSEvan Bacon} 57