diff --git a/web_generator/lib/src/ast/declarations.dart b/web_generator/lib/src/ast/declarations.dart index 025d15d5..f8a35363 100644 --- a/web_generator/lib/src/ast/declarations.dart +++ b/web_generator/lib/src/ast/declarations.dart @@ -426,7 +426,7 @@ class EnumMember { String? dartName; } -class TypeAliasDeclaration extends NamedDeclaration +class TypeAliasDeclaration extends NestableDeclaration implements ExportableDeclaration, DocumentedDeclaration { @override String name; @@ -452,7 +452,8 @@ class TypeAliasDeclaration extends NamedDeclaration this.typeParameters = const [], required this.type, required this.exported, - this.documentation}) + this.documentation, + this.parent}) : dartName = null; @override @@ -463,11 +464,14 @@ class TypeAliasDeclaration extends NamedDeclaration return TypeDef((t) => t ..docs.addAll([...doc]) ..annotations.addAll([...annotations]) - ..name = name + ..name = completedDartName ..types .addAll(typeParameters.map((t) => t.emit(options?.toTypeOptions()))) ..definition = type.emit(options?.toTypeOptions())); } + + @override + NestableDeclaration? parent; } /// The declaration node for a TypeScript Namespace diff --git a/web_generator/lib/src/interop_gen/transform.dart b/web_generator/lib/src/interop_gen/transform.dart index f1e58616..b1a29425 100644 --- a/web_generator/lib/src/interop_gen/transform.dart +++ b/web_generator/lib/src/interop_gen/transform.dart @@ -177,8 +177,16 @@ class ProgramMap { /// The files in the given project final p.PathSet files; + /// A list of declarations to include final List filterDeclSet; + /// The declarations as globs + List get filterDeclSetPatterns => filterDeclSet.map((decl) { + final escapedDecl = RegExp.escape(decl); + if (escapedDecl == decl) return RegExp('^$decl\$'); + return RegExp(decl); + }).toList(); + final bool generateAll; final bool strictUnsupported; @@ -201,28 +209,39 @@ class ProgramMap { } else { final src = program.getSourceFile(file); - final transformer = - _activeTransformers.putIfAbsent(file, () => Transformer(this, src)); + if (src == null && !strictUnsupported) { + // print warning + print('WARN: Could not find file $file'); + // try to transform by yourself + final anonymousTransformer = _activeTransformers.putIfAbsent( + file, () => Transformer(this, null, file: file)); + + // TODO: Replace with .transformAndReturn once #388 lands + return anonymousTransformer.transformAndReturn(node); + } else { + final transformer = + _activeTransformers.putIfAbsent(file, () => Transformer(this, src)); - if (!transformer.nodes.contains(node)) { - if (declName case final d? - when transformer.nodeMap.findByName(d).isEmpty) { - // find the source file decl - if (src == null) return null; + if (!transformer.nodes.contains(node)) { + if (declName case final d? + when transformer.nodeMap.findByName(d).isEmpty) { + // find the source file decl + if (src == null) return null; - final symbol = typeChecker.getSymbolAtLocation(src)!; - final exports = symbol.exports?.toDart ?? {}; + final symbol = typeChecker.getSymbolAtLocation(src)!; + final exports = symbol.exports?.toDart ?? {}; - final targetSymbol = exports[d.toJS]!; + final targetSymbol = exports[d.toJS]!; - transformer.transform(targetSymbol.getDeclarations()!.toDart.first); - } else { - transformer.transform(node); + transformer.transform(targetSymbol.getDeclarations()!.toDart.first); + } else { + transformer.transform(node); + } } - } - nodeMap = transformer.filterAndReturn(); - _activeTransformers[file] = transformer; + nodeMap = transformer.filterAndReturn(); + _activeTransformers[file] = transformer; + } } final name = declName ?? (node as TSNamedDeclaration).name?.text; @@ -287,8 +306,14 @@ class ProgramMap { } else { final exportedSymbols = sourceSymbol.exports?.toDart; - for (final MapEntry(value: symbol) + for (final MapEntry(key: symbolName, value: symbol) in exportedSymbols?.entries ?? >[]) { + // if there are decls to filter by and it does not match any, skip + if (!filterDeclSetPatterns + .any((f) => f.hasMatch(symbolName.toDart)) && + filterDeclSet.isNotEmpty) { + continue; + } final decls = symbol.getDeclarations()?.toDart ?? []; try { final aliasedSymbol = typeChecker.getAliasedSymbol(symbol); diff --git a/web_generator/lib/src/interop_gen/transform/transformer.dart b/web_generator/lib/src/interop_gen/transform/transformer.dart index 87f6ce3a..0ab29973 100644 --- a/web_generator/lib/src/interop_gen/transform/transformer.dart +++ b/web_generator/lib/src/interop_gen/transform/transformer.dart @@ -13,6 +13,7 @@ import '../../ast/documentation.dart'; import '../../ast/helpers.dart'; import '../../ast/types.dart'; import '../../js/annotations.dart'; +import '../../js/filesystem_api.dart'; import '../../js/helpers.dart'; import '../../js/typescript.dart' as ts; import '../../js/typescript.types.dart'; @@ -101,14 +102,14 @@ class Transformer { void transform(TSNode node) { if (nodes.contains(node)) return; - final decls = _transform(node); + final decls = transformAndReturn(node); nodeMap.addAll({for (final d in decls) d.id.toString(): d}); nodes.add(node); } - List _transform(TSNode node, + List transformAndReturn(TSNode node, {Set? exportSet, UniqueNamer? namer, NamespaceDeclaration? parent}) { @@ -287,6 +288,33 @@ class Transformer { } } + void transformDeclAndAppendParent( + NamespaceDeclaration outputNamespace, TSNode decl) { + if (outputNamespace.nodes.contains(decl)) return; + final outputDecls = + transformAndReturn(decl, namer: scopedNamer, parent: outputNamespace); + switch (decl.kind) { + case TSSyntaxKind.ClassDeclaration || TSSyntaxKind.InterfaceDeclaration: + final outputDecl = outputDecls.single as TypeDeclaration; + outputDecl.parent = outputNamespace; + outputNamespace.nestableDeclarations.add(outputDecl); + case TSSyntaxKind.EnumDeclaration: + final outputDecl = outputDecls.single as EnumDeclaration; + outputDecl.parent = outputNamespace; + outputNamespace.nestableDeclarations.add(outputDecl); + case TSSyntaxKind.TypeAliasDeclaration: + final outputDecl = outputDecls.single as TypeAliasDeclaration; + outputDecl.parent = outputNamespace; + outputNamespace.nestableDeclarations.add(outputDecl); + default: + outputNamespace.topLevelDeclarations.addAll(outputDecls); + } + outputNamespace.nodes.add(decl); + + // update namespace state + updateNSInParent(); + } + // preload nodemap updateNSInParent(); @@ -306,26 +334,7 @@ class Transformer { for (final decl in decls) { // TODO: We could also ignore namespace decls with the same name, as // a single instance should consider such non-necessary - if (outputNamespace.nodes.contains(decl)) continue; - final outputDecls = - _transform(decl, namer: scopedNamer, parent: outputNamespace); - switch (decl.kind) { - case TSSyntaxKind.ClassDeclaration || - TSSyntaxKind.InterfaceDeclaration: - final outputDecl = outputDecls.first as TypeDeclaration; - outputDecl.parent = outputNamespace; - outputNamespace.nestableDeclarations.add(outputDecl); - case TSSyntaxKind.EnumDeclaration: - final outputDecl = outputDecls.first as EnumDeclaration; - outputDecl.parent = outputNamespace; - outputNamespace.nestableDeclarations.add(outputDecl); - default: - outputNamespace.topLevelDeclarations.addAll(outputDecls); - } - outputNamespace.nodes.add(decl); - - // update namespace state - updateNSInParent(); + transformDeclAndAppendParent(outputNamespace, decl); } } // fallback @@ -334,24 +343,7 @@ class Transformer { when namespaceBody.kind == TSSyntaxKind.ModuleBlock) { for (final statement in (namespaceBody as TSModuleBlock).statements.toDart) { - final outputDecls = _transform(statement, - namer: scopedNamer, parent: outputNamespace); - switch (statement.kind) { - case TSSyntaxKind.ClassDeclaration || - TSSyntaxKind.InterfaceDeclaration: - final outputDecl = outputDecls.first as TypeDeclaration; - outputDecl.parent = outputNamespace; - outputNamespace.nestableDeclarations.add(outputDecl); - case TSSyntaxKind.EnumDeclaration: - final outputDecl = outputDecls.first as EnumDeclaration; - outputDecl.parent = outputNamespace; - outputNamespace.nestableDeclarations.add(outputDecl); - default: - outputNamespace.topLevelDeclarations.addAll(outputDecls); - } - - // update namespace state - updateNSInParent(); + transformDeclAndAppendParent(outputNamespace, statement); } } else if (namespace.body case final namespaceBody?) { // namespace import @@ -1041,7 +1033,8 @@ class Transformer { default: // TODO: Support Destructured Object Parameters // and Destructured Array Parameters - throw Exception('Unsupported Parameter Name kind ${parameter.kind}'); + throw Exception( + 'Unsupported Parameter Name kind ${parameter.name.kind}'); } } @@ -1527,6 +1520,17 @@ class Transformer { bool isNotTypableDeclaration = false, bool typeArg = false, bool isNullable = false}) { + print(( + name.map((d) => d.part).join('.'), + parentName: parent?.qualifiedName, + symbol.getDeclarations()?.toDart.map((d) => d.kind), + isNullable: isNullable, + typeArg: typeArg, + parentDecls: { + ...?parent?.namespaceDeclarations, + ...?parent?.nestableDeclarations + }.map((d) => d.name) + )); // get name and map final firstName = name.first.part; @@ -1577,9 +1581,10 @@ class Transformer { ...parent.topLevelDeclarations ].map((d) => d.id.toString())) : null; + // TODO: multi-decls final transformedDecls = - _transform(firstDecl, namer: namer, parent: parent); + transformAndReturn(firstDecl, namer: namer, parent: parent); if (parent != null) { switch (firstDecl.kind) { @@ -1613,6 +1618,12 @@ class Transformer { } // get node finally + print(( + name: name.map((n) => n.part).join('.'), + declarationsMatching.map((d) => (d.id, d is NamedDeclaration)), + symbol.getDeclarations()?.toDart.map((d) => d.kind), + name.first + )); final decl = declarationsMatching.whereType().first; // are we done? @@ -1868,8 +1879,15 @@ class Transformer { isNullable: isNullable); } else { // if import there and not this file, imported from specified file - final importUrl = - !nameImport.endsWith('.d.ts') ? '$nameImport.d.ts' : nameImport; + final importUrl = !nameImport.endsWith('.d.ts') && + fs + .existsSync((p.isAbsolute('$nameImport.d.ts') + ? '$nameImport.d.ts' + : p.join(p.dirname(file), '$nameImport.d.ts')) + .toJS) + .toDart + ? '$nameImport.d.ts' + : nameImport; final relativePath = programMap.files.contains(importUrl) ? p.relative(importUrl, from: p.dirname(file)) : null; @@ -1915,7 +1933,9 @@ class Transformer { } } } - throw Exception('Could not resolve type for node'); + throw Exception( + 'Could not resolve type for node ${fullyQualifiedName.asName}' + '${nameImport == null ? '' : ' from $nameImport'}'); } /// Get the type of a type node named [typeName] by referencing its