@markuplint/rules 2.4.2 → 3.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.
Files changed (98) hide show
  1. package/README.md +1 -5
  2. package/lib/attr-duplication/index.js +10 -7
  3. package/lib/attr-equal-space-after/index.d.ts +1 -1
  4. package/lib/attr-equal-space-before/index.d.ts +1 -1
  5. package/lib/attr-spacing/index.d.ts +3 -3
  6. package/lib/attr-value-quotes/index.js +21 -25
  7. package/lib/case-sensitive-attr-name/index.d.ts +1 -5
  8. package/lib/case-sensitive-attr-name/index.js +51 -81
  9. package/lib/case-sensitive-tag-name/index.js +38 -30
  10. package/lib/character-reference/index.d.ts +2 -1
  11. package/lib/character-reference/index.js +6 -8
  12. package/lib/class-naming/index.js +11 -11
  13. package/lib/deprecated-attr/index.js +17 -18
  14. package/lib/deprecated-element/index.js +5 -7
  15. package/lib/disallowed-element/index.js +8 -9
  16. package/lib/doctype/index.js +7 -7
  17. package/lib/end-tag/index.js +4 -1
  18. package/lib/helper/aria/get-aria.d.ts +7 -0
  19. package/lib/helper/aria/get-aria.js +53 -0
  20. package/lib/helper/aria/get-computed-role.d.ts +10 -0
  21. package/lib/helper/aria/get-computed-role.js +24 -0
  22. package/lib/helper/aria/get-implicit-role.d.ts +7 -0
  23. package/lib/helper/aria/get-implicit-role.js +12 -0
  24. package/lib/helper/aria/get-permitted-roles.d.ts +14 -0
  25. package/lib/helper/aria/get-permitted-roles.js +26 -0
  26. package/lib/helper/aria/resolve-version.d.ts +2 -0
  27. package/lib/helper/aria/resolve-version.js +21 -0
  28. package/lib/helper/spec/aria-spec.d.ts +5 -0
  29. package/lib/helper/spec/aria-spec.js +12 -0
  30. package/lib/helper/spec/get-role-spec.d.ts +11 -0
  31. package/lib/helper/spec/get-role-spec.js +39 -0
  32. package/lib/helper/spec/html-spec.d.ts +2 -0
  33. package/lib/helper/spec/html-spec.js +14 -0
  34. package/lib/helpers.d.ts +10 -28
  35. package/lib/helpers.js +29 -149
  36. package/lib/id-duplication/index.js +4 -6
  37. package/lib/indentation/index.d.ts +3 -3
  38. package/lib/index.d.ts +14 -19
  39. package/lib/index.js +0 -8
  40. package/lib/ineffective-attr/index.js +19 -27
  41. package/lib/invalid-attr/index.js +83 -83
  42. package/lib/landmark-roles/index.d.ts +2 -2
  43. package/lib/landmark-roles/index.js +9 -9
  44. package/lib/no-boolean-attr-value/index.js +33 -34
  45. package/lib/no-default-value/index.js +20 -24
  46. package/lib/no-hard-code-id/index.js +9 -13
  47. package/lib/no-refer-to-non-existent-id/index.d.ts +4 -1
  48. package/lib/no-refer-to-non-existent-id/index.js +77 -83
  49. package/lib/no-use-event-handler-attr/index.js +20 -22
  50. package/lib/permitted-contents/index.d.ts +5 -2
  51. package/lib/permitted-contents/index.js +67 -68
  52. package/lib/permitted-contents/permitted-content.spec-to-regexp.d.ts +5 -5
  53. package/lib/permitted-contents/permitted-content.spec-to-regexp.js +19 -14
  54. package/lib/permitted-contents/unfold-content-models-to-tags.d.ts +2 -2
  55. package/lib/require-accessible-name/index.d.ts +5 -1
  56. package/lib/require-accessible-name/index.js +6 -3
  57. package/lib/required-attr/index.js +16 -25
  58. package/lib/required-element/index.d.ts +1 -1
  59. package/lib/required-element/index.js +6 -6
  60. package/lib/required-h1/index.d.ts +2 -2
  61. package/lib/required-h1/index.js +3 -3
  62. package/lib/wai-aria/checkings/abstract-role.d.ts +3 -0
  63. package/lib/wai-aria/checkings/abstract-role.js +22 -0
  64. package/lib/wai-aria/checkings/checking-required-owned-elements.d.ts +11 -0
  65. package/lib/wai-aria/checkings/checking-required-owned-elements.js +63 -0
  66. package/lib/wai-aria/checkings/default-value.d.ts +6 -0
  67. package/lib/wai-aria/checkings/default-value.js +17 -0
  68. package/lib/wai-aria/checkings/deprecated-props.d.ts +7 -0
  69. package/lib/wai-aria/checkings/deprecated-props.js +18 -0
  70. package/lib/wai-aria/checkings/disallowed-prop.d.ts +7 -0
  71. package/lib/wai-aria/checkings/disallowed-prop.js +22 -0
  72. package/lib/wai-aria/checkings/implicit-props.d.ts +7 -0
  73. package/lib/wai-aria/checkings/implicit-props.js +50 -0
  74. package/lib/wai-aria/checkings/implicit-role.d.ts +3 -0
  75. package/lib/wai-aria/checkings/implicit-role.js +21 -0
  76. package/lib/wai-aria/checkings/interaction-in-hidden.d.ts +11 -0
  77. package/lib/wai-aria/checkings/interaction-in-hidden.js +47 -0
  78. package/lib/wai-aria/checkings/no-global-prop.d.ts +6 -0
  79. package/lib/wai-aria/checkings/no-global-prop.js +14 -0
  80. package/lib/wai-aria/checkings/non-existant-role.d.ts +3 -0
  81. package/lib/wai-aria/checkings/non-existant-role.js +28 -0
  82. package/lib/wai-aria/checkings/permitted-roles.d.ts +3 -0
  83. package/lib/wai-aria/checkings/permitted-roles.js +28 -0
  84. package/lib/wai-aria/checkings/presentational-children.d.ts +29 -0
  85. package/lib/wai-aria/checkings/presentational-children.js +60 -0
  86. package/lib/wai-aria/checkings/required-owned-elements.d.ts +11 -0
  87. package/lib/wai-aria/checkings/required-owned-elements.js +63 -0
  88. package/lib/wai-aria/checkings/required-prop.d.ts +9 -0
  89. package/lib/wai-aria/checkings/required-prop.js +27 -0
  90. package/lib/wai-aria/checkings/value.d.ts +7 -0
  91. package/lib/wai-aria/checkings/value.js +21 -0
  92. package/lib/wai-aria/index.d.ts +1 -8
  93. package/lib/wai-aria/index.js +66 -207
  94. package/lib/wai-aria/types.d.ts +13 -0
  95. package/lib/wai-aria/types.js +2 -0
  96. package/package.json +6 -6
  97. package/schema.json +0 -12
  98. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,29 @@
