From b0031bdba3cfe81faaec0f4f711e7ea236742627 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 21 Aug 2024 13:28:40 -0700 Subject: [PATCH 1/5] Fix error message for type-only import of ES module from CJS --- src/compiler/checker.ts | 10 ++++----- src/compiler/diagnosticMessages.json | 8 +++++++ ...t-correctly-with-cts-and-mts-extensions.js | 4 ++-- .../typeOnlyESMImportFromCJS.errors.txt | 19 ++++++++++++++++ .../reference/typeOnlyESMImportFromCJS.js | 19 ++++++++++++++++ .../typeOnlyESMImportFromCJS.symbols | 19 ++++++++++++++++ .../reference/typeOnlyESMImportFromCJS.types | 22 +++++++++++++++++++ .../typeOnly/typeOnlyESMImportFromCJS.ts | 12 ++++++++++ 8 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt create mode 100644 tests/baselines/reference/typeOnlyESMImportFromCJS.js create mode 100644 tests/baselines/reference/typeOnlyESMImportFromCJS.symbols create mode 100644 tests/baselines/reference/typeOnlyESMImportFromCJS.types create mode 100644 tests/cases/conformance/externalModules/typeOnly/typeOnlyESMImportFromCJS.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d757890fa641e..8ddbf7bbd5065 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4639,14 +4639,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } } + + const message = overrideHost?.kind === SyntaxKind.ImportDeclaration && overrideHost.importClause?.isTypeOnly ? Diagnostics.Type_only_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_Colon_import_attribute : + overrideHost?.kind === SyntaxKind.ImportType ? Diagnostics.Type_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_Colon_import_attribute : + Diagnostics.The_current_file_is_a_CommonJS_module_whose_imports_will_produce_require_calls_however_the_referenced_file_is_an_ECMAScript_module_and_cannot_be_imported_with_require_Consider_writing_a_dynamic_import_0_call_instead; diagnostics.add(createDiagnosticForNodeFromMessageChain( getSourceFileOfNode(errorNode), errorNode, - chainDiagnosticMessages( - diagnosticDetails, - Diagnostics.The_current_file_is_a_CommonJS_module_whose_imports_will_produce_require_calls_however_the_referenced_file_is_an_ECMAScript_module_and_cannot_be_imported_with_require_Consider_writing_a_dynamic_import_0_call_instead, - moduleReference, - ), + chainDiagnosticMessages(diagnosticDetails, message, moduleReference), )); } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 272cd5bd4f524..8363c09d81607 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1649,6 +1649,14 @@ "category": "Error", "code": 1498 }, + "Type-only import of an ECMAScript module from a CommonJS module must have a `\"resolution-mode\": \"import\"` attribute.": { + "category": "Error", + "code": 1499 + }, + "Type import of an ECMAScript module from a CommonJS module must have a `\"resolution-mode\": \"import\"` attribute.": { + "category": "Error", + "code": 1500 + }, "The types of '{0}' are incompatible between these types.": { "category": "Error", diff --git a/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js b/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js index 03f926abaff58..a455ce1e0c75b 100644 --- a/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js +++ b/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js @@ -388,7 +388,7 @@ File '/user/username/projects/myproject/packages/pkg2/build/const.d.cts' exists File '/a/lib/package.json' does not exist. File '/a/package.json' does not exist. File '/package.json' does not exist. -packages/pkg1/index.ts:1:29 - error TS1479: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("pkg2")' call instead. +packages/pkg1/index.ts:1:29 - error TS1499: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. To convert this file to an ECMAScript module, change its file extension to '.mts' or create a local package.json file with `{ "type": "module" }`. 1 import type { TheNum } from 'pkg2' @@ -584,7 +584,7 @@ File '/user/username/projects/myproject/packages/pkg2/build/const.d.cts' exists File '/a/lib/package.json' does not exist. File '/a/package.json' does not exist. File '/package.json' does not exist. -packages/pkg1/index.ts:1:29 - error TS1479: The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("pkg2")' call instead. +packages/pkg1/index.ts:1:29 - error TS1499: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. To convert this file to an ECMAScript module, change its file extension to '.mts' or create a local package.json file with `{ "type": "module" }`. 1 import type { TheNum } from 'pkg2' diff --git a/tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt b/tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt new file mode 100644 index 0000000000000..390122aee9360 --- /dev/null +++ b/tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt @@ -0,0 +1,19 @@ +common.cts(1,21): error TS1499: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +common.cts(4,25): error TS1500: Type import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. + + +==== module.mts (0 errors) ==== + export {}; + +==== common.cts (2 errors) ==== + import type {} from "./module.mts"; + ~~~~~~~~~~~~~~ +!!! error TS1499: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. + import type {} from "./module.mts" with { "resolution-mode": "import" }; + import type {} from "./module.mts" with { "resolution-mode": "require" }; + type _1 = typeof import("./module.mts"); + ~~~~~~~~~~~~~~ +!!! error TS1500: Type import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. + type _2 = typeof import("./module.mts", { with: { "resolution-mode": "import" } }); + type _3 = typeof import("./module.mts", { with: { "resolution-mode": "require" } }); + \ No newline at end of file diff --git a/tests/baselines/reference/typeOnlyESMImportFromCJS.js b/tests/baselines/reference/typeOnlyESMImportFromCJS.js new file mode 100644 index 0000000000000..01b406ded5d80 --- /dev/null +++ b/tests/baselines/reference/typeOnlyESMImportFromCJS.js @@ -0,0 +1,19 @@ +//// [tests/cases/conformance/externalModules/typeOnly/typeOnlyESMImportFromCJS.ts] //// + +//// [module.mts] +export {}; + +//// [common.cts] +import type {} from "./module.mts"; +import type {} from "./module.mts" with { "resolution-mode": "import" }; +import type {} from "./module.mts" with { "resolution-mode": "require" }; +type _1 = typeof import("./module.mts"); +type _2 = typeof import("./module.mts", { with: { "resolution-mode": "import" } }); +type _3 = typeof import("./module.mts", { with: { "resolution-mode": "require" } }); + + +//// [module.mjs] +export {}; +//// [common.cjs] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); diff --git a/tests/baselines/reference/typeOnlyESMImportFromCJS.symbols b/tests/baselines/reference/typeOnlyESMImportFromCJS.symbols new file mode 100644 index 0000000000000..b47541e6f6105 --- /dev/null +++ b/tests/baselines/reference/typeOnlyESMImportFromCJS.symbols @@ -0,0 +1,19 @@ +//// [tests/cases/conformance/externalModules/typeOnly/typeOnlyESMImportFromCJS.ts] //// + +=== module.mts === + +export {}; + +=== common.cts === +import type {} from "./module.mts"; +import type {} from "./module.mts" with { "resolution-mode": "import" }; +import type {} from "./module.mts" with { "resolution-mode": "require" }; +type _1 = typeof import("./module.mts"); +>_1 : Symbol(_1, Decl(common.cts, 2, 73)) + +type _2 = typeof import("./module.mts", { with: { "resolution-mode": "import" } }); +>_2 : Symbol(_2, Decl(common.cts, 3, 40)) + +type _3 = typeof import("./module.mts", { with: { "resolution-mode": "require" } }); +>_3 : Symbol(_3, Decl(common.cts, 4, 83)) + diff --git a/tests/baselines/reference/typeOnlyESMImportFromCJS.types b/tests/baselines/reference/typeOnlyESMImportFromCJS.types new file mode 100644 index 0000000000000..1b0e522401930 --- /dev/null +++ b/tests/baselines/reference/typeOnlyESMImportFromCJS.types @@ -0,0 +1,22 @@ +//// [tests/cases/conformance/externalModules/typeOnly/typeOnlyESMImportFromCJS.ts] //// + +=== module.mts === + +export {}; + +=== common.cts === +import type {} from "./module.mts"; +import type {} from "./module.mts" with { "resolution-mode": "import" }; +import type {} from "./module.mts" with { "resolution-mode": "require" }; +type _1 = typeof import("./module.mts"); +>_1 : typeof import("module", { with: { "resolution-mode": "import" } }) +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +type _2 = typeof import("./module.mts", { with: { "resolution-mode": "import" } }); +>_2 : typeof import("module", { with: { "resolution-mode": "import" } }) +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +type _3 = typeof import("./module.mts", { with: { "resolution-mode": "require" } }); +>_3 : typeof import("module", { with: { "resolution-mode": "import" } }) +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + diff --git a/tests/cases/conformance/externalModules/typeOnly/typeOnlyESMImportFromCJS.ts b/tests/cases/conformance/externalModules/typeOnly/typeOnlyESMImportFromCJS.ts new file mode 100644 index 0000000000000..ce350f09b9358 --- /dev/null +++ b/tests/cases/conformance/externalModules/typeOnly/typeOnlyESMImportFromCJS.ts @@ -0,0 +1,12 @@ +// @module: nodenext + +// @Filename: module.mts +export {}; + +// @Filename: common.cts +import type {} from "./module.mts"; +import type {} from "./module.mts" with { "resolution-mode": "import" }; +import type {} from "./module.mts" with { "resolution-mode": "require" }; +type _1 = typeof import("./module.mts"); +type _2 = typeof import("./module.mts", { with: { "resolution-mode": "import" } }); +type _3 = typeof import("./module.mts", { with: { "resolution-mode": "require" } }); From 56ad3ab9ece1a77ad2062890e2a7b27f6fc74d06 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 21 Aug 2024 13:53:19 -0700 Subject: [PATCH 2/5] Update baselines --- ...enced-project-correctly-with-cts-and-mts-extensions.js | 4 ++-- .../reference/typeOnlyESMImportFromCJS.errors.txt | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js b/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js index 7f488c85f0714..698fcfc51a55a 100644 --- a/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js +++ b/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js @@ -396,7 +396,7 @@ File '/user/username/projects/myproject/packages/pkg2/build/const.d.cts' exists File '/a/lib/package.json' does not exist. File '/a/package.json' does not exist. File '/package.json' does not exist. -packages/pkg1/index.ts:1:29 - error TS1499: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +packages/pkg1/index.ts:1:29 - error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. To convert this file to an ECMAScript module, change its file extension to '.mts' or create a local package.json file with `{ "type": "module" }`. 1 import type { TheNum } from 'pkg2' @@ -631,7 +631,7 @@ File '/user/username/projects/myproject/packages/pkg2/build/const.d.cts' exists File '/a/lib/package.json' does not exist. File '/a/package.json' does not exist. File '/package.json' does not exist. -packages/pkg1/index.ts:1:29 - error TS1499: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +packages/pkg1/index.ts:1:29 - error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. To convert this file to an ECMAScript module, change its file extension to '.mts' or create a local package.json file with `{ "type": "module" }`. 1 import type { TheNum } from 'pkg2' diff --git a/tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt b/tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt index 390122aee9360..daf3b113ecf3f 100644 --- a/tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt +++ b/tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt @@ -1,5 +1,5 @@ -common.cts(1,21): error TS1499: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. -common.cts(4,25): error TS1500: Type import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +common.cts(1,21): error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +common.cts(4,25): error TS1542: Type import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. ==== module.mts (0 errors) ==== @@ -8,12 +8,12 @@ common.cts(4,25): error TS1500: Type import of an ECMAScript module from a Commo ==== common.cts (2 errors) ==== import type {} from "./module.mts"; ~~~~~~~~~~~~~~ -!!! error TS1499: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +!!! error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. import type {} from "./module.mts" with { "resolution-mode": "import" }; import type {} from "./module.mts" with { "resolution-mode": "require" }; type _1 = typeof import("./module.mts"); ~~~~~~~~~~~~~~ -!!! error TS1500: Type import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +!!! error TS1542: Type import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. type _2 = typeof import("./module.mts", { with: { "resolution-mode": "import" } }); type _3 = typeof import("./module.mts", { with: { "resolution-mode": "require" } }); \ No newline at end of file From 080075daf2dbef01978a73573550df4210323001 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 21 Aug 2024 14:53:10 -0700 Subject: [PATCH 3/5] Add codefix, tweak message --- src/compiler/checker.ts | 4 +- src/compiler/diagnosticMessages.json | 13 ++- src/harness/fourslashImpl.ts | 6 +- src/services/_namespaces/ts.codefix.ts | 1 + ...addMissingResolutionModeImportAttribute.ts | 109 ++++++++++++++++++ .../typeOnlyESMImportFromCJS.errors.txt | 8 +- ...FixMissingResolutionModeImportAttribute.ts | 57 +++++++++ 7 files changed, 187 insertions(+), 11 deletions(-) create mode 100644 src/services/codefixes/addMissingResolutionModeImportAttribute.ts create mode 100644 tests/cases/fourslash/codeFixMissingResolutionModeImportAttribute.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 43c007457ce01..13523516bab99 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4678,8 +4678,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { diagnosticDetails = createModeMismatchDetails(currentSourceFile); } - const message = overrideHost?.kind === SyntaxKind.ImportDeclaration && overrideHost.importClause?.isTypeOnly ? Diagnostics.Type_only_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_Colon_import_attribute : - overrideHost?.kind === SyntaxKind.ImportType ? Diagnostics.Type_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_Colon_import_attribute : + const message = overrideHost?.kind === SyntaxKind.ImportDeclaration && overrideHost.importClause?.isTypeOnly ? Diagnostics.Type_only_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute : + overrideHost?.kind === SyntaxKind.ImportType ? Diagnostics.Type_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute : Diagnostics.The_current_file_is_a_CommonJS_module_whose_imports_will_produce_require_calls_however_the_referenced_file_is_an_ECMAScript_module_and_cannot_be_imported_with_require_Consider_writing_a_dynamic_import_0_call_instead; diagnostics.add(createDiagnosticForNodeFromMessageChain( getSourceFileOfNode(errorNode), diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a11b09ad67f3f..c77aaf068ed42 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1816,11 +1816,11 @@ "code": 1540, "reportsDeprecated": true }, - "Type-only import of an ECMAScript module from a CommonJS module must have a `\"resolution-mode\": \"import\"` attribute.": { + "Type-only import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute.": { "category": "Error", "code": 1541 }, - "Type import of an ECMAScript module from a CommonJS module must have a `\"resolution-mode\": \"import\"` attribute.": { + "Type import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute.": { "category": "Error", "code": 1542 }, @@ -8184,6 +8184,15 @@ "category": "Message", "code": 95195 }, + "Add 'resolution-mode' import attribute": { + "category": "Message", + "code": 95196 + }, + "Add 'resolution-mode' import attribute to all type-only imports that need it": { + "category": "Message", + "code": 95197 + }, + "No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": { "category": "Error", "code": 18004 diff --git a/src/harness/fourslashImpl.ts b/src/harness/fourslashImpl.ts index 1abdbe2f6bf68..2e23f91a36ece 100644 --- a/src/harness/fourslashImpl.ts +++ b/src/harness/fourslashImpl.ts @@ -3368,8 +3368,8 @@ export class TestState { this.verifyTextMatches(this.rangeText(this.getOnlyRange()), !!includeWhiteSpace, expectedText); } - private getOnlyRange() { - const ranges = this.getRanges(); + private getOnlyRange(fileName?: string) { + const ranges = fileName ? this.getRangesInFile(fileName) : this.getRanges(); if (ranges.length !== 1) { this.raiseError("Exactly one range should be specified in the testfile."); } @@ -3455,7 +3455,7 @@ export class TestState { const change = ts.first(changes); assert(change.fileName = this.activeFile.fileName); const newText = ts.textChanges.applyChanges(this.getFileContent(this.activeFile.fileName), change.textChanges); - const newRange = updateTextRangeForTextChanges(this.getOnlyRange(), change.textChanges); + const newRange = updateTextRangeForTextChanges(this.getOnlyRange(this.activeFile.fileName), change.textChanges); const actualText = newText.slice(newRange.pos, newRange.end); this.verifyTextMatches(actualText, /*includeWhitespace*/ true, newRangeContent); } diff --git a/src/services/_namespaces/ts.codefix.ts b/src/services/_namespaces/ts.codefix.ts index 988323bd6a885..3cf05630388de 100644 --- a/src/services/_namespaces/ts.codefix.ts +++ b/src/services/_namespaces/ts.codefix.ts @@ -8,6 +8,7 @@ export * from "../codefixes/addMissingAwait.js"; export * from "../codefixes/addMissingConst.js"; export * from "../codefixes/addMissingDeclareProperty.js"; export * from "../codefixes/addMissingInvocationForDecorator.js"; +export * from "../codefixes/addMissingResolutionModeImportAttribute.js"; export * from "../codefixes/addNameToNamelessParameter.js"; export * from "../codefixes/addOptionalPropertyUndefined.js"; export * from "../codefixes/annotateWithTypeFromJSDoc.js"; diff --git a/src/services/codefixes/addMissingResolutionModeImportAttribute.ts b/src/services/codefixes/addMissingResolutionModeImportAttribute.ts new file mode 100644 index 0000000000000..1467ff9691155 --- /dev/null +++ b/src/services/codefixes/addMissingResolutionModeImportAttribute.ts @@ -0,0 +1,109 @@ +import { + codeFixAll, + createCodeFixAction, + registerCodeFix, +} from "../_namespaces/ts.codefix.js"; +import { + Debug, + Diagnostics, + factory, + findAncestor, + getQuotePreference, + getTokenAtPosition, + isImportDeclaration, + isImportTypeNode, + LanguageServiceHost, + ModuleKind, + or, + Program, + QuotePreference, + resolveModuleName, + SourceFile, + SyntaxKind, + textChanges, + tryGetModuleSpecifierFromDeclaration, + UserPreferences, +} from "../_namespaces/ts.js"; + +const fixId = "addMissingResolutionModeImportAttribute"; +const errorCodes = [ + Diagnostics.Type_only_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute.code, + Diagnostics.Type_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute.code, +]; + +registerCodeFix({ + errorCodes, + getCodeActions: function getCodeActionsToAddMissingResolutionModeImportAttribute(context) { + const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start, context.program, context.host, context.preferences)); + return [createCodeFixAction(fixId, changes, Diagnostics.Add_resolution_mode_import_attribute, fixId, Diagnostics.Add_resolution_mode_import_attribute_to_all_type_only_imports_that_need_it)]; + }, + fixIds: [fixId], + getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag.start, context.program, context.host, context.preferences)), +}); + +function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, pos: number, program: Program, host: LanguageServiceHost, preferences: UserPreferences) { + const token = getTokenAtPosition(sourceFile, pos); + const importNode = findAncestor(token, or(isImportDeclaration, isImportTypeNode))!; + Debug.assert(!!importNode, "Expected position to be owned by an ImportDeclaration or ImportType."); + const useSingleQuotes = getQuotePreference(sourceFile, preferences) === QuotePreference.Single; + const moduleSpecifier = tryGetModuleSpecifierFromDeclaration(importNode); + const canUseImportMode = !moduleSpecifier || (resolveModuleName( + moduleSpecifier.text, + sourceFile.fileName, + program.getCompilerOptions(), + host, + program.getModuleResolutionCache(), + /*redirectedReference*/ undefined, + ModuleKind.ESNext, + ).resolvedModule?.resolvedFileName === program.getResolvedModuleFromModuleSpecifier( + moduleSpecifier, + sourceFile, + )?.resolvedModule?.resolvedFileName); + + const attributes = importNode.attributes + ? factory.updateImportAttributes( + importNode.attributes, + factory.createNodeArray([ + ...importNode.attributes.elements, + factory.createImportAttribute( + factory.createStringLiteral("resolution-mode", useSingleQuotes), + factory.createStringLiteral(canUseImportMode ? "import" : "require", useSingleQuotes), + ), + ], importNode.attributes.elements.hasTrailingComma), + importNode.attributes.multiLine, + ) + : factory.createImportAttributes( + factory.createNodeArray([ + factory.createImportAttribute( + factory.createStringLiteral("resolution-mode", useSingleQuotes), + factory.createStringLiteral(canUseImportMode ? "import" : "require", useSingleQuotes), + ), + ]), + ); + if (importNode.kind === SyntaxKind.ImportDeclaration) { + changeTracker.replaceNode( + sourceFile, + importNode, + factory.updateImportDeclaration( + importNode, + importNode.modifiers, + importNode.importClause, + importNode.moduleSpecifier, + attributes, + ), + ); + } + else { + changeTracker.replaceNode( + sourceFile, + importNode, + factory.updateImportTypeNode( + importNode, + importNode.argument, + attributes, + importNode.qualifier, + importNode.typeArguments, + ), + ); + } +} diff --git a/tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt b/tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt index daf3b113ecf3f..dde6c0e433b96 100644 --- a/tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt +++ b/tests/baselines/reference/typeOnlyESMImportFromCJS.errors.txt @@ -1,5 +1,5 @@ -common.cts(1,21): error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. -common.cts(4,25): error TS1542: Type import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +common.cts(1,21): error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute. +common.cts(4,25): error TS1542: Type import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute. ==== module.mts (0 errors) ==== @@ -8,12 +8,12 @@ common.cts(4,25): error TS1542: Type import of an ECMAScript module from a Commo ==== common.cts (2 errors) ==== import type {} from "./module.mts"; ~~~~~~~~~~~~~~ -!!! error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +!!! error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute. import type {} from "./module.mts" with { "resolution-mode": "import" }; import type {} from "./module.mts" with { "resolution-mode": "require" }; type _1 = typeof import("./module.mts"); ~~~~~~~~~~~~~~ -!!! error TS1542: Type import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +!!! error TS1542: Type import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute. type _2 = typeof import("./module.mts", { with: { "resolution-mode": "import" } }); type _3 = typeof import("./module.mts", { with: { "resolution-mode": "require" } }); \ No newline at end of file diff --git a/tests/cases/fourslash/codeFixMissingResolutionModeImportAttribute.ts b/tests/cases/fourslash/codeFixMissingResolutionModeImportAttribute.ts new file mode 100644 index 0000000000000..29ec01f4f32d1 --- /dev/null +++ b/tests/cases/fourslash/codeFixMissingResolutionModeImportAttribute.ts @@ -0,0 +1,57 @@ +/// + +// @module: nodenext + +// @Filename: /package.json +//// { "type": "module" } + +// @Filename: /module.ts +//// export {}; + +// @Filename: /common1.cts +//// [|import type { } from "./module.ts"/*1*/;|] + +// @Filename: /common2.cts +//// [|import type { } from "./module.ts"/*2*/ with { "type": "typescript" };|] + +// @Filename: /common3.cts +//// [|import type { } from "./module"/*3*/;|] + +// @Filename: /common4.cts +//// [|type _1 = typeof import("./module.ts"/*4*/);|] + +goTo.marker("1"); +verify.codeFix({ + index: 0, + errorCode: ts.Diagnostics.Type_only_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute.code, + applyChanges: false, + description: "Add 'resolution-mode' import attribute", + newRangeContent: `import type { } from "./module.ts" with { "resolution-mode": "import" };`, +}); + +goTo.marker("2"); +verify.codeFix({ + index: 0, + errorCode: ts.Diagnostics.Type_only_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute.code, + applyChanges: false, + description: "Add 'resolution-mode' import attribute", + newRangeContent: `import type { } from "./module.ts" with { "type": "typescript", "resolution-mode": "import" };`, +}); + +goTo.marker("3"); +verify.codeFix({ + index: 0, + errorCode: ts.Diagnostics.Type_only_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute.code, + applyChanges: false, + description: "Add 'resolution-mode' import attribute", + newRangeContent: `import type { } from "./module" with { "resolution-mode": "require" };`, +}); + +goTo.marker("4"); +verify.codeFix({ + index: 0, + errorCode: ts.Diagnostics.Type_import_of_an_ECMAScript_module_from_a_CommonJS_module_must_have_a_resolution_mode_attribute.code, + applyChanges: false, + description: "Add 'resolution-mode' import attribute", + newRangeContent: `type _1 = typeof import("./module.ts", { with: { "resolution-mode": "import" } });`, +}); \ No newline at end of file From d08751f526baccce69d0dd3afea1a00d50622127 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 21 Aug 2024 14:55:47 -0700 Subject: [PATCH 4/5] Fix diagnosticMessages formatting --- src/compiler/diagnosticMessages.json | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index c77aaf068ed42..6f5d7ada6c028 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -971,6 +971,7 @@ "category": "Error", "code": 1293 }, + "'with' statements are not allowed in an async function block.": { "category": "Error", "code": 1300 @@ -1531,6 +1532,7 @@ "category": "Error", "code": 1464 }, + "The 'import.meta' meta-property is not allowed in files which will build into CommonJS output.": { "category": "Error", "code": 1470 @@ -1824,6 +1826,7 @@ "category": "Error", "code": 1542 }, + "The types of '{0}' are incompatible between these types.": { "category": "Error", "code": 2200 @@ -1864,6 +1867,7 @@ "category": "Error", "code": 2208 }, + "The project root is ambiguous, but is required to resolve export map entry '{0}' in file '{1}'. Supply the `rootDir` compiler option to disambiguate.": { "category": "Error", "code": 2209 @@ -1880,6 +1884,7 @@ "category": "Message", "code": 2212 }, + "Duplicate identifier '{0}'.": { "category": "Error", "code": 2300 @@ -2856,6 +2861,7 @@ "category": "Error", "code": 2568 }, + "Could not find name '{0}'. Did you mean '{1}'?": { "category": "Error", "code": 2570 @@ -3096,6 +3102,7 @@ "category": "Error", "code": 2639 }, + "Cannot augment module '{0}' with value exports because it resolves to a non-module entity.": { "category": "Error", "code": 2649 @@ -4377,6 +4384,7 @@ "category": "Error", "code": 4126 }, + "The current host does not support the '{0}' option.": { "category": "Error", "code": 5001 @@ -4645,6 +4653,7 @@ "category": "Error", "code": 5111 }, + "Generates a sourcemap for each corresponding '.d.ts' file.": { "category": "Message", "code": 6000 @@ -5496,6 +5505,7 @@ "category": "Message", "code": 6243 }, + "Modules": { "category": "Message", "code": 6244 @@ -5588,6 +5598,7 @@ "category": "Error", "code": 6266 }, + "Directory '{0}' has no containing package.json scope. Imports will not resolve.": { "category": "Message", "code": 6270 @@ -5644,6 +5655,7 @@ "category": "Message", "code": 6283 }, + "Enable project compilation": { "category": "Message", "code": 6302 @@ -5922,6 +5934,7 @@ "category": "Message", "code": 6420 }, + "The expected type comes from property '{0}' which is declared here on type '{1}'": { "category": "Message", "code": 6500 @@ -6414,6 +6427,7 @@ "category": "Message", "code": 6719 }, + "Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'.": { "category": "Message", "code": 6720 @@ -6438,6 +6452,7 @@ "category": "Error", "code": 6807 }, + "one of:": { "category": "Message", "code": 6900 @@ -6566,6 +6581,7 @@ "category": "Error", "code": 6931 }, + "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 @@ -6788,6 +6804,7 @@ "category": "Error", "code": 7061 }, + "You cannot rename this element.": { "category": "Error", "code": 8000 @@ -6932,6 +6949,7 @@ "category": "Error", "code": 8039 }, + "Declaration emit for this file requires using private name '{0}'. An explicit type annotation may unblock declaration emit.": { "category": "Error", "code": 9005 @@ -7164,6 +7182,7 @@ "category": "Error", "code": 18003 }, + "File is a CommonJS module; it may be converted to an ES module.": { "category": "Suggestion", "code": 80001 @@ -7204,6 +7223,7 @@ "category": "Suggestion", "code": 80010 }, + "Add missing 'super()' call": { "category": "Message", "code": 90001 @@ -7424,6 +7444,7 @@ "category": "Message", "code": 90071 }, + "Convert function to an ES2015 class": { "category": "Message", "code": 95001 From 86fb7dfb3a83532de6a7a99a37e29b6c2f642727 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Wed, 21 Aug 2024 15:47:32 -0700 Subject: [PATCH 5/5] Update baseline --- ...eferenced-project-correctly-with-cts-and-mts-extensions.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js b/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js index 698fcfc51a55a..abb47d2f30304 100644 --- a/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js +++ b/tests/baselines/reference/tsbuildWatch/moduleResolution/resolves-specifier-in-output-declaration-file-from-referenced-project-correctly-with-cts-and-mts-extensions.js @@ -396,7 +396,7 @@ File '/user/username/projects/myproject/packages/pkg2/build/const.d.cts' exists File '/a/lib/package.json' does not exist. File '/a/package.json' does not exist. File '/package.json' does not exist. -packages/pkg1/index.ts:1:29 - error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +packages/pkg1/index.ts:1:29 - error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute. To convert this file to an ECMAScript module, change its file extension to '.mts' or create a local package.json file with `{ "type": "module" }`. 1 import type { TheNum } from 'pkg2' @@ -631,7 +631,7 @@ File '/user/username/projects/myproject/packages/pkg2/build/const.d.cts' exists File '/a/lib/package.json' does not exist. File '/a/package.json' does not exist. File '/package.json' does not exist. -packages/pkg1/index.ts:1:29 - error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a `"resolution-mode": "import"` attribute. +packages/pkg1/index.ts:1:29 - error TS1541: Type-only import of an ECMAScript module from a CommonJS module must have a 'resolution-mode' attribute. To convert this file to an ECMAScript module, change its file extension to '.mts' or create a local package.json file with `{ "type": "module" }`. 1 import type { TheNum } from 'pkg2'