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