Skip to content

Commit e9868e9

Browse files
authored
--moduleResolution bundler: Require ESM for module and remove node from hard-coded conditions (#52940)
1 parent 3f7bf69 commit e9868e9

File tree

78 files changed

+635
-209
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

78 files changed

+635
-209
lines changed

src/compiler/checker.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -5019,7 +5019,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
50195019
* @see https://github.com/microsoft/TypeScript/issues/42151
50205020
*/
50215021
if (emitModuleKindIsNonNodeESM(moduleKind) || mode === ModuleKind.ESNext) {
5022-
return importSourceWithoutExtension + (tsExtension === Extension.Mts ? ".mjs" : tsExtension === Extension.Cts ? ".cjs" : ".js");
5022+
const preferTs = isDeclarationFileName(moduleReference) && shouldAllowImportingTsExtension(compilerOptions);
5023+
const ext =
5024+
tsExtension === Extension.Mts || tsExtension === Extension.Dmts ? preferTs ? ".mts" : ".mjs" :
5025+
tsExtension === Extension.Cts || tsExtension === Extension.Dmts ? preferTs ? ".cts" : ".cjs" :
5026+
preferTs ? ".ts" : ".js";
5027+
return importSourceWithoutExtension + ext;
50235028
}
50245029
return importSourceWithoutExtension;
50255030
}
@@ -43963,9 +43968,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4396343968
// Import equals declaration is deprecated in es6 or above
4396443969
grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead);
4396543970
}
43966-
else if (!(node.flags & NodeFlags.Ambient) && getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Bundler) {
43967-
grammarErrorOnNode(node, Diagnostics.Import_assignment_is_not_allowed_when_moduleResolution_is_set_to_bundler_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead);
43968-
}
4396943971
}
4397043972
}
4397143973
}
@@ -44208,9 +44210,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4420844210
// system modules does not support export assignment
4420944211
grammarErrorOnNode(node, Diagnostics.Export_assignment_is_not_supported_when_module_flag_is_system);
4421044212
}
44211-
else if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Bundler && !(node.flags & NodeFlags.Ambient)) {
44212-
grammarErrorOnNode(node, Diagnostics.Export_assignment_cannot_be_used_when_moduleResolution_is_set_to_bundler_Consider_using_export_default_or_another_module_format_instead);
44213-
}
4421444213
}
4421544214
}
4421644215

src/compiler/diagnosticMessages.json

