@angular-eslint/eslint-plugin-template 16.0.4-alpha.3 → 16.0.4-alpha.34
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.
package/dist/configs/all.json
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"@angular-eslint/template/no-interpolation-in-attributes": "error",
|
|
25
25
|
"@angular-eslint/template/no-negated-async": "error",
|
|
26
26
|
"@angular-eslint/template/no-positive-tabindex": "error",
|
|
27
|
+
"@angular-eslint/template/prefer-self-closing-tags": "error",
|
|
27
28
|
"@angular-eslint/template/role-has-required-aria": "error",
|
|
28
29
|
"@angular-eslint/template/table-scope": "error",
|
|
29
30
|
"@angular-eslint/template/use-track-by-function": "error",
|
package/dist/index.js
CHANGED
|
@@ -52,6 +52,7 @@ const no_inline_styles_1 = __importStar(require("./rules/no-inline-styles"));
|
|
|
52
52
|
const no_interpolation_in_attributes_1 = __importStar(require("./rules/no-interpolation-in-attributes"));
|
|
53
53
|
const no_negated_async_1 = __importStar(require("./rules/no-negated-async"));
|
|
54
54
|
const no_positive_tabindex_1 = __importStar(require("./rules/no-positive-tabindex"));
|
|
55
|
+
const prefer_self_closing_tags_1 = __importStar(require("./rules/prefer-self-closing-tags"));
|
|
55
56
|
const role_has_required_aria_1 = __importStar(require("./rules/role-has-required-aria"));
|
|
56
57
|
const table_scope_1 = __importStar(require("./rules/table-scope"));
|
|
57
58
|
const use_track_by_function_1 = __importStar(require("./rules/use-track-by-function"));
|
|
@@ -87,6 +88,7 @@ module.exports = {
|
|
|
87
88
|
[no_interpolation_in_attributes_1.RULE_NAME]: no_interpolation_in_attributes_1.default,
|
|
88
89
|
[no_negated_async_1.RULE_NAME]: no_negated_async_1.default,
|
|
89
90
|
[no_positive_tabindex_1.RULE_NAME]: no_positive_tabindex_1.default,
|
|
91
|
+
[prefer_self_closing_tags_1.RULE_NAME]: prefer_self_closing_tags_1.default,
|
|
90
92
|
[role_has_required_aria_1.RULE_NAME]: role_has_required_aria_1.default,
|
|
91
93
|
[table_scope_1.RULE_NAME]: table_scope_1.default,
|
|
92
94
|
[use_track_by_function_1.RULE_NAME]: use_track_by_function_1.default,
|
|
@@ -146,9 +146,13 @@ function byLocation(one, other) {
|
|
|
146
146
|
}
|
|
147
147
|
function byOrder(order, alphabetical) {
|
|
148
148
|
return function (one, other) {
|
|
149
|
+
var _a, _b, _c, _d;
|
|
149
150
|
const orderComparison = getOrderIndex(one, order) - getOrderIndex(other, order);
|
|
150
151
|
if (alphabetical && orderComparison === 0) {
|
|
151
|
-
return one.
|
|
152
|
+
return ((_b = (_a = one.keySpan) === null || _a === void 0 ? void 0 : _a.details) !== null && _b !== void 0 ? _b : one.name) >
|
|
153
|
+
((_d = (_c = other.keySpan) === null || _c === void 0 ? void 0 : _c.details) !== null && _d !== void 0 ? _d : other.name)
|
|
154
|
+
? 1
|
|
155
|
+
: -1;
|
|
152
156
|
}
|
|
153
157
|
return orderComparison;
|
|
154
158
|
};
|
|
@@ -175,9 +179,27 @@ function toTemplateReferenceVariableOrderType(reference) {
|
|
|
175
179
|
return Object.assign(Object.assign({}, reference), { orderType: "TEMPLATE_REFERENCE" /* OrderType.TemplateReferenceVariable */ });
|
|
176
180
|
}
|
|
177
181
|
function extractTemplateAttrs(node) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
182
|
+
if (!isTmplAstTemplate(node.parent)) {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
/*
|
|
186
|
+
* There may be multiple "attributes" for a structural directive even though
|
|
187
|
+
* there is only a single HTML attribute:
|
|
188
|
+
* e.g. `<ng-container *ngFor="let foo of bar"></ng-container>`
|
|
189
|
+
* will parsed as two attributes (`ngFor` and `ngForOf`)
|
|
190
|
+
*/
|
|
191
|
+
const attrs = node.parent.templateAttrs.map(toStructuralDirectiveOrderType);
|
|
192
|
+
// Pick up on any subsequent `let` bindings, e.g. `index as i`
|
|
193
|
+
let sourceEnd = attrs[attrs.length - 1].sourceSpan.end;
|
|
194
|
+
node.parent.variables.forEach((v) => {
|
|
195
|
+
if (v.sourceSpan.start.offset <= sourceEnd.offset &&
|
|
196
|
+
sourceEnd.offset < v.sourceSpan.end.offset) {
|
|
197
|
+
sourceEnd = v.sourceSpan.end;
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
return [
|
|
201
|
+
Object.assign(Object.assign({}, attrs[0]), { sourceSpan: new bundled_angular_compiler_1.ParseSourceSpan(attrs[0].sourceSpan.start, sourceEnd) }),
|
|
202
|
+
];
|
|
181
203
|
}
|
|
182
204
|
function normalizeInputsOutputs(inputs, outputs) {
|
|
183
205
|
const extractedInputs = inputs
|
|
@@ -200,19 +222,21 @@ function isOnSameLocation(input, output) {
|
|
|
200
222
|
input.sourceSpan.end === output.sourceSpan.end);
|
|
201
223
|
}
|
|
202
224
|
function getMessageName(expected) {
|
|
225
|
+
var _a, _b;
|
|
226
|
+
const fullName = (_b = (_a = expected.keySpan) === null || _a === void 0 ? void 0 : _a.details) !== null && _b !== void 0 ? _b : expected.name;
|
|
203
227
|
switch (expected.orderType) {
|
|
204
228
|
case "STRUCTURAL_DIRECTIVE" /* OrderType.StructuralDirective */:
|
|
205
|
-
return `*${
|
|
229
|
+
return `*${fullName}`;
|
|
206
230
|
case "TEMPLATE_REFERENCE" /* OrderType.TemplateReferenceVariable */:
|
|
207
|
-
return `#${
|
|
231
|
+
return `#${fullName}`;
|
|
208
232
|
case "INPUT_BINDING" /* OrderType.InputBinding */:
|
|
209
|
-
return `[${
|
|
233
|
+
return `[${fullName}]`;
|
|
210
234
|
case "OUTPUT_BINDING" /* OrderType.OutputBinding */:
|
|
211
|
-
return `(${
|
|
235
|
+
return `(${fullName})`;
|
|
212
236
|
case "TWO_WAY_BINDING" /* OrderType.TwoWayBinding */:
|
|
213
|
-
return `[(${
|
|
237
|
+
return `[(${fullName})]`;
|
|
214
238
|
default:
|
|
215
|
-
return
|
|
239
|
+
return fullName;
|
|
216
240
|
}
|
|
217
241
|
}
|
|
218
242
|
function getStartPos(expected) {
|
|
@@ -7,6 +7,7 @@ const get_original_attribute_name_1 = require("../utils/get-original-attribute-n
|
|
|
7
7
|
exports.RULE_NAME = 'no-duplicate-attributes';
|
|
8
8
|
const DEFAULT_OPTIONS = {
|
|
9
9
|
allowTwoWayDataBinding: true,
|
|
10
|
+
allowStylePrecedenceDuplicates: false,
|
|
10
11
|
ignore: [],
|
|
11
12
|
};
|
|
12
13
|
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
@@ -27,6 +28,11 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
27
28
|
default: DEFAULT_OPTIONS.allowTwoWayDataBinding,
|
|
28
29
|
description: `Whether or not two-way data binding is allowed as an exception to the rule.`,
|
|
29
30
|
},
|
|
31
|
+
allowStylePrecedenceDuplicates: {
|
|
32
|
+
type: 'boolean',
|
|
33
|
+
default: DEFAULT_OPTIONS.allowStylePrecedenceDuplicates,
|
|
34
|
+
description: `Whether or not Angular style precedence is allowed as an exception to the rule. See https://angular.io/guide/style-precedence#style-precedence`,
|
|
35
|
+
},
|
|
30
36
|
ignore: {
|
|
31
37
|
type: 'array',
|
|
32
38
|
items: { type: 'string' },
|
|
@@ -44,14 +50,37 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
44
50
|
},
|
|
45
51
|
},
|
|
46
52
|
defaultOptions: [DEFAULT_OPTIONS],
|
|
47
|
-
create(context, [{ allowTwoWayDataBinding, ignore }]) {
|
|
53
|
+
create(context, [{ allowTwoWayDataBinding, allowStylePrecedenceDuplicates, ignore }]) {
|
|
48
54
|
const parserServices = (0, utils_1.getTemplateParserServices)(context);
|
|
49
55
|
return {
|
|
50
56
|
Element$1({ inputs, outputs, attributes }) {
|
|
51
|
-
|
|
57
|
+
// According to the Angular documentation (https://angular.io/guide/style-precedence#style-precedence)
|
|
58
|
+
// Angular merges both attributes which means their combined use can be seen as valid
|
|
59
|
+
const angularStylePrecedenceDuplicatesAllowed = ['class', 'style'];
|
|
60
|
+
let duplicateInputsAndAttributes = findDuplicates([
|
|
52
61
|
...inputs,
|
|
53
62
|
...attributes,
|
|
54
63
|
]);
|
|
64
|
+
if (allowStylePrecedenceDuplicates) {
|
|
65
|
+
const inputsIgnored = inputs.filter((input) => angularStylePrecedenceDuplicatesAllowed.includes((0, get_original_attribute_name_1.getOriginalAttributeName)(input)));
|
|
66
|
+
if ((inputsIgnored === null || inputsIgnored === void 0 ? void 0 : inputsIgnored.length) > 0) {
|
|
67
|
+
const attributesIgnored = attributes.filter((attr) => angularStylePrecedenceDuplicatesAllowed.includes((0, get_original_attribute_name_1.getOriginalAttributeName)(attr)));
|
|
68
|
+
const inputsNotIgnored = inputs.filter((input) => !inputsIgnored.includes(input));
|
|
69
|
+
const attributesNotIgnored = attributes.filter((attr) => !attributesIgnored.includes(attr));
|
|
70
|
+
const ignoreDuplicated = [
|
|
71
|
+
...findDuplicates(inputsIgnored),
|
|
72
|
+
...findDuplicates(attributesIgnored),
|
|
73
|
+
];
|
|
74
|
+
const notIgnoredDuplicates = [
|
|
75
|
+
...findDuplicates(inputsNotIgnored),
|
|
76
|
+
...findDuplicates(attributesNotIgnored),
|
|
77
|
+
];
|
|
78
|
+
duplicateInputsAndAttributes = [
|
|
79
|
+
...ignoreDuplicated,
|
|
80
|
+
...notIgnoredDuplicates,
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
55
84
|
const filteredOutputs = allowTwoWayDataBinding
|
|
56
85
|
? outputs.filter((output) => {
|
|
57
86
|
return !inputs.some((input) => input.sourceSpan.start === output.sourceSpan.start &&
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RULE_NAME = exports.MESSAGE_ID = void 0;
|
|
4
|
+
const utils_1 = require("@angular-eslint/utils");
|
|
5
|
+
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
6
|
+
const get_dom_elements_1 = require("../utils/get-dom-elements");
|
|
7
|
+
exports.MESSAGE_ID = 'preferSelfClosingTags';
|
|
8
|
+
exports.RULE_NAME = 'prefer-self-closing-tags';
|
|
9
|
+
exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
10
|
+
name: exports.RULE_NAME,
|
|
11
|
+
meta: {
|
|
12
|
+
type: 'layout',
|
|
13
|
+
docs: {
|
|
14
|
+
description: 'Ensures that self-closing tags are used for elements with a closing tag but no content.',
|
|
15
|
+
recommended: false,
|
|
16
|
+
},
|
|
17
|
+
fixable: 'code',
|
|
18
|
+
schema: [],
|
|
19
|
+
messages: {
|
|
20
|
+
[exports.MESSAGE_ID]: 'Use self-closing tags for elements with a closing tag but no content.',
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
defaultOptions: [],
|
|
24
|
+
create(context) {
|
|
25
|
+
const parserServices = (0, utils_1.getTemplateParserServices)(context);
|
|
26
|
+
return {
|
|
27
|
+
Element$1({ children, name, startSourceSpan, endSourceSpan, }) {
|
|
28
|
+
// Ignore native elements.
|
|
29
|
+
if ((0, get_dom_elements_1.getDomElements)().has(name)) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const noContent = !children.length ||
|
|
33
|
+
children.every((node) => {
|
|
34
|
+
const text = node.value;
|
|
35
|
+
// If the node has no value, or only whitespace,
|
|
36
|
+
// we can consider it empty.
|
|
37
|
+
return (typeof text === 'string' && text.replace(/\n/g, '').trim() === '');
|
|
38
|
+
});
|
|
39
|
+
const noCloseTag = !endSourceSpan ||
|
|
40
|
+
(startSourceSpan.start.offset === endSourceSpan.start.offset &&
|
|
41
|
+
startSourceSpan.end.offset === endSourceSpan.end.offset);
|
|
42
|
+
if (!noContent || noCloseTag) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
context.report({
|
|
46
|
+
loc: parserServices.convertNodeSourceSpanToLoc(endSourceSpan),
|
|
47
|
+
messageId: exports.MESSAGE_ID,
|
|
48
|
+
fix: (fixer) => fixer.replaceTextRange([startSourceSpan.end.offset - 1, endSourceSpan.end.offset], ' />'),
|
|
49
|
+
});
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
});
|
package/dist/rules/valid-aria.js
CHANGED
|
@@ -98,6 +98,12 @@ function isNil(value) {
|
|
|
98
98
|
function isString(value) {
|
|
99
99
|
return typeof value == 'string';
|
|
100
100
|
}
|
|
101
|
+
function isMixed(value) {
|
|
102
|
+
return isString(value) && value === 'mixed';
|
|
103
|
+
}
|
|
104
|
+
function isTristate(value) {
|
|
105
|
+
return isMixed(value) || isBooleanLike(value) || isNil(value);
|
|
106
|
+
}
|
|
101
107
|
function isValidAriaPropertyValue({ allowundefined, type, values }, attributeValue) {
|
|
102
108
|
if (allowundefined && isNil(attributeValue))
|
|
103
109
|
return true;
|
|
@@ -105,7 +111,7 @@ function isValidAriaPropertyValue({ allowundefined, type, values }, attributeVal
|
|
|
105
111
|
case 'boolean':
|
|
106
112
|
return isBooleanLike(attributeValue);
|
|
107
113
|
case 'tristate':
|
|
108
|
-
return
|
|
114
|
+
return isTristate(attributeValue);
|
|
109
115
|
case 'id':
|
|
110
116
|
case 'idlist':
|
|
111
117
|
return true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-eslint/eslint-plugin-template",
|
|
3
|
-
"version": "16.0.4-alpha.
|
|
3
|
+
"version": "16.0.4-alpha.34+93464ca",
|
|
4
4
|
"description": "ESLint plugin for Angular Templates",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,11 +17,11 @@
|
|
|
17
17
|
"LICENSE"
|
|
18
18
|
],
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@angular-eslint/bundled-angular-compiler": "16.0.4-alpha.
|
|
21
|
-
"@angular-eslint/utils": "16.0.4-alpha.
|
|
22
|
-
"@typescript-eslint/type-utils": "5.59.
|
|
23
|
-
"@typescript-eslint/utils": "5.59.
|
|
24
|
-
"aria-query": "5.
|
|
20
|
+
"@angular-eslint/bundled-angular-compiler": "16.0.4-alpha.34+93464ca",
|
|
21
|
+
"@angular-eslint/utils": "16.0.4-alpha.34+93464ca",
|
|
22
|
+
"@typescript-eslint/type-utils": "5.59.9",
|
|
23
|
+
"@typescript-eslint/utils": "5.59.9",
|
|
24
|
+
"aria-query": "5.3.0",
|
|
25
25
|
"axobject-query": "3.1.1"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
@@ -31,5 +31,5 @@
|
|
|
31
31
|
"eslint": "^7.20.0 || ^8.0.0",
|
|
32
32
|
"typescript": "*"
|
|
33
33
|
},
|
|
34
|
-
"gitHead": "
|
|
34
|
+
"gitHead": "93464ca55f7938a3bd99c7560d05ca24bea1e7c7"
|
|
35
35
|
}
|