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 importMetroInspectorProxyFromProject(
34  projectRoot: string
35): typeof import('metro-inspector-proxy') {
36  return importFromProject(projectRoot, 'metro-inspector-proxy');
37}
38export function importMetroCreateWebsocketServerFromProject(
39  projectRoot: string
40): typeof import('metro/src/lib/createWebsocketServer').createWebsocketServer {
41  return importFromProject(projectRoot, 'metro/src/lib/createWebsocketServer');
42}
43export function importMetroHmrServerFromProject(
44  projectRoot: string
45): typeof import('metro/src/HmrServer').MetroHmrServer {
46  return importFromProject(projectRoot, 'metro/src/HmrServer');
47}
48
49/** Import `@expo/metro-config` from the project. */
50export function importExpoMetroConfigFromProject(
51  projectRoot: string
52): typeof import('@expo/metro-config') {
53  return importFromProject(projectRoot, '@expo/metro-config');
54}
55
56/** Import `metro-resolver` from the project. */
57export function importMetroResolverFromProject(
58  projectRoot: string
59): typeof import('metro-resolver') {
60  return importFromProject(projectRoot, 'metro-resolver');
61}
62
63/**
64 * Import the internal `saveAssets()` function from `react-native` for the purpose
65 * of saving production assets as-is instead of converting them to a hash.
66 */
67export function importCliSaveAssetsFromProject(
68  projectRoot: string
69): typeof import('@react-native-community/cli-plugin-metro/build/commands/bundle/saveAssets').default {
70  return importFromProject(
71    projectRoot,
72    '@react-native-community/cli-plugin-metro/build/commands/bundle/saveAssets'
73  ).default;
74}
75
76/** Resolve the installed Metro version from project */
77export function resolveMetroVersionFromProject(projectRoot: string): string {
78  return importFromProject(projectRoot, 'metro/package.json').version;
79}
80