diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts
index 4600dfcd9b0c6..a1ce5f38e2bef 100644
--- a/src/services/codefixes/importFixes.ts
+++ b/src/services/codefixes/importFixes.ts
@@ -64,7 +64,7 @@ namespace ts.codefix {
const symbol = checker.getMergedSymbol(skipAlias(exportedSymbol, checker));
const exportInfos = getAllReExportingModules(sourceFile, symbol, moduleSymbol, symbolName, host, program, useAutoImportProvider);
const preferTypeOnlyImport = !!usageIsTypeOnly && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error;
- const useRequire = shouldUseRequire(sourceFile, compilerOptions);
+ const useRequire = shouldUseRequire(sourceFile, program);
const fix = getImportFixForSymbol(sourceFile, exportInfos, moduleSymbol, symbolName, program, /*position*/ undefined, preferTypeOnlyImport, useRequire, host, preferences);
addImport({ fixes: [fix], symbolName });
}
@@ -211,7 +211,7 @@ namespace ts.codefix {
): { readonly moduleSpecifier: string, readonly codeAction: CodeAction } {
const compilerOptions = program.getCompilerOptions();
const exportInfos = getAllReExportingModules(sourceFile, exportedSymbol, moduleSymbol, symbolName, host, program, /*useAutoImportProvider*/ true);
- const useRequire = shouldUseRequire(sourceFile, compilerOptions);
+ const useRequire = shouldUseRequire(sourceFile, program);
const preferTypeOnlyImport = compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error && !isSourceFileJS(sourceFile) && isValidTypeOnlyAliasUseSite(getTokenAtPosition(sourceFile, position));
const moduleSpecifier = first(getNewImportInfos(program, sourceFile, position, preferTypeOnlyImport, useRequire, exportInfos, host, preferences)).moduleSpecifier;
const fix = getImportFixForSymbol(sourceFile, exportInfos, moduleSymbol, symbolName, program, position, preferTypeOnlyImport, useRequire, host, preferences);
@@ -362,10 +362,31 @@ namespace ts.codefix {
});
}
- function shouldUseRequire(sourceFile: SourceFile, compilerOptions: CompilerOptions): boolean {
- return isSourceFileJS(sourceFile)
- && !sourceFile.externalModuleIndicator
- && (!!sourceFile.commonJsModuleIndicator || getEmitModuleKind(compilerOptions) < ModuleKind.ES2015);
+ function shouldUseRequire(sourceFile: SourceFile, program: Program): boolean {
+ // 1. TypeScript files don't use require variable declarations
+ if (!isSourceFileJS(sourceFile)) {
+ return false;
+ }
+
+ // 2. If the current source file is unambiguously CJS or ESM, go with that
+ if (sourceFile.commonJsModuleIndicator && !sourceFile.externalModuleIndicator) return true;
+ if (sourceFile.externalModuleIndicator && !sourceFile.commonJsModuleIndicator) return false;
+
+ // 3. If there's a tsconfig/jsconfig, use its module setting
+ const compilerOptions = program.getCompilerOptions();
+ if (compilerOptions.configFile) {
+ return getEmitModuleKind(compilerOptions) < ModuleKind.ES2015;
+ }
+
+ // 4. Match the first other JS file in the program that's unambiguously CJS or ESM
+ for (const otherFile of program.getSourceFiles()) {
+ if (otherFile === sourceFile || !isSourceFileJS(otherFile) || program.isSourceFileFromExternalLibrary(otherFile)) continue;
+ if (otherFile.commonJsModuleIndicator && !otherFile.externalModuleIndicator) return true;
+ if (otherFile.externalModuleIndicator && !otherFile.commonJsModuleIndicator) return false;
+ }
+
+ // 5. Literally nothing to go on
+ return true;
}
function getNewImportInfos(
@@ -445,7 +466,7 @@ namespace ts.codefix {
const symbol = checker.getAliasedSymbol(umdSymbol);
const symbolName = umdSymbol.name;
const exportInfos: readonly SymbolExportInfo[] = [{ moduleSymbol: symbol, importKind: getUmdImportKind(sourceFile, program.getCompilerOptions()), exportedSymbolIsTypeOnly: false }];
- const useRequire = shouldUseRequire(sourceFile, program.getCompilerOptions());
+ const useRequire = shouldUseRequire(sourceFile, program);
const fixes = getFixForImport(exportInfos, symbolName, isIdentifier(token) ? token.getStart(sourceFile) : undefined, /*preferTypeOnlyImport*/ false, useRequire, program, sourceFile, host, preferences);
return { fixes, symbolName };
}
@@ -497,7 +518,7 @@ namespace ts.codefix {
const compilerOptions = program.getCompilerOptions();
const preferTypeOnlyImport = compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error && isValidTypeOnlyAliasUseSite(symbolToken);
- const useRequire = shouldUseRequire(sourceFile, compilerOptions);
+ const useRequire = shouldUseRequire(sourceFile, program);
const exportInfos = getExportInfos(symbolName, getMeaningFromLocation(symbolToken), cancellationToken, sourceFile, program, useAutoImportProvider, host);
const fixes = arrayFrom(flatMapIterator(exportInfos.entries(), ([_, exportInfos]) =>
getFixForImport(exportInfos, symbolName, symbolToken.getStart(sourceFile), preferTypeOnlyImport, useRequire, program, sourceFile, host, preferences)));
diff --git a/tests/cases/fourslash/importFixWithMultipleModuleExportAssignment.ts b/tests/cases/fourslash/importFixWithMultipleModuleExportAssignment.ts
index b5b9f808e6fd3..669b065a56d47 100644
--- a/tests/cases/fourslash/importFixWithMultipleModuleExportAssignment.ts
+++ b/tests/cases/fourslash/importFixWithMultipleModuleExportAssignment.ts
@@ -17,6 +17,6 @@
goTo.file("/c.js");
verify.importFixAtPosition([
-`import { foo } from "./b";
+`const { foo } = require("./b");
foo`]);
diff --git a/tests/cases/fourslash/importNameCodeFixNewImportExportEqualsJavaScript.ts b/tests/cases/fourslash/importNameCodeFixNewImportExportEqualsJavaScript.ts
deleted file mode 100644
index d7a9e82c200a8..0000000000000
--- a/tests/cases/fourslash/importNameCodeFixNewImportExportEqualsJavaScript.ts
+++ /dev/null
@@ -1,31 +0,0 @@
-///
-
-// @allowJs: true
-// @checkJs: true
-
-// @Filename: /foo.d.ts
-////declare module "foo" {
-//// const foo: number;
-//// export = foo;
-////}
-
-// @Filename: /a.js
-////foo
-
-// @Filename: /b.js
-////import "";
-////
-////foo
-
-// 1. JavaScript should default to 'const ... = require...' without compiler flags set
-goTo.file('/a.js');
-verify.importFixAtPosition([`const foo = require("foo");
-
-foo`]);
-
-// 2. If there are any ImportDeclarations, assume a default import is fine
-goTo.file('/b.js');
-verify.importFixAtPosition([`import "";
-import foo from "foo";
-
-foo`]);
diff --git a/tests/cases/fourslash/importNameCodeFix_jsCJSvsESM1.ts b/tests/cases/fourslash/importNameCodeFix_jsCJSvsESM1.ts
new file mode 100644
index 0000000000000..3bd805cd0515f
--- /dev/null
+++ b/tests/cases/fourslash/importNameCodeFix_jsCJSvsESM1.ts
@@ -0,0 +1,19 @@
+///
+// @allowJs: true
+// @checkJs: true
+
+// @Filename: types/dep.d.ts
+//// export declare class Dep {}
+
+// @Filename: index.js
+//// Dep/**/
+
+// @Filename: util.js
+//// import fs from 'fs';
+
+
+// When current file has no imports/requires, look at other JS files
+goTo.marker("");
+verify.importFixAtPosition([`import { Dep } from "./types/dep";
+
+Dep`]);
\ No newline at end of file
diff --git a/tests/cases/fourslash/importNameCodeFix_jsCJSvsESM2.ts b/tests/cases/fourslash/importNameCodeFix_jsCJSvsESM2.ts
new file mode 100644
index 0000000000000..31e5af80618b3
--- /dev/null
+++ b/tests/cases/fourslash/importNameCodeFix_jsCJSvsESM2.ts
@@ -0,0 +1,22 @@
+///
+// @allowJs: true
+// @checkJs: true
+
+// @Filename: types/dep.d.ts
+//// export declare class Dep {}
+
+// @Filename: index.js
+//// Dep/**/
+
+// @Filename: util1.ts
+//// import fs from 'fs';
+
+// @Filename: util2.js
+//// const fs = require('fs');
+
+
+// TypeScript files don't count as indicators of import style for JS
+goTo.marker("");
+verify.importFixAtPosition([`const { Dep } = require("./types/dep");
+
+Dep`]);
\ No newline at end of file
diff --git a/tests/cases/fourslash/importNameCodeFix_jsCJSvsESM3.ts b/tests/cases/fourslash/importNameCodeFix_jsCJSvsESM3.ts
new file mode 100644
index 0000000000000..64291919580f6
--- /dev/null
+++ b/tests/cases/fourslash/importNameCodeFix_jsCJSvsESM3.ts
@@ -0,0 +1,24 @@
+///
+// @allowJs: true
+// @checkJs: true
+
+// @Filename: types/dep.d.ts
+//// export declare class Dep {}
+
+// @Filename: index.js
+//// import fs from 'fs';
+//// const path = require('path');
+////
+//// Dep/**/
+
+// @Filename: util2.js
+//// export {};
+
+
+// When current file has both imports and requires, get import style from other files
+goTo.marker("");
+verify.importFixAtPosition([`import fs from 'fs';
+import { Dep } from './types/dep';
+const path = require('path');
+
+Dep`]);
\ No newline at end of file
diff --git a/tests/cases/fourslash/importNameCodeFix_require_namedAndDefault.ts b/tests/cases/fourslash/importNameCodeFix_require_namedAndDefault.ts
index d4772d166ff9d..df4fe31ad8bce 100644
--- a/tests/cases/fourslash/importNameCodeFix_require_namedAndDefault.ts
+++ b/tests/cases/fourslash/importNameCodeFix_require_namedAndDefault.ts
@@ -3,7 +3,7 @@
// @allowJs: true
// @checkJs: true
-// @Filename: blah.js
+// @Filename: blah.ts
////export default class Blah {}
////export const Named1 = 0;
////export const Named2 = 1;