@angular-eslint/eslint-plugin-template 22.3.1-alpha.1 → 22.3.1-alpha.3
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/rules/button-has-type.d.ts.map +1 -1
- package/dist/rules/button-has-type.js +4 -16
- package/dist/rules/click-events-have-key-events.d.ts.map +1 -1
- package/dist/rules/click-events-have-key-events.js +4 -16
- package/dist/rules/prefer-self-closing-tags.d.ts.map +1 -1
- package/dist/rules/prefer-self-closing-tags.js +11 -1
- package/dist/utils/has-ignored-directive.d.ts +11 -0
- package/dist/utils/has-ignored-directive.d.ts.map +1 -0
- package/dist/utils/has-ignored-directive.js +28 -0
- package/dist/utils/literal-primitive.d.ts.map +1 -1
- package/dist/utils/literal-primitive.js +6 -1
- package/package.json +5 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"button-has-type.d.ts","sourceRoot":"","sources":["../../src/rules/button-has-type.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"button-has-type.d.ts","sourceRoot":"","sources":["../../src/rules/button-has-type.ts"],"names":[],"mappings":"AAYA,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC1C;CACF,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,aAAa,GAAG,aAAa,CAAC;AACvD,eAAO,MAAM,SAAS,oBAAoB,CAAC;AAI3C,eAAO,MAAM,qBAAqB,SAAS,CAAC;;;;AAW5C,wBA+DG;AAEH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -4,6 +4,7 @@ exports.RULE_DOCS_EXTENSION = exports.INVALID_TYPE_DATA_KEY = exports.RULE_NAME
|
|
|
4
4
|
const bundled_angular_compiler_1 = require("@angular-eslint/bundled-angular-compiler");
|
|
5
5
|
const utils_1 = require("@angular-eslint/utils");
|
|
6
6
|
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
7
|
+
const has_ignored_directive_1 = require("../utils/has-ignored-directive");
|
|
7
8
|
exports.RULE_NAME = 'button-has-type';
|
|
8
9
|
const DEFAULT_OPTIONS = {
|
|
9
10
|
ignoreWithDirectives: [],
|
|
@@ -27,6 +28,7 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
27
28
|
type: 'array',
|
|
28
29
|
items: { type: 'string' },
|
|
29
30
|
uniqueItems: true,
|
|
31
|
+
description: 'Directive names that, when present on the element, cause it to be ignored. Entries wrapped in slashes, e.g. `/^tui/`, are treated as regular expressions.',
|
|
30
32
|
default: DEFAULT_OPTIONS.ignoreWithDirectives,
|
|
31
33
|
},
|
|
32
34
|
},
|
|
@@ -41,10 +43,11 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
41
43
|
},
|
|
42
44
|
create(context, [{ ignoreWithDirectives }]) {
|
|
43
45
|
const parserServices = (0, utils_1.getTemplateParserServices)(context);
|
|
46
|
+
const hasIgnoredDirective = (0, has_ignored_directive_1.createIgnoredDirectiveMatcher)(ignoreWithDirectives);
|
|
44
47
|
return {
|
|
45
48
|
[`Element[name=/^(button)$/i]`](element) {
|
|
46
49
|
if (!isTypeAttributePresentInElement(element)) {
|
|
47
|
-
if (!
|
|
50
|
+
if (!hasIgnoredDirective(element)) {
|
|
48
51
|
context.report({
|
|
49
52
|
loc: parserServices.convertNodeSourceSpanToLoc(element.sourceSpan),
|
|
50
53
|
messageId: 'missingType',
|
|
@@ -71,21 +74,6 @@ exports.RULE_DOCS_EXTENSION = {
|
|
|
71
74
|
function isTypeAttributePresentInElement({ inputs, attributes, }) {
|
|
72
75
|
return [...inputs, ...attributes].some(({ name }) => name === TYPE_ATTRIBUTE_NAME);
|
|
73
76
|
}
|
|
74
|
-
function isIgnored(ignoreWithDirectives, { inputs, attributes }) {
|
|
75
|
-
if (ignoreWithDirectives && ignoreWithDirectives.length > 0) {
|
|
76
|
-
for (const input of inputs) {
|
|
77
|
-
if (ignoreWithDirectives.includes(input.name)) {
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
for (const attribute of attributes) {
|
|
82
|
-
if (ignoreWithDirectives.includes(attribute.name)) {
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return false;
|
|
88
|
-
}
|
|
89
77
|
function getInvalidButtonTypeIfPresent(element) {
|
|
90
78
|
const invalidTextAttribute = element.attributes.find(({ name, value }) => name === TYPE_ATTRIBUTE_NAME && !VALID_BUTTON_TYPES.includes(value));
|
|
91
79
|
if (invalidTextAttribute) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"click-events-have-key-events.d.ts","sourceRoot":"","sources":["../../src/rules/click-events-have-key-events.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"click-events-have-key-events.d.ts","sourceRoot":"","sources":["../../src/rules/click-events-have-key-events.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,OAAO,GAAG;IACpB;QACE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;QACzC,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;QAClC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;KACrC;CACF,CAAC;AACF,MAAM,MAAM,UAAU,GAClB,0BAA0B,GAC1B,wBAAwB,GACxB,+BAA+B,CAAC;AACpC,eAAO,MAAM,SAAS,iCAAiC,CAAC;;;;AASxD,wBAmIG;AAqBH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RULE_DOCS_EXTENSION = exports.RULE_NAME = void 0;
|
|
4
4
|
const utils_1 = require("@angular-eslint/utils");
|
|
5
5
|
const create_eslint_rule_1 = require("../utils/create-eslint-rule");
|
|
6
|
+
const has_ignored_directive_1 = require("../utils/has-ignored-directive");
|
|
6
7
|
const get_dom_elements_1 = require("../utils/get-dom-elements");
|
|
7
8
|
const is_hidden_from_screen_reader_1 = require("../utils/is-hidden-from-screen-reader");
|
|
8
9
|
const is_interactive_element_1 = require("../utils/is-interactive-element");
|
|
@@ -30,6 +31,7 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
30
31
|
type: 'array',
|
|
31
32
|
items: { type: 'string' },
|
|
32
33
|
uniqueItems: true,
|
|
34
|
+
description: 'Directive names that, when present on the element, cause it to be ignored. Entries wrapped in slashes, e.g. `/^tui/`, are treated as regular expressions.',
|
|
33
35
|
default: DEFAULT_OPTIONS.ignoreWithDirectives,
|
|
34
36
|
},
|
|
35
37
|
requireKeyCode: {
|
|
@@ -57,12 +59,13 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
57
59
|
},
|
|
58
60
|
create(context, [{ ignoreWithDirectives, requireKeyCode, allowedKeyCodes }]) {
|
|
59
61
|
const normalizedAllowedKeyCodes = (allowedKeyCodes ?? []).map((keyCode) => keyCode.toLowerCase());
|
|
62
|
+
const hasIgnoredDirective = (0, has_ignored_directive_1.createIgnoredDirectiveMatcher)(ignoreWithDirectives);
|
|
60
63
|
return {
|
|
61
64
|
Element(node) {
|
|
62
65
|
if (!(0, get_dom_elements_1.getDomElements)().has(node.name.toLowerCase())) {
|
|
63
66
|
return;
|
|
64
67
|
}
|
|
65
|
-
if (
|
|
68
|
+
if (hasIgnoredDirective(node) ||
|
|
66
69
|
(0, is_presentation_role_1.isPresentationRole)(node) ||
|
|
67
70
|
(0, is_hidden_from_screen_reader_1.isHiddenFromScreenReader)(node) ||
|
|
68
71
|
(0, is_interactive_element_1.isInherentlyInteractiveElement)(node)) {
|
|
@@ -139,21 +142,6 @@ function getKeyCode({ name }) {
|
|
|
139
142
|
const keyCode = segments[segments.length - 1].toLowerCase();
|
|
140
143
|
return keyCode.length > 0 ? keyCode : undefined;
|
|
141
144
|
}
|
|
142
|
-
function isIgnored(ignoreWithDirectives, { inputs, attributes }) {
|
|
143
|
-
if (ignoreWithDirectives && ignoreWithDirectives.length > 0) {
|
|
144
|
-
for (const input of inputs) {
|
|
145
|
-
if (ignoreWithDirectives.includes(input.name)) {
|
|
146
|
-
return true;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
for (const attribute of attributes) {
|
|
150
|
-
if (ignoreWithDirectives.includes(attribute.name)) {
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
145
|
exports.RULE_DOCS_EXTENSION = {
|
|
158
146
|
rationale: 'Elements with click handlers must also be operable via keyboard for users who cannot use a mouse. This includes users with motor disabilities, users of assistive technologies, and keyboard-only users. While native interactive elements like buttons and links are keyboard-accessible by default (activated by Enter or Space keys), non-interactive elements with click handlers (like divs or spans) are not. For such elements, you must add keyboard event handlers (keyup, keydown, or keypress) that perform the same action as the click handler. Alternatively, use a semantic button element which handles this automatically. This is a WCAG Level A requirement for keyboard accessibility.',
|
|
159
147
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prefer-self-closing-tags.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-self-closing-tags.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,uBAAuB,CAAC;AACjD,eAAO,MAAM,SAAS,6BAA6B,CAAC;;;;AAEpD,wBA+JG;
|
|
1
|
+
{"version":3,"file":"prefer-self-closing-tags.d.ts","sourceRoot":"","sources":["../../src/rules/prefer-self-closing-tags.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,OAAO,GAAG,EAAE,CAAC;AACzB,MAAM,MAAM,UAAU,GAAG,uBAAuB,CAAC;AACjD,eAAO,MAAM,SAAS,6BAA6B,CAAC;;;;AAEpD,wBA+JG;AAoDH,eAAO,MAAM,mBAAmB;;CAG/B,CAAC"}
|
|
@@ -34,7 +34,7 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
34
34
|
}
|
|
35
35
|
else {
|
|
36
36
|
// Ignore native elements.
|
|
37
|
-
if ('name' in node && (
|
|
37
|
+
if ('name' in node && isNativeElement(node.name)) {
|
|
38
38
|
return;
|
|
39
39
|
}
|
|
40
40
|
processElementOrTemplateNode(node);
|
|
@@ -135,6 +135,16 @@ exports.default = (0, create_eslint_rule_1.createESLintRule)({
|
|
|
135
135
|
}
|
|
136
136
|
},
|
|
137
137
|
});
|
|
138
|
+
function isNativeElement(name) {
|
|
139
|
+
const lowerCaseName = name.toLowerCase();
|
|
140
|
+
// The Angular compiler namespaces the children of `<svg>` and `<math>`
|
|
141
|
+
// (e.g. `:svg:use` and `:math:mi`). These are foreign elements that are
|
|
142
|
+
// native to the browser, and self-closing some of them (such as `<title />`)
|
|
143
|
+
// produces a template that Angular cannot parse.
|
|
144
|
+
return (lowerCaseName.startsWith(':svg:') ||
|
|
145
|
+
lowerCaseName.startsWith(':math:') ||
|
|
146
|
+
(0, get_dom_elements_1.getDomElements)().has(lowerCaseName));
|
|
147
|
+
}
|
|
138
148
|
function isContentNode(node) {
|
|
139
149
|
return 'name' in node && node.name.toLowerCase() === 'ng-content';
|
|
140
150
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TmplAstElement } from '@angular-eslint/bundled-angular-compiler';
|
|
2
|
+
/**
|
|
3
|
+
* Compiles an `ignoreWithDirectives` option into a predicate that reports
|
|
4
|
+
* whether an element carries one of the ignored directives.
|
|
5
|
+
*
|
|
6
|
+
* Entries wrapped in slashes, e.g. `/^tui/`, are treated as regular
|
|
7
|
+
* expressions (an optional flags suffix is supported, e.g. `/^tui/i`).
|
|
8
|
+
* All other entries are matched exactly against the directive name.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createIgnoredDirectiveMatcher(ignoreWithDirectives: readonly string[] | undefined): (element: TmplAstElement) => boolean;
|
|
11
|
+
//# sourceMappingURL=has-ignored-directive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"has-ignored-directive.d.ts","sourceRoot":"","sources":["../../src/utils/has-ignored-directive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0CAA0C,CAAC;AAM/E;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAC3C,oBAAoB,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAClD,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAsBtC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createIgnoredDirectiveMatcher = createIgnoredDirectiveMatcher;
|
|
4
|
+
const REGEX_LITERAL_PATTERN = /^\/(.+)\/([a-z]*)$/;
|
|
5
|
+
/**
|
|
6
|
+
* Compiles an `ignoreWithDirectives` option into a predicate that reports
|
|
7
|
+
* whether an element carries one of the ignored directives.
|
|
8
|
+
*
|
|
9
|
+
* Entries wrapped in slashes, e.g. `/^tui/`, are treated as regular
|
|
10
|
+
* expressions (an optional flags suffix is supported, e.g. `/^tui/i`).
|
|
11
|
+
* All other entries are matched exactly against the directive name.
|
|
12
|
+
*/
|
|
13
|
+
function createIgnoredDirectiveMatcher(ignoreWithDirectives) {
|
|
14
|
+
if (!ignoreWithDirectives || ignoreWithDirectives.length === 0) {
|
|
15
|
+
return () => false;
|
|
16
|
+
}
|
|
17
|
+
const matchers = ignoreWithDirectives.map((entry) => {
|
|
18
|
+
const regexLiteral = REGEX_LITERAL_PATTERN.exec(entry);
|
|
19
|
+
if (regexLiteral) {
|
|
20
|
+
const pattern = new RegExp(regexLiteral[1], regexLiteral[2]);
|
|
21
|
+
return (name) => pattern.test(name);
|
|
22
|
+
}
|
|
23
|
+
return (name) => name === entry;
|
|
24
|
+
});
|
|
25
|
+
const isIgnoredName = (name) => matchers.some((matcher) => matcher(name));
|
|
26
|
+
return ({ inputs, attributes }) => inputs.some(({ name }) => isIgnoredName(name)) ||
|
|
27
|
+
attributes.some(({ name }) => isIgnoredName(name));
|
|
28
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"literal-primitive.d.ts","sourceRoot":"","sources":["../../src/utils/literal-primitive.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,EACH,gBAAgB,EACjB,MAAM,0CAA0C,CAAC;AAElD,MAAM,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEpC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,IAAI,gBAAgB,
|
|
1
|
+
{"version":3,"file":"literal-primitive.d.ts","sourceRoot":"","sources":["../../src/utils/literal-primitive.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,EACH,gBAAgB,EACjB,MAAM,0CAA0C,CAAC;AAElD,MAAM,MAAM,KAAK,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;AAEpC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,IAAI,gBAAgB,CAStE;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,GAAG,GACR,IAAI,IAAI,IAAI,CAAC,gBAAgB,EAAE,OAAO,CAAC,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAE7D;AAED,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,KAAK,GACX,MAAM,CAIR"}
|
|
@@ -5,7 +5,12 @@ exports.isStringLiteralPrimitive = isStringLiteralPrimitive;
|
|
|
5
5
|
exports.getLiteralPrimitiveStringValue = getLiteralPrimitiveStringValue;
|
|
6
6
|
const bundled_angular_compiler_1 = require("@angular-eslint/bundled-angular-compiler");
|
|
7
7
|
function isLiteralPrimitive(node) {
|
|
8
|
-
|
|
8
|
+
// The template parser stamps `type` from `constructor.name`. `instanceof`
|
|
9
|
+
// fails when more than one copy of `@angular-eslint/bundled-angular-compiler`
|
|
10
|
+
// is installed (version mismatches, Yarn `hoistingLimits`, etc.).
|
|
11
|
+
return (!!node &&
|
|
12
|
+
(node.type === 'LiteralPrimitive' ||
|
|
13
|
+
node instanceof bundled_angular_compiler_1.LiteralPrimitive));
|
|
9
14
|
}
|
|
10
15
|
function isStringLiteralPrimitive(node) {
|
|
11
16
|
return isLiteralPrimitive(node) && typeof node.value === 'string';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-eslint/eslint-plugin-template",
|
|
3
|
-
"version": "22.3.1-alpha.
|
|
3
|
+
"version": "22.3.1-alpha.3",
|
|
4
4
|
"description": "ESLint plugin for Angular Templates",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,17 +19,17 @@
|
|
|
19
19
|
"LICENSE"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@angular-eslint/bundled-angular-compiler": "22.3.1-alpha.
|
|
23
|
-
"@angular-eslint/utils": "22.3.1-alpha.
|
|
22
|
+
"@angular-eslint/bundled-angular-compiler": "22.3.1-alpha.3",
|
|
23
|
+
"@angular-eslint/utils": "22.3.1-alpha.3",
|
|
24
24
|
"aria-query": "5.3.2",
|
|
25
25
|
"axobject-query": "4.1.0"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@angular-eslint/test-utils": "22.3.1-alpha.
|
|
28
|
+
"@angular-eslint/test-utils": "22.3.1-alpha.3",
|
|
29
29
|
"@types/aria-query": "5.0.4"
|
|
30
30
|
},
|
|
31
31
|
"peerDependencies": {
|
|
32
|
-
"@angular-eslint/template-parser": "22.3.1-alpha.
|
|
32
|
+
"@angular-eslint/template-parser": "22.3.1-alpha.3",
|
|
33
33
|
"@typescript-eslint/types": "^8.0.0",
|
|
34
34
|
"@typescript-eslint/utils": "^8.0.0",
|
|
35
35
|
"eslint": "^9.0.0 || ^10.0.0",
|