w.brian
w.brian

Reputation: 17407

How can I configure metro to resolve modules outside of my project directory?

For reasons that are out of my control, I need to resolve a module that is outside of my react-native project directory. So, consider the following directory structure:

react-native-project/
├─ App.jsx
├─ babel.config.js
external-directory/
├─ Foo.jsx

I would like any import Foo from 'Foo' inside of react-native-project to resolve ../external-directory/Foo.jsx. My first attempt at this was to use babel-plugin-module-loader with the following configuration:

  plugins: [
    [
      'module-resolver',
      {
        alias: {
          Foo: '/absolute/path/to/external-directory/Foo',
        },
      },
    ],
  ],

This doesn't work, with metro emitting the following error:

error: Error: Unable to resolve module /absolute/path/to/external-directory/Foo from /absolute/path/to/react-native-project/App.jsx: 

None of these files exist:
  * ../external-directory/Foo(.native|.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)
  * ../external-directory/Foo/index(.native|.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)

This error message is wrong: ../external-directory/Foo.jsx does exist. I've verified this numerous times. I've also set up a standalone babel package to test an identical import scenario, and babel correctly resolves the external module.

The other approach I took was to add a custom resolveRequest function in my metro.config.js:

const defaultResolver = require('metro-resolver').resolve;

module.exports = {
  ...
  resolver: {
    resolveRequest: (context, moduleName, platform, realModuleName) => {
      if (moduleName === 'Foo') {
        return {
          filePath: '/absolute/path/to/external-directory/Foo.jsx',
          type: 'sourceFile',
        };
      } else {
        return defaultResolver(
          {
            ...context,
            resolveRequest: null,
          },
          moduleName,
          platform,
          realModuleName,
        );
      }
    },
  },
};

This also doesn't work, emitting the following error message:

error: ReferenceError: SHA-1 for file /absolute/path/to/external-directory/Foo.jsx (/absolute/path/to/external-directory/Foo.jsx) is not computed.
         Potential causes:
           1) You have symlinks in your project - watchman does not follow symlinks.
           2) Check `blockList` in your metro.config.js and make sure it isn't excluding the file path.

The potential causes do not apply in this scenario: There are no symlinks nor does the blockList contain the external directory (I explicitly configured blockList: null to verify).

Is there any way to accomplish what I'm trying to do? Or does metro (either by design or incidentally) prevent this?

Upvotes: 2

Views: 6159

Answers (1)

Volodymyr Savin
Volodymyr Savin

Reputation: 71

You can use a metro bundler build in option - extraNodeModules and watchFolders.

const path = require('path');

module.exports = {
  resolver: {
    ...,
    extraNodeModules: {
      app: path.resolve(__dirname + '/../app')
    }
  },
  ...,
  watchFolders: [
    path.resolve(__dirname + '/../app')
  ]
};

Upvotes: 7

Related Questions