Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions web_generator/lib/src/ast/declarations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import 'package:code_builder/code_builder.dart';

import '../interop_gen/namer.dart';
import '../js/typescript.types.dart';
import 'base.dart';
import 'builtin.dart';
import 'helpers.dart';
Expand All @@ -21,6 +22,10 @@ abstract class NestableDeclaration extends NamedDeclaration {
: (dartName ?? name);
}

abstract class ParentDeclaration {
Set<TSNode> get nodes;
}

/// A declaration that defines a type (class or interface)
/// which contains declarations
sealed class TypeDeclaration extends NestableDeclaration
Expand Down Expand Up @@ -400,7 +405,7 @@ class TypeAliasDeclaration extends NamedDeclaration
/// The declaration node for a TypeScript Namespace
// TODO: Refactor into shared class when supporting modules
class NamespaceDeclaration extends NestableDeclaration
implements ExportableDeclaration {
implements ExportableDeclaration, ParentDeclaration {
@override
String name;

Expand All @@ -424,6 +429,9 @@ class NamespaceDeclaration extends NestableDeclaration

List<NestableDeclaration> nestableDeclarations;

@override
Set<TSNode> nodes = {};

NamespaceDeclaration(
{required this.name,
this.exported = true,
Expand All @@ -434,15 +442,6 @@ class NamespaceDeclaration extends NestableDeclaration
this.nestableDeclarations = const []})
: _id = id;

void count() {
print('For $name: $completedDartName');
print((
nestableCount: nestableDeclarations.length,
namespaceCount: namespaceDeclarations.length,
));
namespaceDeclarations.map((n) => n.count());
}

@override
ExtensionType emit([covariant DeclarationOptions? options]) {
// static props and vars
Expand Down
3 changes: 0 additions & 3 deletions web_generator/lib/src/interop_gen/transform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,9 @@ class TransformResult {

_setGlobalOptions(config);

print('Map (${programDeclarationMap.values.map((v) => v.length)}):');

return programDeclarationMap.map((file, declMap) {
final emitter =
DartEmitter.scoped(useNullSafetySyntax: true, orderDirectives: true);
print((declMap.length, declMap.keys.toList()));
final specs = declMap.values
.map((d) {
return switch (d) {
Expand Down
149 changes: 102 additions & 47 deletions web_generator/lib/src/interop_gen/transform/transformer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import '../../ast/declarations.dart';
import '../../ast/helpers.dart';
import '../../ast/types.dart';
import '../../js/annotations.dart';
import '../../js/helpers.dart';
import '../../js/typescript.dart' as ts;
import '../../js/typescript.types.dart';
import '../namer.dart';
Expand Down Expand Up @@ -134,6 +135,10 @@ class Transformer {
TSSyntaxKind.InterfaceDeclaration =>
_transformClassOrInterface(node as TSObjectDeclaration,
namer: namer),
TSSyntaxKind.ImportEqualsDeclaration
when (node as TSImportEqualsDeclaration).moduleReference.kind !=
TSSyntaxKind.ExternalModuleReference =>
_transformImportEqualsDeclarationAsTypeAlias(node),
TSSyntaxKind.ModuleDeclaration
when (node as TSModuleDeclaration).name.kind ==
TSSyntaxKind.Identifier &&
Expand Down Expand Up @@ -185,8 +190,33 @@ class Transformer {
}
}

// TODO(): Support `import = require` declarations, https://github.com/dart-lang/web/issues/438
TypeAliasDeclaration _transformImportEqualsDeclarationAsTypeAlias(
TSImportEqualsDeclaration typealias,
{UniqueNamer? namer}) {
namer ??= this.namer;
final name = typealias.name.text;

// get modifiers
final modifiers = typealias.modifiers?.toDart ?? [];
final isExported = modifiers.any((m) {
return m.kind == TSSyntaxKind.ExportKeyword;
});

// As Identifier or Qualified Name
final type = typealias.moduleReference;

namer.markUsed(name, 'typealias');

return TypeAliasDeclaration(
name: name,
type: _getTypeFromDeclaration(type, null),
exported: isExported);
}

/// Transforms a TS Namespace (identified as a [TSModuleDeclaration] with
/// an identifier name that isn't "global") into a Dart Namespace.
/// an identifier name that isn't "global") into a Dart Namespace
/// Representation.
NamespaceDeclaration _transformNamespace(TSModuleDeclaration namespace,
{UniqueNamer? namer, NamespaceDeclaration? parent}) {
namer ??= this.namer;
Expand Down Expand Up @@ -244,34 +274,70 @@ class Transformer {
// preload nodemap
updateNSInParent();

// TODO: We could just get the declarations exported by the namespace
// however, the type reference chain is unknown (for now)
if (namespace.body case final namespaceBody?
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);
// to reduce probing, we can use exported decls instead
final symbol = typeChecker.getSymbolAtLocation(namespace.name);
final exports = symbol?.exports?.toDart;

if (exports case final exportedMap?) {
for (final symbol in exportedMap.values) {
final decls = symbol.getDeclarations()?.toDart ?? [];
try {
final aliasedSymbol = typeChecker.getAliasedSymbol(symbol);
decls.addAll(aliasedSymbol.getDeclarations()?.toDart ?? []);
} catch (_) {
// throws error if no aliased symbol, so ignore
}
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);
updateNSInParent();
}
}
} else {
if (namespace.body case final namespaceBody?
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);
}

updateNSInParent();
updateNSInParent();
}
} else if (namespace.body case final namespaceBody?) {
// namespace import
_transformNamespace(namespaceBody as TSNamespaceDeclaration,
namer: scopedNamer, parent: outputNamespace);
}
} else if (namespace.body case final namespaceBody?) {
// namespace import
_transformNamespace(namespaceBody as TSNamespaceDeclaration,
namer: scopedNamer, parent: outputNamespace);
}

updateNSInParent();
Expand Down Expand Up @@ -1125,15 +1191,6 @@ class Transformer {

if (declarationsMatching.isEmpty) {
// if not referred type, then check here

// print((
// name.join('.'),
// firstName,
// matches: declarationsMatching.length,
// map: map,
// kind: symbol.getDeclarations()?.toDart.map((d) => d.kind)
// ));

// transform
final declarations = symbol.getDeclarations()?.toDart ?? [];
var firstDecl = declarations.first as TSNamedDeclaration;
Expand Down Expand Up @@ -1273,7 +1330,8 @@ class Transformer {
if (symbol == null) {
symbol = type?.aliasSymbol ?? type?.symbol;
} else if (symbol.getDeclarations()?.toDart ?? [] case [final d]
when d.kind == TSSyntaxKind.ImportSpecifier) {
when d.kind == TSSyntaxKind.ImportSpecifier ||
d.kind == TSSyntaxKind.ImportEqualsDeclaration) {
// prefer using type node ref for such cases
// reduces import declaration handling
symbol = type?.aliasSymbol ?? type?.symbol;
Expand All @@ -1299,12 +1357,6 @@ class Transformer {
final (fullyQualifiedName, nameImport) =
parseTSFullyQualifiedName(tsFullyQualifiedName);

// print((
// fullyQualifiedName.asName,
// from: nameImport,
// declarations.map((d) => (d.kind, parent: d.parent.kind))
// ));

if (nameImport == null) {
// if import not there, most likely from an import

Expand Down Expand Up @@ -1334,8 +1386,6 @@ class Transformer {
declSource.contains('dom')) &&
!isNotTypableDeclaration) {
// dom declaration: supported by package:web
// TODO(nikeokoronkwo): It is possible that we may get a type
// that isn't in `package:web`
return PackageWebType.parse(firstName,
typeParams: (typeArguments ?? [])
.map(_transformType)
Expand Down Expand Up @@ -1498,8 +1548,14 @@ class Transformer {
/// supported `dart:js_interop` types and related [EnumDeclaration]-like and
/// [TypeDeclaration]-like checks
Type _getTypeFromDeclaration(
TSIdentifier typeName, List<TSTypeNode>? typeArguments,
{bool typeArg = false, bool isNotTypableDeclaration = false}) {
@UnionOf([TSIdentifier, TSQualifiedName]) TSNode typeName,
List<TSTypeNode>? typeArguments,
{bool typeArg = false,
bool isNotTypableDeclaration = false}) {
// union assertion
assert(typeName.kind == TSSyntaxKind.Identifier ||
typeName.kind == TSSyntaxKind.QualifiedName);

final symbol = typeChecker.getSymbolAtLocation(typeName);

return _getTypeFromSymbol(symbol, typeChecker.getTypeOfSymbol(symbol!),
Expand Down Expand Up @@ -1652,7 +1708,6 @@ class Transformer {
namespaceDeclarations: final namespaceDecls,
):
for (final tlDecl in [...typeDecls, ...namespaceDecls]) {
print((tlDecl.completedDartName, tlDecl.qualifiedName));
filteredDeclarations.add(tlDecl);
updateFilteredDeclsForDecl(tlDecl, filteredDeclarations);
}
Expand Down
24 changes: 23 additions & 1 deletion web_generator/lib/src/js/typescript.types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ extension type const TSSyntaxKind._(num _) {
static const TSSyntaxKind PropertyDeclaration = TSSyntaxKind._(172);
static const TSSyntaxKind MethodDeclaration = TSSyntaxKind._(174);
static const TSSyntaxKind ImportDeclaration = TSSyntaxKind._(272);
static const TSSyntaxKind ImportEqualsDeclaration = TSSyntaxKind._(271);
static const TSSyntaxKind ImportSpecifier = TSSyntaxKind._(276);

static const TSSyntaxKind Constructor = TSSyntaxKind._(176);
Expand Down Expand Up @@ -92,7 +93,7 @@ extension type const TSSyntaxKind._(num _) {

/// Other
static const TSSyntaxKind Identifier = TSSyntaxKind._(80);
static const TSSyntaxKind QualifiedName = TSSyntaxKind._(167);
static const TSSyntaxKind QualifiedName = TSSyntaxKind._(166);
static const TSSyntaxKind PropertyAccessExpression = TSSyntaxKind._(211);
static const TSSyntaxKind ObjectBindingPattern = TSSyntaxKind._(206);
static const TSSyntaxKind ArrayBindingPattern = TSSyntaxKind._(207);
Expand All @@ -104,6 +105,7 @@ extension type const TSSyntaxKind._(num _) {
static const TSSyntaxKind NamedImports = TSSyntaxKind._(275);
static const TSSyntaxKind ExportSpecifier = TSSyntaxKind._(281);
static const TSSyntaxKind ModuleBlock = TSSyntaxKind._(268);
static const TSSyntaxKind ExternalModuleReference = TSSyntaxKind._(283);
static const TSSyntaxKind SourceFile = TSSyntaxKind._(308);
}

Expand Down Expand Up @@ -322,6 +324,26 @@ extension type TSExportAssignment._(JSObject _)
external TSExpression get expression;
}

@JS('ImportEqualsDeclaration')
extension type TSImportEqualsDeclaration._(JSObject _)
implements TSDeclarationStatement {
@UnionOf([TSSourceFile, TSModuleDeclaration])
@redeclare
external TSDeclaration get parent;

external TSNodeArray<TSNode>? get modifiers;
external TSIdentifier get name;
external bool get isTypeOnly;

@UnionOf([TSIdentifier, TSQualifiedName, TSExternalModuleReference])
external TSNode get moduleReference;
}

@JS('ExternalModuleReference')
extension type TSExternalModuleReference._(JSObject _) implements TSNode {
external TSExpression get expression;
}

@JS('VariableStatement')
extension type TSVariableStatement._(JSObject _) implements TSStatement {
external TSVariableDeclarationList get declarationList;
Expand Down
Loading