Skip to content

Module resolution failure when using "#/" in imports #51949

Closed

Description

@jakezatecky

Version

v20.11.1

Platform

Microsoft Windows NT 10.0.22631.0 x64

Subsystem

npm

What steps will reproduce the bug?

  1. Create a minimal package.json that specifies the following in imports:
{
  "imports": {
    "#/*": "./*"
  }
}
  1. Create two files at the project root:
// file1.js
import number from '#/file2.js';
console.log(number);
// file2.js
export default 2;
  1. Execute the first script: node file1.js
  2. Observe the error:
TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module "#/file2.js" is not a valid internal imports specifier name

How often does it reproduce? Is there a required condition?

This happens every time a key/value pair in imports has a forward slash (/) immediately following the pound sign (#).

What is the expected behavior? Why is that the expected behavior?

I would expect that file1.js is able to resolve file2.js with the specified alias. The documentation does not specify that this is an invalid setting.

What do you see instead?

TypeError [ERR_INVALID_MODULE_SPECIFIER]: Invalid module "#/file2.js" is not a valid internal imports specifier name

Additional information

There is no indication that the above configuration would not work based on the documentation. Indeed, even WebStorm's IntelliSense resolves this alias as if it would work.

Of course, setting the alias to to "#src/*": "./*" and changing file1.js to use import number from '#src/file2.js'; would work. However, I feel that would be inelegant. Imagine we have a project structure where all the files of interest are in src/, such as the following:

└── test-project/
    ├── src/
    │   ├── components/
    │   │   └── SomeComponent.js
    │   └── pages/
    │       └── SomePage.js
    └── package.json

It would be much cleaner to be able to import these files with minimal boilerplate, such as:

import SomeComponent from '#/components/SomeComponent.js';
import SomePage from '#/pages/SomePage.js';

...as opposed to having some needless additional text or specifying each directory as an alias:

{
  "imports": {
    "#components/*": "./src/components/*",
    "#pages/*": "./src/pages/*"
  }
}

Activity

  1. aduh95 commented on Mar 3, 2024

    @aduh95
    Contributor

    The spec says it should throw an error:

    node/doc/api/esm.md

    Lines 959 to 963 in 2f01f02

    **PACKAGE\_IMPORTS\_RESOLVE**(_specifier_, _parentURL_, _conditions_)
    > 1. Assert: _specifier_ begins with _"#"_.
    > 2. If _specifier_ is exactly equal to _"#"_ or starts with _"#/"_, then
    > 1. Throw an _Invalid Module Specifier_ error.

    After a bit of digging, I found this comment that's related: #34117 (comment)

    /cc @nodejs/loaders

  2. guybedford commented on Mar 3, 2024

    @guybedford
    Contributor

    Yes, this is disallowed. Instead write:

    {
      "imports": {
        "#*": "./*"
      }
    }

    If there's a strong argument to allow these my note in that comment still stands.

  3. jakezatecky commented on Mar 4, 2024

    @jakezatecky
    Author

    Keeping the current restriction is not much of a problem if the main documentation includes some warning or if the error message for using #/ was unique. As it stands, Node throws the generic error that applies to any failed module resolution and gives no indication that #/ itself is invalid.

  4. added
    esmIssues and PRs related to the ECMAScript Modules implementation.
    loadersIssues and PRs related to ES module loaders.
    on Apr 28, 2024
  5. ndabAP commented on Nov 21, 2024

    @ndabAP

    A full example thanks to @guybedford:

    "imports": {
        "#*": "./*"
    },
    import '#index.js'
  6. holvi-sebastian commented on Jan 23, 2026

    @holvi-sebastian

    If there's no hard technical reason why #/ should not be allowed as an alias, this would be quite useful to allow because it would serve as an alternative to the popular @/ syntax in frontend to denote root dirs.

    The advantage being that using package.json imports would mean internal path mappings would not have to be rewritten in emitted .js files by build tools such as vite.

    As a comparison, vite is fine with #/ for example.

  7. aduh95 commented on Jan 23, 2026

    @aduh95
    Contributor

    Fixed by #60864

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    esmIssues and PRs related to the ECMAScript Modules implementation.loadersIssues and PRs related to ES module loaders.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions