[interop] Add Support for JSDoc Documentation (#435)

* wip: docs

* completed doc integration

* made fields final and added const for performance

* resolved issues

* refactored `generateFromDocumentation` and redundant annotation transform

refactored `generateFromDocumentation` for nullable docs and removed unnecessary add for annotations in `transformer.dart`
diff --git a/web_generator/lib/src/ast/base.dart b/web_generator/lib/src/ast/base.dart
index c158715..54158a6 100644
--- a/web_generator/lib/src/ast/base.dart
+++ b/web_generator/lib/src/ast/base.dart
@@ -5,6 +5,7 @@
 import 'package:code_builder/code_builder.dart';
 
 import '../interop_gen/namer.dart';
+import 'documentation.dart';
 import 'types.dart';
 
 class GlobalOptions {
@@ -65,6 +66,8 @@
   @override
   abstract String name;
 
+  abstract Documentation? documentation;
+
   ReferredType asReferredType([List<Type>? typeArgs, String? url]) =>
       ReferredType(
           name: name, declaration: this, typeParams: typeArgs ?? [], url: url);
diff --git a/web_generator/lib/src/ast/declarations.dart b/web_generator/lib/src/ast/declarations.dart
index 2c7367b..6fc2357 100644
--- a/web_generator/lib/src/ast/declarations.dart
+++ b/web_generator/lib/src/ast/declarations.dart
@@ -8,6 +8,7 @@
 import '../js/typescript.types.dart';
 import 'base.dart';
 import 'builtin.dart';
+import 'documentation.dart';
 import 'helpers.dart';
 import 'types.dart';
 
@@ -52,6 +53,9 @@
 
   final List<ConstructorDeclaration> constructors;
 
+  @override
+  Documentation? documentation;
+
   TypeDeclaration(
       {required this.name,
       this.dartName,
@@ -61,7 +65,8 @@
       this.properties = const [],
       this.operators = const [],
       this.constructors = const [],
-      this.parent});
+      this.parent,
+      this.documentation});
 
   ExtensionType _emit(
       [covariant DeclarationOptions? options,
@@ -72,6 +77,8 @@
 
     final hierarchy = getMemberHierarchy(this);
 
+    final (doc, annotations) = generateFromDocumentation(documentation);
+
     final fieldDecs = <Field>[];
     final methodDecs = <Method>[];
 
@@ -98,6 +105,8 @@
         : BuiltinType.primitiveType(PrimitiveType.object, isNullable: false);
 
     return ExtensionType((e) => e
+      ..docs.addAll([...doc])
+      ..annotations.addAll([...annotations])
       ..name = completedDartName
       ..annotations.addAll([
         if (parent != null)
@@ -151,11 +160,15 @@
   @override
   bool exported;
 
+  @override
+  Documentation? documentation;
+
   VariableDeclaration(
       {required this.name,
       required this.type,
       required this.modifier,
-      required this.exported});
+      required this.exported,
+      this.documentation});
 
   @override
   ID get id => ID(type: 'var', name: name);
@@ -165,8 +178,11 @@
 
   @override
   Spec emit([DeclarationOptions? options]) {
+    final (doc, annotations) = generateFromDocumentation(documentation);
     if (modifier == VariableModifier.$const) {
       return Method((m) => m
+        ..docs.addAll([...doc])
+        ..annotations.addAll([...annotations])
         ..name = name
         ..type = MethodType.getter
         ..annotations.add(generateJSAnnotation())
@@ -176,6 +192,8 @@
     } else {
       // getter and setter -> single variable
       return Field((f) => f
+        ..docs.addAll([...doc])
+        ..annotations.addAll([...annotations])
         ..external = true
         ..static = options?.static ?? false
         ..name = name
@@ -217,6 +235,9 @@
   @override
   ID id;
 
+  @override
+  Documentation? documentation;
+
   FunctionDeclaration(
       {required this.name,
       required this.id,
@@ -224,16 +245,20 @@
       this.parameters = const [],
       this.typeParameters = const [],
       required this.exported,
-      required this.returnType});
+      required this.returnType,
+      this.documentation});
 
   @override
   Method emit([DeclarationOptions? options]) {
     options ??= DeclarationOptions();
+    final (doc, annotations) = generateFromDocumentation(documentation);
 
     final (requiredParams, optionalParams) =
         emitParameters(parameters, options);
 
     return Method((m) => m
+      ..docs.addAll([...doc])
+      ..annotations.addAll([...annotations])
       ..external = true
       ..name = dartName ?? name
       ..annotations.add(generateJSAnnotation(
@@ -275,20 +300,27 @@
   @override
   NestableDeclaration? parent;
 
+  @override
+  Documentation? documentation;
+
   EnumDeclaration(
       {required this.name,
       required this.baseType,
       required this.members,
       required this.exported,
-      this.dartName});
+      this.dartName,
+      this.documentation});
 
   @override
   Spec emit([DeclarationOptions? options]) {
+    final (doc, annotations) = generateFromDocumentation(documentation);
     final baseTypeIsJSType = getJSTypeAlternative(baseType) == baseType;
     final externalMember = members.any((m) => m.isExternal);
     final shouldUseJSRepType = externalMember || baseTypeIsJSType;
 
     return ExtensionType((e) => e
+      ..docs.addAll([...doc])
+      ..annotations.addAll([...annotations])
       ..annotations.addAll([
         if (externalMember)
           if (parent != null)
@@ -325,12 +357,18 @@
 
   bool get isExternal => value == null;
 
+  Documentation? documentation;
+
   EnumMember(this.name, this.value,
-      {this.type, required this.parent, this.dartName});
+      {this.type, required this.parent, this.dartName, this.documentation});
 
   Field emit([bool? shouldUseJSRepType]) {
     final jsRep = shouldUseJSRepType ?? (value == null);
+    final (doc, annotations) = generateFromDocumentation(documentation);
     return Field((f) {
+      f
+        ..docs.addAll([...doc])
+        ..annotations.addAll([...annotations]);
       // TODO(nikeokoronkwo): This does not render correctly on `code_builder`.
       //  Until the update is made, we will omit examples concerning this
       //  Luckily, not many real-world instances of enums use this anyways, https://github.com/dart-lang/tools/issues/2118
@@ -374,18 +412,25 @@
   @override
   ID get id => ID(type: 'typealias', name: name);
 
+  @override
+  Documentation? documentation;
+
   TypeAliasDeclaration(
       {required this.name,
       this.typeParameters = const [],
       required this.type,
-      required this.exported})
+      required this.exported,
+      this.documentation})
       : dartName = null;
 
   @override
   TypeDef emit([DeclarationOptions? options]) {
     options ??= DeclarationOptions();
+    final (doc, annotations) = generateFromDocumentation(documentation);
 
     return TypeDef((t) => t
+      ..docs.addAll([...doc])
+      ..annotations.addAll([...annotations])
       ..name = name
       ..types
           .addAll(typeParameters.map((t) => t.emit(options?.toTypeOptions())))
@@ -423,6 +468,9 @@
   @override
   Set<TSNode> nodes = {};
 
+  @override
+  Documentation? documentation;
+
   NamespaceDeclaration(
       {required this.name,
       this.exported = true,
@@ -430,13 +478,17 @@
       this.dartName,
       this.topLevelDeclarations = const {},
       this.namespaceDeclarations = const {},
-      this.nestableDeclarations = const {}})
+      this.nestableDeclarations = const {},
+      this.documentation})
       : _id = id;
 
   @override
   ExtensionType emit([covariant DeclarationOptions? options]) {
     options ??= DeclarationOptions();
     options.static = true;
+
+    final (doc, annotations) = generateFromDocumentation(documentation);
+
     // static props and vars
     final methods = <Method>[];
     final fields = <Field>[];
@@ -525,6 +577,8 @@
 
     // put them together...
     return ExtensionType((eType) => eType
+      ..docs.addAll([...doc])
+      ..annotations.addAll([...annotations])
       ..name = completedDartName
       ..annotations.addAll([
         if (parent != null)
@@ -565,7 +619,8 @@
       super.constructors,
       required super.methods,
       required super.properties,
-      super.operators});
+      super.operators,
+      super.documentation});
 
   @override
   ExtensionType emit([covariant DeclarationOptions? options]) {
@@ -602,7 +657,8 @@
       super.methods,
       super.properties,
       super.operators,
-      super.constructors})
+      super.constructors,
+      super.documentation})
       : _id = id;
 
   @override
@@ -642,6 +698,9 @@
   @override
   Type type;
 
+  @override
+  Documentation? documentation;
+
   PropertyDeclaration(
       {required this.name,
       this.dartName,
@@ -650,15 +709,20 @@
       this.scope = DeclScope.public,
       this.readonly = false,
       required this.static,
-      this.isNullable = false});
+      this.isNullable = false,
+      this.documentation});
 
   @override
   Spec emit([covariant DeclarationOptions? options]) {
     options ??= DeclarationOptions();
     assert(scope == DeclScope.public, 'Only public members can be emitted');
 
+    final (doc, annotations) = generateFromDocumentation(documentation);
+
     if (readonly) {
       return Method((m) => m
+        ..docs.addAll([...doc])
+        ..annotations.addAll([...annotations])
         ..external = true
         ..name = dartName ?? name
         ..type = MethodType.getter
@@ -669,6 +733,8 @@
         ..returns = type.emit(options?.toTypeOptions(nullable: isNullable)));
     } else {
       return Field((f) => f
+        ..docs.addAll([...doc])
+        ..annotations.addAll([...annotations])
         ..external = true
         ..name = dartName ?? name
         ..annotations.addAll([
@@ -712,6 +778,9 @@
 
   final bool isNullable;
 
+  @override
+  Documentation? documentation;
+
   MethodDeclaration(
       {required this.name,
       this.dartName,
@@ -722,12 +791,15 @@
       required this.returnType,
       this.static = false,
       this.scope = DeclScope.public,
-      this.isNullable = false});
+      this.isNullable = false,
+      this.documentation});
 
   @override
   Method emit([covariant DeclarationOptions? options]) {
     options ??= DeclarationOptions();
 
+    final (doc, annotations) = generateFromDocumentation(documentation);
+
     final (requiredParams, optionalParams) =
         emitParameters(parameters, options);
 
@@ -735,6 +807,8 @@
 
     if (isNullable) {
       return Method((m) => m
+        ..docs.addAll([...doc])
+        ..annotations.addAll([...annotations])
         ..external = true
         ..name = dartName ?? name
         ..type = MethodType.getter
@@ -753,6 +827,8 @@
     }
 
     return Method((m) => m
+      ..docs.addAll([...doc])
+      ..annotations.addAll([...annotations])
       ..external = true
       ..name = dartName ?? name
       ..type = switch (kind) {
@@ -804,12 +880,15 @@
 
   final String? dartName;
 
+  Documentation? documentation;
+
   ConstructorDeclaration(
       {this.parameters = const [],
       this.name,
       String? dartName,
       required this.id,
-      this.scope = DeclScope.public})
+      this.scope = DeclScope.public,
+      this.documentation})
       : dartName = dartName == 'unnamed' ? null : dartName;
 
   static ConstructorDeclaration defaultFor(TypeDeclaration decl) {
@@ -819,6 +898,7 @@
 
   Constructor emit([covariant DeclarationOptions? options]) {
     options ??= DeclarationOptions();
+    final (doc, annotations) = generateFromDocumentation(documentation);
 
     final (requiredParams, optionalParams) =
         emitParameters(parameters, options);
@@ -826,6 +906,8 @@
     final isFactory = dartName != null && dartName != name;
 
     return Constructor((c) => c
+      ..docs.addAll([...doc])
+      ..annotations.addAll([...annotations])
       ..external = true
       ..name = dartName ?? name
       ..annotations
@@ -869,6 +951,9 @@
 
   final bool static;
 
+  @override
+  Documentation? documentation;
+
   OperatorDeclaration(
       {required this.kind,
       this.dartName,
@@ -876,12 +961,15 @@
       required this.returnType,
       this.typeParameters = const [],
       this.scope = DeclScope.public,
-      this.static = false});
+      this.static = false,
+      this.documentation});
 
   @override
   Method emit([covariant DeclarationOptions? options]) {
     options ??= DeclarationOptions();
 
+    final (doc, annotations) = generateFromDocumentation(documentation);
+
     final requiredParams = <Parameter>[];
     final optionalParams = <Parameter>[];
     for (final p in parameters) {
@@ -896,6 +984,8 @@
     }
 
     return Method((m) => m
+      ..docs.addAll([...doc])
+      ..annotations.addAll([...annotations])
       ..external = true
       ..name = 'operator $name'
       ..types
diff --git a/web_generator/lib/src/ast/documentation.dart b/web_generator/lib/src/ast/documentation.dart
new file mode 100644
index 0000000..3f9ebb8
--- /dev/null
+++ b/web_generator/lib/src/ast/documentation.dart
@@ -0,0 +1,52 @@
+import 'package:code_builder/code_builder.dart';
+
+/// Parsed documentation from JSDoc suitable for Dart
+///
+/// The documentation produced for Dart follows a given pattern to
+/// make docs that are simple and follow Dart conventions.
+///
+/// Some tags used in JSDoc may also be converted to Dart
+/// annotations
+class Documentation {
+  final String docs;
+
+  final List<Annotation> annotations;
+
+  const Documentation({required this.docs, this.annotations = const []});
+}
+
+class Annotation {
+  final AnnotationKind kind;
+
+  final List<(String, {String? name})> arguments;
+
+  const Annotation({required this.kind, this.arguments = const []});
+
+  Expression emit() {
+    if (arguments.isEmpty) {
+      return refer(kind.name, kind.source);
+    }
+    final positionalArgs = <Expression>[];
+    final namedArgs = <String, Expression>{};
+
+    for (final (name, name: nameArg) in arguments) {
+      if (nameArg != null) {
+        namedArgs[nameArg] = literal(name);
+      } else {
+        positionalArgs.add(literal(name));
+      }
+    }
+
+    return refer(kind.name, kind.source).call(positionalArgs, namedArgs);
+  }
+}
+
+enum AnnotationKind {
+  deprecated('Deprecated'),
+  experimental('experimental', source: 'package:meta/meta.dart');
+
+  const AnnotationKind(this.name, {this.source});
+
+  final String name;
+  final String? source;
+}
diff --git a/web_generator/lib/src/ast/helpers.dart b/web_generator/lib/src/ast/helpers.dart
index acd3c82..472ade6 100644
--- a/web_generator/lib/src/ast/helpers.dart
+++ b/web_generator/lib/src/ast/helpers.dart
@@ -2,11 +2,14 @@
 // 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:convert';
+
 import 'package:code_builder/code_builder.dart';
 
 import 'base.dart';
 import 'builtin.dart';
 import 'declarations.dart';
+import 'documentation.dart';
 import 'types.dart';
 
 Type getJSTypeAlternative(Type type) {
@@ -113,6 +116,22 @@
   }
 }
 
+(List<String>, List<Expression>) generateFromDocumentation(
+    Documentation? docs) {
+  if (docs == null) return ([], []);
+
+  if (docs.docs.trim().isEmpty) {
+    return ([], docs.annotations.map((d) => d.emit()).toList());
+  }
+  return (
+    const LineSplitter()
+        .convert(docs.docs.trim())
+        .map((d) => '/// $d')
+        .toList(),
+    docs.annotations.map((d) => d.emit()).toList()
+  );
+}
+
 (List<Parameter>, List<Parameter>) emitParameters(
     List<ParameterDeclaration> parameters,
     [DeclarationOptions? options]) {
diff --git a/web_generator/lib/src/interop_gen/transform/transformer.dart b/web_generator/lib/src/interop_gen/transform/transformer.dart
index d209f4d..e2e3634 100644
--- a/web_generator/lib/src/interop_gen/transform/transformer.dart
+++ b/web_generator/lib/src/interop_gen/transform/transformer.dart
@@ -8,6 +8,7 @@
 import '../../ast/base.dart';
 import '../../ast/builtin.dart';
 import '../../ast/declarations.dart';
+import '../../ast/documentation.dart';
 import '../../ast/helpers.dart';
 import '../../ast/types.dart';
 import '../../js/annotations.dart';
@@ -212,7 +213,8 @@
     return TypeAliasDeclaration(
         name: name,
         type: _getTypeFromDeclaration(type, null),
-        exported: isExported);
+        exported: isExported,
+        documentation: _parseAndTransformDocumentation(typealias));
   }
 
   /// Transforms a TS Namespace (identified as a [TSModuleDeclaration] with
@@ -249,7 +251,8 @@
             exported: isExported,
             topLevelDeclarations: {},
             namespaceDeclarations: {},
-            nestableDeclarations: {});
+            nestableDeclarations: {},
+            documentation: _parseAndTransformDocumentation(namespace));
 
     // TODO: We can implement this in classes and interfaces.
     //  however, since namespaces and modules are a thing,
@@ -410,7 +413,8 @@
             methods: [],
             properties: [],
             operators: [],
-            constructors: [])
+            constructors: [],
+            documentation: _parseAndTransformDocumentation(typeDecl))
         : ClassDeclaration(
             name: name,
             dartName: dartName,
@@ -423,7 +427,8 @@
             constructors: [],
             methods: [],
             properties: [],
-            operators: []);
+            operators: [],
+            documentation: _parseAndTransformDocumentation(typeDecl));
 
     final typeNamer = ScopedUniqueNamer({'get', 'set'});
 
@@ -528,7 +533,8 @@
                 : _transformType(property.type!)),
         static: isStatic,
         readonly: isReadonly,
-        isNullable: property.questionToken != null);
+        isNullable: property.questionToken != null,
+        documentation: _parseAndTransformDocumentation(property));
     propertyDeclaration.parent = parent;
     return propertyDeclaration;
   }
@@ -590,7 +596,8 @@
                 ? _transformType(method.type!)
                 : BuiltinType.anyType),
         isNullable: (method.kind == TSSyntaxKind.MethodSignature) &&
-            (method as TSMethodSignature).questionToken != null);
+            (method as TSMethodSignature).questionToken != null,
+        documentation: _parseAndTransformDocumentation(method));
     methodDeclaration.parent = parent;
     return methodDeclaration;
   }
