@@ -3488,7 +3488,11 @@ namespace ts {
3488
3488
}
3489
3489
if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) {
3490
3490
const isSyncImport = (currentSourceFile.impliedNodeFormat === ModuleKind.CommonJS && !findAncestor(location, isImportCall)) || !!findAncestor(location, isImportEqualsDeclaration);
3491
- if (isSyncImport && sourceFile.impliedNodeFormat === ModuleKind.ESNext) {
3491
+ const overrideClauseHost = findAncestor(location, l => isImportTypeNode(l) || isExportDeclaration(l) || isImportDeclaration(l)) as ImportTypeNode | ImportDeclaration | ExportDeclaration | undefined;
3492
+ const overrideClause = overrideClauseHost && isImportTypeNode(overrideClauseHost) ? overrideClauseHost.assertions?.assertClause : overrideClauseHost?.assertClause;
3493
+ // An override clause will take effect for type-only imports and import types, and allows importing the types across formats, regardless of
3494
+ // normal mode restrictions
3495
+ if (isSyncImport && sourceFile.impliedNodeFormat === ModuleKind.ESNext && !getResolutionModeOverrideForClause(overrideClause)) {
3492
3496
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);
3493
3497
}
3494
3498
if (mode === ModuleKind.ESNext && compilerOptions.resolveJsonModule && resolvedModule.extension === Extension.Json) {
@@ -5969,7 +5973,7 @@ namespace ts {
5969
5973
return top;
5970
5974
}
5971
5975
5972
- function getSpecifierForModuleSymbol(symbol: Symbol, context: NodeBuilderContext) {
5976
+ function getSpecifierForModuleSymbol(symbol: Symbol, context: NodeBuilderContext, overrideImportMode?: SourceFile["impliedNodeFormat"] ) {
5973
5977
let file = getDeclarationOfKind<SourceFile>(symbol, SyntaxKind.SourceFile);
5974
5978
if (!file) {
5975
5979
const equivalentFileSymbol = firstDefined(symbol.declarations, d => getFileSymbolIfFileSymbolExportEqualsContainer(d, symbol));
@@ -6002,8 +6006,10 @@ namespace ts {
6002
6006
return getSourceFileOfNode(getNonAugmentationDeclaration(symbol)!).fileName; // A resolver may not be provided for baselines and errors - in those cases we use the fileName in full
6003
6007
}
6004
6008
const contextFile = getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration));
6009
+ const resolutionMode = overrideImportMode || contextFile?.impliedNodeFormat;
6010
+ const cacheKey = getSpecifierCacheKey(contextFile.path, resolutionMode);
6005
6011
const links = getSymbolLinks(symbol);
6006
- let specifier = links.specifierCache && links.specifierCache.get(contextFile.path );
6012
+ let specifier = links.specifierCache && links.specifierCache.get(cacheKey );
6007
6013
if (!specifier) {
6008
6014
const isBundle = !!outFile(compilerOptions);
6009
6015
// For declaration bundles, we need to generate absolute paths relative to the common source dir for imports,
@@ -6018,12 +6024,25 @@ namespace ts {
6018
6024
specifierCompilerOptions,
6019
6025
contextFile,
6020
6026
moduleResolverHost,
6021
- { importModuleSpecifierPreference: isBundle ? "non-relative" : "project-relative", importModuleSpecifierEnding: isBundle ? "minimal" : undefined },
6027
+ {
6028
+ importModuleSpecifierPreference: isBundle ? "non-relative" : "project-relative",
6029
+ importModuleSpecifierEnding: isBundle ? "minimal"
6030
+ : resolutionMode === ModuleKind.ESNext ? "js"
6031
+ : undefined,
6032
+ overrideImportMode:
6033
+ overrideImportMode === ModuleKind.CommonJS ? "require"
6034
+ : overrideImportMode === ModuleKind.ESNext ? "import"
6035
+ : undefined
6036
+ },
6022
6037
));
6023
6038
links.specifierCache ??= new Map();
6024
- links.specifierCache.set(contextFile.path , specifier);
6039
+ links.specifierCache.set(cacheKey , specifier);
6025
6040
}
6026
6041
return specifier;
6042
+
6043
+ function getSpecifierCacheKey(path: string, mode: SourceFile["impliedNodeFormat"] | undefined) {
6044
+ return mode === undefined ? path : `${mode}|${path}`;
6045
+ }
6027
6046
}
6028
6047
6029
6048
function symbolToEntityNameNode(symbol: Symbol): EntityName {
@@ -6039,13 +6058,53 @@ namespace ts {
6039
6058
// module is root, must use `ImportTypeNode`
6040
6059
const nonRootParts = chain.length > 1 ? createAccessFromSymbolChain(chain, chain.length - 1, 1) : undefined;
6041
6060
const typeParameterNodes = overrideTypeArguments || lookupTypeParameterNodes(chain, 0, context);
6042
- const specifier = getSpecifierForModuleSymbol(chain[0], context);
6061
+ const contextFile = getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration));
6062
+ const targetFile = getSourceFileOfModule(chain[0]);
6063
+ let specifier: string | undefined;
6064
+ let assertion: ImportTypeAssertionContainer | undefined;
6065
+ if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) {
6066
+ // An `import` type directed at an esm format file is only going to resolve in esm mode - set the esm mode assertion
6067
+ if (targetFile?.impliedNodeFormat === ModuleKind.ESNext && targetFile.impliedNodeFormat !== contextFile?.impliedNodeFormat) {
6068
+ specifier = getSpecifierForModuleSymbol(chain[0], context, ModuleKind.ESNext);
6069
+ assertion = factory.createImportTypeAssertionContainer(factory.createAssertClause(factory.createNodeArray([
6070
+ factory.createAssertEntry(
6071
+ factory.createStringLiteral("resolution-mode"),
6072
+ factory.createStringLiteral("import")
6073
+ )
6074
+ ])));
6075
+ }
6076
+ }
6077
+ if (!specifier) {
6078
+ specifier = getSpecifierForModuleSymbol(chain[0], context);
6079
+ }
6043
6080
if (!(context.flags & NodeBuilderFlags.AllowNodeModulesRelativePaths) && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Classic && specifier.indexOf("/node_modules/") >= 0) {
6044
- // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error
6045
- // since declaration files with these kinds of references are liable to fail when published :(
6046
- context.encounteredError = true;
6047
- if (context.tracker.reportLikelyUnsafeImportRequiredError) {
6048
- context.tracker.reportLikelyUnsafeImportRequiredError(specifier);
6081
+ const oldSpecifier = specifier;
6082
+ if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) {
6083
+ // We might be able to write a portable import type using a mode override; try specifier generation again, but with a different mode set
6084
+ const swappedMode = contextFile?.impliedNodeFormat === ModuleKind.ESNext ? ModuleKind.CommonJS : ModuleKind.ESNext;
6085
+ specifier = getSpecifierForModuleSymbol(chain[0], context, swappedMode);
6086
+
6087
+ if (specifier.indexOf("/node_modules/") >= 0) {
6088
+ // Still unreachable :(
6089
+ specifier = oldSpecifier;
6090
+ }
6091
+ else {
6092
+ assertion = factory.createImportTypeAssertionContainer(factory.createAssertClause(factory.createNodeArray([
6093
+ factory.createAssertEntry(
6094
+ factory.createStringLiteral("resolution-mode"),
6095
+ factory.createStringLiteral(swappedMode === ModuleKind.ESNext ? "import" : "require")
6096
+ )
6097
+ ])));
6098
+ }
6099
+ }
6100
+
6101
+ if (!assertion) {
6102
+ // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error
6103
+ // since declaration files with these kinds of references are liable to fail when published :(
6104
+ context.encounteredError = true;
6105
+ if (context.tracker.reportLikelyUnsafeImportRequiredError) {
6106
+ context.tracker.reportLikelyUnsafeImportRequiredError(oldSpecifier);
6107
+ }
6049
6108
}
6050
6109
}
6051
6110
const lit = factory.createLiteralTypeNode(factory.createStringLiteral(specifier));
@@ -6056,12 +6115,12 @@ namespace ts {
6056
6115
const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right;
6057
6116
lastId.typeArguments = undefined;
6058
6117
}
6059
- return factory.createImportTypeNode(lit, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf);
6118
+ return factory.createImportTypeNode(lit, assertion, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf);
6060
6119
}
6061
6120
else {
6062
6121
const splitNode = getTopmostIndexedAccessType(nonRootParts);
6063
6122
const qualifier = (splitNode.objectType as TypeReferenceNode).typeName;
6064
- return factory.createIndexedAccessTypeNode(factory.createImportTypeNode(lit, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType);
6123
+ return factory.createIndexedAccessTypeNode(factory.createImportTypeNode(lit, assertion, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType);
6065
6124
}
6066
6125
}
6067
6126
@@ -35124,6 +35183,16 @@ namespace ts {
35124
35183
35125
35184
function checkImportType(node: ImportTypeNode) {
35126
35185
checkSourceElement(node.argument);
35186
+
35187
+ if (node.assertions) {
35188
+ const override = getResolutionModeOverrideForClause(node.assertions.assertClause, grammarErrorOnNode);
35189
+ if (override) {
35190
+ if (getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Node12 && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeNext) {
35191
+ grammarErrorOnNode(node.assertions.assertClause, Diagnostics.Resolution_modes_are_only_supported_when_moduleResolution_is_node12_or_nodenext);
35192
+ }
35193
+ }
35194
+ }
35195
+
35127
35196
getTypeFromTypeNode(node);
35128
35197
}
35129
35198
@@ -39952,6 +40021,15 @@ namespace ts {
39952
40021
39953
40022
function checkAssertClause(declaration: ImportDeclaration | ExportDeclaration) {
39954
40023
if (declaration.assertClause) {
40024
+ const validForTypeAssertions = isExclusivelyTypeOnlyImportOrExport(declaration);
40025
+ const override = getResolutionModeOverrideForClause(declaration.assertClause, validForTypeAssertions ? grammarErrorOnNode : undefined);
40026
+ if (validForTypeAssertions && override) {
40027
+ if (getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Node12 && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeNext) {
40028
+ return grammarErrorOnNode(declaration.assertClause, Diagnostics.Resolution_modes_are_only_supported_when_moduleResolution_is_node12_or_nodenext);
40029
+ }
40030
+ return; // Other grammar checks do not apply to type-only imports with resolution mode assertions
40031
+ }
40032
+
39955
40033
const mode = (moduleKind === ModuleKind.NodeNext) && declaration.moduleSpecifier && getUsageModeForExpression(declaration.moduleSpecifier);
39956
40034
if (mode !== ModuleKind.ESNext && moduleKind !== ModuleKind.ESNext) {
39957
40035
return grammarErrorOnNode(declaration.assertClause,
@@ -39963,6 +40041,10 @@ namespace ts {
39963
40041
if (isImportDeclaration(declaration) ? declaration.importClause?.isTypeOnly : declaration.isTypeOnly) {
39964
40042
return grammarErrorOnNode(declaration.assertClause, Diagnostics.Import_assertions_cannot_be_used_with_type_only_imports_or_exports);
39965
40043
}
40044
+
40045
+ if (override) {
40046
+ return grammarErrorOnNode(declaration.assertClause, Diagnostics.resolution_mode_can_only_be_set_for_type_only_imports);
40047
+ }
39966
40048
}
39967
40049
}
39968
40050
0 commit comments