Skip to content

Commit 7dd8bfa

Browse files
authored
fix(eslint-plugin-template): [prefer-self-closing-tags] do not remove HTML-encoded whitespace (#2229)
1 parent 8ae541c commit 7dd8bfa

File tree

3 files changed

+65
-6
lines changed

3 files changed

+65
-6
lines changed

packages/eslint-plugin-template/docs/rules/prefer-self-closing-tags.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,58 @@ The rule does not have any configuration options.
641641
<app-root></app-root>
642642
```
643643

644+
<br>
645+
646+
---
647+
648+
<br>
649+
650+
#### Default Config
651+
652+
```json
653+
{
654+
"rules": {
655+
"@angular-eslint/template/prefer-self-closing-tags": [
656+
"error"
657+
]
658+
}
659+
}
660+
```
661+
662+
<br>
663+
664+
#### ✅ Valid Code
665+
666+
```html
667+
<ng-container>&nbsp;</ng-container>
668+
```
669+
670+
<br>
671+
672+
---
673+
674+
<br>
675+
676+
#### Default Config
677+
678+
```json
679+
{
680+
"rules": {
681+
"@angular-eslint/template/prefer-self-closing-tags": [
682+
"error"
683+
]
684+
}
685+
}
686+
```
687+
688+
<br>
689+
690+
#### ✅ Valid Code
691+
692+
```html
693+
<my-component> <!-- not empty --> </ng-container>
694+
```
695+
644696
</details>
645697

646698
<br>

packages/eslint-plugin-template/src/rules/prefer-self-closing-tags.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import type {
22
TmplAstContent,
33
TmplAstElement,
44
TmplAstTemplate,
5-
TmplAstText,
65
} from '@angular-eslint/bundled-angular-compiler';
6+
import { TmplAstText } from '@angular-eslint/bundled-angular-compiler';
77
import { getTemplateParserServices } from '@angular-eslint/utils';
88
import { createESLintRule } from '../utils/create-eslint-rule';
99
import { getDomElements } from '../utils/get-dom-elements';
@@ -60,12 +60,17 @@ export default createESLintRule<Options, MessageIds>({
6060
const noContent =
6161
!children.length ||
6262
children.every((node) => {
63-
const text = (node as TmplAstText).value;
64-
65-
// If the node has no value, or only whitespace,
66-
// we can consider it empty.
63+
// If the node is only whitespace, we can consider it empty.
64+
// We need to look at the text from the source code, rather
65+
// than the `TmplAstText.value` property. The `value` property
66+
// contains the HTML-decoded value, so if the raw text contains
67+
// `&nbsp;`, that is decoded to a space, but we don't want to
68+
// treat that as empty text.
6769
return (
68-
typeof text === 'string' && text.replace(/\n/g, '').trim() === ''
70+
node instanceof TmplAstText &&
71+
context.sourceCode.text
72+
.slice(node.sourceSpan.start.offset, node.sourceSpan.end.offset)
73+
.trim() === ''
6974
);
7075
});
7176
const noCloseTag =

packages/eslint-plugin-template/tests/rules/prefer-self-closing-tags/cases.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export const valid: readonly (string | ValidTestCase<Options>)[] = [
3434
<p>Fallback content</p>
3535
</ng-content>`,
3636
{ code: '<app-root></app-root>', filename: 'src/index.html' },
37+
'<ng-container>&nbsp;</ng-container>',
38+
'<my-component> <!-- not empty --> </ng-container>',
3739
];
3840

3941
export const invalid: readonly InvalidTestCase<MessageIds, Options>[] = [

0 commit comments

Comments
 (0)