ipfs / js-ipfs

IPFS implementation in JavaScript
https://js.ipfs.tech
Other
7.44k stars 1.25k forks source link

ERR_PACKAGE_PATH_NOT_EXPORTED with ipfs-http-client and typescript. Suggested fixes do not work. #4130

Closed haydenyoung closed 2 years ago

haydenyoung commented 2 years ago

Severity:

Medium

Description:

When attempting to import ipfs-http-client, the following error is thrown:

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in /path/to/dev/node_modules/ipfs-http-client/package.json
    at new NodeError (node:internal/errors:371:5)
    at throwExportsNotFound (node:internal/modules/esm/resolve:440:9)
    at packageExportsResolve (node:internal/modules/esm/resolve:645:7)
    at resolveExports (node:internal/modules/cjs/loader:482:36)
    at Function.Module._findPath (node:internal/modules/cjs/loader:522:31)
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:919:27)
    at Function.Module._resolveFilename.sharedData.moduleResolveFilenameHook.installedValue [as _resolveFilename] ( /path/to/dev/node_modules/@cs
potcode/source-map-support/source-map-support.js:679:30)
    at Function.Module._load (node:internal/modules/cjs/loader:778:27)
    at Module.require (node:internal/modules/cjs/loader:1005:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at  /path/to/dev/test/create.ts:14:3
    at processTicksAndRejections (node:internal/process/task_queues:96:5) {

The problem appears to be related to the recent ES change. Changing module to "esnext" is not available because I am using Hardhat for the project's management and the HH devs have expressed no short term desire to address the issue.

I have applied the following update to try and load ipfs-http-client (https://github.com/ipfs/js-ipfs/blob/master/docs/upgrading/v0.62-v0.63.md#esm) and (https://github.com/ipfs/js-ipfs/issues/4125#issuecomment-1146754121) but without success:

  import("ipfs-http-client")
    .then(module => {
      return module.create();
    })
    .catch(err => {
      console.log(err);
    });

but this gets me back to where I started, I.e. it is console.logging the error ERR_PACKAGE_PATH_NOT_EXPORTED.

I have also looked at the dynamic imports documentation https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_import and have implemented it in exactly the way documented but still no success.

I'm at an impasse. Am I missing something about dynamic imports for non-esm compliant environments?

Steps to reproduce the error:

Install ipfs-http-client to a node project using typescript:

npm i -D ipfs-http-client

Make sure tsconfig.json's compileOptions -> module setting is "commonjs":

module: "commonjs"

Try and dynamically import ipfs-http-client:


try {
  const { create } = await import("ipfs-http-client"); // it will throw ERR_PACKAGE_PATH_NOT_EXPORTED here.
} catch (error) {
  console.log(error); // it will error on import.
}
welcome[bot] commented 2 years ago

Thank you for submitting your first issue to this repository! A maintainer will be here shortly to triage and review. In the meantime, please double-check that you have provided all the necessary information to make this process easy! Any information that can help save additional round trips is useful! We currently aim to give initial feedback within two business days. If this does not happen, feel free to leave a comment. Please keep an eye on how this issue will be labeled, as labels give an overview of priorities, assignments and additional actions requested by the maintainers:

Finally, remember to use https://discuss.ipfs.io if you just need general support.

ottodevs commented 2 years ago

Same error happens to me, but with the package ipfs-core:

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: No "exports" main defined in node_modules/ipfs-core/package.json

I am also using hardhat typescript project.

Reproduction steps:

  1. Start new hardhat project from hardhat boilerplate: npx hardhat init
  2. Select typescript project from the options, and continue with other default options.
  3. Import ipfs-core in any ts file in the project and try to run it with hardhat or ts-node
achingbrain commented 2 years ago

Please examine the code tsc produces - you cannot use module: "commonjs" as import('ipfs-core') will be transpiled to require('ipfs-core') which fails with No "exports" main....

Instead use at least "module": "ES2020" to have import('ipfs-core') left as import('ipfs-core') and "target": "ES2015" to support #private identifiers.

Better yet use "module": "ES2022" and "target": "ES2022", to be the most future-proof, but please check what your target environment supports.

haydenyoung commented 2 years ago

Unfortunately with projects such as Hardhat (a pretty crucial platform in smart contract dev) having no plans to support ESM short/mid term https://github.com/NomicFoundation/hardhat/issues/1808, it means we're stuck with older versions of IPFS if we want to use it with NFTs and other solidity-related projects. Changing module: and target: is not going to be an option for devs using projects such as Hardhat as their base environment.

My understanding is there is cross-compatibility available between ES and commonjs and IPFS's own documentation even states this is possible (https://github.com/ipfs/js-ipfs/blob/master/docs/upgrading/v0.62-v0.63.md#esm)

Therefore, there must be a way to solve this problem with an intermediary solution while other projects catch up. My problem is, I have implemented the documented solutions but nothing seems to work and I'm back to the original error no matter what I try.

achingbrain commented 2 years ago

IPFS's own documentation even states this is possible

Dynamic imports are how you import ESM code in a CJS environment and is what the upgrade guide says to use. It's supported in all browsers and node 14+.

If your TypeScript settings transpile the dynamic import to require though , it's not going to work.

To stop that from happening you need "module": "ES2020" and "target": "ES2015".

haydenyoung commented 2 years ago

To stop that from happening you need "module": "ES2020" and "target": "ES2015".

Thanks for the suggestion. Unfortunately, I tried this but this breaks a whole bunch of import "side-effects" which Hardhat uses to modularize 3rd party functionality. And even if I resolve this, I then get other export problems so it seems to be a cascading problem.