[go: nahoru, domu]

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for package.json exports and imports ? #1868

Closed
bradennapier opened this issue Aug 5, 2020 · 21 comments
Closed

Support for package.json exports and imports ? #1868

bradennapier opened this issue Aug 5, 2020 · 21 comments

Comments

@bradennapier
Copy link
Contributor
bradennapier commented Aug 5, 2020

Sorry if this was posted, I did search quite a few things before posting :-P

So just came across this today, https://nodejs.org/dist/latest-v14.x/docs/api/esm.html#esm_packages - not sure how i missed it for so long but it appears that the internal imports mapping was just released in a recent .x version of v14.

Basically it allows node to resolve situations like Typescript paths would. There are a few cases here that seem like they are fairly advanced to handle properly, and it doesn't seem like it works at the moment, although just initial testing so far.

While I know that there are import resolvers that would allow defining the rules manually, since this is an official node feature already launched in the LTS version of node, it probably makes sense to support it with the default node resolver?

{
"name": "example",
"type": "module",
"exports": {
    ".": "./src/index.js",
    "./quick": "./src/quick.js"
  },
  "imports": {
    "#alias": "./alias.js",
    "#tryPeer": ["some-peer-dep", "./src/peer-not-found.js"],
    "#conditionally": {
          "node": "./src/node.js",
          "require": "./src/required.js",
          "import": "./src/imported.js",
          "default": "./src/defaulted.js"
     }
  }
}

Given the above, I can run the following in node (no feature flags needed)

// src/test.js

// loads src/quick.js due to exports "./quick" mapping pointing to "./src/quick.js"
import * as mods from 'example/quick'; 

// tries to load some-peer-dep package but if it fails it will load ./src/peer-not-found.js instead
import peer from '#tryPeer';

// if node env loads ./src/node, 
// if (not node) and required with require('example') loads ./src/required.js
// if (not node) and imported with import 'example' or import('example') loads ./src/imported.js
// if no match (dont think its possible here) , loads './src/defaulted.js'
import conditional from '#conditionally'

mods.runMain()
mods.runAlias()
// src/quick.js
export * from '#alias'; // exports all modules from './alias.js' due to internal imports mapping for #alias
export * from 'example'; // exports all modules from './src/index.js' due to exports "." mapping to "./src/index.js"
// src/index.js

export function runMain() {
   console.log('runs');
}
// ./alias.js

export function runAlias() {
    console.log('alias runs')
}

There is additional support for conditional handling which makes things a bit more complicated, but should potentially be considered.

With the import maps I believe it also allows importing subpaths if supported so that if you have #alias resolve to eslint then you could do import subpath from '#alias/lib/someFile.js'

I (think?) this is actually pretty similar to how deno handles module resolution mapping?


Running things with some flags to provide helper fns (no require.resolve by default) we do see:

 node --experimental-import-meta-resolve --experimental-top-level-await  ./src/test.js
// src/test.js
console.log(
  await import.meta.resolve('example'),
  await import.meta.resolve('example/quick'),
  await import.meta.resolve('#alias'),
);
// file:///.../example/src/index.js 
// file:///.../example/src/quick.js 
// file:///.../example/alias.js

Also just realized this even works if not using "type": "module" for standard require-based modules, although with require.resolve it doesnt return file path:

console.log(
  require.resolve('example'),
  require.resolve('example/quick'),
  require.resolve('#alias'),
);
// /.../example/src/index.js 
// /.../example/src/quick.js 
// /.../example/alias.js
@ljharb
Copy link
Member
ljharb commented Aug 5, 2020

Yes, both should be supported (altho "imports" is super brand new, like, less than a week).

However, support first needs to be added in resolve - which I'm the primary maintainer of - and I'm planning to work on that during the month of August. Once that's updated, adding the support here should be trivial.

@bradennapier
Copy link
Contributor Author

Awesome! Glad to hear it. Thanks for the reply. This issue can track its progress then!

Look forward to it.

@clintwood
Copy link

@ljharb any movement on this?

@ljharb
Copy link
Member
ljharb commented Sep 7, 2020

