react / react/metro

Metro resolver could not resolve an import when run via xCode or from deeper directory - works without watchman

Open
#1,426 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
JavaScript
Stars
5.6k
Forks
696
Avg merge
8m
Merged PRs (30d)
7

Description

React Native version: 0.76.6
Metro version: 0.81.0
Watchman version: 2024.12.02.00

Hello. I am sorry this is long but I try to be as descriptive as possible. I am trying to upgrade RN 0.72.6 all the way up to the newest version and while I managed to make the project running when i do yarn start and press i (iOS), it just is incostent with xCode way of build. I have the react-native app in monorepo that spans multiple folders (e.g., packages/, col-mobile/, col-web/). node_modules are in the root and in the col-mobile/ folder too.

The first problem

Note: Full metro.config.js file below.

When I run the same command xCode does, in <projectDir>/col-mobile/ios:

node /Users/Martin/Tresors/Projects/<projectDir>/col-mobile/node_modules/react-native/scripts/bundle.js bundle --entry-file index.js --platform ios --dev true --reset-cache --bundle-output /Users/Martin/Library/Developer/Xcode/DerivedData/ColMobile-eefulauxzmjhbdgyseffwktprjcy/Build/Products/Debug-iphoneos/main.jsbundle --assets-dest /Users/Martin/Library/Developer/Xcode/DerivedData/ColMobile-eefulauxzmjhbdgyseffwktprjcy/Build/Products/Debug-iphoneos/ColMobile.app --config-cmd 'node  /Users/Martin/Tresors/Projects/<projectDir>/col-mobile/node_modules/react-native/cli.js config'

I get the following error (note that it says "unable to resolve module packages/" without the dots at the start - ../ which might be NOT wrong but changes when I run it from the col-mobile folder):

