Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 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
270 changes: 253 additions & 17 deletions web_generator/lib/src/ast/declarations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,30 @@
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';
import 'types.dart';

/// A declaration that defines a type
///
// TODO: Add support for `ClassOrInterfaceDeclaration`
// once implementing namespaces and module support
sealed class TypeDeclaration extends NamedDeclaration
abstract class NestableDeclaration extends NamedDeclaration {
NestableDeclaration? get parent;

String get qualifiedName =>
parent != null ? '${parent!.qualifiedName}.$name' : name;

String get completedDartName => parent != null
? '${parent!.completedDartName}${dartName ?? name}'
: (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
implements ExportableDeclaration {
@override
String name;
Expand All @@ -25,6 +39,9 @@ sealed class TypeDeclaration extends NamedDeclaration
@override
final bool exported;

@override
NestableDeclaration? parent;

final List<GenericType> typeParameters;

final List<MethodDeclaration> methods;
Expand All @@ -43,7 +60,8 @@ sealed class TypeDeclaration extends NamedDeclaration
this.methods = const [],
this.properties = const [],
this.operators = const [],
this.constructors = const []});
this.constructors = const [],
this.parent});

ExtensionType _emit(
[covariant DeclarationOptions? options,
Expand Down Expand Up @@ -80,9 +98,12 @@ sealed class TypeDeclaration extends NamedDeclaration
: BuiltinType.primitiveType(PrimitiveType.object, isNullable: false);

return ExtensionType((e) => e
..name = dartName ?? name
..name = completedDartName
..annotations.addAll([
if (dartName != null && dartName != name) generateJSAnnotation(name)
if (parent != null)
generateJSAnnotation(qualifiedName)
else if (dartName != null && dartName != name)
generateJSAnnotation(name)
])
..primaryConstructorName = '_'
..representationDeclaration = RepresentationDeclaration((r) => r
Expand Down Expand Up @@ -244,7 +265,7 @@ class FunctionDeclaration extends CallableDeclaration
}
}

class EnumDeclaration extends NamedDeclaration
class EnumDeclaration extends NestableDeclaration
implements ExportableDeclaration {
@override
String name;
Expand All @@ -260,6 +281,9 @@ class EnumDeclaration extends NamedDeclaration
@override
String? dartName;

@override
NestableDeclaration? parent;

EnumDeclaration(
{required this.name,
required this.baseType,
Expand All @@ -275,11 +299,14 @@ class EnumDeclaration extends NamedDeclaration

return ExtensionType((e) => e
..annotations.addAll([
if (dartName != null && dartName != name && externalMember)
generateJSAnnotation(name)
if (externalMember)
if (parent != null)
generateJSAnnotation(qualifiedName)
else if (dartName != null && dartName != name)
generateJSAnnotation(name)
])
..constant = !shouldUseJSRepType
..name = dartName ?? name
..name = completedDartName
..primaryConstructorName = '_'
..representationDeclaration = RepresentationDeclaration((r) => r
..declaredRepresentationType = (
Expand All @@ -293,7 +320,7 @@ class EnumDeclaration extends NamedDeclaration
}

@override
ID get id => ID(type: 'enum', name: name);
ID get id => ID(type: 'enum', name: qualifiedName);
}

class EnumMember {
Expand Down Expand Up @@ -375,6 +402,212 @@ 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, ParentDeclaration {
@override
String name;

@override
String? dartName;

final ID _id;

@override
ID get id => ID(type: _id.type, name: qualifiedName, index: _id.index);

@override
bool exported;

@override
NamespaceDeclaration? parent;

List<NamespaceDeclaration> namespaceDeclarations;

List<Declaration> topLevelDeclarations;

List<NestableDeclaration> nestableDeclarations;

@override
Set<TSNode> nodes = {};

NamespaceDeclaration(
{required this.name,
this.exported = true,
required ID id,
this.dartName,
this.topLevelDeclarations = const [],
this.namespaceDeclarations = const [],
this.nestableDeclarations = const []})
: _id = id;

@override
ExtensionType emit([covariant DeclarationOptions? options]) {
// static props and vars
final methods = <Method>[];
final fields = <Field>[];

for (final decl in topLevelDeclarations) {
if (decl case final VariableDeclaration variable) {
if (variable.modifier == VariableModifier.$const) {
methods.add(Method((m) => m
..name = variable.name
..type = MethodType.getter
..static = true
..external = true
..returns = variable.type.emit(options?.toTypeOptions())));
} else {
fields.add(Field((f) => f
..external = true
..name = variable.name
..static = true
..type = variable.type.emit(options?.toTypeOptions())));
}
} else if (decl case final FunctionDeclaration fn) {
options ??= DeclarationOptions();

final requiredParams = <Parameter>[];
final optionalParams = <Parameter>[];
for (final p in fn.parameters) {
if (p.variadic) {
optionalParams
.addAll(spreadParam(p, GlobalOptions.variadicArgsCount));
requiredParams.add(p.emit(options));
} else {
if (p.optional) {
optionalParams.add(p.emit(options));
} else {
requiredParams.add(p.emit(options));
}
}
}

methods.add(Method((m) => m
..external = true
..static = true
..name = fn.dartName ?? fn.name
..annotations.add(generateJSAnnotation(
fn.dartName == null || fn.dartName == fn.name ? null : fn.name))
..types.addAll(
fn.typeParameters.map((t) => t.emit(options?.toTypeOptions())))
..returns = fn.returnType.emit(options?.toTypeOptions())
..requiredParameters.addAll(requiredParams)
..optionalParameters.addAll(optionalParams)));
}
}

// namespace refs
for (final NamespaceDeclaration(
name: namespaceName,
dartName: namespaceDartName,
) in namespaceDeclarations) {
methods.add(Method((m) => m
..name = namespaceDartName ?? namespaceName
..annotations
.addAll([generateJSAnnotation('$qualifiedName.$namespaceName')])
..type = MethodType.getter
..returns =
refer('$completedDartName${namespaceDartName ?? namespaceName}')
..external = true
..static = true));
}

// class refs
for (final nestable in nestableDeclarations) {
switch (nestable) {
case ClassDeclaration(
name: final className,
dartName: final classDartName,
constructors: final constructors,
typeParameters: final typeParams,
abstract: final abstract
):
var constr = constructors
.where((c) => c.name == null || c.name == 'unnamed')
.firstOrNull;

if (constructors.isEmpty && !abstract) {
constr = ConstructorDeclaration.defaultFor(nestable);
}

// static call to class constructor
if (constr != null) {
options ??= DeclarationOptions();

final requiredParams = <Parameter>[];
final optionalParams = <Parameter>[];

for (final p in constr.parameters) {
if (p.variadic) {
optionalParams
.addAll(spreadParam(p, GlobalOptions.variadicArgsCount));
requiredParams.add(p.emit(options));
} else {
if (p.optional) {
optionalParams.add(p.emit(options));
} else {
requiredParams.add(p.emit(options));
}
}
}

methods.add(Method((m) => m
..name = classDartName ?? className
..annotations
.addAll([generateJSAnnotation('$qualifiedName.$className')])
..types.addAll(
typeParams.map((t) => t.emit(options?.toTypeOptions())))
..requiredParameters.addAll(requiredParams)
..optionalParameters.addAll(optionalParams)
..returns =
refer('$completedDartName${classDartName ?? className}')
..lambda = true
..static = true
..body = refer(nestable.completedDartName).call(
[
...requiredParams
.where((p) => !p.named)
.map((p) => refer(p.name)),
if (optionalParams.isNotEmpty)
...optionalParams
.where((p) => !p.named)
.map((p) => refer(p.name))
],
[
...requiredParams.where((p) => p.named),
if (optionalParams.isNotEmpty)
...optionalParams.where((p) => p.named)
].asMap().map((_, v) => MapEntry(v.name, refer(v.name))),
typeParams
.map((t) => t.emit(options?.toTypeOptions()))
.toList()).code));
}
break;
default:
break;
}
}

// put them together...
return ExtensionType((eType) => eType
..name = completedDartName
..annotations.addAll([
if (parent != null)
generateJSAnnotation(qualifiedName)
else if (dartName != null && dartName != name)
generateJSAnnotation(name)
])
..implements.add(refer('JSObject', 'dart:js_interop'))
..primaryConstructorName = '_'
..representationDeclaration = RepresentationDeclaration((rep) => rep
..name = '_'
..declaredRepresentationType = refer('JSObject', 'dart:js_interop'))
..fields.addAll(fields)
..methods.addAll(methods));
}
}

/// The declaration node for a TypeScript/JavaScript Class
///
/// ```ts
Expand Down Expand Up @@ -407,7 +640,7 @@ class ClassDeclaration extends TypeDeclaration {
}

@override
ID get id => ID(type: 'class', name: name);
ID get id => ID(type: 'class', name: qualifiedName);
}

/// The declaration node for a TypeScript [Interface]()
Expand All @@ -418,22 +651,25 @@ class ClassDeclaration extends TypeDeclaration {
/// }
/// ```
class InterfaceDeclaration extends TypeDeclaration {
final ID _id;

@override
ID id;
ID get id => ID(type: _id.type, name: qualifiedName, index: _id.index);

final List<Type> extendedTypes;

InterfaceDeclaration(
{required super.name,
required super.exported,
required this.id,
required ID id,
super.dartName,
super.typeParameters,
this.extendedTypes = const [],
super.methods,
super.properties,
super.operators,
super.constructors});
super.constructors})
: _id = id;

@override
ExtensionType emit([covariant DeclarationOptions? options]) {
Expand Down
4 changes: 3 additions & 1 deletion web_generator/lib/src/ast/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ class ReferredType<T extends Declaration> extends Type {
@override
Reference emit([TypeOptions? options]) {
return TypeReference((t) => t
..symbol = declaration.dartName ?? declaration.name
..symbol = (declaration is NestableDeclaration)
? (declaration as NestableDeclaration).completedDartName
: declaration.dartName ?? declaration.name
..types.addAll(typeParams.map((t) => t.emit(options)))
..isNullable = options?.nullable
..url = options?.url ?? url);
Expand Down
Loading