@angular-eslint/eslint-plugin-template 17.0.0-alpha.0 → 17.0.1-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -23,35 +23,90 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
23
23
|
create(context) {
|
|
24
24
|
const parserServices = (0, utils_1.getTemplateParserServices)(context);
|
|
25
25
|
return {
|
|
26
|
-
Element$1
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
return;
|
|
26
|
+
'Element$1, Template, Content'(node) {
|
|
27
|
+
if (isContentNode(node)) {
|
|
28
|
+
processContentNode(node);
|
|
30
29
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
38
|
-
const noCloseTag = !endSourceSpan ||
|
|
39
|
-
(startSourceSpan.start.offset === endSourceSpan.start.offset &&
|
|
40
|
-
startSourceSpan.end.offset === endSourceSpan.end.offset);
|
|
41
|
-
if (!noContent || noCloseTag) {
|
|
42
|
-
return;
|
|
30
|
+
else {
|
|
31
|
+
// Ignore native elements.
|
|
32
|
+
if ('name' in node && (0, get_dom_elements_1.getDomElements)().has(node.name)) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
processElementOrTemplateNode(node);
|
|
43
36
|
}
|
|
44
|
-
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
function processElementOrTemplateNode(node) {
|
|
40
|
+
const { children, startSourceSpan, endSourceSpan } = node;
|
|
41
|
+
const noContent = !children.length ||
|
|
42
|
+
children.every((node) => {
|
|
43
|
+
const text = node.value;
|
|
44
|
+
// If the node has no value, or only whitespace,
|
|
45
|
+
// we can consider it empty.
|
|
46
|
+
return (typeof text === 'string' && text.replace(/\n/g, '').trim() === '');
|
|
47
|
+
});
|
|
48
|
+
const noCloseTag = !endSourceSpan ||
|
|
49
|
+
(startSourceSpan.start.offset === endSourceSpan.start.offset &&
|
|
50
|
+
startSourceSpan.end.offset === endSourceSpan.end.offset);
|
|
51
|
+
if (!noContent || noCloseTag) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// HTML tags always have more than two characters
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
56
|
+
const openingTagLastChar = startSourceSpan.toString().at(-2);
|
|
57
|
+
const closingTagPrefix = getClosingTagPrefix(openingTagLastChar);
|
|
58
|
+
context.report({
|
|
59
|
+
loc: parserServices.convertNodeSourceSpanToLoc(endSourceSpan),
|
|
60
|
+
messageId: exports.MESSAGE_ID,
|
|
61
|
+
fix: (fixer) => fixer.replaceTextRange([startSourceSpan.end.offset - 1, endSourceSpan.end.offset], closingTagPrefix + '/>'),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function processContentNode(node) {
|
|
65
|
+
var _a, _b;
|
|
66
|
+
const { sourceSpan } = node;
|
|
67
|
+
const ngContentCloseTag = '</ng-content>';
|
|
68
|
+
if (sourceSpan.toString().includes(ngContentCloseTag)) {
|
|
69
|
+
// content nodes can only contain whitespaces
|
|
70
|
+
const content = (_b = (_a = sourceSpan
|
|
71
|
+
.toString()
|
|
72
|
+
.match(/>(\s*)</m)) === null || _a === void 0 ? void 0 : _a.at(1)) !== null && _b !== void 0 ? _b : '';
|
|
73
|
+
const openingTagLastChar =
|
|
74
|
+
// This is more than the minimum length of a ng-content element
|
|
45
75
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
76
|
+
sourceSpan
|
|
77
|
+
.toString()
|
|
78
|
+
.at(-2 - ngContentCloseTag.length - content.length);
|
|
79
|
+
const closingTagPrefix = getClosingTagPrefix(openingTagLastChar);
|
|
49
80
|
context.report({
|
|
50
|
-
|
|
81
|
+
// content nodes don't have information about startSourceSpan and endSourceSpan,
|
|
82
|
+
// so we need to calculate it by our own
|
|
83
|
+
loc: {
|
|
84
|
+
start: {
|
|
85
|
+
line: sourceSpan.end.line + 1,
|
|
86
|
+
column: sourceSpan.end.col - ngContentCloseTag.length,
|
|
87
|
+
},
|
|
88
|
+
end: {
|
|
89
|
+
line: sourceSpan.end.line + 1,
|
|
90
|
+
column: sourceSpan.end.col,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
51
93
|
messageId: exports.MESSAGE_ID,
|
|
52
|
-
fix: (fixer) => fixer.replaceTextRange([
|
|
94
|
+
fix: (fixer) => fixer.replaceTextRange([
|
|
95
|
+
sourceSpan.end.offset -
|
|
96
|
+
ngContentCloseTag.length -
|
|
97
|
+
content.length -
|
|
98
|
+
1,
|
|
99
|
+
sourceSpan.end.offset,
|
|
100
|
+
], closingTagPrefix + '/>'),
|
|
53
101
|
});
|
|
54
|
-
}
|
|
55
|
-
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
56
104
|
},
|
|
57
105
|
});
|
|
106
|
+
function isContentNode(node) {
|
|
107
|
+
return 'name' in node && node.name === 'ng-content';
|
|
108
|
+
}
|
|
109
|
+
function getClosingTagPrefix(openingTagLastChar) {
|
|
110
|
+
const hasOwnWhitespace = openingTagLastChar.trim() === '';
|
|
111
|
+
return hasOwnWhitespace ? '' : ' ';
|
|
112
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-eslint/eslint-plugin-template",
|
|
3
|
-
"version": "17.0.
|
|
3
|
+
"version": "17.0.1-alpha.0+4eddce1",
|
|
4
4
|
"description": "ESLint plugin for Angular Templates",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,19 +17,19 @@
|
|
|
17
17
|
"LICENSE"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@angular-eslint/bundled-angular-compiler": "17.0.
|
|
21
|
-
"@angular-eslint/utils": "17.0.
|
|
20
|
+
"@angular-eslint/bundled-angular-compiler": "17.0.1-alpha.0+4eddce1",
|
|
21
|
+
"@angular-eslint/utils": "17.0.1-alpha.0+4eddce1",
|
|
22
22
|
"@typescript-eslint/type-utils": "6.10.0",
|
|
23
23
|
"@typescript-eslint/utils": "6.10.0",
|
|
24
24
|
"aria-query": "5.3.0",
|
|
25
|
-
"axobject-query": "
|
|
25
|
+
"axobject-query": "4.0.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@types/aria-query": "5.0.
|
|
28
|
+
"@types/aria-query": "5.0.4"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"eslint": "^7.20.0 || ^8.0.0",
|
|
32
32
|
"typescript": "*"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "4eddce1ede6e3baccb297dcbb5b980af95126f64"
|
|
35
35
|
}
|