1import path from 'path';
2import resolveFrom from 'resolve-from';
3
4/**
5 * Return true if the parent folder for a given file path is named "node_modules".
6 *
7 * @example
8 * isModuleRootPathInNodeModulesFolder('./foo/expo') -> false
9 * isModuleRootPathInNodeModulesFolder('./node_modules/expo') -> true
10 */
11function isModuleRootPathInNodeModulesFolder(moduleRootPath: string): boolean {
12  const parentFolderName = path.basename(path.dirname(moduleRootPath));
13  return parentFolderName === 'node_modules';
14}
15
16/**
17 * Given a node module name, and a project path, this method will:
18 *
19 * 1. Resolve the module path.
20 * 2. Find the module root folder.
21 * 3. Return true if the module root folder is in a folder named `node_modules`
22 *
23 * @param projectRoot
24 * @param moduleId
25 *
26 * @example
27 * isModuleSymlinked('./expo/apps/native-component-list', {
28 *   moduleId: 'react-native'
29 * })
30 */
31export function isModuleSymlinked(
32  projectRoot: string,
33  {
34    moduleId,
35    isSilent,
36  }: {
37    moduleId: string;
38    isSilent?: boolean;
39  }
40): boolean {
41  try {
42    const moduleRootPath = path.dirname(resolveFrom(projectRoot, `${moduleId}/package.json`));
43    return !isModuleRootPathInNodeModulesFolder(moduleRootPath);
44  } catch (error) {
45    if (!isSilent) {
46      throw error;
47    }
48    // Failed to resolve the package.json relative to the project, not sure what to do here.
49    // This is probably not possible due to node module resolution.
50    return false;
51  }
52}
53