/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:148
        throw new UnableToResolveError(
              ^

UnableToResolveError: Unable to resolve module packages/col-tech/common/mobile/src/theme/themeConfigurations from /Users/Martin/Tresors/Projects/<projectDir>/col-mobile/App.tsx: packages/col-tech/common/mobile/src/theme/themeConfigurations could not be found within the project or in these directories:
  node_modules
  ../node_modules
  ../../../../../node_modules
  ...
> 6 | import { loadThemeConfigurations } from "packages/col-tech/common/mobile/src/theme/themeConfigurations";
    |                                          ^
  7 | import { AuthProvider } from "col-mobile/auth/AuthProvider";
  ...
  at ModuleResolver.resolveDependency (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:148:15)
    at DependencyGraph.resolveDependency (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/node-haste/DependencyGraph.js:235:43)
    ... 4 lines matching cause stack trace ...
    at async Promise.all (index 3)
    at async visit (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/DeltaBundler/buildSubgraph.js:92:5)
    at async Promise.all (index 3)
    at async visit (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/DeltaBundler/buildSubgraph.js:92:5) {
  originModulePath: '/Users/Martin/Tresors/Projects/<projectDir>/col-mobile/App.tsx',
  targetModuleName: 'packages/col-tech/common/mobile/src/theme/themeConfigurations',
  cause: FailedToResolveNameError: Module does not exist in the Haste module map or in these directories:
    /Users/Martin/Tresors/Projects/<projectDir>/col-mobile/node_modules
    /Users/Martin/Tresors/Projects/<projectDir>/node_modules
    /Users/Martin/Tresors/Projects/CEZ/node_modules
    /Users/Martin/Tresors/Projects/node_modules
    /Users/Martin/Tresors/node_modules
    /Users/Martin/node_modules
    /Users/node_modules
    /node_modules
  
      at Object.resolve (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro-resolver/src/resolve.js:158:9)
      at resolveRequest (/Users/Martin/Tresors/Projects/<projectDir>/col-mobile/metro.config.js:64:25)
      at Object.resolve (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro-resolver/src/resolve.js:35:12)
      at ModuleResolver.resolveDependency (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:73:31)
      at DependencyGraph.resolveDependency (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/node-haste/DependencyGraph.js:235:43)
      at /Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/lib/transformHelpers.js:160:21
      at resolveDependencies (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/DeltaBundler/buildSubgraph.js:42:25)
      at visit (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/DeltaBundler/buildSubgraph.js:83:30)
      at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
      at async Promise.all (index 3) {
    dirPaths: [
      '/Users/Martin/Tresors/Projects/<projectDir>/col-mobile/node_modules',
      '/Users/Martin/Tresors/Projects/<projectDir>/node_modules',
      '/Users/Martin/Tresors/Projects/CEZ/node_modules',
      '/Users/Martin/Tresors/Projects/node_modules',
      '/Users/Martin/Tresors/node_modules',
      '/Users/Martin/node_modules',
      '/Users/node_modules',
      '/node_modules'
    ],
    extraPaths: []
  }
}

But when I run the command from <projectDir>/col-mobile, it builds properly. Of course, that could be a problem with current working directory, so I tried to change the PWD at top of the metro.config.js (ugly, but worked when running from terminal):

process.chdir(__dirname, "../col-mobile/");

The metro config with chdir

const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config");
const path = require("path");
const process = require("process");
const resolver = require("metro-resolver");
const repositoryRoot = path.resolve(__dirname, "../col-mobile/");

// Fixes resolution when the bundler is run from ios/ directory
process.chdir(__dirname, "../col-mobile/");

/**
 * Metro configuration
 * https://reactnative.dev/docs/metro
 *
 * @type {import('metro-config').MetroConfig}
 */
const config = {
	cacheStores: ({ FileStore }) => [
		new FileStore({
			root: path.join(__dirname, "metro-cache4"),
		}),
	],
	transformer: {
		minifierConfig: {
			mangle: { toplevel: false },
			output: {
				ascii_only: true,
				comments: false,
				quote_style: 3,
				wrap_iife: true,
			},
			sourceMap: { includeSources: false },
			toplevel: false,
			compress: { reduce_funcs: false },
		},
	},
	resolver: {
		useWatchman: true, // does work with watchman, but not in xCode with watchman enabled. 
		projectRoot: repositoryRoot,
		sourceExts: ["js", "jsx", "json", "ts", "tsx"],
		resolverMainFields: ["react-native", "browser", "local", "main"],
		hasteImplModulePath: path.resolve(__dirname, "../col-mobile/haste.js"),
		resolveRequest: (context, moduleName, platform) => {
			const resolveRealPath = (absolutePath) => {
				return absolutePath;
			};

			const resolve = (moduleName, platform) => {
				return context.resolveRequest(context, moduleName, platform);
			};

			if (moduleName.includes("packages/")) {
				const aa = resolver.resolve(context, moduleName, platform);
				console.log("[AAA] " + moduleName + " and resolved is " + aa);
				return aa;
			}

			/// ... more code here which isn't resolved for the themeConfigurations file 
			
			return resolver.resolve({ ...context, resolveRequest: resolver.resolve }, moduleName, platform);
		},
	},
	watchFolders: [
		path.join(__dirname, "../col-web/"),
		path.join(__dirname, "../packages/"),
		path.join(__dirname, "../node_modules"),
		path.join(__dirname, "../mocks/"),
	],
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);

Problem number 2

It works! But only in Terminal. When I run build in xCode, it fails searching for the themeConfigurations.ts file. The weird thing is, the error contains a full path to the file it DID NOT FIND. The file does exist though. I guess watchman didn't pick it up somehow and the lookup is through watchman I suppose. And yes, I did this command like a hundred times:

watchman watch-del '/Users/Martin/Tresors/Projects/<projectDir>/' ; watchman watch-project '/Users/Martin/Tresors/Projects/<projectDir>/'

The error (note the I guess correct "../packages"; also, look at the <-------------- line):

UnableToResolveError: Unable to resolve module ../packages/col-tech/common/mobile/src/theme/themeConfigurations from /Users/Martin/Tresors/Projects/<projectDir>/col-mobile/App.tsx: 


None of these files exist:

  * ../packages/col-tech/common/mobile/src/theme/themeConfigurations(.ios.js|.native.js|.js|.ios.jsx|.native.jsx|.jsx|.ios.json|.native.json|.json|.ios.ts|.native.ts|.ts|.ios.tsx|.native.tsx|.tsx)
  * ../packages/col-tech/common/mobile/src/theme/themeConfigurations

...

import { loadThemeConfigurations } from "packages/col-tech/common/mobile/src/theme/themeConfigurations"

...

    at ModuleResolver.resolveDependency (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:112:15)
    at DependencyGraph.resolveDependency (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/node-haste/DependencyGraph.js:235:43)
    ... 4 lines matching cause stack trace ...
    at async Promise.all (index 3)
    at async visit (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/DeltaBundler/buildSubgraph.js:92:5)
    at async Promise.all (index 3)
    at async visit (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/DeltaBundler/buildSubgraph.js:92:5) {
  originModulePath: '/Users/Martin/Tresors/Projects/<projectDir>/col-mobile/App.tsx',
  targetModuleName: '../packages/col-tech/common/mobile/src/theme/themeConfigurations',
  cause: FailedToResolvePathError: The module could not be resolved because none of these files exist:

    * /Users/Martin/Tresors/Projects/<projectDir>/packages/col-tech/common/mobile/src/theme/themeConfigurations(.ios.js|.native.js|.js|.ios.jsx|.native.jsx|.jsx|.ios.json|.native.json|.json|.ios.ts|.native.ts|.ts|.ios.tsx|.native.tsx|.tsx) <--------------
    * /Users/Martin/Tresors/Projects/<projectDir>/packages/col-tech/common/mobile/src/theme/themeConfigurations
      at Object.resolve (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro-resolver/src/resolve.js:49:13)
      at resolveRequest (/Users/Martin/Tresors/Projects/<projectDir>/col-mobile/metro.config.js:73:25)
      at Object.resolve (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro-resolver/src/resolve.js:35:12)
      at ModuleResolver.resolveDependency (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:73:31)
      at DependencyGraph.resolveDependency (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/node-haste/DependencyGraph.js:235:43)
      at /Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/lib/transformHelpers.js:160:21
     at resolveDependencies (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/DeltaBundler/buildSubgraph.js:42:25)
      at visit (/Users/Martin/Tresors/Projects/<projectDir>/node_modules/metro/src/DeltaBundler/buildSubgraph.js:83:30)
      at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
      at async Promise.all (index 3) {
    candidates: {
      file: {
        type: 'sourceFile',
        filePathPrefix: '../packages/col-tech/common/mobile/src/theme/themeConfigurations',
        candidateExts: [
          '',             '.ios.js',
          '.native.js',   '.js',
          '.ios.jsx',     '.native.jsx',
          '.jsx',         '.ios.json',
          '.native.json', '.json',
          '.ios.ts',      '.native.ts',
          '.ts',          '.ios.tsx',
          '.native.tsx',  '.tsx'
        ]
      },
      dir: {
        type: 'sourceFile',
        filePathPrefix: '../packages/col-tech/common/mobile/src/theme/themeConfigurations',
        candidateExts: []
      }
    }
  }
}

However, when I do

cat /Users/Martin/Tresors/Projects/<projectDir>/packages/col-tech/common/mobile/src/theme/themeConfigurations.ts

I get the contents of the file.

When I set useWatchman: false in the metro config, it manages to build just fine. But I guess I will lose fast refresh and so on.

What is going on? It was working in previous versions of react-native (Metro). I can give more info if needed.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with col-mobile/metro.config.js, especially its custom resolveRequest, projectRoot, watchFolders, and hasteImplModulePath settings; compare the Xcode bundle command with the working terminal invocation. Reproduce the import failure for App.tsx using the listed Metro and Watchman versions. Done means the monorepo import resolves consistently from Xcode and the terminal without relying on process.chdir().

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, react-native
Domain
build-system, tooling
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.