Skip to content

Commit 615a82c

Browse files
committed
Make specifier generation from export map information conditional on module resolution mode
1 parent e2f47a2 commit 615a82c

5 files changed

+175
-7
lines changed

src/compiler/moduleSpecifiers.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -707,13 +707,15 @@ namespace ts.moduleSpecifiers {
707707
if (host.fileExists(packageJsonPath)) {
708708
const packageJsonContent = JSON.parse(host.readFile!(packageJsonPath)!);
709709
// TODO: Inject `require` or `import` condition based on the intended import mode
710-
const fromExports = packageJsonContent.exports && typeof packageJsonContent.name === "string" ? tryGetModuleNameFromExports(options, path, packageRootPath, packageJsonContent.name, packageJsonContent.exports, ["node", "types"]) : undefined;
711-
if (fromExports) {
712-
const withJsExtension = !hasTSFileExtension(fromExports.moduleFileToTry) ? fromExports : { moduleFileToTry: removeFileExtension(fromExports.moduleFileToTry) + tryGetJSExtensionForFile(fromExports.moduleFileToTry, options) };
713-
return { ...withJsExtension, verbatimFromExports: true };
714-
}
715-
if (packageJsonContent.exports) {
716-
return { moduleFileToTry: path, blockedByExports: true };
710+
if (getEmitModuleResolutionKind(options) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(options) === ModuleResolutionKind.NodeNext) {
711+
const fromExports = packageJsonContent.exports && typeof packageJsonContent.name === "string" ? tryGetModuleNameFromExports(options, path, packageRootPath, packageJsonContent.name, packageJsonContent.exports, ["node", "types"]) : undefined;
712+
if (fromExports) {
713+
const withJsExtension = !hasTSFileExtension(fromExports.moduleFileToTry) ? fromExports : { moduleFileToTry: removeFileExtension(fromExports.moduleFileToTry) + tryGetJSExtensionForFile(fromExports.moduleFileToTry, options) };
714+
return { ...withJsExtension, verbatimFromExports: true };
715+
}
716+
if (packageJsonContent.exports) {
717+
return { moduleFileToTry: path, blockedByExports: true };
718+
}
717719
}
718720
const versionPaths = packageJsonContent.typesVersions
719721
? getPackageJsonTypesVersionsPaths(packageJsonContent.typesVersions)
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//// [tests/cases/conformance/node/legacyNodeModulesExportsSpecifierGenerationConditions.ts] ////
2+
3+
//// [index.ts]
4+
export const a = async () => (await import("inner")).x();
5+
//// [index.d.ts]
6+
export { x } from "./other.js";
7+
//// [other.d.ts]
8+
import { Thing } from "./private.js"
9+
export const x: () => Thing;
10+
//// [private.d.ts]
11+
export interface Thing {} // not exported in export map, inaccessible under new module modes
12+
//// [package.json]
13+
{
14+
"name": "package",
15+
"private": true,
16+
"type": "module",
17+
"exports": "./index.js"
18+
}
19+
//// [package.json]
20+
{
21+
"name": "inner",
22+
"private": true,
23+
"type": "module",
24+
"exports": {
25+
".": {
26+
"default": "./index.js"
27+
},
28+
"./other": {
29+
"default": "./other.js"
30+
}
31+
}
32+
}
33+
34+
//// [index.js]
35+
"use strict";
36+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
37+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
38+
return new (P || (P = Promise))(function (resolve, reject) {
39+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
40+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
41+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
42+
step((generator = generator.apply(thisArg, _arguments || [])).next());
43+
});
44+
};
45+
var __generator = (this && this.__generator) || function (thisArg, body) {
46+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
47+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
48+
function verb(n) { return function (v) { return step([n, v]); }; }
49+
function step(op) {
50+
if (f) throw new TypeError("Generator is already executing.");
51+
while (_) try {
52+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
53+
if (y = 0, t) op = [op[0] & 2, t.value];
54+
switch (op[0]) {
55+
case 0: case 1: t = op; break;
56+
case 4: _.label++; return { value: op[1], done: false };
57+
case 5: _.label++; y = op[1]; op = [0]; continue;
58+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
59+
default:
60+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
61+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
62+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
63+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
64+
if (t[2]) _.ops.pop();
65+
_.trys.pop(); continue;
66+
}
67+
op = body.call(thisArg, _);
68+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
69+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
70+
}
71+
};
72+
exports.__esModule = true;
73+
exports.a = void 0;
74+
var a = function () { return __awaiter(void 0, void 0, void 0, function () { return __generator(this, function (_a) {
75+
switch (_a.label) {
76+
case 0: return [4 /*yield*/, Promise.resolve().then(function () { return require("inner"); })];
77+
case 1: return [2 /*return*/, (_a.sent()).x()];
78+
}
79+
}); }); };
80+
exports.a = a;
81+
82+
83+
//// [index.d.ts]
84+
export declare const a: () => Promise<import("inner/private").Thing>;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/conformance/node/index.ts ===
2+
export const a = async () => (await import("inner")).x();
3+
>a : Symbol(a, Decl(index.ts, 0, 12))
4+
>(await import("inner")).x : Symbol(x, Decl(index.d.ts, 0, 8))
5+
>"inner" : Symbol("tests/cases/conformance/node/node_modules/inner/index", Decl(index.d.ts, 0, 0))
6+
>x : Symbol(x, Decl(index.d.ts, 0, 8))
7+
8+
=== tests/cases/conformance/node/node_modules/inner/index.d.ts ===
9+
export { x } from "./other.js";
10+
>x : Symbol(x, Decl(index.d.ts, 0, 8))
11+
12+
=== tests/cases/conformance/node/node_modules/inner/other.d.ts ===
13+
import { Thing } from "./private.js"
14+
>Thing : Symbol(Thing, Decl(other.d.ts, 0, 8))
15+
16+
export const x: () => Thing;
17+
>x : Symbol(x, Decl(other.d.ts, 1, 12))
18+
>Thing : Symbol(Thing, Decl(other.d.ts, 0, 8))
19+
20+
=== tests/cases/conformance/node/node_modules/inner/private.d.ts ===
21+
export interface Thing {} // not exported in export map, inaccessible under new module modes
22+
>Thing : Symbol(Thing, Decl(private.d.ts, 0, 0))
23+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
=== tests/cases/conformance/node/index.ts ===
2+
export const a = async () => (await import("inner")).x();
3+
>a : () => Promise<import("tests/cases/conformance/node/node_modules/inner/private").Thing>
4+
>async () => (await import("inner")).x() : () => Promise<import("tests/cases/conformance/node/node_modules/inner/private").Thing>
5+
>(await import("inner")).x() : import("tests/cases/conformance/node/node_modules/inner/private").Thing
6+
>(await import("inner")).x : () => import("tests/cases/conformance/node/node_modules/inner/private").Thing
7+
>(await import("inner")) : typeof import("tests/cases/conformance/node/node_modules/inner/index")
8+
>await import("inner") : typeof import("tests/cases/conformance/node/node_modules/inner/index")
9+
>import("inner") : Promise<typeof import("tests/cases/conformance/node/node_modules/inner/index")>
10+
>"inner" : "inner"
11+
>x : () => import("tests/cases/conformance/node/node_modules/inner/private").Thing
12+
13+
=== tests/cases/conformance/node/node_modules/inner/index.d.ts ===
14+
export { x } from "./other.js";
15+
>x : () => import("tests/cases/conformance/node/node_modules/inner/private").Thing
16+
17+
=== tests/cases/conformance/node/node_modules/inner/other.d.ts ===
18+
import { Thing } from "./private.js"
19+
>Thing : any
20+
21+
export const x: () => Thing;
22+
>x : () => Thing
23+
24+
=== tests/cases/conformance/node/node_modules/inner/private.d.ts ===
25+
export interface Thing {} // not exported in export map, inaccessible under new module modes
26+
No type information for this code.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// @module: commonjs
2+
// @lib: es2020
3+
// @declaration: true
4+
// @filename: index.ts
5+
export const a = async () => (await import("inner")).x();
6+
// @filename: node_modules/inner/index.d.ts
7+
export { x } from "./other.js";
8+
// @filename: node_modules/inner/other.d.ts
9+
import { Thing } from "./private.js"
10+
export const x: () => Thing;
11+
// @filename: node_modules/inner/private.d.ts
12+
export interface Thing {} // not exported in export map, inaccessible under new module modes
13+
// @filename: package.json
14+
{
15+
"name": "package",
16+
"private": true,
17+
"type": "module",
18+
"exports": "./index.js"
19+
}
20+
// @filename: node_modules/inner/package.json
21+
{
22+
"name": "inner",
23+
"private": true,
24+
"type": "module",
25+
"exports": {
26+
".": {
27+
"default": "./index.js"
28+
},
29+
"./other": {
30+
"default": "./other.js"
31+
}
32+
}
33+
}

0 commit comments

Comments
 (0)