Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): more accurate matching of classes during content projection #48888

Closed
wants to merge 1 commit into from

Conversation

masaoki
Copy link
Contributor

@masaoki masaoki commented Jan 31, 2023

Small bug fix. Showing a minimum app to reproduce the bug.

  1. Create the app and add angular material.
ng new project
cd project
ng add @angular/material
  1. Overwrite the src/app/app.component.html with minimal content.
<button mat-button *ngIf="true"><span *ngIf="true" class="{{'a'}}"></span></button>
  1. Run the app. The button is not shown because of an exception.
main.ts:6
ERROR TypeError: item.toLowerCase is not a function
    at isCssClassMatching (core.mjs:8726:35)
    at isNodeMatchingSelector (core.mjs:8814:22)
    at isNodeMatchingSelectorList (core.mjs:8931:13)
    at matchingProjectionSlotIndex (core.mjs:14179:13)
    at Module.ɵɵprojectionDef (core.mjs:14222:49)
    at MatButton_Template (button.mjs:113:99)
    at executeTemplate (core.mjs:10534:9)
    at renderView (core.mjs:10356:13)
    at renderComponent (core.mjs:11529:5)
    at renderChildComponents (core.mjs:10216:9)

Because isCssClassMatching() function does not take care if the value is not string, while attrs[] may contain AttributeMarker which is actually numbers, item.toLowerCase() throws the exception. Just inserted a check if the item is string.

PR Checklist

Please check if your PR fulfills the following requirements:

PR Type

What kind of change does this PR introduce?

  • Bugfix
  • Feature
  • Code style update (formatting, local variables)
  • Refactoring (no functional changes, no api changes)
  • Build related changes
  • CI related changes
  • Documentation content changes
  • angular.io application / infrastructure changes
  • Other... Please describe:

What is the current behavior?

Issue Number: N/A

What is the new behavior?

Does this PR introduce a breaking change?

  • Yes
  • No

Other information

@masaoki masaoki changed the title fix(core): add a type check in isCssClass isCssClassMatching fix(core): add a type check in isCssClassMatching Jan 31, 2023
@AndrewKushnir AndrewKushnir added action: review The PR is still awaiting reviews from at least one requested reviewer target: patch This PR is targeted for the next patch release area: core Issues related to the framework runtime labels Jan 31, 2023
@ngbot ngbot bot added this to the Backlog milestone Jan 31, 2023
@AndrewKushnir
Copy link
Contributor

@masaoki thanks for creating this PR. This fix would also require a test in the packages/core/test/acceptance/content_spec.ts spec. Could you please try to reproduce this problem by putting together a test (see this doc on how to run tests locally)? I think it'd be possible to reproduce this issue without Material components for tests, you can mimic the behavior of the MatButton (the content projection aspect of it). Please let us know if you have any questions. Thank you.

@masaoki
Copy link
Contributor Author

masaoki commented Jan 31, 2023

@AndrewKushnir Ok. I'm going to find a way to reproduce the exception without angular material, and try to reflect the result to the test.

@masaoki
Copy link
Contributor Author

masaoki commented Feb 3, 2023

@AndrewKushnir Pushed content_spec.ts with a test added. The exception could be reproduced when the component has <ng-content> looking at the inner children with both *ngIf and class='{{interpolation}}'.

@truskowc
Copy link

Getting the same issue. What's going on with this?

@truskowc
Copy link

truskowc commented Feb 23, 2023

For those looking for a fix you can rewrite the class interpolation:

class="icon {{ parentMenu.iconClass }}"

to

[ngClass]="[ parentMenu.iconClass ? parentMenu.iconClass : '' ]"

this worked for me

@masaoki
Copy link
Contributor Author

masaoki commented Mar 3, 2023

@AndrewKushnir
Are there any obstacles for merging? What can I do?

@AndrewKushnir
Copy link
Contributor

@masaoki thanks for the PR and sorry for the delay.

While the fix avoids the JS exception, I'm not 100% sure it resolves the root cause. The TAttributes array consists of strings (used as keys and values) and numbers (used as markers to indicate a start of a section). If we come across a situation where a a string is class and the next element is a number, it means that we are in not in a "static attribute" section, but instead, we are in bindings section, thus we should not try to match. However in a situation like this:

