ESM mode nonrelative imports should assume index.js entrypoints even if no package main is present (#47854)

This commit is contained in:
Wesley Wigham 2022-02-11 15:44:11 -08:00 committed by GitHub
parent e204acfa26
commit 1bdb0d90bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 140 additions and 2 deletions

View File

@ -1394,7 +1394,13 @@ namespace ts {
onlyRecordFailures = true;
}
}
return loadNodeModuleFromDirectory(extensions, candidate, onlyRecordFailures, state, considerPackageJson);
// esm mode relative imports shouldn't do any directory lookups (either inside `package.json`
// files or implicit `index.js`es). This is a notable depature from cjs norms, where `./foo/pkg`
// could have been redirected by `./foo/pkg/package.json` to an arbitrary location!
if (!(state.features & NodeResolutionFeatures.EsmMode)) {
return loadNodeModuleFromDirectory(extensions, candidate, onlyRecordFailures, state, considerPackageJson);
}
return undefined;
}
/*@internal*/
@ -2178,7 +2184,7 @@ namespace ts {
if (packageInfo && packageInfo.packageJsonContent.exports && state.features & NodeResolutionFeatures.Exports) {
return loadModuleFromExports(packageInfo, extensions, combinePaths(".", rest), state, cache, redirectedReference)?.value;
}
const pathAndExtension =
let pathAndExtension =
loadModuleFromFile(extensions, candidate, onlyRecordFailures, state) ||
loadNodeModuleFromDirectoryWorker(
extensions,
@ -2188,6 +2194,16 @@ namespace ts {
packageInfo && packageInfo.packageJsonContent,
packageInfo && packageInfo.versionPaths
);
if (
!pathAndExtension && packageInfo
&& packageInfo.packageJsonContent.exports === undefined
&& packageInfo.packageJsonContent.main === undefined
&& state.features & NodeResolutionFeatures.EsmMode
) {
// EsmMode disables index lookup in `loadNodeModuleFromDirectoryWorker` generally, however non-relative package resolutions still assume
// a default `index.js` entrypoint if no `main` or `exports` are present
pathAndExtension = loadModuleFromFile(extensions, combinePaths(candidate, "index.js"), onlyRecordFailures, state);
}
return withPackageId(packageInfo, pathAndExtension);
};

View File

@ -0,0 +1,31 @@
tests/cases/compiler/index.ts(2,31): error TS2834: Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node12' or 'nodenext'. Consider adding an extension to the import path.
tests/cases/compiler/index.ts(3,31): error TS2834: Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node12' or 'nodenext'. Consider adding an extension to the import path.
==== tests/cases/compiler/node_modules/pkg/package.json (0 errors) ====
{
"name": "pkg",
"version": "0.0.1"
}
==== tests/cases/compiler/node_modules/pkg/index.d.ts (0 errors) ====
export const item = 4;
==== tests/cases/compiler/pkg/package.json (0 errors) ====
{
"private": true
}
==== tests/cases/compiler/pkg/index.d.ts (0 errors) ====
export const item = 4;
==== tests/cases/compiler/package.json (0 errors) ====
{
"type": "module",
"private": true
}
==== tests/cases/compiler/index.ts (2 errors) ====
import { item } from "pkg"; // should work (`index.js` is assumed to be the entrypoint for packages found via nonrelative import)
import { item as item2 } from "./pkg"; // shouldn't work (`index.js` is _not_ assumed to be the entrypoint for packages found via relative import)
~~~~~~~
!!! error TS2834: Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node12' or 'nodenext'. Consider adding an extension to the import path.
import { item as item3 } from "./node_modules/pkg" // _even if they're in a node_modules folder_
~~~~~~~~~~~~~~~~~~~~
!!! error TS2834: Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node12' or 'nodenext'. Consider adding an extension to the import path.

View File

@ -0,0 +1,28 @@
//// [tests/cases/compiler/nodeNextImportModeImplicitIndexResolution.ts] ////
//// [package.json]
{
"name": "pkg",
"version": "0.0.1"
}
//// [index.d.ts]
export const item = 4;
//// [package.json]
{
"private": true
}
//// [index.d.ts]
export const item = 4;
//// [package.json]
{
"type": "module",
"private": true
}
//// [index.ts]
import { item } from "pkg"; // should work (`index.js` is assumed to be the entrypoint for packages found via nonrelative import)
import { item as item2 } from "./pkg"; // shouldn't work (`index.js` is _not_ assumed to be the entrypoint for packages found via relative import)
import { item as item3 } from "./node_modules/pkg" // _even if they're in a node_modules folder_
//// [index.js]
export {};

View File

@ -0,0 +1,18 @@
=== tests/cases/compiler/node_modules/pkg/index.d.ts ===
export const item = 4;
>item : Symbol(item, Decl(index.d.ts, 0, 12))
=== tests/cases/compiler/pkg/index.d.ts ===
export const item = 4;
>item : Symbol(item, Decl(index.d.ts, 0, 12))
=== tests/cases/compiler/index.ts ===
import { item } from "pkg"; // should work (`index.js` is assumed to be the entrypoint for packages found via nonrelative import)
>item : Symbol(item, Decl(index.ts, 0, 8))
import { item as item2 } from "./pkg"; // shouldn't work (`index.js` is _not_ assumed to be the entrypoint for packages found via relative import)
>item2 : Symbol(item2, Decl(index.ts, 1, 8))
import { item as item3 } from "./node_modules/pkg" // _even if they're in a node_modules folder_
>item3 : Symbol(item3, Decl(index.ts, 2, 8))

View File

@ -0,0 +1,22 @@
=== tests/cases/compiler/node_modules/pkg/index.d.ts ===
export const item = 4;
>item : 4
>4 : 4
=== tests/cases/compiler/pkg/index.d.ts ===
export const item = 4;
>item : 4
>4 : 4
=== tests/cases/compiler/index.ts ===
import { item } from "pkg"; // should work (`index.js` is assumed to be the entrypoint for packages found via nonrelative import)
>item : 4
import { item as item2 } from "./pkg"; // shouldn't work (`index.js` is _not_ assumed to be the entrypoint for packages found via relative import)
>item : any
>item2 : any
import { item as item3 } from "./node_modules/pkg" // _even if they're in a node_modules folder_
>item : any
>item3 : any

View File

@ -0,0 +1,23 @@
// @module: nodenext
// @filename: node_modules/pkg/package.json
{
"name": "pkg",
"version": "0.0.1"
}
// @filename: node_modules/pkg/index.d.ts
export const item = 4;
// @filename: pkg/package.json
{
"private": true
}
// @filename: pkg/index.d.ts
export const item = 4;
// @filename: package.json
{
"type": "module",
"private": true
}
// @filename: index.ts
import { item } from "pkg"; // should work (`index.js` is assumed to be the entrypoint for packages found via nonrelative import)
import { item as item2 } from "./pkg"; // shouldn't work (`index.js` is _not_ assumed to be the entrypoint for packages found via relative import)
import { item as item3 } from "./node_modules/pkg" // _even if they're in a node_modules folder_