Skip to content

fix: support outputFromObservable() from @angular/core/rxjs-interop#257

Merged
Brooooooklyn merged 4 commits intovoidzero-dev:mainfrom
ashley-hunter:output-from-observable
May 2, 2026
Merged

fix: support outputFromObservable() from @angular/core/rxjs-interop#257
Brooooooklyn merged 4 commits intovoidzero-dev:mainfrom
ashley-hunter:output-from-observable

Conversation

@ashley-hunter
Copy link
Copy Markdown
Contributor

@ashley-hunter ashley-hunter commented May 1, 2026

Problem

Properties declared with `outputFromObservable()` from `@angular/core/rxjs-interop` were silently dropped from the compiled output metadata. The Angular runtime uses the `outputs: {}` object in `ɵɵdefineComponent`/`ɵɵdefineDirective` to wire up event bindings — missing entries mean parent components can never bind to those outputs.

Example that was broken:

readonly queryChanged = outputFromObservable(
  this.dataService.value$.pipe(
    skip(1),
    debounceTime(300),
  ),
);

Root cause

`try_parse_signal_output` in `property_decorators.rs` only matched the identifier `"output"`. `outputFromObservable` was never recognised, so its properties returned `None` and were excluded from the outputs metadata.

Fix

Extend `try_parse_signal_output` to detect both `output()` and `outputFromObservable()` (including namespaced forms like `core.outputFromObservable()`). The key behavioural difference is argument position: `output()` takes options at index 0, while `outputFromObservable(observable, options?)` takes them at index 1. The observable expression itself is irrelevant for metadata extraction and is ignored.

Tests

5 new unit tests added via TDD (red → green):

  • Simple arg — `outputFromObservable(new EventEmitter())`
  • Property reference — `outputFromObservable(this.service.obs$)`
  • Piped observable — `outputFromObservable(this.dataService.value$.pipe(skip(1), debounceTime(300)))`
  • Alias — `outputFromObservable(new EventEmitter(), { alias: 'clicked' })`
  • Mixed — class using both `output()` and `outputFromObservable()` together

Each test uses normalized whitespace assertions to verify the exact `outputs:{}` structure inside `ɵɵdefineComponent`, plus insta snapshots locking the full compiled output.

Also adds an e2e compare fixture (`inputs-outputs/output-from-observable`) covering all four scenarios, verified against the official Angular compiler (100% pass).

Coverage

All 8 Angular initializer API functions are now handled by the Rust compiler. `outputFromObservable` was the only missing one.

ashley-hunter and others added 4 commits May 1, 2026 14:37
outputFromObservable() was not recognised as an output initializer,
so properties declared with it were silently dropped from the outputs
metadata in the compiled ɵɵdefineComponent/ɵɵdefineDirective call.

Extend try_parse_signal_output to detect both output() and
outputFromObservable(). The key difference: output() takes options as
its first argument while outputFromObservable(observable, options?)
takes them as its second — the observable expression itself is
irrelevant for metadata extraction.

Adds 5 unit tests covering: simple EventEmitter arg, direct property
reference, piped observable chain (the reported real-world case),
alias via second arg, and mixed usage with output(). Also adds an e2e
compare fixture so the output can be verified against the official
Angular compiler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s and snapshots

Replace loose contains() checks with normalized whitespace assertions that
verify the exact outputs:{} object inside ɵɵdefineComponent, plus insta
snapshots that lock the full compiled output. Also add a negative assertion
for the alias test confirming the class property name is not used as the
binding name when an alias is set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Brooooooklyn Brooooooklyn merged commit 807a3df into voidzero-dev:main May 2, 2026
9 checks passed
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.

2 participants