@@ -617,7 +624,8 @@
         dartName: dartName.isEmpty ? null : dartName,
         name: name,
         parameters: params.map(_transformParameter).toList(),
-        scope: scope);
+        scope: scope,
+        documentation: _parseAndTransformDocumentation(constructor));
   }
 
   MethodDeclaration _transformCallSignature(
@@ -655,7 +663,8 @@
         returnType: methodType ??
             (callSignature.type != null
                 ? _transformType(callSignature.type!)
-                : BuiltinType.anyType));
+                : BuiltinType.anyType),
+        documentation: _parseAndTransformDocumentation(callSignature));
     methodDeclaration.parent = parent;
     return methodDeclaration;
   }
@@ -686,6 +695,8 @@
       indexerType = parent.asReferredType(parent.typeParameters);
     }
 
+    final doc = _parseAndTransformDocumentation(indexSignature);
+
     final getOperatorDeclaration = OperatorDeclaration(
         kind: OperatorKind.squareBracket,
         parameters: params.map(_transformParameter).toList(),
@@ -693,7 +704,8 @@
         scope: scope,
         typeParameters:
             typeParams?.map(_transformTypeParamDeclaration).toList() ?? [],
-        static: isStatic);
+        static: isStatic,
+        documentation: doc);
     final setOperatorDeclaration = isReadonly
         ? OperatorDeclaration(
             kind: OperatorKind.squareBracketSet,
@@ -702,7 +714,8 @@
             scope: scope,
             typeParameters:
                 typeParams?.map(_transformTypeParamDeclaration).toList() ?? [],
-            static: isStatic)
+            static: isStatic,
+            documentation: doc)
         : null;
 
     getOperatorDeclaration.parent = parent;
