Commit Graph

2837 Commits

Author SHA1 Message Date
Paul Gschwendtner abdc7e4578 feat(compiler-cli): support type-checking for generic signal inputs (#53521)
This commit adds the last remaining piece for signal input
type-checking. Bound values to signal inputs are already checked
properly at this point, but inference of generic directive/component
types through their inputs is not implemented.

This commit fixes this. To achieve this, there are a couple of potential
solutions. The generics of a directive are inferred based on input
value expressions using a so-called type constructor. The constructor
looks something like this:

```
const _ctor = <T>(v: Pick<Dir<T>, 'input1', 'input2'>) => Dir<T>;

_ctor({input1: expr1, input2: expr2});
```

This works very well for non-signal inputs where the class member is
directly holding the input values. For signal inputs, this does NOT
work because the class member will actually hold the `InputSignal`
instance. There are a couple of solutions to this:

1. Calling `_ctor` with an `InputSignal<typeof value>`
2. Converting the `_ctor` input signal fields to their write types
   (unwrapping the input signals).

We've decided to go with the second option as TypeScript is very
sensitive with assignments and its checks. i.e. co-variance,
contravariance or bivariance. Semantically it makes more sense to unwrap
the input signal "write type" directly and "assign to it". This is safer
and conceptually also easier to follow. A type constructor continues to
only receive the "expresison values". This simplifies code as well.

It's worth noting that the unwrapping as per option 2 also comes at a
cost. We need to be able to generate imports in type constructors. This
was not possible until the previous commit because inline type constructors
did not have an associated type-check block `Environment` and we were
missing access to expression translation and correct import generation.

Overall, solution 2 is now implemented as works as expected. This commit
adds additional unit tests to ensure this.

PR Close #53521
2023-12-13 15:44:00 -08:00
Paul Gschwendtner 5f7db018eb refactor(compiler-cli): finalize transform support for signal input transforms (#53521)
Signal inputs do not need coercion members for their transforms. That is
because the `InputSignal` type- which is accessible in the class member-
already holds the type of potential "write values". This eliminates the
need for coercion members which were simply used to somehow capture this
write type (especially when libraries are consumed and only `.d.ts` is
available).

We can simplify this, and also significantlky loosen restrictions
of transform functions- given that we can fully rely on TypeScript for
inferring the type. There is no requirement in being able to
"transplant" the type into different places- hence also allowing
supporting transform functions with generics, or overloads.

In a follow-up commit, once more parts are place, there will be some
compliance tests to ensure these new "loosend restrictions".

PR Close #53521
2023-12-13 15:44:00 -08:00
Paul Gschwendtner 8908fcd809 refactor(compiler-cli): initial type-checking for signal inputs (#53521)
This commit introduces the initial type-checking for signal inputs.
To enable type-checking od signal inputs, there are a couple of tricks
needed. It's not trivial as it would look like at first glance.

Initial attempts could have been to generate additional statements in
type-checking blocks for signal inputs to simply call a method like
`InputSignal#applyNewValue`. This would seem natural, as it would match
what will happen at runtime, but this would break the language-service
auto completion in a highly subtle way. Consider the case where multiple
directives match the same input. Consider the directives have some
overlap in accepted input values, but they also have distinct diverging
values, like:

```ts
class DirA {
  value = input<'apple'|'shared'>();
}

class DirB {
  value = input<'orange'|'shared'>();
}
```

In such cases, auto completion for the binding expression should suggest
the following values: `apple`, `shared`, `orange` and `undefined`.

The language service achieves this by getting completions in the
type-check block where the user expression would live. This BREAKS if
we'd have multiple places where the expression from the user is used.

Two different places, or more, surface additional problems with
diagnostic collection. Previously diagnostics would surface the union
type of allowed values, but with multiple places, we'd have to work with
potentially 1+ diagnostics. This is non-ideal.

Another important consideration is test coverage. It might sound
problematic to consider the existing test infrastructure as relevant,
but in practice, we have thousands of diagnostic type check block tests
that would greatly benefit if the general emit structure would still
match conceptually. This is another bonus argument on why changing the
way inputs are applied is probably an option we should consider as a
last resort.

Ultimately, there is a good solution where we unwrap directive signal
inputs, based on metadata, and access a brand type field on the
`InputSignal`. This ensures auto-completion continues to work as is, and
also the structure of type check blocks doesn't change conceptually. In
future commits we also need to handle type-inference for generic signal
inputs.

Note: Another alternative considered, in terms of using metadata or not.
We could have type helpers to unwrap signal inputs using type helpers
like: `T extends InputSignal<any, WriteT> ? WriteT : T`. This would
allow us to drop the input signal metadata dependency, but in reality,
this has a few issues:

- users might have `@Input`'s passing around `InputSignal`'s. This is
  unlikely, but shows that the solution would not be fully correct.
- we need the metadata regardless, as we plan on accessing it at runtime
  as well, to distinguish between signal inputs and normal inputs when
  applying new values. This was not clear when this option was
  considered initially.

PR Close #53521
2023-12-13 15:44:00 -08:00
Paul Gschwendtner 3e0e0b42fa refactor(compiler): emit signal input info in d.ts and generate partial compilation output (#53521)
This commit captures the metadata on whether an input is signal based or
not, in the `.d.ts` of directives and components. This exposes this
information to consumers of the directives. This is needed because
libraries may use signal inputs, and we need to know whether bound
inputs to this library are signal-based or not- so that we can generate
proper type-checking code (account for `InputSignal` or not).

Additionally, this commit introduces a new structure for the partial
compilation output of directive inputs. With the current emit, inputs
are captured in a data structure that is equivalent to the internal data
structure passed to `defineDirective` (the full compilation output).
This worked fine as we only captured a few strings, but in ends up
being a bad practice because partial compilation output should NOT
capture internal data structures that might be specific to a certian
Angular core version. Instead, we introduce a new "future proof"
structure that:

- can hold additional metadata in backwards-compatible ways, like
  `isSignal` or `isRequired`.
- can be parsed trivially using the `AstHost` for the linker, instead of
  having to unwrap/parse an array structure.

The new structure is only emitted when we discover that some inputs are
signal based (or ultimately end up configuring input flags). This is
done for backwards compatibility, so that libraries without signal
inputs remain compatible with older linker versions. In the future,
this might be the only emit.

Compliance tests for this follow in future commits, when the linker
portion is also in place. This commit specialices on the code
generation. With the linker, and compliance test infrastructure fixed
(that is broken right now), we can test the full integration.

PR Close #53521
2023-12-13 15:44:00 -08:00
Paul Gschwendtner d0ff9b2ceb refactor(core): clean-up duplicate interface for input metadata in facade (#53521)
When working on integrating a new metadata field for inputs, I realized
there are quite a lot of duplications of interfaces. Turns out, the
facade input map type can be replaced in favor of just
`R3DirectiveInput`- even improving type safety-ness of e.g. the wrapped
node expressions of transform functions.

PR Close #53521
2023-12-13 15:44:00 -08:00
Paul Gschwendtner bbd918acd3 refactor(core): introduce signal input() function and compiler detection (#53521)
This commit introduces a function for declaring inputs in
components. The function is called `input`. It comes in two flavors:

- `input` for optional inputs with initial values
- `input.required` for required inputs

Inputs are declared as class members, like with `@Input`- except that
the class field will no longer hold the input value directly. Angular
takes control over the input field and exposes the input value as a
signal. The runtime implementation will follow in future commits.

This commit simply introduces:

- initial compiler detection to recognize such inputs in classes
- the initial signature of `input` and `input.required`.

Note: the defer size test is flawed and there is no minification- hence
this commit also needs to incorporate the new dependency graph changes.

PR Close #53521
2023-12-13 15:44:00 -08:00
Andrew Scott 629343f247 fix(core): Multiple subscribers to ApplicationRef.isStable should all see values (#53541)
The behavior of `ApplicationRef.isStable` changed in 16.1 due to
https://github.com/angular/angular/commit/28c68f709cdc930e12bac51a266e7bf790656034.
This change added a `share` to the `isStable` observable, which prevents
additional subscribers from getting a value until a new one emits. One
solution to the problem would be `shareReplay(1)`. However, that would
increase the bundle size since we do not use `shareReplay` elsewhere.
Instead, we don't even really need to share the observable.

The `Observable` available in `ApplicationRef.isStable` before the above commit
was the zone stable observable, without a `share`. The new behavior adds
only an additional observable to the stream, `hasPendingTasks` (a `BehaviorSubject`).
The observables in this stream are not expensive to subscribe to. The
only one with side effects is the `isStable` (because it subscribes to
onStable), but that one already has the `share` operator on it.
Omitting the `share` in `ApplicationRef` also means that applications on `zoneless` will not
have to pay the cost of the operator when we make zones optional because
the zone stable observable is the only place we use it.

PR Close #53541
2023-12-13 11:26:51 -08:00
Kara Erickson f35adcb9b2 fix(core): update feature usage marker (#53542)
This commit updates the name of the 'performance.mark'
counter used to track feature usage. It now matches
the name agreed upon by W3C for this use case:
https://github.com/w3c/user-timing/pull/108

PR Close #53542
2023-12-13 09:24:09 -08:00
cexbrayat 160363a626 refactor(core): remove no longer needed FlushableEffectRunner private export (#53525)
It is no longer needed after https://github.com/angular/angular/commit/70a442eb023096b329ea2c0c7010df5339993fe7

PR Close #53525
2023-12-13 09:22:42 -08:00
Pawel Kozlowski 1fc5442947 perf(core): avoid changes Observable creation on QueryList (#53498)
The changes Observable (impl: EventEmitter) on the QueryList is initalized
lazy - it is created only if someone calls a geter to get a hand on its
instance. But the destroy method was calling this getter thus creating
a new Observable even if no one subscribed to it.

This commit changes the destroy logic to skip creation of an EventEmitter
if it wasn't initialized.

PR Close #53498
2023-12-12 09:00:08 -08:00
Pawel Kozlowski e3a6bf9b6c perf(core): optimize memory allocation when reconcilling lists (#52245)
This change replaces the implementation of the multi-map used to store
detached views while reconciling lists. The new implementation optimizes
memory allocation for such map and avoid arrays allocation when there are
no duplicated keys.

PR Close #52245
2023-12-08 15:00:13 -08:00
Andrew Scott 51ed3b510d refactor(core): Separate application and platform files even more (#53371)
This separates application and platform code into even more files. This now removes
the ciruclar dependency between scheduling and application ref.

PR Close #53371
2023-12-08 14:29:46 -08:00
Andrew Scott 0663708a75 refactor(core): Move code out of application_ref (#53371)
The application_ref is somewhat of a dumping ground for code. This commit
moves things around into more appropriately separate files.

PR Close #53371
2023-12-08 14:29:46 -08:00
Andrew Scott 2565121851 fix(core): Avoid refreshing a host view twice when using transplanted views (#53021)
This change fixes and issue where the expectation was that change
detection always goes through `detectChangesInView`. In reality,
`detectChangesInternal` directly calls `refreshView`
and refreshes a view directly without checking if it was dirty (to my discontent).

This update changes the implementation of `detectChangesInternal` to
actually be "detect changes" not "force refresh of root view and detect
changes". In addition, it adds the refresh flag to APIs that were
previously calling `detectChangesInternal` so we get the same behavior
as before (host view is forced to refresh).

Note that the use of `RefreshView` instead of `Dirty` is _intentional_
here. The `RefreshView` flag is cleared before refreshing the view while
the `Dirty` flag is cleared at the very end. Using the `Dirty` flag
could have consequences because it is a more long-lasting change to the
view flags. Because `detectChangesInView` will immediately clear the
`RefreshView` flag, this change is much more limited and does not
result in a different set of flags during the view refresh.

PR Close #53021
2023-12-07 12:05:23 -08:00
Alex Rickabaugh 580af8e5f8 Revert "refactor(core): output an error guide link in prod mode (#53324)" (#53392)
This reverts commit f245aba782.

Reason: breaks g3

PR Close #53392
2023-12-06 07:23:23 -08:00
Andrew Kushnir 596b43d631 refactor(core): skip disconnected nodes while computing node path for hydration (#53317)
In certain cases Angular hydration logic can not rely on the order in which elements are present in a template (for example, in content-projection use-cases) and there is a need to serialize a path from one node to another, so that hydration can locate an element on a page. The logic attempts to use an immediate parent element as an anchor and compute the path from it. If it fails - the path is computed starting from the <body> (this is a fallback).

This commit updates the logic to walk up the parents tree if an immediate parent (from a template) is disconnected from the DOM. This helps to shorten the lookup path and make it more stable.

PR Close #53317
2023-12-04 21:44:08 -08:00
Andrew Kushnir f245aba782 refactor(core): output an error guide link in prod mode (#53324)
Currently, the link to an error guide is only included into an error message in dev mode. This change makes the `Find more at https://angular.io/errors/NG0XYZ` appear in the error message even in prod mode. Note: the rest of the error message is still tree-shaken away in prod mode (as it happens today).

PR Close #53324
2023-12-04 21:42:28 -08:00
JoostK 77939a3bd3 fix(core): cleanup signal consumers for all views (#53351)
This commit fixes a memory leak where signal consumers would not be cleaned up for
descendant views when a view is destroyed, because the cleanup logic was only invoked
for the view that is itself being destroyed.

PR Close #53351
2023-12-04 21:41:12 -08:00
Andrew Kushnir 82609d471c fix(core): support swapping hydrated views in @for loops (#53274)
This commit fixes an issue where swapping hydrated views was not possible in the new control flow repeater. The problem was caused by the fact that an internal representation of a view had no indication that hydration is completed and further detaching/attaching should work in a regular (non-hydration) mode. This commit adds a logic that resets a pointer to a dehydrated content and we use this as an indication that the view is swtiched to a regular mode.

Resolves #53163.

PR Close #53274
2023-12-01 10:35:36 -08:00
Andrew Kushnir 4b23221b4e fix(core): support hydration for cases when content is re-projected using ng-template (#53304)
This commit fixes an issue with hydration, which happens when a content is projected in a certain way, leaving host elements non-projected, but the child content projected.

The fix is to detect such situations and add extra annotations to help runtime logic locate those elements at the right locations.

Resolves #53276.

PR Close #53304
2023-12-01 09:12:54 -08:00
Andrew Kushnir 899f6c4a12 fix(core): handle hydration of multiple nodes projected in a single slot (#53270)
This commit updates the logic to handle hydration of multiple nodes projected in a single slot. Currently, in case component nodes are content-projected and their order is changed during the projection, hydration can not find the correct element. With this fix, extra annotation info would be included for such nodes and hydration logic at runtime will use it to locate the right element.

Resolves #53246.

PR Close #53270
2023-11-30 09:16:23 -08:00
Kristiyan Kostadinov c7c7ea9813 fix(core): inherit host directives (#52992)
Adds support for inheriting host directives from the parent class. This is consistent with how we inherit other features like host bindings.

Fixes #51203.

PR Close #52992
2023-11-20 13:16:15 -08:00
Andrew Scott b35c6731e5 fix(core): Reattached views that are dirty from a signal update should refresh (#53001)
Related to #52928 but `updateAncestorTraversalFlagsOnAttach` is called
on view insertion and _should_ have made that work for views dirty from
signals but it wasn't updated to read the `dirty` flag when we changed
it from sharing the `RefreshView` flag.

For #52928, we've traditionally worked under the assumption that this is working
as expected.  The created view is `CheckAlways`. There is a question of whether we
should automatically mark things for check when the attached view has
the `Dirty` flag and/or has the `FirstLViewPass` flag set (or other
flags that indicate it definitely needs to be prefreshed).

PR Close #53001
2023-11-20 12:06:32 -08:00
yuki d9a1528265 docs: fix packages\core\src\application_init.ts (#53040)
PR Close #53040
2023-11-20 12:04:17 -08:00
arturovt 7affa57754 fix(common): scan images once page is loaded (#52991)
This commit updates the implementation of the `ImagePerformanceWarning` and
runs the image scan even if the page has already been loaded. The `window.load`
event would never fire if the page has already been loaded; that's why we're
checking for the document's ready state.

PR Close #52991
2023-11-20 08:57:31 -08:00
arturovt ed0fbd4071 fix(core): cleanup loading promise when no dependencies are defined (#53031)
This commit cleans up the `loadingPromise` when no `dependenciesFn` is defined,
as it's already cleaned up after the resolution of `Promise.allSettled`. This
occurs with `prefetch on` triggers, such as when `triggerResourceLoading` is called
from `ɵɵdeferPrefetchOnImmediate`, where there are no dependencies to load. The
`loadingPromise` should still be cleaned up because it typically involves the
`ZoneAwarePromise`, which isn't properly garbage collected when referenced elsewhere
(in this case, it would be referenced from the `tView` data).

PR Close #53031
2023-11-20 08:54:41 -08:00
Enea Jahollari c3e9a2b7cc docs(core): replace RefreshTransplantedView with RefreshView (#52982)
PR Close #52982
2023-11-17 08:10:13 -08:00
arturovt 2205c8993f refactor(common): remove load event listener once it is fired (#52512)
This commit removes the `load` event listener once it has fired within the
`ImagePerformanceWarning`. The `load` event listener prevents the zone stuff from
being garbage collected in development mode when debugging microfrontend applications
that may be destroyed multiple times.

PR Close #52512
2023-11-16 15:11:59 -08:00
Andrew Kushnir 1ce31d819b fix(core): handle local refs when getDeferBlocks is invoked in tests (#52973)
This commit fixes an issue where having elements with local refs in some cases causes JS exception.

PR Close #52973
2023-11-16 12:20:54 -08:00
Kristiyan Kostadinov ec2d6e7b9c fix(compiler): changed after checked error in for loops (#52935)
Reworks the `repeater` instruction to go through `advance`, instead of passing in the index directly. This ensures that lifecycle hooks run at the right time and that we don't throw "changed after checked" errors when we shouldn't be.

Fixes #52885.

PR Close #52935
2023-11-15 21:13:36 +00:00
Andrew Scott 0a976c86d6 refactor(core): Remove detectChanges private export (#52875)
detectChanges private export is not needed.

PR Close #52875
2023-11-14 21:05:42 +00:00
Andrew Scott 7f9ab9dc56 refactor(core): Simplify detectChangesInternal call signature (#52866)
The call signature of detectChangesInternal requires parameters that can all be
found directly on lView. This commit removes those paramters and instead
grabs them in the function implementation.

PR Close #52866
2023-11-14 15:11:41 +00:00
Andrew Scott c5ead61462 refactor(core): Move booleans in LContainer to flags slot (#52338)
There are now 2 booleans in the LContainer so this commit moves them to a shared
FLAGS slot like the LView.

PR Close #52338
2023-11-13 18:25:17 +00:00
AleksanderBodurri 6aef0f6fc8 fix(core): handle non-container environment injector cases (#52774)
Previously we had logic for a special case where a root injector in standalone apps would skip the import paths calculation step for the `getEnvironmentInjectorProviders` function.

This commit intends to fix this for two other cases, namely:
- When an injector is created by a route (via the `providers` field and lazy loading).
- When an injector is manually created and attached to the injector tree

It does this by assuming that any environment injector it cannot find a provider imports container for was created without one, and simply returns the raw provider records without the import paths calculation.

PR Close #52774
2023-11-13 16:21:03 +00:00
Pawel Kozlowski 6c8776ff71 fix(core): limit rate of markers invocations (#52742)
This PR assures that the performance markers are invoked
only once for a given feature.

Closes #52524

PR Close #52742
2023-11-09 19:58:26 +00:00
Mikhail Filin 867161f204 docs: remove broken link pill in dynamic component page (#52744)
PR Close #52744
2023-11-09 19:18:29 +00:00
Andrew Kushnir 55d2c427c2 refactor(core): delay applying component metadata until it's needed in tests (#52708)
When a component contains `@defer` blocks, Angular compiler generates the code to apply component metadata (from the `@Component` decorator) after resolving all dynamic dependencies. Currently, this function is invoked eagerly at runtime, which causes dynamic imports to be kicked off earlier than expected. With the change in this commit, Angular will start resolving async metadata when it becomes necessary during testing.

PR Close #52708
2023-11-09 16:58:06 +00:00
Pawel Kozlowski ea8c9b61d2 fix(core): properly update collection with repeated keys in @for (#52697)
This change fixes a bug in the new list reconcilation algorithm
that could lead to an infinite loop in certain situations.

More specifically, it adjusts the internal MultiMap implementation
such that an entry returned from the .get call is the same entry
(for an identical key) removed by the .delete call.

The existing logic of the MultiMap was leading to a situation where
one view was requested and attached to LContainer, but a very different
view was removed from the MultiMap. This was leaving an attached LView
in a collection that was supposed to hold only detached views.

Closes #52524

PR Close #52697
2023-11-09 15:44:53 +00:00
cexbrayat cc68b0e9f5 fix(core): error code in image performance warning (#52727)
The warnings link to https://angular.io/errors/NG2965 which is a 404
The proper error page is https://angular.io/errors/NG0913

PR Close #52727
2023-11-09 15:44:24 +00:00
Andrew Scott 33da677ecc refactor(core): Remove RootViewRef<T> because it is the same as ViewRef<T> (#52430)
`RootViewRef<T>` extends `ViewRef<T>` and overrides 3 methods with behavior
that is identical to `ViewRef<T>`. This commit removes `RootViewRef<T>`
because it is not needed.

PR Close #52430
2023-11-08 08:50:50 -08:00
Andrew Scott 8592585f6a test(core): Add test to ensure writing to signals in afterRender hooks throws error (#52475)
We do not yet handle running change detection again if `afterRender`
hooks write to signals.

PR Close #52475
2023-11-08 08:48:44 -08:00
Andrew Scott a8071ffcbd refactor(core): Remove change detection flag used in g3 (#52530)
This has now been cleaned up internally and is no longer needed

PR Close #52530
2023-11-06 11:42:15 -08:00
AleksanderBodurri 3d73b0cbfb fix(core): use TNode instead of LView for mapping injector providers (#52436)
Previously, LViews were used here to be consistent with other debug APIs. Using LViews for tracking injector providers does not work because providers only get configured once per TNode type.

Now we use the TNode as the key to track element injector providers, allowing the injector for each item rendered in a list (`ngFor` or `@for`) to be targeted with debug APIs for inspecting providers

PR Close #52436
2023-11-03 09:36:00 -07:00
Aanchal Agarwal 25600375a3 docs: omit repetitions (#52413)
PR Close #52413
2023-11-03 07:46:54 -07:00
Alan Agius d4bd3f198b build: configure tslint to ban performance.mark usages (#52505)
While `performance.mark` is available on all supported browsers and node.js version this API is not available in JSDOM which is used by Jest and Cloudflare worker.

PR Close #52505
2023-11-03 07:43:36 -07:00
Alan Agius 93d32a9acb fix(core): guard usages of performance.mark (#52505)
While `performance.mark` is available on all supported browsers and node.js version this API is not available in JSDOM which is used by Jest and Cloudflare worker.

This commit, updates the usage to a safer variant.

PR Close #52505
2023-11-03 07:43:36 -07:00
Alex Rickabaugh 1f528f1e8d refactor(core): mark effect as developer preview again (#52490)
Re-add the `@developerPreview` flags to `effect()`. While we don't expect
the `effect` API itself to change, we may change how other FW APIs interact
with `effect`s. Also, add a missing export for one of those effect APIs.

PR Close #52490
2023-11-02 15:29:44 -07:00
Andrew Scott 83a3b85c35 refactor(core): Do not refresh view if producers did not actually change (#52476)
Producers represent values which can deliver change notifications.
When a producer value is changed, a change notification is propagated through the graph,
notifying live consumers which depend on the producer of the potential update.
Note here that this is a _potential_ update.

A producer may not have actually "changed" based on its equality function. With
this commit, before refreshing a view that is only marked for refresh
because its consumer is dirty, we poll producers for change to see if
they really have. If not, we can skip the refresh. The example test in this commit
shows that a `computed` which depends on a `signal` that is updated but
produces a value that is the same as before will _not_ cause the
component's template to refresh.

fixes #51797

PR Close #52476
2023-11-02 13:23:49 -07:00
Andrew Scott 164cfc0acf refactor(core): Update LView consumer to use only 1 consumer for a component (#52476)
This commit updates the reactive consumer used for `LView`s to be shared
between a component and its embedded views. This allows us to use the
consumer flag directly for a dirty indicator rather than needing to
find a component view for updating its flags.

In the future, this will also allow us to effectively poll producers to see if
they really changed before refreshing a view.

PR Close #52476
2023-11-02 13:23:49 -07:00
Andrew Scott 58d74a29f8 refactor(core): Remove warning about signal set during template execution (#52476)
The significance of the combination of #51854 and #52302 went mostly
unnoticed. The first removed a unidirectional data flow constraint for
transplanted views and the second updated the signal implementation to
share transplanted view logic. The result is that we automatically get behavior
that (mostly) removes `ExpressionChangedAfterItWasCheckedError` when signals are
used to drive application state to DOM synchronization.

fixes #50320

PR Close #52476
2023-11-02 13:23:49 -07:00