metro icon indicating copy to clipboard operation
metro copied to clipboard

Cannot find documentation for metro.config.js > resolver > resolveRequest

Open Arlen22 opened this issue 5 years ago • 11 comments

I cannot find any docs for the resolveRequest function listed at https://facebook.github.io/metro/docs/configuration#resolverequest

I implemented the function and inspected it with console.log(arguments), but when I return a string, it throws a type error.

resolveRequest(context, module, platform){
      let res = require.resolve(path.resolve(context.originModulePath, module));
      console.log(res);
      return res;
}

Arlen22 avatar Apr 07 '20 16:04 Arlen22

return {
  filePath: <path_to_source>,
  type: 'sourceFile',
};

benjaminhr avatar Jul 23 '20 15:07 benjaminhr

Docs have been updated with an example.

benjaminhr avatar Aug 12 '20 12:08 benjaminhr

I get the following @benjaminhr

metro.config.js (part of it)

resolveRequest: (context, realModuleName, platform, moduleName) => {
      return {
        filePath: resolve(context.originModulePath, module),
        type: 'sourceFile'
      }
    }

output

Error: Unable to resolve module `./index` from ``: ./index could not be found within the project.

johntimothybailey avatar Sep 09 '20 03:09 johntimothybailey

There isn't any useful docs for how to use the resolveRequest prop for module aliasing, so I played around with it and this is what I came up with:

    resolveRequest: (context, realModuleName, platform, moduleName) => {
      let module = realModuleName;
        if(realModuleName.includes('bla') || moduleName.includes('bla')) {
          module = module.replace('bla', 'blamos');
        }
        const { resolveRequest: removed, ...restContext } = context;
        return require("metro-resolver").resolve(restContext, module, platform);
    },

Niryo avatar Mar 14 '21 15:03 Niryo

I get the following @benjaminhr

metro.config.js (part of it)

resolveRequest: (context, realModuleName, platform, moduleName) => {
      return {
        filePath: resolve(context.originModulePath, module),
        type: 'sourceFile'
      }
    }

output

Error: Unable to resolve module `./index` from ``: ./index could not be found within the project.

I have this exact same problem. I just wonder where is the original implementation of resolveRequest() call that handles this kind of situation? All I want right now I to ignore the path module.

VictorQueiroz avatar Mar 03 '22 07:03 VictorQueiroz

Note that this API changed in 0.68

@VictorQueiroz - your use case sounds similar to the one mentioned in the docs - the crucial point is to chain to the default resolver via context.resolveRequest for anything you’re not handling yourself.


resolveRequest: (context, moduleName, platform) => {
  if (moduleName.startsWith('my-custom-resolver:')) {
    // Resolve file path logic...
    // NOTE: Throw an error if there is no resolution.
    return {
      filePath: "path/to/file",
      type: 'sourceFile',
    };
  }
  // Optionally, chain to the standard Metro resolver.
  return context.resolveRequest(context, moduleName, platform);
}

robhogan avatar Mar 03 '22 08:03 robhogan

Note that this API changed in 0.68

@VictorQueiroz - your use case sounds similar to the one mentioned in the docs - the crucial point is to chain to the default resolver via context.resolveRequest for anything you’re not handling yourself.

resolveRequest: (context, moduleName, platform) => {
  if (moduleName.startsWith('my-custom-resolver:')) {
    // Resolve file path logic...
    // NOTE: Throw an error if there is no resolution.
    return {
      filePath: "path/to/file",
      type: 'sourceFile',
    };
  }
  // Optionally, chain to the standard Metro resolver.
  return context.resolveRequest(context, moduleName, platform);
}

I saw this on the docs, but when I try to replicate it I get this problem I described. I'm wondering how can I fix this since it's a huge blocker for me.

VictorQueiroz avatar Mar 03 '22 19:03 VictorQueiroz

Note that this API changed in 0.68

@VictorQueiroz - your use case sounds similar to the one mentioned in the docs - the crucial point is to chain to the default resolver via context.resolveRequest for anything you’re not handling yourself.

