GithubHelp home page GithubHelp logo

Package Exports not working about metro HOT 11 CLOSED

Bekaxp avatar Bekaxp commented on April 28, 2024
Package Exports not working

from metro.

Comments (11)

kraenhansen avatar kraenhansen commented on April 28, 2024 3

Well, that wasn't too difficult: #1136.
Please take it for a spin to see if it fixes your issue too 🙂

from metro.

kraenhansen avatar kraenhansen commented on April 28, 2024 1

I think I understand the issue better now. This happens only when the package being imported is in the haste map, because it's in the list of watchFolders.

I've added a failing test to main...kraenhansen:metro:fixing-1128 showing how this is broken and I'll try to see if I can contribute a fix for it too 🙏

from metro.

kraenhansen avatar kraenhansen commented on April 28, 2024 1

leave it open until your PR is merged in?

Please leave it open 🙂 I expect it to close as the PR merge 🙏

from metro.

MadeinFrance avatar MadeinFrance commented on April 28, 2024

Reproducible example

from metro.

Bekaxp avatar Bekaxp commented on April 28, 2024

Hi!

I think i discovered the issue and I have a proposal for a solution but I need your help!

The error is easily reproducible with the examples from the repo I posted above in the issue. The problem I found is in the metro-resolver package.

Whenever you have a module which is using the "new" exports way of exposing its content, like this in package.json:

"exports": {
    "./native": {
      "types": "./lib/native/index.d.ts",
      "import": "./lib/native/index.mjs",
      "require": "./lib/native/index.cjs"
    },
    "./web": {
      "types": "./lib/web/index.d.ts",
      "import": "./lib/web/index.mjs",
      "require": "./lib/web/index.cjs"
    },
    "./package.json": "./package.json"
  },

and then you are consuming this module like this:
import Whatever from '@dp/mypackage/native' OR import Whatever from '@dp/mypackage/web' for web in this case...

the metro-resolver => resolve.js will consider this as a "Haste Module" BUT IT IS NOT!!

My Solution
I created this additional method, which checks if the exports are defined in the package.json of the imported module and if the unstable_enablePackageExports flag is set. If that is the case then this module should not be considered as a "Haste Module" and that part of the code should be skipped...

My function:

function isExportedSource(context, moduleName) {
  if (context.unstable_enablePackageExports) {
    let packageName = moduleName;
    let packageJsonPath = context.resolveHastePackage(packageName);
    while (packageJsonPath == null && packageName && packageName !== ".") {
      packageName = _path.default.dirname(packageName);
      packageJsonPath = context.resolveHastePackage(packageName);
    }
    if (packageJsonPath == null) {
      return failedFor();
    }

    if (packageJsonPath) {
      const pkg = context.getPackageForModule(packageJsonPath);
      const exportsField = pkg?.packageJson.exports;
      const packageName = pkg?.packageJson.name;
      const exportName = moduleName.replace(packageName, '');

      return Object.keys(exportsField).some((item) => item.includes(exportName));
    }
  }

  return false;
}

and should be used in the condition which wraps the call for resolving Haste named modules:

const isExportedSrc = isExportedSource(context, normalizePath(realModuleName));

  if (context.allowHaste && !isDirectImport && !isExportedSrc) {
    const normalizedName = normalizePath(realModuleName);
    const result = resolveHasteName(context, normalizedName, platform);
    if (result.type === "resolved") {
      return result.resolution;
    }
  }

When I have this in place, all modules all resolved correctly and my app builds without issues :)

Any thoughts? Should I create a proposal PR for you guys? PLEASE let me know :) :)

Thank you very much!

from metro.

Bekaxp avatar Bekaxp commented on April 28, 2024

Maybe @robhogan or @huntie could you have look please? If you think this actually could be the solution I can open a PR as well 🥸. Thank you very much for your help and contribution!

from metro.

Bekaxp avatar Bekaxp commented on April 28, 2024

One additional important note...

If in the example above, I remove config.watchFolders = [".."]; option for metro, then it will look for packages only inside the current directory (single repo situation) and it will work. BUT if we have a monorepo, like in our case and we need to look for modules outside the app folder this problem re-occurs.

from metro.

kraenhansen avatar kraenhansen commented on April 28, 2024

@Bekaxp I wanted to verify your fix: Where do I put / call the isExportedSource and isExportedSrc functions?

from metro.

Bekaxp avatar Bekaxp commented on April 28, 2024

Hi @kraenhansen, thanks for trying it out.

Once you install all the dependencies of the repro above, you should have in the node_modules a package named metro-resolver. In there you will find a file src/resolve.js. You should put that code in there. The isExportedSource should be checked in the condition for haste modules => if (context.allowHaste && !isDirectImport) {, which should be around line 90 in the resolve.js file.

I did some more testings with my function and is not perfect because actually some modules should be considered as haste and should enter into that condition. But at least I think it shows that there is a problem how Metro is trying to resolve modules in a monorepo.
Also, if you run a react-native project standalone (not in a monorepo) then the Metro resolver works correctly. This problem only happens when we have a monorepo and the files which needs to be resolved are all over the place 😃.

For now I solved the problem with exporting our packages in a different way (package.json example):

{
  "react-native": "./lib/native/index.mjs",
  "exports": {
    ".": {
      "types": "./lib/native/index.d.ts",
    },
    "./native": {
      "types": "./lib/native/index.d.ts",
      "import": "./lib/native/index.mjs",
      "require": "./lib/native/index.cjs"
    },
    "./web": {
      "types": "./lib/web/index.d.ts",
      "import": "./lib/web/index.mjs",
      "require": "./lib/web/index.cjs"
    },
    "./package.json": "./package.json"
  },
}

Of course then we need to import it from @dp/mypackage and not @dp/mypackage/native. Then Metro is happy and is able to resolve it correctly. But like I said it is just a workaround. I would really like to see Metro resolving it correctly.

Thank you again for trying it out!

from metro.

kraenhansen avatar kraenhansen commented on April 28, 2024

I would really like to see Metro resolving it correctly.

I agree. As a React Native library developer, I'm experiencing the same issue.

I verified that simply disabling the if (context.allowHaste && !isDirectImport) branch (adding && false) in metro-resolver/src/resolve.js makes a package exports sub-path resolve correctly, which indicates that the PackageExportsResolve works as expected.

It definitely looks like this code is thoroughly tested by packages/metro-resolver/src/tests/package-exports-test.js. I've found context.allowHaste is true when the tests are running, but the result from returned by resolveHasteName is { type: 'failed', candidates: undefined } instead of it throwing.

from metro.

Bekaxp avatar Bekaxp commented on April 28, 2024

@kraenhansen OMG!!! You did it! It is fixed! It works 😃😃😃. I will patch it for our projects but I guess the next version will already have your fix in place.

Should I close the issue or leave it open until your PR is merged in?

Thank you very much for this!

from metro.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.