@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.
Files changed (163) hide show
  1. package/README.md +1 -1
  2. package/lib/attr-check.d.ts +20 -8
  3. package/lib/attr-check.js +31 -22
  4. package/lib/attr-duplication/index.d.ts +3 -1
  5. package/lib/attr-duplication/index.js +2 -1
  6. package/lib/attr-value-quotes/index.d.ts +2 -2
  7. package/lib/case-sensitive-attr-name/index.d.ts +1 -1
  8. package/lib/case-sensitive-tag-name/index.d.ts +1 -1
  9. package/lib/character-reference/index.d.ts +1 -1
  10. package/lib/class-naming/index.d.ts +1 -1
  11. package/lib/class-naming/index.js +2 -4
  12. package/lib/create-message.d.ts +13 -2
  13. package/lib/create-message.js +4 -3
  14. package/lib/debug.js +1 -1
  15. package/lib/deprecated-attr/index.d.ts +3 -1
  16. package/lib/deprecated-element/index.d.ts +3 -1
  17. package/lib/deprecated-element/index.js +2 -2
  18. package/lib/disallowed-element/index.d.ts +1 -1
  19. package/lib/doctype/index.d.ts +2 -2
  20. package/lib/end-tag/index.d.ts +1 -1
  21. package/lib/helpers.d.ts +37 -22
  22. package/lib/helpers.js +27 -14
  23. package/lib/id-duplication/index.d.ts +3 -1
  24. package/lib/index.d.ts +243 -3
  25. package/lib/index.js +6 -1
  26. package/lib/ineffective-attr/index.d.ts +3 -1
  27. package/lib/ineffective-attr/index.js +2 -5
  28. package/lib/invalid-attr/index.d.ts +39 -12
  29. package/lib/invalid-attr/index.js +153 -27
  30. package/lib/label-has-control/index.d.ts +3 -1
  31. package/lib/landmark-roles/index.d.ts +3 -3
  32. package/lib/landmark-roles/index.js +9 -5
  33. package/lib/no-boolean-attr-value/index.d.ts +3 -1
  34. package/lib/no-default-value/index.d.ts +3 -1
  35. package/lib/no-empty-palpable-content/index.d.ts +3 -3
  36. package/lib/no-empty-palpable-content/index.js +11 -3
  37. package/lib/no-hard-code-id/index.d.ts +3 -1
  38. package/lib/no-refer-to-non-existent-id/index.d.ts +9 -4
  39. package/lib/no-refer-to-non-existent-id/index.js +65 -5
  40. package/lib/no-use-event-handler-attr/index.d.ts +2 -2
  41. package/lib/no-use-event-handler-attr/index.js +4 -4
  42. package/lib/permitted-contents/choice.d.ts +8 -1
  43. package/lib/permitted-contents/choice.js +37 -2
  44. package/lib/permitted-contents/complex-branch.d.ts +8 -1
  45. package/lib/permitted-contents/complex-branch.js +4 -2
  46. package/lib/permitted-contents/content-model.d.ts +1 -1
  47. package/lib/permitted-contents/content-model.js +7 -3
  48. package/lib/permitted-contents/count-pattern.d.ts +18 -2
  49. package/lib/permitted-contents/count-pattern.js +71 -64
  50. package/lib/permitted-contents/debug.browser.d.ts +30 -0
  51. package/lib/permitted-contents/debug.browser.js +14 -0
  52. package/lib/permitted-contents/debug.d.ts +2 -1
  53. package/lib/permitted-contents/debug.js +9 -1
  54. package/lib/permitted-contents/index.d.ts +1 -1
  55. package/lib/permitted-contents/index.js +12 -15
  56. package/lib/permitted-contents/matches-selector.d.ts +7 -2
  57. package/lib/permitted-contents/matches-selector.js +8 -6
  58. package/lib/permitted-contents/order.d.ts +8 -1
  59. package/lib/permitted-contents/order.js +25 -15
  60. package/lib/permitted-contents/recursive-branch.d.ts +8 -1
  61. package/lib/permitted-contents/recursive-branch.js +5 -3
  62. package/lib/permitted-contents/represent-transparent-nodes.d.ts +7 -3
  63. package/lib/permitted-contents/represent-transparent-nodes.js +4 -5
  64. package/lib/permitted-contents/start.d.ts +7 -1
  65. package/lib/permitted-contents/start.js +5 -3
  66. package/lib/permitted-contents/transparent.d.ts +2 -2
  67. package/lib/permitted-contents/transparent.js +11 -14
  68. package/lib/permitted-contents/types.d.ts +35 -28
  69. package/lib/permitted-contents/utils.d.ts +96 -28
  70. package/lib/permitted-contents/utils.js +135 -17
  71. package/lib/placeholder-label-option/index.d.ts +2 -0
  72. package/lib/placeholder-label-option/index.js +88 -0
  73. package/lib/require-accessible-name/index.d.ts +2 -2
  74. package/lib/require-accessible-name/index.js +5 -5
  75. package/lib/require-datetime/index.d.ts +6 -0
  76. package/lib/require-datetime/index.js +34 -0
  77. package/lib/require-datetime/types.d.ts +10 -0
  78. package/lib/require-datetime/types.js +2 -0
  79. package/lib/require-datetime/utils.d.ts +11 -0
  80. package/lib/require-datetime/utils.js +137 -0
  81. package/lib/required-attr/index.d.ts +3 -3
  82. package/lib/required-attr/index.js +19 -16
  83. package/lib/required-element/index.d.ts +2 -2
  84. package/lib/required-element/index.js +3 -2
  85. package/lib/required-h1/index.d.ts +3 -3
  86. package/lib/required-h1/index.js +4 -3
  87. package/lib/use-list/index.d.ts +3 -3
  88. package/lib/use-list/index.js +7 -5
  89. package/lib/wai-aria/checkings/_.d.ts +15 -0
  90. package/lib/wai-aria/checkings/_.js +27 -0
  91. package/lib/wai-aria/checkings/abstract-role.js +1 -1
  92. package/lib/wai-aria/checkings/default-value.d.ts +7 -3
  93. package/lib/wai-aria/checkings/deprecated-props.d.ts +8 -4
  94. package/lib/wai-aria/checkings/disallowed-prop.d.ts +8 -4
  95. package/lib/wai-aria/checkings/disallowed-prop.js +27 -3
  96. package/lib/wai-aria/checkings/implicit-props.d.ts +8 -4
  97. package/lib/wai-aria/checkings/implicit-props.js +3 -2
  98. package/lib/wai-aria/checkings/implicit-role.js +1 -1
  99. package/lib/wai-aria/checkings/interaction-in-hidden.js +3 -1
  100. package/lib/wai-aria/checkings/no-global-prop.d.ts +7 -3
  101. package/lib/wai-aria/checkings/non-existent-role.js +1 -1
  102. package/lib/wai-aria/checkings/permitted-roles.js +1 -1
  103. package/lib/wai-aria/checkings/presentational-children.js +4 -2
  104. package/lib/wai-aria/checkings/required-owned-elements.d.ts +7 -3
  105. package/lib/wai-aria/checkings/required-owned-elements.js +28 -12
  106. package/lib/wai-aria/checkings/required-prop copy.d.ts +15 -0
  107. package/lib/wai-aria/checkings/required-prop copy.js +27 -0
  108. package/lib/wai-aria/checkings/required-prop.d.ts +12 -6
  109. package/lib/wai-aria/checkings/unadopted-explicit-roles.d.ts +15 -0
  110. package/lib/wai-aria/checkings/unadopted-explicit-roles.js +17 -0
  111. package/lib/wai-aria/checkings/value.d.ts +15 -6
  112. package/lib/wai-aria/checkings/value.js +3 -1
  113. package/lib/wai-aria/index.d.ts +1 -1
  114. package/lib/wai-aria/index.js +13 -12
  115. package/lib/wai-aria/types.d.ts +10 -10
  116. package/lib/xxx/index.d.ts +2 -0
  117. package/lib/xxx/index.js +32 -0
  118. package/package.json +16 -11
  119. package/schema.json +6 -0
  120. package/test/attr-check.spec.js +77 -0
  121. package/test/attr-duplication/index.spec.js +159 -0
  122. package/test/attr-value-quotes/index.spec.js +133 -0
  123. package/test/case-sensitive-attr-name/index.spec.js +65 -0
  124. package/test/case-sensitive-tag-name/index.spec.js +80 -0
  125. package/test/character-reference/index.spec.js +57 -0
  126. package/test/class-naming/index.spec.js +470 -0
  127. package/test/create-message.spec.js +497 -0
  128. package/test/deprecated-attr/index.spec.js +48 -0
  129. package/test/deprecated-element/index.spec.js +48 -0
  130. package/test/disallowed-element/index.spec.js +90 -0
  131. package/test/doctype/index.spec.js +50 -0
  132. package/test/end-tag/index.spec.js +162 -0
  133. package/test/id-duplication/index.spec.js +47 -0
  134. package/test/ineffective-attr/index.spec.js +31 -0
  135. package/test/invalid-attr/index.spec.js +1632 -0
  136. package/test/label-has-control/index.spec.js +29 -0
  137. package/test/landmark-roles/index.spec.js +187 -0
  138. package/test/no-boolean-attr-value/index.spec.js +41 -0
  139. package/test/no-default-value/index.spec.js +74 -0
  140. package/test/no-empty-palpable-content/index.spec.js +115 -0
  141. package/test/no-hard-code-id/index.spec.js +100 -0
  142. package/test/no-refer-to-non-existent-id/index.spec.js +254 -0
  143. package/test/no-use-event-handler-attr/index.spec.js +57 -0
  144. package/test/permitted-contents/choice.spec.js +174 -0
  145. package/test/permitted-contents/count-pattern.spec.js +308 -0
  146. package/test/permitted-contents/index.spec.js +1371 -0
  147. package/test/permitted-contents/matches-selector.spec.js +59 -0
  148. package/test/permitted-contents/order.spec.js +351 -0
  149. package/test/permitted-contents/recursive-branch.spec.js +41 -0
  150. package/test/permitted-contents/represent-transparent-nodes.spec.js +24 -0
  151. package/test/permitted-contents/start.spec.js +67 -0
  152. package/test/placeholder-label-option/index.spec.js +113 -0
  153. package/test/require-accessible-name/index.spec.js +446 -0
  154. package/test/require-datetime/index.spec.js +54 -0
  155. package/test/require-datetime/utils.spec.js +52 -0
  156. package/test/required-attr/index.spec.js +414 -0
  157. package/test/required-element/index.spec.js +188 -0
  158. package/test/required-h1/index.spec.js +69 -0
  159. package/test/use-list/index.spec.js +109 -0
  160. package/test/wai-aria/checkings/value.spec.js +33 -0
  161. package/test/wai-aria/index.spec.js +1173 -0
  162. package/tsconfig.test.json +0 -3
  163. 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 ansi_colors_1 = require("ansi-colors");
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(el) {
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, node, specs) {
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(selectorResult) {
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 (!dep.has.length) {
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(origin) {
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(nodes) {
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(ansi_colors_1.bgBlue.bold(raw));
234
+ out.push(debug_1.bgBlue.bold(raw));
146
235
  }
147
236
  else {
148
- out.push(ansi_colors_1.bgGreen.bold(raw));
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(ansi_colors_1.blue.bold(raw));
243
+ out.push(debug_1.blue.bold(raw));
155
244
  }
156
245
  else {
157
- out.push(ansi_colors_1.green.bold(raw));
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(ansi_colors_1.bgMagenta.bold(raw));
252
+ out.push(debug_1.bgMagenta.bold(raw));
164
253
  }
165
254
  else {
166
- out.push(ansi_colors_1.bgRed.bold(raw));
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, ansi_colors_1.cyan)(raw));
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,2 @@
1
+ declare const _default: Readonly<import('@markuplint/ml-core').RuleSeed<boolean, undefined>>;
2
+ export default _default;
@@ -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
- ariaVersion: ARIAVersion;
3
+ ariaVersion: ARIAVersion;
4
4
  };
5
- declare const _default: import("@markuplint/ml-core").RuleSeed<boolean, Option>;
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: '1.2',
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.option.ariaVersion)) {
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.option.ariaVersion);
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.option.ariaVersion);
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.option.ariaVersion).trim();
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,6 @@
1
+ import type { Lang } from './types';
2
+ type Options = {
3
+ langs?: Lang[];
4
+ };
5
+ declare const _default: Readonly<import('@markuplint/ml-core').RuleSeed<boolean, Options>>;
6
+ export default _default;
@@ -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,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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
- name: string;
4
- value: string | string[];
3
+ name: string;
4
+ value?: string | string[];
5
5
  };
6
- declare const _default: import("@markuplint/ml-core").RuleSeed<RequiredAttributes, null>;
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 = Array.isArray(spec.required) ? spec.required.join(',') : spec.required;
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 || attrToken[0];
76
- report({
77
- scope: {
78
- rule: el.rule,
79
- raw: token.raw,
80
- startCol: token.startCol,
81
- startLine: token.startLine,
82
- },
83
- message,
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
- ignoreHasMutableContents: boolean;
2
+ ignoreHasMutableContents: boolean;
3
3
  };
4
- declare const _default: import("@markuplint/ml-core").RuleSeed<string[], Options>;
4
+ declare const _default: Readonly<import('@markuplint/ml-core').RuleSeed<string[], Options>>;
5
5
  export default _default;