@@ -748,7 +761,8 @@
         returnType: methodType ??
             (getter.type != null
                 ? _transformType(getter.type!)
-                : BuiltinType.anyType));
+                : BuiltinType.anyType),
+        documentation: _parseAndTransformDocumentation(getter));
     methodDeclaration.parent = parent;
     return methodDeclaration;
   }
@@ -791,7 +805,8 @@
             typeParams?.map(_transformTypeParamDeclaration).toList() ?? [],
         returnType: setter.type != null
             ? _transformType(setter.type!)
-            : BuiltinType.anyType);
+            : BuiltinType.anyType,
+        documentation: _parseAndTransformDocumentation(setter));
     methodDeclaration.parent = parent;
     return methodDeclaration;
   }
@@ -822,7 +837,8 @@
             typeParams?.map(_transformTypeParamDeclaration).toList() ?? [],
         returnType: function.type != null
             ? _transformType(function.type!)
-            : BuiltinType.anyType);
+            : BuiltinType.anyType,
+        documentation: _parseAndTransformDocumentation(function));
   }
 
   List<VariableDeclaration> _transformVariable(TSVariableStatement variable,
@@ -866,7 +882,8 @@
         name: d.name.text,
         type: d.type == null ? BuiltinType.anyType : _transformType(d.type!),
         modifier: modifier,
-        exported: isExported ?? false);
+        exported: isExported ?? false,
+        documentation: _parseAndTransformDocumentation(d));
   }
 
   EnumDeclaration _transformEnum(TSEnumDeclaration enumeration,
@@ -904,7 +921,8 @@
             members.add(EnumMember(memName, value,
                 type: BuiltinType.primitiveType(primitiveType),
                 parent: name,
-                dartName: dartMemName));
+                dartName: dartMemName,
+                documentation: _parseAndTransformDocumentation(member)));
             if (enumRepType == null &&
                 !(primitiveType == PrimitiveType.int &&
                     enumRepType == PrimitiveType.double)) {
@@ -921,7 +939,8 @@
             members.add(EnumMember(memName, value,
                 type: BuiltinType.primitiveType(primitiveType),
                 parent: name,
-                dartName: dartMemName));
+                dartName: dartMemName,
+                documentation: _parseAndTransformDocumentation(member)));
             if (enumRepType == null) {
               enumRepType = primitiveType;
             } else if (enumRepType != primitiveType) {
@@ -930,13 +949,14 @@
             break;
           default:
             // unsupported
-
             break;
         }
       } else {
         // get the type
-        members.add(
-            EnumMember(memName, null, parent: name, dartName: dartMemName));
+        members.add(EnumMember(memName, null,
+            parent: name,
+            dartName: dartMemName,
+            documentation: _parseAndTransformDocumentation(member)));
       }
     }
 
@@ -946,7 +966,8 @@
         name: name,
         baseType: BuiltinType.primitiveType(enumRepType ?? PrimitiveType.num),
         members: members,
-        exported: isExported);
+        exported: isExported,
+        documentation: _parseAndTransformDocumentation(enumeration));
   }
 
   num _parseNumericLiteral(TSNumericLiteral numericLiteral) {
@@ -979,7 +1000,8 @@
         type: _transformType(type),
         typeParameters:
             typeParams?.map(_transformTypeParamDeclaration).toList() ?? [],
-        exported: isExported);
+        exported: isExported,
+        documentation: _parseAndTransformDocumentation(typealias));
   }
 
   ParameterDeclaration _transformParameter(TSParameterDeclaration parameter,
@@ -1601,6 +1623,138 @@
         typeArguments, isNotTypableDeclaration, typeArg);
   }
 
