diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts
index 10e40cf4cecbd..3cefcf33bf2a8 100644
--- a/src/compiler/binder.ts
+++ b/src/compiler/binder.ts
@@ -2834,12 +2834,14 @@ namespace ts {
}
function setCommonJsModuleIndicator(node: Node) {
- if (file.externalModuleIndicator) {
+ if (file.externalModuleIndicator && file.externalModuleIndicator !== true) {
return false;
}
if (!file.commonJsModuleIndicator) {
file.commonJsModuleIndicator = node;
- bindSourceFileAsExternalModule();
+ if (!file.externalModuleIndicator) {
+ bindSourceFileAsExternalModule();
+ }
}
return true;
}
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index b16ef4991a9f6..413600e0dcede 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -2747,7 +2747,7 @@ namespace ts {
return hasExportAssignmentSymbol(moduleSymbol);
}
// JS files have a synthetic default if they do not contain ES2015+ module syntax (export = is not valid in js) _and_ do not have an __esModule marker
- return !file.externalModuleIndicator && !resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), /*sourceNode*/ undefined, dontResolveAlias);
+ return typeof file.externalModuleIndicator !== "object" && !resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), /*sourceNode*/ undefined, dontResolveAlias);
}
function getTargetOfImportClause(node: ImportClause, dontResolveAlias: boolean): Symbol | undefined {
diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts
index 5850d36971828..a4cb8f9d8a5ee 100644
--- a/src/compiler/utilities.ts
+++ b/src/compiler/utilities.ts
@@ -6312,8 +6312,9 @@ namespace ts {
*/
function isFileForcedToBeModuleByFormat(file: SourceFile): true | undefined {
// Excludes declaration files - they still require an explicit `export {}` or the like
- // for back compat purposes.
- return file.impliedNodeFormat === ModuleKind.ESNext && !file.isDeclarationFile ? true : undefined;
+ // for back compat purposes. The only non-declaration files _not_ forced to be a module are `.js` files
+ // that aren't esm-mode (meaning not in a `type: module` scope).
+ return (file.impliedNodeFormat === ModuleKind.ESNext || (fileExtensionIsOneOf(file.fileName, [Extension.Cjs, Extension.Cts]))) && !file.isDeclarationFile ? true : undefined;
}
export function getSetExternalModuleIndicator(options: CompilerOptions): (file: SourceFile) => void {
@@ -6322,7 +6323,7 @@ namespace ts {
case ModuleDetectionKind.Force:
// All non-declaration files are modules, declaration files still do the usual isFileProbablyExternalModule
return (file: SourceFile) => {
- file.externalModuleIndicator = !file.isDeclarationFile || isFileProbablyExternalModule(file);
+ file.externalModuleIndicator = isFileProbablyExternalModule(file) || !file.isDeclarationFile || undefined;
};
case ModuleDetectionKind.Legacy:
// Files are modules if they have imports, exports, or import.meta
@@ -6382,7 +6383,8 @@ namespace ts {
}
export function getEmitModuleDetectionKind(options: CompilerOptions) {
- return options.moduleDetection || ModuleDetectionKind.Auto;
+ return options.moduleDetection ||
+ (getEmitModuleKind(options) === ModuleKind.Node16 || getEmitModuleKind(options) === ModuleKind.NodeNext ? ModuleDetectionKind.Force : ModuleDetectionKind.Auto);
}
export function hasJsonModuleEmitEnabled(options: CompilerOptions) {
diff --git a/tests/baselines/reference/moduleResolutionWithoutExtension8.js b/tests/baselines/reference/moduleResolutionWithoutExtension8.js
index 593f3668b3c73..7410092fdbcbb 100644
--- a/tests/baselines/reference/moduleResolutionWithoutExtension8.js
+++ b/tests/baselines/reference/moduleResolutionWithoutExtension8.js
@@ -3,5 +3,7 @@
import("./foo").then(x => x); // should error, ask for extension
//// [bar.cjs]
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
// Extensionless relative path dynamic import in a cjs module
import("./foo").then(x => x); // should error, ask for extension
diff --git a/tests/baselines/reference/nodeModulesAllowJsExportAssignment(module=node16).js b/tests/baselines/reference/nodeModulesAllowJsExportAssignment(module=node16).js
index 10d6da622e9a4..7eb839f9e4697 100644
--- a/tests/baselines/reference/nodeModulesAllowJsExportAssignment(module=node16).js
+++ b/tests/baselines/reference/nodeModulesAllowJsExportAssignment(module=node16).js
@@ -34,6 +34,8 @@ module.exports = a;
const a = {};
module.exports = a;
//// [file.js]
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
// cjs format file
const a = {};
module.exports = a;
diff --git a/tests/baselines/reference/nodeModulesAllowJsExportAssignment(module=nodenext).js b/tests/baselines/reference/nodeModulesAllowJsExportAssignment(module=nodenext).js
index 10d6da622e9a4..7eb839f9e4697 100644
--- a/tests/baselines/reference/nodeModulesAllowJsExportAssignment(module=nodenext).js
+++ b/tests/baselines/reference/nodeModulesAllowJsExportAssignment(module=nodenext).js
@@ -34,6 +34,8 @@ module.exports = a;
const a = {};
module.exports = a;
//// [file.js]
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
// cjs format file
const a = {};
module.exports = a;
diff --git a/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=node16).js b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=node16).js
new file mode 100644
index 0000000000000..e3ca6368e77fb
--- /dev/null
+++ b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=node16).js
@@ -0,0 +1,13 @@
+//// [tests/cases/conformance/node/allowJs/nodeModulesAllowJsExportlessJsModuleDetectionAuto.ts] ////
+
+//// [foo.cjs]
+// this file is a module despite having no imports
+//// [bar.js]
+// however this file is _not_ a module
+
+//// [foo.cjs]
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+// this file is a module despite having no imports
+//// [bar.js]
+// however this file is _not_ a module
diff --git a/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=node16).symbols b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=node16).symbols
new file mode 100644
index 0000000000000..e4e9a53aa590d
--- /dev/null
+++ b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=node16).symbols
@@ -0,0 +1,5 @@
+=== tests/cases/conformance/node/allowJs/foo.cjs ===
+// this file is a module despite having no imports
+No type information for this code.=== tests/cases/conformance/node/allowJs/bar.js ===
+// however this file is _not_ a module
+No type information for this code.
\ No newline at end of file
diff --git a/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=node16).types b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=node16).types
new file mode 100644
index 0000000000000..e4e9a53aa590d
--- /dev/null
+++ b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=node16).types
@@ -0,0 +1,5 @@
+=== tests/cases/conformance/node/allowJs/foo.cjs ===
+// this file is a module despite having no imports
+No type information for this code.=== tests/cases/conformance/node/allowJs/bar.js ===
+// however this file is _not_ a module
+No type information for this code.
\ No newline at end of file
diff --git a/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=nodenext).js b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=nodenext).js
new file mode 100644
index 0000000000000..e3ca6368e77fb
--- /dev/null
+++ b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=nodenext).js
@@ -0,0 +1,13 @@
+//// [tests/cases/conformance/node/allowJs/nodeModulesAllowJsExportlessJsModuleDetectionAuto.ts] ////
+
+//// [foo.cjs]
+// this file is a module despite having no imports
+//// [bar.js]
+// however this file is _not_ a module
+
+//// [foo.cjs]
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+// this file is a module despite having no imports
+//// [bar.js]
+// however this file is _not_ a module
diff --git a/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=nodenext).symbols b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=nodenext).symbols
new file mode 100644
index 0000000000000..e4e9a53aa590d
--- /dev/null
+++ b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=nodenext).symbols
@@ -0,0 +1,5 @@
+=== tests/cases/conformance/node/allowJs/foo.cjs ===
+// this file is a module despite having no imports
+No type information for this code.=== tests/cases/conformance/node/allowJs/bar.js ===
+// however this file is _not_ a module
+No type information for this code.
\ No newline at end of file
diff --git a/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=nodenext).types b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=nodenext).types
new file mode 100644
index 0000000000000..e4e9a53aa590d
--- /dev/null
+++ b/tests/baselines/reference/nodeModulesAllowJsExportlessJsModuleDetectionAuto(module=nodenext).types
@@ -0,0 +1,5 @@
+=== tests/cases/conformance/node/allowJs/foo.cjs ===
+// this file is a module despite having no imports
+No type information for this code.=== tests/cases/conformance/node/allowJs/bar.js ===
+// however this file is _not_ a module
+No type information for this code.
\ No newline at end of file
diff --git a/tests/baselines/reference/tripleSlashTypesReferenceWithMissingExports(module=node16).js b/tests/baselines/reference/tripleSlashTypesReferenceWithMissingExports(module=node16).js
index 89dd2c7541a29..7c518b3b9d9cc 100644
--- a/tests/baselines/reference/tripleSlashTypesReferenceWithMissingExports(module=node16).js
+++ b/tests/baselines/reference/tripleSlashTypesReferenceWithMissingExports(module=node16).js
@@ -14,5 +14,7 @@ interface GlobalThing { a: number }
const a: GlobalThing = { a: 0 };
//// [usage.js]
+"use strict";
///
+Object.defineProperty(exports, "__esModule", { value: true });
const a = { a: 0 };
diff --git a/tests/baselines/reference/tripleSlashTypesReferenceWithMissingExports(module=nodenext).js b/tests/baselines/reference/tripleSlashTypesReferenceWithMissingExports(module=nodenext).js
index 89dd2c7541a29..7c518b3b9d9cc 100644
--- a/tests/baselines/reference/tripleSlashTypesReferenceWithMissingExports(module=nodenext).js
+++ b/tests/baselines/reference/tripleSlashTypesReferenceWithMissingExports(module=nodenext).js
@@ -14,5 +14,7 @@ interface GlobalThing { a: number }
const a: GlobalThing = { a: 0 };
//// [usage.js]
+"use strict";
///
+Object.defineProperty(exports, "__esModule", { value: true });
const a = { a: 0 };
diff --git a/tests/cases/conformance/node/allowJs/nodeModulesAllowJsExportlessJsModuleDetectionAuto.ts b/tests/cases/conformance/node/allowJs/nodeModulesAllowJsExportlessJsModuleDetectionAuto.ts
new file mode 100644
index 0000000000000..24037e466bafa
--- /dev/null
+++ b/tests/cases/conformance/node/allowJs/nodeModulesAllowJsExportlessJsModuleDetectionAuto.ts
@@ -0,0 +1,8 @@
+// @module: node16,nodenext
+// @allowJs: true
+// @outDir: ./out
+// @moduleDetection: auto
+// @filename: foo.cjs
+// this file is a module despite having no imports
+// @filename: bar.js
+// however this file is _not_ a module
\ No newline at end of file