Skip to content

Commit 769e483

Browse files
authored
Named extension methods so that they get included in the library's export scope (dart-archive#54)
* Named extension methods so that they get included in the library's export scope * Added type arguments to Futures and cleaned up visitName function
1 parent 28b8c1e commit 769e483

File tree

2 files changed

+64
-20
lines changed

2 files changed

+64
-20
lines changed

lib/declaration.ts

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,6 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
502502
let sourceFile = node as ts.SourceFile;
503503
this.visitMergingOverloads(sourceFile.statements);
504504
if (this.containsPromises) {
505-
this.addImport('dart:async', 'Completer');
506505
this.addImport('package:js/js_util.dart', 'promiseToFuture');
507506
this.emit(`@JS() abstract class Promise<T> {}\n`);
508507
}
@@ -843,17 +842,14 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
843842
keyword: string, decl: base.ClassLike|ts.TypeLiteralNode, name: ts.Identifier,
844843
typeParameters: ts.NodeArray<ts.TypeParameterDeclaration>,
845844
heritageClauses: ts.NodeArray<ts.HeritageClause>) {
845+
const visitName = () => {
846+
this.visitClassLikeName(name, typeParameters, heritageClauses, false);
847+
};
848+
const visitNameOfExtensions = () => {
849+
this.visitClassLikeName(name, typeParameters, heritageClauses, true);
850+
};
846851
this.emit(keyword);
847-
this.fc.visitTypeName(name);
848-
if (typeParameters) {
849-
this.emit('<');
850-
this.enterTypeArguments();
851-
this.visitList(typeParameters);
852-
this.exitTypeArguments();
853-
this.emit('>');
854-
}
855-
856-
this.visitEachIfPresent(heritageClauses);
852+
visitName();
857853
this.emit('{');
858854

859855
this.maybeEmitFakeConstructors(decl);
@@ -876,12 +872,32 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
876872
this.visitClassBody(decl, name);
877873
this.emit('}\n');
878874
if (this.promiseMethods.size) {
879-
this.emitMethodsAsExtensions(name, this.promiseMethods);
875+
this.emitMethodsAsExtensions(name, visitName, visitNameOfExtensions, this.promiseMethods);
880876
this.promiseMethods.clear();
881877
}
882878
this.emit('\n');
883879
}
884880

881+
private visitClassLikeName(
882+
name: ts.Identifier, typeParameters: ts.NodeArray<ts.TypeParameterDeclaration>,
883+
heritageClauses: ts.NodeArray<ts.HeritageClause>, extension: boolean) {
884+
this.fc.visitTypeName(name);
885+
886+
if (extension) {
887+
this.emitNoSpace('Extensions');
888+
}
889+
890+
if (typeParameters) {
891+
this.emit('<');
892+
this.enterTypeArguments();
893+
this.visitList(typeParameters);
894+
this.exitTypeArguments();
895+
this.emit('>');
896+
}
897+
898+
this.visitEachIfPresent(heritageClauses);
899+
}
900+
885901
private visitDeclarationMetadata(decl: ts.Declaration) {
886902
this.visitEachIfPresent(decl.modifiers);
887903

@@ -932,26 +948,32 @@ export default class DeclarationTranspiler extends base.TranspilerBase {
932948
}
933949

934950
private emitMethodsAsExtensions(
935-
className: ts.Identifier, methods: Set<ts.FunctionLikeDeclaration>) {
951+
className: ts.Identifier, visitName: () => void, visitNameOfExtensions: () => void,
952+
methods: Set<ts.FunctionLikeDeclaration>) {
936953
// Emit private class containing external methods
937954
this.emit(`@JS('${base.ident(className)}')`);
938955
this.emit(`abstract class _`);
939-
this.fc.visitTypeName(className);
956+
visitName();
940957
this.emit('{');
941958
for (const declaration of methods) {
942959
this.visitFunctionLike(declaration);
943960
}
944961
this.emit('}\n');
945962

946963
// Emit extensions on public class to expose methods
947-
this.emit('extension on');
948-
this.fc.visitTypeName(className);
964+
this.emit('extension');
965+
visitNameOfExtensions();
966+
this.emit('on');
967+
visitName();
949968
this.emit('{');
950969
for (const declaration of methods) {
951970
if (!base.isPromise(declaration.type)) {
952971
continue;
953972
}
954973
this.emit('Future');
974+
if (ts.isTypeReferenceNode(declaration.type)) {
975+
this.maybeVisitTypeArguments(declaration.type);
976+
}
955977
this.emit(base.ident(declaration.name));
956978
this.visitParameters(declaration.parameters, {namesOnly: false});
957979
this.emit('{');

test/declaration_test.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,7 @@ class X {
260260
it('should emit extension methods to return Futures from methods that return Promises', () => {
261261
expectTranslate(`declare interface MyMath {
262262
randomInRange(start: number, end: number): Promise<number>;
263-
}`).to.equal(`import "dart:async" show Completer;
264-
import "package:js/js_util.dart" show promiseToFuture;
263+
}`).to.equal(`import "package:js/js_util.dart" show promiseToFuture;
265264
266265
@anonymous
267266
@JS()
@@ -272,14 +271,37 @@ abstract class _MyMath {
272271
Promise<num> randomInRange(num start, num end);
273272
}
274273
275-
extension on MyMath {
276-
Future randomInRange(num start, num end) {
274+
extension MyMathExtensions on MyMath {
275+
Future<num> randomInRange(num start, num end) {
277276
final Object t = this;
278277
final _MyMath tt = t;
279278
return promiseToFuture(tt.randomInRange(start, end));
280279
}
281280
}
282281
282+
@JS()
283+
abstract class Promise<T> {}`);
284+
expectTranslate(`declare interface X<T> {
285+
f(a: T): Promise<T>;
286+
}`).to.equal(`import "package:js/js_util.dart" show promiseToFuture;
287+
288+
@anonymous
289+
@JS()
290+
abstract class X<T> {}
291+
292+
@JS('X')
293+
abstract class _X<T> {
294+
Promise<T> f(T a);
295+
}
296+
297+
extension XExtensions<T> on X<T> {
298+
Future<T> f(T a) {
299+
final Object t = this;
300+
final _X tt = t;
301+
return promiseToFuture(tt.f(a));
302+
}
303+
}
304+
283305
@JS()
284306
abstract class Promise<T> {}`);
285307
});

0 commit comments

Comments
 (0)