Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(jsii): consider interfaces from erased base classes #491

Merged
merged 3 commits into from
May 5, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions packages/jsii-calc/lib/erasures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,12 @@ interface IJSII417PrivateBase extends IJSII417PublicBaseOfBase {
export interface IJSII417Derived extends IJSII417PrivateBase {
baz(): void;
}

//
// Interfaces should be copied from erased classes to public classes
// https://github.com/awslabs/jsii/issues/487
//
// tslint:disable-next-line:no-empty-interface
export interface IJsii487External { }
class Jsii487Internal implements IJsii487External { }
export class Jsii487Derived extends Jsii487Internal { }
26 changes: 25 additions & 1 deletion packages/jsii-calc/test/assembly.jsii
Original file line number Diff line number Diff line change
Expand Up @@ -3035,6 +3035,16 @@
}
]
},
"jsii-calc.IJsii487External": {
"assembly": "jsii-calc",
"fqn": "jsii-calc.IJsii487External",
"kind": "interface",
"locationInModule": {
"filename": "lib/erasures.ts",
"line": 46
},
"name": "IJsii487External"
},
"jsii-calc.IMutableObjectLiteral": {
"assembly": "jsii-calc",
"fqn": "jsii-calc.IMutableObjectLiteral",
Expand Down Expand Up @@ -4038,6 +4048,20 @@
}
]
},
"jsii-calc.Jsii487Derived": {
"assembly": "jsii-calc",
"fqn": "jsii-calc.Jsii487Derived",
"initializer": {},
"interfaces": [
"jsii-calc.IJsii487External"
],
"kind": "class",
"locationInModule": {
"filename": "lib/erasures.ts",
"line": 48
},
"name": "Jsii487Derived"
},
"jsii-calc.JsiiAgent": {
"assembly": "jsii-calc",
"docs": {
Expand Down Expand Up @@ -6744,5 +6768,5 @@
}
},
"version": "0.10.3",
"fingerprint": "HkyspjelXmNd6gQibMCkuk+WfsM6Q9e3uZLl2ee3kbY="
"fingerprint": "CSE0eCLDxK7s+qnmR58ZJo0mcEROi4f8Sxlx2pHaBDs="
}
40 changes: 28 additions & 12 deletions packages/jsii/lib/assembler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -450,10 +450,11 @@ export class Assembler implements Emitter {
continue;
}

/*
* Crawl up the inheritance tree if the current base type is not exported, so we identify the type(s) to be
* erased, and identify the closest exported base class, should there be one.
*/
//
// base classes ("extends foo")

// Crawl up the inheritance tree if the current base type is not exported, so we identify the type(s) to be
// erased, and identify the closest exported base class, should there be one.
// tslint:disable-next-line: no-bitwise
while (base && this._isPrivateOrInternal(base.symbol)) {
LOG.debug(`Base class of ${colors.green(jsiiType.fqn)} named ${colors.green(base.symbol.name)} is not exported, erasing it...`);
Expand Down Expand Up @@ -482,15 +483,29 @@ export class Assembler implements Emitter {
});
jsiiType.base = ref.fqn;
}
for (const clause of (type.symbol.valueDeclaration as ts.ClassDeclaration).heritageClauses || []) {
if (clause.token === ts.SyntaxKind.ExtendsKeyword) {
// Handled by `getBaseTypes`
continue;
} else if (clause.token !== ts.SyntaxKind.ImplementsKeyword) {
this._diagnostic(clause, ts.DiagnosticCategory.Error, `Ignoring ${ts.SyntaxKind[clause.token]} heritage clause`);
continue;

//
// base interfaces ("implements foo")

// collect all "implements" declarations from the current type and all
// erased base types (because otherwise we lose them, see jsii#487)
const implementsClauses = new Array<ts.HeritageClause>();
for (const heritage of [ type, ...erasedBases ].map(t => (t.symbol.valueDeclaration as ts.ClassDeclaration).heritageClauses || [])) {
for (const clause of heritage) {
if (clause.token === ts.SyntaxKind.ExtendsKeyword) {
// Handled by `getBaseTypes`
continue;
} else if (clause.token !== ts.SyntaxKind.ImplementsKeyword) {
this._diagnostic(clause, ts.DiagnosticCategory.Error, `Ignoring ${ts.SyntaxKind[clause.token]} heritage clause`);
continue;
}

implementsClauses.push(clause);
}
}

// process all "implements" clauses
for (const clause of implementsClauses) {
const { interfaces } = await this._processBaseInterfaces(fqn, clause.types.map(t => this._typeChecker.getTypeFromTypeNode(t)));
jsiiType.interfaces = apply(interfaces, arr => arr.map(i => i.fqn));
if (interfaces) {
Expand All @@ -512,7 +527,8 @@ export class Assembler implements Emitter {

const allDeclarations: Array<{ decl: ts.Declaration, type: ts.InterfaceType | ts.BaseType }>
= type.symbol.declarations.map(decl => ({ decl, type }));
// Considering erased bases' declarations, too, so they are "blended in"

// Considering erased bases' declarations, too, so they are "blended in"
for (const base of erasedBases) {
allDeclarations.push(...base.symbol.declarations.map(decl => ({ decl, type: base })));
}
Expand Down