Skip to content

Commit 6cb6fc9

Browse files
authored
Add --module preserve, support require in --moduleResolution bundler (#56785)
1 parent 458eae0 commit 6cb6fc9

File tree

173 files changed

+4719
-534
lines changed

Some content is hidden

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

173 files changed

+4719
-534
lines changed

src/compiler/binder.ts

-2
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,6 @@ import {
282282
setValueDeclaration,
283283
ShorthandPropertyAssignment,
284284
shouldPreserveConstEnums,
285-
shouldResolveJsRequire,
286285
SignatureDeclaration,
287286
skipParentheses,
288287
sliceAfter,
@@ -3559,7 +3558,6 @@ function createBinder(): (file: SourceFile, options: CompilerOptions) => void {
35593558
const possibleVariableDecl = node.kind === SyntaxKind.VariableDeclaration ? node : node.parent.parent;
35603559
if (
35613560
isInJSFile(node) &&
3562-
shouldResolveJsRequire(options) &&
35633561
isVariableDeclarationInitializedToBareOrAccessedRequire(possibleVariableDecl) &&
35643562
!getJSDocTypeTag(node) &&
35653563
!(getCombinedModifierFlags(node) & ModifierFlags.Export)

src/compiler/checker.ts

+26-19
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,6 @@ import {
322322
getLineAndCharacterOfPosition,
323323
getLocalSymbolForExportDefault,
324324
getMembersOfDeclaration,
325-
getModeForUsageLocation,
326325
getModifiers,
327326
getModuleInstanceState,
328327
getNameFromImportAttribute,
@@ -960,7 +959,6 @@ import {
960959
ShorthandPropertyAssignment,
961960
shouldAllowImportingTsExtension,
962961
shouldPreserveConstEnums,
963-
shouldResolveJsRequire,
964962
Signature,
965963
SignatureDeclaration,
966964
SignatureFlags,
@@ -4059,7 +4057,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
40594057
}
40604058

40614059
function getUsageModeForExpression(usage: Expression) {
4062-
return isStringLiteralLike(usage) ? getModeForUsageLocation(getSourceFileOfNode(usage), usage) : undefined;
4060+
return isStringLiteralLike(usage) ? host.getModeForUsageLocation(getSourceFileOfNode(usage), usage) : undefined;
40634061
}
40644062

40654063
function isESMFormatImportImportingCommonjsFormatFile(usageMode: ResolutionMode, targetMode: ResolutionMode) {
@@ -4073,7 +4071,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
40734071

40744072
function canHaveSyntheticDefault(file: SourceFile | undefined, moduleSymbol: Symbol, dontResolveAlias: boolean, usage: Expression) {
40754073
const usageMode = file && getUsageModeForExpression(usage);
4076-
if (file && usageMode !== undefined) {
4074+
if (file && usageMode !== undefined && ModuleKind.Node16 <= moduleKind && moduleKind <= ModuleKind.NodeNext) {
40774075
const result = isESMFormatImportImportingCommonjsFormatFile(usageMode, file.impliedNodeFormat);
40784076
if (usageMode === ModuleKind.ESNext || result) {
40794077
return result;
@@ -5005,13 +5003,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
50055003
const currentSourceFile = getSourceFileOfNode(location);
50065004
const contextSpecifier = isStringLiteralLike(location)
50075005
? location
5008-
: findAncestor(location, isImportCall)?.arguments[0] ||
5006+
: (isModuleDeclaration(location) ? location : location.parent && isModuleDeclaration(location.parent) && location.parent.name === location ? location.parent : undefined)?.name ||
5007+
(isLiteralImportTypeNode(location) ? location : undefined)?.argument.literal ||
5008+
(isVariableDeclaration(location) && location.initializer && isRequireCall(location.initializer, /*requireStringLiteralLikeArgument*/ true) ? location.initializer.arguments[0] : undefined) ||
5009+
findAncestor(location, isImportCall)?.arguments[0] ||
50095010
findAncestor(location, isImportDeclaration)?.moduleSpecifier ||
50105011
findAncestor(location, isExternalModuleImportEqualsDeclaration)?.moduleReference.expression ||
5011-
findAncestor(location, isExportDeclaration)?.moduleSpecifier ||
5012-
(isModuleDeclaration(location) ? location : location.parent && isModuleDeclaration(location.parent) && location.parent.name === location ? location.parent : undefined)?.name ||
5013-
(isLiteralImportTypeNode(location) ? location : undefined)?.argument.literal;
5014-
const mode = contextSpecifier && isStringLiteralLike(contextSpecifier) ? getModeForUsageLocation(currentSourceFile, contextSpecifier) : currentSourceFile.impliedNodeFormat;
5012+
findAncestor(location, isExportDeclaration)?.moduleSpecifier;
5013+
const mode = contextSpecifier && isStringLiteralLike(contextSpecifier) ? host.getModeForUsageLocation(currentSourceFile, contextSpecifier) : currentSourceFile.impliedNodeFormat;
50155014
const moduleResolutionKind = getEmitModuleResolutionKind(compilerOptions);
50165015
const resolvedModule = host.getResolvedModule(currentSourceFile, moduleReference, mode)?.resolvedModule;
50175016
const resolutionDiagnostic = resolvedModule && getResolutionDiagnostic(compilerOptions, resolvedModule, currentSourceFile);
@@ -32083,7 +32082,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3208332082
const errorMessage = isClassic
3208432083
? Diagnostics.Cannot_find_module_0_Did_you_mean_to_set_the_moduleResolution_option_to_nodenext_or_to_add_aliases_to_the_paths_option
3208532084
: Diagnostics.Cannot_find_module_0_or_its_corresponding_type_declarations;
32086-
const mod = resolveExternalModule(location!, runtimeImportSpecifier, errorMessage, location!);
32085+
// Synthesized JSX import is either first or after tslib
32086+
const jsxImportIndex = compilerOptions.importHelpers ? 1 : 0;
32087+
const specifier = file?.imports[jsxImportIndex];
32088+
if (specifier) {
32089+
Debug.assert(nodeIsSynthesized(specifier) && specifier.text === runtimeImportSpecifier, `Expected sourceFile.imports[${jsxImportIndex}] to be the synthesized JSX runtime import`);
32090+
}
32091+
const mod = resolveExternalModule(specifier || location!, runtimeImportSpecifier, errorMessage, location!);
3208732092
const result = mod && mod !== unknownSymbol ? getMergedSymbol(resolveSymbol(mod)) : undefined;
3208832093
if (links) {
3208932094
links.jsxImplicitImportContainer = result || false;
@@ -35811,7 +35816,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3581135816
}
3581235817

3581335818
// In JavaScript files, calls to any identifier 'require' are treated as external module imports
35814-
if (isInJSFile(node) && shouldResolveJsRequire(compilerOptions) && isCommonJsRequire(node)) {
35819+
if (isInJSFile(node) && isCommonJsRequire(node)) {
3581535820
return resolveExternalModuleTypeByLiteral(node.arguments![0] as StringLiteral);
3581635821
}
3581735822

@@ -37745,15 +37750,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3774537750
// fallthrough
3774637751
case ModuleKind.ES2022:
3774737752
case ModuleKind.ESNext:
37753+
case ModuleKind.Preserve:
3774837754
case ModuleKind.System:
3774937755
if (languageVersion >= ScriptTarget.ES2017) {
3775037756
break;
3775137757
}
3775237758
// fallthrough
3775337759
default:
3775437760
span ??= getSpanOfTokenAtPosition(sourceFile, node.pos);
37755-
const message = isAwaitExpression(node) ? Diagnostics.Top_level_await_expressions_are_only_allowed_when_the_module_option_is_set_to_es2022_esnext_system_node16_or_nodenext_and_the_target_option_is_set_to_es2017_or_higher :
37756-
Diagnostics.Top_level_await_using_statements_are_only_allowed_when_the_module_option_is_set_to_es2022_esnext_system_node16_or_nodenext_and_the_target_option_is_set_to_es2017_or_higher;
37761+
const message = isAwaitExpression(node) ? Diagnostics.Top_level_await_expressions_are_only_allowed_when_the_module_option_is_set_to_es2022_esnext_system_node16_nodenext_or_preserve_and_the_target_option_is_set_to_es2017_or_higher :
37762+
Diagnostics.Top_level_await_using_statements_are_only_allowed_when_the_module_option_is_set_to_es2022_esnext_system_node16_nodenext_or_preserve_and_the_target_option_is_set_to_es2017_or_higher;
3775737763
diagnostics.add(createFileDiagnostic(sourceFile, span.start, span.length, message));
3775837764
hasError = true;
3775937765
break;
@@ -46056,14 +46062,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4605646062
}
4605746063

4605846064
const mode = (moduleKind === ModuleKind.NodeNext) && declaration.moduleSpecifier && getUsageModeForExpression(declaration.moduleSpecifier);
46059-
if (mode !== ModuleKind.ESNext && moduleKind !== ModuleKind.ESNext) {
46065+
if (mode !== ModuleKind.ESNext && moduleKind !== ModuleKind.ESNext && moduleKind !== ModuleKind.Preserve) {
4606046066
const message = isImportAttributes
4606146067
? moduleKind === ModuleKind.NodeNext
4606246068
? Diagnostics.Import_attributes_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls
46063-
: Diagnostics.Import_attributes_are_only_supported_when_the_module_option_is_set_to_esnext_or_nodenext
46069+
: Diagnostics.Import_attributes_are_only_supported_when_the_module_option_is_set_to_esnext_nodenext_or_preserve
4606446070
: moduleKind === ModuleKind.NodeNext
4606546071
? Diagnostics.Import_assertions_are_not_allowed_on_statements_that_compile_to_CommonJS_require_calls
46066-
: Diagnostics.Import_assertions_are_only_supported_when_the_module_option_is_set_to_esnext_or_nodenext;
46072+
: Diagnostics.Import_assertions_are_only_supported_when_the_module_option_is_set_to_esnext_nodenext_or_preserve;
4606746073
return grammarErrorOnNode(node, message);
4606846074
}
4606946075

@@ -46147,7 +46153,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4614746153
}
4614846154
}
4614946155
else {
46150-
if (moduleKind >= ModuleKind.ES2015 && getSourceFileOfNode(node).impliedNodeFormat === undefined && !node.isTypeOnly && !(node.flags & NodeFlags.Ambient)) {
46156+
if (moduleKind >= ModuleKind.ES2015 && moduleKind !== ModuleKind.Preserve && getSourceFileOfNode(node).impliedNodeFormat === undefined && !node.isTypeOnly && !(node.flags & NodeFlags.Ambient)) {
4615146157
// Import equals declaration is deprecated in es6 or above
4615246158
grammarErrorOnNode(node, Diagnostics.Import_assignment_cannot_be_used_when_targeting_ECMAScript_modules_Consider_using_import_Asterisk_as_ns_from_mod_import_a_from_mod_import_d_from_mod_or_another_module_format_instead);
4615346159
}
@@ -46433,6 +46439,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4643346439
// Forbid export= in esm implementation files, and esm mode declaration files
4643446440
if (
4643546441
moduleKind >= ModuleKind.ES2015 &&
46442+
moduleKind !== ModuleKind.Preserve &&
4643646443
((node.flags & NodeFlags.Ambient && getSourceFileOfNode(node).impliedNodeFormat === ModuleKind.ESNext) ||
4643746444
(!(node.flags & NodeFlags.Ambient) && getSourceFileOfNode(node).impliedNodeFormat !== ModuleKind.CommonJS))
4643846445
) {
@@ -47605,7 +47612,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4760547612
if (
4760647613
(isExternalModuleImportEqualsDeclaration(node.parent.parent) && getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node) ||
4760747614
((node.parent.kind === SyntaxKind.ImportDeclaration || node.parent.kind === SyntaxKind.ExportDeclaration) && (node.parent as ImportDeclaration).moduleSpecifier === node) ||
47608-
((isInJSFile(node) && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Bundler && isRequireCall(node.parent, /*requireStringLiteralLikeArgument*/ false)) || isImportCall(node.parent)) ||
47615+
((isInJSFile(node) && isRequireCall(node.parent, /*requireStringLiteralLikeArgument*/ false)) || isImportCall(node.parent)) ||
4760947616
(isLiteralTypeNode(node.parent) && isLiteralImportTypeNode(node.parent.parent) && node.parent.parent.argument === node.parent)
4761047617
) {
4761147618
return resolveExternalModuleName(node, node as LiteralExpression, ignoreErrors);
@@ -50004,7 +50011,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
5000450011
// fallthrough
5000550012
default:
5000650013
diagnostics.add(
50007-
createDiagnosticForNode(forInOrOfStatement.awaitModifier, Diagnostics.Top_level_for_await_loops_are_only_allowed_when_the_module_option_is_set_to_es2022_esnext_system_node16_or_nodenext_and_the_target_option_is_set_to_es2017_or_higher),
50014+
createDiagnosticForNode(forInOrOfStatement.awaitModifier, Diagnostics.Top_level_for_await_loops_are_only_allowed_when_the_module_option_is_set_to_es2022_esnext_system_node16_nodenext_or_preserve_and_the_target_option_is_set_to_es2017_or_higher),
5000850015
);
5000950016
break;
5001050017
}

src/compiler/commandLineParser.ts

+1
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ export const moduleOptionDeclaration: CommandLineOptionOfCustomType = {
559559
esnext: ModuleKind.ESNext,
560560
node16: ModuleKind.Node16,
561561
nodenext: ModuleKind.NodeNext,
562+
preserve: ModuleKind.Preserve,
562563
})),
563564
affectsSourceFile: true,
564565
affectsModuleResolution: true,

src/compiler/diagnosticMessages.json

+7-7
Original file line numberDiff line numberDiff line change
@@ -1220,7 +1220,7 @@
12201220
"category": "Message",
12211221
"code": 1377
12221222
},
1223-
"Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', or 'nodenext', and the 'target' option is set to 'es2017' or higher.": {
1223+
"Top-level 'await' expressions are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.": {
12241224
"category": "Error",
12251225
"code": 1378
12261226
},
@@ -1424,7 +1424,7 @@
14241424
"category": "Error",
14251425
"code": 1431
14261426
},
1427-
"Top-level 'for await' loops are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', or 'nodenext', and the 'target' option is set to 'es2017' or higher.": {
1427+
"Top-level 'for await' loops are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.": {
14281428
"category": "Error",
14291429
"code": 1432
14301430
},
@@ -3595,15 +3595,15 @@
35953595
"category": "Error",
35963596
"code": 2820
35973597
},
3598-
"Import assertions are only supported when the '--module' option is set to 'esnext' or 'nodenext'.": {
3598+
"Import assertions are only supported when the '--module' option is set to 'esnext', 'nodenext', or 'preserve'.": {
35993599
"category": "Error",
36003600
"code": 2821
36013601
},
36023602
"Import assertions cannot be used with type-only imports or exports.": {
36033603
"category": "Error",
36043604
"code": 2822
36053605
},
3606-
"Import attributes are only supported when the '--module' option is set to 'esnext' or 'nodenext'.": {
3606+
"Import attributes are only supported when the '--module' option is set to 'esnext', 'nodenext', or 'preserve'.": {
36073607
"category": "Error",
36083608
"code": 2823
36093609
},
@@ -3683,7 +3683,7 @@
36833683
"category": "Error",
36843684
"code": 2853
36853685
},
3686-
"Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', or 'nodenext', and the 'target' option is set to 'es2017' or higher.": {
3686+
"Top-level 'await using' statements are only allowed when the 'module' option is set to 'es2022', 'esnext', 'system', 'node16', 'nodenext', or 'preserve', and the 'target' option is set to 'es2017' or higher.": {
36873687
"category": "Error",
36883688
"code": 2854
36893689
},
@@ -4309,7 +4309,7 @@
43094309
"category": "Error",
43104310
"code": 5070
43114311
},
4312-
"Option '--resolveJsonModule' can only be specified when module code generation is 'commonjs', 'amd', 'es2015' or 'esNext'.": {
4312+
"Option '--resolveJsonModule' cannot be specified when 'module' is set to 'none', 'system', or 'umd'.": {
43134313
"category": "Error",
43144314
"code": 5071
43154315
},
@@ -4401,7 +4401,7 @@
44014401
"category": "Error",
44024402
"code": 5094
44034403
},
4404-
"Option '{0}' can only be used when 'module' is set to 'es2015' or later.": {
4404+
"Option '{0}' can only be used when 'module' is set to 'preserve' or to 'es2015' or later.": {
44054405
"category": "Error",
44064406
"code": 5095
44074407
},

src/compiler/moduleNameResolver.ts

+1-15
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ import {
4444
getCompilerOptionValue,
4545
getDirectoryPath,
4646
GetEffectiveTypeRootsHost,
47-
getEmitModuleKind,
4847
getEmitModuleResolutionKind,
4948
getNormalizedAbsolutePath,
5049
getOwnKeys,
@@ -1417,20 +1416,7 @@ export function resolveModuleName(moduleName: string, containingFile: string, co
14171416
else {
14181417
let moduleResolution = compilerOptions.moduleResolution;
14191418
if (moduleResolution === undefined) {
1420-
switch (getEmitModuleKind(compilerOptions)) {
1421-
case ModuleKind.CommonJS:
1422-
moduleResolution = ModuleResolutionKind.Node10;
1423-
break;
1424-
case ModuleKind.Node16:
1425-
moduleResolution = ModuleResolutionKind.Node16;
1426-
break;
1427-
case ModuleKind.NodeNext:
1428-
moduleResolution = ModuleResolutionKind.NodeNext;
1429-
break;
1430-
default:
1431-
moduleResolution = ModuleResolutionKind.Classic;
1432-
break;
1433-
}
1419+
moduleResolution = getEmitModuleResolutionKind(compilerOptions);
14341420
if (traceEnabled) {
14351421
trace(host, Diagnostics.Module_resolution_kind_is_not_specified_using_0, ModuleResolutionKind[moduleResolution]);
14361422
}

src/compiler/moduleSpecifiers.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ function computeModuleSpecifiers(
385385
if (reason.kind !== FileIncludeKind.Import || reason.file !== importingSourceFile.path) return undefined;
386386
// If the candidate import mode doesn't match the mode we're generating for, don't consider it
387387
// TODO: maybe useful to keep around as an alternative option for certain contexts where the mode is overridable
388-
if (importingSourceFile.impliedNodeFormat && importingSourceFile.impliedNodeFormat !== getModeForResolutionAtIndex(importingSourceFile, reason.index)) return undefined;
388+
if (importingSourceFile.impliedNodeFormat && importingSourceFile.impliedNodeFormat !== getModeForResolutionAtIndex(importingSourceFile, reason.index, compilerOptions)) return undefined;
389389
const specifier = getModuleNameStringLiteralAt(importingSourceFile, reason.index).text;
390390
// If the preference is for non relative and the module specifier is relative, ignore it
391391
return preferences.relativePreference !== RelativePreference.NonRelative || !pathIsRelative(specifier) ?

0 commit comments

Comments
 (0)