@markuplint/rules 4.11.2 → 5.0.0-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.
- package/CHANGELOG.md +63 -2
- package/SKILL.md +76 -0
- package/lib/attr-check.d.ts +46 -7
- package/lib/attr-check.js +42 -8
- package/lib/attr-duplication/index.d.ts +7 -0
- package/lib/attr-duplication/index.js +7 -0
- package/lib/attr-duplication/meta.d.ts +1 -0
- package/lib/attr-duplication/meta.js +1 -0
- package/lib/attr-value-quotes/index.d.ts +10 -0
- package/lib/attr-value-quotes/index.js +10 -0
- package/lib/attr-value-quotes/meta.d.ts +1 -0
- package/lib/attr-value-quotes/meta.js +1 -0
- package/lib/case-sensitive-attr-name/index.d.ts +9 -0
- package/lib/case-sensitive-attr-name/index.js +8 -0
- package/lib/case-sensitive-attr-name/meta.d.ts +1 -0
- package/lib/case-sensitive-attr-name/meta.js +1 -0
- package/lib/case-sensitive-tag-name/index.d.ts +9 -0
- package/lib/case-sensitive-tag-name/index.js +8 -0
- package/lib/case-sensitive-tag-name/meta.d.ts +1 -0
- package/lib/case-sensitive-tag-name/meta.js +1 -0
- package/lib/character-reference/index.d.ts +9 -0
- package/lib/character-reference/index.js +20 -0
- package/lib/character-reference/meta.d.ts +1 -0
- package/lib/character-reference/meta.js +1 -0
- package/lib/class-naming/index.d.ts +9 -0
- package/lib/class-naming/index.js +8 -0
- package/lib/class-naming/meta.d.ts +1 -0
- package/lib/class-naming/meta.js +1 -0
- package/lib/correct-aspect-ratio/index.d.ts +5 -0
- package/lib/correct-aspect-ratio/index.js +80 -0
- package/lib/correct-aspect-ratio/meta.d.ts +4 -0
- package/lib/correct-aspect-ratio/meta.js +3 -0
- package/lib/correct-aspect-ratio/resolve-image-size.d.ts +24 -0
- package/lib/correct-aspect-ratio/resolve-image-size.js +165 -0
- package/lib/create-message.d.ts +29 -2
- package/lib/create-message.js +60 -2
- package/lib/debug.d.ts +9 -0
- package/lib/debug.js +5 -0
- package/lib/deprecated-attr/index.d.ts +6 -0
- package/lib/deprecated-attr/index.js +6 -0
- package/lib/deprecated-attr/meta.d.ts +1 -0
- package/lib/deprecated-attr/meta.js +1 -0
- package/lib/deprecated-element/index.d.ts +6 -0
- package/lib/deprecated-element/index.js +8 -2
- package/lib/deprecated-element/meta.d.ts +1 -0
- package/lib/deprecated-element/meta.js +1 -0
- package/lib/disallowed-element/index.d.ts +7 -0
- package/lib/disallowed-element/index.js +7 -0
- package/lib/disallowed-element/meta.d.ts +1 -0
- package/lib/disallowed-element/meta.js +1 -0
- package/lib/doctype/index.d.ts +10 -0
- package/lib/doctype/index.js +8 -0
- package/lib/doctype/meta.d.ts +1 -0
- package/lib/doctype/meta.js +1 -0
- package/lib/end-tag/index.d.ts +7 -0
- package/lib/end-tag/index.js +8 -1
- package/lib/end-tag/meta.d.ts +1 -0
- package/lib/end-tag/meta.js +1 -0
- package/lib/heading-levels/index.d.ts +6 -0
- package/lib/heading-levels/index.js +6 -1
- package/lib/heading-levels/meta.d.ts +1 -0
- package/lib/heading-levels/meta.js +1 -0
- package/lib/helpers.d.ts +105 -0
- package/lib/helpers.js +112 -16
- package/lib/id-duplication/index.d.ts +7 -0
- package/lib/id-duplication/index.js +7 -0
- package/lib/id-duplication/meta.d.ts +1 -0
- package/lib/id-duplication/meta.js +1 -0
- package/lib/index.d.ts +43 -41
- package/lib/index.js +24 -0
- package/lib/ineffective-attr/index.d.ts +7 -0
- package/lib/ineffective-attr/index.js +7 -0
- package/lib/ineffective-attr/meta.d.ts +1 -0
- package/lib/ineffective-attr/meta.js +1 -0
- package/lib/invalid-attr/index.d.ts +29 -21
- package/lib/invalid-attr/index.js +60 -109
- package/lib/invalid-attr/meta.d.ts +1 -0
- package/lib/invalid-attr/meta.js +1 -0
- package/lib/label-has-control/index.d.ts +7 -0
- package/lib/label-has-control/index.js +11 -0
- package/lib/label-has-control/meta.d.ts +1 -0
- package/lib/label-has-control/meta.js +1 -0
- package/lib/landmark-roles/index.d.ts +14 -0
- package/lib/landmark-roles/index.js +21 -0
- package/lib/landmark-roles/meta.d.ts +1 -0
- package/lib/landmark-roles/meta.js +1 -0
- package/lib/link-types/index.d.ts +10 -0
- package/lib/link-types/index.js +180 -0
- package/lib/link-types/meta.d.ts +5 -0
- package/lib/link-types/meta.js +4 -0
- package/lib/neighbor-popovers/index.d.ts +8 -0
- package/lib/neighbor-popovers/index.js +28 -6
- package/lib/neighbor-popovers/meta.d.ts +1 -0
- package/lib/neighbor-popovers/meta.js +1 -0
- package/lib/no-ambiguous-navigable-target-names/index.d.ts +8 -0
- package/lib/no-ambiguous-navigable-target-names/index.js +8 -0
- package/lib/no-ambiguous-navigable-target-names/meta.d.ts +1 -0
- package/lib/no-ambiguous-navigable-target-names/meta.js +1 -0
- package/lib/no-boolean-attr-value/index.d.ts +7 -0
- package/lib/no-boolean-attr-value/index.js +7 -0
- package/lib/no-boolean-attr-value/meta.d.ts +1 -0
- package/lib/no-boolean-attr-value/meta.js +1 -0
- package/lib/no-consecutive-br/index.d.ts +8 -0
- package/lib/no-consecutive-br/index.js +8 -0
- package/lib/no-consecutive-br/meta.d.ts +1 -0
- package/lib/no-consecutive-br/meta.js +1 -0
- package/lib/no-default-value/index.d.ts +7 -0
- package/lib/no-default-value/index.js +7 -0
- package/lib/no-default-value/meta.d.ts +1 -0
- package/lib/no-default-value/meta.js +1 -0
- package/lib/no-duplicate-dt/index.d.ts +6 -0
- package/lib/no-duplicate-dt/index.js +6 -0
- package/lib/no-duplicate-dt/meta.d.ts +1 -0
- package/lib/no-duplicate-dt/meta.js +1 -0
- package/lib/no-empty-palpable-content/index.d.ts +13 -0
- package/lib/no-empty-palpable-content/index.js +12 -0
- package/lib/no-empty-palpable-content/meta.d.ts +1 -0
- package/lib/no-empty-palpable-content/meta.js +1 -0
- package/lib/no-hard-code-id/index.d.ts +8 -0
- package/lib/no-hard-code-id/index.js +8 -0
- package/lib/no-hard-code-id/meta.d.ts +1 -0
- package/lib/no-hard-code-id/meta.js +1 -0
- package/lib/no-orphaned-end-tag/index.d.ts +7 -0
- package/lib/no-orphaned-end-tag/index.js +8 -2
- package/lib/no-orphaned-end-tag/meta.d.ts +1 -0
- package/lib/no-orphaned-end-tag/meta.js +1 -0
- package/lib/no-refer-to-non-existent-id/index.d.ts +9 -1
- package/lib/no-refer-to-non-existent-id/index.js +11 -2
- package/lib/no-refer-to-non-existent-id/meta.d.ts +1 -0
- package/lib/no-refer-to-non-existent-id/meta.js +1 -0
- package/lib/no-unsupported-features/compat-data.d.ts +71 -0
- package/lib/no-unsupported-features/compat-data.js +204 -0
- package/lib/no-unsupported-features/index.d.ts +24 -0
- package/lib/no-unsupported-features/index.js +139 -0
- package/lib/no-unsupported-features/meta.d.ts +5 -0
- package/lib/no-unsupported-features/meta.js +4 -0
- package/lib/no-unsupported-features/resolve-browsers.d.ts +30 -0
- package/lib/no-unsupported-features/resolve-browsers.js +125 -0
- package/lib/no-use-event-handler-attr/index.d.ts +10 -0
- package/lib/no-use-event-handler-attr/index.js +8 -0
- package/lib/no-use-event-handler-attr/meta.d.ts +1 -0
- package/lib/no-use-event-handler-attr/meta.js +1 -0
- package/lib/permitted-contents/choice.d.ts +17 -0
- package/lib/permitted-contents/choice.js +31 -1
- package/lib/permitted-contents/complex-branch.d.ts +11 -7
- package/lib/permitted-contents/complex-branch.js +11 -7
- package/lib/permitted-contents/content-model.d.ts +11 -0
- package/lib/permitted-contents/content-model.js +32 -0
- package/lib/permitted-contents/count-pattern.d.ts +13 -7
- package/lib/permitted-contents/count-pattern.js +24 -8
- package/lib/permitted-contents/debug.browser.d.ts +12 -0
- package/lib/permitted-contents/debug.browser.js +12 -0
- package/lib/permitted-contents/debug.d.ts +12 -0
- package/lib/permitted-contents/debug.js +12 -0
- package/lib/permitted-contents/index.d.ts +12 -0
- package/lib/permitted-contents/index.js +21 -0
- package/lib/permitted-contents/matches-selector.d.ts +19 -0
- package/lib/permitted-contents/matches-selector.js +33 -0
- package/lib/permitted-contents/meta.d.ts +1 -0
- package/lib/permitted-contents/meta.js +1 -0
- package/lib/permitted-contents/order.d.ts +14 -7
- package/lib/permitted-contents/order.js +15 -8
- package/lib/permitted-contents/recursive-branch.d.ts +17 -0
- package/lib/permitted-contents/recursive-branch.js +17 -0
- package/lib/permitted-contents/represent-transparent-nodes.d.ts +54 -0
- package/lib/permitted-contents/represent-transparent-nodes.js +119 -12
- package/lib/permitted-contents/start.d.ts +12 -6
- package/lib/permitted-contents/start.js +12 -6
- package/lib/permitted-contents/transparent.d.ts +12 -0
- package/lib/permitted-contents/transparent.js +12 -0
- package/lib/permitted-contents/types.d.ts +67 -0
- package/lib/permitted-contents/utils.d.ts +160 -0
- package/lib/permitted-contents/utils.js +216 -35
- package/lib/placeholder-label-option/index.d.ts +7 -0
- package/lib/placeholder-label-option/index.js +19 -15
- package/lib/placeholder-label-option/meta.d.ts +1 -0
- package/lib/placeholder-label-option/meta.js +1 -0
- package/lib/redundant-accessible-name/index.d.ts +10 -0
- package/lib/redundant-accessible-name/index.js +123 -0
- package/lib/redundant-accessible-name/meta.d.ts +5 -0
- package/lib/redundant-accessible-name/meta.js +4 -0
- package/lib/require-accessible-name/index.d.ts +10 -4
- package/lib/require-accessible-name/index.js +13 -5
- package/lib/require-accessible-name/meta.d.ts +1 -0
- package/lib/require-accessible-name/meta.js +1 -0
- package/lib/require-datetime/index.d.ts +12 -0
- package/lib/require-datetime/index.js +8 -0
- package/lib/require-datetime/meta.d.ts +1 -0
- package/lib/require-datetime/meta.js +1 -0
- package/lib/require-datetime/types.d.ts +7 -0
- package/lib/require-datetime/utils.d.ts +18 -5
- package/lib/require-datetime/utils.js +53 -9
- package/lib/require-dialog-autofocus/index.d.ts +22 -0
- package/lib/require-dialog-autofocus/index.js +62 -0
- package/lib/require-dialog-autofocus/meta.d.ts +5 -0
- package/lib/require-dialog-autofocus/meta.js +4 -0
- package/lib/required-attr/index.d.ts +21 -1
- package/lib/required-attr/index.js +34 -4
- package/lib/required-attr/meta.d.ts +1 -0
- package/lib/required-attr/meta.js +1 -0
- package/lib/required-element/index.d.ts +14 -0
- package/lib/required-element/index.js +14 -2
- package/lib/required-element/meta.d.ts +1 -0
- package/lib/required-element/meta.js +1 -0
- package/lib/required-h1/index.d.ts +12 -0
- package/lib/required-h1/index.js +7 -0
- package/lib/required-h1/meta.d.ts +1 -0
- package/lib/required-h1/meta.js +1 -0
- package/lib/srcset-sizes-constraint/index.d.ts +6 -0
- package/lib/srcset-sizes-constraint/index.js +108 -0
- package/lib/srcset-sizes-constraint/meta.d.ts +5 -0
- package/lib/srcset-sizes-constraint/meta.js +4 -0
- package/lib/srcset-sizes-constraint/parse-srcset.d.ts +40 -0
- package/lib/srcset-sizes-constraint/parse-srcset.js +56 -0
- package/lib/table-row-column-alignment/find-children.d.ts +10 -0
- package/lib/table-row-column-alignment/find-children.js +11 -0
- package/lib/table-row-column-alignment/grid.d.ts +60 -0
- package/lib/table-row-column-alignment/grid.js +104 -26
- package/lib/table-row-column-alignment/index.d.ts +8 -0
- package/lib/table-row-column-alignment/index.js +8 -0
- package/lib/table-row-column-alignment/meta.d.ts +1 -0
- package/lib/table-row-column-alignment/meta.js +1 -0
- package/lib/table-row-column-alignment/types.d.ts +10 -0
- package/lib/use-list/index.d.ts +16 -0
- package/lib/use-list/index.js +20 -0
- package/lib/use-list/meta.d.ts +1 -0
- package/lib/use-list/meta.js +1 -0
- package/lib/wai-aria/checkings/abstract-role.d.ts +9 -0
- package/lib/wai-aria/checkings/abstract-role.js +14 -2
- package/lib/wai-aria/checkings/default-value.d.ts +11 -0
- package/lib/wai-aria/checkings/default-value.js +11 -0
- package/lib/wai-aria/checkings/deprecated-props.d.ts +11 -0
- package/lib/wai-aria/checkings/deprecated-props.js +11 -0
- package/lib/wai-aria/checkings/deprecated-role.d.ts +10 -0
- package/lib/wai-aria/checkings/deprecated-role.js +10 -0
- package/lib/wai-aria/checkings/disallowed-prop.d.ts +14 -0
- package/lib/wai-aria/checkings/disallowed-prop.js +19 -2
- package/lib/wai-aria/checkings/implicit-props.d.ts +13 -0
- package/lib/wai-aria/checkings/implicit-props.js +13 -0
- package/lib/wai-aria/checkings/implicit-role.d.ts +9 -0
- package/lib/wai-aria/checkings/implicit-role.js +14 -2
- package/lib/wai-aria/checkings/interaction-in-hidden.d.ts +9 -0
- package/lib/wai-aria/checkings/interaction-in-hidden.js +15 -4
- package/lib/wai-aria/checkings/no-global-prop.d.ts +10 -0
- package/lib/wai-aria/checkings/no-global-prop.js +10 -0
- package/lib/wai-aria/checkings/non-existent-role.d.ts +11 -0
- package/lib/wai-aria/checkings/non-existent-role.js +19 -2
- package/lib/wai-aria/checkings/permitted-roles.d.ts +10 -0
- package/lib/wai-aria/checkings/permitted-roles.js +15 -2
- package/lib/wai-aria/checkings/presentational-children.d.ts +7 -1
- package/lib/wai-aria/checkings/presentational-children.js +17 -3
- package/lib/wai-aria/checkings/required-accessibility-parent-role.d.ts +25 -0
- package/lib/wai-aria/checkings/required-accessibility-parent-role.js +56 -0
- package/lib/wai-aria/checkings/required-owned-elements.d.ts +10 -1
- package/lib/wai-aria/checkings/required-owned-elements.js +69 -25
- package/lib/wai-aria/checkings/required-prop.d.ts +13 -0
- package/lib/wai-aria/checkings/required-prop.js +18 -2
- package/lib/wai-aria/checkings/value.d.ts +23 -0
- package/lib/wai-aria/checkings/value.js +32 -0
- package/lib/wai-aria/index.d.ts +10 -0
- package/lib/wai-aria/index.js +22 -4
- package/lib/wai-aria/meta.d.ts +1 -0
- package/lib/wai-aria/meta.js +1 -0
- package/lib/wai-aria/types.d.ts +25 -1
- package/package.json +16 -11
- package/schema.json +18 -0
|
@@ -1,37 +1,45 @@
|
|
|
1
1
|
import type { AttributeType } from '@markuplint/ml-spec';
|
|
2
|
+
import type { Pattern } from '@markuplint/types';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration options for the `invalid-attr` rule.
|
|
5
|
+
*/
|
|
2
6
|
type Option = {
|
|
3
7
|
/**
|
|
8
|
+
* Attributes to allow beyond what the spec permits.
|
|
9
|
+
* Can override spec-level restrictions such as `noUse`.
|
|
10
|
+
*
|
|
4
11
|
* @since 3.7.0
|
|
5
12
|
*/
|
|
6
|
-
allowAttrs?: (string | Attr)[] | Record<string,
|
|
13
|
+
allowAttrs?: (string | Attr)[] | Record<string, AttributeType | Pattern>;
|
|
7
14
|
/**
|
|
15
|
+
* Attributes to disallow in addition to spec restrictions.
|
|
16
|
+
*
|
|
8
17
|
* @since 3.7.0
|
|
9
18
|
*/
|
|
10
|
-
disallowAttrs?: (string | Attr)[] | Record<string,
|
|
19
|
+
disallowAttrs?: (string | Attr)[] | Record<string, AttributeType | Pattern>;
|
|
20
|
+
/** Attribute name prefix(es) to ignore during validation. */
|
|
11
21
|
ignoreAttrNamePrefix?: string | string[];
|
|
22
|
+
/** Whether to allow additional properties for pretender elements (defaults to `true`). */
|
|
12
23
|
allowToAddPropertiesForPretender?: boolean;
|
|
13
|
-
/**
|
|
14
|
-
* @deprecated Since version 3.7.0. Use `allowAttrs` or `disallowAttrs` instead.
|
|
15
|
-
* This option (`attr`) is now considered ambiguous and may lead to confusion.
|
|
16
|
-
* Please use the more explicit `allowAttrs` or `disallowAttrs` option
|
|
17
|
-
* to specify allowed attributes for the `invalid-attr` rule.
|
|
18
|
-
* @see {@link Option.allowAttrs}
|
|
19
|
-
* @see {@link Option.disallowAttrs}
|
|
20
|
-
*/
|
|
21
|
-
attrs?: Record<string, ValueRule | {
|
|
22
|
-
disallowed: true;
|
|
23
|
-
}>;
|
|
24
24
|
};
|
|
25
|
+
/**
|
|
26
|
+
* Describes a single attribute with its name and expected value constraint.
|
|
27
|
+
*/
|
|
25
28
|
type Attr = {
|
|
29
|
+
/** The attribute name. */
|
|
26
30
|
name: string;
|
|
27
|
-
value
|
|
28
|
-
|
|
29
|
-
type ValueRule = {
|
|
30
|
-
enum: [string, ...string[]];
|
|
31
|
-
} | {
|
|
32
|
-
pattern: string;
|
|
33
|
-
} | {
|
|
34
|
-
type: AttributeType;
|
|
31
|
+
/** The expected attribute value type or validation rule. */
|
|
32
|
+
value: AttributeType | Pattern;
|
|
35
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Rule that validates attributes against the HTML spec, allowed lists,
|
|
36
|
+
* and disallowed lists.
|
|
37
|
+
*
|
|
38
|
+
* Checks each attribute for: existence in the spec, correct value type,
|
|
39
|
+
* allowed/disallowed overrides from configuration, and typo suggestions
|
|
40
|
+
* via candidate matching. Supports `allowAttrs` and `disallowAttrs`.
|
|
41
|
+
* Non-existent attributes on elements that allow additional properties
|
|
42
|
+
* (pretenders) can be optionally permitted.
|
|
43
|
+
*/
|
|
36
44
|
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, Option>>;
|
|
37
45
|
export default _default;
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import { createRule, getAttrSpecs, getSpec } from '@markuplint/ml-core';
|
|
2
|
+
import { check, isEnum, isPattern } from '@markuplint/types';
|
|
2
3
|
import { attrCheck } from '../attr-check.js';
|
|
3
4
|
import { log as ruleLog } from '../debug.js';
|
|
4
|
-
import { isValidAttr
|
|
5
|
+
import { isValidAttr } from '../helpers.js';
|
|
5
6
|
import meta from './meta.js';
|
|
6
7
|
const log = ruleLog.extend('invalid-attr');
|
|
8
|
+
/**
|
|
9
|
+
* Rule that validates attributes against the HTML spec, allowed lists,
|
|
10
|
+
* and disallowed lists.
|
|
11
|
+
*
|
|
12
|
+
* Checks each attribute for: existence in the spec, correct value type,
|
|
13
|
+
* allowed/disallowed overrides from configuration, and typo suggestions
|
|
14
|
+
* via candidate matching. Supports `allowAttrs` and `disallowAttrs`.
|
|
15
|
+
* Non-existent attributes on elements that allow additional properties
|
|
16
|
+
* (pretenders) can be optionally permitted.
|
|
17
|
+
*/
|
|
7
18
|
export default createRule({
|
|
8
19
|
meta: meta,
|
|
9
20
|
defaultOptions: {},
|
|
@@ -47,19 +58,15 @@ export default createRule({
|
|
|
47
58
|
if (Array.isArray(attr.rule.options.allowAttrs)) {
|
|
48
59
|
for (const allowAttr of attr.rule.options.allowAttrs) {
|
|
49
60
|
if (typeof allowAttr === 'string') {
|
|
50
|
-
allowAttrs[allowAttr] =
|
|
61
|
+
allowAttrs[allowAttr] = 'Any';
|
|
51
62
|
continue;
|
|
52
63
|
}
|
|
53
|
-
|
|
54
|
-
allowAttrs[allowAttr.name] = allowAttr.value;
|
|
55
|
-
continue;
|
|
56
|
-
}
|
|
57
|
-
allowAttrs[allowAttr.name] = { type: allowAttr.value };
|
|
64
|
+
allowAttrs[allowAttr.name] = allowAttr.value;
|
|
58
65
|
}
|
|
59
66
|
}
|
|
60
67
|
else {
|
|
61
|
-
for (const [attrName,
|
|
62
|
-
allowAttrs[attrName] =
|
|
68
|
+
for (const [attrName, attrType] of Object.entries(attr.rule.options.allowAttrs)) {
|
|
69
|
+
allowAttrs[attrName] = attrType;
|
|
63
70
|
}
|
|
64
71
|
}
|
|
65
72
|
}
|
|
@@ -67,99 +74,50 @@ export default createRule({
|
|
|
67
74
|
if (Array.isArray(attr.rule.options.disallowAttrs)) {
|
|
68
75
|
for (const disallowAttr of attr.rule.options.disallowAttrs) {
|
|
69
76
|
if (typeof disallowAttr === 'string') {
|
|
70
|
-
disallowAttrs[disallowAttr] =
|
|
77
|
+
disallowAttrs[disallowAttr] = 'Any';
|
|
71
78
|
continue;
|
|
72
79
|
}
|
|
73
|
-
|
|
74
|
-
disallowAttrs[disallowAttr.name] = disallowAttr.value;
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
disallowAttrs[disallowAttr.name] = { type: disallowAttr.value };
|
|
80
|
+
disallowAttrs[disallowAttr.name] = disallowAttr.value;
|
|
78
81
|
}
|
|
79
82
|
}
|
|
80
83
|
else {
|
|
81
|
-
for (const [attrName,
|
|
82
|
-
disallowAttrs[attrName] =
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
if (attr.rule.options.attrs) {
|
|
87
|
-
for (const [attrName, valueRule] of Object.entries(attr.rule.options.attrs)) {
|
|
88
|
-
if ('disallowed' in valueRule) {
|
|
89
|
-
disallowAttrs[attrName] = { type: 'Any' };
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
allowAttrs[attrName] = valueRule;
|
|
84
|
+
for (const [attrName, attrType] of Object.entries(attr.rule.options.disallowAttrs)) {
|
|
85
|
+
disallowAttrs[attrName] = attrType;
|
|
93
86
|
}
|
|
94
87
|
}
|
|
95
88
|
}
|
|
96
89
|
const allowValue = allowAttrs[name] ?? null;
|
|
97
90
|
const disallowValue = disallowAttrs[name] ?? null;
|
|
98
|
-
if (allowValue) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
},
|
|
105
|
-
description: '',
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
else if ('pattern' in allowValue) {
|
|
109
|
-
if (!match(value, allowValue.pattern)) {
|
|
110
|
-
invalid = {
|
|
111
|
-
invalidType: 'invalid-value',
|
|
112
|
-
message: t('{0} is unmatched with the below patterns: {1}', t('the "{0*}" {1}', name, 'attribute'), allowValue.pattern),
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
else if ('type' in allowValue) {
|
|
117
|
-
invalid = attrCheck(t, name, value, true, { name, type: allowValue.type, description: '' });
|
|
118
|
-
}
|
|
91
|
+
if (allowValue !== null) {
|
|
92
|
+
invalid = attrCheck(t, name, value, true, {
|
|
93
|
+
name,
|
|
94
|
+
type: allowValue,
|
|
95
|
+
description: '',
|
|
96
|
+
});
|
|
119
97
|
}
|
|
120
|
-
else if (disallowValue) {
|
|
121
|
-
if ('
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
},
|
|
127
|
-
description: '',
|
|
128
|
-
});
|
|
129
|
-
if (checkResult === false) {
|
|
130
|
-
invalid = {
|
|
131
|
-
invalidType: 'invalid-value',
|
|
132
|
-
message: t('{0} is disallowed to accept the following values: {1}', t('the "{0*}" {1}', name, 'attribute'), t(disallowValue.enum)),
|
|
133
|
-
};
|
|
134
|
-
}
|
|
98
|
+
else if (disallowValue !== null) {
|
|
99
|
+
if (disallowValue === 'Any') {
|
|
100
|
+
invalid = {
|
|
101
|
+
invalidType: 'non-existent',
|
|
102
|
+
message: t('{0} is disallowed', t('the "{0*}" {1}', name, 'attribute')),
|
|
103
|
+
};
|
|
135
104
|
}
|
|
136
|
-
else
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
message: t('{0} is matched with the below disallowed patterns: {1}', t('the "{0*}" {1}', name, 'attribute'), disallowValue.pattern),
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
else if ('type' in disallowValue) {
|
|
145
|
-
if (disallowValue.type === 'Any') {
|
|
146
|
-
invalid = {
|
|
147
|
-
invalidType: 'non-existent',
|
|
148
|
-
message: t('{0} is disallowed', t('the "{0*}" {1}', name, 'attribute')),
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
const checkResult = attrCheck(t, name, value, true, {
|
|
105
|
+
else {
|
|
106
|
+
const checkResult = isPattern(disallowValue)
|
|
107
|
+
? check(value, disallowValue)
|
|
108
|
+
: attrCheck(t, name, value, true, {
|
|
153
109
|
name,
|
|
154
|
-
type: disallowValue
|
|
110
|
+
type: disallowValue,
|
|
155
111
|
description: '',
|
|
156
112
|
});
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
113
|
+
const isMatched = isPattern(disallowValue)
|
|
114
|
+
? checkResult.matched
|
|
115
|
+
: checkResult === false;
|
|
116
|
+
if (isMatched) {
|
|
117
|
+
invalid = {
|
|
118
|
+
invalidType: 'invalid-value',
|
|
119
|
+
message: createDisallowMessage(t, name, disallowValue),
|
|
120
|
+
};
|
|
163
121
|
}
|
|
164
122
|
}
|
|
165
123
|
}
|
|
@@ -216,29 +174,22 @@ export default createRule({
|
|
|
216
174
|
});
|
|
217
175
|
},
|
|
218
176
|
});
|
|
219
|
-
|
|
177
|
+
/**
|
|
178
|
+
* Creates an appropriate disallow message based on the type structure.
|
|
179
|
+
*
|
|
180
|
+
* @param t - The i18n translator
|
|
181
|
+
* @param name - The attribute name
|
|
182
|
+
* @param type - The attribute type or pattern
|
|
183
|
+
* @returns A localized message string
|
|
184
|
+
*/
|
|
185
|
+
function createDisallowMessage(t, name,
|
|
220
186
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
-
return
|
|
224
|
-
}
|
|
225
|
-
if ('enum' in value) {
|
|
226
|
-
if (Object.keys(value).length > 1) {
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
return true;
|
|
230
|
-
}
|
|
231
|
-
if ('token' in value) {
|
|
232
|
-
return false;
|
|
233
|
-
}
|
|
234
|
-
if ('pattern' in value) {
|
|
235
|
-
return true;
|
|
236
|
-
}
|
|
237
|
-
if (value.type === 'integer' || value.type === 'float') {
|
|
238
|
-
return false;
|
|
187
|
+
type) {
|
|
188
|
+
if (isPattern(type)) {
|
|
189
|
+
return t('{0} is matched with the below disallowed patterns: {1}', t('the "{0*}" {1}', name, 'attribute'), type.pattern);
|
|
239
190
|
}
|
|
240
|
-
if (
|
|
241
|
-
return
|
|
191
|
+
if (typeof type !== 'string' && isEnum(type)) {
|
|
192
|
+
return t('{0} is disallowed to accept the following values: {1}', t('the "{0*}" {1}', name, 'attribute'), t(type.enum));
|
|
242
193
|
}
|
|
243
|
-
return
|
|
194
|
+
return t('{0} is disallowed', t('{0} of {1}', t('the {0}', 'type'), t('the "{0*}" {1}', name, 'attribute')));
|
|
244
195
|
}
|
package/lib/invalid-attr/meta.js
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule that validates `<label>` elements are properly associated with form controls.
|
|
3
|
+
*
|
|
4
|
+
* Checks that each label either wraps a control or has a `for` attribute,
|
|
5
|
+
* and warns when a label contains more than one form control (only the first
|
|
6
|
+
* is associated per the HTML spec).
|
|
7
|
+
*/
|
|
1
8
|
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
|
|
2
9
|
export default _default;
|
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { createRule } from '@markuplint/ml-core';
|
|
2
2
|
import meta from './meta.js';
|
|
3
|
+
/**
|
|
4
|
+
* CSS selector that matches form control elements eligible for label association.
|
|
5
|
+
* Excludes hidden inputs since they do not need labels.
|
|
6
|
+
*/
|
|
3
7
|
const controlSelector = ["input:not([type='hidden' i])", 'select', 'textarea'].join(',');
|
|
8
|
+
/**
|
|
9
|
+
* Rule that validates `<label>` elements are properly associated with form controls.
|
|
10
|
+
*
|
|
11
|
+
* Checks that each label either wraps a control or has a `for` attribute,
|
|
12
|
+
* and warns when a label contains more than one form control (only the first
|
|
13
|
+
* is associated per the HTML spec).
|
|
14
|
+
*/
|
|
4
15
|
export default createRule({
|
|
5
16
|
meta: meta,
|
|
6
17
|
defaultSeverity: 'warning',
|
|
@@ -1,8 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration options for the landmark-roles rule.
|
|
3
|
+
*/
|
|
1
4
|
type Options = {
|
|
5
|
+
/** Landmark roles to exclude from validation. */
|
|
2
6
|
ignoreRoles: Roles[];
|
|
7
|
+
/** Whether to require a unique accessible name when duplicate landmark roles exist. */
|
|
3
8
|
labelEachArea: boolean;
|
|
4
9
|
};
|
|
10
|
+
/** Landmark roles that should appear at the top level of the document. */
|
|
5
11
|
type TopLevelRoles = 'banner' | 'main' | 'complementary' | 'contentinfo';
|
|
12
|
+
/** All recognized ARIA landmark roles for this rule. */
|
|
6
13
|
type Roles = TopLevelRoles | 'form' | 'navigation' | 'region';
|
|
14
|
+
/**
|
|
15
|
+
* Rule that validates proper usage of ARIA landmark roles.
|
|
16
|
+
*
|
|
17
|
+
* Checks that `banner`, `main`, `complementary`, and `contentinfo` landmarks are
|
|
18
|
+
* top-level (not nested inside other landmarks), and that when duplicate landmarks
|
|
19
|
+
* of the same role exist, each has a unique accessible name.
|
|
20
|
+
*/
|
|
7
21
|
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, Options>>;
|
|
8
22
|
export default _default;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createRule } from '@markuplint/ml-core';
|
|
2
2
|
import meta from './meta.js';
|
|
3
|
+
/** CSS selectors used to identify elements with each landmark role. */
|
|
3
4
|
const selectors = {
|
|
4
5
|
complementary: ['[role="complementary"]', 'aside'],
|
|
5
6
|
contentinfo: ['[role="contentinfo"]'],
|
|
@@ -9,7 +10,15 @@ const selectors = {
|
|
|
9
10
|
navigation: ['[role="navigation"]', 'nav'],
|
|
10
11
|
region: ['[role="region"]', 'section[aria-labelledby]', 'section[aria-label]', 'section[title]'],
|
|
11
12
|
};
|
|
13
|
+
/** Roles that are required to be top-level landmarks per WAI-ARIA practices. */
|
|
12
14
|
const topLevelRoles = ['banner', 'main', 'complementary', 'contentinfo'];
|
|
15
|
+
/**
|
|
16
|
+
* Rule that validates proper usage of ARIA landmark roles.
|
|
17
|
+
*
|
|
18
|
+
* Checks that `banner`, `main`, `complementary`, and `contentinfo` landmarks are
|
|
19
|
+
* top-level (not nested inside other landmarks), and that when duplicate landmarks
|
|
20
|
+
* of the same role exist, each has a unique accessible name.
|
|
21
|
+
*/
|
|
13
22
|
export default createRule({
|
|
14
23
|
meta: meta,
|
|
15
24
|
defaultSeverity: 'warning',
|
|
@@ -105,11 +114,23 @@ export default createRule({
|
|
|
105
114
|
}
|
|
106
115
|
},
|
|
107
116
|
});
|
|
117
|
+
/**
|
|
118
|
+
* Collects all UUIDs of elements that have landmark roles.
|
|
119
|
+
*
|
|
120
|
+
* @param roleset - The mapping of roles to their matched elements.
|
|
121
|
+
* @returns A flat array of UUIDs for all landmark role elements.
|
|
122
|
+
*/
|
|
108
123
|
function landmarkRoleElementUUIDList(
|
|
109
124
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
110
125
|
roleset) {
|
|
111
126
|
return Object.values(roleset).flatMap(elements => elements.map(element => element.uuid));
|
|
112
127
|
}
|
|
128
|
+
/**
|
|
129
|
+
* Checks whether an element has an accessible label via `aria-label` or `aria-labelledby`.
|
|
130
|
+
*
|
|
131
|
+
* @param el - The landmark element to check.
|
|
132
|
+
* @returns `true` if the element has an accessible name, `false` otherwise.
|
|
133
|
+
*/
|
|
113
134
|
function hasLabel(
|
|
114
135
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
115
136
|
el) {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
type Options = {
|
|
2
|
+
readonly allowMicroformats?: boolean | readonly string[];
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* Rule that validates link type keywords in the `rel` attribute on
|
|
6
|
+
* `<link>`, `<a>`, `<area>`, and `<form>` elements against the WHATWG
|
|
7
|
+
* standard and optionally the Microformats registry.
|
|
8
|
+
*/
|
|
9
|
+
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, Options>>;
|
|
10
|
+
export default _default;
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { createRule } from '@markuplint/ml-core';
|
|
2
|
+
import { DEF_LINK_TYPE_WHATWG, ALLOWED_LINK_TYPE_MICROFORMATS, DEF_LINK_TYPE_MICROFORMATS_DROPPED, DEF_LINK_TYPE_MICROFORMATS_DROPPED_WITHOUT_PREJUDICE, DEF_LINK_TYPE_MICROFORMATS_REJECTED, DEF_LINK_TYPE_MICROFORMATS_NON_HTML_REL_VALUES, } from '@markuplint/types';
|
|
3
|
+
import meta from './meta.js';
|
|
4
|
+
const TARGET_ELEMENTS = new Set(['link', 'a', 'area', 'form']);
|
|
5
|
+
/**
|
|
6
|
+
* Rule that validates link type keywords in the `rel` attribute on
|
|
7
|
+
* `<link>`, `<a>`, `<area>`, and `<form>` elements against the WHATWG
|
|
8
|
+
* standard and optionally the Microformats registry.
|
|
9
|
+
*/
|
|
10
|
+
export default createRule({
|
|
11
|
+
meta: meta,
|
|
12
|
+
defaultOptions: {
|
|
13
|
+
allowMicroformats: false,
|
|
14
|
+
},
|
|
15
|
+
async verify({ document, report, t }) {
|
|
16
|
+
await document.walkOn('Element', el => {
|
|
17
|
+
if (!TARGET_ELEMENTS.has(el.localName)) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const relAttr = el.getAttributeNode('rel');
|
|
21
|
+
if (!relAttr) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (relAttr.isDynamicValue) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const tokenList = relAttr.tokenList;
|
|
28
|
+
if (!tokenList || tokenList.length === 0) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const context = getElementContext(el);
|
|
32
|
+
if (!context) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const options = el.rule.options;
|
|
36
|
+
for (const tokenInfo of tokenList.allTokens()) {
|
|
37
|
+
const keyword = tokenInfo.raw;
|
|
38
|
+
const keywordLower = keyword.toLowerCase();
|
|
39
|
+
const message = validateKeyword(keywordLower, context, options, t);
|
|
40
|
+
if (message) {
|
|
41
|
+
report({
|
|
42
|
+
scope: el,
|
|
43
|
+
line: tokenInfo.startLine,
|
|
44
|
+
col: tokenInfo.startCol,
|
|
45
|
+
raw: keyword,
|
|
46
|
+
message,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
function getElementContext(
|
|
54
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
55
|
+
el) {
|
|
56
|
+
const tagName = el.localName;
|
|
57
|
+
switch (tagName) {
|
|
58
|
+
case 'link': {
|
|
59
|
+
if (el.closest('body') !== null) {
|
|
60
|
+
return 'body-link';
|
|
61
|
+
}
|
|
62
|
+
return 'link';
|
|
63
|
+
}
|
|
64
|
+
case 'a':
|
|
65
|
+
case 'area': {
|
|
66
|
+
return 'a-area';
|
|
67
|
+
}
|
|
68
|
+
case 'form': {
|
|
69
|
+
return 'form';
|
|
70
|
+
}
|
|
71
|
+
default: {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function validateKeyword(keyword, context, options, t) {
|
|
77
|
+
// Check WHATWG standard keywords
|
|
78
|
+
const whatwgDef = DEF_LINK_TYPE_WHATWG.find(def => def.keyword.toLowerCase() === keyword);
|
|
79
|
+
if (whatwgDef) {
|
|
80
|
+
return validateWhatwgKeyword(whatwgDef, context, t);
|
|
81
|
+
}
|
|
82
|
+
// Check dropped keywords
|
|
83
|
+
if (DEF_LINK_TYPE_MICROFORMATS_DROPPED.some(def => def.keyword.toLowerCase() === keyword)) {
|
|
84
|
+
return t('{0} is {1:c}', `"${keyword}"`, 'dropped');
|
|
85
|
+
}
|
|
86
|
+
// Check dropped without prejudice keywords
|
|
87
|
+
if (DEF_LINK_TYPE_MICROFORMATS_DROPPED_WITHOUT_PREJUDICE.some(def => def.keyword.toLowerCase() === keyword)) {
|
|
88
|
+
return t('{0} is {1:c}', `"${keyword}"`, 'dropped');
|
|
89
|
+
}
|
|
90
|
+
// Check rejected keywords
|
|
91
|
+
if (DEF_LINK_TYPE_MICROFORMATS_REJECTED.some(def => def.keyword.toLowerCase() === keyword)) {
|
|
92
|
+
return t('{0} is {1:c}', `"${keyword}"`, 'rejected');
|
|
93
|
+
}
|
|
94
|
+
// Check non-HTML rel values
|
|
95
|
+
if (DEF_LINK_TYPE_MICROFORMATS_NON_HTML_REL_VALUES.some(def => def.keyword.toLowerCase() === keyword)) {
|
|
96
|
+
return t('{0} is {1:c}', `"${keyword}"`, 'not allowed');
|
|
97
|
+
}
|
|
98
|
+
// Handle Microformats
|
|
99
|
+
const { allowMicroformats } = options;
|
|
100
|
+
if (allowMicroformats === false || allowMicroformats === undefined) {
|
|
101
|
+
return t('The "{0*}" {1} is {2:c}', keyword, 'keyword', 'not allowed');
|
|
102
|
+
}
|
|
103
|
+
if (allowMicroformats === true) {
|
|
104
|
+
return validateMicroformatKeyword(keyword, context, t);
|
|
105
|
+
}
|
|
106
|
+
// allowMicroformats is string[]
|
|
107
|
+
if (Array.isArray(allowMicroformats)) {
|
|
108
|
+
const isAllowed = allowMicroformats.some(allowed => allowed.toLowerCase() === keyword);
|
|
109
|
+
if (!isAllowed) {
|
|
110
|
+
return t('The "{0*}" {1} is {2:c}', keyword, 'keyword', 'not allowed');
|
|
111
|
+
}
|
|
112
|
+
// Even if in the allow list, check element context
|
|
113
|
+
const microDef = ALLOWED_LINK_TYPE_MICROFORMATS.find(def => def.keyword.toLowerCase() === keyword);
|
|
114
|
+
if (microDef) {
|
|
115
|
+
return validateMicroformatContext(microDef, context, t);
|
|
116
|
+
}
|
|
117
|
+
// Keyword is in the user's allow list but not in any registry — allow it
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
function validateWhatwgKeyword(def, context, t) {
|
|
123
|
+
switch (context) {
|
|
124
|
+
case 'link': {
|
|
125
|
+
if (def.link === 'not allowed') {
|
|
126
|
+
return t('The "{0*}" {1} is not allowed on the {2}', def.keyword, 'keyword', '"link" element');
|
|
127
|
+
}
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
case 'body-link': {
|
|
131
|
+
if (def.link === 'not allowed') {
|
|
132
|
+
return t('The "{0*}" {1} is not allowed on the {2}', def.keyword, 'keyword', '"link" element');
|
|
133
|
+
}
|
|
134
|
+
if (def.bodyOk !== 'Yes') {
|
|
135
|
+
return t('The "{0*}" {1} is not allowed on the {2} inside the {3}', def.keyword, 'keyword', '"link" element', '"body" element');
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
case 'a-area': {
|
|
140
|
+
if (def.a === 'not allowed') {
|
|
141
|
+
return t('The "{0*}" {1} is not allowed on the {2}', def.keyword, 'keyword', '"a" element');
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
case 'form': {
|
|
146
|
+
if (def.form === 'not allowed') {
|
|
147
|
+
return t('The "{0*}" {1} is not allowed on the {2}', def.keyword, 'keyword', '"form" element');
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
function validateMicroformatKeyword(keyword, context, t) {
|
|
154
|
+
const microDef = ALLOWED_LINK_TYPE_MICROFORMATS.find(def => def.keyword.toLowerCase() === keyword);
|
|
155
|
+
if (!microDef) {
|
|
156
|
+
return t('The "{0*}" {1} is {2:c}', keyword, 'keyword', 'not allowed');
|
|
157
|
+
}
|
|
158
|
+
return validateMicroformatContext(microDef, context, t);
|
|
159
|
+
}
|
|
160
|
+
function validateMicroformatContext(def, context, t) {
|
|
161
|
+
switch (context) {
|
|
162
|
+
case 'link':
|
|
163
|
+
case 'body-link': {
|
|
164
|
+
if (!def.link) {
|
|
165
|
+
return t('The "{0*}" {1} is not allowed on the {2}', def.keyword, 'keyword', '"link" element');
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
case 'a-area': {
|
|
170
|
+
if (!def.a) {
|
|
171
|
+
return t('The "{0*}" {1} is not allowed on the {2}', def.keyword, 'keyword', '"a" element');
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
case 'form': {
|
|
176
|
+
// Microformats don't define form context; reject
|
|
177
|
+
return t('The "{0*}" {1} is not allowed on the {2}', def.keyword, 'keyword', '"form" element');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
@@ -1,2 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule that detects perceptible content between a popover trigger and its target.
|
|
3
|
+
*
|
|
4
|
+
* When a `[popovertarget]` element or a `[commandfor]` element with a popover-related
|
|
5
|
+
* `command` attribute and its corresponding `[popover]` target exist in the DOM, any
|
|
6
|
+
* focusable elements, elements with accessible names, or non-whitespace text nodes
|
|
7
|
+
* between them are reported as violations.
|
|
8
|
+
*/
|
|
1
9
|
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
|
|
2
10
|
export default _default;
|