We had some tests that were leaking LViews, because we were testing things like `createComponent`, but not destroying them afterwards. These changes clean up most of them, although there are a handful still left that I didn't have time to fully track down.
PR Close#57546
When we create the LView for a component, we track it in the `TRACKED_LVIEWS` map. It gets untracked when it is destroy, but if it throws during creation, the user won't have access to a `ComponentRef` in order to clean it up.
These changes automatically untrack the related LViews if the component couldn't be created.
PR Close#57546
This commit reduces the bundle size of `@angular/core` by 19.5 MB by excluding sourcemaps from the schematics.
Since the `esbuild` rule doesn't allow disabling sourcemaps directly, we work around this by setting the sourcemaps to `external`. Afterward, we filter the output to include only the `.js` files.
PR Close#57545
This commit updates a directive mock instance to include an extra field that a compiler code was expecting, which caused issues while processing elements with local refs and exported directives.
PR Close#57537
Adds initial migration logic to convert decorator query declarations to
signal queries.
We will re-use more part of the signal input migration in follow-ups, to
properly migrate, and e.g. even handle control flow
PR Close#57525
Previously, if an application had DOM Nodes injected into it from other frames, DevTools would fail to parse component trees with the render tree strategy properly because of an instanceof Node check that the framework performs.
Now we check for instanceof Node before even calling framework debug APIs on DOM nodes so that we can skip nodes that come from other frames entirely.
PR Close#57518
In some cases apps need to schedule a bit later the loading of the animation module. This private token will allow to investigate which other strategy could be useful.
PR Close#57493
BREAKING CHANGE: Render default fallback with empty `projectableNodes`.
When passing an empty array to `projectableNodes` in the `createComponent` API, the default fallback content of the `ng-content` will be rendered if present. To prevent rendering the default content, pass `document.createTextNode('')` as a `projectableNode`.
For example:
```ts
// The first ng-content will render the default fallback content if present
createComponent(MyComponent. { projectableNodes: [[], [secondNode]] });
// To prevent projecting the default fallback content:
createComponent(MyComponent. { projectableNodes: [[document.createTextNode('')], [secondNode]] });
```
Fixes#57471
PR Close#57480
When disabling `i18nPreserveSignificantWhitespaceForLegacyExtraction` I was looking at a test case with ICU messages containing leading and trailing whitespace:
```angular
<div i18n>
{apples, plural, =other {I have many apples.}}
</div>
```
This would historically generate two messages:
```javascript
const MSG_TMP = goog.getMsg('{apples, plural, =other {I have many apples.}}');
const MSG_FOO = goog.getMsg(' {$ICU} ', { 'ICU': MSG_TMP });
```
But I found that I was getting just one message:
```javascript
const MSG_TMP = goog.getMsg(' {apples, plural, =other {I have many apples.}} ');
```
This is arguably an improvement, but changed the messages and message IDs, which isn't desirable with this option. I eventually traced this back to the `isIcu` initialization in [`i18n_parser.ts`](/packages/compiler/src/i18n/i18n_parser.ts):
```typescript
const context: I18nMessageVisitorContext = {
isIcu: nodes.length == 1 && nodes[0] instanceof html.Expansion,
// ...
};
```
[`_I18nVisitor.prototype.visitExpansion`](/packages/compiler/src/i18n/i18n_parser.ts) uses this to decide whether or not to generate a sub-message for a given ICU expansion:
```typescript
if (context.isIcu || context.icuDepth > 0) {
// Returns an ICU node when:
// - the message (vs a part of the message) is an ICU message, or
// - the ICU message is nested.
const expPh = context.placeholderRegistry.getUniquePlaceholder(`VAR_${icu.type}`);
i18nIcu.expressionPlaceholder = expPh;
context.placeholderToContent[expPh] = {
text: icu.switchValue,
sourceSpan: icu.switchValueSourceSpan,
};
return context.visitNodeFn(icu, i18nIcu);
}
// Else returns a placeholder
// ICU placeholders should not be replaced with their original content but with the their
// translations.
// TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
const phName = context.placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
context.placeholderToMessage[phName] = this.toI18nMessage([icu], '', '', '', undefined);
const node = new i18n.IcuPlaceholder(i18nIcu, phName, icu.sourceSpan);
return context.visitNodeFn(icu, node);
```
Note that `isIcu` is the key condition between these two cases and depends on whether or not the ICU expansion has any siblings. The introduction of `WhitespaceVisitor` to `I18nMetaVisitor` trims insignificant whitespace, including empty text nodes not adjacent to an ICU expansion (from [`WhitespaceVisitor.prototype.visitText`](/packages/compiler/src/ml_parser/html_whitespaces.ts)):
```typescript
const isNotBlank = text.value.match(NO_WS_REGEXP);
const hasExpansionSibling =
context && (context.prev instanceof html.Expansion || context.next instanceof html.Expansion);
if (isNotBlank || hasExpansionSibling) {
// Transform node by trimming it...
return trimmedNode;
}
return null; // Drop node which is empty and has no ICU expansion sibling.
```
`hasExpansionSibling` was intended to retain empty text nodes leading or trailing an ICU expansion, however `context` was `undefined`, so this check failed and the leading / trailing text nodes were dropped. This resulted in trimming the ICU text by dropping the leading / trailing whitespace nodes. Having only a single ICU expansion with no leading / trailing text nodes caused `_I18nVisitor` to initialize `isIcu` incorrectly and caused it to generate one message instead of two.
`WhitespaceVisitor` is supposed to get this context from `visitAllWithSiblings`. So the fix here is to make sure `WhitespaceVisitor` is always visited via this function which provides the required context. I updated all usage sites to make sure this context is use consistently and implemented the `WhitespaceVisitor.prototype.visit` method to throw when the context is missing to make sure we don't encounter a similar mistake in the future.
Unfortunately this broke one compliance test. Specifically the [`icu_logic/icu_only.js`](/home/douglasparker/Source/ng/packages/compiler-cli/test/compliance/test_cases/r3_view_compiler_i18n/icu_logic/icu_only.js) test which changed from generating:
```javascript
function MyComponent_Template(rf, ctx) {
if (rf & 1) {
i0.ɵɵi18n(0, 0);
}
// ...
}
```
To now generating:
```javascript
function MyComponent_Template(rf, ctx) {
if (rf & 1) {
i0.ɵɵtext(0, " ");
i0.ɵɵi18n(1, 0);
i0.ɵɵtext(2, "\n");
}
// ...
}
```
This test uses the default value `preserveWhitespaces: false` (`i18nPreserveSignificantWhitespaceForLegacyExtraction` should not affect compiled JS output, we already retain significant whitespace there). So what this indicates to me is that ICU logic is already broken because it's not preserving significant whitespace in this case. My change is probably a bug fix, but one which would affect the compiled runtime, which is not in scope here. The root cause is because using `visitAllWithSiblings` everywhere means the context is retained correctly in this case and the whitespace is leading/trailing an ICU message, therefore it is retained per the logic of `WhitespaceVisitor.prototype.visitText` I mentioned eariler.
To address this, I left one usage of `WhitespaceVisitor` using `html.visitAll` instead of `visitAllWithSiblings` to retain this bug. I has to lossen the assertion I put in `WhitespaceVisitor.prototype.visit` to make this possible, but it should still throw by default when misused, which is the important part.
PR Close#56507
This configures whether or not to preserve whitespace content when extracting messages from Angular templates in the legacy (View Engine) extraction pipeline.
This includes several bug fixes which unfortunately cannot be landed without changing message IDs in a breaking fashion and are necessary to properly trim whitespace. Instead these bug fixes are included only when the new flag is disabled.
PR Close#56507
When disabling autodetect (not recommeneded) with zoneless,
`fixture.detectChanges` would previously not refresh the fixture's component.
PR Close#57416
Disabling `checkNoChanges` in `ComponentFixture.detectChanges` was an
error for the zoneless fixture since it was not yet working. This now
allows checkNoChanges to be disabled. This option isn't really
used/shouldn't be used by anyone except the FW so marked as a refactor.
PR Close#57416
This commit removes the abstract base class and two separate
implementations of `ComponentFixture` for zone vs zoneless. Now that the
behaviors have gotten close enough to the same, the diverged concrete
implementations serve less value. Instead, the different behaviors can
be easily handled in if/else branches. The difference is now limited to
the default for `autoDetect` and how `detectChanges` functions.
PR Close#57416
This commit moves the ngZone onError subscription to the base fixture
implementation. While this subscription isn't necessary for zoneless, it does
no harm because the observable never emits.
PR Close#57416
This commit updates the implementations of `autoDetectChanges` to be
shared between the zone-based and zoneless fixtures. This now allows
`autoDetect` to be turned off for zoneless fixtures after it was
previously on because the host view is no longer directly attached to
`ApplicationRef`.
PR Close#57416
When a browser extension is updated it becomes invalidated on currently open pages. If that extension then tries to send a message to those pages through `chrome.runtime.sendMessage(..)` then an error is thrown in the console
For Angular DevTools, this results in spamming the console with "Uncaught Error: Extension context invalidated." errors.
This commit catches that error and removes the event listener that triggers the `chrome.runtime.sendMessage(...)` call.
PR Close#55697
Previously, the component handler was processing and resolving stylesheets
referenced via `styleUrl`/`styleUrls` multiple times when generating the
compiler metadata for components. The style resource information collection
for such styles has been further consolidated to avoid repeat resource loader
resolve calls which potentially could be expensive. Further optimization is
possible for the inline style case. However, inline styles here only require
AST traversal and no potentially expensive external resolve calls.
PR Close#57502
Angular applications that are AngularJS hybrids are currently unable to
adopt Trusted Types due to violations eminating from an innerHTML
assignment in the @angular/upgrade package. This commit allows
developers of such applications to optionally ignore this class of
violations by configuring the Trusted Types header to allow the new
angular#unsafe-upgrade policy.
Note that the policy is explicitly labeled as unsafe as it does not in
any way mitigate the security risk of using AngularJS in an Angular
application, but does unblock Trusted Types adoption enabling XSS
protection for other parts of the application.
The implementation follows the approach taken in @angular/core;
see packages/core/src/util/security.
PR Close#57454
These should only fire if the target is the same as the targetElement. Also, delete an out of date test since capture/non-capture tests are separately covered.
PR Close#57476
The custom element implementation previously used a custom code path for
setting inputs, which contained bespoke code for writing input properties,
detecting whether inputs actually change, marking the component dirty,
scheduling and running CD, invoking `ngOnChanges`, etc. This custom logic
had several downsides:
* Its behavior different from how Angular components behave in a normal
template.
For example, inputs setters were invoked in `NgZone.run`, which (when
called from outside the zone) would trigger synchronous CD in the
component, _without_ calling `ngOnChanges`. Only when the custom rAF-
scheduled `detectChanges()` call triggered would `ngOnChanges` be called.
* CD always ran multiple times, because of the above. `NgZone.run` would
trigger CD, and then separately the scheduler would trigger CD.
* Signal inputs were not supported, since inputs were set via direct
property writes.
This change refactors the custom element implementation with two changes:
1. `ComponentRef.setInput` is used instead of a custom code path for
writing inputs.
This allows us to drop all the custom logic related to managing
`ngOnChanges`, since `setInput` does that under the hood. `ngOnChanges`
behavior now matches how the component would behave when _not_ rendered
as a custom element.
2. The custom rAF-based CD scheduler is removed in favor of the main Angular
scheduler, which now handles custom elements as necessary.
Running `NgZone.run` is sufficient to trigger CD when zones are used, and
the hybrid zoneless scheduler now ensures CD is scheduled when `setInput` is
called even with no ZoneJS enabled. As a result, the dedicated elements
scheduler is now only used when Angular's built-in scheduler is disabled.
As a concession to backwards compatibility, the element's view is also
marked for refresh when an input changes. Doing this allows CD to revisit
the element even if it becomes dirty during CD, mimicking how it would be
detected by the former elements scheduler unconditionally refreshing the
view a second time.
As a part of this change, the elements tests have been significantly
refactored. Previously all of Angular was faked/spied, which had a number
of downsides. For example, there were tests which asserted that change
detection only happens once when setting multiple inputs. This wasn't
actually the case (because of `NgZone.run` - see logic above) but the test
didn't catch the issue because it was only spying on `detectChanges()` which
isn't called from `ApplicationRef.tick()`. Even the components were fake.
Now, the tests use real Angular components and factories. They've also been
updated to not use `fakeAsync`.
A number of tests have been disabled, which were previously asserting
behavior that wasn't matching what was actually happening (as above). Other
tests were disabled due to real differences with `ngOnChanges` behavior,
where the current behavior could be seen as a bug.
Fixes#53981
BREAKING CHANGE: as part of switching away from custom CD behavior to the
hybrid scheduler, timing of change detection around custom elements has
changed subtly. These changes make elements more efficient, but can cause
tests which encoded assumptions about how or when elements would be checked
to require updating.
PR Close#56728
This removes an unnecessary type wrapping that can be removed because
`Replacements` is expected to be serializable by default.
Similar to how it's done in `TsurgeComplexMigration`.
PR Close#57501
The `afterRender` infrastructure was first implemented around the idea of
independent, singular hooks. It was later updated to support a spec of
multiple hooks that pass values from one to another as they execute, but the
implementation still worked in terms of singular hooks under the hood. This
creates a number of maintenance issues, and a few bugs. For example, when
one hook fails, further hooks in the pipeline should no longer execute, but
this was hard to ensure under the old design.
This refactoring restructures `afterRender` infrastructure significantly to
introduce the concept of a "sequence", a collection of hooks of different
phases that execute together. Overall, the implementation is simplified
while making it more resilient to issues and future use cases, such as the
upcoming `afterRenderEffect`.
As part of this refactoring, the `internalAfterNextRender` concept is
removed, as well as the unused `queueStateUpdate` concept which used it.
PR Close#57453
Previously the zoneless scheduler had a concept of whether views needed to
be refreshed or not, based on the notification type that was received. It
tracked this information as a boolean.
This commit refactors things to track dirtiness in `ApplicationRef` itself,
as a `dirtyFlags` field with bits corresponding to either view tree
dirtiness or after-render hooks.
PR Close#57453
Introduces a simpler, smaller variant of the current `Tsurge` migration
class. The difference is simply that for the migration phase (the third
stage), some migrations do not need a full set of workers re-analyzing
every compilation unit again to compute the "final migration
replacements".
This can be the case, for example, if a migration eagerly computes all
replacements in the analyze stage, visiting every unit, and then after
deriving the global metadata, problematic replacements are simply
filtered (e.g. via some unique IDs again).
PR Close#57484
This commit adds indentations to the file tree section in `app-shell.md`, it makes the tree easier to read. The indentation is now two spaces instead of no spaces.
PR Close#57436
docs(docs-infra): add top level banner component
- create top level banner component
- write unit tests
- close banner and keep state in the local storage
- fix: support screens of tablets and phones
PR Close#57458
This commit adds an internal util method that allows to detect:
* which selectors are matching nodes in a template
* which pipes are present in a template
Both directives and pipes are split into 2 buckets: eagerly used and the ones that might potentially be defer-loaded.
PR Close#57466