Skip to content

Commit 07d1922

Browse files
committed
Update error messages for CJS imports resolving to ES modules
1 parent b7355e3 commit 07d1922

File tree

57 files changed

+758
-418
lines changed

Some content is hidden

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

57 files changed

+758
-418
lines changed

src/compiler/checker.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3581,7 +3581,32 @@ namespace ts {
35813581
// An override clause will take effect for type-only imports and import types, and allows importing the types across formats, regardless of
35823582
// normal mode restrictions
35833583
if (isSyncImport && sourceFile.impliedNodeFormat === ModuleKind.ESNext && !getResolutionModeOverrideForClause(overrideClause)) {
3584-
error(errorNode, Diagnostics.Module_0_cannot_be_imported_using_this_construct_The_specifier_only_resolves_to_an_ES_module_which_cannot_be_imported_synchronously_Use_dynamic_import_instead, moduleReference);
3584+
if (findAncestor(location, isImportEqualsDeclaration)) {
3585+
// ImportEquals in a ESM file resolving to another ESM file
3586+
error(errorNode, Diagnostics.Module_0_cannot_be_imported_using_this_construct_The_specifier_only_resolves_to_an_ES_module_which_cannot_be_imported_with_require_Use_an_ECMAScript_import_instead, moduleReference);
3587+
}
3588+
else {
3589+
// CJS file resolving to an ESM file
3590+
const diag = error(errorNode, 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);
3591+
const ext = tryGetExtensionFromPath(currentSourceFile.fileName);
3592+
if (ext === Extension.Ts || ext === Extension.Js) {
3593+
const scope = host.getPackageScopeForPath(currentSourceFile.path);
3594+
const targetExt = ext === Extension.Ts ? Extension.Mts : Extension.Mjs;
3595+
if (scope && !scope.packageJsonContent.type) {
3596+
addRelatedInfo(diag, createDiagnosticForNode(
3597+
errorNode,
3598+
Diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0_or_add_type_Colon_module_to_its_package_json_file_with_path_1,
3599+
targetExt,
3600+
combinePaths(scope.packageDirectory, "package.json")));
3601+
}
3602+
else {
3603+
addRelatedInfo(diag, createDiagnosticForNode(
3604+
errorNode,
3605+
Diagnostics.To_convert_this_file_to_an_ECMAScript_module_change_its_file_extension_to_0,
3606+
targetExt));
3607+
}
3608+
}
3609+
}
35853610
}
35863611
}
35873612
// merged symbol is module declaration symbol combined with all augmentations

src/compiler/diagnosticMessages.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1469,7 +1469,7 @@
14691469
"category": "Error",
14701470
"code": 1470
14711471
},
1472-
"Module '{0}' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported synchronously. Use dynamic import instead.": {
1472+
"Module '{0}' cannot be imported using this construct. The specifier only resolves to an ES module, which cannot be imported with 'require'. Use an ECMAScript import instead.": {
14731473
"category": "Error",
14741474
"code": 1471
14751475
},
@@ -1501,6 +1501,18 @@
15011501
"category": "Error",
15021502
"code": 1478
15031503
},
1504+
"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.": {
1505+
"category": "Error",
1506+
"code": 1479
1507+
},
1508+
"To convert this file to an ECMAScript module, change its file extension to '{0}'.": {
1509+
"category": "Message",
1510+
"code": 1480
1511+
},
1512+
"To convert this file to an ECMAScript module, change its file extension to '{0}', or add `\"type\": \"module\"` to its package.json file with path '{1}'.": {
1513+
"category": "Message",
1514+
"code": 1481
1515+
},
15041516

