Skip to content

Commit e32281c

Browse files
authored
Allow extensionless mains for cjs mode packages even from an esm import (microsoft#47893)
1 parent 03f9035 commit e32281c

5 files changed

+161
-1
lines changed

Diff for: src/compiler/moduleNameResolver.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -1819,7 +1819,17 @@ namespace ts {
18191819
// Even if extensions is DtsOnly, we can still look up a .ts file as a result of package.json "types"
18201820
const nextExtensions = extensions === Extensions.DtsOnly ? Extensions.TypeScript : extensions;
18211821
// Don't do package.json lookup recursively, because Node.js' package lookup doesn't.
1822-
return nodeLoadModuleByRelativeName(nextExtensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ false);
1822+
1823+
// Disable `EsmMode` for the resolution of the package path for cjs-mode packages (so the `main` field can omit extensions)
1824+
// (technically it only emits a deprecation warning in esm packages right now, but that's probably
1825+
// enough to mean we don't need to support it)
1826+
const features = state.features;
1827+
if (jsonContent?.type !== "module") {
1828+
state.features &= ~NodeResolutionFeatures.EsmMode;
1829+
}
1830+
const result = nodeLoadModuleByRelativeName(nextExtensions, candidate, onlyRecordFailures, state, /*considerPackageJson*/ false);
1831+
state.features = features;
1832+
return result;
18231833
};
18241834

18251835
const onlyRecordFailuresForPackageFile = packageFile ? !directoryProbablyExists(getDirectoryPath(packageFile), state.host) : undefined;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [tests/cases/compiler/nodeNextEsmImportsOfPackagesWithExtensionlessMains.ts] ////
2+
3+
//// [package.json]
4+
{
5+
"name": "@types/ip",
6+
"version": "1.1.0",
7+
"main": "",
8+
"types": "index"
9+
}
10+
//// [index.d.ts]
11+
export function address(): string;
12+
//// [package.json]
13+
{
14+
"name": "nullthrows",
15+
"version": "1.1.1",
16+
"main": "nullthrows.js",
17+
"types": "nullthrows.d.ts"
18+
}
19+
//// [nullthrows.d.ts]
20+
declare function nullthrows(x: any): any;
21+
declare namespace nullthrows {
22+
export {nullthrows as default};
23+
}
24+
export = nullthrows;
25+
//// [package.json]
26+
{
27+
"type": "module"
28+
}
29+
//// [index.ts]
30+
import * as ip from 'ip';
31+
import nullthrows from 'nullthrows'; // shouldn't be callable, `nullthrows` is a cjs package, so the `default` is the module itself
32+
33+
export function getAddress(): string {
34+
return nullthrows(ip.address());
35+
}
36+
37+
//// [index.js]
38+
import * as ip from 'ip';
39+
import nullthrows from 'nullthrows'; // shouldn't be callable, `nullthrows` is a cjs package, so the `default` is the module itself
40+
export function getAddress() {
41+
return nullthrows(ip.address());
42+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/compiler/index.ts ===
2+
import * as ip from 'ip';
3+
>ip : Symbol(ip, Decl(index.ts, 0, 6))
4+
5+
import nullthrows from 'nullthrows'; // shouldn't be callable, `nullthrows` is a cjs package, so the `default` is the module itself
6+
>nullthrows : Symbol(nullthrows, Decl(index.ts, 1, 6))
7+
8+
export function getAddress(): string {
9+
>getAddress : Symbol(getAddress, Decl(index.ts, 1, 36))
10+
11+
return nullthrows(ip.address());
12+
>nullthrows : Symbol(nullthrows, Decl(index.ts, 1, 6))
13+
>ip.address : Symbol(ip.address, Decl(index.d.ts, 0, 0))
14+
>ip : Symbol(ip, Decl(index.ts, 0, 6))
15+
>address : Symbol(ip.address, Decl(index.d.ts, 0, 0))
16+
}
17+
=== tests/cases/compiler/node_modules/@types/ip/index.d.ts ===
18+
export function address(): string;
19+
>address : Symbol(address, Decl(index.d.ts, 0, 0))
20+
21+
=== tests/cases/compiler/node_modules/nullthrows/nullthrows.d.ts ===
22+
declare function nullthrows(x: any): any;
23+
>nullthrows : Symbol(nullthrows, Decl(nullthrows.d.ts, 0, 0), Decl(nullthrows.d.ts, 0, 41))
24+
>x : Symbol(x, Decl(nullthrows.d.ts, 0, 28))
25+
26+
declare namespace nullthrows {
27+
>nullthrows : Symbol(nullthrows, Decl(nullthrows.d.ts, 0, 0), Decl(nullthrows.d.ts, 0, 41))
28+
29+
export {nullthrows as default};
30+
>nullthrows : Symbol(nullthrows, Decl(nullthrows.d.ts, 0, 0), Decl(nullthrows.d.ts, 0, 41))
31+
>default : Symbol(default, Decl(nullthrows.d.ts, 2, 12))
32+
}
33+
export = nullthrows;
34+
>nullthrows : Symbol(nullthrows, Decl(nullthrows.d.ts, 0, 0), Decl(nullthrows.d.ts, 0, 41))
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== tests/cases/compiler/index.ts ===
2+
import * as ip from 'ip';
3+
>ip : typeof ip
4+
5+
import nullthrows from 'nullthrows'; // shouldn't be callable, `nullthrows` is a cjs package, so the `default` is the module itself
6+
>nullthrows : typeof nullthrows
7+
8+
export function getAddress(): string {
9+
>getAddress : () => string
10+
11+
return nullthrows(ip.address());
12+
>nullthrows(ip.address()) : any
13+
>nullthrows : typeof nullthrows
14+
>ip.address() : string
15+
>ip.address : () => string
16+
>ip : typeof ip
17+
>address : () => string
18+
}
19+
=== tests/cases/compiler/node_modules/@types/ip/index.d.ts ===
20+
export function address(): string;
21+
>address : () => string
22+
23+
=== tests/cases/compiler/node_modules/nullthrows/nullthrows.d.ts ===
24+
declare function nullthrows(x: any): any;
25+
>nullthrows : typeof nullthrows
26+
>x : any
27+
28+
declare namespace nullthrows {
29+
>nullthrows : typeof nullthrows
30+
31+
export {nullthrows as default};
32+
>nullthrows : typeof nullthrows
33+
>default : typeof nullthrows
34+
}
35+
export = nullthrows;
36+
>nullthrows : typeof nullthrows
37+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// @noImplicitReferences: true
2+
// @module: nodenext
3+
// @outDir: esm
4+
// @filename: node_modules/@types/ip/package.json
5+
{
6+
"name": "@types/ip",
7+
"version": "1.1.0",
8+
"main": "",
9+
"types": "index"
10+
}
11+
// @filename: node_modules/@types/ip/index.d.ts
12+
export function address(): string;
13+
// @filename: node_modules/nullthrows/package.json
14+
{
15+
"name": "nullthrows",
16+
"version": "1.1.1",
17+
"main": "nullthrows.js",
18+
"types": "nullthrows.d.ts"
19+
}
20+
// @filename: node_modules/nullthrows/nullthrows.d.ts
21+
declare function nullthrows(x: any): any;
22+
declare namespace nullthrows {
23+
export {nullthrows as default};
24+
}
25+
export = nullthrows;
26+
// @filename: package.json
27+
{
28+
"type": "module"
29+
}
30+
// @filename: index.ts
31+
import * as ip from 'ip';
32+
import nullthrows from 'nullthrows'; // shouldn't be callable, `nullthrows` is a cjs package, so the `default` is the module itself
33+
34+
export function getAddress(): string {
35+
return nullthrows(ip.address());
36+
}

0 commit comments

Comments
 (0)