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

Fix index fallback of CJS package from ESM-mode import when main is present but does not resolve #49327

Merged
merged 1 commit into from
May 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2413,8 +2413,8 @@ namespace ts {
);
if (
!pathAndExtension && packageInfo
&& packageInfo.packageJsonContent.exports === undefined
&& packageInfo.packageJsonContent.main === undefined
// eslint-disable-next-line no-null/no-null
&& (packageInfo.packageJsonContent.exports === undefined || packageInfo.packageJsonContent.exports === null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double checking on https://nodejs.org/api/esm.html#resolution-algorithm, recognizing both null and undefined here for exports seems good (only one usage makes that explicit, but it should be fine), however ignoring main and looking up an index regardless is not what PACKAGE_RESOLVE describes for an exportless package... it just says Return the URL resolution of packageSubpath in packageURL., which doesn't imply any fallback to index.js lookup if the main fails. If in practice it is, the algorithm in the node docs may be wrong. (Which doesn't surprise me, if I'm honest, I think the people writing the code are more pragmatic than the people reviewing/writing the docs.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, there's a large legacy main resolution code block which isn't included in the documented algorithm.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've opened nodejs/node#43269 for the docs issue on node.

&& state.features & NodeResolutionFeatures.EsmMode
) {
// EsmMode disables index lookup in `loadNodeModuleFromDirectoryWorker` generally, however non-relative package resolutions still assume
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/index.cts(4,21): error TS2307: Cannot find module 'dedent4' or its corresponding type declarations.
/index.mts(4,21): error TS2307: Cannot find module 'dedent4' or its corresponding type declarations.


==== /node_modules/@types/dedent/package.json (0 errors) ====
{ "name": "@types/dedent", "version": "1.0.0", "main": "" }

==== /node_modules/@types/dedent2/package.json (0 errors) ====
{ "name": "@types/dedent2", "version": "1.0.0", "main": "asdfasdfasdf" }

==== /node_modules/@types/dedent3/package.json (0 errors) ====
{ "name": "@types/dedent3", "version": "1.0.0", "main": "asdfasdfasdf", "exports": null }

==== /node_modules/@types/dedent4/package.json (0 errors) ====
{ "name": "@types/dedent4", "version": "1.0.0", "main": "asdfasdfasdf", "exports": "./asdfasdfasdf" }

==== /node_modules/@types/dedent/index.d.ts (0 errors) ====
export {};

==== /node_modules/@types/dedent2/index.d.ts (0 errors) ====
export {};

==== /node_modules/@types/dedent3/index.d.ts (0 errors) ====
export {};

==== /node_modules/@types/dedent4/index.d.ts (0 errors) ====
export {};

==== /index.mts (1 errors) ====
import dedent from "dedent";
import dedent2 from "dedent2";
import dedent3 from "dedent3";
import dedent4 from "dedent4"; // Error
~~~~~~~~~
!!! error TS2307: Cannot find module 'dedent4' or its corresponding type declarations.

==== /index.cts (1 errors) ====
import dedent from "dedent";
import dedent2 from "dedent2";
import dedent3 from "dedent3";
import dedent4 from "dedent4"; // Error
~~~~~~~~~
!!! error TS2307: Cannot find module 'dedent4' or its corresponding type declarations.

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//// [tests/cases/compiler/nodeNextImportModeImplicitIndexResolution2.ts] ////

//// [package.json]
{ "name": "@types/dedent", "version": "1.0.0", "main": "" }

//// [package.json]
{ "name": "@types/dedent2", "version": "1.0.0", "main": "asdfasdfasdf" }

//// [package.json]
{ "name": "@types/dedent3", "version": "1.0.0", "main": "asdfasdfasdf", "exports": null }

//// [package.json]
{ "name": "@types/dedent4", "version": "1.0.0", "main": "asdfasdfasdf", "exports": "./asdfasdfasdf" }

//// [index.d.ts]
export {};

//// [index.d.ts]
export {};

//// [index.d.ts]
export {};

//// [index.d.ts]
export {};

//// [index.mts]
import dedent from "dedent";
import dedent2 from "dedent2";
import dedent3 from "dedent3";
import dedent4 from "dedent4"; // Error

//// [index.cts]
import dedent from "dedent";
import dedent2 from "dedent2";
import dedent3 from "dedent3";
import dedent4 from "dedent4"; // Error


//// [index.mjs]
export {};
//// [index.cjs]
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
=== /node_modules/@types/dedent/index.d.ts ===
export {};
No type information for this code.
No type information for this code.=== /node_modules/@types/dedent2/index.d.ts ===
export {};
No type information for this code.
No type information for this code.=== /node_modules/@types/dedent3/index.d.ts ===
export {};
No type information for this code.
No type information for this code.=== /node_modules/@types/dedent4/index.d.ts ===
export {};
No type information for this code.
No type information for this code.=== /index.mts ===
import dedent from "dedent";
>dedent : Symbol(dedent, Decl(index.mts, 0, 6))

import dedent2 from "dedent2";
>dedent2 : Symbol(dedent2, Decl(index.mts, 1, 6))

import dedent3 from "dedent3";
>dedent3 : Symbol(dedent3, Decl(index.mts, 2, 6))

import dedent4 from "dedent4"; // Error
>dedent4 : Symbol(dedent4, Decl(index.mts, 3, 6))

=== /index.cts ===
import dedent from "dedent";
>dedent : Symbol(dedent, Decl(index.cts, 0, 6))

import dedent2 from "dedent2";
>dedent2 : Symbol(dedent2, Decl(index.cts, 1, 6))

import dedent3 from "dedent3";
>dedent3 : Symbol(dedent3, Decl(index.cts, 2, 6))

import dedent4 from "dedent4"; // Error
>dedent4 : Symbol(dedent4, Decl(index.cts, 3, 6))

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
=== /node_modules/@types/dedent/index.d.ts ===
export {};
No type information for this code.
No type information for this code.=== /node_modules/@types/dedent2/index.d.ts ===
export {};
No type information for this code.
No type information for this code.=== /node_modules/@types/dedent3/index.d.ts ===
export {};
No type information for this code.
No type information for this code.=== /node_modules/@types/dedent4/index.d.ts ===
export {};
No type information for this code.
No type information for this code.=== /index.mts ===
import dedent from "dedent";
>dedent : typeof dedent

import dedent2 from "dedent2";
>dedent2 : typeof dedent2

import dedent3 from "dedent3";
>dedent3 : typeof dedent3

import dedent4 from "dedent4"; // Error
>dedent4 : any

=== /index.cts ===
import dedent from "dedent";
>dedent : typeof dedent

import dedent2 from "dedent2";
>dedent2 : typeof dedent2

import dedent3 from "dedent3";
>dedent3 : typeof dedent3

import dedent4 from "dedent4"; // Error
>dedent4 : any

37 changes: 37 additions & 0 deletions tests/cases/compiler/nodeNextImportModeImplicitIndexResolution2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// @module: nodenext

// @Filename: /node_modules/@types/dedent/package.json
{ "name": "@types/dedent", "version": "1.0.0", "main": "" }

// @Filename: /node_modules/@types/dedent2/package.json
{ "name": "@types/dedent2", "version": "1.0.0", "main": "asdfasdfasdf" }

// @Filename: /node_modules/@types/dedent3/package.json
{ "name": "@types/dedent3", "version": "1.0.0", "main": "asdfasdfasdf", "exports": null }

// @Filename: /node_modules/@types/dedent4/package.json
{ "name": "@types/dedent4", "version": "1.0.0", "main": "asdfasdfasdf", "exports": "./asdfasdfasdf" }

// @Filename: /node_modules/@types/dedent/index.d.ts
export {};

// @Filename: /node_modules/@types/dedent2/index.d.ts
export {};

// @Filename: /node_modules/@types/dedent3/index.d.ts
export {};

// @Filename: /node_modules/@types/dedent4/index.d.ts
export {};

// @Filename: /index.mts
import dedent from "dedent";
import dedent2 from "dedent2";
import dedent3 from "dedent3";
import dedent4 from "dedent4"; // Error

// @Filename: /index.cts
import dedent from "dedent";
import dedent2 from "dedent2";
import dedent3 from "dedent3";
import dedent4 from "dedent4"; // Error