15051517
"The types of '{0}' are incompatible between these types.": {
15061518
"category": "Error",

src/compiler/moduleNameResolver.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1790,7 +1790,7 @@ namespace ts {
17901790
}
17911791

17921792
/*@internal*/
1793-
interface PackageJsonInfo {
1793+
export interface PackageJsonInfo {
17941794
packageDirectory: string;
17951795
packageJsonContent: PackageJsonPathFields;
17961796
versionPaths: VersionPaths | undefined;

src/compiler/program.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1325,6 +1325,7 @@ namespace ts {
13251325
directoryExists,
13261326
getSymlinkCache,
13271327
realpath: host.realpath?.bind(host),
1328+
getPackageScopeForPath: path => getPackageScopeForPath(path, moduleResolutionCache?.getPackageJsonInfoCache(), host, options),
13281329
useCaseSensitiveFileNames: () => host.useCaseSensitiveFileNames(),
13291330
getFileIncludeReasons: () => fileReasons,
13301331
structureIsReused,

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4353,6 +4353,7 @@ namespace ts {
43534353
getResolvedTypeReferenceDirectives(): ModeAwareCache<ResolvedTypeReferenceDirective | undefined>;
43544354
getProjectReferenceRedirect(fileName: string): string | undefined;
43554355
isSourceOfProjectReferenceRedirect(fileName: string): boolean;
4356+
getPackageScopeForPath(path: Path): PackageJsonInfo | undefined;
43564357

43574358
readonly redirectTargetsMap: RedirectTargetsMap;
43584359
}

src/server/editorServices.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4185,11 +4185,11 @@ namespace ts.server {
41854185
}
41864186

41874187
/*@internal*/
4188-
getPackageJsonsVisibleToFile(fileName: string, rootDir?: string): readonly PackageJsonInfo[] {
4188+
getPackageJsonsVisibleToFile(fileName: string, rootDir?: string): readonly ProjectPackageJsonInfo[] {
41894189
const packageJsonCache = this.packageJsonCache;
41904190
const rootPath = rootDir && this.toPath(rootDir);
41914191
const filePath = this.toPath(fileName);
4192-
const result: PackageJsonInfo[] = [];
4192+
const result: ProjectPackageJsonInfo[] = [];
41934193
const processDirectory = (directory: Path): boolean | undefined => {
41944194
switch (packageJsonCache.directoryHasPackageJson(directory)) {
41954195
// Sync and check same directory again

src/server/packageJsonCache.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,16 @@
22
namespace ts.server {
33
export interface PackageJsonCache {
44
addOrUpdate(fileName: Path): void;
5-
forEach(action: (info: PackageJsonInfo, fileName: Path) => void): void;
5+
forEach(action: (info: ProjectPackageJsonInfo, fileName: Path) => void): void;
66
delete(fileName: Path): void;
7-
get(fileName: Path): PackageJsonInfo | false | undefined;
8-
getInDirectory(directory: Path): PackageJsonInfo | undefined;
7+
get(fileName: Path): ProjectPackageJsonInfo | false | undefined;
8+
getInDirectory(directory: Path): ProjectPackageJsonInfo | undefined;
99
directoryHasPackageJson(directory: Path): Ternary;
1010
searchDirectoryAndAncestors(directory: Path): void;
1111
}
1212

1313
export function createPackageJsonCache(host: ProjectService): PackageJsonCache {
14-
const packageJsons = new Map<string, PackageJsonInfo>();
14+
const packageJsons = new Map<string, ProjectPackageJsonInfo>();
1515
const directoriesWithoutPackageJson = new Map<string, true>();
1616
return {
1717
addOrUpdate,

src/server/project.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,7 +1751,7 @@ namespace ts.server {
17511751
}
17521752

17531753
/*@internal*/
1754-
getPackageJsonsVisibleToFile(fileName: string, rootDir?: string): readonly PackageJsonInfo[] {
1754+
getPackageJsonsVisibleToFile(fileName: string, rootDir?: string): readonly ProjectPackageJsonInfo[] {
17551755
if (this.projectService.serverMode !== LanguageServiceMode.Semantic) return emptyArray;
17561756
return this.projectService.getPackageJsonsVisibleToFile(fileName, rootDir);
17571757
}
@@ -1762,7 +1762,7 @@ namespace ts.server {
17621762
}
17631763

17641764
/*@internal*/
1765-
getPackageJsonsForAutoImport(rootDir?: string): readonly PackageJsonInfo[] {
1765+
getPackageJsonsForAutoImport(rootDir?: string): readonly ProjectPackageJsonInfo[] {
17661766
const packageJsons = this.getPackageJsonsVisibleToFile(combinePaths(this.currentDirectory, inferredTypesContainingFile), rootDir);
17671767
this.packageJsonsForAutoImport = new Set(packageJsons.map(p => p.fileName));
17681768
return packageJsons;

src/services/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ namespace ts {
197197
}
198198

199199
/* @internal */
200-
export interface PackageJsonInfo {
200+
export interface ProjectPackageJsonInfo {
201201
fileName: string;
202202
parseable: boolean;
203203
dependencies?: ESMap<string, string>;
@@ -311,9 +311,9 @@ namespace ts {
311311

312312
/* @internal */ getDocumentPositionMapper?(generatedFileName: string, sourceFileName?: string): DocumentPositionMapper | undefined;
313313
/* @internal */ getSourceFileLike?(fileName: string): SourceFileLike | undefined;
314-
/* @internal */ getPackageJsonsVisibleToFile?(fileName: string, rootDir?: string): readonly PackageJsonInfo[];
314+
/* @internal */ getPackageJsonsVisibleToFile?(fileName: string, rootDir?: string): readonly ProjectPackageJsonInfo[];
315315
/* @internal */ getNearestAncestorDirectoryWithPackageJson?(fileName: string): string | undefined;
316-
/* @internal */ getPackageJsonsForAutoImport?(rootDir?: string): readonly PackageJsonInfo[];
316+
/* @internal */ getPackageJsonsForAutoImport?(rootDir?: string): readonly ProjectPackageJsonInfo[];
317317
/* @internal */ getCachedExportInfoMap?(): ExportInfoMap;
318318
/* @internal */ getModuleSpecifierCache?(): ModuleSpecifierCache;
319319
/* @internal */ setCompilerHost?(host: CompilerHost): void;

src/services/utilities.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3004,12 +3004,12 @@ namespace ts {
30043004
return packageJson;
30053005
}
30063006

3007-
export function getPackageJsonsVisibleToFile(fileName: string, host: LanguageServiceHost): readonly PackageJsonInfo[] {
3007+
export function getPackageJsonsVisibleToFile(fileName: string, host: LanguageServiceHost): readonly ProjectPackageJsonInfo[] {
30083008
if (!host.fileExists) {
30093009
return [];
30103010
}
30113011

3012-
const packageJsons: PackageJsonInfo[] = [];
3012+
const packageJsons: ProjectPackageJsonInfo[] = [];
30133013
forEachAncestorDirectory(getDirectoryPath(fileName), ancestor => {
30143014
const packageJsonFileName = combinePaths(ancestor, "package.json");
30153015
if (host.fileExists(packageJsonFileName)) {
@@ -3023,7 +3023,7 @@ namespace ts {
30233023
return packageJsons;
30243024
}
30253025

3026-
export function createPackageJsonInfo(fileName: string, host: { readFile?(fileName: string): string | undefined }): PackageJsonInfo | undefined {
3026+
export function createPackageJsonInfo(fileName: string, host: { readFile?(fileName: string): string | undefined }): ProjectPackageJsonInfo | undefined {
30273027
if (!host.readFile) {
30283028
return undefined;
30293029
}
@@ -3032,7 +3032,7 @@ namespace ts {
30323032
const dependencyKeys = ["dependencies", "devDependencies", "optionalDependencies", "peerDependencies"] as const;
30333033
const stringContent = host.readFile(fileName) || "";
30343034
const content = tryParseJson(stringContent) as PackageJsonRaw | undefined;
3035-
const info: Pick<PackageJsonInfo, typeof dependencyKeys[number]> = {};
3035+
const info: Pick<ProjectPackageJsonInfo, typeof dependencyKeys[number]> = {};
30363036
if (content) {
30373037
for (const key of dependencyKeys) {
30383038
const dependencies = content[key];

0 commit comments

Comments
 (0)