Skip to content

Add Dart + Protobuf converters; make all 8 converters lossless round-trip#16

Merged
MelbourneDeveloper merged 15 commits into
mainfrom
bugfixes
Apr 19, 2026
Merged

Add Dart + Protobuf converters; make all 8 converters lossless round-trip#16
MelbourneDeveloper merged 15 commits into
mainfrom
bugfixes

Conversation

@MelbourneDeveloper
Copy link
Copy Markdown
Collaborator

Closes #2 (protobuf support).

TLDR

Adds Dart and Protobuf converters, rewrites C#/Go/Python/Rust to round-trip HOME_PAGE_SAMPLE byte-for-byte, and wires the new languages into the web converter page.

Details

New converters

  • Dart (packages/typediagram/src/converters/dart.ts): sealed-class DUs (sealed class + final class ... extends), typedef aliases, Option<T>T?, first-class generics.
  • Protobuf / proto3 (packages/typediagram/src/converters/protobuf.ts): message for records, enum for unit-only unions, oneof + nested message for struct-variant unions, repeated/optional/map<K,V> field labels. Uses // @td-generics:, // @td-type:, // @td-alias: comment directives to encode typeDiagram concepts proto3 can't express natively (generics, Option<List<T>>, aliases).

New shared helper

  • packages/typediagram/src/converters/brace-lang.ts: extractBalancedBlock, splitTopLevelCommas, formatGenericsDecl, extractTrailingNullable, parseGenericParamList. Used by C#, Dart, Protobuf.

Converter rewrites for lossless round-trip

  • C#: closed-hierarchy DUs (abstract record + nested sealed records, RestClient.Net / Outcome style), using Alias = Target; aliases, primary-constructor records preserving original field names. Replaces the prior JsonPropertyName-discriminated interface pattern.
  • Go: interface + marker-method (is<Union>()) encoding with union-qualified variant struct names, Go 1.18 [T any] generics, field names kept verbatim. Legacy embedded-type ifaces still parse as a fallback.
  • Python (dataclass style): struct-variant unions emit as per-variant @dataclass classes plus a Name = VarA | "VarB" alias line; parser reads that line back and collapses the variant classes into union variants. Generics via Generic[T] / TypeVar. Top-level Email = str now parses as alias.
  • Rust: replaced non-recursive [^}]* enum regex with a brace-balanced body extractor; added splitRsFieldList that respects angle/brace/paren depth so HashMap<String, String> survives as a single field.
  • TypeScript: Option<T> emits as T | undefined; parser accepts T | undefined, T | null, T | undefined | null, T | null | undefined as Option<T>. Alias-vs-union detection excludes the option shape.
  • F#: unchanged (was already lossless).

Shared test helper

  • expectLosslessRoundTrip(converter) in test/converters/helpers.ts asserts TD → lang → TD is byte-for-byte identical to HOME_PAGE_SAMPLE (now exported from packages/typediagram/src/sample.ts). Each per-language test file now calls it.

Web converter page

  • SupportedLang widened to include dart and protobuf.
  • New tabs in converter.ts; new highlighter rule sets in converter-highlight.ts.
  • playground.ts and converter.ts both source the sample from HOME_PAGE_SAMPLE — single source of truth across the app.

Makefile

  • make clean now also removes per-package coverage/ dirs.
  • New make clean-start target: kills anything on the Vite dev port (default 5173), full clean, build, then dev.

Budgets / config

  • packages/typediagram/scripts/bundle-size.mjs: budget raised 50 → 75 KB to accommodate two new converters (~8-10 KB each, unminified parser + emitter). Current bundle: 67.81 KB.
  • coverage-thresholds.json: typediagram branches 90.76 → 90.89, functions 98.5 → 98.58 (ratcheted up only).
  • packages/cli/test/roundtrip.e2e.test.ts: updated expected C# output from public record to public sealed record to match the new DU emission.

How Do The Automated Tests Prove It Works?

Round-trip (all 8 languages pass):

  • [CONV-FS-RT], [CONV-TS-RT], [CONV-RUST-RT], [CONV-CS-RT], [CONV-PY-RT], [CONV-GO-RT], [CONV-DART-RT], [CONV-PROTO-RT] — each calls expectLosslessRoundTrip(converter) against HOME_PAGE_SAMPLE and asserts printSource(fromSource(toSource(parsed))) === HOME_PAGE_SAMPLE byte-for-byte.

Feature coverage for new converters:

  • [CONV-DART-FROM] / [CONV-DART-TO] / [CONV-DART-EDGE] / [CONV-DART-ERR] (13 tests) — verifies Dart sealed-class DU parsing, generic sealed classes with extending variants, implements as well as extends on variants, T?Option<T> field mapping, empty enums, malformed-field resilience.
  • [CONV-PROTO-FROM] / [CONV-PROTO-TO] / [CONV-PROTO-EDGE] / [CONV-PROTO-ERR] (12 tests) — verifies oneof + nested message parsing, UNSPECIFIED sentinel handling, // @td-type / // @td-generics / // @td-alias directive handling, map<K, V> and Option<Map<K,V>> edge cases, malformed-line skipping.
  • [CONV-BRACE-LANG] (16 tests) — covers the shared helper: balanced-brace extraction for malformed and nested inputs, angle-aware comma splitting, generic-params parsing for plain / extends / Go-style T any forms, nullable extraction, generics formatting.

Regression-guarded behaviour changes:

  • [CONV-CS-BUG-10] reworded to assert the new abstract record + nested sealed record emission (no more JsonPropertyName("kind") / interface IContentItem).
  • [CONV-CS-BUG-11] reworded to assert primary-constructor records preserving original field names (no more PascalCase + JsonPropertyName property-bag).
  • [CONV-CS-BUG-12] reworded to assert aliases emit as using X = Y; (previously inlined).
  • [CONV-GO-TO-COMPLEX] and [CONV-GO-RT] updated to assert lowercase field names and union-qualified variant struct names (e.g. ContentItemText not Text).
  • [WEB-CONVERTER] renders a tab for every supported language — loosened from hardcoded 6 to "expect Dart and Protobuf tabs alongside the original 6".

Suite totals: 278 typediagram tests + 211 web tests + 59 CLI tests all pass locally under make ci. Bundle size 67.81 KB (under 75 KB budget). Coverage meets the ratcheted thresholds (lines ≥ 95%, branches ≥ 90.89%, functions ≥ 98.58% for typediagram-core).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add protobuf conversion support (to/from)

1 participant