Skip to content

Commit 1b70ac3

Browse files
authored
Support resolution-mode overrides in type-only constructs in all moduleResolution modes (#55725)
1 parent 2e9e9a2 commit 1b70ac3

File tree

41 files changed

+1708
-121
lines changed

Some content is hidden

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

41 files changed

+1708
-121
lines changed

src/compiler/checker.ts

+1-18
Original file line numberDiff line numberDiff line change
@@ -39766,16 +39766,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3976639766
checkSourceElement(node.argument);
3976739767

3976839768
if (node.attributes) {
39769-
const override = getResolutionModeOverride(node.attributes, grammarErrorOnNode);
39770-
const errorNode = node.attributes;
39771-
if (override && errorNode && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Node16 && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeNext) {
39772-
grammarErrorOnNode(
39773-
errorNode,
39774-
node.attributes.token === SyntaxKind.WithKeyword
39775-
? Diagnostics.The_resolution_mode_attribute_is_only_supported_when_moduleResolution_is_node16_or_nodenext
39776-
: Diagnostics.resolution_mode_assertions_are_only_supported_when_moduleResolution_is_node16_or_nodenext,
39777-
);
39778-
}
39769+
getResolutionModeOverride(node.attributes, grammarErrorOnNode);
3977939770
}
3978039771
checkTypeReferenceOrImport(node);
3978139772
}
@@ -45212,14 +45203,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4521245203
const override = getResolutionModeOverride(node, validForTypeAttributes ? grammarErrorOnNode : undefined);
4521345204
const isImportAttributes = declaration.attributes.token === SyntaxKind.WithKeyword;
4521445205
if (validForTypeAttributes && override) {
45215-
if (getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Node16 && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeNext) {
45216-
return grammarErrorOnNode(
45217-
node,
45218-
isImportAttributes
45219-
? Diagnostics.The_resolution_mode_attribute_is_only_supported_when_moduleResolution_is_node16_or_nodenext
45220-
: Diagnostics.resolution_mode_assertions_are_only_supported_when_moduleResolution_is_node16_or_nodenext,
45221-
);
45222-
}
4522345206
return; // Other grammar checks do not apply to type-only imports with resolution mode assertions
4522445207
}
4522545208

src/compiler/diagnosticMessages.json