+1-9
Original file line numberDiff line numberDiff line change
@@ -4265,7 +4265,7 @@
42654265
"category": "Error",
42664266
"code": 5094
42674267
},
4268-
"Option 'preserveValueImports' can only be used when 'module' is set to 'es2015' or later.": {
4268+
"Option '{0}' can only be used when 'module' is set to 'es2015' or later.": {
42694269
"category": "Error",
42704270
"code": 5095
42714271
},
@@ -4281,14 +4281,6 @@
42814281
"category": "Error",
42824282
"code": 5098
42834283
},
4284-
"Import assignment is not allowed when 'moduleResolution' is set to 'bundler'. Consider using 'import * as ns from \"mod\"', 'import {a} from \"mod\"', 'import d from \"mod\"', or another module format instead.": {
4285-
"category": "Error",
4286-
"code": 5099
4287-
},
4288-
"Export assignment cannot be used when 'moduleResolution' is set to 'bundler'. Consider using 'export default' or another module format instead.": {
4289-
"category": "Error",
4290-
"code": 5100
4291-
},
42924284
"Flag '{0}' is deprecated and will stop functioning in TypeScript {1}. Specify compilerOption '\"ignoreDeprecations\": \"{2}\"' to silence this error.": {
42934285
"category": "Error",
42944286
"code": 5101

src/compiler/moduleNameResolver.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import {
7070
ModuleResolutionHost,
7171
ModuleResolutionKind,
7272
moduleResolutionOptionDeclarations,
73+
moduleResolutionSupportsPackageJsonExportsAndImports,
7374
noop,
7475
noopPush,
7576
normalizePath,
@@ -705,11 +706,14 @@ export function getConditions(options: CompilerOptions, esmMode?: boolean) {
705706
// conditions are only used by the node16/nodenext/bundler resolvers - there's no priority order in the list,
706707
// it's essentially a set (priority is determined by object insertion order in the object we look at).
707708
const conditions = esmMode || getEmitModuleResolutionKind(options) === ModuleResolutionKind.Bundler
708-
? ["node", "import"]
709-
: ["node", "require"];
709+
? ["import"]
710+
: ["require"];
710711
if (!options.noDtsResolution) {
711712
conditions.push("types");
712713
}
714+
if (getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Bundler) {
715+
conditions.push("node");
716+
}
713717
return concatenate(conditions, options.customConditions);
714718
}
715719

@@ -1701,7 +1705,7 @@ function nodeModuleNameResolverWorker(features: NodeResolutionFeatures, moduleNa
17011705
candidateIsFromPackageJsonField: false,
17021706
};
17031707

1704-
if (traceEnabled && getEmitModuleResolutionKind(compilerOptions) >= ModuleResolutionKind.Node16 && getEmitModuleResolutionKind(compilerOptions) <= ModuleResolutionKind.NodeNext) {
1708+
if (traceEnabled && moduleResolutionSupportsPackageJsonExportsAndImports(getEmitModuleResolutionKind(compilerOptions))) {
17051709
trace(host, Diagnostics.Resolving_in_0_mode_with_conditions_1, features & NodeResolutionFeatures.EsmMode ? "ESM" : "CJS", conditions.map(c => `'${c}'`).join(", "));
17061710
}
17071711

src/compiler/program.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import {
6969
DirectoryStructureHost,
7070
emitFiles,
7171
EmitHost,
72+
emitModuleKindIsNonNodeESM,
7273
EmitOnly,
7374
EmitResult,
7475
emptyArray,
@@ -4241,11 +4242,11 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
42414242
}
42424243

42434244
if (options.preserveValueImports && getEmitModuleKind(options) < ModuleKind.ES2015) {
4244-
createDiagnosticForOptionName(Diagnostics.Option_preserveValueImports_can_only_be_used_when_module_is_set_to_es2015_or_later, "preserveValueImports");
4245+
createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_module_is_set_to_es2015_or_later, "preserveValueImports");
42454246
}
42464247

4248+
const moduleKind = getEmitModuleKind(options);
42474249
if (options.verbatimModuleSyntax) {
4248-
const moduleKind = getEmitModuleKind(options);
42494250
if (moduleKind === ModuleKind.AMD || moduleKind === ModuleKind.UMD || moduleKind === ModuleKind.System) {
42504251
createDiagnosticForOptionName(Diagnostics.Option_verbatimModuleSyntax_cannot_be_used_when_module_is_set_to_UMD_AMD_or_System, "verbatimModuleSyntax");
42514252
}
@@ -4266,13 +4267,17 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
42664267

42674268
const moduleResolution = getEmitModuleResolutionKind(options);
42684269
if (options.resolvePackageJsonExports && !moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution)) {
4269-
createOptionValueDiagnostic("resolvePackageJsonExports", Diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "resolvePackageJsonExports");
4270+
createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "resolvePackageJsonExports");
42704271
}
42714272
if (options.resolvePackageJsonImports && !moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution)) {
4272-
createOptionValueDiagnostic("resolvePackageJsonImports", Diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "resolvePackageJsonImports");
4273+
createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "resolvePackageJsonImports");
42734274
}
42744275
if (options.customConditions && !moduleResolutionSupportsPackageJsonExportsAndImports(moduleResolution)) {
4275-
createOptionValueDiagnostic("customConditions", Diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "customConditions");
4276+
createDiagnosticForOptionName(Diagnostics.Option_0_can_only_be_used_when_moduleResolution_is_set_to_node16_nodenext_or_bundler, "customConditions");
4277+
}
4278+
4279+
if (moduleResolution === ModuleResolutionKind.Bundler && !emitModuleKindIsNonNodeESM(moduleKind)) {
4280+
createOptionValueDiagnostic("moduleResolution", Diagnostics.Option_0_can_only_be_used_when_module_is_set_to_es2015_or_later, "bundler");
42764281
}
42774282

42784283
// If the emit is enabled make sure that every output file is unique and not overwriting any of the input files

tests/baselines/reference/allowImportingTsExtensions(moduleresolution=bundler).errors.txt

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
error TS5095: Option 'bundler' can only be used when 'module' is set to 'es2015' or later.
12
/c.ts(1,16): error TS2307: Cannot find module './thisfiledoesnotexist.ts' or its corresponding type declarations.
23

34

