@markuplint/rules 3.0.0-canary.3 → 3.0.0-dev.177
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/README.md +1 -1
- package/lib/attr-check.d.ts +20 -8
- package/lib/attr-check.js +31 -22
- package/lib/attr-duplication/index.d.ts +3 -1
- package/lib/attr-duplication/index.js +2 -1
- package/lib/attr-value-quotes/index.d.ts +2 -2
- package/lib/case-sensitive-attr-name/index.d.ts +1 -1
- package/lib/case-sensitive-tag-name/index.d.ts +1 -1
- package/lib/character-reference/index.d.ts +1 -1
- package/lib/class-naming/index.d.ts +1 -1
- package/lib/class-naming/index.js +2 -4
- package/lib/create-message.d.ts +13 -2
- package/lib/create-message.js +4 -3
- package/lib/debug.js +1 -1
- package/lib/deprecated-attr/index.d.ts +3 -1
- package/lib/deprecated-element/index.d.ts +3 -1
- package/lib/deprecated-element/index.js +2 -2
- package/lib/disallowed-element/index.d.ts +1 -1
- package/lib/doctype/index.d.ts +2 -2
- package/lib/end-tag/index.d.ts +1 -1
- package/lib/helpers.d.ts +37 -22
- package/lib/helpers.js +27 -14
- package/lib/id-duplication/index.d.ts +3 -1
- package/lib/index.d.ts +243 -3
- package/lib/index.js +6 -1
- package/lib/ineffective-attr/index.d.ts +3 -1
- package/lib/ineffective-attr/index.js +2 -5
- package/lib/invalid-attr/index.d.ts +39 -12
- package/lib/invalid-attr/index.js +153 -27
- package/lib/label-has-control/index.d.ts +3 -1
- package/lib/landmark-roles/index.d.ts +3 -3
- package/lib/landmark-roles/index.js +9 -5
- package/lib/no-boolean-attr-value/index.d.ts +3 -1
- package/lib/no-default-value/index.d.ts +3 -1
- package/lib/no-empty-palpable-content/index.d.ts +3 -3
- package/lib/no-empty-palpable-content/index.js +11 -3
- package/lib/no-hard-code-id/index.d.ts +3 -1
- package/lib/no-refer-to-non-existent-id/index.d.ts +9 -4
- package/lib/no-refer-to-non-existent-id/index.js +65 -5
- package/lib/no-use-event-handler-attr/index.d.ts +2 -2
- package/lib/no-use-event-handler-attr/index.js +4 -4
- package/lib/permitted-contents/choice.d.ts +8 -1
- package/lib/permitted-contents/choice.js +37 -2
- package/lib/permitted-contents/complex-branch.d.ts +8 -1
- package/lib/permitted-contents/complex-branch.js +4 -2
- package/lib/permitted-contents/content-model.d.ts +1 -1
- package/lib/permitted-contents/content-model.js +7 -3
- package/lib/permitted-contents/count-pattern.d.ts +18 -2
- package/lib/permitted-contents/count-pattern.js +71 -64
- package/lib/permitted-contents/debug.browser.d.ts +30 -0
- package/lib/permitted-contents/debug.browser.js +14 -0
- package/lib/permitted-contents/debug.d.ts +2 -1
- package/lib/permitted-contents/debug.js +9 -1
- package/lib/permitted-contents/index.d.ts +1 -1
- package/lib/permitted-contents/index.js +12 -15
- package/lib/permitted-contents/matches-selector.d.ts +7 -2
- package/lib/permitted-contents/matches-selector.js +8 -6
- package/lib/permitted-contents/order.d.ts +8 -1
- package/lib/permitted-contents/order.js +25 -15
- package/lib/permitted-contents/recursive-branch.d.ts +8 -1
- package/lib/permitted-contents/recursive-branch.js +5 -3
- package/lib/permitted-contents/represent-transparent-nodes.d.ts +7 -3
- package/lib/permitted-contents/represent-transparent-nodes.js +4 -5
- package/lib/permitted-contents/start.d.ts +7 -1
- package/lib/permitted-contents/start.js +5 -3
- package/lib/permitted-contents/transparent.d.ts +2 -2
- package/lib/permitted-contents/transparent.js +11 -14
- package/lib/permitted-contents/types.d.ts +35 -28
- package/lib/permitted-contents/utils.d.ts +96 -28
- package/lib/permitted-contents/utils.js +135 -17
- package/lib/placeholder-label-option/index.d.ts +2 -0
- package/lib/placeholder-label-option/index.js +88 -0
- package/lib/require-accessible-name/index.d.ts +2 -2
- package/lib/require-accessible-name/index.js +5 -5
- package/lib/require-datetime/index.d.ts +6 -0
- package/lib/require-datetime/index.js +34 -0
- package/lib/require-datetime/types.d.ts +10 -0
- package/lib/require-datetime/types.js +2 -0
- package/lib/require-datetime/utils.d.ts +11 -0
- package/lib/require-datetime/utils.js +137 -0
- package/lib/required-attr/index.d.ts +3 -3
- package/lib/required-attr/index.js +19 -16
- package/lib/required-element/index.d.ts +2 -2
- package/lib/required-element/index.js +3 -2
- package/lib/required-h1/index.d.ts +3 -3
- package/lib/required-h1/index.js +4 -3
- package/lib/use-list/index.d.ts +3 -3
- package/lib/use-list/index.js +7 -5
- package/lib/wai-aria/checkings/_.d.ts +15 -0
- package/lib/wai-aria/checkings/_.js +27 -0
- package/lib/wai-aria/checkings/abstract-role.js +1 -1
- package/lib/wai-aria/checkings/default-value.d.ts +7 -3
- package/lib/wai-aria/checkings/deprecated-props.d.ts +8 -4
- package/lib/wai-aria/checkings/disallowed-prop.d.ts +8 -4
- package/lib/wai-aria/checkings/disallowed-prop.js +27 -3
- package/lib/wai-aria/checkings/implicit-props.d.ts +8 -4
- package/lib/wai-aria/checkings/implicit-props.js +3 -2
- package/lib/wai-aria/checkings/implicit-role.js +1 -1
- package/lib/wai-aria/checkings/interaction-in-hidden.js +3 -1
- package/lib/wai-aria/checkings/no-global-prop.d.ts +7 -3
- package/lib/wai-aria/checkings/non-existent-role.js +1 -1
- package/lib/wai-aria/checkings/permitted-roles.js +1 -1
- package/lib/wai-aria/checkings/presentational-children.js +4 -2
- package/lib/wai-aria/checkings/required-owned-elements.d.ts +7 -3
- package/lib/wai-aria/checkings/required-owned-elements.js +28 -12
- package/lib/wai-aria/checkings/required-prop copy.d.ts +15 -0
- package/lib/wai-aria/checkings/required-prop copy.js +27 -0
- package/lib/wai-aria/checkings/required-prop.d.ts +12 -6
- package/lib/wai-aria/checkings/unadopted-explicit-roles.d.ts +15 -0
- package/lib/wai-aria/checkings/unadopted-explicit-roles.js +17 -0
- package/lib/wai-aria/checkings/value.d.ts +15 -6
- package/lib/wai-aria/checkings/value.js +3 -1
- package/lib/wai-aria/index.d.ts +1 -1
- package/lib/wai-aria/index.js +13 -12
- package/lib/wai-aria/types.d.ts +10 -10
- package/lib/xxx/index.d.ts +2 -0
- package/lib/xxx/index.js +32 -0
- package/package.json +16 -11
- package/schema.json +6 -0
- package/test/attr-check.spec.js +77 -0
- package/test/attr-duplication/index.spec.js +159 -0
- package/test/attr-value-quotes/index.spec.js +133 -0
- package/test/case-sensitive-attr-name/index.spec.js +65 -0
- package/test/case-sensitive-tag-name/index.spec.js +80 -0
- package/test/character-reference/index.spec.js +57 -0
- package/test/class-naming/index.spec.js +470 -0
- package/test/create-message.spec.js +497 -0
- package/test/deprecated-attr/index.spec.js +48 -0
- package/test/deprecated-element/index.spec.js +48 -0
- package/test/disallowed-element/index.spec.js +90 -0
- package/test/doctype/index.spec.js +50 -0
- package/test/end-tag/index.spec.js +162 -0
- package/test/id-duplication/index.spec.js +47 -0
- package/test/ineffective-attr/index.spec.js +31 -0
- package/test/invalid-attr/index.spec.js +1632 -0
- package/test/label-has-control/index.spec.js +29 -0
- package/test/landmark-roles/index.spec.js +187 -0
- package/test/no-boolean-attr-value/index.spec.js +41 -0
- package/test/no-default-value/index.spec.js +74 -0
- package/test/no-empty-palpable-content/index.spec.js +115 -0
- package/test/no-hard-code-id/index.spec.js +100 -0
- package/test/no-refer-to-non-existent-id/index.spec.js +254 -0
- package/test/no-use-event-handler-attr/index.spec.js +57 -0
- package/test/permitted-contents/choice.spec.js +174 -0
- package/test/permitted-contents/count-pattern.spec.js +308 -0
- package/test/permitted-contents/index.spec.js +1371 -0
- package/test/permitted-contents/matches-selector.spec.js +59 -0
- package/test/permitted-contents/order.spec.js +351 -0
- package/test/permitted-contents/recursive-branch.spec.js +41 -0
- package/test/permitted-contents/represent-transparent-nodes.spec.js +24 -0
- package/test/permitted-contents/start.spec.js +67 -0
- package/test/placeholder-label-option/index.spec.js +113 -0
- package/test/require-accessible-name/index.spec.js +446 -0
- package/test/require-datetime/index.spec.js +54 -0
- package/test/require-datetime/utils.spec.js +52 -0
- package/test/required-attr/index.spec.js +414 -0
- package/test/required-element/index.spec.js +188 -0
- package/test/required-h1/index.spec.js +69 -0
- package/test/use-list/index.spec.js +109 -0
- package/test/wai-aria/checkings/value.spec.js +33 -0
- package/test/wai-aria/index.spec.js +1173 -0
- package/tsconfig.test.json +0 -3
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var _Collection_locked, _Collection_matched, _Collection_nodes, _Collection_origin;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.UnsupportedError = exports.Collection = exports.isTransparent = exports.isChoice = exports.isZeroOrMore = exports.isOneOrMore = exports.isOptional = exports.isRequire = exports.matches = exports.isModel = exports.getChildNodesWithoutWhitespaces = void 0;
|
|
4
|
+
exports.modelLog = exports.UnsupportedError = exports.Collection = exports.cleanObject = exports.mergeHints = exports.normalizeModel = exports.isTransparent = exports.isChoice = exports.isZeroOrMore = exports.isOneOrMore = exports.isOptional = exports.isRequire = exports.matches = exports.isModel = exports.getChildNodesWithoutWhitespaces = void 0;
|
|
5
5
|
const tslib_1 = require("tslib");
|
|
6
6
|
const selector_1 = require("@markuplint/selector");
|
|
7
|
-
const
|
|
7
|
+
const debug_1 = require("./debug");
|
|
8
8
|
const represent_transparent_nodes_1 = require("./represent-transparent-nodes");
|
|
9
9
|
const getChildNodesWithoutWhitespacesCaches = new Map();
|
|
10
|
-
function getChildNodesWithoutWhitespaces(
|
|
10
|
+
function getChildNodesWithoutWhitespaces(
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
12
|
+
el) {
|
|
11
13
|
let nodes = getChildNodesWithoutWhitespacesCaches.get(el);
|
|
12
14
|
if (nodes) {
|
|
13
15
|
return nodes;
|
|
@@ -36,10 +38,12 @@ function isModel(model) {
|
|
|
36
38
|
return modelMode;
|
|
37
39
|
}
|
|
38
40
|
exports.isModel = isModel;
|
|
39
|
-
function matches(selector,
|
|
41
|
+
function matches(selector,
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
43
|
+
node, specs) {
|
|
40
44
|
const selectorResult = (0, selector_1.createSelector)(selector, specs).search(node);
|
|
41
45
|
const matched = selectorResult.filter((r) => r.matched);
|
|
42
|
-
if (matched.length) {
|
|
46
|
+
if (matched.length > 0) {
|
|
43
47
|
return {
|
|
44
48
|
matched: true,
|
|
45
49
|
};
|
|
@@ -56,11 +60,13 @@ function matches(selector, node, specs) {
|
|
|
56
60
|
};
|
|
57
61
|
}
|
|
58
62
|
exports.matches = matches;
|
|
59
|
-
function descendants(
|
|
63
|
+
function descendants(
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
65
|
+
selectorResult) {
|
|
60
66
|
let nodes = selectorResult.nodes.slice();
|
|
61
|
-
while (selectorResult.has.length) {
|
|
67
|
+
while (selectorResult.has.length > 0) {
|
|
62
68
|
for (const dep of selectorResult.has) {
|
|
63
|
-
if (
|
|
69
|
+
if (dep.has.length === 0) {
|
|
64
70
|
nodes = dep.nodes;
|
|
65
71
|
continue;
|
|
66
72
|
}
|
|
@@ -95,8 +101,89 @@ function isTransparent(content) {
|
|
|
95
101
|
return 'transparent' in content;
|
|
96
102
|
}
|
|
97
103
|
exports.isTransparent = isTransparent;
|
|
104
|
+
function normalizeModel(pattern) {
|
|
105
|
+
var _a, _b, _c, _d, _e;
|
|
106
|
+
let model;
|
|
107
|
+
let min;
|
|
108
|
+
let max;
|
|
109
|
+
let repeat;
|
|
110
|
+
let missingType;
|
|
111
|
+
if (isRequire(pattern)) {
|
|
112
|
+
model = pattern.require;
|
|
113
|
+
min = (_a = pattern.min) !== null && _a !== void 0 ? _a : 1;
|
|
114
|
+
max = Math.max((_b = pattern.max) !== null && _b !== void 0 ? _b : 1, min);
|
|
115
|
+
missingType = 'MISSING_NODE_REQUIRED';
|
|
116
|
+
}
|
|
117
|
+
else if (isOptional(pattern)) {
|
|
118
|
+
model = pattern.optional;
|
|
119
|
+
min = 0;
|
|
120
|
+
max = Math.max((_c = pattern.max) !== null && _c !== void 0 ? _c : 1, 1);
|
|
121
|
+
}
|
|
122
|
+
else if (isOneOrMore(pattern)) {
|
|
123
|
+
model = pattern.oneOrMore;
|
|
124
|
+
min = 1;
|
|
125
|
+
max = Math.max((_d = pattern.max) !== null && _d !== void 0 ? _d : Infinity, 1);
|
|
126
|
+
missingType = 'MISSING_NODE_ONE_OR_MORE';
|
|
127
|
+
}
|
|
128
|
+
else if (isZeroOrMore(pattern)) {
|
|
129
|
+
model = pattern.zeroOrMore;
|
|
130
|
+
min = 0;
|
|
131
|
+
max = Math.max((_e = pattern.max) !== null && _e !== void 0 ? _e : Infinity, 1);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
throw new Error('Unreachable code');
|
|
135
|
+
}
|
|
136
|
+
if (min === 0 && max === 1) {
|
|
137
|
+
repeat = '?';
|
|
138
|
+
}
|
|
139
|
+
else if (min === 0 && !Number.isFinite(max)) {
|
|
140
|
+
repeat = '*';
|
|
141
|
+
}
|
|
142
|
+
else if (min === 1 && max === 1) {
|
|
143
|
+
repeat = '';
|
|
144
|
+
}
|
|
145
|
+
else if (min === 1 && !Number.isFinite(max)) {
|
|
146
|
+
repeat = '+';
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
repeat = `{${min},${max}}`;
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
model,
|
|
153
|
+
min,
|
|
154
|
+
max,
|
|
155
|
+
repeat,
|
|
156
|
+
missingType,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
exports.normalizeModel = normalizeModel;
|
|
160
|
+
function mergeHints(
|
|
161
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
162
|
+
a,
|
|
163
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
164
|
+
b) {
|
|
165
|
+
const missing = [a.missing, b.missing].sort((a, b) => { var _a, _b; return ((_a = b === null || b === void 0 ? void 0 : b.barelyMatchedElements) !== null && _a !== void 0 ? _a : 0) - ((_b = a === null || a === void 0 ? void 0 : a.barelyMatchedElements) !== null && _b !== void 0 ? _b : 0); })[0];
|
|
166
|
+
return cleanObject({
|
|
167
|
+
...a,
|
|
168
|
+
...b,
|
|
169
|
+
missing: missing && cleanObject(missing),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
exports.mergeHints = mergeHints;
|
|
173
|
+
function cleanObject(object) {
|
|
174
|
+
const newObject = {};
|
|
175
|
+
Object.entries(object).forEach(([key, value]) => {
|
|
176
|
+
if (value !== undefined) {
|
|
177
|
+
newObject[key] = value;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return newObject;
|
|
181
|
+
}
|
|
182
|
+
exports.cleanObject = cleanObject;
|
|
98
183
|
class Collection {
|
|
99
|
-
constructor(
|
|
184
|
+
constructor(
|
|
185
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
186
|
+
origin) {
|
|
100
187
|
_Collection_locked.set(this, new Set());
|
|
101
188
|
_Collection_matched.set(this, new Set());
|
|
102
189
|
_Collection_nodes.set(this, void 0);
|
|
@@ -116,7 +203,9 @@ class Collection {
|
|
|
116
203
|
get unmatched() {
|
|
117
204
|
return Array.from(tslib_1.__classPrivateFieldGet(this, _Collection_nodes, "f")).filter(n => !tslib_1.__classPrivateFieldGet(this, _Collection_matched, "f").has(n));
|
|
118
205
|
}
|
|
119
|
-
addMatched(
|
|
206
|
+
addMatched(
|
|
207
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
208
|
+
nodes) {
|
|
120
209
|
const i = tslib_1.__classPrivateFieldGet(this, _Collection_matched, "f").size;
|
|
121
210
|
for (const node of nodes) {
|
|
122
211
|
if (!tslib_1.__classPrivateFieldGet(this, _Collection_nodes, "f").has(node)) {
|
|
@@ -142,33 +231,33 @@ class Collection {
|
|
|
142
231
|
const raw = n.is(n.TEXT_NODE) ? `:text(${n.raw.replace(/\n/g, '\\n')})` : n.raw;
|
|
143
232
|
if (tslib_1.__classPrivateFieldGet(this, _Collection_locked, "f").has(n)) {
|
|
144
233
|
if (represent_transparent_nodes_1.transparentMode.has(n)) {
|
|
145
|
-
out.push(
|
|
234
|
+
out.push(debug_1.bgBlue.bold(raw));
|
|
146
235
|
}
|
|
147
236
|
else {
|
|
148
|
-
out.push(
|
|
237
|
+
out.push(debug_1.bgGreen.bold(raw));
|
|
149
238
|
}
|
|
150
239
|
continue;
|
|
151
240
|
}
|
|
152
241
|
if (tslib_1.__classPrivateFieldGet(this, _Collection_matched, "f").has(n)) {
|
|
153
242
|
if (represent_transparent_nodes_1.transparentMode.has(n)) {
|
|
154
|
-
out.push(
|
|
243
|
+
out.push(debug_1.blue.bold(raw));
|
|
155
244
|
}
|
|
156
245
|
else {
|
|
157
|
-
out.push(
|
|
246
|
+
out.push(debug_1.green.bold(raw));
|
|
158
247
|
}
|
|
159
248
|
continue;
|
|
160
249
|
}
|
|
161
250
|
if (highlightExtraNodes) {
|
|
162
251
|
if (represent_transparent_nodes_1.transparentMode.has(n)) {
|
|
163
|
-
out.push(
|
|
252
|
+
out.push(debug_1.bgMagenta.bold(raw));
|
|
164
253
|
}
|
|
165
254
|
else {
|
|
166
|
-
out.push(
|
|
255
|
+
out.push(debug_1.bgRed.bold(raw));
|
|
167
256
|
}
|
|
168
257
|
continue;
|
|
169
258
|
}
|
|
170
259
|
if (represent_transparent_nodes_1.transparentMode.has(n)) {
|
|
171
|
-
out.push((0,
|
|
260
|
+
out.push((0, debug_1.cyan)(raw));
|
|
172
261
|
}
|
|
173
262
|
else {
|
|
174
263
|
out.push(raw);
|
|
@@ -182,3 +271,32 @@ _Collection_locked = new WeakMap(), _Collection_matched = new WeakMap(), _Collec
|
|
|
182
271
|
class UnsupportedError extends Error {
|
|
183
272
|
}
|
|
184
273
|
exports.UnsupportedError = UnsupportedError;
|
|
274
|
+
function modelLog(model, repeat) {
|
|
275
|
+
if (!isModel(model)) {
|
|
276
|
+
return orderLog(model, repeat);
|
|
277
|
+
}
|
|
278
|
+
if (typeof model === 'string') {
|
|
279
|
+
return `<${model}>${repeat}`;
|
|
280
|
+
}
|
|
281
|
+
return `(<${model.join('>|<')}>)${repeat}`;
|
|
282
|
+
}
|
|
283
|
+
exports.modelLog = modelLog;
|
|
284
|
+
function orderLog(order, repeat) {
|
|
285
|
+
return order.length === 1 && order[0]
|
|
286
|
+
? markRepeat(patternLog(order[0]), repeat)
|
|
287
|
+
: markRepeat(order.map(pattern => patternLog(pattern)).join(''), repeat);
|
|
288
|
+
}
|
|
289
|
+
function patternLog(pattern) {
|
|
290
|
+
if (isTransparent(pattern)) {
|
|
291
|
+
// 適当
|
|
292
|
+
return `:transparent(${modelLog(pattern.transparent, '')})`;
|
|
293
|
+
}
|
|
294
|
+
if (isChoice(pattern)) {
|
|
295
|
+
return `(${pattern.choice.map(candidate => orderLog(candidate, '')).join('|')})`;
|
|
296
|
+
}
|
|
297
|
+
const { model, repeat } = normalizeModel(pattern);
|
|
298
|
+
return modelLog(model, repeat);
|
|
299
|
+
}
|
|
300
|
+
function markRepeat(pattern, repeat) {
|
|
301
|
+
return repeat ? `(${pattern})${repeat}` : pattern;
|
|
302
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ml_core_1 = require("@markuplint/ml-core");
|
|
4
|
+
exports.default = (0, ml_core_1.createRule)({
|
|
5
|
+
verify({ document, report, t }) {
|
|
6
|
+
document.querySelectorAll('select').forEach(select => {
|
|
7
|
+
if (hasPlaceholderLabelOption(select)) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
if (!needPlaceholderLabelOption(select)) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
report({
|
|
14
|
+
scope: select,
|
|
15
|
+
message: t('need {0}', t('the {0}', 'placeholder label option')),
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* > If a select element has a required attribute specified,
|
|
22
|
+
* > does not have a multiple attribute specified,
|
|
23
|
+
* > and has a display size of 1,
|
|
24
|
+
* > then the select element must have a placeholder label option.
|
|
25
|
+
*
|
|
26
|
+
* @param select
|
|
27
|
+
* @returns
|
|
28
|
+
*/
|
|
29
|
+
function needPlaceholderLabelOption(
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
31
|
+
select) {
|
|
32
|
+
var _a;
|
|
33
|
+
const hasRequired = select.hasAttribute('required');
|
|
34
|
+
if (!hasRequired) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
const hasMultiple = select.hasAttribute('multiple');
|
|
38
|
+
if (hasMultiple) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
const size = (_a = select.getAttribute('size')) !== null && _a !== void 0 ? _a : '1';
|
|
42
|
+
if (size !== '1') {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* > If a select element has a required attribute specified,
|
|
49
|
+
* > does not have a multiple attribute specified,
|
|
50
|
+
* > and has a display size of 1;
|
|
51
|
+
* > and if the value of the first option element
|
|
52
|
+
* > in the select element's list of options (if any) is the empty string,
|
|
53
|
+
* > and that option element's parent node is the select element (and not an optgroup element),
|
|
54
|
+
* > then that option is the select element's **placeholder label option**.
|
|
55
|
+
*
|
|
56
|
+
* @param select
|
|
57
|
+
* @returns
|
|
58
|
+
*/
|
|
59
|
+
function hasPlaceholderLabelOption(
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
61
|
+
select) {
|
|
62
|
+
var _a, _b;
|
|
63
|
+
// > has a required attribute specified
|
|
64
|
+
if (!select.hasAttribute('required')) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
// > does not have a multiple attribute specified
|
|
68
|
+
if (select.hasAttribute('multiple')) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
// > has a display size of 1
|
|
72
|
+
const size = (_a = select.getAttribute('size')) !== null && _a !== void 0 ? _a : '1';
|
|
73
|
+
if (size !== '1') {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
// > in the select element's list of options (if any) is the empty string
|
|
77
|
+
const firstOption = select.querySelector('option');
|
|
78
|
+
if (!firstOption) {
|
|
79
|
+
// if any
|
|
80
|
+
return true;
|
|
81
|
+
}
|
|
82
|
+
// > that option element's parent node is the select element (and not an optgroup element)
|
|
83
|
+
if (((_b = firstOption.parentElement) === null || _b === void 0 ? void 0 : _b.localName) === 'optgroup') {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
const value = firstOption.getAttribute('value');
|
|
87
|
+
return value === '' || value === null;
|
|
88
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ARIAVersion } from '@markuplint/ml-spec';
|
|
2
2
|
type Option = {
|
|
3
|
-
|
|
3
|
+
ariaVersion: ARIAVersion;
|
|
4
4
|
};
|
|
5
|
-
declare const _default: import(
|
|
5
|
+
declare const _default: Readonly<import('@markuplint/ml-core').RuleSeed<boolean, Option>>;
|
|
6
6
|
export default _default;
|
|
@@ -5,7 +5,7 @@ const ml_spec_1 = require("@markuplint/ml-spec");
|
|
|
5
5
|
const helpers_1 = require("../helpers");
|
|
6
6
|
exports.default = (0, ml_core_1.createRule)({
|
|
7
7
|
defaultOptions: {
|
|
8
|
-
ariaVersion:
|
|
8
|
+
ariaVersion: ml_spec_1.ARIA_RECOMMENDED_VERSION,
|
|
9
9
|
},
|
|
10
10
|
async verify({ document, report, t }) {
|
|
11
11
|
await document.walkOn('Element', el => {
|
|
@@ -16,18 +16,18 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
16
16
|
if ((0, helpers_1.accnameMayBeMutable)(el, document)) {
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
|
-
if (!(0, ml_spec_1.isExposed)(el, document.specs, el.rule.
|
|
19
|
+
if (!(0, ml_spec_1.isExposed)(el, document.specs, el.rule.options.ariaVersion)) {
|
|
20
20
|
return;
|
|
21
21
|
}
|
|
22
|
-
const computed = (0, ml_core_1.getComputedRole)(document.specs, el, el.rule.
|
|
22
|
+
const computed = (0, ml_core_1.getComputedRole)(document.specs, el, el.rule.options.ariaVersion);
|
|
23
23
|
if (!computed.role) {
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
|
-
const roleSpec = (0, ml_core_1.getRoleSpec)(document.specs, computed.role.name, el.namespaceURI, el.rule.
|
|
26
|
+
const roleSpec = (0, ml_core_1.getRoleSpec)(document.specs, computed.role.name, el.namespaceURI, el.rule.options.ariaVersion);
|
|
27
27
|
if (!roleSpec || !roleSpec.accessibleNameRequired) {
|
|
28
28
|
return;
|
|
29
29
|
}
|
|
30
|
-
const hasAccessibleName = !!el.getAccessibleName(el.rule.
|
|
30
|
+
const hasAccessibleName = !!el.getAccessibleName(el.rule.options.ariaVersion).trim();
|
|
31
31
|
if (!hasAccessibleName) {
|
|
32
32
|
report({ scope: el, message: t('Require {0}', 'accessible name') });
|
|
33
33
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ml_core_1 = require("@markuplint/ml-core");
|
|
4
|
+
const types_1 = require("@markuplint/types");
|
|
5
|
+
const utils_1 = require("./utils");
|
|
6
|
+
exports.default = (0, ml_core_1.createRule)({
|
|
7
|
+
defaultOptions: {
|
|
8
|
+
langs: undefined,
|
|
9
|
+
},
|
|
10
|
+
verify({ document, report, t }) {
|
|
11
|
+
document.querySelectorAll('time:not([datetime])').forEach(time => {
|
|
12
|
+
if (time.hasMutableChildren()) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const content = time.textContent.trim();
|
|
16
|
+
const result = (0, types_1.check)(content, 'DateTime');
|
|
17
|
+
if (result.matched) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const candidate = (0, utils_1.getCandidateDatetimeString)(content, time.rule.options.langs);
|
|
21
|
+
if (candidate) {
|
|
22
|
+
report({
|
|
23
|
+
scope: time,
|
|
24
|
+
message: t('need {0*}', `datetime="${candidate}"`),
|
|
25
|
+
});
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
report({
|
|
29
|
+
scope: time,
|
|
30
|
+
message: t('need {0}', t('the "{0*}" {1}', 'datetime', 'attribute')),
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
},
|
|
34
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type DateTimeKey = 'year' | 'month' | 'day' | 'hour' | 'minute' | 'second' | 'ms';
|
|
2
|
+
export type DateTimeData = Partial<Record<DateTimeKey, number>>;
|
|
3
|
+
export type DateTime = {
|
|
4
|
+
datetime: DateTimeData;
|
|
5
|
+
zone?: number;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* @see https://github.com/wanasit/chrono#locales
|
|
9
|
+
*/
|
|
10
|
+
export type Lang = 'en' | 'ja' | 'fr' | 'nl' | 'ru' | 'de' | 'pt' | 'zh';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { DateTime, Lang } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Datetime-ish text to a datetime data
|
|
4
|
+
*
|
|
5
|
+
* @param content
|
|
6
|
+
* @param langs
|
|
7
|
+
* @param base Reference date for a test
|
|
8
|
+
* @returns
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseADatetime(content: string, langs: readonly Lang[], base?: Readonly<Date>): DateTime | null;
|
|
11
|
+
export declare function getCandidateDatetimeString(content: string, langs?: Lang[]): string | null;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCandidateDatetimeString = exports.parseADatetime = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const chrono = tslib_1.__importStar(require("chrono-node"));
|
|
6
|
+
const defaultLangs = ['en', 'ja', 'fr', 'nl', 'ru', 'de', 'pt', 'zh'];
|
|
7
|
+
/**
|
|
8
|
+
* Datetime-ish text to a datetime data
|
|
9
|
+
*
|
|
10
|
+
* @param content
|
|
11
|
+
* @param langs
|
|
12
|
+
* @param base Reference date for a test
|
|
13
|
+
* @returns
|
|
14
|
+
*/
|
|
15
|
+
function parseADatetime(content, langs, base) {
|
|
16
|
+
var _a;
|
|
17
|
+
const date = parseTryMultipleLangs(content, langs, base);
|
|
18
|
+
if (!date) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
const data = {};
|
|
22
|
+
if (date.isCertain('year')) {
|
|
23
|
+
data.year = date.get('year');
|
|
24
|
+
}
|
|
25
|
+
if (date.isCertain('month')) {
|
|
26
|
+
data.month = date.get('month');
|
|
27
|
+
}
|
|
28
|
+
if (date.isCertain('day')) {
|
|
29
|
+
data.day = date.get('day');
|
|
30
|
+
}
|
|
31
|
+
if (date.isCertain('hour')) {
|
|
32
|
+
data.hour = date.get('hour');
|
|
33
|
+
}
|
|
34
|
+
if (date.isCertain('hour')) {
|
|
35
|
+
data.minute = (_a = date.get('minute')) !== null && _a !== void 0 ? _a : 0;
|
|
36
|
+
}
|
|
37
|
+
if (date.isCertain('second')) {
|
|
38
|
+
data.second = date.get('second');
|
|
39
|
+
}
|
|
40
|
+
if (date.isCertain('millisecond')) {
|
|
41
|
+
data.ms = date.get('millisecond');
|
|
42
|
+
}
|
|
43
|
+
const datetime = {
|
|
44
|
+
datetime: data,
|
|
45
|
+
};
|
|
46
|
+
if (date.isCertain('timezoneOffset')) {
|
|
47
|
+
datetime.zone = date.get('timezoneOffset');
|
|
48
|
+
}
|
|
49
|
+
return datetime;
|
|
50
|
+
}
|
|
51
|
+
exports.parseADatetime = parseADatetime;
|
|
52
|
+
function getCandidateDatetimeString(content, langs = defaultLangs) {
|
|
53
|
+
const date = parseADatetime(content, langs);
|
|
54
|
+
if (!date) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
let datetimeStr = toDatetimeString(date.datetime);
|
|
58
|
+
if (!datetimeStr) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (date.zone != null) {
|
|
62
|
+
const plusMinus = date.zone < 0 ? '-' : '+';
|
|
63
|
+
const hour = Math.floor(Math.abs(date.zone) / 60);
|
|
64
|
+
const minute = Math.abs(date.zone) % 60;
|
|
65
|
+
datetimeStr += `${plusMinus}${f(hour, 2)}${f(minute, 2)}`;
|
|
66
|
+
}
|
|
67
|
+
return datetimeStr;
|
|
68
|
+
}
|
|
69
|
+
exports.getCandidateDatetimeString = getCandidateDatetimeString;
|
|
70
|
+
function toDatetimeString(date) {
|
|
71
|
+
if (only(date, ['year', 'month'])) {
|
|
72
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}`;
|
|
73
|
+
}
|
|
74
|
+
if (only(date, ['year', 'month', 'day'])) {
|
|
75
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}-${f(date.day, 2)}`;
|
|
76
|
+
}
|
|
77
|
+
if (only(date, ['month', 'day'])) {
|
|
78
|
+
return `${f(date.month, 2)}-${f(date.day, 2)}`;
|
|
79
|
+
}
|
|
80
|
+
if (only(date, ['hour', 'minute'])) {
|
|
81
|
+
return `${f(date.hour, 2)}:${f(date.minute, 2)}`;
|
|
82
|
+
}
|
|
83
|
+
if (only(date, ['hour', 'minute', 'second'])) {
|
|
84
|
+
return `${f(date.hour, 2)}:${f(date.minute, 2)}:${f(date.second, 2)}`;
|
|
85
|
+
}
|
|
86
|
+
if (only(date, ['hour', 'minute', 'second', 'ms'])) {
|
|
87
|
+
return `${f(date.hour, 2)}:${f(date.minute, 2)}:${f(date.second, 2)}.${date.ms}`;
|
|
88
|
+
}
|
|
89
|
+
if (only(date, ['year', 'month', 'day', 'hour', 'minute'])) {
|
|
90
|
+
return `${f(date.year, 4)}-${f(date.month, 2)}-${f(date.day, 2)}T${f(date.hour, 2)}:${f(date.minute, 2)}`;
|
|
91
|
+
}
|
|
92
|
+
if (only(date, ['year', 'month', 'day', 'hour', 'minute', 'second'])) {
|
|
93
|
+
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)}`;
|
|
94
|
+
}
|
|
95
|
+
if (only(date, ['year', 'month', 'day', 'hour', 'minute', 'second', 'ms'])) {
|
|
96
|
+
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}`;
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
function parseTryMultipleLangs(content, langs, base) {
|
|
101
|
+
for (const lang of langs) {
|
|
102
|
+
const results = chrono[lang].casual.parse(content, base);
|
|
103
|
+
// Is not multiple datetime contents
|
|
104
|
+
if (results.length < 1) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const result = results[0];
|
|
108
|
+
if (!result) {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
// Is not a range or period
|
|
112
|
+
if (result.end) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
return result.start;
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
function only(date, keys) {
|
|
120
|
+
const list = Object.keys(date);
|
|
121
|
+
for (const exists of list) {
|
|
122
|
+
if (!keys.includes(exists)) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Formatter
|
|
130
|
+
*
|
|
131
|
+
* @param n
|
|
132
|
+
* @param pad zero padding
|
|
133
|
+
* @returns
|
|
134
|
+
*/
|
|
135
|
+
function f(n, pad) {
|
|
136
|
+
return n.toString(10).padStart(pad, '0');
|
|
137
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
type RequiredAttributes = string | (string | Attr)[];
|
|
2
2
|
type Attr = {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
name: string;
|
|
4
|
+
value?: string | string[];
|
|
5
5
|
};
|
|
6
|
-
declare const _default: import(
|
|
6
|
+
declare const _default: Readonly<import('@markuplint/ml-core').RuleSeed<RequiredAttributes, undefined>>;
|
|
7
7
|
export default _default;
|
|
@@ -6,6 +6,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
6
6
|
defaultValue: [],
|
|
7
7
|
async verify({ document, report, t }) {
|
|
8
8
|
await document.walkOn('Element', el => {
|
|
9
|
+
var _a;
|
|
9
10
|
if (el.hasSpreadAttr) {
|
|
10
11
|
return;
|
|
11
12
|
}
|
|
@@ -14,7 +15,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
14
15
|
const attributeSpecs = {};
|
|
15
16
|
if (attrSpec && el.elementType === 'html') {
|
|
16
17
|
for (const spec of attrSpec) {
|
|
17
|
-
if (spec.required || spec.requiredEither || spec.condition) {
|
|
18
|
+
if (spec.required != null || spec.requiredEither || spec.condition != null) {
|
|
18
19
|
attributeSpecs[spec.name] = {
|
|
19
20
|
...spec,
|
|
20
21
|
values: [],
|
|
@@ -33,7 +34,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
33
34
|
if (Array.isArray(req.value)) {
|
|
34
35
|
values.push(...req.value);
|
|
35
36
|
}
|
|
36
|
-
else {
|
|
37
|
+
else if (req.value) {
|
|
37
38
|
values.push(req.value);
|
|
38
39
|
}
|
|
39
40
|
}
|
|
@@ -53,8 +54,8 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
53
54
|
else if (spec.required === true) {
|
|
54
55
|
invalid = (0, helpers_1.attrMatches)(el, spec.condition) && didntHave;
|
|
55
56
|
}
|
|
56
|
-
else if (spec.required) {
|
|
57
|
-
const selector =
|
|
57
|
+
else if (spec.required != null && spec.required !== false) {
|
|
58
|
+
const selector = typeof spec.required === 'string' ? spec.required : spec.required.join(',');
|
|
58
59
|
invalid = el.matches(selector) && didntHave;
|
|
59
60
|
}
|
|
60
61
|
if (invalid) {
|
|
@@ -65,23 +66,25 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
65
66
|
});
|
|
66
67
|
}
|
|
67
68
|
const value = el.getAttribute(spec.name);
|
|
68
|
-
if (spec.values.length && value) {
|
|
69
|
+
if (spec.values.length > 0 && value) {
|
|
69
70
|
const matched = spec.values.some(pattern => (0, helpers_1.match)(value, pattern));
|
|
70
71
|
if (!matched) {
|
|
71
72
|
const expects = spec.values.length === 1 ? t(spec.values) : t('either {0}', t(spec.values));
|
|
72
73
|
const message = t('{0} expects {1}', t('the "{0*}" {1}', spec.name, 'attribute'), expects);
|
|
73
74
|
const attrToken = el.getAttributeToken(spec.name);
|
|
74
|
-
const valueToken = attrToken[0].valueNode;
|
|
75
|
-
const token = valueToken
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
75
|
+
const valueToken = (_a = attrToken[0]) === null || _a === void 0 ? void 0 : _a.valueNode;
|
|
76
|
+
const token = valueToken !== null && valueToken !== void 0 ? valueToken : attrToken[0];
|
|
77
|
+
if (token) {
|
|
78
|
+
report({
|
|
79
|
+
scope: {
|
|
80
|
+
rule: el.rule,
|
|
81
|
+
raw: token.raw,
|
|
82
|
+
startCol: token.startCol,
|
|
83
|
+
startLine: token.startLine,
|
|
84
|
+
},
|
|
85
|
+
message,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
85
88
|
}
|
|
86
89
|
}
|
|
87
90
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
type Options = {
|
|
2
|
-
|
|
2
|
+
ignoreHasMutableContents: boolean;
|
|
3
3
|
};
|
|
4
|
-
declare const _default: import(
|
|
4
|
+
declare const _default: Readonly<import('@markuplint/ml-core').RuleSeed<string[], Options>>;
|
|
5
5
|
export default _default;
|