-8
Original file line numberDiff line numberDiff line change
@@ -1480,10 +1480,6 @@
14801480
"category": "Error",
14811481
"code": 1451
14821482
},
1483-
"'resolution-mode' assertions are only supported when `moduleResolution` is `node16` or `nodenext`.": {
1484-
"category": "Error",
1485-
"code": 1452
1486-
},
14871483
"`resolution-mode` should be either `require` or `import`.": {
14881484
"category": "Error",
14891485
"code": 1453
@@ -1520,10 +1516,6 @@
15201516
"category": "Message",
15211517
"code": 1461
15221518
},
1523-
"The 'resolution-mode' attribute is only supported when 'moduleResolution' is 'node16' or 'nodenext'.": {
1524-
"category": "Error",
1525-
"code": 1462
1526-
},
15271519
"'resolution-mode' is the only valid key for type import attributes.": {
15281520
"category": "Error",
15291521
"code": 1463

src/compiler/moduleNameResolver.ts

+71-32
Large diffs are not rendered by default.

src/compiler/program.ts

+1-10
Original file line numberDiff line numberDiff line change
@@ -865,7 +865,6 @@ export function getModeForResolutionAtIndex(file: SourceFile, index: number): Re
865865
// eslint-disable-next-line @typescript-eslint/unified-signatures
866866
export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number): ResolutionMode;
867867
export function getModeForResolutionAtIndex(file: SourceFileImportsList, index: number): ResolutionMode {
868-
if (file.impliedNodeFormat === undefined) return undefined;
869868
// we ensure all elements of file.imports and file.moduleAugmentations have the relevant parent pointers set during program setup,
870869
// so it's safe to use them even pre-bind
871870
return getModeForUsageLocation(file, getModuleNameStringLiteralAt(file, index));
@@ -892,7 +891,6 @@ export function isExclusivelyTypeOnlyImportOrExport(decl: ImportDeclaration | Ex
892891
* @returns The final resolution mode of the import
893892
*/
894893
export function getModeForUsageLocation(file: { impliedNodeFormat?: ResolutionMode; }, usage: StringLiteralLike) {
895-
if (file.impliedNodeFormat === undefined) return undefined;
896894
if ((isImportDeclaration(usage.parent) || isExportDeclaration(usage.parent))) {
897895
const isTypeOnly = isExclusivelyTypeOnlyImportOrExport(usage.parent);
898896
if (isTypeOnly) {
@@ -908,6 +906,7 @@ export function getModeForUsageLocation(file: { impliedNodeFormat?: ResolutionMo
908906
return override;
909907
}
910908
}
909+
if (file.impliedNodeFormat === undefined) return undefined;
911910
if (file.impliedNodeFormat !== ModuleKind.ESNext) {
912911
// in cjs files, import call expressions are esm format, otherwise everything is cjs
913912
return isImportCall(walkUpParenthesizedExpressions(usage.parent)) ? ModuleKind.ESNext : ModuleKind.CommonJS;
@@ -3863,14 +3862,6 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg
38633862
const fileName = toFileNameLowerCase(ref.fileName);
38643863
resolutionsInFile.set(fileName, getModeForFileReference(ref, file.impliedNodeFormat), resolvedTypeReferenceDirective);
38653864
const mode = ref.resolutionMode || file.impliedNodeFormat;
3866-
if (mode && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.Node16 && getEmitModuleResolutionKind(options) !== ModuleResolutionKind.NodeNext) {
3867-
(fileProcessingDiagnostics ??= []).push({
3868-
kind: FilePreprocessingDiagnosticsKind.ResolutionDiagnostics,
3869-
diagnostics: [
3870-
createDiagnosticForRange(file, ref, Diagnostics.resolution_mode_assertions_are_only_supported_when_moduleResolution_is_node16_or_nodenext),
3871-
],
3872-
});
3873-
}
38743865
processTypeReferenceDirective(fileName, mode, resolvedTypeReferenceDirective, { kind: FileIncludeKind.TypeReferenceDirective, file: file.path, index });
38753866
}
38763867
}

tests/baselines/reference/nodeModulesTripleSlashReferenceModeOverrideOldResolutionError.errors.txt

-45
This file was deleted.

tests/baselines/reference/nodeModulesTripleSlashReferenceModeOverrideOldResolutionError.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ declare global {
2222
//// [index.ts]
2323
/// <reference types="pkg" resolution-mode="require" />
2424
/// <reference types="pkg" resolution-mode="import" />
25-
foo; // `resolution-mode` is an error in old resolution settings, which resolves is arbitrary
25+
foo;
2626
bar;
2727
export {};
2828

@@ -31,5 +31,5 @@ export {};
3131
Object.defineProperty(exports, "__esModule", { value: true });
3232
/// <reference types="pkg" resolution-mode="require" />
3333
/// <reference types="pkg" resolution-mode="import" />
34-
foo; // `resolution-mode` is an error in old resolution settings, which resolves is arbitrary
34+
foo;
3535
bar;
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,28 @@
11
//// [tests/cases/conformance/node/nodeModulesTripleSlashReferenceModeOverrideOldResolutionError.ts] ////
22

33
=== /index.ts ===
4-
54
/// <reference types="pkg" resolution-mode="require" />
65
/// <reference types="pkg" resolution-mode="import" />
7-
foo; // `resolution-mode` is an error in old resolution settings, which resolves is arbitrary
6+
foo;
7+
>foo : Symbol(foo, Decl(import.d.ts, 2, 7))
8+
89
bar;
10+
>bar : Symbol(bar, Decl(require.d.ts, 2, 7))
11+
12+
export {};
13+
=== /node_modules/pkg/import.d.ts ===
914
export {};
15+
declare global {
16+
>global : Symbol(global, Decl(import.d.ts, 0, 10))
17+
18+
var foo: number;
19+
>foo : Symbol(foo, Decl(import.d.ts, 2, 7))
20+
}
21+
=== /node_modules/pkg/require.d.ts ===
22+
export {};
23+
declare global {
24+
>global : Symbol(global, Decl(require.d.ts, 0, 10))
25+
26+
var bar: number;
27+
>bar : Symbol(bar, Decl(require.d.ts, 2, 7))
28+
}

tests/baselines/reference/nodeModulesTripleSlashReferenceModeOverrideOldResolutionError.types

+19-3
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,26 @@
33
=== /index.ts ===
44
/// <reference types="pkg" resolution-mode="require" />
55
/// <reference types="pkg" resolution-mode="import" />
6-
foo; // `resolution-mode` is an error in old resolution settings, which resolves is arbitrary
7-
>foo : any
6+
foo;
7+
>foo : number
88

99
bar;
10-
>bar : any
10+
>bar : number
1111

1212
export {};
13+
=== /node_modules/pkg/import.d.ts ===
14+
export {};
15+
declare global {
16+
>global : typeof global
17+
18+
var foo: number;
19+
>foo : number
20+
}
21+
=== /node_modules/pkg/require.d.ts ===
22+
export {};
23+
declare global {
24+
>global : typeof global
25+
26+
var bar: number;
27+
>bar : number
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//// [tests/cases/conformance/moduleResolution/resolutionModeImportType1.ts] ////
2+
3+
=== /node_modules/@types/foo/index.d.mts ===
4+
export declare const x: "module";
5+
>x : Symbol(x, Decl(index.d.mts, 0, 20))
6+
7+
=== /node_modules/@types/foo/index.d.cts ===
8+
export declare const x: "script";
9+
>x : Symbol(x, Decl(index.d.cts, 0, 20))
10+
11+
=== /app.ts ===
12+
type Default = typeof import("foo").x;
13+
>Default : Symbol(Default, Decl(app.ts, 0, 0))
14+
>x : Symbol(x, Decl(index.d.mts, 0, 20))
15+
16+
type Import = typeof import("foo", { assert: { "resolution-mode": "import" } }).x;
17+
>Import : Symbol(Import, Decl(app.ts, 0, 38))
18+
>x : Symbol(x, Decl(index.d.mts, 0, 20))
19+
20+
type Require = typeof import("foo", { assert: { "resolution-mode": "require" } }).x;
21+
>Require : Symbol(Require, Decl(app.ts, 1, 82))
22+
>x : Symbol(x, Decl(index.d.cts, 0, 20))
23+
24+
// resolution-mode does not enforce file extension in `bundler`, just sets conditions
25+
type ImportRelative = typeof import("./other", { assert: { "resolution-mode": "import" } }).x;
26+
>ImportRelative : Symbol(ImportRelative, Decl(app.ts, 2, 84))
27+
>x : Symbol(x, Decl(other.ts, 0, 12))
28+
29+
type RequireRelative = typeof import("./other", { assert: { "resolution-mode": "require" } }).x;
30+
>RequireRelative : Symbol(RequireRelative, Decl(app.ts, 4, 94))
31+
>x : Symbol(x, Decl(other.ts, 0, 12))
32+
33+
=== /other.ts ===
34+
export const x = "other";
35+
>x : Symbol(x, Decl(other.ts, 0, 12))
36+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [tests/cases/conformance/moduleResolution/resolutionModeImportType1.ts] ////
2+
3+
=== /node_modules/@types/foo/index.d.mts ===
4+
export declare const x: "module";
5+
>x : "module"
6+
7+
=== /node_modules/@types/foo/index.d.cts ===
8+
export declare const x: "script";
9+
>x : "script"
10+
11+
=== /app.ts ===
12+
type Default = typeof import("foo").x;
13+
>Default : "module"
14+
>x : error
15+
16+
type Import = typeof import("foo", { assert: { "resolution-mode": "import" } }).x;
17+
>Import : "module"
18+
>x : error
19+
20+
type Require = typeof import("foo", { assert: { "resolution-mode": "require" } }).x;
21+
>Require : "script"
22+
>x : error
23+
24+
// resolution-mode does not enforce file extension in `bundler`, just sets conditions
25+
type ImportRelative = typeof import("./other", { assert: { "resolution-mode": "import" } }).x;
26+
>ImportRelative : "other"
27+
>x : error
28+
29+
type RequireRelative = typeof import("./other", { assert: { "resolution-mode": "require" } }).x;
30+
>RequireRelative : "other"
31+
>x : error
32+
33+
=== /other.ts ===
34+
export const x = "other";
35+
>x : "other"
36+
>"other" : "other"
37+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
error TS2688: Cannot find type definition file for 'foo'.
2+
The file is in the program because:
3+
Entry point for implicit type library 'foo'
4+
/app.ts(1,30): error TS2792: Cannot find module 'foo'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?
5+
/app.ts(2,29): error TS2792: Cannot find module 'foo'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?
6+
/app.ts(3,30): error TS2792: Cannot find module 'foo'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?
7+
8+
9+
!!! error TS2688: Cannot find type definition file for 'foo'.
10+
!!! error TS2688: The file is in the program because:
11+
!!! error TS2688: Entry point for implicit type library 'foo'
12+
==== /node_modules/@types/foo/package.json (0 errors) ====
13+
{
14+
"name": "@types/foo",
15+
"version": "1.0.0",
16+
"exports": {
17+
".": {
18+
"import": "./index.d.mts",
19+
"require": "./index.d.cts"
20+
}
21+
}
22+
}
23+
24+
==== /node_modules/@types/foo/index.d.mts (0 errors) ====
25+
export declare const x: "module";
26+
27+
==== /node_modules/@types/foo/index.d.cts (0 errors) ====
28+
export declare const x: "script";
29+
30+
==== /app.ts (3 errors) ====
31+
type Default = typeof import("foo").x;
32+
~~~~~
33+
!!! error TS2792: Cannot find module 'foo'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?
34+
type Import = typeof import("foo", { assert: { "resolution-mode": "import" } }).x;
35+
~~~~~
36+
!!! error TS2792: Cannot find module 'foo'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?
37+
type Require = typeof import("foo", { assert: { "resolution-mode": "require" } }).x;
38+
~~~~~
39+
!!! error TS2792: Cannot find module 'foo'. Did you mean to set the 'moduleResolution' option to 'nodenext', or to add aliases to the 'paths' option?
40+
// resolution-mode does not enforce file extension in `bundler`, just sets conditions
41+
type ImportRelative = typeof import("./other", { assert: { "resolution-mode": "import" } }).x;
42+
type RequireRelative = typeof import("./other", { assert: { "resolution-mode": "require" } }).x;
43+
44+
==== /other.ts (0 errors) ====
45+
export const x = "other";
46+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [tests/cases/conformance/moduleResolution/resolutionModeImportType1.ts] ////
2+
3+
=== /node_modules/@types/foo/index.d.mts ===
4+
export declare const x: "module";
5+
>x : Symbol(x, Decl(index.d.mts, 0, 20))
6+
7+
=== /node_modules/@types/foo/index.d.cts ===
8+
export declare const x: "script";
9+
>x : Symbol(x, Decl(index.d.cts, 0, 20))
10+
11+
=== /app.ts ===
12+
type Default = typeof import("foo").x;
13+
>Default : Symbol(Default, Decl(app.ts, 0, 0))
14+
15+
type Import = typeof import("foo", { assert: { "resolution-mode": "import" } }).x;
16+
>Import : Symbol(Import, Decl(app.ts, 0, 38))
17+
18+
type Require = typeof import("foo", { assert: { "resolution-mode": "require" } }).x;
19+
>Require : Symbol(Require, Decl(app.ts, 1, 82))
20+
21+
// resolution-mode does not enforce file extension in `bundler`, just sets conditions
22+
type ImportRelative = typeof import("./other", { assert: { "resolution-mode": "import" } }).x;
23+
>ImportRelative : Symbol(ImportRelative, Decl(app.ts, 2, 84))
24+
>x : Symbol(x, Decl(other.ts, 0, 12))
25+
26+
type RequireRelative = typeof import("./other", { assert: { "resolution-mode": "require" } }).x;
27+
>RequireRelative : Symbol(RequireRelative, Decl(app.ts, 4, 94))
28+
>x : Symbol(x, Decl(other.ts, 0, 12))
29+
30+
=== /other.ts ===
31+
export const x = "other";
32+
>x : Symbol(x, Decl(other.ts, 0, 12))
33+

0 commit comments

Comments
 (0)