<span *ngIf="true" id="5" class="{{'a'}}" [title]="'abc'"></span>

the TAttributes would probably look like this:

['id', '5', 3 /* AttributeMarker.Bindings */, 'class', 'title']

So the elements after a marker would be just binding names, but the logic will treat them as key-value pair and likely match (if you have select=".title" in a projector component).

Could you pleaser try to add a test case based on this example? Also it'd be interesting to see what the attrs array look like in this case.

You can also find additional information about the TAttributes array in the code here.

Thank you.

@masaoki
Copy link
Contributor Author

masaoki commented Mar 8, 2023

@AndrewKushnir
In the my test case with minimum attributes on it, the html is:

<child-comp><span *ngIf="true" class="{{'a'}}"></span></child-comp>

In this case, the attrs inside isCssClassMatching() is

[3, 'class', 4, 'ngIf']

which makes item.toLowerCase() throw the exception. When I change the html to the one you suggested:

<child-comp><span *ngIf="true" id="5" class="{{'a'}}" [title]="'abc'"></span></child-comp>

the attr is

['id', '5', 3, 'class', 'title', 4, 'ngIf']

The exception is gone in this case because of the additional "title" binding, but the isCssClassMatching() confusingly returns true since 'title' is after 'class' as you expected.

The sort order is:

  1. attribute name (string), value (string) pair
  2. AttributeMarker.* (number) with following attribute names (string)

So what we actually have to do here is to find the cssClassToMatch value followed by 'class' before any number value, or the same value between AttributeMarker.Classes and another AttributeMarker.* where the 'class' found is not what we look for.

I rewrote the isCssClassMatching() method to effectively work as expected. Also added both of test cases to packages/core/test/acceptance/content_spec.ts.

@AndrewKushnir
Copy link
Contributor

So what we actually have to do here is to find the cssClassToMatch value followed by 'class' before any number value, or the same value between AttributeMarker.Classes and another AttributeMarker.* where the 'class' found is not what we look for.

Yes, that's correct 👍

Ideally, it'd be great to do it in a single pass over the attrs array, so we can probably do something like this? (note: I haven't tested it, just wanted to illustrate the idea)

