@@ -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) {
@@ -5985,7 +5989,7 @@ namespace ts {
5985
5989
return top;
5986
5990
}
5987
5991
5988
- function getSpecifierForModuleSymbol(symbol: Symbol, context: NodeBuilderContext) {
5992
+ function getSpecifierForModuleSymbol(symbol: Symbol, context: NodeBuilderContext, overrideImportMode?: SourceFile["impliedNodeFormat"] ) {
5989
5993
let file = getDeclarationOfKind<SourceFile>(symbol, SyntaxKind.SourceFile);
5990
5994
if (!file) {
5991
5995
const equivalentFileSymbol = firstDefined(symbol.declarations, d => getFileSymbolIfFileSymbolExportEqualsContainer(d, symbol));
@@ -6018,8 +6022,10 @@ namespace ts {
6018
6022
return getSourceFileOfNode(getNonAugmentationDeclaration(symbol)!).fileName; // A resolver may not be provided for baselines and errors - in those cases we use the fileName in full
6019
6023
}
6020
6024
const contextFile = getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration));
6025
+ const resolutionMode = overrideImportMode || contextFile?.impliedNodeFormat;
6026
+ const cacheKey = getSpecifierCacheKey(contextFile.path, resolutionMode);
6021
6027
const links = getSymbolLinks(symbol);
6022
- let specifier = links.specifierCache && links.specifierCache.get(contextFile.path );
6028
+ let specifier = links.specifierCache && links.specifierCache.get(cacheKey );
6023
6029
if (!specifier) {
6024
6030
const isBundle = !!outFile(compilerOptions);
6025
6031
// For declaration bundles, we need to generate absolute paths relative to the common source dir for imports,
@@ -6034,12 +6040,22 @@ namespace ts {
6034
6040
specifierCompilerOptions,
6035
6041
contextFile,
6036
6042
moduleResolverHost,
6037
- { importModuleSpecifierPreference: isBundle ? "non-relative" : "project-relative", importModuleSpecifierEnding: isBundle ? "minimal" : undefined },
6043
+ {
6044
+ importModuleSpecifierPreference: isBundle ? "non-relative" : "project-relative",
6045
+ importModuleSpecifierEnding: isBundle ? "minimal"
6046
+ : resolutionMode === ModuleKind.ESNext ? "js"
6047
+ : undefined,
6048
+ },
6049
+ { overrideImportMode }
6038
6050
));
6039
6051
links.specifierCache ??= new Map();
6040
- links.specifierCache.set(contextFile.path , specifier);
6052
+ links.specifierCache.set(cacheKey , specifier);
6041
6053
}
6042
6054
return specifier;
6055
+
6056
+ function getSpecifierCacheKey(path: string, mode: SourceFile["impliedNodeFormat"] | undefined) {
6057
+ return mode === undefined ? path : `${mode}|${path}`;
6058
+ }
6043
6059
}
6044
6060
6045
6061
function symbolToEntityNameNode(symbol: Symbol): EntityName {
@@ -6055,13 +6071,53 @@ namespace ts {
6055
6071
// module is root, must use `ImportTypeNode`
6056
6072
const nonRootParts = chain.length > 1 ? createAccessFromSymbolChain(chain, chain.length - 1, 1) : undefined;
6057
6073
const typeParameterNodes = overrideTypeArguments || lookupTypeParameterNodes(chain, 0, context);
6058
- const specifier = getSpecifierForModuleSymbol(chain[0], context);
6074
+ const contextFile = getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration));
6075
+ const targetFile = getSourceFileOfModule(chain[0]);
6076
+ let specifier: string | undefined;
6077
+ let assertion: ImportTypeAssertionContainer | undefined;
6078
+ if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) {
6079
+ // An `import` type directed at an esm format file is only going to resolve in esm mode - set the esm mode assertion
6080
+ if (targetFile?.impliedNodeFormat === ModuleKind.ESNext && targetFile.impliedNodeFormat !== contextFile?.impliedNodeFormat) {
6081
+ specifier = getSpecifierForModuleSymbol(chain[0], context, ModuleKind.ESNext);
6082
+ assertion = factory.createImportTypeAssertionContainer(factory.createAssertClause(factory.createNodeArray([
6083
+ factory.createAssertEntry(
6084
+ factory.createStringLiteral("resolution-mode"),
6085
+ factory.createStringLiteral("import")
6086
+ )
6087
+ ])));
6088
+ }
6089
+ }
6090
+ if (!specifier) {
6091
+ specifier = getSpecifierForModuleSymbol(chain[0], context);
6092
+ }
6059
6093
if (!(context.flags & NodeBuilderFlags.AllowNodeModulesRelativePaths) && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Classic && specifier.indexOf("/node_modules/") >= 0) {
6060
- // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error
6061
- // since declaration files with these kinds of references are liable to fail when published :(
6062
- context.encounteredError = true;
6063
- if (context.tracker.reportLikelyUnsafeImportRequiredError) {
6064
- context.tracker.reportLikelyUnsafeImportRequiredError(specifier);
6094
+ const oldSpecifier = specifier;
6095
+ if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) {
6096
+ // We might be able to write a portable import type using a mode override; try specifier generation again, but with a different mode set
6097
+ const swappedMode = contextFile?.impliedNodeFormat === ModuleKind.ESNext ? ModuleKind.CommonJS : ModuleKind.ESNext;
6098
+ specifier = getSpecifierForModuleSymbol(chain[0], context, swappedMode);
6099
+
6100
+ if (specifier.indexOf("/node_modules/") >= 0) {
6101
+ // Still unreachable :(
6102
+ specifier = oldSpecifier;
6103
+ }
6104
+ else {
6105
+ assertion = factory.createImportTypeAssertionContainer(factory.createAssertClause(factory.createNodeArray([
6106
+ factory.createAssertEntry(
6107
+ factory.createStringLiteral("resolution-mode"),
6108
+ factory.createStringLiteral(swappedMode === ModuleKind.ESNext ? "import" : "require")
6109
+ )
6110
+ ])));
6111
+ }
6112
+ }
6113
+
6114
+ if (!assertion) {
6115
+ // If ultimately we can only name the symbol with a reference that dives into a `node_modules` folder, we should error
6116
+ // since declaration files with these kinds of references are liable to fail when published :(
6117
+ context.encounteredError = true;
6118
+ if (context.tracker.reportLikelyUnsafeImportRequiredError) {
6119
+ context.tracker.reportLikelyUnsafeImportRequiredError(oldSpecifier);
6120
+ }
6065
6121
}
6066
6122
}
6067
6123
const lit = factory.createLiteralTypeNode(factory.createStringLiteral(specifier));
@@ -6072,12 +6128,12 @@ namespace ts {
6072
6128
const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right;
6073
6129
lastId.typeArguments = undefined;
6074
6130
}
6075
- return factory.createImportTypeNode(lit, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf);
6131
+ return factory.createImportTypeNode(lit, assertion, nonRootParts as EntityName, typeParameterNodes as readonly TypeNode[], isTypeOf);
6076
6132
}
6077
6133
else {
6078
6134
const splitNode = getTopmostIndexedAccessType(nonRootParts);
6079
6135
const qualifier = (splitNode.objectType as TypeReferenceNode).typeName;
6080
- return factory.createIndexedAccessTypeNode(factory.createImportTypeNode(lit, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType);
6136
+ return factory.createIndexedAccessTypeNode(factory.createImportTypeNode(lit, assertion, qualifier, typeParameterNodes as readonly TypeNode[], isTypeOf), splitNode.indexType);
6081
6137
}
6082
6138
}
6083
6139
@@ -35293,6 +35349,16 @@ namespace ts {
35293
35349
35294
35350
function checkImportType(node: ImportTypeNode) {
35295
35351
checkSourceElement(node.argument);
35352
+
35353
+ if (node.assertions) {
35354
+ const override = getResolutionModeOverrideForClause(node.assertions.assertClause, grammarErrorOnNode);
35355
+ if (override) {
35356
+ if (getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Node12 && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeNext) {
35357
+ grammarErrorOnNode(node.assertions.assertClause, Diagnostics.Resolution_modes_are_only_supported_when_moduleResolution_is_node12_or_nodenext);
35358
+ }
35359
+ }
35360
+ }
35361
+
35296
35362
getTypeFromTypeNode(node);
35297
35363
}
35298
35364
@@ -40133,6 +40199,15 @@ namespace ts {
40133
40199
40134
40200
function checkAssertClause(declaration: ImportDeclaration | ExportDeclaration) {
40135
40201
if (declaration.assertClause) {
40202
+ const validForTypeAssertions = isExclusivelyTypeOnlyImportOrExport(declaration);
40203
+ const override = getResolutionModeOverrideForClause(declaration.assertClause, validForTypeAssertions ? grammarErrorOnNode : undefined);
40204
+ if (validForTypeAssertions && override) {
40205
+ if (getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.Node12 && getEmitModuleResolutionKind(compilerOptions) !== ModuleResolutionKind.NodeNext) {
40206
+ return grammarErrorOnNode(declaration.assertClause, Diagnostics.Resolution_modes_are_only_supported_when_moduleResolution_is_node12_or_nodenext);
40207
+ }
40208
+ return; // Other grammar checks do not apply to type-only imports with resolution mode assertions
40209
+ }
40210
+
40136
40211
const mode = (moduleKind === ModuleKind.NodeNext) && declaration.moduleSpecifier && getUsageModeForExpression(declaration.moduleSpecifier);
40137
40212
if (mode !== ModuleKind.ESNext && moduleKind !== ModuleKind.ESNext) {
40138
40213
return grammarErrorOnNode(declaration.assertClause,
@@ -40144,6 +40219,10 @@ namespace ts {
40144
40219
if (isImportDeclaration(declaration) ? declaration.importClause?.isTypeOnly : declaration.isTypeOnly) {
40145
40220
return grammarErrorOnNode(declaration.assertClause, Diagnostics.Import_assertions_cannot_be_used_with_type_only_imports_or_exports);
40146
40221
}
40222
+
40223
+ if (override) {
40224
+ return grammarErrorOnNode(declaration.assertClause, Diagnostics.resolution_mode_can_only_be_set_for_type_only_imports);
40225
+ }
40147
40226
}
40148
40227
}
40149
40228
0 commit comments