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

Order of keys in exports field matters to the resolved path #433

Closed
niper1 opened this issue Sep 3, 2024 · 4 comments
Closed

Order of keys in exports field matters to the resolved path #433

niper1 opened this issue Sep 3, 2024 · 4 comments

Comments

@niper1
Copy link

niper1 commented Sep 3, 2024

Using version 5.17.0

Hi guys!

I have an issue with resolving the path to one of our dependencies and have narrowed it down to that enhanced-resolve does not handle the exports field in package.json of said dependency. The path is resolved to the require path and not the import path when import is used.

package.json of the dependency:

...
"main": "./index.js",
  "module": "./esm/index.mjs",
  "exports": {
    ".": {
      "types": "./index.d.ts",
      "require": "./index.js", // This one is picked
      "import": "./esm/index.mjs", // This one Should be picked
      "default": "./index.js"
    },
    "./package.json": "./package.json",
    "./*.css": "./*.css",
    "./*.scss": "./*.scss",
    "./lib": "./lib",
    "./lib/*": [
      "./lib/*",
      "./lib/*.js"
    ],
    "./*.js": {
      "import": "./esm/*.mjs",
      "require": "./*.js"
    },
    "./*": {
      "require": [
        "./*.js",
        "./*"
      ],
      "import": [
        "./esm/*.mjs",
        "./esm/*",
        "./*.js",
        "./*"
      ],
      "default": "./*"
    }
  },
...

Our implementation:

const RESOLVE_EXTENSIONS = ['.js', '.ts', '.tsx', '.jsx', '.json', '.mjs', '.cjs'];
const ALIAS_FIELDS = ['browser', 'module'];

private resolver = Resolve.create.sync({
		mainFields: [...ALIAS_FIELDS, 'main'],
		extensions: RESOLVE_EXTENSIONS,
		aliasFields: ALIAS_FIELDS,
		conditionNames: [
			'import',
			'esm2020',
			'es2020',
			'es2015',
			'require',
			'node',
			'node-addons',
			'browser',
			'default'
		]
	});

...

this.resolver({}, Path.dirname(parentFilePath), dependencyName);

The curious thing is that when swapping the order of the keys require and import in exports in the dependency, it works!

...
  "exports": {
    ".": {
      "types": "./index.d.ts",
      "import": "./esm/index.mjs", // import is placed first and suddenly it works. 
      "require": "./index.js",
      "default": "./index.js"
    },
...

Any ideas about what could be the issue here?

@alexander-akait
Copy link
Member

alexander-akait commented Sep 3, 2024

You need to set fullySpecified: true, otherwise we can't know is it esm or not

@alexander-akait
Copy link
Member

If you can create reproducible test repo I can look deeply

@niper1
Copy link
Author

niper1 commented Sep 5, 2024

You need to set fullySpecified: true, otherwise we can't know is it esm or not

@alexander-akait Thank you for the quick reply! But I don't think that this is the way.

The fullySpecified option assumes that you want to resolve something that already has an extension but I want to resolve a package without one, e.g: @project-name/package-name. So, this option does not help this case.

@alexander-akait
Copy link
Member

Sorry for the delay, I studied the documentation and I will say that such behavior is expected, your import should be higher than require -

Within the "exports" object, key order is significant. During condition matching, earlier entries have higher priority and take precedence over later entries. The general rule is that conditions should be from most specific to least specific in object order.

https://nodejs.org/api/packages.html#conditional-exports

Even in the node documentation the import is always higher than require. I would warn you that such an order may break more tools than you expect.

Feel free to feedback

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

No branches or pull requests

3 participants
@alexander-akait @niper1 and others