@markuplint/rules 3.0.0-dev.25 → 3.0.0-dev.290
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/attr-check.d.ts +3 -2
- package/lib/attr-check.js +42 -38
- package/lib/attr-duplication/index.d.ts +1 -1
- package/lib/attr-duplication/index.js +3 -5
- package/lib/attr-value-quotes/index.d.ts +1 -1
- package/lib/attr-value-quotes/index.js +5 -9
- package/lib/case-sensitive-attr-name/index.d.ts +1 -1
- package/lib/case-sensitive-attr-name/index.js +9 -13
- package/lib/case-sensitive-tag-name/index.d.ts +1 -1
- package/lib/case-sensitive-tag-name/index.js +2 -4
- package/lib/character-reference/index.d.ts +1 -1
- package/lib/character-reference/index.js +7 -9
- package/lib/class-naming/index.d.ts +1 -1
- package/lib/class-naming/index.js +9 -13
- package/lib/create-message.d.ts +3 -2
- package/lib/create-message.js +15 -17
- package/lib/debug.js +2 -6
- package/lib/deprecated-attr/index.d.ts +1 -1
- package/lib/deprecated-attr/index.js +6 -9
- package/lib/deprecated-element/index.d.ts +1 -1
- package/lib/deprecated-element/index.js +5 -7
- package/lib/disallowed-element/index.d.ts +1 -1
- package/lib/disallowed-element/index.js +2 -4
- package/lib/doctype/index.d.ts +1 -1
- package/lib/doctype/index.js +2 -4
- package/lib/end-tag/index.d.ts +1 -1
- package/lib/end-tag/index.js +5 -8
- package/lib/helpers.d.ts +10 -13
- package/lib/helpers.js +39 -37
- package/lib/id-duplication/index.d.ts +1 -1
- package/lib/id-duplication/index.js +2 -4
- package/lib/index.d.ts +102 -3
- package/lib/index.js +64 -62
- package/lib/ineffective-attr/index.d.ts +1 -1
- package/lib/ineffective-attr/index.js +5 -10
- package/lib/invalid-attr/index.d.ts +25 -5
- package/lib/invalid-attr/index.js +166 -43
- package/lib/label-has-control/index.d.ts +1 -1
- package/lib/label-has-control/index.js +2 -4
- package/lib/landmark-roles/index.d.ts +1 -1
- package/lib/landmark-roles/index.js +10 -9
- package/lib/no-boolean-attr-value/index.d.ts +1 -1
- package/lib/no-boolean-attr-value/index.js +6 -9
- package/lib/no-default-value/index.d.ts +1 -1
- package/lib/no-default-value/index.js +9 -12
- package/lib/no-empty-palpable-content/index.d.ts +1 -1
- package/lib/no-empty-palpable-content/index.js +13 -7
- package/lib/no-hard-code-id/index.d.ts +1 -1
- package/lib/no-hard-code-id/index.js +2 -4
- package/lib/no-refer-to-non-existent-id/index.d.ts +4 -4
- package/lib/no-refer-to-non-existent-id/index.js +79 -23
- package/lib/no-use-event-handler-attr/index.d.ts +1 -1
- package/lib/no-use-event-handler-attr/index.js +4 -6
- package/lib/permitted-contents/choice.d.ts +3 -2
- package/lib/permitted-contents/choice.js +54 -14
- package/lib/permitted-contents/complex-branch.d.ts +3 -2
- package/lib/permitted-contents/complex-branch.js +12 -14
- package/lib/permitted-contents/content-model.d.ts +2 -2
- package/lib/permitted-contents/content-model.js +11 -11
- package/lib/permitted-contents/count-pattern.d.ts +3 -2
- package/lib/permitted-contents/count-pattern.js +76 -74
- package/lib/permitted-contents/debug.browser.js +9 -12
- package/lib/permitted-contents/debug.d.ts +8 -1
- package/lib/permitted-contents/debug.js +12 -13
- package/lib/permitted-contents/index.d.ts +2 -2
- package/lib/permitted-contents/index.js +18 -24
- package/lib/permitted-contents/matches-selector.d.ts +2 -2
- package/lib/permitted-contents/matches-selector.js +17 -20
- package/lib/permitted-contents/order.d.ts +3 -2
- package/lib/permitted-contents/order.js +33 -28
- package/lib/permitted-contents/recursive-branch.d.ts +4 -3
- package/lib/permitted-contents/recursive-branch.js +11 -13
- package/lib/permitted-contents/represent-transparent-nodes.d.ts +1 -1
- package/lib/permitted-contents/represent-transparent-nodes.js +18 -21
- package/lib/permitted-contents/start.d.ts +3 -2
- package/lib/permitted-contents/start.js +16 -22
- package/lib/permitted-contents/transparent.d.ts +2 -2
- package/lib/permitted-contents/transparent.js +6 -9
- package/lib/permitted-contents/types.d.ts +20 -13
- package/lib/permitted-contents/types.js +1 -2
- package/lib/permitted-contents/utils.d.ts +27 -8
- package/lib/permitted-contents/utils.js +164 -65
- package/lib/placeholder-label-option/index.d.ts +2 -0
- package/lib/placeholder-label-option/index.js +84 -0
- package/lib/require-accessible-name/index.d.ts +1 -1
- package/lib/require-accessible-name/index.js +10 -13
- package/lib/require-datetime/index.d.ts +6 -0
- package/lib/require-datetime/index.js +32 -0
- package/lib/require-datetime/types.d.ts +10 -0
- package/lib/require-datetime/types.js +1 -0
- package/lib/require-datetime/utils.d.ts +11 -0
- package/lib/require-datetime/utils.js +130 -0
- package/lib/required-attr/index.d.ts +2 -2
- package/lib/required-attr/index.js +24 -24
- package/lib/required-element/index.d.ts +1 -1
- package/lib/required-element/index.js +3 -5
- package/lib/required-h1/index.d.ts +1 -1
- package/lib/required-h1/index.js +4 -6
- package/lib/use-list/index.d.ts +2 -2
- package/lib/use-list/index.js +7 -9
- package/lib/wai-aria/checkings/abstract-role.d.ts +1 -1
- package/lib/wai-aria/checkings/abstract-role.js +5 -10
- package/lib/wai-aria/checkings/default-value.d.ts +2 -2
- package/lib/wai-aria/checkings/default-value.js +2 -6
- package/lib/wai-aria/checkings/deprecated-props.d.ts +2 -2
- package/lib/wai-aria/checkings/deprecated-props.js +3 -8
- package/lib/wai-aria/checkings/disallowed-prop.d.ts +2 -2
- package/lib/wai-aria/checkings/disallowed-prop.js +27 -8
- package/lib/wai-aria/checkings/implicit-props.d.ts +3 -3
- package/lib/wai-aria/checkings/implicit-props.js +7 -11
- package/lib/wai-aria/checkings/implicit-role.d.ts +1 -1
- package/lib/wai-aria/checkings/implicit-role.js +4 -9
- package/lib/wai-aria/checkings/interaction-in-hidden.d.ts +1 -1
- package/lib/wai-aria/checkings/interaction-in-hidden.js +7 -9
- package/lib/wai-aria/checkings/no-global-prop.d.ts +2 -2
- package/lib/wai-aria/checkings/no-global-prop.js +2 -7
- package/lib/wai-aria/checkings/non-existent-role.d.ts +1 -1
- package/lib/wai-aria/checkings/non-existent-role.js +4 -9
- package/lib/wai-aria/checkings/permitted-roles.d.ts +1 -1
- package/lib/wai-aria/checkings/permitted-roles.js +4 -9
- package/lib/wai-aria/checkings/presentational-children.d.ts +1 -1
- package/lib/wai-aria/checkings/presentational-children.js +7 -10
- package/lib/wai-aria/checkings/required-owned-elements.d.ts +1 -1
- package/lib/wai-aria/checkings/required-owned-elements.js +10 -12
- package/lib/wai-aria/checkings/required-prop.d.ts +2 -2
- package/lib/wai-aria/checkings/required-prop.js +2 -7
- package/lib/wai-aria/checkings/value.d.ts +3 -3
- package/lib/wai-aria/checkings/value.js +7 -11
- package/lib/wai-aria/index.d.ts +2 -2
- package/lib/wai-aria/index.js +38 -39
- package/lib/wai-aria/types.js +1 -2
- package/package.json +21 -14
- package/schema.json +6 -0
- package/tsconfig.test.json +0 -3
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import * as chrono from 'chrono-node';
|
|
2
|
+
const defaultLangs = ['en', 'ja', 'fr', 'nl', 'ru', 'de', 'pt', 'zh'];
|
|
3
|
+
/**
|
|
4
|
+
* Datetime-ish text to a datetime data
|
|
5
|
+
*
|
|
6
|
+
* @param content
|
|
7
|
+
* @param langs
|
|
8
|
+
* @param base Reference date for a test
|
|
9
|
+
* @returns
|
|
10
|
+
*/
|
|
11
|
+
export function parseADatetime(content, langs, base) {
|
|
12
|
+
const date = parseTryMultipleLangs(content, langs, base);
|
|
13
|
+
if (!date) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const data = {};
|
|
17
|
+
if (date.isCertain('year')) {
|
|
18
|
+
data.year = date.get('year');
|
|
19
|
+
}
|
|
20
|
+
if (date.isCertain('month')) {
|
|
21
|
+
data.month = date.get('month');
|
|
22
|
+
}
|
|
23
|
+
if (date.isCertain('day')) {
|
|
24
|
+
data.day = date.get('day');
|
|
25
|
+
}
|
|
26
|
+
if (date.isCertain('hour')) {
|
|
27
|
+
data.hour = date.get('hour');
|
|
28
|
+
}
|
|
29
|
+
if (date.isCertain('hour')) {
|
|
30
|
+
data.minute = date.get('minute') ?? 0;
|
|
31
|
+
}
|
|
32
|
+
if (date.isCertain('second')) {
|
|
33
|
+
data.second = date.get('second');
|
|
34
|
+
}
|
|
35
|
+
if (date.isCertain('millisecond')) {
|
|
36
|
+
data.ms = date.get('millisecond');
|
|
37
|
+
}
|
|
38
|
+
const datetime = {
|
|
39
|
+
datetime: data,
|
|
40
|
+
};
|
|
41
|
+
if (date.isCertain('timezoneOffset')) {
|
|
42
|
+
datetime.zone = date.get('timezoneOffset');
|
|
43
|
+
}
|
|
44
|
+
return datetime;
|
|
45
|
+
}
|
|
46
|
+
export function getCandidateDatetimeString(content, langs = defaultLangs) {
|
|
47
|
+
const date = parseADatetime(content, langs);
|
|
48
|
+
if (!date) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
let datetimeStr = toDatetimeString(date.datetime);
|
|
52
|
+
if (!datetimeStr) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
if (date.zone != null) {
|
|
56
|
+
const plusMinus = date.zone < 0 ? '-' : '+';
|
|
57
|
+
const hour = Math.floor(Math.abs(date.zone) / 60);
|
|
58
|
+
const minute = Math.abs(date.zone) % 60;
|
|
59
|
+
datetimeStr += `${plusMinus}${f(hour, 2)}${f(minute, 2)}`;
|
|
60
|
+
}
|
|
61
|
+
return datetimeStr;
|
|
62
|
+
}
|
|
63
|
+
function toDatetimeString(date) {
|
|
64
|
+
if (only(date, ['year', 'month'])) {
|
|
65
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}`;
|
|
66
|
+
}
|
|
67
|
+
if (only(date, ['year', 'month', 'day'])) {
|
|
68
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}-${f(date.day, 2)}`;
|
|
69
|
+
}
|
|
70
|
+
if (only(date, ['month', 'day'])) {
|
|
71
|
+
return `${f(date.month, 2)}-${f(date.day, 2)}`;
|
|
72
|
+
}
|
|
73
|
+
if (only(date, ['hour', 'minute'])) {
|
|
74
|
+
return `${f(date.hour, 2)}:${f(date.minute, 2)}`;
|
|
75
|
+
}
|
|
76
|
+
if (only(date, ['hour', 'minute', 'second'])) {
|
|
77
|
+
return `${f(date.hour, 2)}:${f(date.minute, 2)}:${f(date.second, 2)}`;
|
|
78
|
+
}
|
|
79
|
+
if (only(date, ['hour', 'minute', 'second', 'ms'])) {
|
|
80
|
+
return `${f(date.hour, 2)}:${f(date.minute, 2)}:${f(date.second, 2)}.${date.ms}`;
|
|
81
|
+
}
|
|
82
|
+
if (only(date, ['year', 'month', 'day', 'hour', 'minute'])) {
|
|
83
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}-${f(date.day, 2)}T${f(date.hour, 2)}:${f(date.minute, 2)}`;
|
|
84
|
+
}
|
|
85
|
+
if (only(date, ['year', 'month', 'day', 'hour', 'minute', 'second'])) {
|
|
86
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}-${f(date.day, 2)}T${f(date.hour, 2)}:${f(date.minute, 2)}:${f(date.second, 2)}`;
|
|
87
|
+
}
|
|
88
|
+
if (only(date, ['year', 'month', 'day', 'hour', 'minute', 'second', 'ms'])) {
|
|
89
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}-${f(date.day, 2)}T${f(date.hour, 2)}:${f(date.minute, 2)}:${f(date.second, 2)}.${date.ms}`;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
function parseTryMultipleLangs(content, langs, base) {
|
|
94
|
+
for (const lang of langs) {
|
|
95
|
+
const results = chrono[lang].casual.parse(content, base);
|
|
96
|
+
// Is not multiple datetime contents
|
|
97
|
+
if (results.length < 1) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const result = results[0];
|
|
101
|
+
if (!result) {
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
// Is not a range or period
|
|
105
|
+
if (result.end) {
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
return result.start;
|
|
109
|
+
}
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
function only(date, keys) {
|
|
113
|
+
const list = Object.keys(date);
|
|
114
|
+
for (const exists of list) {
|
|
115
|
+
if (!keys.includes(exists)) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Formatter
|
|
123
|
+
*
|
|
124
|
+
* @param n
|
|
125
|
+
* @param pad zero padding
|
|
126
|
+
* @returns
|
|
127
|
+
*/
|
|
128
|
+
function f(n, pad) {
|
|
129
|
+
return n.toString(10).padStart(pad, '0');
|
|
130
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
type RequiredAttributes = string | (string | Attr)[];
|
|
2
2
|
type Attr = {
|
|
3
3
|
name: string;
|
|
4
|
-
value
|
|
4
|
+
value?: string | string[];
|
|
5
5
|
};
|
|
6
|
-
declare const _default: import("@markuplint/ml-core").RuleSeed<RequiredAttributes,
|
|
6
|
+
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<RequiredAttributes, undefined>>;
|
|
7
7
|
export default _default;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const helpers_1 = require("../helpers");
|
|
5
|
-
exports.default = (0, ml_core_1.createRule)({
|
|
1
|
+
import { createRule, getAttrSpecs } from '@markuplint/ml-core';
|
|
2
|
+
import { attrMatches, match } from '../helpers.js';
|
|
3
|
+
export default createRule({
|
|
6
4
|
defaultValue: [],
|
|
7
5
|
async verify({ document, report, t }) {
|
|
8
6
|
await document.walkOn('Element', el => {
|
|
@@ -10,11 +8,11 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
10
8
|
return;
|
|
11
9
|
}
|
|
12
10
|
const customRequiredAttrs = typeof el.rule.value === 'string' ? [el.rule.value] : el.rule.value;
|
|
13
|
-
const attrSpec =
|
|
11
|
+
const attrSpec = getAttrSpecs(el, document.specs);
|
|
14
12
|
const attributeSpecs = {};
|
|
15
13
|
if (attrSpec && el.elementType === 'html') {
|
|
16
14
|
for (const spec of attrSpec) {
|
|
17
|
-
if (spec.required || spec.requiredEither || spec.condition) {
|
|
15
|
+
if (spec.required != null || spec.requiredEither || spec.condition != null) {
|
|
18
16
|
attributeSpecs[spec.name] = {
|
|
19
17
|
...spec,
|
|
20
18
|
values: [],
|
|
@@ -33,7 +31,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
33
31
|
if (Array.isArray(req.value)) {
|
|
34
32
|
values.push(...req.value);
|
|
35
33
|
}
|
|
36
|
-
else {
|
|
34
|
+
else if (req.value) {
|
|
37
35
|
values.push(req.value);
|
|
38
36
|
}
|
|
39
37
|
}
|
|
@@ -51,10 +49,10 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
51
49
|
invalid = !candidate.some(attrName => el.hasAttribute(attrName));
|
|
52
50
|
}
|
|
53
51
|
else if (spec.required === true) {
|
|
54
|
-
invalid =
|
|
52
|
+
invalid = attrMatches(el, spec.condition) && didntHave;
|
|
55
53
|
}
|
|
56
|
-
else if (spec.required) {
|
|
57
|
-
const selector =
|
|
54
|
+
else if (spec.required != null && spec.required !== false) {
|
|
55
|
+
const selector = typeof spec.required === 'string' ? spec.required : spec.required.join(',');
|
|
58
56
|
invalid = el.matches(selector) && didntHave;
|
|
59
57
|
}
|
|
60
58
|
if (invalid) {
|
|
@@ -65,23 +63,25 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
65
63
|
});
|
|
66
64
|
}
|
|
67
65
|
const value = el.getAttribute(spec.name);
|
|
68
|
-
if (spec.values.length && value) {
|
|
69
|
-
const matched = spec.values.some(pattern =>
|
|
66
|
+
if (spec.values.length > 0 && value) {
|
|
67
|
+
const matched = spec.values.some(pattern => match(value, pattern));
|
|
70
68
|
if (!matched) {
|
|
71
69
|
const expects = spec.values.length === 1 ? t(spec.values) : t('either {0}', t(spec.values));
|
|
72
70
|
const message = t('{0} expects {1}', t('the "{0*}" {1}', spec.name, 'attribute'), expects);
|
|
73
71
|
const attrToken = el.getAttributeToken(spec.name);
|
|
74
|
-
const valueToken = attrToken[0]
|
|
75
|
-
const token = valueToken
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
72
|
+
const valueToken = attrToken[0]?.valueNode;
|
|
73
|
+
const token = valueToken ?? attrToken[0];
|
|
74
|
+
if (token) {
|
|
75
|
+
report({
|
|
76
|
+
scope: {
|
|
77
|
+
rule: el.rule,
|
|
78
|
+
raw: token.raw,
|
|
79
|
+
startCol: token.startCol,
|
|
80
|
+
startLine: token.startLine,
|
|
81
|
+
},
|
|
82
|
+
message,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const ml_core_1 = require("@markuplint/ml-core");
|
|
4
|
-
exports.default = (0, ml_core_1.createRule)({
|
|
1
|
+
import { createRule } from '@markuplint/ml-core';
|
|
2
|
+
export default createRule({
|
|
5
3
|
defaultValue: [],
|
|
6
4
|
defaultOptions: {
|
|
7
5
|
ignoreHasMutableContents: true,
|
|
@@ -17,7 +15,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
17
15
|
message,
|
|
18
16
|
line: 1,
|
|
19
17
|
col: 1,
|
|
20
|
-
raw: document.nodeList[0]
|
|
18
|
+
raw: document.nodeList[0]?.raw.slice(0, 1) ?? '',
|
|
21
19
|
});
|
|
22
20
|
}
|
|
23
21
|
}
|
|
@@ -2,5 +2,5 @@ export interface Options {
|
|
|
2
2
|
'expected-once': boolean;
|
|
3
3
|
'in-document-fragment': boolean;
|
|
4
4
|
}
|
|
5
|
-
declare const _default: import("@markuplint/ml-core").RuleSeed<boolean, Options
|
|
5
|
+
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<boolean, Options>>;
|
|
6
6
|
export default _default;
|
package/lib/required-h1/index.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const ml_core_1 = require("@markuplint/ml-core");
|
|
4
|
-
exports.default = (0, ml_core_1.createRule)({
|
|
1
|
+
import { createRule } from '@markuplint/ml-core';
|
|
2
|
+
export default createRule({
|
|
5
3
|
defaultOptions: {
|
|
6
4
|
'expected-once': true,
|
|
7
5
|
'in-document-fragment': false,
|
|
@@ -25,10 +23,10 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
25
23
|
message,
|
|
26
24
|
line: 1,
|
|
27
25
|
col: 1,
|
|
28
|
-
raw: document.nodeList[0]
|
|
26
|
+
raw: document.nodeList[0]?.raw.slice(0, 1) ?? '',
|
|
29
27
|
});
|
|
30
28
|
}
|
|
31
|
-
else if (document.rule.options['expected-once'] && h1Stack.length > 1) {
|
|
29
|
+
else if (document.rule.options['expected-once'] && h1Stack.length > 1 && h1Stack[1]) {
|
|
32
30
|
const message = t('{0} is {1:c}', t('the "{0*}" {1}', 'h1', 'element'), 'duplicated');
|
|
33
31
|
report({
|
|
34
32
|
message,
|
package/lib/use-list/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
type Bullets = string[];
|
|
1
|
+
type Bullets = readonly string[];
|
|
2
2
|
type Options = {
|
|
3
3
|
spaceNeededBullets?: string[];
|
|
4
4
|
};
|
|
5
|
-
declare const _default: import("@markuplint/ml-core").RuleSeed<Bullets, Options
|
|
5
|
+
declare const _default: Readonly<import("@markuplint/ml-core").RuleSeed<Bullets, Options>>;
|
|
6
6
|
export default _default;
|
package/lib/use-list/index.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const helpers_1 = require("../helpers");
|
|
5
|
-
exports.default = (0, ml_core_1.createRule)({
|
|
1
|
+
import { createRule } from '@markuplint/ml-core';
|
|
2
|
+
import { decodeEntities } from '@markuplint/shared';
|
|
3
|
+
export default createRule({
|
|
6
4
|
defaultValue: [
|
|
7
5
|
/**
|
|
8
6
|
* @see https://en.wikipedia.org/wiki/Bullet_(typography)#In_Unicode
|
|
@@ -57,7 +55,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
57
55
|
defaultSeverity: 'warning',
|
|
58
56
|
async verify({ document, report, t }) {
|
|
59
57
|
await document.walkOn('Text', textNode => {
|
|
60
|
-
const text = (
|
|
58
|
+
const text = decodeEntities(textNode.raw.trim());
|
|
61
59
|
if (!text) {
|
|
62
60
|
// empty
|
|
63
61
|
return;
|
|
@@ -67,7 +65,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
67
65
|
return;
|
|
68
66
|
}
|
|
69
67
|
const bullets = textNode.rule.value;
|
|
70
|
-
const spaceNeededBullets = textNode.rule.options.spaceNeededBullets
|
|
68
|
+
const spaceNeededBullets = textNode.rule.options.spaceNeededBullets ?? [];
|
|
71
69
|
if (isMayListItem(text, bullets, spaceNeededBullets)) {
|
|
72
70
|
report({
|
|
73
71
|
scope: textNode,
|
|
@@ -79,14 +77,14 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
79
77
|
});
|
|
80
78
|
function isMayListItem(text, bullets, spaceNeededBullets) {
|
|
81
79
|
const textArray = Array.from(text);
|
|
82
|
-
const firstLetter = textArray[0];
|
|
80
|
+
const firstLetter = textArray[0] ?? '';
|
|
83
81
|
const isBullet = bullets.includes(firstLetter);
|
|
84
82
|
const needSpace = spaceNeededBullets.includes(firstLetter);
|
|
85
83
|
const continuous = firstLetter === textArray[1];
|
|
86
84
|
if (continuous) {
|
|
87
85
|
return false;
|
|
88
86
|
}
|
|
89
|
-
if (isBullet && needSpace) {
|
|
87
|
+
if (isBullet && needSpace && text[1]) {
|
|
90
88
|
const secondLetter = text[1];
|
|
91
89
|
const isSpace = /^\s$/.test(secondLetter);
|
|
92
90
|
return isSpace;
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const checkingAbstractRole = ({ attr }) => t => {
|
|
6
|
-
var _a;
|
|
7
|
-
const { roles } = (0, ml_spec_1.ariaSpecs)(attr.ownerMLDocument.specs, attr.rule.options.version);
|
|
8
|
-
const tokens = (_a = attr.tokenList) === null || _a === void 0 ? void 0 : _a.allTokens();
|
|
1
|
+
import { ariaSpecs } from '@markuplint/ml-spec';
|
|
2
|
+
export const checkingAbstractRole = ({ attr }) => t => {
|
|
3
|
+
const { roles } = ariaSpecs(attr.ownerMLDocument.specs, attr.rule.options.version);
|
|
4
|
+
const tokens = attr.tokenList?.allTokens();
|
|
9
5
|
if (!tokens) {
|
|
10
6
|
return;
|
|
11
7
|
}
|
|
12
8
|
for (const token of tokens) {
|
|
13
9
|
const role = roles.find(r => r.name === token.raw);
|
|
14
|
-
if (role
|
|
10
|
+
if (role?.isAbstract) {
|
|
15
11
|
return {
|
|
16
12
|
scope: token,
|
|
17
13
|
message: t('{0} is {1}', t('the "{0*}" {1}', token.raw, 'role'), 'the abstract role'),
|
|
@@ -19,4 +15,3 @@ const checkingAbstractRole = ({ attr }) => t => {
|
|
|
19
15
|
}
|
|
20
16
|
}
|
|
21
17
|
};
|
|
22
|
-
exports.checkingAbstractRole = checkingAbstractRole;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Options } from '../types';
|
|
1
|
+
import type { Options } from '../types.js';
|
|
2
2
|
import type { AttrChecker } from '@markuplint/ml-core';
|
|
3
3
|
import type { ARIAProperty } from '@markuplint/ml-spec';
|
|
4
4
|
export declare const checkingDefaultValue: AttrChecker<boolean, Options, {
|
|
5
|
-
propSpecs: ARIAProperty[];
|
|
5
|
+
propSpecs: readonly ARIAProperty[];
|
|
6
6
|
}>;
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkingDefaultValue = void 0;
|
|
4
|
-
const checkingDefaultValue = ({ attr, propSpecs }) => t => {
|
|
1
|
+
export const checkingDefaultValue = ({ attr, propSpecs }) => t => {
|
|
5
2
|
if (attr.isDynamicValue) {
|
|
6
3
|
return;
|
|
7
4
|
}
|
|
8
5
|
const propSpec = propSpecs.find(p => p.name === attr.name);
|
|
9
6
|
const value = attr.value.trim().toLowerCase();
|
|
10
|
-
if (
|
|
7
|
+
if (propSpec?.defaultValue != null && propSpec.defaultValue === value) {
|
|
11
8
|
return {
|
|
12
9
|
scope: attr,
|
|
13
10
|
message: t('It is {0}', 'default value'),
|
|
14
11
|
};
|
|
15
12
|
}
|
|
16
13
|
};
|
|
17
|
-
exports.checkingDefaultValue = checkingDefaultValue;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Options } from '../types';
|
|
1
|
+
import type { Options } from '../types.js';
|
|
2
2
|
import type { AttrChecker } from '@markuplint/ml-core';
|
|
3
3
|
import type { ARIAProperty, ARIARole } from '@markuplint/ml-spec';
|
|
4
4
|
export declare const checkingDeprecatedProps: AttrChecker<boolean, Options, {
|
|
5
5
|
role: ARIARole | null;
|
|
6
|
-
propSpecs: ARIAProperty[];
|
|
6
|
+
propSpecs: readonly ARIAProperty[];
|
|
7
7
|
}>;
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkingDeprecatedProps = void 0;
|
|
4
|
-
const checkingDeprecatedProps = ({ attr, role, propSpecs }) => t => {
|
|
5
|
-
var _a;
|
|
1
|
+
export const checkingDeprecatedProps = ({ attr, role, propSpecs }) => t => {
|
|
6
2
|
if (!role) {
|
|
7
3
|
return;
|
|
8
4
|
}
|
|
9
5
|
const props = role.ownedProperties.find(p => p.name === attr.name);
|
|
10
|
-
if (props
|
|
6
|
+
if (props?.deprecated) {
|
|
11
7
|
const propSpec = propSpecs.find(p => p.name === attr.name);
|
|
12
8
|
return {
|
|
13
9
|
scope: attr,
|
|
14
|
-
message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0*}" {1}', attr.name, `ARIA ${
|
|
10
|
+
message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0*}" {1}', attr.name, `ARIA ${propSpec?.type ?? 'property'}`), 'deprecated'), t('the "{0*}" {1}', role.name, 'role')),
|
|
15
11
|
};
|
|
16
12
|
}
|
|
17
13
|
};
|
|
18
|
-
exports.checkingDeprecatedProps = checkingDeprecatedProps;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Options } from '../types';
|
|
1
|
+
import type { Options } from '../types.js';
|
|
2
2
|
import type { AttrChecker } from '@markuplint/ml-core';
|
|
3
3
|
import type { ARIAProperty, ARIARole } from '@markuplint/ml-spec';
|
|
4
4
|
export declare const checkingDisallowedProp: AttrChecker<boolean, Options, {
|
|
5
5
|
role: ARIARole | null;
|
|
6
|
-
propSpecs: ARIAProperty[];
|
|
6
|
+
propSpecs: readonly ARIAProperty[];
|
|
7
7
|
}>;
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.checkingDisallowedProp = void 0;
|
|
4
|
-
const checkingDisallowedProp = ({ attr, role, propSpecs }) => t => {
|
|
5
|
-
var _a;
|
|
1
|
+
import { getARIA } from '@markuplint/ml-spec';
|
|
2
|
+
export const checkingDisallowedProp = ({ attr, role, propSpecs }) => t => {
|
|
6
3
|
if (!role) {
|
|
7
4
|
return;
|
|
8
5
|
}
|
|
@@ -10,13 +7,35 @@ const checkingDisallowedProp = ({ attr, role, propSpecs }) => t => {
|
|
|
10
7
|
return;
|
|
11
8
|
}
|
|
12
9
|
const statesAndProp = role.ownedProperties.find(p => p.name === attr.name);
|
|
10
|
+
const propSpec = propSpecs.find(p => p.name === attr.name);
|
|
11
|
+
const elAriaSpec = getARIA(attr.ownerMLDocument.specs, attr.ownerElement.localName, attr.ownerElement.namespaceURI, attr.rule.options.version, attr.ownerElement.matches.bind(attr.ownerElement));
|
|
12
|
+
if (elAriaSpec?.properties !== false && elAriaSpec?.properties?.without) {
|
|
13
|
+
for (const ignore of elAriaSpec.properties.without) {
|
|
14
|
+
if (ignore.name === attr.name) {
|
|
15
|
+
return {
|
|
16
|
+
scope: attr,
|
|
17
|
+
message: t('{0:c} on {1}', t(ignore.type === 'must-not'
|
|
18
|
+
? '{0} must not {1}'
|
|
19
|
+
: ignore.type === 'should-not'
|
|
20
|
+
? '{0} should not {1}'
|
|
21
|
+
: '{0} is not recommended to {1}',
|
|
22
|
+
// {0}
|
|
23
|
+
t('the "{0*}" {1}', attr.name, `ARIA ${propSpec?.type ?? 'property'}`),
|
|
24
|
+
// {1}
|
|
25
|
+
'use'), t('the "{0*}" {1}', attr.ownerElement.localName, 'element')) +
|
|
26
|
+
(ignore.alt
|
|
27
|
+
? t('. ') +
|
|
28
|
+
t('{0} if you {1} {2}', t(ignore.alt.method === 'remove-attr' ? 'Remove {0}' : 'Add {0}', t('the "{0*}" {1}', ignore.alt.target, 'attribute')), 'use', t('the {0}', `ARIA ${propSpec?.type ?? 'property'}`))
|
|
29
|
+
: ''),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
13
34
|
if (statesAndProp) {
|
|
14
35
|
return;
|
|
15
36
|
}
|
|
16
|
-
const propSpec = propSpecs.find(p => p.name === attr.name);
|
|
17
37
|
return {
|
|
18
38
|
scope: attr,
|
|
19
|
-
message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0*}" {1}', attr.name, `ARIA ${
|
|
39
|
+
message: t('{0:c} on {1}', t('{0} is {1:c}', t('the "{0*}" {1}', attr.name, `ARIA ${propSpec?.type ?? 'property'}`), 'disallowed'), t('the "{0*}" {1}', role.name, 'role')),
|
|
20
40
|
};
|
|
21
41
|
};
|
|
22
|
-
exports.checkingDisallowedProp = checkingDisallowedProp;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Options } from '../types';
|
|
1
|
+
import type { Options } from '../types.js';
|
|
2
2
|
import type { AttrChecker } from '@markuplint/ml-core';
|
|
3
3
|
import type { ARIAProperty, Attribute } from '@markuplint/ml-spec';
|
|
4
4
|
export declare const checkingImplicitProps: AttrChecker<boolean, Options, {
|
|
5
|
-
propSpecs: ARIAProperty[];
|
|
6
|
-
attrSpecs: Attribute[] | null;
|
|
5
|
+
propSpecs: readonly ARIAProperty[];
|
|
6
|
+
attrSpecs: readonly Attribute[] | null;
|
|
7
7
|
}>;
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.checkingImplicitProps = void 0;
|
|
4
|
-
const helpers_1 = require("../../helpers");
|
|
5
|
-
const checkingImplicitProps = ({ attr, propSpecs, attrSpecs }) => t => {
|
|
1
|
+
import { isValidAttr } from '../../helpers.js';
|
|
2
|
+
export const checkingImplicitProps = ({ attr, propSpecs, attrSpecs }) => t => {
|
|
6
3
|
if (!attrSpecs) {
|
|
7
4
|
return;
|
|
8
5
|
}
|
|
@@ -10,13 +7,13 @@ const checkingImplicitProps = ({ attr, propSpecs, attrSpecs }) => t => {
|
|
|
10
7
|
return;
|
|
11
8
|
}
|
|
12
9
|
const propSpec = propSpecs.find(p => p.name === attr.name);
|
|
13
|
-
if (!
|
|
10
|
+
if (!propSpec?.equivalentHtmlAttrs) {
|
|
14
11
|
return;
|
|
15
12
|
}
|
|
16
13
|
for (const equivalentHtmlAttr of propSpec.equivalentHtmlAttrs) {
|
|
17
14
|
const htmlAttrSpec = attrSpecs.find(a => a.name === equivalentHtmlAttr.htmlAttrName);
|
|
18
|
-
const isValid =
|
|
19
|
-
if (isValid && isValid.invalidType === 'non-existent') {
|
|
15
|
+
const isValid = isValidAttr(t, equivalentHtmlAttr.htmlAttrName, equivalentHtmlAttr.value ?? '', false, attr.ownerElement, attrSpecs);
|
|
16
|
+
if (isValid !== false && isValid.invalidType === 'non-existent') {
|
|
20
17
|
continue;
|
|
21
18
|
}
|
|
22
19
|
const value = attr.value.trim().toLowerCase();
|
|
@@ -29,7 +26,7 @@ const checkingImplicitProps = ({ attr, propSpecs, attrSpecs }) => t => {
|
|
|
29
26
|
message: t('{0} has {1}', t('the "{0*}" {1}', attr.name, `ARIA ${propSpec.type}`), 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')))),
|
|
30
27
|
};
|
|
31
28
|
}
|
|
32
|
-
if (
|
|
29
|
+
if (htmlAttrSpec?.type === 'Boolean' && value !== 'false') {
|
|
33
30
|
continue;
|
|
34
31
|
}
|
|
35
32
|
return {
|
|
@@ -38,7 +35,7 @@ const checkingImplicitProps = ({ attr, propSpecs, attrSpecs }) => t => {
|
|
|
38
35
|
};
|
|
39
36
|
}
|
|
40
37
|
else if (value === 'true') {
|
|
41
|
-
if (!equivalentHtmlAttr.isNotStrictEquivalent &&
|
|
38
|
+
if (!equivalentHtmlAttr.isNotStrictEquivalent && htmlAttrSpec?.type === 'Boolean') {
|
|
42
39
|
return {
|
|
43
40
|
scope: attr,
|
|
44
41
|
message: t('{0} contradicts {1}', t('the "{0*}" {1}', attr.name, `ARIA ${propSpec.type}`), t('the implicit "{0}" {1}', equivalentHtmlAttr.htmlAttrName, 'attribute')),
|
|
@@ -47,4 +44,3 @@ const checkingImplicitProps = ({ attr, propSpecs, attrSpecs }) => t => {
|
|
|
47
44
|
}
|
|
48
45
|
}
|
|
49
46
|
};
|
|
50
|
-
exports.checkingImplicitProps = checkingImplicitProps;
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
const checkingImplicitRole = ({ attr }) => t => {
|
|
6
|
-
var _a;
|
|
7
|
-
const implicitRole = (0, ml_spec_1.getImplicitRoleName)(attr.ownerElement, attr.rule.options.version, attr.ownerMLDocument.specs);
|
|
8
|
-
const tokens = (_a = attr.tokenList) === null || _a === void 0 ? void 0 : _a.allTokens();
|
|
1
|
+
import { getImplicitRoleName } from '@markuplint/ml-spec';
|
|
2
|
+
export const checkingImplicitRole = ({ attr }) => t => {
|
|
3
|
+
const implicitRole = getImplicitRoleName(attr.ownerElement, attr.rule.options.version, attr.ownerMLDocument.specs);
|
|
4
|
+
const tokens = attr.tokenList?.allTokens();
|
|
9
5
|
if (!tokens) {
|
|
10
6
|
return;
|
|
11
7
|
}
|
|
@@ -18,4 +14,3 @@ const checkingImplicitRole = ({ attr }) => t => {
|
|
|
18
14
|
}
|
|
19
15
|
}
|
|
20
16
|
};
|
|
21
|
-
exports.checkingImplicitRole = checkingImplicitRole;
|
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.checkingInteractionInHidden = void 0;
|
|
4
|
-
const ml_spec_1 = require("@markuplint/ml-spec");
|
|
1
|
+
import { mayBeFocusable } from '@markuplint/ml-spec';
|
|
5
2
|
/**
|
|
6
3
|
* Including Elements in the Accessibility Tree
|
|
7
4
|
*
|
|
@@ -14,8 +11,8 @@ const ml_spec_1 = require("@markuplint/ml-spec");
|
|
|
14
11
|
// なので、aria-hidden=trueの子孫要素で且つインタラクティブ要素である場合、
|
|
15
12
|
// それは開発者にとってはほとんどの場合、意図しないことが多いはずなので、
|
|
16
13
|
// 仕様上不正にはならないが、十分に注意しなければならない状態である。
|
|
17
|
-
const checkingInteractionInHidden = ({ el }) => t => {
|
|
18
|
-
if (!
|
|
14
|
+
export const checkingInteractionInHidden = ({ el }) => t => {
|
|
15
|
+
if (!mayBeFocusable(el, el.ownerMLDocument.specs)) {
|
|
19
16
|
return;
|
|
20
17
|
}
|
|
21
18
|
const ariaHidden = getClosestAriaHidden(el);
|
|
@@ -33,12 +30,13 @@ const checkingInteractionInHidden = ({ el }) => t => {
|
|
|
33
30
|
message: t('It may be focusable in spite of it has the ancestor that has aria-hidden=true'),
|
|
34
31
|
};
|
|
35
32
|
};
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
function getClosestAriaHidden(
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
35
|
+
el) {
|
|
38
36
|
let current = el;
|
|
39
37
|
while (current) {
|
|
40
38
|
const ariaHidden = current.getAttributeNode('aria-hidden');
|
|
41
|
-
if (
|
|
39
|
+
if (ariaHidden?.value === 'true') {
|
|
42
40
|
return ariaHidden;
|
|
43
41
|
}
|
|
44
42
|
current = current.parentElement;
|