From d0d406747d580ec4972dc411a9f158c232695987 Mon Sep 17 00:00:00 2001 From: Andrew Branch Date: Mon, 9 Oct 2023 11:38:15 -0700 Subject: [PATCH] Add type reference directive for all visited references to ambient modules (#56015) --- src/compiler/checker.ts | 5 +++ src/compiler/emitter.ts | 1 + src/compiler/transformers/declarations.ts | 27 ++++++++++++++-- src/compiler/types.ts | 1 + src/compiler/utilities.ts | 5 ++- ...larationEmitBundleWithAmbientReferences.js | 1 + ...onEmitTripleSlashReferenceAmbientModule.js | 31 +++++++++++++++++++ ...ImportHelpersCollisions2(module=node16).js | 1 + ...portHelpersCollisions2(module=nodenext).js | 1 + ...ModulesImportAssignments(module=node16).js | 3 ++ ...dulesImportAssignments(module=nodenext).js | 3 ++ ...ImportHelpersCollisions2(module=node16).js | 2 ++ ...portHelpersCollisions2(module=nodenext).js | 2 ++ ...ImportHelpersCollisions3(module=node16).js | 2 ++ ...portHelpersCollisions3(module=nodenext).js | 2 ++ ...onEmitTripleSlashReferenceAmbientModule.ts | 20 ++++++++++++ 16 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/declarationEmitTripleSlashReferenceAmbientModule.js create mode 100644 tests/cases/compiler/declarationEmitTripleSlashReferenceAmbientModule.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4c31ece8602..6098245cb6c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -47930,6 +47930,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return !sym.exports ? [] : nodeBuilder.symbolTableToDeclarationStatements(sym.exports, node, flags, tracker, bundled); }, isImportRequiredByAugmentation, + tryFindAmbientModule: moduleReferenceExpression => { + const node = getParseTreeNode(moduleReferenceExpression); + const moduleSpecifier = node && isStringLiteralLike(node) ? node.text : undefined; + return moduleSpecifier !== undefined ? tryFindAmbientModule(moduleSpecifier, /*withAugmentations*/ true) : undefined; + }, }; function isImportRequiredByAugmentation(node: ImportDeclaration) { diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 3d035053761..463651bdb00 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1182,6 +1182,7 @@ export const notImplementedResolver: EmitResolver = { isBindingCapturedByNode: notImplemented, getDeclarationStatementsForSourceFile: notImplemented, isImportRequiredByAugmentation: notImplemented, + tryFindAmbientModule: notImplemented, }; /** diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 05b5ad1c8b9..512c2eb4704 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -212,6 +212,7 @@ import { TransformationContext, transformNodes, tryCast, + tryGetModuleSpecifierFromDeclaration, TypeAliasDeclaration, TypeNode, TypeParameterDeclaration, @@ -345,6 +346,18 @@ export function transformDeclarations(context: TransformationContext) { refs.set(getOriginalNodeId(container), container); } + function trackReferencedAmbientModuleFromImport(node: ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration | ImportTypeNode) { + const moduleSpecifier = tryGetModuleSpecifierFromDeclaration(node); + const symbol = moduleSpecifier && resolver.tryFindAmbientModule(moduleSpecifier); + if (symbol?.declarations) { + for (const decl of symbol.declarations) { + if (isAmbientModule(decl) && getSourceFileOfNode(decl) !== currentSourceFile) { + trackReferencedAmbientModule(decl, symbol); + } + } + } + } + function handleSymbolAccessibilityError(symbolAccessibilityResult: SymbolAccessibilityResult) { if (symbolAccessibilityResult.accessibility === SymbolAccessibility.Accessible) { // Add aliases back onto the possible imports list if they're not there so we can try them again with updated visibility info @@ -1312,6 +1325,7 @@ export function transformDeclarations(context: TransformationContext) { } case SyntaxKind.ImportType: { if (!isLiteralImportTypeNode(input)) return cleanup(input); + trackReferencedAmbientModuleFromImport(input); return cleanup(factory.updateImportTypeNode( input, factory.updateLiteralTypeNode(input.argument, rewriteModuleSpecifier(input, input.argument.literal)), @@ -1370,6 +1384,7 @@ export function transformDeclarations(context: TransformationContext) { } resultHasScopeMarker = true; // Always visible if the parent node isn't dropped for being not visible + trackReferencedAmbientModuleFromImport(input); // Rewrite external module names if necessary return factory.updateExportDeclaration( input, @@ -1456,10 +1471,18 @@ export function transformDeclarations(context: TransformationContext) { if (shouldStripInternal(input)) return; switch (input.kind) { case SyntaxKind.ImportEqualsDeclaration: { - return transformImportEqualsDeclaration(input); + const transformed = transformImportEqualsDeclaration(input); + if (transformed) { + trackReferencedAmbientModuleFromImport(input); + } + return transformed; } case SyntaxKind.ImportDeclaration: { - return transformImportDeclaration(input); + const transformed = transformImportDeclaration(input); + if (transformed) { + trackReferencedAmbientModuleFromImport(input); + } + return transformed; } } if (isDeclaration(input) && isDeclarationAndNotVisible(input)) return; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9a185a49232..a64c4c86b59 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -5675,6 +5675,7 @@ export interface EmitResolver { isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement): boolean; getDeclarationStatementsForSourceFile(node: SourceFile, flags: NodeBuilderFlags, tracker: SymbolTracker, bundled?: boolean): Statement[] | undefined; isImportRequiredByAugmentation(decl: ImportDeclaration): boolean; + tryFindAmbientModule(moduleReferenceExpression: Expression): Symbol | undefined; } // dprint-ignore diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 126b74d91da..8d4a3d24969 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3975,12 +3975,13 @@ export function isFunctionSymbol(symbol: Symbol | undefined) { } /** @internal */ -export function tryGetModuleSpecifierFromDeclaration(node: AnyImportOrBareOrAccessedRequire | AliasDeclarationNode): StringLiteralLike | undefined { +export function tryGetModuleSpecifierFromDeclaration(node: AnyImportOrBareOrAccessedRequire | AliasDeclarationNode | ExportDeclaration | ImportTypeNode): StringLiteralLike | undefined { switch (node.kind) { case SyntaxKind.VariableDeclaration: case SyntaxKind.BindingElement: return findAncestor(node.initializer, (node): node is RequireOrImportCall => isRequireCall(node, /*requireStringLiteralLikeArgument*/ true))?.arguments[0]; case SyntaxKind.ImportDeclaration: + case SyntaxKind.ExportDeclaration: return tryCast(node.moduleSpecifier, isStringLiteralLike); case SyntaxKind.ImportEqualsDeclaration: return tryCast(tryCast(node.moduleReference, isExternalModuleReference)?.expression, isStringLiteralLike); @@ -3992,6 +3993,8 @@ export function tryGetModuleSpecifierFromDeclaration(node: AnyImportOrBareOrAcce return tryCast(node.parent.parent.moduleSpecifier, isStringLiteralLike); case SyntaxKind.ImportSpecifier: return tryCast(node.parent.parent.parent.moduleSpecifier, isStringLiteralLike); + case SyntaxKind.ImportType: + return isLiteralImportTypeNode(node) ? node.argument.literal : undefined; default: Debug.assertNever(node); } diff --git a/tests/baselines/reference/declarationEmitBundleWithAmbientReferences.js b/tests/baselines/reference/declarationEmitBundleWithAmbientReferences.js index 0d6946277ad..ece01c1794a 100644 --- a/tests/baselines/reference/declarationEmitBundleWithAmbientReferences.js +++ b/tests/baselines/reference/declarationEmitBundleWithAmbientReferences.js @@ -37,6 +37,7 @@ define("conditional_directive_field", ["require", "exports"], function (require, //// [datastore.bundle.d.ts] +/// declare module "datastore_result" { import { Result } from "lib/result"; export type T = Result; diff --git a/tests/baselines/reference/declarationEmitTripleSlashReferenceAmbientModule.js b/tests/baselines/reference/declarationEmitTripleSlashReferenceAmbientModule.js new file mode 100644 index 00000000000..b283b08edcd --- /dev/null +++ b/tests/baselines/reference/declarationEmitTripleSlashReferenceAmbientModule.js @@ -0,0 +1,31 @@ +//// [tests/cases/compiler/declarationEmitTripleSlashReferenceAmbientModule.ts] //// + +//// [index.d.ts] +declare module "url" { + export class Url {} + export function parse(): Url; +} + +//// [usage1.ts] +export { parse } from "url"; + +//// [usage2.ts] +import { parse } from "url"; +export const thing: import("url").Url = parse(); + +//// [usage3.ts] +import { parse } from "url"; +export const thing = parse(); + + + + +//// [usage1.d.ts] +/// +export { parse } from "url"; +//// [usage2.d.ts] +/// +export declare const thing: import("url").Url; +//// [usage3.d.ts] +/// +export declare const thing: import("url").Url; diff --git a/tests/baselines/reference/nodeModulesAllowJsImportHelpersCollisions2(module=node16).js b/tests/baselines/reference/nodeModulesAllowJsImportHelpersCollisions2(module=node16).js index 7c584a82861..0d0046de24d 100644 --- a/tests/baselines/reference/nodeModulesAllowJsImportHelpersCollisions2(module=node16).js +++ b/tests/baselines/reference/nodeModulesAllowJsImportHelpersCollisions2(module=node16).js @@ -40,6 +40,7 @@ export * as fs from "fs"; //// [index.d.ts] +/// export * from "fs"; export * as fs from "fs"; //// [index.d.ts] diff --git a/tests/baselines/reference/nodeModulesAllowJsImportHelpersCollisions2(module=nodenext).js b/tests/baselines/reference/nodeModulesAllowJsImportHelpersCollisions2(module=nodenext).js index 7c584a82861..0d0046de24d 100644 --- a/tests/baselines/reference/nodeModulesAllowJsImportHelpersCollisions2(module=nodenext).js +++ b/tests/baselines/reference/nodeModulesAllowJsImportHelpersCollisions2(module=nodenext).js @@ -40,6 +40,7 @@ export * as fs from "fs"; //// [index.d.ts] +/// export * from "fs"; export * as fs from "fs"; //// [index.d.ts] diff --git a/tests/baselines/reference/nodeModulesImportAssignments(module=node16).js b/tests/baselines/reference/nodeModulesImportAssignments(module=node16).js index e56ab00bb87..779a5236c5d 100644 --- a/tests/baselines/reference/nodeModulesImportAssignments(module=node16).js +++ b/tests/baselines/reference/nodeModulesImportAssignments(module=node16).js @@ -58,8 +58,11 @@ export { fs2 }; //// [index.d.ts] +/// export import fs2 = require("fs"); //// [index.d.ts] +/// export import fs2 = require("fs"); //// [file.d.ts] +/// export import fs2 = require("fs"); diff --git a/tests/baselines/reference/nodeModulesImportAssignments(module=nodenext).js b/tests/baselines/reference/nodeModulesImportAssignments(module=nodenext).js index e56ab00bb87..779a5236c5d 100644 --- a/tests/baselines/reference/nodeModulesImportAssignments(module=nodenext).js +++ b/tests/baselines/reference/nodeModulesImportAssignments(module=nodenext).js @@ -58,8 +58,11 @@ export { fs2 }; //// [index.d.ts] +/// export import fs2 = require("fs"); //// [index.d.ts] +/// export import fs2 = require("fs"); //// [file.d.ts] +/// export import fs2 = require("fs"); diff --git a/tests/baselines/reference/nodeModulesImportHelpersCollisions2(module=node16).js b/tests/baselines/reference/nodeModulesImportHelpersCollisions2(module=node16).js index e8e9b78464c..53eeb9bd5a7 100644 --- a/tests/baselines/reference/nodeModulesImportHelpersCollisions2(module=node16).js +++ b/tests/baselines/reference/nodeModulesImportHelpersCollisions2(module=node16).js @@ -40,8 +40,10 @@ export * as fs from "fs"; //// [index.d.ts] +/// export * from "fs"; export * as fs from "fs"; //// [index.d.ts] +/// export * from "fs"; export * as fs from "fs"; diff --git a/tests/baselines/reference/nodeModulesImportHelpersCollisions2(module=nodenext).js b/tests/baselines/reference/nodeModulesImportHelpersCollisions2(module=nodenext).js index e8e9b78464c..53eeb9bd5a7 100644 --- a/tests/baselines/reference/nodeModulesImportHelpersCollisions2(module=nodenext).js +++ b/tests/baselines/reference/nodeModulesImportHelpersCollisions2(module=nodenext).js @@ -40,8 +40,10 @@ export * as fs from "fs"; //// [index.d.ts] +/// export * from "fs"; export * as fs from "fs"; //// [index.d.ts] +/// export * from "fs"; export * as fs from "fs"; diff --git a/tests/baselines/reference/nodeModulesImportHelpersCollisions3(module=node16).js b/tests/baselines/reference/nodeModulesImportHelpersCollisions3(module=node16).js index 8abf8ba9ada..5f98164aaaa 100644 --- a/tests/baselines/reference/nodeModulesImportHelpersCollisions3(module=node16).js +++ b/tests/baselines/reference/nodeModulesImportHelpersCollisions3(module=node16).js @@ -39,6 +39,8 @@ export { default } from "fs"; //// [index.d.ts] +/// export { default } from "fs"; //// [index.d.ts] +/// export { default } from "fs"; diff --git a/tests/baselines/reference/nodeModulesImportHelpersCollisions3(module=nodenext).js b/tests/baselines/reference/nodeModulesImportHelpersCollisions3(module=nodenext).js index 8abf8ba9ada..5f98164aaaa 100644 --- a/tests/baselines/reference/nodeModulesImportHelpersCollisions3(module=nodenext).js +++ b/tests/baselines/reference/nodeModulesImportHelpersCollisions3(module=nodenext).js @@ -39,6 +39,8 @@ export { default } from "fs"; //// [index.d.ts] +/// export { default } from "fs"; //// [index.d.ts] +/// export { default } from "fs"; diff --git a/tests/cases/compiler/declarationEmitTripleSlashReferenceAmbientModule.ts b/tests/cases/compiler/declarationEmitTripleSlashReferenceAmbientModule.ts new file mode 100644 index 00000000000..df9274fa371 --- /dev/null +++ b/tests/cases/compiler/declarationEmitTripleSlashReferenceAmbientModule.ts @@ -0,0 +1,20 @@ +// @declaration: true +// @emitDeclarationOnly: true +// @noTypesAndSymbols: true + +// @Filename: /node_modules/@types/node/index.d.ts +declare module "url" { + export class Url {} + export function parse(): Url; +} + +// @Filename: /usage1.ts +export { parse } from "url"; + +// @Filename: /usage2.ts +import { parse } from "url"; +export const thing: import("url").Url = parse(); + +// @Filename: /usage3.ts +import { parse } from "url"; +export const thing = parse();