1
+ import type { Options } from '../types';
2
+ import type { ElementChecker } from '@markuplint/ml-core';
3
+ /**
4
+ * Presentational Children
5
+ *
6
+ * @see https://www.w3.org/TR/wai-aria/#childrenArePresentational
7
+ * @see https://w3c.github.io/aria/#childrenArePresentational
8
+ *
9
+ * > The DOM descendants are presentational.
10
+ * > user agents SHOULD NOT expose descendants of
11
+ * > this element through the platform accessibility API.
12
+ * > If user agents do not hide the descendant nodes,
13
+ * > some information may be read twice.
14
+ *
15
+ * @see https://w3c.github.io/aria/#tree_exclusion
16
+ *
17
+ * > Any descendants of elements that have the characteristic "Children Presentational: True"
18
+ * > unless the descendant is not allowed to be presentational
19
+ * > because it meets one of the conditions for exception described
20
+ * > in Presentational Roles Conflict Resolution.
21
+ * > However, the text content of any excluded descendants is included.
22
+ *
23
+ * @see https://w3c.github.io/aria/#tree_inclusion
24
+ *
25
+ * > Text equivalents for hidden referenced objects
26
+ * > may still be used in the name and description computation
27
+ * > even when not included in the accessibility tree.
28
+ */
29
+ export declare const checkingPresentationalChildren: ElementChecker<boolean, Options>;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkingPresentationalChildren = void 0;
4
+ const ml_spec_1 = require("@markuplint/ml-spec");
5
+ /**
6
+ * Presentational Children
7
+ *
8
+ * @see https://www.w3.org/TR/wai-aria/#childrenArePresentational
9
+ * @see https://w3c.github.io/aria/#childrenArePresentational
10
+ *
11
+ * > The DOM descendants are presentational.
12
+ * > user agents SHOULD NOT expose descendants of
13
+ * > this element through the platform accessibility API.
14
+ * > If user agents do not hide the descendant nodes,
15
+ * > some information may be read twice.
16
+ *
17
+ * @see https://w3c.github.io/aria/#tree_exclusion
18
+ *
19
+ * > Any descendants of elements that have the characteristic "Children Presentational: True"
20
+ * > unless the descendant is not allowed to be presentational
21
+ * > because it meets one of the conditions for exception described
22
+ * > in Presentational Roles Conflict Resolution.
23
+ * > However, the text content of any excluded descendants is included.
24
+ *
25
+ * @see https://w3c.github.io/aria/#tree_inclusion
26
+ *
27
+ * > Text equivalents for hidden referenced objects
28
+ * > may still be used in the name and description computation
29
+ * > even when not included in the accessibility tree.
30
+ */
31
+ const checkingPresentationalChildren = ({ el }) => t => {
32
+ const ancestor = getAncestorHasPresentationalChildren(el);
33
+ if (!ancestor) {
34
+ return;
35
+ }
36
+ if (!ancestor.role) {
37
+ return;
38
+ }
39
+ const hasAriaAttr = Array.from(el.attributes).some(attr => /^aria-|^role$/i.test(attr.name));
40
+ if (!hasAriaAttr) {
41
+ return;
42
+ }
43
+ return {
44
+ scope: el,
45
+ message: t('it may be ineffective because {0}', t("it has {0} as an ancestor that doesn't expose its descendants to the accessibility tree", t('the "{0*}" {1}', ancestor.role.name, 'role'))),
46
+ };
47
+ };
48
+ exports.checkingPresentationalChildren = checkingPresentationalChildren;
49
+ function getAncestorHasPresentationalChildren(el) {
50
+ var _a;
51
+ let current = el.parentElement;
52
+ while (current) {
53
+ const computed = (0, ml_spec_1.getComputedRole)(el.ownerMLDocument.specs, current, el.rule.option.version);
54
+ if ((_a = computed.role) === null || _a === void 0 ? void 0 : _a.childrenPresentational) {
55
+ return computed;
56
+ }
57
+ current = current.parentElement;
58
+ }
59
+ return null;
60
+ }
@@ -0,0 +1,11 @@
1
+ import type { Options } from '../types';
2
+ import type { ElementChecker } from '@markuplint/ml-core';
3
+ import type { ARIARole } from '@markuplint/ml-spec';
4
+ /**
5
+ * Required Owned Elements
6
+ *
7
+ * @see https://w3c.github.io/aria/#mustContain
8
+ */
9
+ export declare const checkingRequiredOwnedElements: ElementChecker<boolean, Options, {
10
+ role?: ARIARole | null;
11
+ }>;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkingRequiredOwnedElements = void 0;
4
+ const ml_spec_1 = require("@markuplint/ml-spec");
5
+ /**
6
+ * Required Owned Elements
7
+ *
8
+ * @see https://w3c.github.io/aria/#mustContain
9
+ */
10
+ const checkingRequiredOwnedElements = ({ el, role }) => t => {
11
+ if (!role) {
12
+ return;
13
+ }
14
+ if (!role.requiredOwnedElements.length) {
15
+ return;
16
+ }
17
+ /**
18
+ * > There may be times that required owned elements are missing,
19
+ * > for example, while editing or while loading a data set.
20
+ * > When a widget is missing required owned elements due to script execution or loading,
21
+ * > authors MUST mark a containing element with aria-busy equal to true.
22
+ * > For example, until a page is fully initialized and complete,
23
+ * > an author could mark the document element as busy.
24
+ *
25
+ * Stop to evaluate when it has `aria-busy=true` for it considers the contents are missing.
26
+ */
27
+ if (el.matches('[aria-busy="true" i]')) {
28
+ return;
29
+ }
30
+ // TODO: Needs to resolve `aria-own`
31
+ if (!el.children.length) {
32
+ return {
33
+ scope: el,
34
+ message: t('{0}. Or, {1}', t('Require {0} to content', t('the {0*} {1}', t(role.requiredOwnedElements), 'role')), t('require {0}', 'aria-busy="true"')),
35
+ };
36
+ }
37
+ const children = Array.from(el.childNodes).map(child => {
38
+ if (child.is(child.ELEMENT_NODE)) {
39
+ const computedChild = (0, ml_spec_1.getComputedRole)(child.ownerMLDocument.specs, child, child.rule.option.version);
40
+ if (role.requiredOwnedElements.some(ownedRole => (0, ml_spec_1.isRequiredOwnedElement)(computedChild, ownedRole, child.ownerMLDocument.specs, child.rule.option.version))) {
41
+ return 'REQUIRED';
42
+ }
43
+ return 'OTHER';
44
+ }
45
+ else if (child.MARKUPLINT_PREPROCESSOR_BLOCK) {
46
+ return 'PB';
47
+ }
48
+ return 'NO_ELEMENT';
49
+ });
50
+ if (children.includes('PB')) {
51
+ return {
52
+ scope: el,
53
+ message: t('{0} expects {1}', t('the "{0*}" {1}', role.name, 'role'), t(role.requiredContextRole.map(ownedRole => t('the "{0*}" {1}', ownedRole, 'role')))),
54
+ };
55
+ }
56
+ if (!children.includes('REQUIRED')) {
57
+ return {
58
+ scope: el,
59
+ message: t('{0} expects {1}', t('the "{0*}" {1}', role.name, 'role'), t('the {0*} {1}', t(role.requiredOwnedElements), 'roles')),
60
+ };
61
+ }
62
+ };
63
+ exports.checkingRequiredOwnedElements = checkingRequiredOwnedElements;
@@ -0,0 +1,9 @@
1
+ import type { Options } from '../types';
2
+ import type { ElementChecker } from '@markuplint/ml-core';
3
+ import type { ARIAProperty, ARIARole } from '@markuplint/ml-spec';
4
+ export declare const checkingRequiredProp: ElementChecker<boolean, Options, {
5
+ role?: (ARIARole & {
6
+ isImplicit?: boolean;
7
+ }) | null;
8
+ propSpecs: ARIAProperty[];
9
+ }>;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkingRequiredProp = void 0;
4
+ const checkingRequiredProp = ({ el, role, propSpecs }) => t => {
5
+ var _a;
6
+ if (!role) {
7
+ return;
8
+ }
9
+ if (role.isImplicit) {
10
+ return;
11
+ }
12
+ const requiredProps = role.ownedProperties.filter(s => s.required).map(s => s.name);
13
+ for (const requiredProp of requiredProps) {
14
+ const has = el.attributes.some(attr => {
15
+ const attrName = attr.name.toLowerCase();
16
+ return attrName === requiredProp;
17
+ });
18
+ if (!has) {
19
+ const propSpec = propSpecs.find(p => p.name === requiredProp);
20
+ return {
21
+ scope: el,
22
+ message: t('{0:c} on {1}', t('Require {0}', t('the "{0*}" {1}', requiredProp, `ARIA ${(_a = propSpec === null || propSpec === void 0 ? void 0 : propSpec.type) !== null && _a !== void 0 ? _a : 'property'}`)), t('the "{0*}" {1}', role.name, 'role')),
23
+ };
24
+ }
25
+ }
26
+ };
27
+ exports.checkingRequiredProp = checkingRequiredProp;
@@ -0,0 +1,7 @@
1
+ import type { Options } from '../types';
2
+ import type { AttrChecker } from '@markuplint/ml-core';
3
+ import type { ARIAProperty, ARIARole } from '@markuplint/ml-spec';
4
+ export declare const checkingValue: AttrChecker<boolean, Options, {
5
+ role?: ARIARole | null;
6
+ propSpecs: ARIAProperty[];
7
+ }>;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkingValue = void 0;
4
+ const helpers_1 = require("../../helpers");
5
+ const checkingValue = ({ attr, role, propSpecs }) => t => {
6
+ var _a;
7
+ if (attr.isDynamicValue) {
8
+ return;
9
+ }
10
+ const propSpec = propSpecs.find(p => p.name === attr.name);
11
+ const result = (0, helpers_1.checkAria)(attr.ownerMLDocument.specs, attr.name, attr.value, attr.rule.option.version, role === null || role === void 0 ? void 0 : role.name);
12
+ if (result.isValid) {
13
+ return;
14
+ }
15
+ return {
16
+ scope: attr,
17
+ message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0}"', attr.value), 'disallowed'), t('the "{0*}" {1}', attr.name, `ARIA ${(_a = propSpec === null || propSpec === void 0 ? void 0 : propSpec.type) !== null && _a !== void 0 ? _a : 'property'}`)) +
18
+ ('enum' in result && result.enum.length ? t('. ') + t('Allowed values are: {0}', t(result.enum)) : ''),
19
+ };
20
+ };
21
+ exports.checkingValue = checkingValue;
@@ -1,10 +1,3 @@
1
- declare type Options = {
2
- checkingValue?: boolean;
3
- checkingDeprecatedProps?: boolean;
4
- permittedAriaRoles?: boolean;
5
- disallowSetImplicitRole?: boolean;
6
- disallowSetImplicitProps?: boolean;
7
- disallowDefaultValue?: boolean;
8
- };
1
+ import type { Options } from './types';
9
2
  declare const _default: import("@markuplint/ml-core").RuleSeed<boolean, Options>;
