@markuplint/rules 4.0.3 → 4.1.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/lib/heading-levels/index.d.ts +2 -0
- package/lib/heading-levels/index.js +22 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +8 -0
- package/lib/neighbor-popovers/index.d.ts +2 -0
- package/lib/neighbor-popovers/index.js +45 -0
- package/lib/no-consecutive-br/index.d.ts +2 -0
- package/lib/no-consecutive-br/index.js +22 -0
- package/lib/no-duplicate-dt/index.d.ts +2 -0
- package/lib/no-duplicate-dt/index.js +30 -0
- package/package.json +5 -5
- package/schema.json +15 -3
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createRule } from '@markuplint/ml-core';
|
|
2
|
+
export default createRule({
|
|
3
|
+
meta: {
|
|
4
|
+
category: 'validation',
|
|
5
|
+
},
|
|
6
|
+
defaultValue: true,
|
|
7
|
+
defaultOptions: null,
|
|
8
|
+
verify({ document, report, t }) {
|
|
9
|
+
const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
|
|
10
|
+
let prevLevel = 1;
|
|
11
|
+
for (const heading of headings) {
|
|
12
|
+
const level = Number.parseInt(heading.nodeName.slice(1));
|
|
13
|
+
if (prevLevel + 1 < level) {
|
|
14
|
+
report({
|
|
15
|
+
scope: heading,
|
|
16
|
+
message: t('Heading levels should not be skipped'),
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
prevLevel = level;
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
});
|
package/lib/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ declare const rules: {
|
|
|
12
12
|
denyObsoleteType: boolean;
|
|
13
13
|
}>>;
|
|
14
14
|
readonly 'end-tag': Readonly<import("@markuplint/ml-core").RuleSeed<boolean, undefined>>;
|
|
15
|
+
readonly 'heading-levels': Readonly<import("@markuplint/ml-core").RuleSeed<boolean, null>>;
|
|
15
16
|
readonly 'id-duplication': Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
|
|
16
17
|
readonly 'ineffective-attr': Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
|
|
17
18
|
readonly 'invalid-attr': Readonly<import("@markuplint/ml-core").RuleSeed<boolean, {
|
|
@@ -64,8 +65,11 @@ declare const rules: {
|
|
|
64
65
|
ignoreRoles: (("banner" | "main" | "complementary" | "contentinfo") | "form" | "navigation" | "region")[];
|
|
65
66
|
labelEachArea: boolean;
|
|
66
67
|
}>>;
|
|
68
|
+
readonly 'neighbor-popovers': Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
|
|
67
69
|
readonly 'no-boolean-attr-value': Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
|
|
70
|
+
readonly 'no-consecutive-br': Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
|
|
68
71
|
readonly 'no-default-value': Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
|
|
72
|
+
readonly 'no-duplicate-dt': Readonly<import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, undefined>>;
|
|
69
73
|
readonly 'no-empty-palpable-content': Readonly<import("@markuplint/ml-core").RuleSeed<boolean, {
|
|
70
74
|
extendsExposableElements?: boolean | undefined;
|
|
71
75
|
ignoreIfAriaBusy?: boolean | undefined;
|
package/lib/index.js
CHANGED
|
@@ -9,13 +9,17 @@ import DeprecatedElement from './deprecated-element/index.js';
|
|
|
9
9
|
import DisallowedElement from './disallowed-element/index.js';
|
|
10
10
|
import Doctype from './doctype/index.js';
|
|
11
11
|
import EndTag from './end-tag/index.js';
|
|
12
|
+
import HeadingLevels from './heading-levels/index.js';
|
|
12
13
|
import IdDuplication from './id-duplication/index.js';
|
|
13
14
|
import IneffectiveAttr from './ineffective-attr/index.js';
|
|
14
15
|
import InvalidAttr from './invalid-attr/index.js';
|
|
15
16
|
import LabelHasControl from './label-has-control/index.js';
|
|
16
17
|
import LandmarkRoles from './landmark-roles/index.js';
|
|
18
|
+
import NeighborPopovers from './neighbor-popovers/index.js';
|
|
17
19
|
import NoBooleanAttrValue from './no-boolean-attr-value/index.js';
|
|
20
|
+
import NoConsecutiveBr from './no-consecutive-br/index.js';
|
|
18
21
|
import NoDefaultValue from './no-default-value/index.js';
|
|
22
|
+
import NoDuplicateDt from './no-duplicate-dt/index.js';
|
|
19
23
|
import NoEmptyPalpableContent from './no-empty-palpable-content/index.js';
|
|
20
24
|
import NoHardCodeId from './no-hard-code-id/index.js';
|
|
21
25
|
import NoReferToNonExistentId from './no-refer-to-non-existent-id/index.js';
|
|
@@ -41,13 +45,17 @@ const rules = {
|
|
|
41
45
|
'disallowed-element': DisallowedElement,
|
|
42
46
|
doctype: Doctype,
|
|
43
47
|
'end-tag': EndTag,
|
|
48
|
+
'heading-levels': HeadingLevels,
|
|
44
49
|
'id-duplication': IdDuplication,
|
|
45
50
|
'ineffective-attr': IneffectiveAttr,
|
|
46
51
|
'invalid-attr': InvalidAttr,
|
|
47
52
|
'label-has-control': LabelHasControl,
|
|
48
53
|
'landmark-roles': LandmarkRoles,
|
|
54
|
+
'neighbor-popovers': NeighborPopovers,
|
|
49
55
|
'no-boolean-attr-value': NoBooleanAttrValue,
|
|
56
|
+
'no-consecutive-br': NoConsecutiveBr,
|
|
50
57
|
'no-default-value': NoDefaultValue,
|
|
58
|
+
'no-duplicate-dt': NoDuplicateDt,
|
|
51
59
|
'no-empty-palpable-content': NoEmptyPalpableContent,
|
|
52
60
|
'no-hard-code-id': NoHardCodeId,
|
|
53
61
|
'no-refer-to-non-existent-id': NoReferToNonExistentId,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { createRule } from '@markuplint/ml-core';
|
|
2
|
+
import { mayBeFocusable } from '@markuplint/ml-spec';
|
|
3
|
+
// TODO: It will be received from config
|
|
4
|
+
const ARIA_VERSION = '1.2';
|
|
5
|
+
export default createRule({
|
|
6
|
+
meta: {
|
|
7
|
+
category: 'a11y',
|
|
8
|
+
},
|
|
9
|
+
verify({ document, report, t }) {
|
|
10
|
+
const triggers = document.querySelectorAll('[popovertarget]');
|
|
11
|
+
Triggers: for (const trigger of triggers) {
|
|
12
|
+
const targetId = trigger.getAttribute('popovertarget');
|
|
13
|
+
if (!targetId) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
const target = document.getElementById(targetId);
|
|
17
|
+
if (!target) {
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
if (!target.hasAttribute('popover')) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
const subsequentNodes = trigger.findSubsequentNodes();
|
|
24
|
+
for (const node of subsequentNodes) {
|
|
25
|
+
if (node === target) {
|
|
26
|
+
continue Triggers;
|
|
27
|
+
}
|
|
28
|
+
if ((node.is(node.ELEMENT_NODE) &&
|
|
29
|
+
// Element has accessible name
|
|
30
|
+
(node.getAccessibleName(ARIA_VERSION) ||
|
|
31
|
+
// Element is focusable
|
|
32
|
+
mayBeFocusable(node, node.ownerMLDocument.specs))) ||
|
|
33
|
+
(node.is(node.TEXT_NODE) &&
|
|
34
|
+
// Text node has non-whitespace characters
|
|
35
|
+
node.nodeValue?.trim())) {
|
|
36
|
+
report({
|
|
37
|
+
scope: node,
|
|
38
|
+
message: t('Detected {0} between {1} and {2}', t('perceptible nodes'), t('the {0}', 'trigger'), t('corresponding {0}', 'target')),
|
|
39
|
+
});
|
|
40
|
+
continue Triggers;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { createRule } from '@markuplint/ml-core';
|
|
2
|
+
export default createRule({
|
|
3
|
+
meta: {
|
|
4
|
+
category: 'a11y',
|
|
5
|
+
},
|
|
6
|
+
defaultSeverity: 'warning',
|
|
7
|
+
verify({ document, report, t }) {
|
|
8
|
+
const brList = [...document.querySelectorAll('br')];
|
|
9
|
+
for (const br of brList) {
|
|
10
|
+
let next = br.nextSibling;
|
|
11
|
+
while (next && next.is(next.TEXT_NODE) && !next.nodeValue?.trim()) {
|
|
12
|
+
next = next.nextSibling;
|
|
13
|
+
}
|
|
14
|
+
if (next && next.nodeName === 'BR') {
|
|
15
|
+
report({
|
|
16
|
+
scope: next,
|
|
17
|
+
message: t('{0} detected', t('Consecutive {0}', t('"{0*}" {1}', 'br', 'elements'))),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { createRule } from '@markuplint/ml-core';
|
|
2
|
+
export default createRule({
|
|
3
|
+
meta: {
|
|
4
|
+
category: 'validation',
|
|
5
|
+
},
|
|
6
|
+
verify({ document, report, t }) {
|
|
7
|
+
const dlList = [...document.querySelectorAll('dl')];
|
|
8
|
+
for (const dl of dlList) {
|
|
9
|
+
const dtList = [...dl.querySelectorAll(':scope > dt, :scope > div > dt')];
|
|
10
|
+
const names = new Set();
|
|
11
|
+
for (const dt of dtList) {
|
|
12
|
+
// TODO: Supoort for alternative text for images and accessible names for contained elements.
|
|
13
|
+
const name = dt.textContent?.trim();
|
|
14
|
+
if (!name) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (name) {
|
|
18
|
+
if (names.has(name)) {
|
|
19
|
+
report({
|
|
20
|
+
scope: dt,
|
|
21
|
+
message: t('{0} {1:c}', t('The {0}', 'name'), 'duplicated'),
|
|
22
|
+
});
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
names.add(name);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/rules",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Built-in rules of markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
@@ -25,10 +25,10 @@
|
|
|
25
25
|
"./lib/permitted-contents/debug.js": "./lib/permitted-contents/debug.browser.js"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@markuplint/html-spec": "4.0
|
|
29
|
-
"@markuplint/ml-core": "4.0
|
|
28
|
+
"@markuplint/html-spec": "4.1.0",
|
|
29
|
+
"@markuplint/ml-core": "4.1.0",
|
|
30
30
|
"@markuplint/ml-spec": "4.0.1",
|
|
31
|
-
"@markuplint/selector": "4.0
|
|
31
|
+
"@markuplint/selector": "4.1.0",
|
|
32
32
|
"@markuplint/shared": "4.0.1",
|
|
33
33
|
"@markuplint/types": "4.0.1",
|
|
34
34
|
"@types/debug": "^4.1.12",
|
|
@@ -38,5 +38,5 @@
|
|
|
38
38
|
"debug": "^4.3.4",
|
|
39
39
|
"type-fest": "^4.10.2"
|
|
40
40
|
},
|
|
41
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "b9817c30c2df71faa866e3b3fe286afa499deede"
|
|
42
42
|
}
|
package/schema.json
CHANGED
|
@@ -36,12 +36,15 @@
|
|
|
36
36
|
"doctype": {
|
|
37
37
|
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/doctype/schema.json"
|
|
38
38
|
},
|
|
39
|
-
"id-duplication": {
|
|
40
|
-
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/id-duplication/schema.json"
|
|
41
|
-
},
|
|
42
39
|
"end-tag": {
|
|
43
40
|
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/end-tag/schema.json"
|
|
44
41
|
},
|
|
42
|
+
"heading-levels": {
|
|
43
|
+
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/heading-levels/schema.json"
|
|
44
|
+
},
|
|
45
|
+
"id-duplication": {
|
|
46
|
+
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/id-duplication/schema.json"
|
|
47
|
+
},
|
|
45
48
|
"ineffective-attr": {
|
|
46
49
|
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/ineffective-attr/schema.json"
|
|
47
50
|
},
|
|
@@ -54,12 +57,21 @@
|
|
|
54
57
|
"landmark-roles": {
|
|
55
58
|
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/landmark-roles/schema.json"
|
|
56
59
|
},
|
|
60
|
+
"neighbor-popovers": {
|
|
61
|
+
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/neighbor-popovers/schema.json"
|
|
62
|
+
},
|
|
57
63
|
"no-boolean-attr-value": {
|
|
58
64
|
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/no-boolean-attr-value/schema.json"
|
|
59
65
|
},
|
|
66
|
+
"no-consecutive-br": {
|
|
67
|
+
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/no-consecutive-br/schema.json"
|
|
68
|
+
},
|
|
60
69
|
"no-default-value": {
|
|
61
70
|
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/no-default-value/schema.json"
|
|
62
71
|
},
|
|
72
|
+
"no-duplicate-dt": {
|
|
73
|
+
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/no-duplicate-dt/schema.json"
|
|
74
|
+
},
|
|
63
75
|
"no-empty-palpable-content": {
|
|
64
76
|
"$ref": "https://raw.githubusercontent.com/markuplint/markuplint/main/packages/%40markuplint/rules/src/no-empty-palpable-content/schema.json"
|
|
65
77
|
},
|