Skip to content

Commit 141ee01

Browse files
authored
Retain imports in declaration emit if they augment an export of the importing file (microsoft#37820)
* Retain imports in declaration emit if they augment an export of the importing file * (sp) * Check that a merge occurs, just because
1 parent a8e1ad4 commit 141ee01

8 files changed

+203
-1
lines changed

src/compiler/checker.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36118,9 +36118,31 @@ namespace ts {
3611836118
return !node.locals ? [] : nodeBuilder.symbolTableToDeclarationStatements(node.locals, node, flags, tracker, bundled);
3611936119
}
3612036120
return !sym.exports ? [] : nodeBuilder.symbolTableToDeclarationStatements(sym.exports, node, flags, tracker, bundled);
36121-
}
36121+
},
36122+
isImportRequiredByAugmentation,
3612236123
};
3612336124

36125+
function isImportRequiredByAugmentation(node: ImportDeclaration) {
36126+
const file = getSourceFileOfNode(node);
36127+
if (!file.symbol) return false;
36128+
const importTarget = getExternalModuleFileFromDeclaration(node);
36129+
if (!importTarget) return false;
36130+
if (importTarget === file) return false;
36131+
const exports = getExportsOfModule(file.symbol);
36132+
for (const s of arrayFrom(exports.values())) {
36133+
if (s.mergeId) {
36134+
const merged = getMergedSymbol(s);
36135+
for (const d of merged.declarations) {
36136+
const declFile = getSourceFileOfNode(d);
36137+
if (declFile === importTarget) {
36138+
return true;
36139+
}
36140+
}
36141+
}
36142+
}
36143+
return false;
36144+
}
36145+
3612436146
function isInHeritageClause(node: PropertyAccessEntityNameExpression) {
3612536147
return node.parent && node.parent.kind === SyntaxKind.ExpressionWithTypeArguments && node.parent.parent && node.parent.parent.kind === SyntaxKind.HeritageClause;
3612636148
}

src/compiler/emitter.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,7 @@ namespace ts {
664664
getSymbolOfExternalModuleSpecifier: notImplemented,
665665
isBindingCapturedByNode: notImplemented,
666666
getDeclarationStatementsForSourceFile: notImplemented,
667+
isImportRequiredByAugmentation: notImplemented,
667668
};
668669

669670
/*@internal*/

src/compiler/transformers/declarations.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,16 @@ namespace ts {
717717
rewriteModuleSpecifier(decl, decl.moduleSpecifier)
718718
);
719719
}
720+
// Augmentation of export depends on import
721+
if (resolver.isImportRequiredByAugmentation(decl)) {
722+
return updateImportDeclaration(
723+
decl,
724+
/*decorators*/ undefined,
725+
decl.modifiers,
726+
/*importClause*/ undefined,
727+
rewriteModuleSpecifier(decl, decl.moduleSpecifier)
728+
);
729+
}
720730
// Nothing visible
721731
}
722732

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4004,6 +4004,7 @@ namespace ts {
40044004
getSymbolOfExternalModuleSpecifier(node: StringLiteralLike): Symbol | undefined;
40054005
isBindingCapturedByNode(node: Node, decl: VariableDeclaration | BindingElement): boolean;
40064006
getDeclarationStatementsForSourceFile(node: SourceFile, flags: NodeBuilderFlags, tracker: SymbolTracker, bundled?: boolean): Statement[] | undefined;
4007+
isImportRequiredByAugmentation(decl: ImportDeclaration): boolean;
40074008
}
40084009