diff --git a/packages/core/src/render3/node_selector_matcher.ts b/packages/core/src/render3/node_selector_matcher.ts
index 77beac2b7d..5599805554 100644
--- a/packages/core/src/render3/node_selector_matcher.ts
+++ b/packages/core/src/render3/node_selector_matcher.ts
@@ -35,9 +35,14 @@ function isCssClassMatching(
       assertEqual(
           cssClassToMatch, cssClassToMatch.toLowerCase(), 'Class name expected to be lowercase.');
   let i = 0;
+  // Indicates whether we are processing value from the implicit
+  // attribute section (i.e. before the first marker in the array).
+  let isImplicitAttrsSection = true;
   while (i < attrs.length) {
     let item = attrs[i++];
-    if (isProjectionMode && item === 'class') {
+    if (isProjectionMode && item === 'class' && isImplicitAttrsSection) {
+      // We found a `class` attribute in the implicit attribute section,
+      // check if it matches the value of the `cssClassToMatch` argument.
       item = attrs[i] as string;
       if (classIndexOf(item.toLowerCase(), cssClassToMatch, 0) !== -1) {
         return true;
@@ -49,6 +54,10 @@ function isCssClassMatching(
         if (item.toLowerCase() === cssClassToMatch) return true;
       }
       return false;
+    } else if (typeof item === 'number') {
+      // We've came across a first marker, which indicates
+      // that the implicit attribute section is over.
+      isImplicitAttrsSection = false;
     }
   }
   return false;

@masaoki
Copy link
Contributor Author

masaoki commented Mar 10, 2023

@AndrewKushnir
I see but this code will make another defect with something like:

<child-comp><span *ngIf="true" id="class" class="{{'a'}}"></span></child-comp>

I pushed changes a little modification based on your code again with a test covering the above case.
Check it pls.

@AndrewKushnir
Copy link
Contributor

I see but this code will make another defect with something like:
...
I pushed changes a little modification based on your code again with a test covering the above case.
Check it pls.

Yes, it makes sense. When we are in the "implicit attrs" section, we should handle elements in pair ([key, value, ...]) as implemented in your change 👍

@masaoki
Copy link
Contributor Author

masaoki commented Mar 11, 2023

@AndrewKushnir Did it. Thanks for your assistance!

@AndrewKushnir
Copy link
Contributor

@masaoki thanks for addressing the feedback!

It looks like CI is failing for a couple reasons:

  • The spec file is not formatted correctly, please run a command mentioned in the CI job output (see the CI job output here)
  • There is a failing test (see CI job output here) - it looks like the test is failing due to the override of the item var and instead it should look something like this:
--- a/packages/core/src/render3/node_selector_matcher.ts
+++ b/packages/core/src/render3/node_selector_matcher.ts
@@ -41,11 +41,11 @@ function isCssClassMatching(
   while (i < attrs.length) {
     let item = attrs[i++];
     if (typeof item === 'string' && isImplicitAttrsSection) {
-      item = attrs[i++] as string;
+      const value = attrs[i++] as string;
       if (isProjectionMode && item === 'class') {
         // We found a `class` attribute in the implicit attribute section,
         // check if it matches the value of the `cssClassToMatch` argument.
-        if (classIndexOf(item.toLowerCase(), cssClassToMatch, 0) !== -1) {
+        if (classIndexOf(value.toLowerCase(), cssClassToMatch, 0) !== -1) {
           return true;
         }
       }

Please try to apply the changes and let us know if CI is still "red".

Could you please also merge all commits into one and may be rephrase the commit message a bit to reflect the changes (something like fix(core): more accurate matching of classes during content projection)?

Thank you.

@masaoki masaoki force-pushed the fix_iscssclassmatching branch 2 times, most recently from 78f371a to 4fc44cd Compare March 14, 2023 16:01
Showing a minimum app to reproduce the bug.
1. Create the app and add angular material.
```
ng new project
cd project
ng add @angular/material
```
1. Overwrite the src/app/app.component.html with minimal content.
```
<button mat-button *ngIf="true"><span *ngIf="true" class="{{'a'}}"></span></button>
```
1. Run the app. The button is not shown because of an exception.
```
main.ts:6
ERROR TypeError: item.toLowerCase is not a function
    at isCssClassMatching (core.mjs:8726:35)
    at isNodeMatchingSelector (core.mjs:8814:22)
    at isNodeMatchingSelectorList (core.mjs:8931:13)
    at matchingProjectionSlotIndex (core.mjs:14179:13)
    at Module.ɵɵprojectionDef (core.mjs:14222:49)
    at MatButton_Template (button.mjs:113:99)
    at executeTemplate (core.mjs:10534:9)
    at renderView (core.mjs:10356:13)
    at renderComponent (core.mjs:11529:5)
    at renderChildComponents (core.mjs:10216:9)
```
Because isCssClassMatching() function does not take care if the value is not string, while attrs[] may contain AttributeMarker which is actually numbers, item.toLowerCase() throws the exception.
Just inserted a check if the item is string.

Created a testcase for the original fix. It causes an exception without the fix.

fix(core): add a check code to avoid an exception inside isCssClassMatching

Showing a minimum app to reproduce the bug.
1. Create the app and add angular material.
```
ng new project
cd project
ng add @angular/material
```
1. Add `import { MatButtonModule } from '@angular/material/button'`,
   and also MatButtonModule inside @NgModule imports in src/app/app.module.ts to use MatButtonModule.
1. Overwrite the src/app/app.component.html with minimal content.
```
<button mat-button *ngIf="true"><span *ngIf="true" class="{{'a'}}"></span></button>
```
1. Run the app. The button is not shown because of an exception.
```
main.ts:6
ERROR TypeError: item.toLowerCase is not a function
    at isCssClassMatching (core.mjs:8726:35)
    at isNodeMatchingSelector (core.mjs:8814:22)
    at isNodeMatchingSelectorList (core.mjs:8931:13)
    at matchingProjectionSlotIndex (core.mjs:14179:13)
    at Module.ɵɵprojectionDef (core.mjs:14222:49)
    at MatButton_Template (button.mjs:113:99)
    at executeTemplate (core.mjs:10534:9)
    at renderView (core.mjs:10356:13)
    at renderComponent (core.mjs:11529:5)
    at renderChildComponents (core.mjs:10216:9)
```
Because isCssClassMatching() function does not take care if the value is not string, while attrs[] may contain AttributeMarker which is actually numbers, item.toLowerCase() throws the exception.
Just inserted a check if the item is string.
@masaoki
Copy link
Contributor Author

masaoki commented Mar 15, 2023

@AndrewKushnir
Thanks again for review and suggestion. Hope this is last time.

Copy link
Contributor

@AndrewKushnir AndrewKushnir left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@masaoki the change looks good, thanks for addressing the feedback 👍

We'll run some additional internal tests in Google's codebase and if everything is ok, we'll add the PR to the merge queue.

Thank you.

@AndrewKushnir AndrewKushnir added action: global presubmit The PR is in need of a google3 global presubmit and removed action: review The PR is still awaiting reviews from at least one requested reviewer labels Mar 18, 2023
@AndrewKushnir AndrewKushnir changed the title fix(core): add a type check in isCssClassMatching fix(core): more accurate matching of classes during content projection Mar 18, 2023
@AndrewKushnir
Copy link
Contributor

Global Presubmit (internal-only link).

@AndrewKushnir AndrewKushnir added action: merge The PR is ready for merge by the caretaker merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note and removed action: global presubmit The PR is in need of a google3 global presubmit labels Mar 19, 2023
@AndrewKushnir
Copy link
Contributor

Caretaker note: TGP is "green", this PR is ready for merge.

@pkozlowski-opensource
Copy link
Member

This PR was merged into the repository by commit e655e8a.

pkozlowski-opensource pushed a commit that referenced this pull request Mar 20, 2023
#48888)

Showing a minimum app to reproduce the bug.
1. Create the app and add angular material.
```
ng new project
cd project
ng add @angular/material
```
1. Overwrite the src/app/app.component.html with minimal content.
```
<button mat-button *ngIf="true"><span *ngIf="true" class="{{'a'}}"></span></button>
```
1. Run the app. The button is not shown because of an exception.
```
main.ts:6
ERROR TypeError: item.toLowerCase is not a function
    at isCssClassMatching (core.mjs:8726:35)
    at isNodeMatchingSelector (core.mjs:8814:22)
    at isNodeMatchingSelectorList (core.mjs:8931:13)
    at matchingProjectionSlotIndex (core.mjs:14179:13)
    at Module.ɵɵprojectionDef (core.mjs:14222:49)
    at MatButton_Template (button.mjs:113:99)
    at executeTemplate (core.mjs:10534:9)
    at renderView (core.mjs:10356:13)
    at renderComponent (core.mjs:11529:5)
    at renderChildComponents (core.mjs:10216:9)
```
Because isCssClassMatching() function does not take care if the value is not string, while attrs[] may contain AttributeMarker which is actually numbers, item.toLowerCase() throws the exception.
Just inserted a check if the item is string.

Created a testcase for the original fix. It causes an exception without the fix.

fix(core): add a check code to avoid an exception inside isCssClassMatching

Showing a minimum app to reproduce the bug.
1. Create the app and add angular material.
```
ng new project
cd project
ng add @angular/material
```
1. Add `import { MatButtonModule } from '@angular/material/button'`,
   and also MatButtonModule inside @NgModule imports in src/app/app.module.ts to use MatButtonModule.
1. Overwrite the src/app/app.component.html with minimal content.
```
<button mat-button *ngIf="true"><span *ngIf="true" class="{{'a'}}"></span></button>
```
1. Run the app. The button is not shown because of an exception.
```
main.ts:6
ERROR TypeError: item.toLowerCase is not a function
    at isCssClassMatching (core.mjs:8726:35)
    at isNodeMatchingSelector (core.mjs:8814:22)
    at isNodeMatchingSelectorList (core.mjs:8931:13)
    at matchingProjectionSlotIndex (core.mjs:14179:13)
    at Module.ɵɵprojectionDef (core.mjs:14222:49)
    at MatButton_Template (button.mjs:113:99)
    at executeTemplate (core.mjs:10534:9)
    at renderView (core.mjs:10356:13)
    at renderComponent (core.mjs:11529:5)
    at renderChildComponents (core.mjs:10216:9)
```
Because isCssClassMatching() function does not take care if the value is not string, while attrs[] may contain AttributeMarker which is actually numbers, item.toLowerCase() throws the exception.
Just inserted a check if the item is string.

PR Close #48888
crapStone pushed a commit to Calciumdibromid/CaBr2 that referenced this pull request Mar 29, 2023
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [@angular/animations](https://github.com/angular/angular) | dependencies | patch | [`15.2.0` -> `15.2.4`](https://renovatebot.com/diffs/npm/@angular%2fanimations/15.2.0/15.2.4) |
| [@angular/common](https://github.com/angular/angular) | dependencies | patch | [`15.2.0` -> `15.2.4`](https://renovatebot.com/diffs/npm/@angular%2fcommon/15.2.0/15.2.4) |
| [@angular/compiler](https://github.com/angular/angular) | dependencies | patch | [`15.2.0` -> `15.2.4`](https://renovatebot.com/diffs/npm/@angular%2fcompiler/15.2.0/15.2.4) |
| [@angular/compiler-cli](https://github.com/angular/angular/tree/main/packages/compiler-cli) ([source](https://github.com/angular/angular)) | devDependencies | patch | [`15.2.0` -> `15.2.4`](https://renovatebot.com/diffs/npm/@angular%2fcompiler-cli/15.2.0/15.2.4) |
| [@angular/core](https://github.com/angular/angular) | dependencies | patch | [`15.2.0` -> `15.2.4`](https://renovatebot.com/diffs/npm/@angular%2fcore/15.2.0/15.2.4) |
| [@angular/forms](https://github.com/angular/angular) | dependencies | patch | [`15.2.0` -> `15.2.4`](https://renovatebot.com/diffs/npm/@angular%2fforms/15.2.0/15.2.4) |
| [@angular/platform-browser](https://github.com/angular/angular) | dependencies | patch | [`15.2.0` -> `15.2.4`](https://renovatebot.com/diffs/npm/@angular%2fplatform-browser/15.2.0/15.2.4) |
| [@angular/platform-browser-dynamic](https://github.com/angular/angular) | dependencies | patch | [`15.2.0` -> `15.2.4`](https://renovatebot.com/diffs/npm/@angular%2fplatform-browser-dynamic/15.2.0/15.2.4) |
| [zone.js](https://github.com/angular/angular) ([changelog](https://github.com/angular/angular/blob/master/packages/zone.js/CHANGELOG.md)) | dependencies | minor | [`0.12.0` -> `0.13.0`](https://renovatebot.com/diffs/npm/zone.js/0.12.0/0.13.0) |

---

### Release Notes

<details>
<summary>angular/angular (@&#8203;angular/animations)</summary>

### [`v15.2.4`](https://github.com/angular/angular/blob/HEAD/CHANGELOG.md#&#8203;1524-2023-03-22)

[Compare Source](angular/angular@15.2.3...15.2.4)

##### core

| Commit | Type | Description |
| -- | -- | -- |
| [bae6b5ceb1](angular/angular@bae6b5c) | fix | Allow `TestBed.configureTestingModule` to work with recursive cycle of standalone components. ([#&#8203;49473](angular/angular#49473)) |
| [087f4412af](angular/angular@087f441) | fix | more accurate matching of classes during content projection ([#&#8203;48888](angular/angular#48888)) |

#### Special Thanks

Aditya Srinivasan, Alex Rickabaugh, Andrew Scott, Kristiyan Kostadinov, Masaoki Kobayashi, Matthieu Riegler, Paul Gschwendtner, Peter Götz, Thomas Pischke, Virginia Dooley and avmaxim

<!-- CHANGELOG SPLIT MARKER -->

### [`v15.2.3`](https://github.com/angular/angular/blob/HEAD/CHANGELOG.md#&#8203;1523-2023-03-16)

[Compare Source](angular/angular@15.2.2...15.2.3)

#### Special Thanks

Alan Agius, Esteban Gehring, Matthieu Riegler and Virginia Dooley

<!-- CHANGELOG SPLIT MARKER -->

### [`v15.2.2`](https://github.com/angular/angular/blob/HEAD/CHANGELOG.md#&#8203;1522-2023-03-08)

[Compare Source](angular/angular@15.2.1...15.2.2)

##### migrations

| Commit | Type | Description |
| -- | -- | -- |
| [6207d6f1f0](angular/angular@6207d6f) | fix | add protractor support if protractor imports are detected ([#&#8203;49274](angular/angular#49274)) |

#### Special Thanks

Alan Agius, Andrew Kushnir, Andrew Scott, Kristiyan Kostadinov, Matthieu Riegler, Paul Gschwendtner, Sai Kartheek Bommisetty and Vinit Neogi

<!-- CHANGELOG SPLIT MARKER -->

### [`v15.2.1`](https://github.com/angular/angular/blob/HEAD/CHANGELOG.md#&#8203;1521-2023-03-01)

[Compare Source](angular/angular@15.2.0...15.2.1)

##### common

| Commit | Type | Description |
| -- | -- | -- |
| [f0e926074d](angular/angular@f0e9260) | fix | make Location.normalize() return the correct path when the base path contains characters that interfere with regex syntax. ([#&#8203;49181](angular/angular#49181)) |

##### compiler-cli

| Commit | Type | Description |
| -- | -- | -- |
| [04d8b6c61a](angular/angular@04d8b6c) | fix | do not persist component analysis if template/styles are missing ([#&#8203;49184](angular/angular#49184)) |

##### core

| Commit | Type | Description |
| -- | -- | -- |
| [d60ea6ab5a](angular/angular@d60ea6a) | fix | update zone.js peerDependencies ranges ([#&#8203;49244](angular/angular#49244)) |

##### migrations

| Commit | Type | Description |
| -- | -- | -- |
| [44d095a61c](angular/angular@44d095a) | fix | avoid migrating the same class multiple times in standalone migration ([#&#8203;49245](angular/angular#49245)) |
| [92b0bda9e4](angular/angular@92b0bda) | fix | delete barrel exports in standalone migration ([#&#8203;49176](angular/angular#49176)) |

##### router

| Commit | Type | Description |
| -- | -- | -- |
| [3062442728](angular/angular@3062442) | fix | add error message when using loadComponent with a NgModule ([#&#8203;49164](angular/angular#49164)) |

#### Special Thanks

Alan Agius, Andrew Kushnir, Aristeidis Bampakos, Craig Spence, Doug Parker, Iván Navarro, Joey Perrott, Kristiyan Kostadinov, Matthieu Riegler, Michael Ziluck, Paul Gschwendtner, Stephanie Tuerk, Vincent and Virginia Dooley

<!-- CHANGELOG SPLIT MARKER -->

</details>

<details>
<summary>angular/angular (zone.js)</summary>

### [`v0.13.0`](angular/angular@zone.js-0.12.0...zone.js-0.13.0)

[Compare Source](angular/angular@zone.js-0.12.0...zone.js-0.13.0)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://github.com/renovatebot/renovate/discussions) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNC4xNTQuMSIsInVwZGF0ZWRJblZlciI6IjM1LjI0LjUifQ==-->

Co-authored-by: cabr2-bot <cabr2.help@gmail.com>
Reviewed-on: https://codeberg.org/Calciumdibromid/CaBr2/pulls/1801
Reviewed-by: Epsilon_02 <epsilon_02@noreply.codeberg.org>
Co-authored-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>
Co-committed-by: Calciumdibromid Bot <cabr2_bot@noreply.codeberg.org>
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Apr 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker area: core Issues related to the framework runtime core: content projection merge: caretaker note Alert the caretaker performing the merge to check the PR for an out of normal action needed or note target: patch This PR is targeted for the next patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants