@@ -3491,7 +3491,11 @@ namespace ts {
34913491 }
34923492 if (getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.Node12 || getEmitModuleResolutionKind(compilerOptions) === ModuleResolutionKind.NodeNext) {
34933493 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)) {
34953499 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);
34963500 }
34973501 if (mode === ModuleKind.ESNext && compilerOptions.resolveJsonModule && resolvedModule.extension === Extension.Json) {
@@ -5979,7 +5983,7 @@ namespace ts {
59795983 return top;
59805984 }
59815985
5982- function getSpecifierForModuleSymbol(symbol: Symbol, context: NodeBuilderContext) {
5986+ function getSpecifierForModuleSymbol(symbol: Symbol, context: NodeBuilderContext, overrideImportMode?: SourceFile["impliedNodeFormat"] ) {
59835987 let file = getDeclarationOfKind<SourceFile>(symbol, SyntaxKind.SourceFile);
59845988 if (!file) {
59855989 const equivalentFileSymbol = firstDefined(symbol.declarations, d => getFileSymbolIfFileSymbolExportEqualsContainer(d, symbol));
@@ -6012,8 +6016,10 @@ namespace ts {
60126016 return getSourceFileOfNode(getNonAugmentationDeclaration(symbol)!).fileName; // A resolver may not be provided for baselines and errors - in those cases we use the fileName in full
60136017 }
60146018 const contextFile = getSourceFileOfNode(getOriginalNode(context.enclosingDeclaration));
6019+ const resolutionMode = overrideImportMode || contextFile?.impliedNodeFormat;
6020+ const cacheKey = getSpecifierCacheKey(contextFile.path, resolutionMode);
60156021 const links = getSymbolLinks(symbol);
6016- let specifier = links.specifierCache && links.specifierCache.get(contextFile.path );
6022+ let specifier = links.specifierCache && links.specifierCache.get(cacheKey );
60176023 if (!specifier) {
60186024 const isBundle = !!outFile(compilerOptions);
60196025 // For declaration bundles, we need to generate absolute paths relative to the common source dir for imports,
@@ -6028,12 +6034,25 @@ namespace ts {
60286034 specifierCompilerOptions,
60296035 contextFile,
60306036 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+ },
60326047 ));
60336048 links.specifierCache ??= new Map();
6034- links.specifierCache.set(contextFile.path , specifier);
6049+ links.specifierCache.set(cacheKey , specifier);
60356050 }
60366051 return specifier;
6052+
6053+ function getSpecifierCacheKey(path: string, mode: SourceFile["impliedNodeFormat"] | undefined) {
6054+ return mode === undefined ? path : `${mode}|${path}`;
6055+ }
60376056 }
60386057
60396058 function symbolToEntityNameNode(symbol: Symbol): EntityName {
@@ -6049,13 +6068,53 @@ namespace ts {
60496068 // module is root, must use `ImportTypeNode`
60506069 const nonRootParts = chain.length > 1 ? createAccessFromSymbolChain(chain, chain.length - 1, 1) : undefined;
60516070 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+ }
60536090 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+ }
60596118 }
60606119 }
60616120 const lit = factory.createLiteralTypeNode(factory.createStringLiteral(specifier));
@@ -6066,12 +6125,12 @@ namespace ts {
60666125 const lastId = isIdentifier(nonRootParts) ? nonRootParts : nonRootParts.right;
60676126 lastId.typeArguments = undefined;
60686127 }
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);
60706129 }
60716130 else {
60726131 const splitNode = getTopmostIndexedAccessType(nonRootParts);
60736132 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);
60756134 }
60766135 }
60776136
@@ -35202,6 +35261,16 @@ namespace ts {
3520235261
3520335262 function checkImportType(node: ImportTypeNode) {
3520435263 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+
3520535274 getTypeFromTypeNode(node);
3520635275 }
3520735276
@@ -40031,6 +40100,15 @@ namespace ts {
4003140100
4003240101 function checkAssertClause(declaration: ImportDeclaration | ExportDeclaration) {
4003340102 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+
4003440112 const mode = (moduleKind === ModuleKind.NodeNext) && declaration.moduleSpecifier && getUsageModeForExpression(declaration.moduleSpecifier);
4003540113 if (mode !== ModuleKind.ESNext && moduleKind !== ModuleKind.ESNext) {
4003640114 return grammarErrorOnNode(declaration.assertClause,
@@ -40042,6 +40120,10 @@ namespace ts {
4004240120 if (isImportDeclaration(declaration) ? declaration.importClause?.isTypeOnly : declaration.isTypeOnly) {
4004340121 return grammarErrorOnNode(declaration.assertClause, Diagnostics.Import_assertions_cannot_be_used_with_type_only_imports_or_exports);
4004440122 }
40123+
40124+ if (override) {
40125+ return grammarErrorOnNode(declaration.assertClause, Diagnostics.resolution_mode_can_only_be_set_for_type_only_imports);
40126+ }
4004540127 }
4004640128 }
4004740129
0 commit comments