40094010
export const enum SymbolFlags {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//// [tests/cases/compiler/declarationEmitForModuleImportingModuleAugmentationRetainsImport.ts] ////
2+
3+
//// [child1.ts]
4+
import { ParentThing } from './parent';
5+
6+
declare module './parent' {
7+
interface ParentThing {
8+
add: (a: number, b: number) => number;
9+
}
10+
}
11+
12+
export function child1(prototype: ParentThing) {
13+
prototype.add = (a: number, b: number) => a + b;
14+
}
15+
16+
//// [parent.ts]
17+
import { child1 } from './child1'; // this import should still exist in some form in the output, since it augments this module
18+
19+
export class ParentThing implements ParentThing {}
20+
21+
child1(ParentThing.prototype);
22+
23+
//// [parent.js]
24+
"use strict";
25+
exports.__esModule = true;
26+
exports.ParentThing = void 0;
27+
var child1_1 = require("./child1"); // this import should still exist in some form in the output, since it augments this module
28+
var ParentThing = /** @class */ (function () {
29+
function ParentThing() {
30+
}
31+
return ParentThing;
32+
}());
33+
exports.ParentThing = ParentThing;
34+
child1_1.child1(ParentThing.prototype);
35+
//// [child1.js]
36+
"use strict";
37+
exports.__esModule = true;
38+
exports.child1 = void 0;
39+
function child1(prototype) {
40+
prototype.add = function (a, b) { return a + b; };
41+
}
42+
exports.child1 = child1;
43+
44+
45+
//// [parent.d.ts]
46+
import './child1';
47+
export declare class ParentThing implements ParentThing {
48+
}
49+
//// [child1.d.ts]
50+
import { ParentThing } from './parent';
51+
declare module './parent' {
52+
interface ParentThing {
53+
add: (a: number, b: number) => number;
54+
}
55+
}
56+
export declare function child1(prototype: ParentThing): void;
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
=== tests/cases/compiler/child1.ts ===
2+
import { ParentThing } from './parent';
3+
>ParentThing : Symbol(ParentThing, Decl(child1.ts, 0, 8))
4+
5+
declare module './parent' {
6+
>'./parent' : Symbol("tests/cases/compiler/parent", Decl(parent.ts, 0, 0), Decl(child1.ts, 0, 39))
7+
8+
interface ParentThing {
9+
>ParentThing : Symbol(ParentThing, Decl(parent.ts, 0, 34), Decl(child1.ts, 2, 27))
10+
11+
add: (a: number, b: number) => number;
12+
>add : Symbol(ParentThing.add, Decl(child1.ts, 3, 27))
13+
>a : Symbol(a, Decl(child1.ts, 4, 14))
14+
>b : Symbol(b, Decl(child1.ts, 4, 24))
15+
}
16+
}
17+
18+
export function child1(prototype: ParentThing) {
19+
>child1 : Symbol(child1, Decl(child1.ts, 6, 1))
20+
>prototype : Symbol(prototype, Decl(child1.ts, 8, 23))
21+
>ParentThing : Symbol(ParentThing, Decl(child1.ts, 0, 8))
22+
23+
prototype.add = (a: number, b: number) => a + b;
24+
>prototype.add : Symbol(ParentThing.add, Decl(child1.ts, 3, 27))
25+
>prototype : Symbol(prototype, Decl(child1.ts, 8, 23))
26+
>add : Symbol(ParentThing.add, Decl(child1.ts, 3, 27))
27+
>a : Symbol(a, Decl(child1.ts, 9, 21))
28+
>b : Symbol(b, Decl(child1.ts, 9, 31))
29+
>a : Symbol(a, Decl(child1.ts, 9, 21))
30+
>b : Symbol(b, Decl(child1.ts, 9, 31))
31+
}
32+
33+
=== tests/cases/compiler/parent.ts ===
34+
import { child1 } from './child1'; // this import should still exist in some form in the output, since it augments this module
35+
>child1 : Symbol(child1, Decl(parent.ts, 0, 8))
36+
37+
export class ParentThing implements ParentThing {}
38+
>ParentThing : Symbol(ParentThing, Decl(parent.ts, 0, 34), Decl(child1.ts, 2, 27))
39+
>ParentThing : Symbol(ParentThing, Decl(parent.ts, 0, 34), Decl(child1.ts, 2, 27))
40+
41+
child1(ParentThing.prototype);
42+
>child1 : Symbol(child1, Decl(parent.ts, 0, 8))
43+
>ParentThing.prototype : Symbol(ParentThing.prototype)
44+
>ParentThing : Symbol(ParentThing, Decl(parent.ts, 0, 34), Decl(child1.ts, 2, 27))
45+
>prototype : Symbol(ParentThing.prototype)
46+
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
=== tests/cases/compiler/child1.ts ===
2+
import { ParentThing } from './parent';
3+
>ParentThing : typeof ParentThing
4+
5+
declare module './parent' {
6+
>'./parent' : typeof import("tests/cases/compiler/parent")
7+
8+
interface ParentThing {
9+
add: (a: number, b: number) => number;
10+
>add : (a: number, b: number) => number
11+
>a : number
12+
>b : number
13+
}
14+
}
15+
16+
export function child1(prototype: ParentThing) {
17+
>child1 : (prototype: ParentThing) => void
18+
>prototype : ParentThing
19+
20+
prototype.add = (a: number, b: number) => a + b;
21+
>prototype.add = (a: number, b: number) => a + b : (a: number, b: number) => number
22+
>prototype.add : (a: number, b: number) => number
23+
>prototype : ParentThing
24+
>add : (a: number, b: number) => number
25+
>(a: number, b: number) => a + b : (a: number, b: number) => number
26+
>a : number
27+
>b : number
28+
>a + b : number
29+
>a : number
30+
>b : number
31+
}
32+
33+
=== tests/cases/compiler/parent.ts ===
34+
import { child1 } from './child1'; // this import should still exist in some form in the output, since it augments this module
35+
>child1 : (prototype: ParentThing) => void
36+
37+
export class ParentThing implements ParentThing {}
38+
>ParentThing : ParentThing
39+
40+
child1(ParentThing.prototype);
41+
>child1(ParentThing.prototype) : void
42+
>child1 : (prototype: ParentThing) => void
43+
>ParentThing.prototype : ParentThing
44+
>ParentThing : typeof ParentThing
45+
>prototype : ParentThing
46+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @declaration: true
2+
// @filename: child1.ts
3+
import { ParentThing } from './parent';
4+
5+
declare module './parent' {
6+
interface ParentThing {
7+
add: (a: number, b: number) => number;
8+
}
9+
}
10+
11+
export function child1(prototype: ParentThing) {
12+
prototype.add = (a: number, b: number) => a + b;
13+
}
14+
15+
// @filename: parent.ts
16+
import { child1 } from './child1'; // this import should still exist in some form in the output, since it augments this module
17+
18+
export class ParentThing implements ParentThing {}
19+
20+
child1(ParentThing.prototype);

0 commit comments

Comments
 (0)