@clintwood not quite yet - browserify/resolve#224 is still in progress, which would be the first step.

@clintwood
Copy link

@ljharb - I see - thanks for the feedback! Things have become a little complicated in nodeland 😉

tilgovi added a commit to apache/incubator-annotator that referenced this issue Sep 20, 2020
With Node.js adopting a standard for packages to declare their exports,
this rule may not be useful anymore. It should be legal to import any
valid export from a package.

An open [issue] on eslint-plugin-import tracks this ecosystem feature
and when that resolves there may be a better rule to enable.

[issue]: import-js/eslint-plugin-import#1868
@ctavan
Copy link
ctavan commented Oct 2, 2020

@ljharb will the modifications to the resolve module also fix package self references which make use of pkg.exports (they currently result in import/no-extraneous-dependencies errors)?

@ljharb
Copy link
Member
ljharb commented Oct 2, 2020

@ctavan once resolve supports that, yes.

@nfantone
Copy link
nfantone commented Jan 14, 2021

Any workaround in the meantime? Getting import/no-unresolved errors on modules referenced by "exports", currently. Would defining custom aliases through eslint-import-resolver-custom-alias matching "exports" shape work?

@mattfysh
Copy link

Best workaround is just to turn off the rule until they add support for routing via package.json exports/imports fields

@ljharb
Copy link
Member
ljharb commented Jun 20, 2021

A package that doesn’t provide equivalent CJS paths for exports paths isn’t back-compat and is thus being user-hostile, so this shouldn’t require workarounds most of the time.

@noahnu
Copy link
noahnu commented Jun 20, 2021

Best workaround is just to turn off the rule until they add support for routing via package.json exports/imports fields

If using Yarn Berry's PnP, you can use a custom resolver I wrote such as https://github.com/tophat/eslint-import-resolver-require (just delegates to require.resolve).

@mattfysh
Copy link

A package that doesn’t provide equivalent CJS paths for exports paths isn’t back-compat and is thus being user-hostile, so this shouldn’t require workarounds most of the time.

Not all packages are open source and made available for public consumption. In some closed development systems, a minimum version of nodejs is set allowing exclusive access to features such as subpath fields.

@ljharb
Copy link
Member
ljharb commented Jun 21, 2021

True enough. Unfortunately those will have to use the above resolver or wait until proper exports support is added to resolve.

@bertho-zero
Copy link

Why not use https://github.com/webpack/enhanced-resolve which supports export fields instead of https://github.com/browserify/resolve?

resolve requests according to the Node.js resolving rules

I think modifying this file a bit should be enough, right?

@MikeG96
Copy link
MikeG96 commented Nov 5, 2021

I'm using this in eslint rules file

"import/no-unresolved": [2, { "ignore": ["^#[\\w\\d\/\\-]+$"] }]

@joezappie
Copy link
joezappie commented Dec 11, 2021

I had to modify @MikeG96's code to this to fully ignore these errors for imported modules with the # syntax:

"import/no-unresolved": [2, { "ignore": ["^#.+$"] }],
"import/extensions": 0

@ljharb
Copy link
Member
ljharb commented Dec 11, 2021

@jrj2211 voicing support is unnecessary; there is a 100% chance this feature will eventually be implemented.

@danielweck
Copy link
danielweck commented Feb 23, 2022

Hi all, FYI I am successfully using this tiny ESLint import resolver to support ESM modules imports via package.json exports map:

https://gist.github.com/danielweck/cd63af8e9a8b3492abacc312af9f28fd

Duplicate issue? #1810

@ljharb
Copy link
Member
ljharb commented Feb 23, 2022

Indeed, duplicate of #1810.

@kellengreen
Copy link

Was support for this added? I'm still seeing import/no-unresolved when importing packages with exports.

@karlhorky
Copy link
Contributor
karlhorky commented Apr 3, 2024

I think this is not yet completed, but has been moved to #1810 - there are also multiple workarounds in that thread, including:

@ljharb, maybe the discussion in #1810 could be reopened to allow comments again?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests