diff --git a/web_generator/bin/gen_interop_bindings.dart b/web_generator/bin/gen_interop_bindings.dart index 495ae0ea..4ee64189 100644 --- a/web_generator/bin/gen_interop_bindings.dart +++ b/web_generator/bin/gen_interop_bindings.dart @@ -8,6 +8,7 @@ import 'package:args/args.dart'; import 'package:io/ansi.dart' as ansi; import 'package:io/io.dart'; import 'package:path/path.dart' as p; +import 'package:web_generator/src/base_path.dart'; import 'package:web_generator/src/cli.dart'; void main(List arguments) async { @@ -58,10 +59,20 @@ $_usage'''); await compileDartMain(); } - // TODO(nikeokoronkwo): Multi-file input - final inputFile = argResult.rest.firstOrNull; - final outputFile = argResult['output'] as String? ?? - p.join(p.current, inputFile?.replaceAll('.d.ts', '.dart')); + final inputFiles = argResult.rest; + if (inputFiles.isEmpty) { + throw Exception('Input files is empty: $_usage'); + } + final String outputFile; + if (argResult['output'] case final out?) { + outputFile = out as String; + } else if (inputFiles.singleOrNull case final singleInput?) { + outputFile = p.join(p.current, singleInput.replaceAll('.d.ts', '.dart')); + } else { + final base = basePath(inputFiles); + outputFile = + p.join(p.current, p.extension(base) == '' ? base : p.dirname(base)); + } final defaultWebGenConfigPath = p.join(p.current, 'webgen.yaml'); final configFile = argResult['config'] as String? ?? (File(defaultWebGenConfigPath).existsSync() @@ -83,7 +94,8 @@ $_usage'''); 'main.mjs', '--declaration', if (argResult.rest.isNotEmpty) ...[ - '--input=${p.relative(inputFile!, from: bindingsGeneratorPath)}', + ...inputFiles.map( + (inp) => '--input=${p.relative(inp, from: bindingsGeneratorPath)}'), '--output=$relativeOutputPath', ], if (tsConfigRelativePath case final tsConfig?) '--ts-config=$tsConfig', diff --git a/web_generator/lib/src/_js_supertypes_src.dart b/web_generator/lib/src/_js_supertypes_src.dart new file mode 100644 index 00000000..eaf6477e --- /dev/null +++ b/web_generator/lib/src/_js_supertypes_src.dart @@ -0,0 +1,4 @@ +import 'dart:js_interop'; + +@JS() +external JSPromise get promise; diff --git a/web_generator/lib/src/ast/declarations.dart b/web_generator/lib/src/ast/declarations.dart index 4f88dfa8..05a8cae9 100644 --- a/web_generator/lib/src/ast/declarations.dart +++ b/web_generator/lib/src/ast/declarations.dart @@ -8,6 +8,8 @@ import 'package:code_builder/code_builder.dart'; import 'package:collection/collection.dart'; import '../interop_gen/namer.dart'; +import '../interop_gen/qualified_name.dart'; +import '../interop_gen/transform.dart'; import '../js/typescript.types.dart'; import 'base.dart'; import 'builtin.dart'; @@ -27,8 +29,10 @@ abstract class NestableDeclaration extends NamedDeclaration : (dartName ?? name); } -abstract class ParentDeclaration { +abstract class ParentDeclaration extends NestableDeclaration { Set get nodes; + + NodeMap get nodeMap; } /// A declaration that defines a type (class or interface) @@ -490,6 +494,8 @@ class TypeAliasDeclaration extends NestableDeclaration final Type type; + final String namedReference; + @override String? dartName; @@ -507,6 +513,7 @@ class TypeAliasDeclaration extends NestableDeclaration this.typeParameters = const [], required this.type, required this.exported, + required this.namedReference, this.documentation, this.parent}) : dartName = null; @@ -531,8 +538,8 @@ class TypeAliasDeclaration extends NestableDeclaration /// The declaration node for a TypeScript Namespace // TODO: Refactor into shared class when supporting modules -class NamespaceDeclaration extends NestableDeclaration - implements ExportableDeclaration, ParentDeclaration { +class NamespaceDeclaration extends ParentDeclaration + implements ExportableDeclaration { @override String name; @@ -648,6 +655,25 @@ class NamespaceDeclaration extends NestableDeclaration CompositeDeclaration get asComposite => CompositeDeclaration.fromNamespace(this); + + @override + NodeMap get nodeMap => NodeMap({ + ...{for (final decl in topLevelDeclarations) decl.id.toString(): decl}, + ...[...nestableDeclarations, ...namespaceDeclarations] + .asMap() + .map((_, v) { + final mainName = QualifiedName.raw(v.id.name); + return MapEntry( + ID( + type: v.id.type, + index: v.id.index, + name: mainName.length > 1 + ? mainName.skip(1).map((p) => p.part).join('.') + : mainName.asName) + .toString(), + v); + }) + }); } /// A composite declaration is formed from merging declarations together, diff --git a/web_generator/lib/src/ast/merger.dart b/web_generator/lib/src/ast/merger.dart index baf10de1..dc604000 100644 --- a/web_generator/lib/src/ast/merger.dart +++ b/web_generator/lib/src/ast/merger.dart @@ -352,7 +352,8 @@ InterfaceDeclaration mergeInterfaces(List interfaces, id: ID( type: referenceInterface.id.type, name: referenceInterface.id.name), typeParameters: interfaces.map((i) => i.typeParameters).flattenedToSet, - extendedTypes: interfaces.map((i) => i.extendedTypes).flattenedToList, + extendedTypes: + interfaces.map((i) => i.extendedTypes).flattenedToSet.toList(), properties: interfaces .map((i) => _rescopeDecls(i.properties, namer: namer)) .flattenedToList, diff --git a/web_generator/lib/src/base_path.dart b/web_generator/lib/src/base_path.dart new file mode 100644 index 00000000..94992a4f --- /dev/null +++ b/web_generator/lib/src/base_path.dart @@ -0,0 +1,31 @@ +// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:collection/collection.dart'; +import 'package:path/path.dart' as p; + +/// Gets the base, shared path between a list of paths +/// +/// The paths must either all be absolute, or from the +/// same origin +String basePath(List paths) { + assert(paths.isNotEmpty, 'No paths provided'); + + if (paths.singleOrNull case final singlePath?) return singlePath; + + final splitPaths = paths.map(p.split); + final minimumSplit = splitPaths.map((p) => p.length).min; + + final finalParts = []; + + final referenceParts = splitPaths.first; + + for (var i = 0; i < minimumSplit; ++i) { + if (splitPaths.every((parts) => parts[i] == referenceParts[i])) { + finalParts.add(referenceParts[i]); + } + } + + return finalParts.join(p.separator); +} diff --git a/web_generator/lib/src/interop_gen/transform.dart b/web_generator/lib/src/interop_gen/transform.dart index 62805c47..d9ee70f2 100644 --- a/web_generator/lib/src/interop_gen/transform.dart +++ b/web_generator/lib/src/interop_gen/transform.dart @@ -2,10 +2,12 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'dart:collection'; import 'dart:convert'; import 'dart:js_interop'; import 'package:code_builder/code_builder.dart'; +import 'package:collection/collection.dart'; import 'package:dart_style/dart_style.dart'; import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; @@ -13,6 +15,7 @@ import 'package:path/path.dart' as p; import '../ast/base.dart'; import '../ast/declarations.dart'; import '../ast/helpers.dart'; +import '../ast/types.dart'; import '../config.dart'; import '../js/helpers.dart'; import '../js/typescript.dart' as ts; @@ -118,13 +121,22 @@ extension type NodeMap._(Map decls) List findByQualifiedName(QualifiedName qName) { return decls.entries - .where((e) { + .map((e) { final name = UniqueNamer.parse(e.key).name; final qualifiedName = QualifiedName.raw(name); - return qualifiedName.map((n) => n.part) == qName.map((n) => n.part); + if (e.value case ParentDeclaration(nodeMap: final parentNodeMap) + when qName.length > 1) { + return parentNodeMap + .findByQualifiedName(QualifiedName.raw( + qName.skip(1).map((p) => p.part).join('.'))) + .whereType(); + } + return qualifiedName.map((n) => n.part) == qName.map((n) => n.part) + ? [e.value] + : null; }) - .map((e) => e.value) - .toList(); + .nonNulls + .flattenedToList; } void add(N decl) => decls[decl.id.toString()] = decl; @@ -201,7 +213,8 @@ class ProgramMap { /// Find the node definition for a given declaration named [declName] /// or associated with a TypeScript node [node] from the map of files - List? getDeclarationRef(String file, TSNode node, [String? declName]) { + List? getDeclarationRef(String file, TSNode node, + [QualifiedName? declName, TSSymbol? symbol]) { // check NodeMap nodeMap; if (_pathMap.containsKey(file)) { @@ -216,27 +229,59 @@ class ProgramMap { final anonymousTransformer = _activeTransformers.putIfAbsent( file, () => Transformer(this, null, file: file)); - // TODO: Replace with .transformAndReturn once #388 lands - return anonymousTransformer.transformAndReturn(node); + if (declName == null) { + return anonymousTransformer.transformAndReturn(node); + } else { + final referredTypes = anonymousTransformer + .searchForDeclRecursive( + declName, + (symbol ?? typeChecker.getSymbolAtLocation(node))!, + ) + .whereType(); + return referredTypes.map((r) => r.declaration).toList(); + } } else { final transformer = _activeTransformers.putIfAbsent(file, () => Transformer(this, src)); if (!transformer.nodes.contains(node)) { + // node might be nested if (declName case final d? - when transformer.nodeMap.findByName(d).isEmpty) { + when transformer.nodeMap.findByQualifiedName(d).isEmpty) { // find the source file decl if (src == null) return null; - final symbol = typeChecker.getSymbolAtLocation(src)!; - final exports = symbol.exports?.toDart ?? {}; - - final targetSymbol = exports[d.toJS]!; - - for (final decl in targetSymbol.getDeclarations()?.toDart ?? - []) { - transformer.transform(decl); + final srcSymbol = typeChecker.getSymbolAtLocation(src); + // print((declName, file, node.kind, transformer.nodeMap)); + if (srcSymbol == null) { + ts.forEachChild( + src, + (TSNode node) { + if (node.kind == TSSyntaxKind.EndOfFileToken) return; + + transformer.transform(node); + }.toJS as ts.TSNodeCallback); + } else { + final exports = srcSymbol.exports?.toDart ?? {}; + + final targetSymbol = exports[d.first.part.toJS]!; + + final referredTypes = transformer + .searchForDeclRecursive( + declName, + targetSymbol, + ) + .whereType(); + return referredTypes.map((r) => r.declaration).toList(); } + } else if (declName != null) { + final referredTypes = transformer + .searchForDeclRecursive( + declName, + (symbol ?? typeChecker.getSymbolAtLocation(node))!, + ) + .whereType(); + return referredTypes.map((r) => r.declaration).toList(); } else { transformer.transform(node); } @@ -247,8 +292,13 @@ class ProgramMap { } } - final name = declName ?? (node as TSNamedDeclaration).name?.text; - return name == null ? null : nodeMap.findByName(name); + final name = declName?.asName ?? (node as TSNamedDeclaration).name?.text; + return switch (name) { + final String n when n.contains('.') => + nodeMap.findByQualifiedName(declName ?? QualifiedName.raw(name)), + null => null, + _ => nodeMap.findByName(name) + }; } (String, NamedDeclaration)? getCommonType(String name, diff --git a/web_generator/lib/src/interop_gen/transform/recursion.dart b/web_generator/lib/src/interop_gen/transform/recursion.dart new file mode 100644 index 00000000..e228ca43 --- /dev/null +++ b/web_generator/lib/src/interop_gen/transform/recursion.dart @@ -0,0 +1,16 @@ +import '../../js/typescript.dart'; +import '../../js/typescript.types.dart'; + +class RecursionMap { + String kind; + String name; + Map children; + + RecursionMap(this.kind, this.name, {this.children = const {}}); +} + +RecursionMap exploreType(TSType node, { + required TSTypeChecker typeChecker +}) { + +} \ No newline at end of file diff --git a/web_generator/lib/src/interop_gen/transform/transformer.dart b/web_generator/lib/src/interop_gen/transform/transformer.dart index 3237e4ee..e2680359 100644 --- a/web_generator/lib/src/interop_gen/transform/transformer.dart +++ b/web_generator/lib/src/interop_gen/transform/transformer.dart @@ -5,6 +5,7 @@ import 'dart:collection'; import 'dart:js_interop'; import 'package:collection/collection.dart'; +import 'package:meta/meta.dart'; import 'package:path/path.dart' as p; import '../../ast/base.dart'; import '../../ast/builtin.dart'; @@ -58,6 +59,11 @@ class Transformer { /// A map of types final TypeMap typeMap = TypeMap(); + /// A map of nodes to types + /// This helps if a type has already been indexed to another, and prevents + /// unnecessary transformations + final Map _nodeAliases = {}; + /// The program map final ProgramMap programMap; @@ -227,6 +233,7 @@ class Transformer { name: name, type: _getTypeFromDeclaration(type, null), exported: isExported, + namedReference: parseQualifiedName(type).asName, documentation: _parseAndTransformDocumentation(typealias)); } @@ -385,7 +392,7 @@ class Transformer { /// Transforms a TS Class or Interface declaration into a node representing /// a class or interface respectively. TypeDeclaration _transformClassOrInterface(TSObjectDeclaration typeDecl, - {UniqueNamer? namer}) { + {UniqueNamer? namer, NamespaceDeclaration? parent}) { namer ??= this.namer; final name = typeDecl.name.text; @@ -455,6 +462,24 @@ class Transformer { final typeNamer = ScopedUniqueNamer({'get', 'set'}); + // add to namespace in parent + void updateTypeInParent() { + if (parent != null) { + if (parent.nestableDeclarations.any((n) => n.id == outputType.id)) { + parent.nestableDeclarations.removeWhere((d) => d.id == outputType.id); + parent.nestableDeclarations.add(outputType); + } else { + outputType.parent = parent; + parent.nestableDeclarations.add(outputType); + } + } else { + nodeMap.update(outputType.id.toString(), (v) => outputType, + ifAbsent: () => outputType); + } + } + + updateTypeInParent(); + for (final member in typeDecl.members.toDart) { switch (member.kind) { case TSSyntaxKind.PropertySignature || TSSyntaxKind.PropertyDeclaration: @@ -515,8 +540,11 @@ class Transformer { // skipping break; } + updateTypeInParent(); } + updateTypeInParent(); + return outputType; } @@ -1049,11 +1077,18 @@ class Transformer { typeParameters: typeParams?.map(_transformTypeParamDeclaration).toList() ?? [], exported: isExported, + namedReference: typealias.type.getText(), documentation: _parseAndTransformDocumentation(typealias)); } ParameterDeclaration _transformParameter(TSParameterDeclaration parameter, [Type? type]) { + // print('Parameter Information: ${( + // parameter.name.kind, + // parameter.name.getText(), + // parameter.type?.kind, + // file + // )}'); type ??= parameter.type != null ? _transformType(parameter.type!, parameter: true) : BuiltinType.anyType; @@ -1104,6 +1139,8 @@ class Transformer { // https://github.com/dart-lang/web/issues/422 Type _transformType(TSTypeNode type, {bool parameter = false, bool typeArg = false, bool? isNullable}) { + if (_nodeAliases.containsKey(type)) return _nodeAliases[type]!; + // print((type.getText(), type.kind)); switch (type.kind) { case TSSyntaxKind.ParenthesizedType: return _transformType((type as TSParenthesizedTypeNode).type, @@ -1429,6 +1466,7 @@ class Transformer { final literalType = type as TSLiteralTypeNode; final literal = literalType.literal; + // TODO: Support Binary and Prefix Expression Literals return LiteralType( isNullable: isNullable ?? false, kind: switch (literal.kind) { @@ -1441,7 +1479,8 @@ class Transformer { TSSyntaxKind.FalseKeyword => LiteralKind.$false, TSSyntaxKind.NullKeyword => LiteralKind.$null, _ => throw UnimplementedError( - 'Unsupported Literal Kind ${literal.kind}') + 'Unsupported Literal Kind ${literal.kind}:' + ' ${literal.getText()}') }, value: switch (literal.kind) { // TODO: Will we support Regex? @@ -1451,7 +1490,8 @@ class Transformer { TSSyntaxKind.FalseKeyword => false, TSSyntaxKind.NullKeyword => null, _ => throw UnimplementedError( - 'Unsupported Literal Kind ${literal.kind}') + 'Unsupported Literal Kind ${literal.kind}:' + ' ${literal.getText()}') }); case TSSyntaxKind.TypeQuery: final typeQuery = type as TSTypeQueryNode; @@ -1594,31 +1634,89 @@ class Transformer { } else { print('WARN: The given type with kind ${type.kind} is ' 'not supported yet'); - return BuiltinType.primitiveType(PrimitiveType.any, - isNullable: isNullable); + return _nodeAliases.putIfAbsent(type, () => BuiltinType.primitiveType(PrimitiveType.any, + isNullable: isNullable)); } } } Type _transformTypeExpressionWithTypeArguments( TSExpressionWithTypeArguments type) { - if (type.expression.kind == TSSyntaxKind.Identifier) { - final identifier = type.expression as TSIdentifier; - + if (type.expression case final node + when node.kind == TSSyntaxKind.Identifier || + node.kind == TSSyntaxKind.PropertyAccessExpression) { final getTypeFromDeclaration = - _getTypeFromDeclaration(identifier, type.typeArguments?.toDart); + _getTypeFromDeclaration(node, type.typeArguments?.toDart); - return getTypeFromDeclaration; - } else if (type.expression.kind == TSSyntaxKind.PropertyAccessExpression) { // TODO(nikeokoronkwo): Support Globbed Imports and Exports, https://github.com/dart-lang/web/issues/420 - throw UnimplementedError("The given type expression's expression of kind " - '${type.expression.kind} is not supported yet'); + return getTypeFromDeclaration; } else { throw UnimplementedError("The given type expression's expression of kind " '${type.expression.kind} is not supported yet'); } } + Type? transformNestedDecl(TSDeclaration declaration, String firstName, + {List? typeArguments, + bool isNotTypableDeclaration = false, + TSSymbol? symbol, + bool typeArg = false, + bool isNullable = false, + NamespaceDeclaration? parent, + UniqueNamer? namer}) { + symbol ??= typeChecker.getSymbolAtLocation(declaration); + + if (declaration.kind == TSSyntaxKind.ExportSpecifier) { + // in order to prevent recursion, we need to find the source of the + // export specifier + final aliasedSymbol = typeChecker.getAliasedSymbol(symbol!); + final aliasedSymbolName = aliasedSymbol.name; + + exportSet.removeWhere((e) => e.name == aliasedSymbolName); + exportSet.add(ExportReference(aliasedSymbolName, as: firstName)); + // TODO: Is nullable + return _getTypeFromSymbol( + aliasedSymbol, + typeChecker.getTypeOfSymbol(aliasedSymbol), + typeArguments, + typeArg, + isNotTypableDeclaration, + isNullable); + } + + var d = declaration as TSNamedDeclaration; + + while (d.name?.text != firstName && + d.parent.kind == TSSyntaxKind.ModuleBlock) { + d = (declaration.parent as TSModuleBlock).parent; + } + + final transformedDecls = + transformAndReturn(d, namer: namer, parent: parent); + + if (parent != null) { + switch (declaration.kind) { + case TSSyntaxKind.ClassDeclaration || TSSyntaxKind.InterfaceDeclaration: + final outputDecl = transformedDecls.first as TypeDeclaration; + outputDecl.parent = parent; + parent.nestableDeclarations.add(outputDecl); + case TSSyntaxKind.EnumDeclaration: + final outputDecl = transformedDecls.first as EnumDeclaration; + outputDecl.parent = parent; + parent.nestableDeclarations.add(outputDecl); + default: + parent.topLevelDeclarations.addAll(transformedDecls); + } + parent.nodes.add(declaration); + } else { + nodeMap.addAll( + {for (final decl in transformedDecls) decl.id.toString(): decl}); + nodes.add(declaration); + } + + return null; + } + /// Given a [symbol] with declarations defined in the given file, this method /// searches for the declaration recursively (either as a top level on the /// [ts.TSSourceFile], or recursively inside a module or namespace). @@ -1633,7 +1731,8 @@ class Transformer { /// to [nodeMap], or, if in recursion, added to its [parent] declaration. /// /// The referred type may accept [typeArguments], which are passed as well. - Type _searchForDeclRecursive( + @internal + List searchForDeclRecursive( Iterable name, TSSymbol symbol, {NamespaceDeclaration? parent, List? typeArguments, @@ -1667,64 +1766,29 @@ class Transformer { ...parent.topLevelDeclarations ].map((d) => d.id.toString())) : null; - for (var declaration in declarations) { - if (declaration.kind == TSSyntaxKind.ExportSpecifier) { - // in order to prevent recursion, we need to find the source of the - // export specifier - final aliasedSymbol = typeChecker.getAliasedSymbol(symbol); - final aliasedSymbolName = aliasedSymbol.name; - - exportSet.removeWhere((e) => e.name == aliasedSymbolName); - exportSet.add(ExportReference(aliasedSymbolName, as: firstName)); - // TODO: Is nullable - return _getTypeFromSymbol( - aliasedSymbol, - typeChecker.getTypeOfSymbol(aliasedSymbol), - typeArguments, - typeArg, - isNotTypableDeclaration, - isNullable); - } - var d = declaration as TSNamedDeclaration; + final outputTypes = []; - while (d.name?.text != firstName && - d.parent.kind == TSSyntaxKind.ModuleBlock) { - d = (declaration.parent as TSModuleBlock).parent; - } + for (var declaration in declarations) { + final exportTypeOrNull = transformNestedDecl(declaration, firstName, + symbol: symbol, + typeArg: typeArg, + typeArguments: typeArguments, + isNotTypableDeclaration: isNotTypableDeclaration, + parent: parent, + namer: namer); - // TODO: multi-decls - final transformedDecls = - transformAndReturn(declaration, namer: namer, parent: parent); + if (exportTypeOrNull != null) outputTypes.add(exportTypeOrNull); - if (parent != null) { - switch (declaration.kind) { - case TSSyntaxKind.ClassDeclaration || - TSSyntaxKind.InterfaceDeclaration: - final outputDecl = transformedDecls.first as TypeDeclaration; - outputDecl.parent = parent; - parent.nestableDeclarations.add(outputDecl); - case TSSyntaxKind.EnumDeclaration: - final outputDecl = transformedDecls.first as EnumDeclaration; - outputDecl.parent = parent; - parent.nestableDeclarations.add(outputDecl); - default: - parent.topLevelDeclarations.addAll(transformedDecls); - } - parent.nodes.add(declaration); - } else { - nodeMap.addAll( - {for (final decl in transformedDecls) decl.id.toString(): decl}); - nodes.add(declaration); - } + map = parent != null + ? NodeMap([ + ...parent.nestableDeclarations, + ...parent.namespaceDeclarations + ].asMap().map((_, v) => MapEntry(v.id.toString(), v))) + : nodeMap; } - map = parent != null - ? NodeMap([ - ...parent.nestableDeclarations, - ...parent.namespaceDeclarations - ].asMap().map((_, v) => MapEntry(v.id.toString(), v))) - : nodeMap; + if (outputTypes.isNotEmpty) return outputTypes; declarationsMatching = map.findByName(firstName); } @@ -1733,34 +1797,39 @@ class Transformer { // are we done? final rest = name.skip(1); + if (rest.isEmpty) { - // TODO: Merge - final decl = declarationsMatching.whereType().first; - // return decl - switch (decl) { - case TypeAliasDeclaration(type: final t): - case EnumDeclaration(baseType: final t): - final jsType = getJSTypeAlternative(t); - if (jsType != t && typeArg) { - return jsType..isNullable = isNullable; - } - } + final outputTypes = []; - final asReferredType = decl.asReferredType( - (typeArguments ?? []) - .map((type) => _transformType(type, typeArg: true)) - .toList(), - isNullable); + for (final decl in declarationsMatching.whereType()) { + switch (decl) { + case TypeAliasDeclaration(type: final t): + case EnumDeclaration(baseType: final t): + final jsType = getJSTypeAlternative(t); + if (jsType != t && typeArg) { + outputTypes.add(jsType..isNullable = isNullable); + } + } - if (asReferredType case ReferredDeclarationType(type: final type) - when type is BuiltinType) { - final jsType = getJSTypeAlternative(type); - if (jsType != type && typeArg) { - asReferredType.type = jsType..isNullable = isNullable; + final asReferredType = decl.asReferredType( + (typeArguments ?? []) + .map((type) => _transformType(type, typeArg: true)) + .toList(), + isNullable); + + if (asReferredType case ReferredDeclarationType(type: final type) + when type is BuiltinType) { + final jsType = getJSTypeAlternative(type); + if (jsType != type && typeArg) { + asReferredType.type = jsType..isNullable = isNullable; + } } - } - return asReferredType; + if (decl is! TypeAliasDeclaration || decl is! EnumDeclaration) { + outputTypes.add(asReferredType); + } + } + return outputTypes; } else { // we go one more time @@ -1772,12 +1841,14 @@ class Transformer { if (rest.singleOrNull?.part case final generic? when typeParams.any((t) => t.name == generic)) { final typeParam = typeParams.firstWhere((t) => t.name == generic); - return GenericType( - name: typeParam.name, parent: d, isNullable: isNullable); + return [ + GenericType( + name: typeParam.name, parent: d, isNullable: isNullable) + ]; } break; case final NamespaceDeclaration n: - final searchForDeclRecursive = _searchForDeclRecursive(rest, symbol, + final recursiveDecl = searchForDeclRecursive(rest, symbol, typeArguments: typeArguments, typeArg: typeArg, parent: n, @@ -1785,14 +1856,14 @@ class Transformer { if (parent == null) { nodeMap.update(d.id.toString(), (v) => n); } - return searchForDeclRecursive; + return recursiveDecl; // recursive } } } throw Exception( - 'Could not find type for given declaration ${name.join('.')}'); + 'Could not search for type for given declaration ${name.join('.')}'); } /// Get the type of a type node [node] by gettings its type from @@ -1834,7 +1905,7 @@ class Transformer { /// If the qualified name has no import file associated with it, it is either /// a built-in type or an imported type. If it has an import file associated /// with it and the file is this file, then the declaration is searched for - /// and transformed recursively via [_searchForDeclRecursive], else the + /// and transformed recursively via [searchForDeclRecursive], else the /// associated file is used to find and transform the associated declaration /// through the [programMap]. /// @@ -1889,10 +1960,23 @@ class Transformer { final (fullyQualifiedName, nameImport) = parseTSFullyQualifiedName(tsFullyQualifiedName); + print(( + fullyQualifiedName.asName, + from: nameImport, + symbol.getDeclarations()?.toDart.map((m) => m.kind), + typeParam: type?.isTypeParameter() + )); + + if (symbol.name == 'globalThis') { + // return globalThis as any + // TODO: This should correspond to what is in global space + return BuiltinType.anyType; + } + if (nameImport == null) { // if import not there, most likely from an import - if (type?.isTypeParameter() ?? false) { + if ((type?.isTypeParameter() ?? false) || (symbol.getDeclarations()?.toDart.any((d) => d.kind == TSSyntaxKind.TypeParameter) ?? false)) { // generic type return GenericType( name: fullyQualifiedName.last.part, isNullable: isNullable); @@ -1954,20 +2038,104 @@ class Transformer { relativePath = p.relative(declSource, from: p.dirname(file)); } final referencedDeclarations = programMap.getDeclarationRef( - declSource, decl, fullyQualifiedName.asName); + declSource, decl, fullyQualifiedName, symbol); mappedDecls = referencedDeclarations?.whereType().toList() ?? []; } else { - var declarationsMatching = nodeMap.findByName(firstName); - if (declarationsMatching.isEmpty) { - transform(decl); - declarationsMatching = nodeMap.findByName(firstName); - } + if (fullyQualifiedName.length == 1) { + var declarationsMatching = nodeMap.findByName(firstName); + if (declarationsMatching.isEmpty) { + transform(decl); + declarationsMatching = nodeMap.findByName(firstName); + } - mappedDecls = - declarationsMatching.whereType().toList(); + mappedDecls = + declarationsMatching.whereType().toList(); + } else { + List findDecl( + Iterable names, + List targetDecls, + ) { + final outputDecls = []; + for (final target in targetDecls) { + switch (target) { + case NamespaceDeclaration( + topLevelDeclarations: final topLevelDecls, + nestableDeclarations: final nestableDecls, + namespaceDeclarations: final namespaceDecls + ): + if (names.length == 1) { + outputDecls.addAll([ + ...topLevelDecls, + ...nestableDecls, + ...namespaceDecls + ].where((d) => d.name == names.first.part)); + } else { + return findDecl(names.skip(1), [ + ...topLevelDecls.whereType(), + ...nestableDecls, + ...namespaceDecls + ]); + } + break; + case TypeDeclaration( + properties: final properties, + methods: final methods, + operators: final operators + ): + outputDecls.addAll([...properties, ...methods, ...operators] + .where((d) => d.name == names.first.part)); + break; + } + } + + return outputDecls; + } + + var declarationsMatching = + nodeMap.findByName(fullyQualifiedName.first.part); + TSNode rootDecl = decl; + for (var i = 1; i < fullyQualifiedName.length; ++i) { + rootDecl = decl.parent; + print((decl.kind, to: rootDecl.kind)); + } + + print(( + 'Final', + decl.kind, + to: rootDecl.kind, + toText: rootDecl.getText(), + parent: rootDecl.parent.getText() + )); + + if (isTypeNode(rootDecl)) { + final rootType = + _transformType(rootDecl as TSTypeNode, typeArg: typeArg); + + if (rootType + case DeclarationType(declaration: final transformedRootDecl) + when transformedRootDecl is NamedDeclaration) { + mappedDecls = + findDecl(fullyQualifiedName.skip(1), [transformedRootDecl]); + } else { + throw Exception('The given declaration root of kind' + ' ${rootDecl.kind} is not supported for nested member ' + 'search: ${rootDecl.getText()}'); + } + } else { + transform(rootDecl); + + declarationsMatching = nodeMap.findByName(firstName); + + final mappedRootDecls = + declarationsMatching.whereType().toList(); + + mappedDecls = + findDecl(fullyQualifiedName.skip(1), mappedRootDecls); + } + } } } @@ -2015,11 +2183,13 @@ class Transformer { } // recursiveness - return _searchForDeclRecursive(fullyQualifiedName, symbol, - typeArguments: typeArguments, - typeArg: typeArg, - isNotTypableDeclaration: isNotTypableDeclaration, - isNullable: isNullable); + // TODO: Merge + return searchForDeclRecursive(fullyQualifiedName, symbol, + typeArguments: typeArguments, + typeArg: typeArg, + isNotTypableDeclaration: isNotTypableDeclaration, + isNullable: isNullable) + .first; } else { // if import there and not this file, imported from specified file final importUrl = !nameImport.endsWith('.d.ts') && @@ -2034,17 +2204,18 @@ class Transformer { final relativePath = programMap.files.contains(importUrl) ? p.relative(importUrl, from: p.dirname(file)) : null; - final referencedDeclarations = declarations.map((decl) { - return programMap.getDeclarationRef( - importUrl, decl, fullyQualifiedName.asName); - }).reduce((prev, next) => - [if (prev != null) ...prev, if (next != null) ...next]); - final nodes = - referencedDeclarations?.whereType().toList() ?? - []; - - final (mergedNodes, :additionals) = mergeDeclarations(nodes); + final referencedDeclarations = declarations + .map((decl) { + final declarationRef = programMap.getDeclarationRef( + importUrl, decl, fullyQualifiedName, symbol); + return declarationRef?.whereType(); + }) + .nonNulls + .flattenedToList; + + final (mergedNodes, :additionals) = + mergeDeclarations(referencedDeclarations); nodeMap.addAll({for (final add in additionals) add.id.toString(): add}); if (mergedNodes case [final firstNode]) { @@ -2082,19 +2253,26 @@ class Transformer { /// supported `dart:js_interop` types and related [EnumDeclaration]-like and /// [TypeDeclaration]-like checks Type _getTypeFromDeclaration( - @UnionOf([TSIdentifier, TSQualifiedName]) TSNode typeName, + @UnionOf([TSIdentifier, TSQualifiedName, TSPropertyAccessExpression]) + TSNode typeName, List? typeArguments, {bool typeArg = false, bool isNotTypableDeclaration = false, bool isNullable = false}) { // union assertion assert(typeName.kind == TSSyntaxKind.Identifier || - typeName.kind == TSSyntaxKind.QualifiedName); + typeName.kind == TSSyntaxKind.QualifiedName || + typeName.kind == TSSyntaxKind.PropertyAccessExpression); final symbol = typeChecker.getSymbolAtLocation(typeName); - return _getTypeFromSymbol(symbol, typeChecker.getTypeOfSymbol(symbol!), - typeArguments, isNotTypableDeclaration, typeArg, isNullable); + return _getTypeFromSymbol( + symbol, + symbol == null ? null : typeChecker.getTypeOfSymbol(symbol), + typeArguments, + isNotTypableDeclaration, + typeArg, + isNullable); } /// Extracts associated documentation (JSDoc) from a [TSNode] and transforms @@ -2521,3 +2699,8 @@ QualifiedName parseQualifiedName( return parseQualifiedNameFromTSQualifiedName(name as TSQualifiedName); } } + +bool isTypeNode(TSNode node) { + return node.kind >= TSSyntaxKind.FirstTypeNode && + node.kind <= TSSyntaxKind.LastTypeNode; +} diff --git a/web_generator/lib/src/js/typescript.types.dart b/web_generator/lib/src/js/typescript.types.dart index 2a81435a..a5db4eab 100644 --- a/web_generator/lib/src/js/typescript.types.dart +++ b/web_generator/lib/src/js/typescript.types.dart @@ -103,6 +103,10 @@ extension type const TSSyntaxKind._(num _) { static const TSSyntaxKind TypePredicate = TSSyntaxKind._(182); static const TSSyntaxKind ConditionalType = TSSyntaxKind._(194); + // bounds + static const TSSyntaxKind FirstTypeNode = TSSyntaxKind._(182); + static const TSSyntaxKind LastTypeNode = TSSyntaxKind._(205); + // Other static const TSSyntaxKind Identifier = TSSyntaxKind._(80); static const TSSyntaxKind QualifiedName = TSSyntaxKind._(166); @@ -120,6 +124,9 @@ extension type const TSSyntaxKind._(num _) { static const TSSyntaxKind ExternalModuleReference = TSSyntaxKind._(283); static const TSSyntaxKind EnumMember = TSSyntaxKind._(306); static const TSSyntaxKind SourceFile = TSSyntaxKind._(308); + + bool operator <=(TSSyntaxKind kind) => _ <= kind._; + bool operator >=(TSSyntaxKind kind) => _ >= kind._; } extension type const TSNodeFlags._(int _) implements int { diff --git a/web_generator/test/integration/interop_gen/classes_expected.dart b/web_generator/test/integration/interop_gen/classes_expected.dart index beea1802..bdd4731a 100644 --- a/web_generator/test/integration/interop_gen/classes_expected.dart +++ b/web_generator/test/integration/interop_gen/classes_expected.dart @@ -238,13 +238,6 @@ extension type Vector2D._(_i1.JSObject _) implements Vector { Point2D end, ); } -extension type DirectionAngles._(_i1.JSObject _) implements _i1.JSObject { - external double alpha; - - external double beta; - - external double gamma; -} extension type Vector3D._(_i1.JSObject _) implements Vector { external Vector3D( num x, @@ -274,6 +267,13 @@ extension type Vector3D._(_i1.JSObject _) implements Vector { Point3D end, ); } +extension type DirectionAngles._(_i1.JSObject _) implements _i1.JSObject { + external double alpha; + + external double beta; + + external double gamma; +} extension type EpahsImpl._(_i1.JSObject _) implements Epahs { external EpahsImpl( diff --git a/web_generator/test/integration/interop_gen/import_export_expected.dart b/web_generator/test/integration/interop_gen/import_export_expected.dart index 6679669b..f7c5b520 100644 --- a/web_generator/test/integration/interop_gen/import_export_expected.dart +++ b/web_generator/test/integration/interop_gen/import_export_expected.dart @@ -1,4 +1,5 @@ -// ignore_for_file: constant_identifier_names, non_constant_identifier_names +// ignore_for_file: camel_case_types, constant_identifier_names +// ignore_for_file: non_constant_identifier_names // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:js_interop' as _i1; @@ -21,12 +22,14 @@ external Vector3D crossProduct( ); @_i1.JS() external Vector3D mapTo3D(Vector2D v); -extension type TransformerMatrix._(_i1.JSObject _) - implements Matrix { +@_i1.JS('mapTo3D') +external Vector3D mapTo3D$1(Vector2D v); +extension type TransformerMatrix._(_i1.JSObject _) + implements Geometry_Matrix<_i1.JSNumber> { external V call(V v); } extension type ComparatorMatrix._(_i1.JSObject _) - implements Matrix, Comparator {} + implements Geometry_Matrix<_i1.JSNumber>, Comparator {} @_i1.JS() external _i1.JSArray get unitVectors; @_i1.JS('PolarPoint') @@ -53,13 +56,39 @@ extension type SphericalCoordinate._(_i1.JSObject _) implements _i1.JSObject { } @_i1.JS() external PolarCoordinate toPolarCoordinate(Point2D point); +@_i1.JS('toPolarCoordinate') +external PolarCoordinate toPolarCoordinate$1(Point2D point); @_i1.JS() external SphericalCoordinate toSphericalCoordinate(Point3D point); @_i1.JS('toSphericalCoordinate') -external SphericalCoordinate toSphericalCoordinate$1( +external SphericalCoordinate toSphericalCoordinate$1(Point3D point); +@_i1.JS('toSphericalCoordinate') +external SphericalCoordinate toSphericalCoordinate$2( CylindricalCoordinate point); @_i1.JS() external CylindricalCoordinate toCylindricalCoordinate(Point3D point); +@_i1.JS('toCylindricalCoordinate') +external CylindricalCoordinate toCylindricalCoordinate$1(Point3D point); +@_i1.JS() +external _i1.JSArray transformVector( + Geometry_Matrix<_i1.JSNumber> matrix, + _i1.JSArray vectors, [ + _i1.JSArray vectors2, + _i1.JSArray vectors3, + _i1.JSArray vectors4, +]); +@_i1.JS('transformVector') +external _i1.JSArray transformVector$1( + TransformerMatrix matrix, + _i1.JSArray vectors, [ + _i1.JSArray vectors2, + _i1.JSArray vectors3, + _i1.JSArray vectors4, +]); +extension type LocatableUser._(_i1.JSObject _) + implements Data_Models_IUser, Point2D { + external Point2D get locationAsPoint; +} @_i1.JS() external Vector2D get unitI2D; @_i1.JS() @@ -70,6 +99,8 @@ external Vector3D get unitI3D; external Vector3D get unitJ3D; @_i1.JS() external Vector3D get unitK3D; +@_i1.JS() +external Data_Models_User get userLocation; extension type Point2D._(_i1.JSObject _) implements _i1.JSObject { external double x; @@ -147,12 +178,14 @@ extension type Vector2D._(_i1.JSObject _) implements Vector { Point2D end, ); } -extension type Matrix._(_i1.JSObject _) implements _i1.JSObject { - external double rows; - - external double columns; - - external _i1.JSArray<_i1.JSNumber> operator [](num index); +@_i1.JS('Geometry.Matrix') +extension type Geometry_Matrix._(_i1.JSObject _) + implements _i1.JSObject { + external double get rows; + external double get columns; + external _i1.JSArray getColumn(num index); + external _i1.JSArray getRow(num index); + external _i1.JSArray operator [](num index); } extension type Comparator._(_i1.JSObject _) implements _i1.JSObject { @@ -161,3 +194,25 @@ extension type Comparator._(_i1.JSObject _) T b, ); } +@_i1.JS('Data.Models.IUser') +extension type Data_Models_IUser._(_i1.JSObject _) implements _i1.JSObject { + external double id; + + external String name; + + external String email; +} +@_i1.JS('Data.Models.User') +extension type Data_Models_User._(_i1.JSObject _) implements Data_Models_IUser { + external Data_Models_User( + num id, + String name, + String email, + ); + + external double id; + + external String name; + + external String email; +} diff --git a/web_generator/test/integration/interop_gen/import_export_input.d.ts b/web_generator/test/integration/interop_gen/import_export_input.d.ts index 89a55399..0e56acf9 100644 --- a/web_generator/test/integration/interop_gen/import_export_input.d.ts +++ b/web_generator/test/integration/interop_gen/import_export_input.d.ts @@ -1,5 +1,6 @@ import { Vector, Vector2D, Vector3D, Point2D, Point3D, origin2D as origin, origin3D } from "./classes_input" import { Comparator } from "./interfaces_input" +import { Data, Geometry } from "./namespaces_input" interface PolarCoordinate { magnitude: number; angle: number; @@ -14,15 +15,15 @@ interface SphericalCoordinate { theta: number; tau: number; } -interface Matrix { - [index: number]: number[]; - rows: number; - columns: number; -} -interface TransformerMatrix extends Matrix { +interface TransformerMatrix extends Geometry.Matrix { (v: V): V; } -interface ComparatorMatrix extends Matrix, Comparator {} +export interface LocatableUser extends Data.Models.IUser, Point2D { + get locationAsPoint(): Point2D; +} +interface ComparatorMatrix extends Geometry.Matrix, Comparator {} +export declare function transformVector(matrix: Geometry.Matrix, ...vectors: V[]): V[]; +export declare function transformVector(matrix: TransformerMatrix, ...vectors: V[]): V[]; declare function dotProduct(v1: V, v2: V): V; declare function crossProduct(v1: Vector3D, v2: Vector3D): Vector3D; declare function mapTo3D(v: Vector2D): Vector3D; @@ -36,6 +37,7 @@ export declare const unitI3D: Vector3D; export declare const unitJ3D: Vector3D; export declare const unitK3D: Vector3D; declare const ijk: Vector3D[]; +export declare const userLocation: Data.Models.User export { origin, origin3D, dotProduct as scalarProduct, crossProduct as vectorProduct, mapTo3D, TransformerMatrix, ComparatorMatrix, diff --git a/web_generator/test/integration/interop_gen/namespaces_expected.dart b/web_generator/test/integration/interop_gen/namespaces_expected.dart index 758f843c..2724e00d 100644 --- a/web_generator/test/integration/interop_gen/namespaces_expected.dart +++ b/web_generator/test/integration/interop_gen/namespaces_expected.dart @@ -57,11 +57,41 @@ extension type EnterpriseApp._(_i1.JSObject _) implements _i1.JSObject { @_i1.JS('EnterpriseApp.UI') external static EnterpriseApp_UI get UI; } +extension type Geometry._(_i1.JSObject _) implements _i1.JSObject { + @_i1.JS() + external static Geometry_Matrix addMatrices( + _i1.JSArray> matrices, [ + _i1.JSArray> matrices2, + _i1.JSArray> matrices3, + _i1.JSArray> matrices4, + ]); + @_i1.JS() + external static Geometry_Matrix subtractMatrices( + Geometry_Matrix a, + Geometry_Matrix b, + ); + @_i1.JS() + external static Geometry_Matrix scalarMultiplyMatrix( + T scalar, + Geometry_Matrix matrix, + ); + @_i1.JS() + external static Geometry_Matrix multiplyMatrices( + Geometry_Matrix a, + Geometry_Matrix b, + ); + @_i1.JS() + external static Geometry_Matrix inverse( + Geometry_Matrix matrix); + @_i1.JS() + external static Geometry_Matrix transpose( + Geometry_Matrix matrix); +} @_i1.JS() external Data_Models_User get user1; -typedef UserService = EnterpriseApp_DataServices_UserService; +typedef UService = EnterpriseApp_DataServices_UserService; @_i1.JS() -external UserService get userService; +external UService get userService; typedef ProductService = EnterpriseApp_DataServices_ProductService; @_i1.JS() external _i1.JSArray get allUsers; @@ -343,3 +373,12 @@ extension type AnonymousUnion_1467782._(_i1.JSAny _) implements _i1.JSAny { double get asDouble => (_ as _i1.JSNumber).toDartDouble; } +@_i1.JS('Geometry.Matrix') +extension type Geometry_Matrix._(_i1.JSObject _) + implements _i1.JSObject { + external double get rows; + external double get columns; + external _i1.JSArray getColumn(num index); + external _i1.JSArray getRow(num index); + external _i1.JSArray operator [](num index); +} diff --git a/web_generator/test/integration/interop_gen/namespaces_input.d.ts b/web_generator/test/integration/interop_gen/namespaces_input.d.ts index d6f42672..f23a5907 100644 --- a/web_generator/test/integration/interop_gen/namespaces_input.d.ts +++ b/web_generator/test/integration/interop_gen/namespaces_input.d.ts @@ -144,11 +144,28 @@ export declare namespace EnterpriseApp { } } } +export namespace Geometry { + interface Matrix { + readonly rows: number; + readonly columns: number; + /** TODO: Should produce 2, not 1 */ + [index: number]: T[]; + getColumn(index: number): T[]; + getRow(index: number): T[]; + } + + function addMatrices(...matrices: Matrix[]): Matrix; + function subtractMatrices(a: Matrix, b: Matrix): Matrix; + function scalarMultiplyMatrix(scalar: T, matrix: Matrix): Matrix; + function multiplyMatrices(a: Matrix, b: Matrix): Matrix; + function inverse(matrix: Matrix): Matrix; + function transpose(matrix: Matrix): Matrix; +} export declare const user1: Data.Models.User; declare const user2: Data.Models.User; declare const product1: EnterpriseApp.Models.Product; -export import UserService = EnterpriseApp.DataServices.UserService; -export declare const userService: UserService; +export import UService = EnterpriseApp.DataServices.UserService; +export declare const userService: UService; export import ProductService = EnterpriseApp.DataServices.ProductService; declare const productService: ProductService; export declare const allUsers: Data.Models.User[]; diff --git a/web_generator/test/integration/interop_gen/overload_expected.dart b/web_generator/test/integration/interop_gen/overload_expected.dart index 3d5400a8..57ee1737 100644 --- a/web_generator/test/integration/interop_gen/overload_expected.dart +++ b/web_generator/test/integration/interop_gen/overload_expected.dart @@ -102,6 +102,11 @@ extension type SomeNamespace._(_i1.JSObject _) implements _i1.JSObject { external static SomeNamespace_NestedNamespace_ByteBuffer arrayToByteBuffer( _i1.JSArrayBuffer bytes); } +@_i1.JS('SomeNamespace.BytesView') +extension type SomeNamespace_BytesView._(_i1.JSObject _) + implements _i1.JSObject { + external _i1.JSArrayBuffer get buffer; +} @_i1.JS('fooHelper.Options') extension type fooHelper_Options._(_i1.JSObject _) implements _i1.JSObject { external bool? round; @@ -121,11 +126,6 @@ extension type foo_fum._(_i1.JSObject _) implements _i1.JSObject { extension type baz._(_i1.JSObject _) implements _i1.JSObject { external String get debugString; } -@_i1.JS('SomeNamespace.BytesView') -extension type SomeNamespace_BytesView._(_i1.JSObject _) - implements _i1.JSObject { - external _i1.JSArrayBuffer get buffer; -} @_i1.JS('SomeNamespace.NestedNamespace') extension type SomeNamespace_NestedNamespace._(_i1.JSObject _) implements _i1.JSObject { diff --git a/web_generator/test/integration/interop_gen/project/output/a.dart b/web_generator/test/integration/interop_gen/project/output/a.dart index 914d7dc9..6dfe50d1 100644 --- a/web_generator/test/integration/interop_gen/project/output/a.dart +++ b/web_generator/test/integration/interop_gen/project/output/a.dart @@ -24,6 +24,17 @@ external V dotProduct( V v2, ); +/// Computes the dot product between two vectors. +/// - [v1]: First vector. +/// - [v2]: Second vector. +/// +/// Returns A scalar projection as a vector. +@_i1.JS('dotProduct') +external V dotProduct$1( + V v1, + V v2, +); + /// Computes the cross product of two 3D vectors. /// - [v1]: First vector. /// - [v2]: Second vector. @@ -35,6 +46,17 @@ external _i2.Vector3D crossProduct( _i2.Vector3D v2, ); +/// Computes the cross product of two 3D vectors. +/// - [v1]: First vector. +/// - [v2]: Second vector. +/// +/// Returns A new 3D vector perpendicular to both. +@_i1.JS('crossProduct') +external _i2.Vector3D crossProduct$1( + _i2.Vector3D v1, + _i2.Vector3D v2, +); + /// Maps a 2D vector to a 3D vector (z = 0). /// - [v]: Input 2D vector. /// @@ -42,13 +64,28 @@ external _i2.Vector3D crossProduct( @_i1.JS() external _i2.Vector3D mapTo3D(_i2.Vector2D v); +/// Maps a 2D vector to a 3D vector (z = 0). +/// - [v]: Input 2D vector. +/// +/// Returns A 3D vector. +@_i1.JS('mapTo3D') +external _i2.Vector3D mapTo3D$1(_i2.Vector2D v); + +/// A transformation matrix that acts as a function on 2D vectors. +/// Type Name [V]: Vector2D subtype +/// -------------------- /// A transformation matrix that acts as a function on 2D vectors. /// Type Name [V]: Vector2D subtype extension type TransformerMatrix._(_i1.JSObject _) implements Matrix { external V call(V v); + @_i1.JS('call') + external V call$1(V v); } +/// A matrix that includes vector comparison capabilities. +/// Type Name [V]: Vector2D subtype +/// -------------------- /// A matrix that includes vector comparison capabilities. /// Type Name [V]: Vector2D subtype extension type ComparatorMatrix._(_i1.JSObject _) @@ -97,6 +134,13 @@ extension type SphericalCoordinate._(_i1.JSObject _) implements _i1.JSObject { @_i1.JS() external PolarCoordinate toPolarCoordinate(_i2.Point2D point); +/// Converts a 2D point to polar coordinates. +/// - [point]: A 2D point. +/// +/// Returns Polar representation of the point. +@_i1.JS('toPolarCoordinate') +external PolarCoordinate toPolarCoordinate$1(_i2.Point2D point); + /// Converts a 3D point to spherical coordinates. /// Converts cylindrical coordinates to spherical coordinates. /// - [point]: A 3D point. @@ -117,7 +161,18 @@ external SphericalCoordinate toSphericalCoordinate(_i2.Point3D point); /// /// Returns Spherical representation. @_i1.JS('toSphericalCoordinate') -external SphericalCoordinate toSphericalCoordinate$1( +external SphericalCoordinate toSphericalCoordinate$1(_i2.Point3D point); + +/// Converts a 3D point to spherical coordinates. +/// Converts cylindrical coordinates to spherical coordinates. +/// - [point]: A 3D point. +/// +/// Returns Spherical representation. +/// - [point]: Cylindrical coordinate. +/// +/// Returns Spherical representation. +@_i1.JS('toSphericalCoordinate') +external SphericalCoordinate toSphericalCoordinate$2( CylindricalCoordinate point); /// Converts a 3D point to cylindrical coordinates. @@ -127,13 +182,12 @@ external SphericalCoordinate toSphericalCoordinate$1( @_i1.JS() external CylindricalCoordinate toCylindricalCoordinate(_i2.Point3D point); -/// Unit vector in 2D x-direction. -@_i1.JS() -external _i2.Vector2D get unitI2D; - -/// Unit vector in 2D y-direction. -@_i1.JS() -external _i2.Vector2D get unitJ2D; +/// Converts a 3D point to cylindrical coordinates. +/// - [point]: A 3D point. +/// +/// Returns Cylindrical representation. +@_i1.JS('toCylindricalCoordinate') +external CylindricalCoordinate toCylindricalCoordinate$1(_i2.Point3D point); /// A 2D coordinate system with vector and point operations. extension type CoordinateSystem2D._(_i1.JSObject _) @@ -167,17 +221,13 @@ extension type CoordinateSystem2D._(_i1.JSObject _) external _i2.Vector2D get yAxis; } -/// Unit vector in 3D x-direction. -@_i1.JS() -external _i2.Vector3D get unitI3D; - -/// Unit vector in 3D y-direction. +/// Unit vector in 2D x-direction. @_i1.JS() -external _i2.Vector3D get unitJ3D; +external _i2.Vector2D get unitI2D; -/// Unit vector in 3D z-direction. +/// Unit vector in 2D y-direction. @_i1.JS() -external _i2.Vector3D get unitK3D; +external _i2.Vector2D get unitJ2D; /// A 3D coordinate system with vector and point operations. extension type CoordinateSystem3D._(_i1.JSObject _) @@ -214,6 +264,18 @@ extension type CoordinateSystem3D._(_i1.JSObject _) external _i2.Vector3D get zAxis; } +/// Unit vector in 3D x-direction. +@_i1.JS() +external _i2.Vector3D get unitI3D; + +/// Unit vector in 3D y-direction. +@_i1.JS() +external _i2.Vector3D get unitJ3D; + +/// Unit vector in 3D z-direction. +@_i1.JS() +external _i2.Vector3D get unitK3D; + /// Represents a mathematical matrix. /// - `rows`: number of rows. /// - `columns`: number of columns. diff --git a/web_generator/test/integration/interop_gen/project/output/b.dart b/web_generator/test/integration/interop_gen/project/output/b.dart index e4bb2ff4..7e99f1ec 100644 --- a/web_generator/test/integration/interop_gen/project/output/b.dart +++ b/web_generator/test/integration/interop_gen/project/output/b.dart @@ -59,13 +59,6 @@ extension type Point3D._(_i1.JSObject _) implements Point { } @_i1.JS() external Point3D get origin3D; -extension type DirectionAngles._(_i1.JSObject _) implements _i1.JSObject { - external double alpha; - - external double beta; - - external double gamma; -} extension type Vector3D._(_i1.JSObject _) implements Vector { external Vector3D( num x, @@ -95,6 +88,13 @@ extension type Vector3D._(_i1.JSObject _) implements Vector { Point3D end, ); } +extension type DirectionAngles._(_i1.JSObject _) implements _i1.JSObject { + external double alpha; + + external double beta; + + external double gamma; +} extension type Shape._(_i1.JSObject _) implements _i1.JSObject {} extension type Shape2D._(_i1.JSObject _) implements Shape { external double? get sides;