10
3
  export default _default;
@@ -2,237 +2,96 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const ml_core_1 = require("@markuplint/ml-core");
4
4
  const helpers_1 = require("../helpers");
5
+ const abstract_role_1 = require("./checkings/abstract-role");
6
+ const default_value_1 = require("./checkings/default-value");
7
+ const deprecated_props_1 = require("./checkings/deprecated-props");
8
+ const disallowed_prop_1 = require("./checkings/disallowed-prop");
9
+ const implicit_props_1 = require("./checkings/implicit-props");
10
+ const implicit_role_1 = require("./checkings/implicit-role");
11
+ const interaction_in_hidden_1 = require("./checkings/interaction-in-hidden");
12
+ const no_global_prop_1 = require("./checkings/no-global-prop");
13
+ const non_existant_role_1 = require("./checkings/non-existant-role");
14
+ const permitted_roles_1 = require("./checkings/permitted-roles");
15
+ const presentational_children_1 = require("./checkings/presentational-children");
16
+ const required_owned_elements_1 = require("./checkings/required-owned-elements");
17
+ const required_prop_1 = require("./checkings/required-prop");
18
+ const value_1 = require("./checkings/value");
5
19
  exports.default = (0, ml_core_1.createRule)({
6
20
  defaultOptions: {
7
21
  checkingValue: true,
8
22
  checkingDeprecatedProps: true,
9
23
  permittedAriaRoles: true,
24
+ checkingRequiredOwnedElements: true,
25
+ checkingPresentationalChildren: false,
26
+ checkingInteractionInHidden: false,
10
27
  disallowSetImplicitRole: true,
11
28
  disallowSetImplicitProps: true,
12
29
  disallowDefaultValue: false,
30
+ version: '1.2',
13
31
  },
14
32
  async verify({ document, report, t }) {
15
- await document.walkOn('Element', node => {
16
- const attrSpecs = (0, ml_core_1.getAttrSpecs)(node.nameWithNS, document.specs);
17
- const html = (0, helpers_1.htmlSpec)(document.specs, node.nodeName);
18
- const { roles, ariaAttrs } = (0, helpers_1.ariaSpec)(document.specs);
19
- if (!html || !attrSpecs) {
33
+ await document.walkOn('Element', el => {
34
+ const roleAttr = el.getAttributeNode('role');
35
+ const propAttrs = el.attributes.filter(attr => /^aria-/i.test(attr.name));
36
+ const ariaAttrs = new helpers_1.Collection(roleAttr, ...propAttrs);
37
+ const elSpec = (0, ml_core_1.getSpec)(el, document.specs);
38
+ if (!elSpec) {
20
39
  return;
21
40
  }
22
- const roleAttrTokens = node.getAttributeToken('role');
23
- const roleAttr = roleAttrTokens[0];
24
- // Roles in the spec
25
- if (roleAttr) {
26
- const value = roleAttr.getValue().potential.trim().toLowerCase();
27
- const existedRole = roles.find(role => role.name === value);
28
- if (!existedRole) {
29
- // Not exist
41
+ if (!elSpec.globalAttrs['#ARIAAttrs']) {
42
+ for (const ariaAttr of ariaAttrs) {
30
43
  report({
31
- scope: node,
32
- message: t('{0} according to {1}', t('{0} does not exist', t('the "{0*}" {1}', value, 'role')), 'the WAI-ARIA specification') + `This "${value}" role does not exist in WAI-ARIA.`,
33
- line: roleAttr.startLine,
34
- col: roleAttr.startCol,
35
- raw: roleAttr.raw,
44
+ scope: ariaAttr,
45
+ message: 'ARIA attribute is disallowed',
36
46
  });
37
47
  }
38
- else if (existedRole.isAbstract) {
39
- // the abstract role
40
- report({
41
- scope: node,
42
- message: t('{0} is {1}', t('the "{0*}" {1}', value, 'role'), 'the abstract role'),
43
- line: roleAttr.startLine,
44
- col: roleAttr.startCol,
45
- raw: roleAttr.raw,
46
- });
48
+ return;
49
+ }
50
+ const computed = (0, ml_core_1.getComputedRole)(document.specs, el, el.rule.option.version);
51
+ const { props: propSpecs } = (0, ml_core_1.ariaSpecs)(document.specs, el.rule.option.version);
52
+ if (roleAttr) {
53
+ if (report((0, non_existant_role_1.checkingNonExistantRole)({ attr: roleAttr }))) {
54
+ return;
47
55
  }
48
- // Set the implicit role explicitly
49
- if (node.rule.option.disallowSetImplicitRole) {
50
- const implictRole = (0, helpers_1.getImplicitRole)(document.specs, node);
51
- if (implictRole && implictRole === value) {
52
- // the implicit role
53
- report({
54
- scope: node,
55
- message: t('{0} is {1}', t('the "{0*}" {1}', value, 'role'), t('{0} of {1}', 'the implicit role', t('the "{0*}" {1}', node.nodeName, 'element'))),
56
- line: roleAttr.startLine,
57
- col: roleAttr.startCol,
58
- raw: roleAttr.raw,
59
- });
60
- }
56
+ if (report((0, abstract_role_1.checkingAbstractRole)({ attr: roleAttr }))) {
57
+ return;
61
58
  }
62
- // Permitted ARIA Roles
63
- if (node.rule.option.permittedAriaRoles) {
64
- const permittedRoles = (0, helpers_1.getPermittedRoles)(document.specs, node);
65
- if (permittedRoles === false) {
66
- report({
67
- scope: node,
68
- message: t('{0} according to {1}', t('Cannot overwrite {0}', t('{0} of {1}', t('the {0}', 'role'), t('the "{0*}" {1}', node.nodeName, 'element'))), 'ARIA in HTML specification'),
69
- line: roleAttr.startLine,
70
- col: roleAttr.startCol,
71
- raw: roleAttr.raw,
72
- });
73
- }
74
- else if (Array.isArray(permittedRoles) && !permittedRoles.includes(value)) {
75
- report({
76
- scope: node,
77
- message: t('{0} according to {1}', t('Cannot overwrite {0} to {1}', t('the "{0*}" {1}', value, 'role'), t('the "{0*}" {1}', node.nodeName, 'element')), 'ARIA in HTML specification'),
78
- line: roleAttr.startLine,
79
- col: roleAttr.startCol,
80
- raw: roleAttr.raw,
81
- });
82
- }
59
+ report((0, required_prop_1.checkingRequiredProp)({ el, role: computed.role, propSpecs }));
60
+ if (el.rule.option.disallowSetImplicitRole) {
61
+ report((0, implicit_role_1.checkingImplicitRole)({ attr: roleAttr }));
83
62
  }
84
- }
85
- const computedRole = (0, helpers_1.getComputedRole)(document.specs, node);
86
- if (computedRole) {
87
- const role = (0, helpers_1.getRoleSpec)(document.specs, computedRole.name);
88
- if (role) {
89
- // Checking aria-* on the role
90
- for (const attr of node.attributes) {
91
- const attrName = attr.getName().potential.trim().toLowerCase();
92
- if (/^aria-/i.test(attrName)) {
93
- const statesAndProp = role.statesAndProps.find(s => s.name === attrName);
94
- if (statesAndProp) {
95
- if (node.rule.option.checkingDeprecatedProps && statesAndProp.deprecated) {
96
- report({
97
- scope: node,
98
- message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0*}" {1}', attrName, 'ARIA state/property'), 'deprecated'), t('the "{0*}" {1}', role.name, 'role')),
99
- line: attr.startLine,
100
- col: attr.startCol,
101
- raw: attr.raw,
102
- });
103
- }
104
- }
105
- else {
106
- report({
107
- scope: node,
108
- message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0*}" {1}', attrName, 'ARIA state/property'), 'disallowed'), t('the "{0*}" {1}', role.name, 'role')),
109
- line: attr.startLine,
110
- col: attr.startCol,
111
- raw: attr.raw,
112
- });
113
- }
114
- }
115
- }
116
- // Checing required props
117
- if (!computedRole.isImplicit) {
118
- const requiredProps = role.statesAndProps.filter(s => s.required).map(s => s.name);
119
- for (const requiredProp of requiredProps) {
120
- const has = node.attributes.some(attr => {
121
- const attrName = attr.getName().potential.trim().toLowerCase();
122
- return attrName === requiredProp;
123
- });
124
- if (!has) {
125
- report({
126
- scope: node,
127
- message: t('{0:c} on {1}', t('Require {0}', t('the "{0*}" {1}', requiredProp, 'ARIA state/property')), t('the "{0*}" {1}', role.name, 'role')),
128
- });
129
- }
130
- }
131
- }
63
+ if (el.rule.option.permittedAriaRoles) {
64
+ report((0, permitted_roles_1.checkingPermittedRoles)({ attr: roleAttr }));
132
65
  }
133
66
  }
134
- else {
135
- // No role element
136
- const { ariaAttrs } = (0, helpers_1.ariaSpec)(document.specs);
137
- for (const attr of node.attributes) {
138
- const attrName = attr.getName().potential.trim().toLowerCase();
139
- if (/^aria-/i.test(attrName)) {
140
- const ariaAttr = ariaAttrs.find(attr => attr.name === attrName);
141
- if (ariaAttr && !ariaAttr.isGlobal) {
142
- report({
143
- scope: node,
144
- message: t('{0} is not {1}', t('the "{0*}" {1}', attrName, 'ARIA state/property'), 'global state/property'),
145
- line: attr.startLine,
146
- col: attr.startCol,
147
- raw: attr.raw,
148
- });
149
- }
150
- }
67
+ for (const attr of propAttrs) {
68
+ report((0, disallowed_prop_1.checkingDisallowedProp)({ attr, role: computed.role, propSpecs }));
69
+ if (el.rule.option.checkingDeprecatedProps) {
70
+ report((0, deprecated_props_1.checkingDeprecatedProps)({ attr, role: computed.role, propSpecs }));
151
71
  }
152
- }
153
- for (const attr of node.attributes) {
154
- if (attr.attrType === 'html-attr' && attr.isDynamicValue) {
155
- continue;
72
+ if (el.rule.option.disallowSetImplicitProps) {
73
+ const attrSpecs = (0, ml_core_1.getAttrSpecs)(el, document.specs);
74
+ report((0, implicit_props_1.checkingImplicitProps)({ attr, propSpecs, attrSpecs }));
75
+ }
76
+ if (el.rule.option.checkingValue) {
77
+ report((0, value_1.checkingValue)({ attr, role: computed.role, propSpecs }));
78
+ }
79
+ if (el.rule.option.disallowDefaultValue) {
80
+ report((0, default_value_1.checkingDefaultValue)({ attr, propSpecs }));
156
81
  }
157
- const attrName = attr.getName().potential.trim().toLowerCase();
158
- if (/^aria-/i.test(attrName)) {
159
- const value = attr.getValue().potential.trim().toLowerCase();
160
- const propSpec = ariaAttrs.find(p => p.name === attrName);
161
- // Checking ARIA Value
162
- if (node.rule.option.checkingValue) {
163
- const result = (0, helpers_1.checkAria)(document.specs, attrName, value, computedRole === null || computedRole === void 0 ? void 0 : computedRole.name);
164
- if (!result.isValid) {
165
- report({
166
- scope: node,
167
- message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0}"', value), 'disallowed'), t('the "{0*}" {1}', attrName, 'ARIA state/property')) +
168
- ('enum' in result && result.enum.length
169
- ? t('. ') + t('Allowed values are: {0}', t(result.enum))
170
- : ''),
171
- line: attr.startLine,
172
- col: attr.startCol,
173
- raw: attr.raw,
174
- });
175
- }
176
- }
177
- // Checking implicit props
178
- if (node.rule.option.disallowSetImplicitProps) {
179
- if (propSpec && propSpec.equivalentHtmlAttrs) {
180
- for (const equivalentHtmlAttr of propSpec.equivalentHtmlAttrs) {
181
- const htmlAttrSpec = attrSpecs.find(a => a.name === equivalentHtmlAttr.htmlAttrName);
182
- const isValid = (0, helpers_1.isValidAttr)(t, equivalentHtmlAttr.htmlAttrName, equivalentHtmlAttr.value || '', false, node, attrSpecs);
183
- if (isValid && isValid.invalidType === 'non-existent') {
184
- continue;
185
- }
186
- if (node.hasAttribute(equivalentHtmlAttr.htmlAttrName)) {
187
- const targetAttrValue = node.getAttribute(equivalentHtmlAttr.htmlAttrName);
188
- if ((equivalentHtmlAttr.value == null && targetAttrValue === value) ||
189
- equivalentHtmlAttr.value === value) {
190
- report({
191
- scope: node,
192
- message: t('{0} has {1}', t('the "{0*}" {1}', attrName, 'ARIA state/property'), t('the same {0} as {1}', 'semantics', t('{0} or {1}', t('the current "{0}" {1}', equivalentHtmlAttr.htmlAttrName, 'attribute'), t('the implicit "{0}" {1}', equivalentHtmlAttr.htmlAttrName, 'attribute')))),
193
- line: attr.startLine,
194
- col: attr.startCol,
195
- raw: attr.raw,
196
- });
197
- continue;
198
- }
199
- if ((htmlAttrSpec === null || htmlAttrSpec === void 0 ? void 0 : htmlAttrSpec.type) === 'Boolean' && value !== 'false') {
200
- continue;
201
- }
202
- report({
203
- scope: node,
204
- message: t('{0} contradicts {1}', t('the "{0*}" {1}', attrName, 'ARIA state/property'), t('the current "{0}" {1}', equivalentHtmlAttr.htmlAttrName, 'attribute')),
205
- line: attr.startLine,
206
- col: attr.startCol,
207
- raw: attr.raw,
208
- });
209
- }
210
- else if (value === 'true') {
211
- if (!equivalentHtmlAttr.isNotStrictEquivalent && (htmlAttrSpec === null || htmlAttrSpec === void 0 ? void 0 : htmlAttrSpec.type) === 'Boolean') {
212
- report({
213
- scope: node,
214
- message: t('{0} contradicts {1}', t('the "{0*}" {1}', attrName, 'ARIA state/property'), t('the implicit "{0}" {1}', equivalentHtmlAttr.htmlAttrName, 'attribute')),
215
- line: attr.startLine,
216
- col: attr.startCol,
217
- raw: attr.raw,
218
- });
219
- }
220
- }
221
- }
222
- }
223
- }
224
- // Default value
225
- if (node.rule.option.disallowDefaultValue && propSpec && propSpec.defaultValue === value) {
226
- report({
227
- scope: node,
228
- message: t('It is {0}', 'default value'),
229
- line: attr.startLine,
230
- col: attr.startCol,
231
- raw: attr.raw,
232
- });
233
- }
82
+ if (!computed.role) {
83
+ report((0, no_global_prop_1.checkingNoGlobalProp)({ attr, propSpecs }));
234
84
  }
235
85
  }
86
+ if (el.rule.option.checkingRequiredOwnedElements) {
87
+ report((0, required_owned_elements_1.checkingRequiredOwnedElements)({ el, role: computed.role }));
88
+ }
89
+ if (el.rule.option.checkingPresentationalChildren) {
90
+ report((0, presentational_children_1.checkingPresentationalChildren)({ el }));
91
+ }
92
+ if (el.rule.option.checkingInteractionInHidden) {
93
+ report((0, interaction_in_hidden_1.checkingInteractionInHidden)({ el }));
94
+ }
236
95
  });
237
96
  },
238
97
  });
@@ -0,0 +1,13 @@
1
+ import type { ARIAVersion } from '@markuplint/ml-spec';
2
+ export declare type Options = {
3
+ checkingValue: boolean;
4
+ checkingDeprecatedProps: boolean;
5
+ permittedAriaRoles: boolean;
6
+ checkingRequiredOwnedElements: boolean;
7
+ checkingPresentationalChildren: boolean;
8
+ checkingInteractionInHidden: boolean;
9
+ disallowSetImplicitRole: boolean;
10
+ disallowSetImplicitProps: boolean;
11
+ disallowDefaultValue: boolean;
12
+ version: ARIAVersion;
13
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/rules",
3
- "version": "2.4.2",
3
+ "version": "3.0.0-alpha.0",
4
4
  "description": "Rules for markuplint",
5
5
  "repository": "git@github.com:markuplint/markuplint.git",
6
6
  "author": "Yusuke Hirao <yusukehirao@me.com>",
@@ -17,10 +17,10 @@
17
17
  "clean": "tsc --build --clean"
18
18
  },
19
19
  "dependencies": {
20
- "@markuplint/html-spec": "2.7.0",
21
- "@markuplint/ml-core": "2.3.2",
22
- "@markuplint/ml-spec": "2.1.0",
23
- "@markuplint/types": "2.1.0",
20
+ "@markuplint/html-spec": "3.0.0-alpha.0",
21
+ "@markuplint/ml-core": "3.0.0-alpha.0",
22
+ "@markuplint/ml-spec": "3.0.0-alpha.0",
23
+ "@markuplint/types": "3.0.0-alpha.0",
24
24
  "debug": "^4.3.4",
25
25
  "html-entities": "^2.3.3",
26
26
  "tslib": "^2.3.1"
@@ -28,5 +28,5 @@
28
28
  "devDependencies": {
29
29
  "@types/debug": "^4.1.7"
30
30
  },
31
- "gitHead": "0c774ce046ebc8a9c494e9884a4bfcd72a66250d"
31
+ "gitHead": "d32c522e016888e20bcbb5f09352c006b964f193"
32
32
  }
package/schema.json CHANGED
@@ -9,15 +9,6 @@
9
9
  "attr-duplication": {
10
10
  "$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/attr-duplication/schema.json"
11
11
  },
12
- "attr-equal-space-after": {
13
- "$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/attr-equal-space-after/schema.json"
14
- },
15
- "attr-equal-space-before": {
16
- "$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/attr-equal-space-before/schema.json"
17
- },
18
- "attr-spacing": {
19
- "$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/attr-spacing/schema.json"
20
- },
21
12
  "attr-value-quotes": {
22
13
  "$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/attr-value-quotes/schema.json"
23
14
  },
@@ -51,9 +42,6 @@
51
42
  "end-tag": {
52
43
  "$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/end-tag/schema.json"
53
44
  },
54
- "indentation": {
55
- "$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/indentation/schema.json"
56
- },
57
45
  "ineffective-attr": {
58
46
  "$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/ineffective-attr/schema.json"
59
47
  },