Skip to content

Commit 0baaea4

Browse files
[interop] Add support for destructured parameters (#469)
* [interop] Added support for destructured parameters Added support for working with destructured parameters, as well as other kind of parameters. These take the place of the param type, since destructuring in parameters is not allowed in Dart. However, this goes a step further and provides documentation on the parameter itself, providing the form of the param as documentation. Signed-off-by: Nike Okoronkwo <[email protected]> * removed comma in comment * added unknown case --------- Signed-off-by: Nike Okoronkwo <[email protected]>
1 parent af37d19 commit 0baaea4

File tree

5 files changed

+170
-23
lines changed

5 files changed

+170
-23
lines changed

web_generator/lib/src/ast/base.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:code_builder/code_builder.dart';
66

77
import '../interop_gen/namer.dart';
88
import 'documentation.dart';
9+
import 'helpers.dart';
910
import 'types.dart';
1011

1112
class GlobalOptions {
@@ -130,7 +131,7 @@ abstract class DeclarationType<T extends Declaration> extends Type {
130131
String get declarationName;
131132
}
132133

133-
class ParameterDeclaration {
134+
class ParameterDeclaration implements DocumentedDeclaration {
134135
final String name;
135136

136137
final bool optional;
@@ -139,15 +140,22 @@ class ParameterDeclaration {
139140

140141
final bool variadic;
141142

143+
@override
144+
Documentation? documentation;
145+
142146
ParameterDeclaration(
143147
{required this.name,
144148
this.optional = false,
145149
required this.type,
146-
this.variadic = false});
150+
this.variadic = false,
151+
this.documentation});
147152

148153
Parameter emit([DeclarationOptions? options]) {
154+
final (doc, annotations) = generateFromDocumentation(documentation);
149155
return Parameter((p) => p
150156
..name = name
157+
..annotations.addAll(annotations)
158+
..docs.addAll(doc)
151159
..type = type.emit(TypeOptions(nullable: optional)));
152160
}
153161
}

web_generator/lib/src/interop_gen/transform/transformer.dart

Lines changed: 98 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ class Transformer {
597597
id: id,
598598
scope: scope,
599599
static: isStatic,
600-
parameters: params.map((t) {
600+
parameters: params.mapIndexed((index, t) {
601601
ReferredType? paramType;
602602
final paramRawType = t.type;
603603
if (paramRawType case final ty? when ts.isTypeReferenceNode(ty)) {
@@ -612,7 +612,7 @@ class Transformer {
612612
paramType = parent?.asReferredType(
613613
parent.typeParameters.map((t) => GenericType(name: t.name)));
614614
}
615-
return _transformParameter(t, paramType);
615+
return _transformParameter(t, type: paramType, index: index);
616616
}).toList(),
617617
typeParameters:
618618
typeParams?.map(_transformTypeParamDeclaration).toList() ?? [],
@@ -654,7 +654,9 @@ class Transformer {
654654
id: id,
655655
dartName: dartName.trim().isEmpty ? null : dartName.trim(),
656656
name: name,
657-
parameters: params.map(_transformParameter).toList(),
657+
parameters: params
658+
.mapIndexed((index, p) => _transformParameter(p, index: index))
659+
.toList(),
658660
scope: scope,
659661
documentation: _parseAndTransformDocumentation(constructor));
660662
}
@@ -689,7 +691,9 @@ class Transformer {
689691
name: 'call',
690692
dartName: dartName,
691693
id: id,
692-
parameters: params.map(_transformParameter).toList(),
694+
parameters: params
695+
.mapIndexed((index, p) => _transformParameter(p, index: index))
696+
.toList(),
693697
typeParameters:
694698
typeParams?.map(_transformTypeParamDeclaration).toList() ?? [],
695699
returnType: methodType ??
@@ -730,7 +734,9 @@ class Transformer {
730734
}
731735

732736
final doc = _parseAndTransformDocumentation(indexSignature);
733-
final transformedParameters = params.map(_transformParameter).toList();
737+
final transformedParameters = params
738+
.mapIndexed((index, p) => _transformParameter(p, index: index))
739+
.toList();
734740
final type = indexerType ?? _transformType(indexSignature.type);
735741
final transformedTypeParams =
736742
typeParams?.map(_transformTypeParamDeclaration).toList() ?? [];
@@ -800,7 +806,9 @@ class Transformer {
800806
id: id,
801807
kind: MethodKind.getter,
802808
scope: scope,
803-
parameters: params.map(_transformParameter).toList(),
809+
parameters: params
810+
.mapIndexed((index, p) => _transformParameter(p, index: index))
811+
.toList(),
804812
typeParameters:
805813
typeParams?.map(_transformTypeParamDeclaration).toList() ?? [],
806814
returnType: methodType ??
@@ -830,7 +838,7 @@ class Transformer {
830838
dartName: dartName,
831839
kind: MethodKind.setter,
832840
id: id,
833-
parameters: params.map((t) {
841+
parameters: params.mapIndexed((index, t) {
834842
ReferredType? paramType;
835843
final paramRawType = t.type;
836844
if (paramRawType case final ty? when ts.isTypeReferenceNode(ty)) {
@@ -845,7 +853,7 @@ class Transformer {
845853
paramType = parent?.asReferredType(
846854
parent.typeParameters.map((t) => GenericType(name: t.name)));
847855
}
848-
return _transformParameter(t, paramType);
856+
return _transformParameter(t, type: paramType, index: index);
849857
}).toList(),
850858
scope: scope,
851859
typeParameters:
@@ -880,7 +888,9 @@ class Transformer {
880888
id: id,
881889
dartName: uniqueName,
882890
exported: isExported,
883-
parameters: params.map(_transformParameter).toList(),
891+
parameters: params
892+
.mapIndexed((index, p) => _transformParameter(p, index: index))
893+
.toList(),
884894
typeParameters:
885895
typeParams?.map(_transformTypeParamDeclaration).toList() ?? [],
886896
returnType: function.type != null
@@ -1053,7 +1063,7 @@ class Transformer {
10531063
}
10541064

10551065
ParameterDeclaration _transformParameter(TSParameterDeclaration parameter,
1056-
[Type? type]) {
1066+
{Type? type, int? index}) {
10571067
type ??= parameter.type != null
10581068
? _transformType(parameter.type!, parameter: true)
10591069
: BuiltinType.anyType;
@@ -1068,11 +1078,82 @@ class Transformer {
10681078
type: type,
10691079
variadic: isVariadic,
10701080
optional: isOptional);
1081+
case TSSyntaxKind.ObjectBindingPattern ||
1082+
TSSyntaxKind.ArrayBindingPattern:
1083+
Iterable<TSDeclaration> expandBindingPatterns(
1084+
@UnionOf(
1085+
[TSIdentifier, TSObjectBindingPattern, TSArrayBindingPattern])
1086+
TSNode name) {
1087+
switch (name.kind) {
1088+
case TSSyntaxKind.Identifier:
1089+
return [name as TSIdentifier];
1090+
case TSSyntaxKind.ObjectBindingPattern ||
1091+
TSSyntaxKind.ArrayBindingPattern:
1092+
return (name as TSBindingPattern)
1093+
.elements
1094+
.toDart
1095+
.map((e) => e.name == null
1096+
? <TSDeclaration>[]
1097+
: expandBindingPatterns(e.name!))
1098+
.flattenedToList;
1099+
default:
1100+
return [];
1101+
}
1102+
}
1103+
final name = parameter.name as TSBindingPattern;
1104+
// just return the object
1105+
final elements = expandBindingPatterns(name);
1106+
final elementText = name.getText();
1107+
final documentation = isVariadic
1108+
? null
1109+
: Documentation(docs: 'Parameter is of the form: $elementText');
1110+
1111+
final rearWord =
1112+
parameter.name.kind == TSSyntaxKind.ObjectBindingPattern
1113+
? 'obj'
1114+
: 'arr';
1115+
1116+
if (elements.isEmpty) {
1117+
return ParameterDeclaration(
1118+
name: 'unknown$rearWord',
1119+
type: type,
1120+
variadic: isVariadic,
1121+
optional: isOptional,
1122+
documentation: documentation);
1123+
} else if (elements.singleOrNull case final singleEl?) {
1124+
final singleElName = singleEl.kind == TSSyntaxKind.Identifier
1125+
? (singleEl as TSIdentifier).text
1126+
: (singleEl as TSNamedDeclaration).name?.text ?? 'unknown';
1127+
return ParameterDeclaration(
1128+
name: '$singleElName$rearWord',
1129+
type: type,
1130+
variadic: isVariadic,
1131+
optional: isOptional,
1132+
documentation: documentation);
1133+
} else {
1134+
final hash = AnonymousHasher.hashTuple(elements
1135+
.map((e) => e.kind == TSSyntaxKind.Identifier
1136+
? (e as TSIdentifier).text
1137+
: (e as TSNamedDeclaration).name?.text ?? '')
1138+
.toList());
1139+
return ParameterDeclaration(
1140+
name: '$rearWord${hash.substring(0, 3)}',
1141+
type: type,
1142+
variadic: isVariadic,
1143+
optional: isOptional,
1144+
documentation: documentation);
1145+
}
10711146
default:
1072-
// TODO: Support Destructured Object Parameters
1073-
// and Destructured Array Parameters
1074-
throw Exception(
1075-
'Unsupported Parameter Name kind ${parameter.name.kind}');
1147+
final elementText = parameter.name.getText();
1148+
final documentation = isVariadic
1149+
? null
1150+
: Documentation(docs: 'Parameter is of the form: $elementText');
1151+
return ParameterDeclaration(
1152+
name: 'unknown${index ?? ""}',
1153+
type: type,
1154+
variadic: isVariadic,
1155+
optional: isOptional,
1156+
documentation: documentation);
10761157
}
10771158
}
10781159

@@ -1247,8 +1328,9 @@ class Transformer {
12471328
case TSSyntaxKind.ConstructorType || TSSyntaxKind.FunctionType:
12481329
final funType = type as TSFunctionOrConstructorTypeNodeBase;
12491330

1250-
final parameters =
1251-
funType.parameters.toDart.map(_transformParameter).toList();
1331+
final parameters = funType.parameters.toDart
1332+
.mapIndexed((index, p) => _transformParameter(p, index: index))
1333+
.toList();
12521334

12531335
final typeParameters = funType.typeParameters?.toDart
12541336
.map(_transformTypeParamDeclaration)

web_generator/lib/src/js/typescript.types.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,26 @@ extension type TSParameterDeclaration._(JSObject _) implements TSDeclaration {
632632
external TSNode? get dotDotDotToken;
633633
}
634634

635+
@JS('BindingPattern')
636+
extension type TSBindingPattern<E extends TSNamedDeclaration>._(JSObject _)
637+
implements TSNode {
638+
external TSNodeArray<E> get elements;
639+
}
640+
641+
@JS('ObjectBindingPattern')
642+
extension type TSObjectBindingPattern._(JSObject _)
643+
implements TSBindingPattern<TSBindingElement> {}
644+
645+
@JS('ArrayBindingPattern')
646+
extension type TSArrayBindingPattern._(JSObject _)
647+
implements TSBindingPattern<TSBindingElement> {}
648+
649+
/** We do not need much from this other than its name */
650+
@JS('BindingElement')
651+
extension type TSBindingElement._(JSObject _) implements TSNamedDeclaration {
652+
external TSNode get name;
653+
}
654+
635655
@JS('TypeParameterDeclaration')
636656
extension type TSTypeParameterDeclaration._(JSObject _)
637657
implements TSDeclaration {

web_generator/test/integration/interop_gen/ts_typing_expected.dart

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,29 @@ external ProductOrrandomNonTypedProduct objectAsProduct(
2727
external _i1.JSArray<AnonymousType_9143117<T>>
2828
indexedArray<T extends _i1.JSAny?>(_i1.JSArray<T> arr);
2929
@_i1.JS()
30+
external double firstTwoNumbers(
31+
32+
/// Parameter is of the form: [a, b]
33+
_i1.JSArray<_i1.JSNumber> arr117);
34+
@_i1.JS()
35+
external double pointlessArrayFunction(
36+
37+
/// Parameter is of the form: [{}]
38+
_i1.JSArray<AnonymousType_4207514> unknownarr);
39+
@_i1.JS()
40+
external String productInfo(
41+
/// Parameter is of the form: { name, id }
42+
Product obj157, [
43+
AnonymousType_9686701? options,
44+
]);
45+
@_i1.JS()
46+
external String shortendedProductCartInfo(
47+
_i1.JSArray<Product> arr903, [
48+
_i1.JSArray<Product> arr9032,
49+
_i1.JSArray<Product> arr9033,
50+
_i1.JSArray<Product> arr9034,
51+
]);
52+
@_i1.JS()
3053
external String get myString;
3154
@_i1.JS()
3255
external _i1.JSArray<_i1.JSNumber> get myNumberArray;
@@ -163,6 +186,21 @@ extension type AnonymousType_9143117<T extends _i1.JSAny?>._(_i1.JSObject _)
163186

164187
external T value;
165188
}
189+
extension type AnonymousType_4207514._(_i1.JSObject _) implements _i1.JSObject {
190+
external AnonymousType_4207514({double a});
191+
192+
external double a;
193+
}
194+
extension type AnonymousType_9686701._(_i1.JSObject _) implements _i1.JSObject {
195+
external AnonymousType_9686701({
196+
bool search,
197+
bool showId,
198+
});
199+
200+
external bool? search;
201+
202+
external bool? showId;
203+
}
166204
extension type MyEnum_EnumType._(_i1.JSObject _) implements _i1.JSObject {
167205
static const MyEnum A = MyEnum._(0);
168206

@@ -376,11 +414,6 @@ extension type AnonymousIntersection_1711585._(_i1.JSAny _)
376414
AnonymousUnion_5737239 get asAnonymousUnion_5737239 =>
377415
(_ as AnonymousUnion_5737239);
378416
}
379-
extension type AnonymousType_4207514._(_i1.JSObject _) implements _i1.JSObject {
380-
external AnonymousType_4207514({double a});
381-
382-
external double a;
383-
}
384417
extension type AnonymousType_1806035._(_i1.JSObject _) implements _i1.JSObject {
385418
external AnonymousType_1806035({String b});
386419

web_generator/test/integration/interop_gen/ts_typing_input.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,7 @@ export declare const myThirdIntersection: string & {
9999
export declare const myTypeGymnastic:
100100
({ a: number } | { b: string }) &
101101
({ c: boolean } | ({ d: bigint } & { e: symbol }));
102+
export declare function firstTwoNumbers([a, b]: number[]): number;
103+
export declare function pointlessArrayFunction([{}]: {a: number}[]): number;
104+
export declare function productInfo({ name, id }: Product, options?: { search?: boolean, showId?: boolean }): string;
105+
export declare function shortendedProductCartInfo(...[{ name: prod1, id: prod1ID }, { name: prod2, id: prod2ID }]: Product[]): string;

0 commit comments

Comments
 (0)