Retain imports in declaration emit if they augment an export of the importing file (#37820)

* Retain imports in declaration emit if they augment an export of the importing file

* (sp)

* Check that a merge occurs, just because
This commit is contained in:
Wesley Wigham 2020-04-13 12:31:14 -07:00 committed by GitHub
parent a8e1ad4e62
commit 141ee01c8c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 203 additions and 1 deletions

View File

@ -36118,9 +36118,31 @@ namespace ts {
return !node.locals ? [] : nodeBuilder.symbolTableToDeclarationStatements(node.locals, node, flags, tracker, bundled);
}
return !sym.exports ? [] : nodeBuilder.symbolTableToDeclarationStatements(sym.exports, node, flags, tracker, bundled);
}
},
isImportRequiredByAugmentation,
};
function isImportRequiredByAugmentation(node: ImportDeclaration) {
const file = getSourceFileOfNode(node);
if (!file.symbol) return false;
const importTarget = getExternalModuleFileFromDeclaration(node);
if (!importTarget) return false;
if (importTarget === file) return false;
const exports = getExportsOfModule(file.symbol);
for (const s of arrayFrom(exports.values())) {
if (s.mergeId) {
const merged = getMergedSymbol(s);
for (const d of merged.declarations) {
const declFile = getSourceFileOfNode(d);
if (declFile === importTarget) {
return true;
}
}
}
}
return false;
}
function isInHeritageClause(node: PropertyAccessEntityNameExpression) {
return node.parent && node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && node.parent.parent && node.parent.parent.kind === SyntaxKind.HeritageClause;
}

View File

@ -664,6 +664,7 @@ namespace ts {
getSymbolOfExternalModuleSpecifier: notImplemented,
isBindingCapturedByNode: notImplemented,
getDeclarationStatementsForSourceFile: notImplemented,
isImportRequiredByAugmentation: notImplemented,
};
/*@internal*/

View File

@ -717,6 +717,16 @@ namespace ts {
rewriteModuleSpecifier(decl, decl.moduleSpecifier)
);
}
// Augmentation of export depends on import
if (resolver.isImportRequiredByAugmentation(decl)) {
return updateImportDeclaration(
decl,
/*decorators*/ undefined,
decl.modifiers,
/*importClause*/ undefined,
rewriteModuleSpecifier(decl, decl.moduleSpecifier)
);
}
// Nothing visible
}

View File

@ -4004,6 +4004,7 @@ namespace ts {
getSymbolOfExternalModuleSpecifier(node: StringLiteralLike): Symbol | undefined;
isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement): boolean;
getDeclarationStatementsForSourceFile(node: SourceFile, flags: NodeBuilderFlags, tracker: SymbolTracker, bundled?: boolean): Statement[] | undefined;
isImportRequiredByAugmentation(decl: ImportDeclaration): boolean;
}
export const enum SymbolFlags {

View File

@ -0,0 +1,56 @@
//// [tests/cases/compiler/declarationEmitForModuleImportingModuleAugmentationRetainsImport.ts] ////
//// [child1.ts]
import { ParentThing } from './parent';
declare module './parent' {
interface ParentThing {
add: (a: number, b: number) => number;
}
}
export function child1(prototype: ParentThing) {
prototype.add = (a: number, b: number) => a + b;
}
//// [parent.ts]
import { child1 } from './child1'; // this import should still exist in some form in the output, since it augments this module
export class ParentThing implements ParentThing {}
child1(ParentThing.prototype);
//// [parent.js]
"use strict";
exports.__esModule = true;
exports.ParentThing = void 0;
var child1_1 = require("./child1"); // this import should still exist in some form in the output, since it augments this module
var ParentThing = /** @class */ (function () {
function ParentThing() {
}
return ParentThing;
}());
exports.ParentThing = ParentThing;
child1_1.child1(ParentThing.prototype);
//// [child1.js]
"use strict";
exports.__esModule = true;
exports.child1 = void 0;
function child1(prototype) {
prototype.add = function (a, b) { return a + b; };
}
exports.child1 = child1;
//// [parent.d.ts]
import './child1';
export declare class ParentThing implements ParentThing {
}
//// [child1.d.ts]
import { ParentThing } from './parent';
declare module './parent' {
interface ParentThing {
add: (a: number, b: number) => number;
}
}
export declare function child1(prototype: ParentThing): void;

View File

@ -0,0 +1,46 @@
=== tests/cases/compiler/child1.ts ===
import { ParentThing } from './parent';
>ParentThing : Symbol(ParentThing, Decl(child1.ts, 0, 8))
declare module './parent' {
>'./parent' : Symbol("tests/cases/compiler/parent", Decl(parent.ts, 0, 0), Decl(child1.ts, 0, 39))
interface ParentThing {
>ParentThing : Symbol(ParentThing, Decl(parent.ts, 0, 34), Decl(child1.ts, 2, 27))
add: (a: number, b: number) => number;
>add : Symbol(ParentThing.add, Decl(child1.ts, 3, 27))
>a : Symbol(a, Decl(child1.ts, 4, 14))
>b : Symbol(b, Decl(child1.ts, 4, 24))
}
}
export function child1(prototype: ParentThing) {
>child1 : Symbol(child1, Decl(child1.ts, 6, 1))
>prototype : Symbol(prototype, Decl(child1.ts, 8, 23))
>ParentThing : Symbol(ParentThing, Decl(child1.ts, 0, 8))
prototype.add = (a: number, b: number) => a + b;
>prototype.add : Symbol(ParentThing.add, Decl(child1.ts, 3, 27))
>prototype : Symbol(prototype, Decl(child1.ts, 8, 23))
>add : Symbol(ParentThing.add, Decl(child1.ts, 3, 27))
>a : Symbol(a, Decl(child1.ts, 9, 21))
>b : Symbol(b, Decl(child1.ts, 9, 31))
>a : Symbol(a, Decl(child1.ts, 9, 21))
>b : Symbol(b, Decl(child1.ts, 9, 31))
}
=== tests/cases/compiler/parent.ts ===
import { child1 } from './child1'; // this import should still exist in some form in the output, since it augments this module
>child1 : Symbol(child1, Decl(parent.ts, 0, 8))
export class ParentThing implements ParentThing {}
>ParentThing : Symbol(ParentThing, Decl(parent.ts, 0, 34), Decl(child1.ts, 2, 27))
>ParentThing : Symbol(ParentThing, Decl(parent.ts, 0, 34), Decl(child1.ts, 2, 27))
child1(ParentThing.prototype);
>child1 : Symbol(child1, Decl(parent.ts, 0, 8))
>ParentThing.prototype : Symbol(ParentThing.prototype)
>ParentThing : Symbol(ParentThing, Decl(parent.ts, 0, 34), Decl(child1.ts, 2, 27))
>prototype : Symbol(ParentThing.prototype)

View File

@ -0,0 +1,46 @@
=== tests/cases/compiler/child1.ts ===
import { ParentThing } from './parent';
>ParentThing : typeof ParentThing
declare module './parent' {
>'./parent' : typeof import("tests/cases/compiler/parent")
interface ParentThing {
add: (a: number, b: number) => number;
>add : (a: number, b: number) => number
>a : number
>b : number
}
}
export function child1(prototype: ParentThing) {
>child1 : (prototype: ParentThing) => void
>prototype : ParentThing
prototype.add = (a: number, b: number) => a + b;
>prototype.add = (a: number, b: number) => a + b : (a: number, b: number) => number
>prototype.add : (a: number, b: number) => number
>prototype : ParentThing
>add : (a: number, b: number) => number
>(a: number, b: number) => a + b : (a: number, b: number) => number
>a : number
>b : number
>a + b : number
>a : number
>b : number
}
=== tests/cases/compiler/parent.ts ===
import { child1 } from './child1'; // this import should still exist in some form in the output, since it augments this module
>child1 : (prototype: ParentThing) => void
export class ParentThing implements ParentThing {}
>ParentThing : ParentThing
child1(ParentThing.prototype);
>child1(ParentThing.prototype) : void
>child1 : (prototype: ParentThing) => void
>ParentThing.prototype : ParentThing
>ParentThing : typeof ParentThing
>prototype : ParentThing

View File

@ -0,0 +1,20 @@
// @declaration: true
// @filename: child1.ts
import { ParentThing } from './parent';
declare module './parent' {
interface ParentThing {
add: (a: number, b: number) => number;
}
}
export function child1(prototype: ParentThing) {
prototype.add = (a: number, b: number) => a + b;
}
// @filename: parent.ts
import { child1 } from './child1'; // this import should still exist in some form in the output, since it augments this module
export class ParentThing implements ParentThing {}
child1(ParentThing.prototype);