+  /// Extracts associated documentation (JSDoc) from a [TSNode] and transforms
+  /// the JSDoc into associated Dart documentation for the given [node]
+  Documentation? _parseAndTransformDocumentation(TSNamedDeclaration node) {
+    // get symbol
+    final symbol = typeChecker.getSymbolAtLocation(node.name ?? node);
+    final jsDocTags = symbol?.getJsDocTags();
+    final doc = symbol?.getDocumentationComment(typeChecker);
+
+    // transform documentation
+    if (doc == null && jsDocTags == null) {
+      return null;
+    } else {
+      return _transformDocumentation(
+          doc?.toDart ?? [], jsDocTags?.toDart ?? []);
+    }
+  }
+
+  Documentation _transformDocumentation(
+      List<TSSymbolDisplayPart> topLevelDocParts,
+      List<JSDocTagInfo> jsDocTags) {
+    final docBuffer = StringBuffer();
+    final annotations = <Annotation>[];
+
+    for (final doc in topLevelDocParts) {
+      final docString = _parseSymbolDisplayPart(doc);
+      docBuffer.write(docString);
+    }
+
+    docBuffer.writeln();
+
+    // parse annotations
+    for (final tag in jsDocTags) {
+      switch (tag.name) {
+        case 'deprecated':
+          final tagBuffer = StringBuffer();
+          for (final part in tag.text?.toDart ?? <TSSymbolDisplayPart>[]) {
+            tagBuffer.write(_parseSymbolDisplayPart(part));
+          }
+          annotations.add(Annotation(
+              kind: AnnotationKind.deprecated,
+              arguments: [
+                if (tag.text?.toDart.isNotEmpty ?? false)
+                  (tagBuffer.toString(), name: null)
+              ]));
+          break;
+        case 'experimental':
+          annotations.add(const Annotation(kind: AnnotationKind.experimental));
+          if (tag.text?.toDart case final expText? when expText.isNotEmpty) {
+            final tagBuffer = StringBuffer();
+            for (final part in expText) {
+              tagBuffer.write(_parseSymbolDisplayPart(part));
+            }
+            docBuffer.writeln('**EXPERIMENTAL**: ${tagBuffer.toString()}');
+          }
+          break;
+        case 'param':
+          final tags = tag.text?.toDart ?? [];
+          if (tags.isEmpty) continue;
+
+          final parameterName =
+              tags.where((t) => t.kind == 'parameterName').firstOrNull;
+          final parameterDesc = tags
+              .where((t) => t.kind == 'text')
+              .fold('', (prev, combine) => '$prev ${combine.text}');
+
+          if (parameterName != null) {
+            docBuffer.writeln('- [${parameterName.text}]: $parameterDesc');
+          }
+          break;
+        case 'returns':
+          final tagBuffer = StringBuffer();
+          for (final part in tag.text?.toDart ?? <TSSymbolDisplayPart>[]) {
+            tagBuffer.write(_parseSymbolDisplayPart(part));
+          }
+          if (tagBuffer.length != 0) {
+            docBuffer.writeln('\nReturns ${tagBuffer.toString()}');
+          }
+          break;
+        case 'example':
+          final tagBuffer = StringBuffer();
+          for (final part in tag.text?.toDart ?? <TSSymbolDisplayPart>[]) {
+            tagBuffer.write(_parseSymbolDisplayPart(part));
+          }
+          docBuffer.writeAll([
+            '\nExample:',
+            '```ts',
+            tagBuffer.toString(),
+            '```',
+          ], '\n');
+        case 'template':
+          final tags = tag.text?.toDart ?? [];
+          if (tags.isEmpty) continue;
+
+          final typeName =
+              tags.where((t) => t.kind == 'typeParameterName').firstOrNull;
+
+          if (typeName == null) continue;
+
+          final tagBuffer = StringBuffer();
+          for (final part in tag.text?.toDart ?? <TSSymbolDisplayPart>[]) {
+            if (part.kind != 'typeParameterName') {
+              tagBuffer.write(_parseSymbolDisplayPart(part));
+            }
+          }
+          docBuffer
+              .writeln('Type Name [${typeName.text}]: ${tagBuffer.toString()}');
+        default:
+          continue;
+      }
+    }
+
+    return Documentation(docs: docBuffer.toString(), annotations: annotations);
+  }
+
+  String _parseSymbolDisplayPart(TSSymbolDisplayPart part) {
+    // what if decl is not already parsed?
+    if (part.kind == 'linkName') {
+      final decls = nodeMap.findByName(part.text);
+      if (decls.isNotEmpty) {
+        final firstNode = decls.first;
+        return firstNode.dartName ?? firstNode.name ?? firstNode.id.name;
+      } else {
+        return part.text;
+      }
+    }
+    return switch (part.kind) {
+      'text' => part.text,
+      'link' => '',
+      _ => part.text
+    };
+  }
+
   /// Filters out the declarations generated from the [transform] function and
   /// returns the declarations needed based on:
   ///
diff --git a/web_generator/lib/src/js/typescript.types.dart b/web_generator/lib/src/js/typescript.types.dart
index d43e0a5..b0c51b6 100644
--- a/web_generator/lib/src/js/typescript.types.dart
+++ b/web_generator/lib/src/js/typescript.types.dart
@@ -234,7 +234,7 @@
 }
 
 @JS('NamedDeclaration')
-extension type TSNamedDeclaration._(JSObject _) implements TSNode {
+extension type TSNamedDeclaration._(JSObject _) implements TSDeclaration {
   // TODO: Support other name specifiers
   external TSIdentifier? get name;
 }
@@ -390,7 +390,7 @@
 
 /// A common API for Classes and Interfaces
 extension type TSObjectDeclaration<T extends TSDeclaration>._(JSObject _)
-    implements TSDeclaration, TSStatement {
+    implements TSDeclarationStatement {
   // TODO: May be undefined for classes in default exports
   external TSIdentifier get name;
   external TSNodeArray<TSNode>? get modifiers;
@@ -422,7 +422,7 @@
   external TSNodeArray<TSTypeNode>? get typeArguments;
 }
 
-extension type TSPropertyEntity._(JSObject _) implements TSDeclaration {
+extension type TSPropertyEntity._(JSObject _) implements TSNamedDeclaration {
   external TSNodeArray<TSNode>? get modifiers;
   external TSIdentifier get name;
   external TSToken? get questionToken;
@@ -441,7 +441,7 @@
 }
 
 @JS('ClassElement')
-extension type TSClassElement._(JSObject _) implements TSDeclaration {
+extension type TSClassElement._(JSObject _) implements TSNamedDeclaration {
   external TSIdentifier? get name;
 }
 
@@ -471,7 +471,7 @@
 }
 
 @JS('TypeElement')
-extension type TSTypeElement._(JSObject _) implements TSDeclaration {
+extension type TSTypeElement._(JSObject _) implements TSNamedDeclaration {
   external TSIdentifier? get name;
   external TSToken? get questionToken;
 }
@@ -531,7 +531,7 @@
 
 @JS('TypeAliasDeclaration')
 extension type TSTypeAliasDeclaration._(JSObject _)
-    implements TSDeclaration, TSStatement {
+    implements TSDeclarationStatement, TSStatement {
   external TSNodeArray<TSNode>? get modifiers;
   external TSNodeArray<TSTypeParameterDeclaration>? get typeParameters;
   external TSIdentifier get name;
@@ -556,14 +556,14 @@
 
 @JS('EnumDeclaration')
 extension type TSEnumDeclaration._(JSObject _)
-    implements TSDeclaration, TSStatement {
+    implements TSDeclarationStatement, TSStatement {
   external TSIdentifier get name;
   external TSNodeArray<TSNode>? get modifiers;
   external TSNodeArray<TSEnumMember> get members;
 }
 
 @JS('EnumMember')
-extension type TSEnumMember._(JSObject _) implements TSDeclaration {
+extension type TSEnumMember._(JSObject _) implements TSNamedDeclaration {
   external TSIdentifier get name;
   external TSExpression? get initializer;
 }
@@ -604,11 +604,26 @@
 extension type TSSymbol._(JSObject _) implements JSObject {
   external String get name;
   external JSArray<TSDeclaration>? getDeclarations();
+  external JSArray<TSSymbolDisplayPart> getDocumentationComment(
+      TSTypeChecker? typeChecker);
+  external JSArray<JSDocTagInfo> getJsDocTags([TSTypeChecker checker]);
   external TSSymbolTable? get exports;
 }
 
 typedef TSSymbolTable = JSMap<JSString, TSSymbol>;
 
+@JS('SymbolDisplayPart')
+extension type TSSymbolDisplayPart._(JSObject _) implements JSObject {
+  external String text;
+  external String kind;
+}
+
+@JS()
+extension type JSDocTagInfo._(JSObject _) implements JSObject {
+  external String name;
+  external JSArray<TSSymbolDisplayPart>? text;
+}
+
 @JS('Type')
 extension type TSType._(JSObject _) implements JSObject {
   external TSSymbol get symbol;
diff --git a/web_generator/test/integration/interop_gen/classes_expected.dart b/web_generator/test/integration/interop_gen/classes_expected.dart
index 244db76..49d010f 100644
--- a/web_generator/test/integration/interop_gen/classes_expected.dart
+++ b/web_generator/test/integration/interop_gen/classes_expected.dart
@@ -296,6 +296,8 @@
   @_i1.JS('area')
   external String area$1(AnonymousUnion unit);
   external static EpahsImpl getById(String id);
+
+  /// Returns a string representation of an object.
   @_i1.JS('toString')
   external String toString$();
 }
diff --git a/web_generator/test/integration/interop_gen/classes_input.d.ts b/web_generator/test/integration/interop_gen/classes_input.d.ts
index d36a423..f1923dd 100644
--- a/web_generator/test/integration/interop_gen/classes_input.d.ts
+++ b/web_generator/test/integration/interop_gen/classes_input.d.ts
@@ -19,8 +19,8 @@
     id: number;
     protected username: string;
     private email;
-    constructor(id: number, // Public property
-    username: string, // Protected property
+    constructor(id: number,
+    username: string,
     email: string);
     greet(): string;
     getEmail(): string;
@@ -163,7 +163,6 @@
 export declare class EpahsImpl<TMeta = any> implements Epahs<TMeta> {
     readonly id: string;
     name: string;
-    /* other decls in Shape */
     metadata?: TMeta;
     constructor(name: string, type?: 'circle' | 'rectangle' | 'polygon');
     onUpdate?(prev: Epahs<TMeta>): void;
diff --git a/web_generator/test/integration/interop_gen/jsdoc_expected.dart b/web_generator/test/integration/interop_gen/jsdoc_expected.dart
new file mode 100644
index 0000000..1d088ed
--- /dev/null
+++ b/web_generator/test/integration/interop_gen/jsdoc_expected.dart
@@ -0,0 +1,102 @@
+// ignore_for_file: constant_identifier_names, non_constant_identifier_names
+
+// ignore_for_file: no_leading_underscores_for_library_prefixes
+import 'dart:js_interop' as _i1;
+
+import 'package:meta/meta.dart' as _i2;
+
+/// Represents a user in the system.
+extension type User._(_i1.JSObject _) implements _i1.JSObject {
+  /// Unique identifier for the user.
+  external String id;
+
+  /// The full name of the user.
+  external String name;
+
+  /// The user's email address.
+  external String email;
+
+  /// The user's age in years.
+  external double age;
+}
+
+/// Gets a user by their ID from the legacy store.
+@Deprecated('Use `getUserById` instead.')
+@_i1.JS()
+external User fetchUser(String userId);
+
+/// Gets a user by their ID.
+/// - [userId]:  - The ID of the user to retrieve.
+///
+/// Returns A promise that resolves to a user object or null.
+@_i1.JS()
+external _i1.JSPromise<User> getUserById(String userId);
+
+/// Registers a new user into the system.
+/// **EXPERIMENTAL**: API under development and may change without notice.
+/// - [newUser]:  - A new user object without an ID.
+///
+/// Returns The created user with a generated ID.
+@_i2.experimental
+@_i1.JS()
+external _i1.JSPromise<User> registerUser(User newUser);
+
+/// Logs an event to the server.
+/// - [event]:  - The name of the event.
+/// - [payload]:  - Additional data associated with the event.
+@_i1.JS()
+external void logEvent(
+  String event,
+  _i1.JSObject payload,
+);
+
+/// Represents a configuration object for the app.
+extension type AppConfig._(_i1.JSObject _) implements _i1.JSObject {
+  /// The environment name (e.g., 'dev', 'prod').
+  external String env;
+
+  /// Whether debug mode is enabled.
+  external bool debug;
+
+  /// Enabled features in the app.
+  external _i1.JSArray<_i1.JSString> features;
+}
+
+/// Updates the application configuration.
+/// - [updates]:  - The config values to update.
+///
+/// Returns The updated configuration.
+@_i1.JS()
+external AppConfig updateConfig(AppConfig updates);
+
+/// **EXPERIMENTAL**: This function is being tested for future use.
+/// Initializes the system with async resources.
+@_i2.experimental
+@_i1.JS()
+external _i1.JSPromise<_i1.JSAny?> initializeSystem();
+
+/// Cleans up resources before shutting down.
+@Deprecated('Use `shutdownSystem()` instead.')
+@_i1.JS()
+external void cleanup();
+
+/// Properly shuts down the system and releases all resources.
+@_i1.JS()
+external _i1.JSPromise<_i1.JSAny?> shutdownSystem();
+extension type Logger._(_i1.JSObject _) implements _i1.JSObject {
+  external void info(String msg);
+  external void warn(String msg);
+  external void error(String msg);
+}
+
+/// Creates a simple logger instance.
+@_i1.JS()
+external Logger createLogger();
+
+/// A constant representing the current application version.
+@_i1.JS()
+external String get APP_VERSION;
+
+/// The default configuration for the application.
+@_i1.JS()
+external AppConfig get DEFAULT_CONFIG;
diff --git a/web_generator/test/integration/interop_gen/jsdoc_input.d.ts b/web_generator/test/integration/interop_gen/jsdoc_input.d.ts
new file mode 100644
index 0000000..702b302
--- /dev/null
+++ b/web_generator/test/integration/interop_gen/jsdoc_input.d.ts
@@ -0,0 +1,100 @@
+/**
+ * @fileoverview A large example TypeScript file demonstrating extensive use
+ * of JSDoc comments, including @deprecated and @experimental annotations.
+ */
+/**
+ * Represents a user in the system.
+ */
+export interface User {
+    /** Unique identifier for the user. */
+    id: string;
+    /** The full name of the user. */
+    name: string;
+    /** The user's email address. */
+    email: string;
+    /** The user's age in years. */
+    age: number;
+}
+/**
+ * A constant representing the current application version.
+ * @type {string}
+ */
+export declare const APP_VERSION: string;
+/**
+ * Gets a user by their ID from the legacy store.
+ * 
+ * @deprecated Use `getUserById` instead.
+ * @param {string} userId
+ * @returns {User}
+ */
+export declare function fetchUser(userId: string): User;
+/**
+ * Gets a user by their ID.
+ * @param {string} userId - The ID of the user to retrieve.
+ * @returns {Promise<User>} A promise that resolves to a user object or null.
+ */
+export declare function getUserById(userId: string): Promise<User>;
+/**
+ * Registers a new user into the system.
+ * 
+ * @experimental API under development and may change without notice.
+ * 
+ * @param {User} newUser - A new user object without an ID.
+ * @returns {Promise<User>} The created user with a generated ID.
+ */
+export declare function registerUser(newUser: User): Promise<User>;
+/**
+ * Logs an event to the server.
+ * @param {string} event - The name of the event.
+ * @param {object} payload - Additional data associated with the event.
+ */
+export declare function logEvent(event: string, payload: object): void;
+/**
+ * Represents a configuration object for the app.
+ */
+export interface AppConfig {
+    /** The environment name (e.g., 'dev', 'prod'). */
+    env: string;
+    /** Whether debug mode is enabled. */
+    debug: boolean;
+    /** Enabled features in the app. */
+    features: string[];
+}
+/**
+ * The default configuration for the application.
+ * @type {AppConfig}
+ */
+export declare const DEFAULT_CONFIG: AppConfig;
+/**
+ * Updates the application configuration.
+ * @param {AppConfig} updates - The config values to update.
+ * @returns {AppConfig} The updated configuration.
+ */
+export declare function updateConfig(updates: AppConfig): AppConfig;
+/**
+ * @experimental This function is being tested for future use.
+ * Initializes the system with async resources.
+ * @returns {Promise<void>}
+ */
+export declare function initializeSystem(): Promise<void>;
+/**
+ * Cleans up resources before shutting down.
+ * 
+ * @deprecated Use `shutdownSystem()` instead.
+ */
+export declare function cleanup(): void;
+/**
+ * Properly shuts down the system and releases all resources.
+ * @returns {Promise<void>}
+ */
+export declare function shutdownSystem(): Promise<void>;
+export interface Logger {
+    info(msg: string): void;
+    warn(msg: string): void;
+    error(msg: string): void;
+}
+/**
+ * Creates a simple logger instance.
+ * @returns {Logger}
+ */
+export declare function createLogger(): Logger;
diff --git a/web_generator/test/integration/interop_gen/namespaces_expected.dart b/web_generator/test/integration/interop_gen/namespaces_expected.dart
index 0194efb..8227441 100644
--- a/web_generator/test/integration/interop_gen/namespaces_expected.dart
+++ b/web_generator/test/integration/interop_gen/namespaces_expected.dart
@@ -75,6 +75,9 @@
 
   external String message;
 }
+
+/// Represents the core application configuration.
+/// This interface is used across multiple services and modules.
 @_i1.JS('Core.IAppConfig')
 extension type Core_IAppConfig._(_i1.JSObject _) implements _i1.JSObject {
   external String apiEndpoint;
@@ -96,6 +99,9 @@
 
   external double userId;
 }
+
+/// A service for handling user authentication.
+/// Demonstrates using a type from another namespace (Core.LogEntry).
 @_i1.JS('Security.AuthService')
 extension type Security_AuthService._(_i1.JSObject _) implements _i1.JSObject {
   external Security_AuthService();
@@ -105,6 +111,9 @@
     String password,
   );
 }
+
+/// A generic repository pattern interface.
+/// T can be a class from another namespace, like Models.User.
 @_i1.JS('Data.IRepository')
 extension type Data_IRepository<T extends _i1.JSAny?>._(_i1.JSObject _)
     implements _i1.JSObject {
@@ -241,11 +250,21 @@
 @_i1.JS('EnterpriseApp.Utilities')
 extension type EnterpriseApp_Utilities._(_i1.JSObject _)
     implements _i1.JSObject {
+  /// Formats a number as currency.
+  /// - [amount]:  The number to format.
+  /// - [currency]:  The currency symbol.
+  ///
+  /// Returns A formatted string.
   @_i1.JS()
   external static String formatCurrency(
     num amount, [
     String? currency,
   ]);
+
+  /// Validates an email address.
+  /// - [email]:  The email string to validate.
+  ///
+  /// Returns True if the email is valid, false otherwise.
   @_i1.JS()
   external static bool isValidEmail(String email);
 }
diff --git a/web_generator/test/integration/interop_gen/project/input/a.d.ts b/web_generator/test/integration/interop_gen/project/input/a.d.ts
index 9445ac4..0e0e19b 100644
--- a/web_generator/test/integration/interop_gen/project/input/a.d.ts
+++ b/web_generator/test/integration/interop_gen/project/input/a.d.ts
@@ -1,68 +1,231 @@
-import { Vector, Vector2D, Vector3D, Point2D, Point3D, origin2D as origin, origin3D, CoordinateSystem, origin2D } from "./b"
-import { Comparator } from "./c"
+import {
+  Vector,
+  Vector2D,
+  Vector3D,
+  Point2D,
+  Point3D,
+  origin2D as origin,
+  origin3D,
+  CoordinateSystem,
+  origin2D,
+} from "./b";
+import { Comparator } from "./c";
+
+/**
+ * Represents a point in 2D space using polar coordinates.
+ * - `magnitude`: radial distance from the origin.
+ * - `angle`: angle in radians from the positive x-axis.
+ */
 interface PolarCoordinate {
-    magnitude: number;
-    angle: number;
+  magnitude: number;
+  angle: number;
 }
+
+/**
+ * Represents a point in 3D space using cylindrical coordinates.
+ * - `radius`: radial distance from the z-axis.
+ * - `angle`: angle in radians from the x-axis.
+ * - `z`: height along the z-axis.
+ */
 interface CylindricalCoordinate {
-    radius: number;
-    angle: number;
-    z: number;
+  radius: number;
+  angle: number;
+  z: number;
 }
+
+/**
+ * Represents a point in 3D space using spherical coordinates.
+ * - `magnitude`: radial distance from the origin.
+ * - `theta`: inclination angle from the z-axis.
+ * - `tau`: azimuthal angle from the x-axis in the xy-plane.
+ */
 interface SphericalCoordinate {
-    magnitude: number;
-    theta: number;
-    tau: number;
+  magnitude: number;
+  theta: number;
+  tau: number;
 }
+
+/**
+ * Represents a mathematical matrix.
+ * - `rows`: number of rows.
+ * - `columns`: number of columns.
+ * - Numeric index maps to an array of numbers (row data).
+ */
 interface Matrix {
-    [index: number]: number[];
-    rows: number;
-    columns: number;
+  [index: number]: number[];
+  rows: number;
+  columns: number;
 }
+
+/**
+ * A transformation matrix that acts as a function on 2D vectors.
+ * @template V Vector2D subtype
+ */
 interface TransformerMatrix<V extends Vector2D> extends Matrix {
-    (v: V): V;
+  /**
+   * Transforms the input vector using this matrix.
+   * @param v Input vector
+   * @returns Transformed vector
+   */
+  (v: V): V;
 }
+
+/**
+ * A 2D coordinate system with vector and point operations.
+ */
 export declare class CoordinateSystem2D implements CoordinateSystem<Point2D> {
-    constructor(origin: typeof origin2D);
-    points: Point2D[];
-    readonly origin: Point2D;
-    addPoint(point: Point2D): void;
-    addVector(vector: Vector2D, start?: Point2D): void;
-    static get xAxis(): typeof unitI2D;
-    static get yAxis(): typeof unitJ2D;
+  /**
+   * @param origin The origin point of the coordinate system.
+   */
+  constructor(origin: typeof origin2D);
+
+  /** Points registered in this coordinate system. */
+  points: Point2D[];
+
+  /** Origin of the coordinate system. */
+  readonly origin: Point2D;
+
+  /**
+   * Adds a point to the coordinate system.
+   * @param point The point to add.
+   */
+  addPoint(point: Point2D): void;
+
+  /**
+   * Adds a vector to the coordinate system from a starting point.
+   * @param vector The vector to add.
+   * @param start The start point (defaults to origin).
+   */
+  addVector(vector: Vector2D, start?: Point2D): void;
+
+  /** The unit vector along the x-axis. */
+  static get xAxis(): typeof unitI2D;
+
+  /** The unit vector along the y-axis. */
+  static get yAxis(): typeof unitJ2D;
 }
+
+/**
+ * A 3D coordinate system with vector and point operations.
+ */
 export declare class CoordinateSystem3D implements CoordinateSystem<Point3D> {
-    constructor(origin: typeof origin3D);
-    points: Point3D[];
-    readonly origin: Point3D;
-    addPoint(point: Point3D): void;
-    addVector(vector: Vector3D, start?: Point3D): void;
-    static get xAxis(): typeof unitI3D;
-    static get yAxis(): typeof unitJ3D;
-    static get zAxis(): typeof unitK3D;
+  /**
+   * @param origin The origin point of the coordinate system.
+   */
+  constructor(origin: typeof origin3D);
+
+  /** Points registered in this coordinate system. */
+  points: Point3D[];
+
+  /** Origin of the coordinate system. */
+  readonly origin: Point3D;
+
+  /**
+   * Adds a point to the coordinate system.
+   * @param point The point to add.
+   */
+  addPoint(point: Point3D): void;
+
+  /**
+   * Adds a vector to the coordinate system from a starting point.
+   * @param vector The vector to add.
+   * @param start The start point (defaults to origin).
+   */
+  addVector(vector: Vector3D, start?: Point3D): void;
+
+  /** The unit vector along the x-axis. */
+  static get xAxis(): typeof unitI3D;
+
+  /** The unit vector along the y-axis. */
+  static get yAxis(): typeof unitJ3D;
+
+  /** The unit vector along the z-axis. */
+  static get zAxis(): typeof unitK3D;
 }
+
+/**
+ * A matrix that includes vector comparison capabilities.
+ * @template V Vector2D subtype
+ */
 interface ComparatorMatrix<V extends Vector2D> extends Matrix, Comparator<V> {}
+
+/**
+ * Computes the dot product between two vectors.
+ * @param v1 First vector.
+ * @param v2 Second vector.
+ * @returns A scalar projection as a vector.
+ */
 declare function dotProduct<V extends Vector>(v1: V, v2: V): V;
+
+/**
+ * Computes the cross product of two 3D vectors.
+ * @param v1 First vector.
+ * @param v2 Second vector.
+ * @returns A new 3D vector perpendicular to both.
+ */
 declare function crossProduct(v1: Vector3D, v2: Vector3D): Vector3D;
+
+/**
+ * Maps a 2D vector to a 3D vector (z = 0).
+ * @param v Input 2D vector.
+ * @returns A 3D vector.
+ */
 declare function mapTo3D(v: Vector2D): Vector3D;
+
+/**
+ * Converts a 2D point to polar coordinates.
+ * @param point A 2D point.
+ * @returns Polar representation of the point.
+ */
 declare function toPolarCoordinate(point: Point2D): PolarCoordinate;
+
+/**
+ * Converts a 3D point to cylindrical coordinates.
+ * @param point A 3D point.
+ * @returns Cylindrical representation.
+ */
 declare function toCylindricalCoordinate(point: Point3D): CylindricalCoordinate;
+
+/**
+ * Converts a 3D point to spherical coordinates.
+ * @param point A 3D point.
+ * @returns Spherical representation.
+ */
 declare function toSphericalCoordinate(point: Point3D): SphericalCoordinate;
+
+/**
+ * Converts cylindrical coordinates to spherical coordinates.
+ * @param point Cylindrical coordinate.
+ * @returns Spherical representation.
+ */
 declare function toSphericalCoordinate(point: CylindricalCoordinate): SphericalCoordinate;
+
+/** Unit vector in 2D x-direction. */
 export declare const unitI2D: Vector2D;
+/** Unit vector in 2D y-direction. */
 export declare const unitJ2D: Vector2D;
+/** Unit vector in 3D x-direction. */
 export declare const unitI3D: Vector3D;
+/** Unit vector in 3D y-direction. */
 export declare const unitJ3D: Vector3D;
+/** Unit vector in 3D z-direction. */
 export declare const unitK3D: Vector3D;
+
 export {
-    origin, origin3D, dotProduct, crossProduct, mapTo3D,
-    TransformerMatrix, ComparatorMatrix
-}
+  origin,
+  origin3D,
+  dotProduct,
+  crossProduct,
+  mapTo3D,
+  TransformerMatrix,
+  ComparatorMatrix,
+};
+
 export {
-    PolarCoordinate as PolarPoint, 
-    CylindricalCoordinate as CylindricalPoint,
-    SphericalCoordinate as SphericalPoint,
-    toPolarCoordinate,
-    toSphericalCoordinate,
-    toCylindricalCoordinate
-}
+  PolarCoordinate as PolarPoint,
+  CylindricalCoordinate as CylindricalPoint,
+  SphericalCoordinate as SphericalPoint,
+  toPolarCoordinate,
+  toSphericalCoordinate,
+  toCylindricalCoordinate,
+};
diff --git a/web_generator/test/integration/interop_gen/project/input/c.d.ts b/web_generator/test/integration/interop_gen/project/input/c.d.ts
index 63dcada..5c63ec4 100644
--- a/web_generator/test/integration/interop_gen/project/input/c.d.ts
+++ b/web_generator/test/integration/interop_gen/project/input/c.d.ts
@@ -1,49 +1,169 @@
+/**
+ * Represents a basic logger interface with optional flush capability.
+ * @interface
+ */
 export interface ILogger {
-    readonly name: string;
-    level?: "debug" | "info" | "warn" | "error";
-    log(message: string): void;
-    error(message: string): void;
-    flush?(): Promise<void>;
+  /** Name of the logger (e.g., subsystem or module). */
+  readonly name: string;
+
+  /** Logging level. Defaults to "info" if unspecified. */
+  level?: "debug" | "info" | "warn" | "error";
+
+  /**
+   * Logs a message at the current level.
+   * @param message - The message to log.
+   */
+  log(message: string): void;
+
+  /**
+   * Logs an error message.
+   * @param message - The error message.
+   */
+  error(message: string): void;
+
+  /**
+   * Flushes any buffered logs.
+   * @returns A promise that resolves when flushing is complete.
+   */
+  flush?(): Promise<void>;
 }
+
+/**
+ * A key-value map of strings.
+ * @interface
+ */
 export interface Dictionary {
-    [key: string]: string;
+  [key: string]: string;
 }
+
+/**
+ * A function that compares two items and returns a number:
+ * - `< 0` if `a < b`
+ * - `0` if `a === b`
+ * - `> 0` if `a > b`
+ * @typeParam T - The type to compare.
+ */
 export interface Comparator<T> {
-    (a: T, b: T): number;
+  (a: T, b: T): number;
 }
+
+/**
+ * A simple repository abstraction.
+ * @typeParam T - The type of the entity.
+ */
 export interface Repository<T> {
-    findById(id: string): T;
-    save(entity: T): void;
+  /**
+   * Finds an entity by its ID.
+   * @param id - The unique identifier.
+   */
+  findById(id: string): T;
+
+  /**
+   * Saves an entity.
+   * @param entity - The entity to persist.
+   */
+  save(entity: T): void;
 }
+
+/**
+ * A constructor that accepts an array of string arguments.
+ * @deprecated Prefer factory functions or specific constructors.
+ */
 export interface RepoConstructor {
-    new (args: string[]): any;
+  new (args: string[]): any;
 }
+
+/**
+ * Describes a service with asynchronous operations.
+ * @experimental This API is under evaluation and may change.
+ */
 export interface AsyncService {
-    fetchData(url: string): Promise<any>;
-    updateData(id: string, payload: string): Promise<boolean>;
+  /**
+   * Fetches remote data from a URL.
+   * @param url - The resource endpoint.
+   */
+  fetchData(url: string): Promise<any>;
+
+  /**
+   * Updates data on the server.
+   * @param id - The resource ID.
+   * @param payload - The update content.
+   * @returns `true` if update succeeded, otherwise `false`.
+   */
+  updateData(id: string, payload: string): Promise<boolean>;
 }
+
+/**
+ * Represents a basic user.
+ */
 export interface User {
-    id: string;
-    email: string;
-    describe?(): string;
+  /** Unique identifier. */
+  id: string;
+
+  /** User's email address. */
+  email: string;
+
+  /**
+   * Returns a human-readable description of the user.
+   */
+  describe?(): string;
 }
+
+/**
+ * An administrator user with logging capabilities.
+ */
 export interface Admin extends User, ILogger {
-    role: string;
-    grantPermission(permission: string): void;
+  /** Admin role label. */
+  role: string;
+
+  /**
+   * Grants the given permission.
+   * @param permission - A named permission string.
+   */
+  grantPermission(permission: string): void;
 }
+
+/**
+ * Configuration environment.
+ */
 export interface Config {
-    env: string;
+  /** Environment name (e.g., 'production', 'dev'). */
+  env: string;
+
+  /** Whether debug mode is enabled. */
+  debug: boolean;
 }
-export interface Config {
-    debug: boolean;
-}
+
+/**
+ * Represents a resource that requires authentication.
+ */
 export interface SecureResource {
-    accessToken: string;
-    authenticate(): boolean;
+  /** A token used for authentication. */
+  accessToken: string;
+
+  /** Authenticates the resource. */
+  authenticate(): boolean;
 }
+
+/**
+ * A basic self-referencing linked list node.
+ */
 interface LinkedList {
-    next(): this;
+  /** Returns the next node in the list. */
+  next(): this;
 }
+
+/**
+ * A global dictionary instance.
+ */
 export declare const dict: Dictionary;
+
+/**
+ * Root node of a linked list.
+ */
 export declare const rootList: LinkedList;
+
+/**
+ * A numeric comparator for sorting numbers.
+ */
 export declare const compareNumbers: Comparator<number>;
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 91ffd51..914d7dc 100644
--- a/web_generator/test/integration/interop_gen/project/output/a.dart
+++ b/web_generator/test/integration/interop_gen/project/output/a.dart
@@ -12,30 +12,62 @@
 external _i2.Point2D get origin;
 @_i1.JS()
 external _i2.Point3D get origin3D;
+
+/// Computes the dot product between two vectors.
+/// - [v1]:  First vector.
+/// - [v2]:  Second vector.
+///
+/// Returns A scalar projection as a vector.
 @_i1.JS()
 external V dotProduct<V extends _i2.Vector>(
   V v1,
   V 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()
 external _i2.Vector3D crossProduct(
   _i2.Vector3D v1,
   _i2.Vector3D v2,
 );
+
+/// Maps a 2D vector to a 3D vector (z = 0).
+/// - [v]:  Input 2D vector.
+///
+/// Returns A 3D vector.
 @_i1.JS()
 external _i2.Vector3D mapTo3D(_i2.Vector2D v);
+
+/// A transformation matrix that acts as a function on 2D vectors.
+/// Type Name [V]:  Vector2D subtype
 extension type TransformerMatrix<V extends _i2.Vector2D>._(_i1.JSObject _)
     implements Matrix {
   external V call(V v);
 }
+
+/// A matrix that includes vector comparison capabilities.
+/// Type Name [V]:  Vector2D subtype
 extension type ComparatorMatrix<V extends _i2.Vector2D>._(_i1.JSObject _)
     implements Matrix, _i3.Comparator<V> {}
+
+/// Represents a point in 2D space using polar coordinates.
+/// - `magnitude`: radial distance from the origin.
+/// - `angle`: angle in radians from the positive x-axis.
 @_i1.JS('PolarPoint')
 extension type PolarCoordinate._(_i1.JSObject _) implements _i1.JSObject {
   external double magnitude;
 
   external double angle;
 }
+
+/// Represents a point in 3D space using cylindrical coordinates.
+/// - `radius`: radial distance from the z-axis.
+/// - `angle`: angle in radians from the x-axis.
+/// - `z`: height along the z-axis.
 @_i1.JS('CylindricalPoint')
 extension type CylindricalCoordinate._(_i1.JSObject _) implements _i1.JSObject {
   external double radius;
@@ -44,6 +76,11 @@
 
   external double z;
 }
+
+/// Represents a point in 3D space using spherical coordinates.
+/// - `magnitude`: radial distance from the origin.
+/// - `theta`: inclination angle from the z-axis.
+/// - `tau`: azimuthal angle from the x-axis in the xy-plane.
 @_i1.JS('SphericalPoint')
 extension type SphericalCoordinate._(_i1.JSObject _) implements _i1.JSObject {
   external double magnitude;
@@ -52,60 +89,135 @@
 
   external double tau;
 }
+
+/// Converts a 2D point to polar coordinates.
+/// - [point]:  A 2D point.
+///
+/// Returns Polar representation of the point.
 @_i1.JS()
 external PolarCoordinate toPolarCoordinate(_i2.Point2D 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()
 external SphericalCoordinate toSphericalCoordinate(_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$1(
     CylindricalCoordinate point);
+
+/// Converts a 3D point to cylindrical coordinates.
+/// - [point]:  A 3D point.
+///
+/// Returns Cylindrical representation.
 @_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;
+
+/// A 2D coordinate system with vector and point operations.
 extension type CoordinateSystem2D._(_i1.JSObject _)
     implements _i2.CoordinateSystem<_i2.Point2D> {
   external CoordinateSystem2D(_i2.Point2D origin);
 
+  /// Points registered in this coordinate system.
   external _i1.JSArray<_i2.Point2D> points;
 
+  /// Origin of the coordinate system.
   @_i4.redeclare
   external _i2.Point2D get origin;
+
+  /// Adds a point to the coordinate system.
+  /// - [point]:  The point to add.
   @_i4.redeclare
   external void addPoint(_i2.Point2D point);
+
+  /// Adds a vector to the coordinate system from a starting point.
+  /// - [vector]:  The vector to add.
+  /// - [start]:  The start point (defaults to origin).
   external void addVector(
     _i2.Vector2D vector, [
     _i2.Point2D? start,
   ]);
+
+  /// The unit vector along the x-axis.
   external _i2.Vector2D get xAxis;
+
+  /// The unit vector along the y-axis.
   external _i2.Vector2D get yAxis;
 }
+
+/// 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;
+
+/// A 3D coordinate system with vector and point operations.
 extension type CoordinateSystem3D._(_i1.JSObject _)
     implements _i2.CoordinateSystem<_i2.Point3D> {
   external CoordinateSystem3D(_i2.Point3D origin);
 
+  /// Points registered in this coordinate system.
   external _i1.JSArray<_i2.Point3D> points;
 
+  /// Origin of the coordinate system.
   @_i4.redeclare
   external _i2.Point3D get origin;
+
+  /// Adds a point to the coordinate system.
+  /// - [point]:  The point to add.
   @_i4.redeclare
   external void addPoint(_i2.Point3D point);
+
+  /// Adds a vector to the coordinate system from a starting point.
+  /// - [vector]:  The vector to add.
+  /// - [start]:  The start point (defaults to origin).
   external void addVector(
     _i2.Vector3D vector, [
     _i2.Point3D? start,
   ]);
+
+  /// The unit vector along the x-axis.
   external _i2.Vector3D get xAxis;
+
+  /// The unit vector along the y-axis.
   external _i2.Vector3D get yAxis;
+
+  /// The unit vector along the z-axis.
   external _i2.Vector3D get zAxis;
 }
+
+/// Represents a mathematical matrix.
+/// - `rows`: number of rows.
+/// - `columns`: number of columns.
+/// - Numeric index maps to an array of numbers (row data).
 extension type Matrix._(_i1.JSObject _) implements _i1.JSObject {
   external double rows;
 
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 46e3a01..fee72b9 100644
--- a/web_generator/test/integration/interop_gen/project/output/b.dart
+++ b/web_generator/test/integration/interop_gen/project/output/b.dart
@@ -268,6 +268,8 @@
   @_i1.JS('area')
   external String area$1(AnonymousUnion unit);
   external static EpahsImpl getById(String id);
+
+  /// Returns a string representation of an object.
   @_i1.JS('toString')
   external String toString$();
 }
diff --git a/web_generator/test/integration/interop_gen/project/output/c.dart b/web_generator/test/integration/interop_gen/project/output/c.dart
index 5179b4b..ba70d51 100644
--- a/web_generator/test/integration/interop_gen/project/output/c.dart
+++ b/web_generator/test/integration/interop_gen/project/output/c.dart
@@ -3,6 +3,12 @@
 // ignore_for_file: no_leading_underscores_for_library_prefixes
 import 'dart:js_interop' as _i1;
 
+import 'package:meta/meta.dart' as _i2;
+
+/// A function that compares two items and returns a number:
+/// - `< 0` if `a < b`
+/// - `0` if `a === b`
+/// - `> 0` if `a > b`
 extension type Comparator<T extends _i1.JSAny?>._(_i1.JSObject _)
     implements _i1.JSObject {
   external double call(
@@ -10,60 +16,120 @@
     T b,
   );
 }
+
+/// Represents a basic logger interface with optional flush capability.
 extension type ILogger._(_i1.JSObject _) implements _i1.JSObject {
+  /// Logging level. Defaults to "info" if unspecified.
   external AnonymousUnion? level;
 
+  /// Name of the logger (e.g., subsystem or module).
   external String get name;
+
+  /// Logs a message at the current level.
+  /// - [message]:  - The message to log.
   external void log(String message);
+
+  /// Logs an error message.
+  /// - [message]:  - The error message.
   external void error(String message);
+
+  /// Flushes any buffered logs.
+  ///
+  /// Returns A promise that resolves when flushing is complete.
   external _i1.JSFunction? get flush;
 }
+
+/// A key-value map of strings.
 extension type Dictionary._(_i1.JSObject _) implements _i1.JSObject {
   external String operator [](String key);
 }
+
+/// A simple repository abstraction.
 extension type Repository<T extends _i1.JSAny?>._(_i1.JSObject _)
     implements _i1.JSObject {
+  /// Finds an entity by its ID.
+  /// - [id]:  - The unique identifier.
   external T findById(String id);
+
+  /// Saves an entity.
+  /// - [entity]:  - The entity to persist.
   external void save(T entity);
 }
+
+/// A constructor that accepts an array of string arguments.
+@Deprecated('Prefer factory functions or specific constructors.')
 extension type RepoConstructor._(_i1.JSObject _) implements _i1.JSObject {
   external RepoConstructor(_i1.JSArray<_i1.JSString> args);
 }
+
+/// Describes a service with asynchronous operations.
+/// **EXPERIMENTAL**: This API is under evaluation and may change.
+@_i2.experimental
 extension type AsyncService._(_i1.JSObject _) implements _i1.JSObject {
+  /// Fetches remote data from a URL.
+  /// - [url]:  - The resource endpoint.
   external _i1.JSPromise<_i1.JSAny?> fetchData(String url);
+
+  /// Updates data on the server.
+  /// - [id]:  - The resource ID.
+  /// - [payload]:  - The update content.
+  ///
+  /// Returns `true` if update succeeded, otherwise `false`.
   external _i1.JSPromise<_i1.JSBoolean> updateData(
     String id,
     String payload,
   );
 }
+
+/// Represents a basic user.
 extension type User._(_i1.JSObject _) implements _i1.JSObject {
+  /// Unique identifier.
   external String id;
 
+  /// User's email address.
   external String email;
 
+  /// Returns a human-readable description of the user.
   external _i1.JSFunction? get describe;
 }
+
+/// An administrator user with logging capabilities.
 extension type Admin._(_i1.JSObject _) implements User, ILogger {
+  /// Admin role label.
   external String role;
 
+  /// Grants the given permission.
+  /// - [permission]:  - A named permission string.
   external void grantPermission(String permission);
 }
+
+/// Configuration environment.
 extension type Config._(_i1.JSObject _) implements _i1.JSObject {
+  /// Environment name (e.g., 'production', 'dev').
   external String env;
-}
-@_i1.JS('Config')
-extension type Config$1._(_i1.JSObject _) implements _i1.JSObject {
+
+  /// Whether debug mode is enabled.
   external bool debug;
 }
+
+/// Represents a resource that requires authentication.
 extension type SecureResource._(_i1.JSObject _) implements _i1.JSObject {
+  /// A token used for authentication.
   external String accessToken;
 
+  /// Authenticates the resource.
   external bool authenticate();
 }
+
+/// A global dictionary instance.
 @_i1.JS()
 external Dictionary get dict;
+
+/// Root node of a linked list.
 @_i1.JS()
 external LinkedList get rootList;
+
+/// A numeric comparator for sorting numbers.
 @_i1.JS()
 external Comparator<_i1.JSNumber> get compareNumbers;
 extension type const AnonymousUnion._(String _) {
@@ -75,6 +141,9 @@
 
   static const AnonymousUnion error = AnonymousUnion._('error');
 }
+
+/// A basic self-referencing linked list node.
 extension type LinkedList._(_i1.JSObject _) implements _i1.JSObject {
+  /// Returns the next node in the list.
   external LinkedList next();
 }