Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Unable to resolve module ./node_modules/expo-router/entry in monorepo #748

Closed
hyoretsu opened this issue Jul 7, 2023 · 11 comments
Closed

Comments

@hyoretsu
Copy link

hyoretsu commented Jul 7, 2023

Which package manager are you using? (Yarn is recommended)

pnpm (known issues due to Metro)

Summary

I can't start an app with Expo Router v2, Expo 49 and PNPM. I already tried expo-router/entry and manually specifying the node_modules path to both local and workspace. I also already tried with and without the .npmrc settings.

Error: Unable to resolve module ./node_modules/expo-router/entry from /home/hyoretsu/@Projects/numerical-analysis/mobile/.: 

None of these files exist:
  * node_modules/expo-router/entry(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json|.android.cjs|.native.cjs|.cjs|.android.svg|.native.svg|.svg)
  * node_modules/expo-router/entry/index(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json|.android.cjs|.native.cjs|.cjs|.android.svg|.native.svg|.svg)
    at ModuleResolver.resolveDependency (/home/hyoretsu/@Projects/numerical-analysis/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:114:15)
    at DependencyGraph.resolveDependency (/home/hyoretsu/@Projects/numerical-analysis/node_modules/metro/src/node-haste/DependencyGraph.js:277:43)
    at /home/hyoretsu/@Projects/numerical-analysis/node_modules/metro/src/lib/transformHelpers.js:169:21
    at Server._resolveRelativePath (/home/hyoretsu/@Projects/numerical-analysis/node_modules/metro/src/Server.js:1045:12)
    at async Server.requestProcessor [as _processBundleRequest] (/home/hyoretsu/@Projects/numerical-analysis/node_modules/metro/src/Server.js:449:37)
    at async Server._processRequest (/home/hyoretsu/@Projects/numerical-analysis/node_modules/metro/src/Server.js:383:7)

Minimal reproducible example

numerical-analysis.zip

@hyoretsu
Copy link
Author

hyoretsu commented Jul 7, 2023

This also happens with yarn, even though it clearly exists
image

@hyoretsu hyoretsu changed the title Unable to resolve module Unable to resolve module ./node_modules/expo-router/entry in monorepo Jul 7, 2023
@hyoretsu
Copy link
Author

hyoretsu commented Jul 7, 2023

Tracked it down, it's an issue with monorepos, at least PNPM's. I can run it just fine in an isolated project.

@EvanBacon
Copy link
Contributor

Create an index.js file and add:

import "expo-router/entry"

Then remove the main field in the project package.json

@hyoretsu
Copy link
Author

hyoretsu commented Jul 7, 2023

Thank you. That should be noted in the docs somewhere though, RN monorepo's already hard as it is.

@nathan-charles
Copy link

Create an index.js file and add:

import "expo-router/entry"

Then remove the main field in the project package.json

i'm still getting the below error only when building in release mode even after verifying the above.

❌ Metro encountered an error:
The resource .../react-native-app/index.js was not found.

@marklawlor
Copy link
Contributor

@nathan-charles Its hard for us to assist you with a filepath issue when we cannot see your repo. Can you please create a new issue with a reproduction repo so we can assit you.

@nathan-charles
Copy link

@marklawlor Thanks for the reply I was finally was able to resolve my issue first I had to remove the EXPO_USE_METRO_WORKSPACE_ROOT=1 env variable as instructed in mono repo guide I'm not sure what this is for or if it's even necessary but it does not work with expo-router when building a release app also another issue was due to react-native-reanimated dependency I was running 3.3.0 as recommend by expo doctor but after upgrading to 3.5.2 I was finally able to create release build.

See the release notes here:
https://github.com/software-mansion/react-native-reanimated/releases/tag/3.5.2

@Chibuife
Copy link

tried the

Create an index.js file and add:

import "expo-router/entry"

Then remove the main field in the project package.json

I tried the same thing but got this error instead.

react-dom.development.js:18687 The above error occurred in the <withDevTools(Anonymous)> component:

at withDevTools(Anonymous)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.

@waymond91
Copy link

waymond91 commented Nov 24, 2023

I just created a fresh app:

npx create-expo-app VoiceTestApp2

Installed the modules with yarn & tested again with npm.

Doing npx expo run:anroid or npx expo start gives the following:

› Opening exp+voicetestapp2://expo-development-client/?url=http%3A%2F%2F192.168.0.101%3A8081 on SM_A528B

› Logs for your project will appear below. Press Ctrl+C to exit.
Error: Unable to resolve module ./.expo/.virtual-metro-entry from /Users/brett/.:

None of these files exist:
  * ../.expo/.virtual-metro-entry(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json)
  * ../.expo/.virtual-metro-entry/index(.native|.android.ts|.native.ts|.ts|.android.tsx|.native.tsx|.tsx|.android.js|.native.js|.js|.android.jsx|.native.jsx|.jsx|.android.json|.native.json|.json)
    at ModuleResolver.resolveDependency (/Users/brett/VoiceTestApp2/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:114:15)
    at DependencyGraph.resolveDependency (/Users/brett/VoiceTestApp2/node_modules/metro/src/node-haste/DependencyGraph.js:277:43)
    at /Users/brett/VoiceTestApp2/node_modules/metro/src/lib/transformHelpers.js:169:21
    at Server._resolveRelativePath (/Users/brett/VoiceTestApp2/node_modules/metro/src/Server.js:1045:12)
    at processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async Server.requestProcessor [as _processBundleRequest] (/Users/brett/VoiceTestApp2/node_modules/metro/src/Server.js:449:37)
    at async Server._processRequest (/Users/brett/VoiceTestApp2/node_modules/metro/src/Server.js:383:7)
› Stopped server

package.json:

➜  VoiceTestApp2 git:(main) ✗ cat package.json
{
  "name": "voicetestapp2",
  "version": "1.0.0",
  "main": "node_modules/expo/AppEntry.js",
  "scripts": {
    "start": "expo start",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "web": "expo start --web"
  },
  "dependencies": {
    "expo": "~49.0.15",
    "expo-modules-core": "~1.5.12",
    "expo-splash-screen": "~0.20.5",
    "expo-status-bar": "~1.6.0",
    "react": "18.2.0",
    "react-native": "0.72.6"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0"
  },
  "private": true
}

app.json:

{
  "expo": {
    "name": "VoiceTestApp2",
    "slug": "VoiceTestApp2",
    "version": "1.0.0",
    "orientation": "portrait",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "light",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#ffffff"
    },
    "assetBundlePatterns": [
      "**/*"
    ],
    "ios": {
      "supportsTablet": true
    },
    "android": {
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#ffffff"
      },
      "package": "com.brettmsmith.VoiceTestApp2"
    },
    "web": {
      "favicon": "./assets/favicon.png"
    }
  }
}
➜  VoiceTestApp2 git:(main) ✗ npx expo start -c
Starting project at /Users/brett/VoiceTestApp2
Error: Cannot find module 'expo/metro-config'
Require stack:
- /Users/brett/metro.config.js
- /Users/brett/VoiceTestApp2/node_modules/import-fresh/index.js
- /Users/brett/VoiceTestApp2/node_modules/cosmiconfig/dist/loaders.js
- /Users/brett/VoiceTestApp2/node_modules/cosmiconfig/dist/createExplorer.js
- /Users/brett/VoiceTestApp2/node_modules/cosmiconfig/dist/index.js
- /Users/brett/VoiceTestApp2/node_modules/metro-config/src/loadConfig.js
- /Users/brett/VoiceTestApp2/node_modules/metro-config/src/index.js
- /Users/brett/VoiceTestApp2/node_modules/@expo/metro-config/build/traveling/metro-config.js
- /Users/brett/VoiceTestApp2/node_modules/@expo/metro-config/build/ExpoMetroConfig.js
- /Users/brett/VoiceTestApp2/node_modules/@expo/cli/build/src/start/server/metro/resolveFromProject.js
- /Users/brett/VoiceTestApp2/node_modules/@expo/cli/build/src/utils/analytics/getMetroProperties.js
- /Users/brett/VoiceTestApp2/node_modules/@expo/cli/build/src/start/server/metro/instantiateMetro.js
- /Users/brett/VoiceTestApp2/node_modules/@expo/cli/build/src/start/server/metro/MetroBundlerDevServer.js
- /Users/brett/VoiceTestApp2/node_modules/@expo/cli/build/src/start/server/DevServerManager.js
- /Users/brett/VoiceTestApp2/node_modules/@expo/cli/build/src/start/startAsync.js
- /Users/brett/VoiceTestApp2/node_modules/@expo/cli/build/src/start/index.js
- /Users/brett/VoiceTestApp2/node_modules/@expo/cli/build/bin/cli
- /Users/brett/VoiceTestApp2/node_modules/expo/bin/cli

@lbxa
Copy link

lbxa commented Oct 13, 2024

Okay I've been wrestling with getting the full pnpm experience with React Native (using expo router) without making any compromises like node-linker=hoisted. The DX you get from pnpm in the long-term justifies losing 2 days on this.

Creating a new entry point file does work for me as shown in this #748 (comment)

import "expo-router/entry";

But even after updating package.json and adding EXPO_USE_METRO_WORKSPACE_ROOT=1 that wasn't enough. You need to get into the dirty details of configuring Metro to work with symlinks to make pnpm work. I could only get my expo router app back up and running after going all in on Metro. Big shout out to this guide.

