18d307f52SEvan Baconimport resolveFrom from 'resolve-from'; 28d307f52SEvan Bacon 3036e9444SEvan Baconconst debug = require('debug')('expo:metro:import'); 4036e9444SEvan Bacon 58d307f52SEvan Bacon// These resolvers enable us to test the CLI in older projects. 68d307f52SEvan Bacon// We may be able to get rid of this in the future. 78d307f52SEvan Bacon// TODO: Maybe combine with AsyncResolver? 88d307f52SEvan Baconclass MetroImportError extends Error { 98d307f52SEvan Bacon constructor(projectRoot: string, moduleId: string) { 108d307f52SEvan Bacon super( 118d307f52SEvan Bacon `Missing package "${moduleId}" in the project at: ${projectRoot}\n` + 128d307f52SEvan Bacon 'This usually means "react-native" is not installed. ' + 138d307f52SEvan Bacon 'Please verify that dependencies in package.json include "react-native" ' + 148d307f52SEvan Bacon 'and run `yarn` or `npm install`.' 158d307f52SEvan Bacon ); 168d307f52SEvan Bacon } 178d307f52SEvan Bacon} 188d307f52SEvan Bacon 19*edeec536SEvan Baconexport function importCliServerApiFromProject( 20*edeec536SEvan Bacon projectRoot: string 21*edeec536SEvan Bacon): typeof import('@react-native-community/cli-server-api') { 22*edeec536SEvan Bacon return importFromProject(projectRoot, '@react-native-community/cli-server-api'); 23*edeec536SEvan Bacon} 24*edeec536SEvan Bacon 25*edeec536SEvan Baconexport function importMetroSourceMapComposeSourceMapsFromProject( 26*edeec536SEvan Bacon projectRoot: string 27*edeec536SEvan Bacon): typeof import('metro-source-map').composeSourceMaps { 28*edeec536SEvan Bacon return importFromProject(projectRoot, 'metro-source-map/src/composeSourceMaps'); 29*edeec536SEvan Bacon} 30*edeec536SEvan Bacon 31*edeec536SEvan Baconexport function resolveFromProject(projectRoot: string, moduleId: string) { 328d307f52SEvan Bacon const resolvedPath = resolveFrom.silent(projectRoot, moduleId); 338d307f52SEvan Bacon if (!resolvedPath) { 348d307f52SEvan Bacon throw new MetroImportError(projectRoot, moduleId); 358d307f52SEvan Bacon } 368d307f52SEvan Bacon return resolvedPath; 378d307f52SEvan Bacon} 388d307f52SEvan Bacon 398d307f52SEvan Baconfunction importFromProject(projectRoot: string, moduleId: string) { 408d307f52SEvan Bacon return require(resolveFromProject(projectRoot, moduleId)); 418d307f52SEvan Bacon} 428d307f52SEvan Bacon 438d307f52SEvan Bacon/** Import `metro` from the project. */ 448d307f52SEvan Baconexport function importMetroFromProject(projectRoot: string): typeof import('metro') { 458d307f52SEvan Bacon return importFromProject(projectRoot, 'metro'); 468d307f52SEvan Bacon} 47*edeec536SEvan Baconexport function importMetroServerFromProject(projectRoot: string): typeof import('metro').Server { 48*edeec536SEvan Bacon return importFromProject(projectRoot, 'metro/src/Server'); 49*edeec536SEvan Bacon} 5033643b60SEvan Baconexport function importMetroCreateWebsocketServerFromProject( 5133643b60SEvan Bacon projectRoot: string 5233643b60SEvan Bacon): typeof import('metro/src/lib/createWebsocketServer').createWebsocketServer { 5333643b60SEvan Bacon return importFromProject(projectRoot, 'metro/src/lib/createWebsocketServer'); 5433643b60SEvan Bacon} 5533643b60SEvan Baconexport function importMetroHmrServerFromProject( 5633643b60SEvan Bacon projectRoot: string 5733643b60SEvan Bacon): typeof import('metro/src/HmrServer').MetroHmrServer { 5833643b60SEvan Bacon return importFromProject(projectRoot, 'metro/src/HmrServer'); 5933643b60SEvan Bacon} 608d307f52SEvan Bacon 61036e9444SEvan Baconexport function importExpoMetroConfig(projectRoot: string) { 62036e9444SEvan Bacon return importFromProjectOrFallback<typeof import('@expo/metro-config')>( 63036e9444SEvan Bacon projectRoot, 64036e9444SEvan Bacon '@expo/metro-config' 65036e9444SEvan Bacon ); 66036e9444SEvan Bacon} 67036e9444SEvan Bacon 68036e9444SEvan Bacon/** 69036e9444SEvan Bacon * Attempt to use the local version of a module or fallback on the CLI version. 70036e9444SEvan Bacon * This should only ever happen when testing Expo CLI in development. 71036e9444SEvan Bacon */ 72036e9444SEvan Baconexport function importFromProjectOrFallback<TModule>( 73036e9444SEvan Bacon projectRoot: string, 74036e9444SEvan Bacon moduleId: string 75036e9444SEvan Bacon): TModule { 76036e9444SEvan Bacon const resolvedPath = resolveFrom.silent(projectRoot, moduleId); 77036e9444SEvan Bacon if (!resolvedPath) { 78036e9444SEvan Bacon debug(`requiring "${moduleId}" relative to the CLI`); 79036e9444SEvan Bacon return require(require.resolve(moduleId)); 80036e9444SEvan Bacon } 81036e9444SEvan Bacon debug(`requiring "${moduleId}" from the project:`, resolvedPath); 82036e9444SEvan Bacon return require(resolvedPath); 838d307f52SEvan Bacon} 846d6b81f9SEvan Bacon 856d6b81f9SEvan Bacon/** Import `metro-resolver` from the project. */ 866d6b81f9SEvan Baconexport function importMetroResolverFromProject( 876d6b81f9SEvan Bacon projectRoot: string 886d6b81f9SEvan Bacon): typeof import('metro-resolver') { 896d6b81f9SEvan Bacon return importFromProject(projectRoot, 'metro-resolver'); 906d6b81f9SEvan Bacon} 916d6b81f9SEvan Bacon 925234fe38SCedric van Putten/** Import `metro-inspector-proxy` from the project. */ 935234fe38SCedric van Puttenexport function importMetroInspectorProxyFromProject( 945234fe38SCedric van Putten projectRoot: string 955234fe38SCedric van Putten): typeof import('metro-inspector-proxy') { 965234fe38SCedric van Putten return importFromProject(projectRoot, 'metro-inspector-proxy'); 975234fe38SCedric van Putten} 985234fe38SCedric van Putten 995234fe38SCedric van Putten/** Import `metro-inspector-proxy/src/Device` from the project. */ 1005234fe38SCedric van Puttenexport function importMetroInspectorDeviceFromProject( 1015234fe38SCedric van Putten projectRoot: string 1025234fe38SCedric van Putten): typeof import('metro-inspector-proxy/src/Device') { 1035234fe38SCedric van Putten return importFromProject(projectRoot, 'metro-inspector-proxy/src/Device'); 1045234fe38SCedric van Putten} 1055234fe38SCedric van Putten 1066d6b81f9SEvan Bacon/** 1076d6b81f9SEvan Bacon * Import the internal `saveAssets()` function from `react-native` for the purpose 1086d6b81f9SEvan Bacon * of saving production assets as-is instead of converting them to a hash. 1096d6b81f9SEvan Bacon */ 1106d6b81f9SEvan Baconexport function importCliSaveAssetsFromProject( 1116d6b81f9SEvan Bacon projectRoot: string 1126d6b81f9SEvan Bacon): typeof import('@react-native-community/cli-plugin-metro/build/commands/bundle/saveAssets').default { 1136d6b81f9SEvan Bacon return importFromProject( 1146d6b81f9SEvan Bacon projectRoot, 1156d6b81f9SEvan Bacon '@react-native-community/cli-plugin-metro/build/commands/bundle/saveAssets' 1166d6b81f9SEvan Bacon ).default; 1176d6b81f9SEvan Bacon} 118d42dd5d4SCedric van Putten 119d42dd5d4SCedric van Putten/** Resolve the installed Metro version from project */ 120d42dd5d4SCedric van Puttenexport function resolveMetroVersionFromProject(projectRoot: string): string { 121d42dd5d4SCedric van Putten return importFromProject(projectRoot, 'metro/package.json').version; 122d42dd5d4SCedric van Putten} 123