resolveRequest: (context, moduleName, platform) => {
  if (moduleName.startsWith('my-custom-resolver:')) {
    // Resolve file path logic...
    // NOTE: Throw an error if there is no resolution.
    return {
      filePath: "path/to/file",
      type: 'sourceFile',
    };
  }
  // Optionally, chain to the standard Metro resolver.
  return context.resolveRequest(context, moduleName, platform);
}

The following configuration did it, but still, resolveRequest can be useful so it'd be nice to have it fixed.

module.exports = {
  resolver: {
    extraNodeModules:{
      path: path.resolve(__dirname,'null-module.js'),
      fs: path.resolve(__dirname,'null-module.js')
    }
  }
  //...
};

Thank you.

VictorQueiroz avatar Mar 03 '22 21:03 VictorQueiroz

Hi @VictorQueiroz - glad you found a solution for your use case. I'm not exactly sure what problem you're seeing - are you saying context.resolveRequest isn't defined within your custom resolveRequest? Would you be able to post a snippet of what you tried?

robhogan avatar Mar 04 '22 12:03 robhogan

@rh389 If you create a new React Native project using the TypeScript template and set the resolver.resolveRequest property on metro.config.js to the following:

function resolveRequest(context, moduleName, platform) {
  if (moduleName.startsWith('my-custom-resolver:')) {
    // Resolve file path logic...
    // NOTE: Throw an error if there is no resolution.
    return {
      filePath: "path/to/file",
      type: 'sourceFile',
    };
  }
  // Optionally, chain to the standard Metro resolver.
  return context.resolveRequest(context, moduleName, platform);
}

Should be enough to reproduce this problem I'm relating. So what it seems like is, whatever is the default implementation of resolver.resolveRequest (metro.config.js), it isn't available in require('metro-resolver').resolve nor context.resolveRequest. I even tried the followinng, but still get the same:

return require('metro-resolver').resolve({
  ...context,
  resolveRequest:undefined
},moduleName,platform);

Maybe I'm doing something wrong on my side. I hope this helps clarify, though.

VictorQueiroz avatar Mar 05 '22 17:03 VictorQueiroz

Ah I see - if you init a new React Native app today you'll get [email protected] (latest stable) which includes [email protected], so the metro docs prior to 0.68 apply. Unfortunately, we don't yet have a versioned docs website for metro, but you can access the raw docs at that point from github - https://github.com/facebook/metro/tree/v0.66.2/docs.

Prior to metro 0.68, context.resolveRequest points directly back to your own custom resolver, rather than the default resolver - so calling it from your own resolver creates a recursive loop which eventually throws, gets caught internally and manifests as a failed resolution. To access the default resolver you do need to use metro-resolver and ensure that you unset resolveRequest when passing the context, so the last thing you try is almost correct. The critical difference is that, in <0.68, the resolver takes a fourth argument, "realModuleName".

So in summary, these are equivalent:

For Metro >=0.68

// metro.config.js

module.exports = {
  resolver: {
    resolveRequest: (context, moduleName, platform) => {
      // For example: redirecting `require('path')` to your own mock
      if (moduleName === 'path') {
        return {
          filePath: __dirname + '/my-mock-path.js',
          type: 'sourceFile',
        };
      }
      
      // Forward everything else to the default resolver
      return context.resolveRequest(
        context,
        moduleName,
        platform,
      );
    },
  },
};

For Metro < 0.68

// metro.config.js

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

module.exports = {
  resolver: {
    resolveRequest: (context, moduleName, platform, realModuleName) => {
      // For example: redirecting `require('path')` to your own mock
      if (moduleName === 'path') {
        return {
          filePath: __dirname + '/my-mock-path.js',
          type: 'sourceFile',
        };
      }

      // Forward everything else to the default resolver
      return defaultResolver(
        {
          ...context,
          resolveRequest: null,
        },
        moduleName,
        platform,
        realModuleName,
      );
    },
  },
};

Hope that helps, and sorry for the docs confusion.

robhogan avatar Mar 05 '22 19:03 robhogan

Just hit same issue, still using v0.64 and was wondering why it doesn't work when following https://facebook.github.io/metro/docs/configuration#resolverequest

Atleast now it's documented here 😂

davispuh avatar Dec 09 '22 14:12 davispuh