I'm guessing most folks who want to place their React Native projects inside a pnpm monorepo are also sharing code from tailwind packages etc. So here's my finished article. With this config pnpm monorepo's work like a charm and Metro can be easily extended.

{
  ...
  "expo": "^51.0.17",
  "expo-router": "~3.5.23",
  "react-native": "^0.74.5",
  ...
}
// Learn more: https://docs.expo.dev/guides/monorepos/
const path = require("path");
const { getDefaultConfig } = require("expo/metro-config");
const { FileStore } = require("metro-cache");
const { withNativeWind } = require("nativewind/metro");
const { makeMetroConfig } = require("@rnx-kit/metro-config");
const { mergeConfig } = require("metro-config");
const MetroSymlinksResolver = require("@rnx-kit/metro-resolver-symlinks");

const symlinksResolver = MetroSymlinksResolver();

const projectDir = __dirname;
const monorepoRoot = path.resolve(projectDir, "../..");
const defaultConfig = getDefaultConfig(projectDir);

/** @type {import('expo/metro-config').MetroConfig} */
const monorepoConfig = {
  resolver: {
    disableHierarchicalLookup: true,
    nodeModulesPaths: [
      path.resolve(projectDir, "node_modules"),
      path.resolve(monorepoRoot, "node_modules"),
    ],
    /**
     * React Native has very frail symlink support for modern monorepo tools  
     * that rely on symlinks and global caches to dramatically increase the
     * performance of installs e.g. pnpm. The best way around this is using
     * Microsoft's rnx-kit. I've written more extensively about this in the
     * README.
     * 
     * @see https://gist.github.com/Zn4rK/ed60c380e7b672e3089074f51792a2b8
     */
    resolveRequest: (context, moduleName, platform) => {
      try {
        // Symlinks resolver throws when it can't find what we're looking for.
        const res = symlinksResolver(context, moduleName, platform);

        if (res) {
          return res;
        }
      } catch {
        /**
         * If we have an error, we pass it on to the next resolver in the chain,
         * which should be one of expos.
         * @see https://github.com/expo/expo/blob/9c025ce7c10b23546ca889f3905f4a46d65608a4/packages/%40expo/cli/src/start/server/metro/withMetroResolvers.ts#L47
         */
        return context.resolveRequest(context, moduleName, platform);
      }
    },
  },
  /**
   * Add the monorepo paths to the Metro config.
   * This allows Metro to resolve modules from the monorepo.
   *
   * @see https://docs.expo.dev/guides/monorepos/#modify-the-metro-config
   */
  watchFolders: [monorepoRoot],
  /**
   * Move the Metro cache to the `node_modules/.cache/metro` folder.
   * This repository configured Turborepo to use this cache location as well.
   * If you have any environment variables, you can configure Turborepo to invalidate it when needed.
   * @see https://turbo.build/repo/docs/reference/configuration#env
   */
  cacheStores: [
    new FileStore({
      root: path.join(projectDir, "node_modules", ".cache", "metro"),
    })
  ]
};

/** @type {import('expo/metro-config').MetroConfig} */
const svgConfig = {
  resolver: {
    assetExts: defaultConfig.resolver.assetExts.filter((ext) => ext !== "svg"),
    sourceExts: [...defaultConfig.resolver.sourceExts, "svg"],
  },
  transformer: {
    // <3 -> https://github.com/kristerkari/react-native-svg-transformer/issues/141
    assetPlugins: ['expo-asset/tools/hashAssetFiles'],
    babelTransformerPath: require.resolve('react-native-svg-transformer/expo'),
  },
};

/**
 * Merging configs do not deeply merge arrays/functions. Keep this in mind to not
 * override important properties. Order matters!
 * 
 * @see https://metrobundler.dev/docs/configuration/#merging-configurations
 */
const finalConfig = makeMetroConfig(mergeConfig(defaultConfig, monorepoConfig, svgConfig));

/**
 * Nativewind config must come last! Internally it uses withCssInterop to 
 * resolve css imports. If this is overridden, Nativewind will not work.
 * 
 * @see https://github.com/nativewind/nativewind/issues/972#issuecomment-2329660147
 *
 * Striking a balance between Nativewind and rnx-kit was tricky
 * 
 * @see https://github.com/nativewind/nativewind/issues/926
 */
module.exports = withNativeWind(finalConfig, {
  input: path.join(projectDir, "./src/global.css"),
  configPath: path.join(projectDir, "./tailwind.config.ts")
});

@boh001
Copy link

boh001 commented Oct 30, 2024

@Chibuife if you already use expo-router, then just use EXPO_USE_METRO_WORKSPACE_ROOT

$ EXPO_USE_METRO_WORKSPACE_ROOT=1 npx expo start

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants