Skip to content

Path which contain parent ("../") in package.json imports throws ERR_INVALID_PACKAGE_TARGET #57894

Open
@jkscx

Description

@jkscx

Version

v22.11.0

Platform

Darwin mbp-m3 23.4.0 Darwin Kernel Version 23.4.0: Fri Mar 15 00:12:25 PDT 2024; root:xnu-10063.101.17~1/RELEASE_ARM64_T6030 arm64

Subsystem

internal/modules/esm/resolve.js

What steps will reproduce the bug?

  1. Add a relative path which refers to a parent directory to the imports part in a package.json. E.g.:
    "imports": {
        "#shared/*": "../shared/*"
    }
  1. Import it, e.g.:
import "#shared/common.js"; // or import { foo } fom "#shared/common.js"
  1. Get error ERR_INVALID_PACKAGE_TARGET
Error [ERR_INVALID_PACKAGE_TARGET]: Invalid "imports" target "../shared/*" defined for '#shared/*' in the package config [root]/package.json imported from [root]/src/main.ts
    at invalidPackageTarget (node:internal/modules/esm/resolve:334:10)
    at resolvePackageTargetString (node:internal/modules/esm/resolve:387:11)
    at resolvePackageTarget (node:internal/modules/esm/resolve:466:12)
    at packageImportsResolve (node:internal/modules/esm/resolve:722:33)
    at moduleResolve (node:internal/modules/esm/resolve:897:16)
    at defaultResolve (node:internal/modules/esm/resolve:1037:11)
    at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:650:12)
    at #cachedDefaultResolve (node:internal/modules/esm/loader:599:25)
    at ModuleLoader.resolve (node:internal/modules/esm/loader:582:38)
    at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:241:38) {
  code: 'ERR_INVALID_PACKAGE_TARGET'
}

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

Deterministic reproduction, 100% of the times.

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

Should not throw an error and the import should succeed.

Context

Have a monorepo with a BE/FE/shared split. In BE/FE want to import files from shared dir. For that need to go to the parent of the FE/BE. Importing via import { foo } from "../shared/bar.js" works, but I want it to look cleaner and uniform without depending in which file the import is. All the other tools support such path mapping/aliasing and I though Node.js does also, but seems like it's arbitrarily restricted to only paths which are in the base dir.

What do you see instead?

ERR_INVALID_PACKAGE_TARGET

Error [ERR_INVALID_PACKAGE_TARGET]: Invalid "imports" target "../shared/*" defined for '#shared/*' in the package config [root]/package.json imported from [root]/src/main.ts
    at invalidPackageTarget (node:internal/modules/esm/resolve:334:10)
    at resolvePackageTargetString (node:internal/modules/esm/resolve:387:11)
    at resolvePackageTarget (node:internal/modules/esm/resolve:466:12)
    at packageImportsResolve (node:internal/modules/esm/resolve:722:33)
    at moduleResolve (node:internal/modules/esm/resolve:897:16)
    at defaultResolve (node:internal/modules/esm/resolve:1037:11)
    at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:650:12)
    at #cachedDefaultResolve (node:internal/modules/esm/loader:599:25)
    at ModuleLoader.resolve (node:internal/modules/esm/loader:582:38)
    at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:241:38) {
  code: 'ERR_INVALID_PACKAGE_TARGET'
}

Additional information

The algorithm used for the imports feature wasn't really reviewed (?) and I think the implementer just mirrored the no-referring-to-parent-dir restriction which was used for exports (for which it makes sense as how would you export something which isn't in your package?) without considering if it would be useful or not for imports.

Also, importing with a reference to a parent via code is anyway possible, be it referring to a parent inside or outside of the base dir (trivially shown by trying to import a file outside of the base dir, e.g. import "../../../foo.js";), so currently this restriction only achieves making imports in the package.json inconsistent with how import via code works.

Could provide a small PR which fixes this as I already explored and tested an MVP solution.

Metadata

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