1import resolveFrom from 'resolve-from';
2
3// These resolvers enable us to test the CLI in older projects.
4// We may be able to get rid of this in the future.
5// TODO: Maybe combine with AsyncResolver?
6class MetroImportError extends Error {
7  constructor(projectRoot: string, moduleId: string) {
8    super(
9      `Missing package "${moduleId}" in the project at: ${projectRoot}\n` +
10        'This usually means "react-native" is not installed. ' +
11        'Please verify that dependencies in package.json include "react-native" ' +
12        'and run `yarn` or `npm install`.'
13    );
14  }
15}
16
17function resolveFromProject(projectRoot: string, moduleId: string) {
18  const resolvedPath = resolveFrom.silent(projectRoot, moduleId);
19  if (!resolvedPath) {
20    throw new MetroImportError(projectRoot, moduleId);
21  }
22  return resolvedPath;
23}
24
25function importFromProject(projectRoot: string, moduleId: string) {
26  return require(resolveFromProject(projectRoot, moduleId));
27}
28
29/** Import `metro` from the project. */
30export function importMetroFromProject(projectRoot: string): typeof import('metro') {
31  return importFromProject(projectRoot, 'metro');
32}
33export function importMetroCreateWebsocketServerFromProject(
34  projectRoot: string
35): typeof import('metro/src/lib/createWebsocketServer').createWebsocketServer {
36  return importFromProject(projectRoot, 'metro/src/lib/createWebsocketServer');
37}
38export function importMetroHmrServerFromProject(
39  projectRoot: string
40): typeof import('metro/src/HmrServer').MetroHmrServer {
41  return importFromProject(projectRoot, 'metro/src/HmrServer');
42}
43
44/** Import `@expo/metro-config` from the project. */
45export function importExpoMetroConfigFromProject(
46  projectRoot: string
47): typeof import('@expo/metro-config') {
48  return importFromProject(projectRoot, '@expo/metro-config');
49}
50
51/** Import `metro-resolver` from the project. */
52export function importMetroResolverFromProject(
53  projectRoot: string
54): typeof import('metro-resolver') {
55  return importFromProject(projectRoot, 'metro-resolver');
56}
57
58/** Import `metro-inspector-proxy` from the project. */
59export function importMetroInspectorProxyFromProject(
60  projectRoot: string
61): typeof import('metro-inspector-proxy') {
62  return importFromProject(projectRoot, 'metro-inspector-proxy');
63}
64
65/** Import `metro-inspector-proxy/src/Device` from the project. */
66export function importMetroInspectorDeviceFromProject(
67  projectRoot: string
68): typeof import('metro-inspector-proxy/src/Device') {
69  return importFromProject(projectRoot, 'metro-inspector-proxy/src/Device');
70}
71
72/**
73 * Import the internal `saveAssets()` function from `react-native` for the purpose
74 * of saving production assets as-is instead of converting them to a hash.
75 */
76export function importCliSaveAssetsFromProject(
77  projectRoot: string
78): typeof import('@react-native-community/cli-plugin-metro/build/commands/bundle/saveAssets').default {
79  return importFromProject(
80    projectRoot,
81    '@react-native-community/cli-plugin-metro/build/commands/bundle/saveAssets'
82  ).default;
83}
84
85export function importCliBuildBundleWithConfigFromProject(
86  projectRoot: string
87): typeof import('@react-native-community/cli-plugin-metro/build/commands/bundle/buildBundle').buildBundleWithConfig {
88  return importFromProject(
89    projectRoot,
90    '@react-native-community/cli-plugin-metro/build/commands/bundle/buildBundle'
91  ).buildBundleWithConfig;
92}
93
94/** Resolve the installed Metro version from project */
95export function resolveMetroVersionFromProject(projectRoot: string): string {
96  return importFromProject(projectRoot, 'metro/package.json').version;
97}
98