Skip to content

Node.js imports field is not supported #327

Open
@pcdevil

Description

Imports fields is not supported

Overview

Short description: resolve fails to handle Node.js' imports field style imports.

Steps to reproduce:

  1. Add a simple entry to imports fields to package.json:
    +  "imports": {
    +    "#src/*": "./src/*"
    +  },
  2. Create a src/module-1.js file
  3. Call resolve (async or sync) with #src/module-1.js argument

Expected behaviour: resolve() returns the full path of the module

Actual behaviour: resolve() throws an error

Minimal reproducible example: I created the resolve-nodejs-import StackBlitz project to showcase the issue with some guiding. Running npm start should execute the script and show the behaviour of resolve() in various circumstances.

Context

I'm using the resolve package as a transitive dependency of eslint-plugin-import for a small project I created, wordoftheday.

This project is very minimal and doesn't utilise any bundler but instead relies on Node.js' module resolution and imports field. By this, the imports are non-relative paths with TypeScript style import aliases. For example the src/lib/word-resolver.mjs file has the following dependencies (at L3-6):

import { UndefinedConfigError, config } from '#src/lib/config.mjs';
import { getLogger } from '#src/util/logger.mjs';
import { NamedError } from '#src/util/named-error.mjs';
import { request } from '#src/util/request.mjs';

This works just fine on run, I don't experience any side-effects by calling pnpm start.


I decided to upgrade ESLint to v9 and after tinkering the config file, I received a lot of errors from import's no-unresolved rule when I ran eslint.

After some investigation I realised the issue lies deeper at resolve, as it doesn't unfold module names starting with a #. I believe the issue is at the check where the module's nature is determined:

  • sync:

    resolve/lib/sync.js

    Lines 101 to 111 in fd788d9

    if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) {
    var res = path.resolve(absoluteStart, x);
    if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/';
    var m = loadAsFileSync(res) || loadAsDirectorySync(res);
    if (m) return maybeRealpathSync(realpathSync, m, opts);
    } else if (includeCoreModules && isCore(x)) {
    return x;
    } else {
    var n = loadNodeModulesSync(x, absoluteStart);
    if (n) return maybeRealpathSync(realpathSync, n, opts);
    }
  • async:

    resolve/lib/async.js

    Lines 141 to 164 in fd788d9

    if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) {
    res = path.resolve(basedir, x);
    if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/';
    if ((/\/$/).test(x) && res === basedir) {
    loadAsDirectory(res, opts.package, onfile);
    } else loadAsFile(res, opts.package, onfile);
    } else if (includeCoreModules && isCore(x)) {
    return cb(null, x);
    } else loadNodeModules(x, basedir, function (err, n, pkg) {
    if (err) cb(err);
    else if (n) {
    return maybeRealpath(realpath, n, opts, function (err, realN) {
    if (err) {
    cb(err);
    } else {
    cb(null, realN, pkg);
    }
    });
    } else {
    var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'");
    moduleError.code = 'MODULE_NOT_FOUND';
    cb(moduleError);
    }
    });

Closing thoughts

The issue is not related to ESLint v9, but I mistakenly didn't include eslint-plugin-import to my .eslintrc.json's extends field (at L7-10) and only realised the mistake on update.

I can understand if you believe it's out of the scope of this package, although I'd think the imports field feature would be supported. In that case a possible workaround for me is to wrap eslint-import-resolver-node and transform imports starting with # to project relative paths.

However, if you believe it's worth fixing it, I'd also volunteer to provide a change-set for review!

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions