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

Question: configure node.js subpath exports resolution for 'not-to-unresolvable' #976

Open
kwonoj opened this issue Dec 31, 2024 · 2 comments

Comments

@kwonoj
Copy link

kwonoj commented Dec 31, 2024

Summary

Quick demo repo: https://github.com/kwonoj/wallaby-workspace-test/tree/test-depcruise-resolution
(test-depcruise-resolution) branch

yarn lint

pkg-core:lint.circular |   error not-to-unresolvable: src/test.ts → @test/util/notworks
pkg-core:lint.circular | x 1 dependency violations (1 errors, 0 warnings). 10 modules, 5 dependencies cruised.

Context

In the repo there are some workspaces package having node.js's subpath exports (https://nodejs.org/api/packages.html#subpath-exports) to control its exports. Dependency cruiser's not-to-unresolvable resolves this in most case without a problem, but if there's a default exports and the others like

"./somepath": {
      "types": "...",
      "default": [
        "./src/util.ts"
      ]
    }

It'll fail to falls back to default and fail to resolve given paths. Per https://nodejs.org/api/packages.html#conditional-exports, I expect default should be the fallback to match.

"default" - the generic fallback that always matches. Can be a CommonJS or ES module file. This condition should always come last.

If I adjust exports condition and manually add import condition explicitly, then dependency cruiser seems to use those.

"./somepath": {
      "import": [
        "./src/util.ts"
      ],
      "types": "...",
      "default": [
        "./src/util.ts"
      ]
    },

This sort works, but requires to repeat same exports conditions twice for import and default.

The question is, is there a way to configure dependency cruiser to honor default exports condition match fallback?

Environment

@sverweij
Copy link
Owner

sverweij commented Jan 3, 2025

Hi @kwonoj thanks for reaching out.

It looks like the issue is with the types export. When used with a * in the RHS, it needs one in the LHS as well (ref nodejs' packages#subpath-patterns documentation), as it's a replacement syntax instead of pattern matching

Updating packages/pkg-util/package.json to this will make that it works as expected (replaced all instances of "types" with "types/*").

{
  "name": "@test/util",
  "imports": {
    "#util/*": [
      "./src/*",
      "./src/*.ts"
    ]
  },
  "exports": {
    "./util": "./src/util.ts",
    "./works": {
      "import": [
        "./src/util.ts"
      ],
      "types/*": "./src/*.d.ts",
      "default": [
        "./src/util.ts"
      ]
    },
    "./notworks": {
      "types/*": "./src/*.d.ts",
      "default": [
        "./src/util.ts"
      ]
    }
  },
  "devDependencies": {
    "@test/tsconfig": "workspace:^"
  }
}

background

It took me a bit to find this

  • I've removed the types from the "./notworks" export -> worked
  • I moved the types export above the import in the ./works export -> stopped working
  • removed the types value from the options.enhancedResolveOptions.conditionNames in .dependency-cruiser.js -> worked.

Only when re-reading the nodejs spec again I realised what the issue was.

B.t.w. dependency-cruiser uses enhanced-resolve for most of its module resolution logic. If after a discussion we think this behavior might need to change (i.e. because of interpretation differences) it'll ultimately have to be updated upstream.

@kwonoj
Copy link
Author

kwonoj commented Jan 8, 2025

But types is not a really alias, it's a field to conditions definitions (https://nodejs.org/api/packages.html#community-conditions-definitions) similar to others like browser.

Making a change to types -> types/* makes tsc fails to resolve those definitions. Similar to import, could have duplicated entry types and types/* as workaround but that is pretty similar to having duplicated import mappings.

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

2 participants