mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-29 16:29:19 -05:00
Improve errors on module: node12 and extensionless relative imports (#46486)
* add new error + suggestions * push down assert defined * remove comment * fix esm module import detection, update baselines * add and update new tests * fix review comments * remove renamed baseline references * update node modules test baselines * fix error message, add new tests * update old tests with new error message
This commit is contained in:
committed by
GitHub
parent
f1a69ec93e
commit
9cdbb7248b
@@ -1016,6 +1016,22 @@ namespace ts {
|
||||
const builtinGlobals = createSymbolTable();
|
||||
builtinGlobals.set(undefinedSymbol.escapedName, undefinedSymbol);
|
||||
|
||||
// Extensions suggested for path imports when module resolution is node12 or higher.
|
||||
// The first element of each tuple is the extension a file has.
|
||||
// The second element of each tuple is the extension that should be used in a path import.
|
||||
// e.g. if we want to import file `foo.mts`, we should write `import {} from "./foo.mjs".
|
||||
const suggestedExtensions: [string, string][] = [
|
||||
[".mts", ".mjs"],
|
||||
[".ts", ".js"],
|
||||
[".cts", ".cjs"],
|
||||
[".mjs", ".mjs"],
|
||||
[".js", ".js"],
|
||||
[".cjs", ".cjs"],
|
||||
[".tsx", compilerOptions.jsx === JsxEmit.Preserve ? ".jsx" : ".js"],
|
||||
[".jsx", ".jsx"],
|
||||
[".json", ".json"],
|
||||
];
|
||||
|
||||
initializeTypeChecker();
|
||||
|
||||
return checker;
|
||||
@@ -3443,7 +3459,7 @@ namespace ts {
|
||||
(isModuleDeclaration(location) ? location : location.parent && isModuleDeclaration(location.parent) && location.parent.name === location ? location.parent : undefined)?.name ||
|
||||
(isLiteralImportTypeNode(location) ? location : undefined)?.argument.literal;
|
||||
const mode = contextSpecifier && isStringLiteralLike(contextSpecifier) ? getModeForUsageLocation(currentSourceFile, contextSpecifier) : currentSourceFile.impliedNodeFormat;
|
||||
const resolvedModule = getResolvedModule(currentSourceFile, moduleReference, mode)!; // TODO: GH#18217
|
||||
const resolvedModule = getResolvedModule(currentSourceFile, moduleReference, mode);
|
||||
const resolutionDiagnostic = resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule);
|
||||
const sourceFile = resolvedModule && !resolutionDiagnostic && host.getSourceFile(resolvedModule.resolvedFileName);
|
||||
if (sourceFile) {
|
||||
@@ -3489,10 +3505,10 @@ namespace ts {
|
||||
if (resolvedModule && !resolutionExtensionIsTSOrJson(resolvedModule.extension) && resolutionDiagnostic === undefined || resolutionDiagnostic === Diagnostics.Could_not_find_a_declaration_file_for_module_0_1_implicitly_has_an_any_type) {
|
||||
if (isForAugmentation) {
|
||||
const diag = Diagnostics.Invalid_module_name_in_augmentation_Module_0_resolves_to_an_untyped_module_at_1_which_cannot_be_augmented;
|
||||
error(errorNode, diag, moduleReference, resolvedModule.resolvedFileName);
|
||||
error(errorNode, diag, moduleReference, resolvedModule!.resolvedFileName);
|
||||
}
|
||||
else {
|
||||
errorOnImplicitAnyModule(/*isError*/ noImplicitAny && !!moduleNotFoundError, errorNode, resolvedModule, moduleReference);
|
||||
errorOnImplicitAnyModule(/*isError*/ noImplicitAny && !!moduleNotFoundError, errorNode, resolvedModule!, moduleReference);
|
||||
}
|
||||
// Failed imports and untyped modules are both treated in an untyped manner; only difference is whether we give a diagnostic first.
|
||||
return undefined;
|
||||
@@ -3513,6 +3529,10 @@ namespace ts {
|
||||
}
|
||||
else {
|
||||
const tsExtension = tryExtractTSExtension(moduleReference);
|
||||
const isExtensionlessRelativePathImport = pathIsRelative(moduleReference) && !hasExtension(moduleReference);
|
||||
const moduleResolutionKind = getEmitModuleResolutionKind(compilerOptions);
|
||||
const resolutionIsNode12OrNext = moduleResolutionKind === ModuleResolutionKind.Node12 ||
|
||||
moduleResolutionKind === ModuleResolutionKind.NodeNext;
|
||||
if (tsExtension) {
|
||||
const diag = Diagnostics.An_import_path_cannot_end_with_a_0_extension_Consider_importing_1_instead;
|
||||
const importSourceWithoutExtension = removeExtension(moduleReference, tsExtension);
|
||||
@@ -3532,6 +3552,18 @@ namespace ts {
|
||||
hasJsonModuleEmitEnabled(compilerOptions)) {
|
||||
error(errorNode, Diagnostics.Cannot_find_module_0_Consider_using_resolveJsonModule_to_import_module_with_json_extension, moduleReference);
|
||||
}
|
||||
else if (mode === ModuleKind.ESNext && resolutionIsNode12OrNext && isExtensionlessRelativePathImport) {
|
||||
const absoluteRef = getNormalizedAbsolutePath(moduleReference, getDirectoryPath(currentSourceFile.path));
|
||||
const suggestedExt = suggestedExtensions.find(([actualExt, _importExt]) => host.fileExists(absoluteRef + actualExt))?.[1];
|
||||
if (suggestedExt) {
|
||||
error(errorNode,
|
||||
Diagnostics.Relative_import_paths_need_explicit_file_extensions_in_EcmaScript_imports_when_moduleResolution_is_node12_or_nodenext_Did_you_mean_0,
|
||||
moduleReference + suggestedExt);
|
||||
}
|
||||
else {
|
||||
error(errorNode, Diagnostics.Relative_import_paths_need_explicit_file_extensions_in_EcmaScript_imports_when_moduleResolution_is_node12_or_nodenext_Consider_adding_an_extension_to_the_import_path);
|
||||
}
|
||||
}
|
||||
else {
|
||||
error(errorNode, moduleNotFoundError, moduleReference);
|
||||
}
|
||||
|
||||
@@ -3345,6 +3345,14 @@
|
||||
"category": "Error",
|
||||
"code": 2833
|
||||
},
|
||||
"Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node12' or 'nodenext'. Consider adding an extension to the import path.": {
|
||||
"category": "Error",
|
||||
"code": 2834
|
||||
},
|
||||
"Relative import paths need explicit file extensions in EcmaScript imports when '--moduleResolution' is 'node12' or 'nodenext'. Did you mean '{0}'?": {
|
||||
"category": "Error",
|
||||
"code": 2835
|
||||
},
|
||||
|
||||
"Import declaration '{0}' is using private name '{1}'.": {
|
||||
"category": "Error",
|
||||
|
||||
@@ -5992,7 +5992,7 @@ namespace ts {
|
||||
export enum ModuleResolutionKind {
|
||||
Classic = 1,
|
||||
NodeJs = 2,
|
||||
// Starting with node12, node's module resolver has significant departures from tranditional cjs resolution
|
||||
// Starting with node12, node's module resolver has significant departures from traditional cjs resolution
|
||||
// to better support ecmascript modules and their use within node - more features are still being added, so
|
||||
// we can expect it to change over time, and as such, offer both a `NodeNext` moving resolution target, and a `Node12`
|
||||
// version-anchored resolution target
|
||||
|
||||
Reference in New Issue
Block a user