diff --git a/web_generator/lib/src/ast/base.dart b/web_generator/lib/src/ast/base.dart index e3c43504..a8064e75 100644 --- a/web_generator/lib/src/ast/base.dart +++ b/web_generator/lib/src/ast/base.dart @@ -6,6 +6,7 @@ import 'package:code_builder/code_builder.dart'; import '../interop_gen/namer.dart'; import 'documentation.dart'; +import 'helpers.dart'; import 'types.dart'; class GlobalOptions { @@ -130,7 +131,7 @@ abstract class DeclarationType extends Type { String get declarationName; } -class ParameterDeclaration { +class ParameterDeclaration implements DocumentedDeclaration { final String name; final bool optional; @@ -139,15 +140,22 @@ class ParameterDeclaration { final bool variadic; + @override + Documentation? documentation; + ParameterDeclaration( {required this.name, this.optional = false, required this.type, - this.variadic = false}); + this.variadic = false, + this.documentation}); Parameter emit([DeclarationOptions? options]) { + final (doc, annotations) = generateFromDocumentation(documentation); return Parameter((p) => p ..name = name + ..annotations.addAll(annotations) + ..docs.addAll(doc) ..type = type.emit(TypeOptions(nullable: optional))); } } diff --git a/web_generator/lib/src/interop_gen/transform/transformer.dart b/web_generator/lib/src/interop_gen/transform/transformer.dart index 3237e4ee..71ec6429 100644 --- a/web_generator/lib/src/interop_gen/transform/transformer.dart +++ b/web_generator/lib/src/interop_gen/transform/transformer.dart @@ -597,7 +597,7 @@ class Transformer { id: id, scope: scope, static: isStatic, - parameters: params.map((t) { + parameters: params.mapIndexed((index, t) { ReferredType? paramType; final paramRawType = t.type; if (paramRawType case final ty? when ts.isTypeReferenceNode(ty)) { @@ -612,7 +612,7 @@ class Transformer { paramType = parent?.asReferredType( parent.typeParameters.map((t) => GenericType(name: t.name))); } - return _transformParameter(t, paramType); + return _transformParameter(t, type: paramType, index: index); }).toList(), typeParameters: typeParams?.map(_transformTypeParamDeclaration).toList() ?? [], @@ -654,7 +654,9 @@ class Transformer { id: id, dartName: dartName.trim().isEmpty ? null : dartName.trim(), name: name, - parameters: params.map(_transformParameter).toList(), + parameters: params + .mapIndexed((index, p) => _transformParameter(p, index: index)) + .toList(), scope: scope, documentation: _parseAndTransformDocumentation(constructor)); } @@ -689,7 +691,9 @@ class Transformer { name: 'call', dartName: dartName, id: id, - parameters: params.map(_transformParameter).toList(), + parameters: params + .mapIndexed((index, p) => _transformParameter(p, index: index)) + .toList(), typeParameters: typeParams?.map(_transformTypeParamDeclaration).toList() ?? [], returnType: methodType ?? @@ -730,7 +734,9 @@ class Transformer { } final doc = _parseAndTransformDocumentation(indexSignature); - final transformedParameters = params.map(_transformParameter).toList(); + final transformedParameters = params + .mapIndexed((index, p) => _transformParameter(p, index: index)) + .toList(); final type = indexerType ?? _transformType(indexSignature.type); final transformedTypeParams = typeParams?.map(_transformTypeParamDeclaration).toList() ?? []; @@ -800,7 +806,9 @@ class Transformer { id: id, kind: MethodKind.getter, scope: scope, - parameters: params.map(_transformParameter).toList(), + parameters: params + .mapIndexed((index, p) => _transformParameter(p, index: index)) + .toList(), typeParameters: typeParams?.map(_transformTypeParamDeclaration).toList() ?? [], returnType: methodType ?? @@ -830,7 +838,7 @@ class Transformer { dartName: dartName, kind: MethodKind.setter, id: id, - parameters: params.map((t) { + parameters: params.mapIndexed((index, t) { ReferredType? paramType; final paramRawType = t.type; if (paramRawType case final ty? when ts.isTypeReferenceNode(ty)) { @@ -845,7 +853,7 @@ class Transformer { paramType = parent?.asReferredType( parent.typeParameters.map((t) => GenericType(name: t.name))); } - return _transformParameter(t, paramType); + return _transformParameter(t, type: paramType, index: index); }).toList(), scope: scope, typeParameters: @@ -880,7 +888,9 @@ class Transformer { id: id, dartName: uniqueName, exported: isExported, - parameters: params.map(_transformParameter).toList(), + parameters: params + .mapIndexed((index, p) => _transformParameter(p, index: index)) + .toList(), typeParameters: typeParams?.map(_transformTypeParamDeclaration).toList() ?? [], returnType: function.type != null @@ -1053,7 +1063,7 @@ class Transformer { } ParameterDeclaration _transformParameter(TSParameterDeclaration parameter, - [Type? type]) { + {Type? type, int? index}) { type ??= parameter.type != null ? _transformType(parameter.type!, parameter: true) : BuiltinType.anyType; @@ -1068,11 +1078,82 @@ class Transformer { type: type, variadic: isVariadic, optional: isOptional); + case TSSyntaxKind.ObjectBindingPattern || + TSSyntaxKind.ArrayBindingPattern: + Iterable expandBindingPatterns( + @UnionOf( + [TSIdentifier, TSObjectBindingPattern, TSArrayBindingPattern]) + TSNode name) { + switch (name.kind) { + case TSSyntaxKind.Identifier: + return [name as TSIdentifier]; + case TSSyntaxKind.ObjectBindingPattern || + TSSyntaxKind.ArrayBindingPattern: + return (name as TSBindingPattern) + .elements + .toDart + .map((e) => e.name == null + ? [] + : expandBindingPatterns(e.name!)) + .flattenedToList; + default: + return []; + } + } + final name = parameter.name as TSBindingPattern; + // just return the object + final elements = expandBindingPatterns(name); + final elementText = name.getText(); + final documentation = isVariadic + ? null + : Documentation(docs: 'Parameter is of the form: $elementText'); + + final rearWord = + parameter.name.kind == TSSyntaxKind.ObjectBindingPattern + ? 'obj' + : 'arr'; + + if (elements.isEmpty) { + return ParameterDeclaration( + name: 'unknown$rearWord', + type: type, + variadic: isVariadic, + optional: isOptional, + documentation: documentation); + } else if (elements.singleOrNull case final singleEl?) { + final singleElName = singleEl.kind == TSSyntaxKind.Identifier + ? (singleEl as TSIdentifier).text + : (singleEl as TSNamedDeclaration).name?.text ?? 'unknown'; + return ParameterDeclaration( + name: '$singleElName$rearWord', + type: type, + variadic: isVariadic, + optional: isOptional, + documentation: documentation); + } else { + final hash = AnonymousHasher.hashTuple(elements + .map((e) => e.kind == TSSyntaxKind.Identifier + ? (e as TSIdentifier).text + : (e as TSNamedDeclaration).name?.text ?? '') + .toList()); + return ParameterDeclaration( + name: '$rearWord${hash.substring(0, 3)}', + type: type, + variadic: isVariadic, + optional: isOptional, + documentation: documentation); + } default: - // TODO: Support Destructured Object Parameters - // and Destructured Array Parameters - throw Exception( - 'Unsupported Parameter Name kind ${parameter.name.kind}'); + final elementText = parameter.name.getText(); + final documentation = isVariadic + ? null + : Documentation(docs: 'Parameter is of the form: $elementText'); + return ParameterDeclaration( + name: 'unknown${index ?? ""}', + type: type, + variadic: isVariadic, + optional: isOptional, + documentation: documentation); } } @@ -1247,8 +1328,9 @@ class Transformer { case TSSyntaxKind.ConstructorType || TSSyntaxKind.FunctionType: final funType = type as TSFunctionOrConstructorTypeNodeBase; - final parameters = - funType.parameters.toDart.map(_transformParameter).toList(); + final parameters = funType.parameters.toDart + .mapIndexed((index, p) => _transformParameter(p, index: index)) + .toList(); final typeParameters = funType.typeParameters?.toDart .map(_transformTypeParamDeclaration) diff --git a/web_generator/lib/src/js/typescript.types.dart b/web_generator/lib/src/js/typescript.types.dart index 2a81435a..4140a9d9 100644 --- a/web_generator/lib/src/js/typescript.types.dart +++ b/web_generator/lib/src/js/typescript.types.dart @@ -632,6 +632,26 @@ extension type TSParameterDeclaration._(JSObject _) implements TSDeclaration { external TSNode? get dotDotDotToken; } +@JS('BindingPattern') +extension type TSBindingPattern._(JSObject _) + implements TSNode { + external TSNodeArray get elements; +} + +@JS('ObjectBindingPattern') +extension type TSObjectBindingPattern._(JSObject _) + implements TSBindingPattern {} + +@JS('ArrayBindingPattern') +extension type TSArrayBindingPattern._(JSObject _) + implements TSBindingPattern {} + +/** We do not need much from this other than its name */ +@JS('BindingElement') +extension type TSBindingElement._(JSObject _) implements TSNamedDeclaration { + external TSNode get name; +} + @JS('TypeParameterDeclaration') extension type TSTypeParameterDeclaration._(JSObject _) implements TSDeclaration { diff --git a/web_generator/test/integration/interop_gen/ts_typing_expected.dart b/web_generator/test/integration/interop_gen/ts_typing_expected.dart index 91048bba..b898cd35 100644 --- a/web_generator/test/integration/interop_gen/ts_typing_expected.dart +++ b/web_generator/test/integration/interop_gen/ts_typing_expected.dart @@ -27,6 +27,29 @@ external ProductOrrandomNonTypedProduct objectAsProduct( external _i1.JSArray> indexedArray(_i1.JSArray arr); @_i1.JS() +external double firstTwoNumbers( + + /// Parameter is of the form: [a, b] + _i1.JSArray<_i1.JSNumber> arr117); +@_i1.JS() +external double pointlessArrayFunction( + + /// Parameter is of the form: [{}] + _i1.JSArray unknownarr); +@_i1.JS() +external String productInfo( + /// Parameter is of the form: { name, id } + Product obj157, [ + AnonymousType_9686701? options, +]); +@_i1.JS() +external String shortendedProductCartInfo( + _i1.JSArray arr903, [ + _i1.JSArray arr9032, + _i1.JSArray arr9033, + _i1.JSArray arr9034, +]); +@_i1.JS() external String get myString; @_i1.JS() external _i1.JSArray<_i1.JSNumber> get myNumberArray; @@ -163,6 +186,21 @@ extension type AnonymousType_9143117._(_i1.JSObject _) external T value; } +extension type AnonymousType_4207514._(_i1.JSObject _) implements _i1.JSObject { + external AnonymousType_4207514({double a}); + + external double a; +} +extension type AnonymousType_9686701._(_i1.JSObject _) implements _i1.JSObject { + external AnonymousType_9686701({ + bool search, + bool showId, + }); + + external bool? search; + + external bool? showId; +} extension type MyEnum_EnumType._(_i1.JSObject _) implements _i1.JSObject { static const MyEnum A = MyEnum._(0); @@ -376,11 +414,6 @@ extension type AnonymousIntersection_1711585._(_i1.JSAny _) AnonymousUnion_5737239 get asAnonymousUnion_5737239 => (_ as AnonymousUnion_5737239); } -extension type AnonymousType_4207514._(_i1.JSObject _) implements _i1.JSObject { - external AnonymousType_4207514({double a}); - - external double a; -} extension type AnonymousType_1806035._(_i1.JSObject _) implements _i1.JSObject { external AnonymousType_1806035({String b}); diff --git a/web_generator/test/integration/interop_gen/ts_typing_input.d.ts b/web_generator/test/integration/interop_gen/ts_typing_input.d.ts index 5e2b5b8f..2d357ae8 100644 --- a/web_generator/test/integration/interop_gen/ts_typing_input.d.ts +++ b/web_generator/test/integration/interop_gen/ts_typing_input.d.ts @@ -99,3 +99,7 @@ export declare const myThirdIntersection: string & { export declare const myTypeGymnastic: ({ a: number } | { b: string }) & ({ c: boolean } | ({ d: bigint } & { e: symbol })); +export declare function firstTwoNumbers([a, b]: number[]): number; +export declare function pointlessArrayFunction([{}]: {a: number}[]): number; +export declare function productInfo({ name, id }: Product, options?: { search?: boolean, showId?: boolean }): string; +export declare function shortendedProductCartInfo(...[{ name: prod1, id: prod1ID }, { name: prod2, id: prod2ID }]: Product[]): string;