Prior to this patch if any backwards-compatible Angular code was using
Ivy then the built-in `window.ng` debug utilies would not be exposed.
PR Close#32725
Now that the `$localize` translations are `MessageId` based the
compiler must render `MessageId`s in its generated `$localize` code.
This is because the `MessageId` used by the compiler is computed
from information that does not get passed through to the `$localize`
tagged string.
For example, the generated code for the following template
```html
<div id="static" i18n-title="m|d" title="introduction"></div>
```
will contain these localization statements
```ts
if (ngI18nClosureMode) {
/**
* @desc d
* @meaning m
*/
const MSG_EXTERNAL_8809028065680254561$$APP_SPEC_TS_1 = goog.getMsg("introduction");
I18N_1 = MSG_EXTERNAL_8809028065680254561$$APP_SPEC_TS_1;
}
else {
I18N_1 = $localize \`:m|d@@8809028065680254561:introduction\`;
}
```
Since `$localize` is not able to accurately regenerate the source-message
(and so the `MessageId`) from the generated code, it must rely upon the
`MessageId` being provided explicitly in the generated code.
The compiler now prepends all localized messages with a "metadata block"
containing the id (and the meaning and description if defined).
Note that this metadata block will also allow translation file extraction
from the compiled code - rather than relying on the legacy ViewEngine
extraction code. (This will be implemented post-v9).
Although these metadata blocks add to the initial code size, compile-time
inlining will completely remove these strings and so will not impact on
production bundle size.
PR Close#32594
Prior to this patch, each time `advance()` would run (or when a
templateFn or hostBindings code exits) then the core change detection
code would check to see whether the styling data needs to be reset. This
patch removes that functionality and places everything inside of the
scheduled styling exit function. This means that each time one or more
styling bindings run (even if the value hasn't changed) then an exit
function will be scheduled and that will do all the cleanup.
PR Close#32591
This patch is a final major refactor in styling Angular.
This PR includes three main fixes:
All temporary state taht is persisted between template style/class application
and style/class application in host bindings is now removed.
Removes the styling() and stylingApply() instructions.
Introduces a "direct apply" mode that is used apply prop-based
style/class in the event that there are no map-based bindings as
well as property collisions.
PR Close#32259
PR Close#32591
If an <ng-template> contains a structural directive (for example *ngIf), Ngtsc generates extra template function with 1 template instruction call. When <ng-template> tag also contains i18n attribute on it, we generate i18nStart and i18nEnd instructions around it, which is unnecessary and breaking runtime. This commit adds a logic to make sure we do not generate i18n instructions in case only `template` is present.
PR Close#32623
This is a refactoring that moves the source code around to provide a better
platform for adding the compile-time inlining.
1. Move the global side-effect import from the primary entry-point to a
secondary entry-point @angular/localize/init.
This has two benefits: first it allows the top level entry-point to
contain tree-shakable shareable code; second it gives the side-effect
import more of an "action" oriented name, which indicates that importing
it does something tangible
2. Move all the source code into the top src folder, and import the localize
related functions into the localize/init/index.ts entry-point.
This allows the different parts of the package to share code without
a proliferation of secondary entry-points (i.e. localize/utils).
3. Avoid publicly exporting any utilities at this time - the only public
API at this point are the global `$localize` function and the two runtime
helpers `loadTranslations()` and `clearTranslations()`.
This does not mean that we will not expose additional helpers for 3rd
party tooling in the future, but it avoid us preemptively exposing
something that we might want to change in the near future.
Notes:
It is not possible to have the `$localize` code in the same Bazel package
as the rest of the code. If we did this, then the bundled `@angular/localize/init`
entry-point code contains all of the helper code, even though most of it is not used.
Equally it is not possible to have the `$localize` types (i.e. `LocalizeFn`
and `TranslateFn`) defined in the `@angular/localize/init` entry-point because
these types are needed for the runtime code, which is inside the primary
entry-point. Importing them from `@angular/localize/init` would run the
side-effect.
The solution is to have a Bazel sub-package at `//packages/localize/src/localize`
which contains these types and the `$localize` function implementation.
The primary `//packages/localize` entry-point imports the types without
any side-effect.
The secondary `//packages/localize/init` entry-point imports the `$localize`
function and attaches it to the global scope as a side-effect, without
bringing with it all the other utility functions.
BREAKING CHANGES:
The entry-points have changed:
* To attach the `$localize` function to the global scope import from
`@angular/localize/init`. Previously it was `@angular/localize`.
* To access the `loadTranslations()` and `clearTranslations()` functions,
import from `@angular/localize`. Previously it was `@angular/localize/run_time`.
PR Close#32488
This perf-focused refactoring moves the TNode's input / output initialization
logic to the first template pass - close to the place where directives are
matched and resolved.
This code change makes it possible to update-mode checks for both property
bindings and listeners registration.
PR Close#32608
Before this refactoring we had 2 utility functions to check if a given
TNode has matching directives. This PR leaves just one such function
(one that does less memory read).
PR Close#32495
While determining a property name to bind to we were checking a mapping object
resulting in the megamorphic read. Replacing such read with a series of if checks
speeds up rproprty update benchmark ~30% (~1400ms down to ~1000ms).
PR Close#32574
Ensures that the "core_all:size_test" target runs with "--define=compile=aot".
This is necessary because we don't run this test on CI currently, but if we run
it manually, we need to ensure that it runs with Ivy for proper size comparisons.
PR Close#32613
This patch is a final major refactor in styling Angular.
This PR includes three main fixes:
All temporary state taht is persisted between template style/class application
and style/class application in host bindings is now removed.
Removes the styling() and stylingApply() instructions.
Introduces a "direct apply" mode that is used apply prop-based
style/class in the event that there are no map-based bindings as
well as property collisions.
PR Close#32259
PR Close#32596
This patch is a final major refactor in styling Angular.
This PR includes three main fixes:
All temporary state taht is persisted between template style/class application
and style/class application in host bindings is now removed.
Removes the styling() and stylingApply() instructions.
Introduces a "direct apply" mode that is used apply prop-based
style/class in the event that there are no map-based bindings as
well as property collisions.
PR Close#32259
Prior to this commit, a directive with a selector `selector=".Titledir"` would not match an element like `div class="titleDir"` in Ivy whereas it would in VE. The same issue was present for `selector="[title=Titledir]` and `title="titleDir"`. This fixes the Ivy behavior by changing the matching algorithm to use lowercased values.
Note that some `render3` tests needed to be changed to reflect that the compiler generates lowercase selectors. These tests are in the process to be migrated to `acceptance` to use `TestBed` in another PR anyway.
PR Close#32548
Adds two acceptance tests to show a current difference in behavior between Ivy and VE.
A directive with a selector `.Titledir` matches an element with `class="titleDir"` in VE but not in Ivy.
Same thing for an attribute value.
PR Close#32548
Prior to this commit, the `previousOrParentTNode` was set to null after performing all operations within `refreshView` function. It's causing problems in more complex scenarios, for example when change detection is triggered during DI (see test added as a part of this commit). As a result, global state might be corrupted. This commit captures current value of `previousOrParentTNode` and restores it after `refreshView` call.
PR Close#32521
Replaces the `select` instruction with a new one called `advance`. Instead of the jumping to a specific index, the new instruction goes forward X amount of elements. The advantage of doing this is that it should generate code the compresses better.
PR Close#32516
The `goog.getMsg()` function requires placeholder names to be camelCased.
This is not the case for `$localize`. Here placeholder names need
match what is serialized to translation files.
Specifically such placeholder names keep their casing but have all characters
that are not in `a-z`, `A-Z`, `0-9` and `_` converted to `_`.
PR Close#32509
Logs a warning instead of throwing when running into a binding to an unknown property in JIT mode. Since we aren't using a schema for the runtime validation anymore, this allows us to support browsers where properties are unsupported.
PR Close#32463
Prior to this commit, listeners order was not preserved in case we coalesce them to avoid triggering unnecessary change detection cycles. For performance reasons we were attaching listeners to existing events at head (always using first listener as an anchor), to avoid going through the list, thus breaking the order in which listeners are registered. In some scenarios this order might be important (for example with `ngModelChange` and `input` listeners), so this commit updates the logic to put new listeners at the end of the list. In order to avoid performance implications, we keep a pointer to the last listener in the list, so adding a new listener takes constant amount of time.
PR Close#32484
Fixes an issue where Ivy incorrectly inserts items in the beginning of an `ngFor`, if the `ngFor` is set on an `ng-container`. The issue comes from the fact that we choose the `ng-container` comment node as the anchor point before which to insert the content, however the node might be after any of the nodes inside the container. These changes switch to picking out the first node inside of the container instead.
PR Close#32324
Prior to this commit, complex expressions (that require additional statements to be generated) were handled incorrectly in case they were used in attributes annotated with i18n flags. The problem was caused by the fact that extra statements were not appended to the temporary vars block, so they were missing in generated code. This commit updated the logic to use the `convertPropertyBinding`, which contains the necessary code to append extra statements. The `convertExpressionBinding` function was removed as it duplicates the `convertPropertyBinding` one (for the most part) and is no longer used.
PR Close#32309
Prior to this fix if a `NO_CHANGE` value was assigned to a binding, or
an interpolation value rendererd a `NO_CHANGE` value, then the presence
of that value would cause the internal counter index values to not
increment properly. This patch ensures that this doesn't happen and
that the counter/bitmask values update accordingly.
PR Close#32143
PR #32154 introduced `platform` and `any` for `providedIn` and the doc has a minor typo.
Also a test name was not changed accordingly to the refactoring done.
PR Close#32410
Since property binding metadata storage is guarded with the ngDevMode now
and several instructions were merged together, we can simplify the way we
store and read property binding metadata.
PR Close#32457
This commit changes the Angular compiler (ivy-only) to generate `$localize`
tagged strings for component templates that use `i18n` attributes.
BREAKING CHANGE
Since `$localize` is a global function, it must be included in any applications
that use i18n. This is achieved by importing the `@angular/localize` package
into an appropriate bundle, where it will be executed before the renderer
needs to call `$localize`. For CLI based projects, this is best done in
the `polyfills.ts` file.
```ts
import '@angular/localize';
```
For non-CLI applications this could be added as a script to the index.html
file or another suitable script file.
PR Close#31609
DebugElement.query also searches elements that may have been created
outside of Angular (ex: with `document.appendChild`). The current
behavior attempts to get the LContext of these nodes but throws an error
because the LContext does not exist.
PR Close#32361
This patch introduces a new micro benchmark that performance tests
against map-based style and class bindings in Ivy running together
on the same element.
PR Close#32401
This patch introduces a new micro benchmark that performance tests
against style and class bindings in Ivy running together on the same
element.
PR Close#32401
Extend the vocabulary of the `providedIn` to also include `'platform'` and `'any'`` scope.
```
@Injectable({
providedId: 'platform', // tree shakable injector for platform injector
})
class MyService {...}
```
PR Close#32154
TestBed.get is not type safe, fixing it would be a massive breaking
change. The Angular team has proposed replacing it with TestBed.inject
and deprecate TestBed.get.
Deprecation from TestBed.get will come as a separate commit.
Issue #26491Fixes#29905
BREAKING CHANGE: Injector.get now accepts abstract classes to return
type-safe values. Previous implementation returned `any` through the
deprecated implementation.
PR Close#32200
Reworks the compiler to output the factories for directives, components and pipes under a new static field called `ngFactoryFn`, instead of the usual `factory` property in their respective defs. This should eventually allow us to inject any kind of decorated class (e.g. a pipe).
**Note:** these changes are the first part of the refactor and they don't include injectables. I decided to leave injectables for a follow-up PR, because there's some more cases we need to handle when it comes to their factories. Furthermore, directives, components and pipes make up most of the compiler output tests that need to be refactored and it'll make follow-up PRs easier to review if the tests are cleaned up now.
This is part of the larger refactor for FW-1468.
PR Close#31953
Binding metadata are only needed:
- for property bindings;
- when TestBed tests are being run.
This commit guards binding metadata storage with the ngDevMode flag
which saves ~6% of a proerty binding processing time in the production
mode (and reduces bundle size).
PR Close#32317
After a series of recent refactorings `enterView` and `leaveView` became
identical. This PR merges both into one concept of view selectio (similar
to a node selection). This reduces number of concepts and code size.
PR Close#32263
Prior to this change, the `BINDING_INDEX` of a given lView was reset after processing a template. However change detection can be triggered as a result of View queries processing, thus leading to subsequent `refreshView` call (and executing a template), which in turn operates with the binding index that is not reset after the previous `refreshView` call. This commit updates the logic to reset binding index before we execute a template, so binding index is correct for instructions inside template function.
PR Close#32201
This commit drops our custom, change-detection specific, equality comparison util
in favour of the standard Object.is which has desired semantics.
There are multiple advantages of this approach:
- less code to maintain on our end;
- avoid NaN checks if both values are equal;
- re-write NaN checks so we don't trigger V8 deoptimizations.
PR Close#32212
Angular hooks come after 2 flavours:
- init hooks (OnInit, AfterContentInit, AfterViewInit);
- check hooks (OnChanges, DoChanges, AfterContentChecked, AfterViewChecked).
We need to do more processing for init hooks to ensure that those hooks
are run once and only once for a given directive (even in case of errors).
As soon as all init hooks execute to completion we are only left with the
checks to execute.
It turns out that keeping track of the remaining init hooks to execute is
rather expensive (multiple LView flags reads, writes and checks). But we can
observe that non of this tracking is needed as soon as all init hooks are
completed.
This PR takes advantage of the above observations and splits hooks processing
functions into:
- init-specific (slower but less common);
- check-specific (faster and more common).
NOTE: there is code duplication in this PR and it is left like this intentinally:
hand-inlining this perf-critical code makes the view refresh process substentially
faster.
PR Close#32131
In VE the `Sanitizer` is always available in `BrowserModule` because the VE retrieves it using injection.
In Ivy the injection is optional and we have instructions instead of component definition arrays. The implication of this is that in Ivy the instructions can pull in the sanitizer only when they are working with a property which is known to be unsafe. Because the Injection is optional this works even if no Sanitizer is present. So in Ivy we first use the sanitizer which is pulled in by the instruction, unless one is available through the `Injector` then we use that one instead.
This PR does few things:
1) It makes `Sanitizer` optional in Ivy.
2) It makes `DomSanitizer` tree shakable.
3) It aligns the semantics of Ivy `Sanitizer` with that of the Ivy sanitization rules.
4) It refactors `DomSanitizer` to use same functions as Ivy sanitization for consistency.
PR Close#31934
For some reason (on OS/X) this transitive dependency is not being passed
through to the final TS builds that rely on this rule, so the build fails
with a missing file error:
```
The specified path does not exist:
'/.../sandbox/darwin-sandbox/451/execroot/angular/packages/tsconfig-build.json'.
```
PR Close#31943
Similar to interpolation, we do not want to completely remove whitespace
nodes that are siblings of an expansion.
For example, the following template
```html
<div>
<strong>items left<strong> {count, plural, =1 {item} other {items}}
</div>
```
was being collapsed to
```html
<div><strong>items left<strong>{count, plural, =1 {item} other {items}}</div>
```
which results in the text looking like
```
items left4
```
instead it should be collapsed to
```html
<div><strong>items left<strong> {count, plural, =1 {item} other {items}}</div>
```
which results in the text looking like
```
items left 4
```
---
**Analysis of the code and manual testing has shown that this does not cause
the generated ids to change, so there is no breaking change here.**
PR Close#31962
Fixes Ivy's directive matching not capturing attribute selectors when there is one class binding, one style binding and a regular binding that precede the attribute that would match the directive. The issue appears to come from the fact that we weren't skipping over style bindings correctly which was throwing the loop off not to go into `bindingsMode` and to skip some of the bindings when matching.
PR Close#31942
Fixes Ivy's `DebugElement.triggerEventHandler` to picking up events that have been registered through a `Renderer2`, unlike ViewEngine.
This PR resolves FW-1480.
PR Close#31845