5+
!!! error TS5095: Option 'bundler' can only be used when 'module' is set to 'es2015' or later.
46
==== /ts.ts (0 errors) ====
57
export {};
68

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
error TS6504: File '/node_modules/conditions/index.node.js' is a JavaScript file. Did you mean to enable the 'allowJs' option?
2+
The file is in the program because:
3+
Root file specified for compilation
4+
error TS6504: File '/node_modules/conditions/index.web.js' is a JavaScript file. Did you mean to enable the 'allowJs' option?
5+
The file is in the program because:
6+
Root file specified for compilation
7+
8+
9+
!!! error TS6504: File '/node_modules/conditions/index.node.js' is a JavaScript file. Did you mean to enable the 'allowJs' option?
10+
!!! error TS6504: The file is in the program because:
11+
!!! error TS6504: Root file specified for compilation
12+
!!! error TS6504: File '/node_modules/conditions/index.web.js' is a JavaScript file. Did you mean to enable the 'allowJs' option?
13+
!!! error TS6504: The file is in the program because:
14+
!!! error TS6504: Root file specified for compilation
15+
==== /node_modules/conditions/package.json (0 errors) ====
16+
{
17+
"name": "conditions",
18+
"version": "1.0.0",
19+
"type": "module",
20+
"main": "index.cjs",
21+
"types": "index.d.cts",
22+
"exports": {
23+
".": {
24+
"node": "./index.node.js",
25+
"default": "./index.web.js"
26+
}
27+
}
28+
}
29+
30+
==== /node_modules/conditions/index.node.js (0 errors) ====
31+
export const node = 0;
32+
33+
==== /node_modules/conditions/index.node.d.ts (0 errors) ====
34+
export const node: number;
35+
36+
==== /node_modules/conditions/index.web.js (0 errors) ====
37+
export const web = 0;
38+
39+
==== /node_modules/conditions/index.web.d.ts (0 errors) ====
40+
export const web: number;
41+
42+
==== /main.ts (0 errors) ====
43+
import { web } from "conditions";
44+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//// [tests/cases/conformance/moduleResolution/bundler/bundlerConditionsExcludesNode.ts] ////
2+
3+
//// [package.json]
4+
{
5+
"name": "conditions",
6+
"version": "1.0.0",
7+
"type": "module",
8+
"main": "index.cjs",
9+
"types": "index.d.cts",
10+
"exports": {
11+
".": {
12+
"node": "./index.node.js",
13+
"default": "./index.web.js"
14+
}
15+
}
16+
}
17+
18+
//// [index.node.js]
19+
export const node = 0;
20+
21+
//// [index.node.d.ts]
22+
export const node: number;
23+
24+
//// [index.web.js]
25+
export const web = 0;
26+
27+
//// [index.web.d.ts]
28+
export const web: number;
29+
30+
//// [main.ts]
31+
import { web } from "conditions";
32+
33+
34+
//// [main.js]
35+
export {};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
=== /node_modules/conditions/index.node.d.ts ===
2+
export const node: number;
3+
>node : Symbol(node, Decl(index.node.d.ts, 0, 12))
4+
5+
=== /node_modules/conditions/index.web.d.ts ===
6+
export const web: number;
7+
>web : Symbol(web, Decl(index.web.d.ts, 0, 12))
8+
9+
=== /main.ts ===
10+
import { web } from "conditions";
11+
>web : Symbol(web, Decl(main.ts, 0, 8))
12+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
[
2+
"======== Resolving module 'conditions' from '/main.ts'. ========",
3+
"Explicitly specified module resolution kind: 'Bundler'.",
4+
"Resolving in CJS mode with conditions 'import', 'types'.",
5+
"File '/package.json' does not exist.",
6+
"Loading module 'conditions' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration, JSON.",
7+
"Found 'package.json' at '/node_modules/conditions/package.json'.",
8+
"Entering conditional exports.",
9+
"Saw non-matching condition 'node'.",
10+
"Matched 'exports' condition 'default'.",
11+
"Using 'exports' subpath '.' with target './index.web.js'.",
12+
"File name '/node_modules/conditions/index.web.js' has a '.js' extension - stripping it.",
13+
"File '/node_modules/conditions/index.web.ts' does not exist.",
14+
"File '/node_modules/conditions/index.web.tsx' does not exist.",
15+
"File '/node_modules/conditions/index.web.d.ts' exists - use it as a name resolution result.",
16+
"Resolved under condition 'default'.",
17+
"Exiting conditional exports.",
18+
"Resolving real path for '/node_modules/conditions/index.web.d.ts', result '/node_modules/conditions/index.web.d.ts'.",
19+
"======== Module name 'conditions' was successfully resolved to '/node_modules/conditions/index.web.d.ts' with Package ID 'conditions/[email protected]'. ========"
20+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
=== /node_modules/conditions/index.node.d.ts ===
2+
export const node: number;
3+
>node : number
4+
5+
=== /node_modules/conditions/index.web.d.ts ===
6+
export const web: number;
7+
>web : number
8+
9+
=== /main.ts ===
10+
import { web } from "conditions";
11+
>web : number
12+

tests/baselines/reference/bundlerImportESM.js

+3-8
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,8 @@ import { esm } from "./esm.mjs";
1414

1515

1616
//// [esm.mjs]
17-
"use strict";
18-
Object.defineProperty(exports, "__esModule", { value: true });
19-
exports.esm = void 0;
20-
exports.esm = 0;
17+
export var esm = 0;
2118
//// [not-actually-cjs.cjs]
22-
"use strict";
23-
Object.defineProperty(exports, "__esModule", { value: true });
19+
export {};
2420
//// [still-not-cjs.js]
25-
"use strict";
26-
Object.defineProperty(exports, "__esModule", { value: true });
21+
export {};

tests/baselines/reference/bundlerImportTsExtensions(allowimportingtsextensions=false,noemit=false).errors.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ error TS6054: File '/project/e.txt' has an unsupported extension. The only suppo
55
Root file specified for compilation
66
/project/main.ts(3,16): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
77
/project/main.ts(7,16): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
8-
/project/main.ts(8,16): error TS2846: A declaration file cannot be imported without 'import type'. Did you mean to import an implementation file './b' instead?
8+
/project/main.ts(8,16): error TS2846: A declaration file cannot be imported without 'import type'. Did you mean to import an implementation file './b.js' instead?
99
/project/main.ts(11,16): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
1010
/project/main.ts(12,16): error TS5097: An import path can only end with a '.tsx' extension when 'allowImportingTsExtensions' is enabled.
1111
/project/main.ts(12,16): error TS6142: Module './c.tsx' was resolved to '/project/c.tsx', but '--jsx' is not set.
1212
/project/main.ts(16,16): error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
13-
/project/types.d.ts(2,16): error TS2846: A declaration file cannot be imported without 'import type'. Did you mean to import an implementation file './a' instead?
13+
/project/types.d.ts(2,16): error TS2846: A declaration file cannot be imported without 'import type'. Did you mean to import an implementation file './a.js' instead?
1414

1515

1616
!!! error TS5056: Cannot write file 'out/b.js' because it would be overwritten by multiple input files.
@@ -68,7 +68,7 @@ error TS6054: File '/project/e.txt' has an unsupported extension. The only suppo
6868
!!! error TS5097: An import path can only end with a '.ts' extension when 'allowImportingTsExtensions' is enabled.
6969
import {} from "./b.d.ts";
7070
~~~~~~~~~~
71-
!!! error TS2846: A declaration file cannot be imported without 'import type'. Did you mean to import an implementation file './b' instead?
71+
!!! error TS2846: A declaration file cannot be imported without 'import type'. Did you mean to import an implementation file './b.js' instead?
7272
import type {} from "./b.d.ts";
7373

7474
import {} from "./c.ts";
@@ -96,5 +96,5 @@ error TS6054: File '/project/e.txt' has an unsupported extension. The only suppo
9696
import {} from "./a.ts";
9797
import {} from "./a.d.ts";
9898
~~~~~~~~~~
99-
!!! error TS2846: A declaration file cannot be imported without 'import type'. Did you mean to import an implementation file './a' instead?
99+
!!! error TS2846: A declaration file cannot be imported without 'import type'. Did you mean to import an implementation file './a.js' instead?
100100
import type {} from "./a.d.ts";

tests/baselines/reference/bundlerImportTsExtensions(allowimportingtsextensions=false,noemit=false).js

+5-10
Original file line numberDiff line numberDiff line change
@@ -66,17 +66,12 @@ import {} from "./a.d.ts";
6666
import type {} from "./a.d.ts";
6767

6868
//// [a.js]
69-
"use strict";
70-
Object.defineProperty(exports, "__esModule", { value: true });
69+
export {};
7170
//// [index.js]
72-
"use strict";
73-
Object.defineProperty(exports, "__esModule", { value: true });
71+
export {};
7472
//// [e.js]
75-
"use strict";
76-
Object.defineProperty(exports, "__esModule", { value: true });
73+
export {};
7774
//// [e.txt.js]
78-
"use strict";
79-
Object.defineProperty(exports, "__esModule", { value: true });
75+
export {};
8076
//// [main.js]
81-
"use strict";
82-
Object.defineProperty(exports, "__esModule", { value: true });
77+
export {};

0 commit comments

Comments
 (0)