@@ -3491,7 +3491,11 @@ namespace ts {
3491
3491
}
3492
3492
if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) {
3493
3493
const isSyncImport = (currentSourceFile.impliedNodeFormat === ModuleKind.CommonJS && !findAncestor(location, isImportCall)) || !!findAncestor(location, isImportEqualsDeclaration);
3494
- if (isSyncImport && sourceFile.impliedNodeFormat === ModuleKind.ESNext) {
3494
+ const overrideClauseHost = findAncestor(location, l => isImportTypeNode(l) || isExportDeclaration(l) || isImportDeclaration(l)) as ImportTypeNode | ImportDeclaration | ExportDeclaration | undefined;
3495
+ const overrideClause = overrideClauseHost && isImportTypeNode(overrideClauseHost) ? overrideClauseHost.assertions?.assertClause : overrideClauseHost?.assertClause;
3496
+ // An override clause will take effect for type-only imports and import types, and allows importing the types across formats, regardless of
3497
+ // normal mode restrictions
3498
+ if (isSyncImport && sourceFile.impliedNodeFormat === ModuleKind.ESNext && !getResolutionModeOverrideForClause(overrideClause)) {
3495
3499
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);
3496
3500
}
3497
3501
if (mode === ModuleKind.ESNext && compilerOptions.resolveJsonModule && resolvedModule.extension === Extension.Json) {
@@ -5979,7 +5983,7 @@ namespace ts {
5979
5983
return top;
5980
5984
}
5981
5985
5982
- function getSpecifierForModuleSymbol(symbol: Symbol, context: NodeBuilderContext) {
5986
+ function getSpecifierForModuleSymbol(symbol: Symbol, context: NodeBuilderContext, overrideImportMode?: SourceFile["impliedNodeFormat"] ) {
5983
5987
let file = getDeclarationOfKind<SourceFile>(symbol, SyntaxKind.SourceFile);
5984
5988
if (!file) {
5985
5989
const equivalentFileSymbol = firstDefined(symbol.declarations, d => getFileSymbolIfFileSymbolExportEqualsContainer(d, symbol));
@@ -6012,8 +6016,10 @@ namespace ts {
6012
6016
return getSourceFileOfNode(getNonAugmentationDeclaration(symbol)!).fileName; // A resolver may not be provided for baselines and errors - in those cases we use the fileName in full
6013
6017
}
6014
6018
const contextFile = getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration));
6019
+ const resolutionMode = overrideImportMode || contextFile?.impliedNodeFormat;
6020
+ const cacheKey = getSpecifierCacheKey(contextFile.path, resolutionMode);
6015
6021
const links = getSymbolLinks(symbol);
6016
- let specifier = links.specifierCache && links.specifierCache.get(contextFile.path );
6022
+ let specifier = links.specifierCache && links.specifierCache.get(cacheKey );
6017
6023
if (!specifier) {
6018
6024
const isBundle = !!outFile(compilerOptions);
6019
6025
// For declaration bundles, we need to generate absolute paths relative to the common source dir for imports,
@@ -6028,12 +6034,25 @@ namespace ts {
6028
6034
specifierCompilerOptions,
6029
6035
contextFile,
6030
6036
moduleResolverHost,
6031
- { importModuleSpecifierPreference: isBundle ? "non-relative" : "project-relative", importModuleSpecifierEnding: isBundle ? "minimal" : undefined },
6037
+ {
6038
+ importModuleSpecifierPreference: isBundle ? "non-relative" : "project-relative",
6039
+ importModuleSpecifierEnding: isBundle ? "minimal"
6040
+ : resolutionMode === ModuleKind.ESNext ? "js"
6041
+ : undefined,
6042
+ overrideImportMode:
6043
+ overrideImportMode === ModuleKind.CommonJS ? "require"
6044
+ : overrideImportMode === ModuleKind.ESNext ? "import"
6045
+ : undefined
6046
+ },
6032
6047
));
6033
6048
links.specifierCache ??= new Map();
6034
- links.specifierCache.set(contextFile.path , specifier);
6049
+ links.specifierCache.set(cacheKey , specifier);
6035
6050
}
6036
6051
return specifier;
6052
+
6053
+ function getSpecifierCacheKey(path: string, mode: SourceFile["impliedNodeFormat"] | undefined) {
6054
+ return mode === undefined ? path : `${mode}|${path}`;
6055
+ }
6037
6056
}
6038
6057
6039
6058
function symbolToEntityNameNode(symbol: Symbol): EntityName {
@@ -6049,13 +6068,53 @@ namespace ts {
6049
6068
// module is root, must use `ImportTypeNode`
6050
6069
const nonRootParts = chain.length > 1 ? createAccessFromSymbolChain(chain, chain.length - 1, 1) : undefined;
6051
6070
const typeParameterNodes = overrideTypeArguments || lookupTypeParameterNodes(chain, 0, context);
6052
- const specifier = getSpecifierForModuleSymbol(chain[0], context);
6071
+ const contextFile = getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration));
6072
+ const targetFile = getSourceFileOfModule(chain[0]);
6073
+ let specifier: string | undefined;
6074
+ let assertion: ImportTypeAssertionContainer | undefined;
6075
+ if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) {
6076
+ // An `import` type directed at an esm format file is only going to resolve in esm mode - set the esm mode assertion
6077
+ if (targetFile?.impliedNodeFormat === ModuleKind.ESNext && targetFile.impliedNodeFormat !== contextFile?.impliedNodeFormat) {
6078
+ specifier = getSpecifierForModuleSymbol(chain[0], context, ModuleKind.ESNext);
6079
+ assertion = factory.createImportTypeAssertionContainer(factory.createAssertClause(factory.createNodeArray([
6080
+ factory.createAssertEntry(
6081
+ factory.createStringLiteral("resolution-mode"),
6082
+ factory.createStringLiteral("import")
6083
+ )
6084
+ ])));
6085
+ }
6086
+ }
6087
+ if (!specifier) {
6088
+ specifier = getSpecifierForModuleSymbol(chain[0], context);
6089
+ }
6053
6090
if (!(context.flags & NodeBuilderFlags.AllowNodeModulesRelativePaths) && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Classic && specifier.indexOf("/node_modules/") >= 0) {
6054
- // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error
6055
- // since declaration files with these kinds of references are liable to fail when published :(
6056
- context.encounteredError = true;
6057
- if (context.tracker.reportLikelyUnsafeImportRequiredError) {
6058
- context.tracker.reportLikelyUnsafeImportRequiredError(specifier);
6091
+ const oldSpecifier = specifier;
6092
+ if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) {
6093
+ // We might be able to write a portable import type using a mode override; try specifier generation again, but with a different mode set
6094
+ const swappedMode = contextFile?.impliedNodeFormat === ModuleKind.ESNext ? ModuleKind.CommonJS : ModuleKind.ESNext;
6095
+ specifier = getSpecifierForModuleSymbol(chain[0], context, swappedMode);
6096
+
6097
+ if (specifier.indexOf("/node_modules/") >= 0) {
6098
+ // Still unreachable :(
6099
+ specifier = oldSpecifier;
6100
+ }
6101
+ else {
6102
+ assertion = factory.createImportTypeAssertionContainer(factory.createAssertClause(factory.createNodeArray([
6103
+ factory.createAssertEntry(
6104
+ factory.createStringLiteral("resolution-mode"),
6105
+ factory.createStringLiteral(swappedMode === ModuleKind.ESNext ? "import" : "require")
6106
+ )
6107
+ ])));
6108
+ }
6109
+ }
6110
+
6111
+ if (!assertion) {
6112
+ // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error
6113
+ // since declaration files with these kinds of references are liable to fail when published :(
6114
+ context.encounteredError = true;
6115
+ if (context.tracker.reportLikelyUnsafeImportRequiredError) {
6116
+ context.tracker.reportLikelyUnsafeImportRequiredError(oldSpecifier);
6117
+ }
6059
6118
}
6060
6119
}
6061
6120
const lit = factory.createLiteralTypeNode(factory.createStringLiteral(specifier));
@@ -6066,12 +6125,12 @@ namespace ts {
6066
6125
const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right;
6067
6126
lastId.typeArguments = undefined;
6068
6127
}
6069
- return factory.createImportTypeNode(lit, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf);
6128
+ return factory.createImportTypeNode(lit, assertion, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf);
6070
6129
}
6071
6130
else {
6072
6131
const splitNode = getTopmostIndexedAccessType(nonRootParts);
6073
6132
const qualifier = (splitNode.objectType as TypeReferenceNode).typeName;
6074
- return factory.createIndexedAccessTypeNode(factory.createImportTypeNode(lit, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType);
6133
+ return factory.createIndexedAccessTypeNode(factory.createImportTypeNode(lit, assertion, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType);
6075
6134
}
6076
6135
}
6077
6136
@@ -35202,6 +35261,16 @@ namespace ts {
35202
35261
35203
35262
function checkImportType(node: ImportTypeNode) {
35204
35263
checkSourceElement(node.argument);
35264
+
35265
+ if (node.assertions) {
35266
+ const override = getResolutionModeOverrideForClause(node.assertions.assertClause, grammarErrorOnNode);
35267
+ if (override) {
35268
+ if (getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Node12 && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeNext) {
35269
+ grammarErrorOnNode(node.assertions.assertClause, Diagnostics.Resolution_modes_are_only_supported_when_moduleResolution_is_node12_or_nodenext);
35270
+ }
35271
+ }
35272
+ }
35273
+
35205
35274
getTypeFromTypeNode(node);
35206
35275
}
35207
35276
@@ -40031,6 +40100,15 @@ namespace ts {
40031
40100
40032
40101
function checkAssertClause(declaration: ImportDeclaration | ExportDeclaration) {
40033
40102
if (declaration.assertClause) {
40103
+ const validForTypeAssertions = isExclusivelyTypeOnlyImportOrExport(declaration);
40104
+ const override = getResolutionModeOverrideForClause(declaration.assertClause, validForTypeAssertions ? grammarErrorOnNode : undefined);
40105
+ if (validForTypeAssertions && override) {
40106
+ if (getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Node12 && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeNext) {
40107
+ return grammarErrorOnNode(declaration.assertClause, Diagnostics.Resolution_modes_are_only_supported_when_moduleResolution_is_node12_or_nodenext);
40108
+ }
40109
+ return; // Other grammar checks do not apply to type-only imports with resolution mode assertions
40110
+ }
40111
+
40034
40112
const mode = (moduleKind === ModuleKind.NodeNext) && declaration.moduleSpecifier && getUsageModeForExpression(declaration.moduleSpecifier);
40035
40113
if (mode !== ModuleKind.ESNext && moduleKind !== ModuleKind.ESNext) {
40036
40114
return grammarErrorOnNode(declaration.assertClause,
@@ -40042,6 +40120,10 @@ namespace ts {
40042
40120
if (isImportDeclaration(declaration) ? declaration.importClause?.isTypeOnly : declaration.isTypeOnly) {
40043
40121
return grammarErrorOnNode(declaration.assertClause, Diagnostics.Import_assertions_cannot_be_used_with_type_only_imports_or_exports);
40044
40122
}
40123
+
40124
+ if (override) {
40125
+ return grammarErrorOnNode(declaration.assertClause, Diagnostics.resolution_mode_can_only_be_set_for_type_only_imports);
40126
+ }
40045
40127
}
40046
40128
}
40047
40129
0 commit comments