@markuplint/rules 3.0.0-canary.3 → 3.0.0-dev.176

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,6 +1,6 @@
1
1
  type Options = {
2
- extendsExposableElements?: boolean;
3
- ignoreIfAriaBusy?: boolean;
2
+ extendsExposableElements?: boolean;
3
+ ignoreIfAriaBusy?: 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;
@@ -10,16 +10,24 @@ exports.default = (0, ml_core_1.createRule)({
10
10
  },
11
11
  async verify({ document, report, t }) {
12
12
  await document.walkOn('Element', el => {
13
+ /**
14
+ * Exception
15
+ *
16
+ * - The `textarea` element is possibly empty because a user inputs it.
17
+ */
18
+ if (el.localName === 'textarea') {
19
+ return;
20
+ }
13
21
  if (!(0, ml_spec_1.isPalpableElement)(el, el.ownerMLDocument.specs, {
14
22
  extendsSvg: false,
15
- extendsExposableElements: el.rule.option.extendsExposableElements,
23
+ extendsExposableElements: el.rule.options.extendsExposableElements,
16
24
  })) {
17
25
  return;
18
26
  }
19
- if ((0, ml_spec_1.isVoidElement)(el)) {
27
+ if ((0, ml_spec_1.isNothingContentModel)(el)) {
20
28
  return;
21
29
  }
22
- if (el.rule.option.ignoreIfAriaBusy && el.getAttribute('aria-busy') === 'true') {
30
+ if (el.rule.options.ignoreIfAriaBusy && el.getAttribute('aria-busy') === 'true') {
23
31
  return;
24
32
  }
25
33
  const isEmpty = Array.from(el.childNodes).every(node => node.is(node.TEXT_NODE) && node.isWhitespace());
@@ -1,2 +1,4 @@
1
- declare const _default: import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, null>;
1
+ declare const _default: Readonly<
2
+ import('@markuplint/ml-core').RuleSeed<import('@markuplint/ml-core').RuleConfigValue, undefined>
3
+ >;
2
4
  export default _default;
@@ -1,5 +1,10 @@
1
- import type { ARIAVersion } from '@markuplint/ml-spec';
2
- declare const _default: import("@markuplint/ml-core").RuleSeed<import("@markuplint/ml-core").RuleConfigValue, {
3
- ariaVersion: ARIAVersion;
4
- }>;
1
+ declare const _default: Readonly<
2
+ import('@markuplint/ml-core').RuleSeed<
3
+ import('@markuplint/ml-core').RuleConfigValue,
4
+ {
5
+ ariaVersion: '1.1' | '1.2' | '1.3';
6
+ fragmentRefersNameAttr: boolean;
7
+ }
8
+ >
9
+ >;
5
10
  export default _default;
@@ -1,27 +1,50 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const ml_core_1 = require("@markuplint/ml-core");
4
+ const ml_spec_1 = require("@markuplint/ml-spec");
5
+ const shared_1 = require("@markuplint/shared");
6
+ const HYPERLINK_SELECTOR = 'a[href], area[href]';
4
7
  exports.default = (0, ml_core_1.createRule)({
5
8
  defaultOptions: {
6
- ariaVersion: '1.2',
9
+ ariaVersion: ml_spec_1.ARIA_RECOMMENDED_VERSION,
10
+ fragmentRefersNameAttr: false,
7
11
  },
8
12
  async verify({ document, report, t }) {
9
13
  const idList = new Set();
14
+ const nameList = new Set();
10
15
  let hasDynamicId = false;
11
- await document.walkOn('Attr', attr => {
12
- if (attr.name.toLowerCase() !== 'id') {
16
+ let hasDynamicName = false;
17
+ const isMutable = document.nodeList.some(node => node.is(node.MARKUPLINT_PREPROCESSOR_BLOCK));
18
+ if (isMutable) {
19
+ return;
20
+ }
21
+ document.querySelectorAll('[id]').forEach(el => {
22
+ const attr = el.getAttributeNode('id');
23
+ if (!attr) {
13
24
  return;
14
25
  }
15
26
  if (attr.isDynamicValue) {
16
27
  hasDynamicId = true;
17
28
  }
18
29
  if (attr.valueType !== 'code') {
19
- idList.add(attr.value);
30
+ idList.add((0, shared_1.decodeEntities)(attr.value));
20
31
  }
21
32
  });
22
33
  if (hasDynamicId) {
23
34
  return;
24
35
  }
36
+ document.querySelectorAll('[name]').forEach(el => {
37
+ const attr = el.getAttributeNode('name');
38
+ if (!attr) {
39
+ return;
40
+ }
41
+ if (attr.isDynamicValue) {
42
+ hasDynamicName = true;
43
+ }
44
+ if (attr.valueType !== 'code') {
45
+ nameList.add((0, shared_1.decodeEntities)(attr.value));
46
+ }
47
+ });
25
48
  await document.walkOn('Attr', attr => {
26
49
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
27
50
  const attrSpec = (0, ml_core_1.getAttrSpecs)(attr.ownerElement, document.specs);
@@ -69,7 +92,7 @@ exports.default = (0, ml_core_1.createRule)({
69
92
  }
70
93
  }
71
94
  }
72
- const { props } = (0, ml_core_1.ariaSpecs)(document.specs, attr.rule.option.ariaVersion);
95
+ const { props } = (0, ml_core_1.ariaSpecs)(document.specs, attr.rule.options.ariaVersion);
73
96
  const aria = props.find(prop => prop.name === name);
74
97
  if (aria) {
75
98
  if (aria.value === 'ID reference' && !idList.has(value)) {
@@ -100,5 +123,42 @@ exports.default = (0, ml_core_1.createRule)({
100
123
  }
101
124
  }
102
125
  });
126
+ /**
127
+ * @see https://html.spec.whatwg.org/multipage/browsing-the-web.html#scrolling-to-a-fragment
128
+ */
129
+ await document.walkOn('Element', el => {
130
+ var _a, _b, _c, _d;
131
+ if (el.rule.options.fragmentRefersNameAttr && hasDynamicName) {
132
+ return;
133
+ }
134
+ if (!el.matches(HYPERLINK_SELECTOR)) {
135
+ return;
136
+ }
137
+ const href = el.getAttributeNode('href');
138
+ if (!href) {
139
+ return;
140
+ }
141
+ const rawFragment = (_a = href.value.match(/^#(.+)/)) === null || _a === void 0 ? void 0 : _a[1];
142
+ if (rawFragment == null) {
143
+ return;
144
+ }
145
+ const decodedFragment = (0, shared_1.decodeHref)(rawFragment);
146
+ // > 2. If fragment is the empty string, then return the special value top of the document.
147
+ // >
148
+ // > 9. If decodedFragment is an ASCII case-insensitive match for the string top, then return the top of the document.
149
+ if (decodedFragment === '' || /^top$/i.test(decodedFragment)) {
150
+ return;
151
+ }
152
+ if (!idList.has(decodedFragment) &&
153
+ (el.rule.options.fragmentRefersNameAttr ? !nameList.has(decodedFragment) : true)) {
154
+ report({
155
+ scope: href,
156
+ line: (_b = href.valueNode) === null || _b === void 0 ? void 0 : _b.startLine,
157
+ col: (_c = href.valueNode) === null || _c === void 0 ? void 0 : _c.startCol,
158
+ raw: (_d = href.valueNode) === null || _d === void 0 ? void 0 : _d.raw,
159
+ message: t('Missing {0}', t('"{0*}" ID', decodedFragment)),
160
+ });
161
+ }
162
+ });
103
163
  },
104
164
  });
@@ -1,5 +1,5 @@
1
1
  type Options = {
2
- ignore?: string | string[];
2
+ ignore?: string | string[];
3
3
  };
4
- declare const _default: import("@markuplint/ml-core").RuleSeed<boolean, Options>;
4
+ declare const _default: Readonly<import('@markuplint/ml-core').RuleSeed<boolean, Options>>;
5
5
  export default _default;
@@ -10,10 +10,10 @@ exports.default = (0, ml_core_1.createRule)({
10
10
  if (attr.ownerElement.elementType !== 'html') {
11
11
  return;
12
12
  }
13
- const ignoreList = Array.isArray(attr.rule.option.ignore)
14
- ? attr.rule.option.ignore
15
- : attr.rule.option.ignore
16
- ? [attr.rule.option.ignore]
13
+ const ignoreList = Array.isArray(attr.rule.options.ignore)
14
+ ? attr.rule.options.ignore
15
+ : attr.rule.options.ignore
16
+ ? [attr.rule.options.ignore]
17
17
  : [];
18
18
  const name = attr.name;
19
19
  for (const ignore of ignoreList) {
@@ -1,3 +1,10 @@
1
1
  import type { ChildNode, Options, Result, Specs } from './types';
2
2
  import type { PermittedContentChoice } from '@markuplint/ml-spec';
3
- export declare function choice(pattern: PermittedContentChoice, elements: ChildNode[], specs: Specs, options: Options, depth: number): Result;
3
+ import type { ReadonlyDeep } from 'type-fest';
4
+ export declare function choice(
5
+ pattern: ReadonlyDeep<PermittedContentChoice>,
6
+ elements: readonly ChildNode[],
7
+ specs: Specs,
8
+ options: Options,
9
+ depth: number,
10
+ ): Result;
@@ -1,14 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.choice = void 0;
4
+ const debug_1 = require("./debug");
4
5
  const order_1 = require("./order");
5
6
  const utils_1 = require("./utils");
6
- function choice(pattern, elements, specs, options, depth) {
7
+ const indexes = new WeakMap();
8
+ function choice(pattern,
9
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
10
+ elements, specs, options, depth) {
11
+ const choiceLog = debug_1.cmLog.extend(`choice#${depth}`);
7
12
  const collection = new utils_1.Collection(elements);
8
13
  const unmatchedResults = [];
14
+ let i = 0;
9
15
  for (const some of pattern.choice) {
16
+ choiceLog('Patterns[%s]: %s', i, (0, utils_1.modelLog)(some, ''));
10
17
  const result = (0, order_1.order)(some, collection.unmatched, specs, options, depth + 1);
11
18
  if (result.type === 'UNEXPECTED_EXTRA_NODE' || result.type === 'MATCHED' || result.type === 'MATCHED_ZERO') {
19
+ choiceLog('Results[%s]: %s', i, choiceLogString(pattern.choice, i));
12
20
  collection.addMatched(result.matched);
13
21
  return {
14
22
  type: result.type,
@@ -20,11 +28,27 @@ function choice(pattern, elements, specs, options, depth) {
20
28
  };
21
29
  }
22
30
  unmatchedResults.push(result);
31
+ indexes.set(result, i);
32
+ i++;
23
33
  }
24
- const barelyMatchedResult = unmatchedResults.sort((a, b) => b.matched.length - a.matched.length)[0];
34
+ const barelyMatchedResult = unmatchedResults.sort((a, b) => {
35
+ var _c, _d, _e, _f;
36
+ const computed1 = b.matched.length - a.matched.length;
37
+ if (computed1 !== 0) {
38
+ return computed1;
39
+ }
40
+ const _a = (_d = (_c = a.hint.missing) === null || _c === void 0 ? void 0 : _c.barelyMatchedElements) !== null && _d !== void 0 ? _d : 0;
41
+ const _b = (_f = (_e = b.hint.missing) === null || _e === void 0 ? void 0 : _e.barelyMatchedElements) !== null && _f !== void 0 ? _f : 0;
42
+ const computed2 = _b - _a;
43
+ return computed2;
44
+ })[0];
25
45
  if (!barelyMatchedResult) {
26
46
  throw new Error('Unreachable code');
27
47
  }
48
+ const index = indexes.get(barelyMatchedResult);
49
+ if (index != null) {
50
+ choiceLog('Results[%s]: %s', index, choiceLogString(pattern.choice, index, true));
51
+ }
28
52
  return {
29
53
  type: barelyMatchedResult.type,
30
54
  matched: collection.matched,
@@ -35,3 +59,14 @@ function choice(pattern, elements, specs, options, depth) {
35
59
  };
36
60
  }
37
61
  exports.choice = choice;
62
+ function choiceLogString(choice, index, barely = false) {
63
+ const colorFn = barely ? debug_1.bgBlue : debug_1.bgGreen;
64
+ return choice
65
+ .map((pattern, i) => {
66
+ if (i === index) {
67
+ return colorFn((0, utils_1.modelLog)(pattern, ''));
68
+ }
69
+ return (0, utils_1.modelLog)(pattern, '');
70
+ })
71
+ .join(', ');
72
+ }
@@ -1,5 +1,6 @@
1
1
  import type { ChildNode, Options, Result, Specs } from './types';
2
2
  import type { PermittedContentPattern } from '@markuplint/ml-spec';
3
+ import type { ReadonlyDeep } from 'type-fest';
3
4
  /**
4
5
  * Check content condition
5
6
  *
@@ -10,4 +11,10 @@ import type { PermittedContentPattern } from '@markuplint/ml-spec';
10
11
  * @param depth
11
12
  * @returns
12
13
  */
13
- export declare function complexBranch(pattern: PermittedContentPattern, elements: ChildNode[], specs: Specs, options: Options, depth: number): Result;
14
+ export declare function complexBranch(
15
+ pattern: ReadonlyDeep<PermittedContentPattern>,
16
+ elements: readonly ChildNode[],
17
+ specs: Specs,
18
+ options: Options,
19
+ depth: number,
20
+ ): Result;
@@ -15,12 +15,14 @@ const utils_1 = require("./utils");
15
15
  * @param depth
16
16
  * @returns
17
17
  */
18
- function complexBranch(pattern, elements, specs, options, depth) {
18
+ function complexBranch(pattern,
19
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
20
+ elements, specs, options, depth) {
19
21
  if ((0, utils_1.isChoice)(pattern)) {
20
22
  return (0, choice_1.choice)(pattern, elements, specs, options, depth);
21
23
  }
22
24
  if ((0, utils_1.isTransparent)(pattern)) {
23
- return (0, transparent_1.transparent)(elements, specs, options, depth);
25
+ return (0, transparent_1.transparent)(elements);
24
26
  }
25
27
  return (0, count_pattern_1.countPattern)(pattern, elements, specs, options, depth);
26
28
  }
@@ -1,2 +1,2 @@
1
1
  import type { ContentModelResult, Element, Options, TagRule } from './types';
2
- export declare function contentModel(el: Element, rules: TagRule[], options: Options): ContentModelResult[];
2
+ export declare function contentModel(el: Element, rules: readonly TagRule[], options: Options): ContentModelResult[];
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.contentModel = void 0;
4
4
  const ml_spec_1 = require("@markuplint/ml-spec");
5
5
  const start_1 = require("./start");
6
- function contentModel(el, rules, options) {
6
+ function contentModel(
7
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
8
+ el, rules, options) {
7
9
  const model = createModel(el, rules);
8
10
  if (model == null) {
9
11
  return [
@@ -19,14 +21,16 @@ function contentModel(el, rules, options) {
19
21
  return result;
20
22
  }
21
23
  exports.contentModel = contentModel;
22
- function createModel(el, rules) {
24
+ function createModel(
25
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
26
+ el, rules) {
23
27
  const specs = cachedSpecs(el.ownerMLDocument.specs, rules);
24
28
  const model = (0, ml_spec_1.getContentModel)(el, specs.specs);
25
29
  return model;
26
30
  }
27
31
  const caches = new Map();
28
32
  function cachedSpecs(specs, rules) {
29
- if (!rules.length) {
33
+ if (rules.length === 0) {
30
34
  return specs;
31
35
  }
32
36
  const key = JSON.stringify(rules);
@@ -1,5 +1,11 @@
1
1
  import type { ChildNode, Options, Result, Specs } from './types';
2
- import type { PermittedContentOneOrMore, PermittedContentOptional, PermittedContentRequire, PermittedContentZeroOrMore } from '@markuplint/ml-spec';
2
+ import type {
3
+ PermittedContentOneOrMore,
4
+ PermittedContentOptional,
5
+ PermittedContentRequire,
6
+ PermittedContentZeroOrMore,
7
+ } from '@markuplint/ml-spec';
8
+ import type { ReadonlyDeep } from 'type-fest';
3
9
  /**
4
10
  * Check count
5
11
  *
@@ -10,4 +16,14 @@ import type { PermittedContentOneOrMore, PermittedContentOptional, PermittedCont
10
16
  * @param depth
11
17
  * @returns
12
18
  */
13
- export declare function countPattern(pattern: PermittedContentOneOrMore | PermittedContentOptional | PermittedContentRequire | PermittedContentZeroOrMore, elements: readonly ChildNode[], specs: Specs, options: Options, depth: number): Result;
19
+ export declare function countPattern(
20
+ pattern:
21
+ | ReadonlyDeep<PermittedContentOneOrMore>
22
+ | ReadonlyDeep<PermittedContentOptional>
23
+ | ReadonlyDeep<PermittedContentRequire>
24
+ | ReadonlyDeep<PermittedContentZeroOrMore>,
25
+ elements: readonly ChildNode[],
26
+ specs: Specs,
27
+ options: Options,
28
+ depth: number,
29
+ ): Result;
@@ -14,95 +14,75 @@ const utils_1 = require("./utils");
14
14
  * @param depth
15
15
  * @returns
16
16
  */
17
- function countPattern(pattern, elements, specs, options, depth) {
18
- var _a, _b, _c, _d, _e;
17
+ function countPattern(pattern,
18
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
19
+ elements, specs, options, depth) {
19
20
  const ptLog = debug_1.cmLog.extend(`countPattern#${depth}`);
20
21
  const collection = new utils_1.Collection(elements);
21
- let min;
22
- let max;
23
- let model;
24
- let type;
25
- let _type;
26
- if ((0, utils_1.isRequire)(pattern)) {
27
- min = (_a = pattern.min) !== null && _a !== void 0 ? _a : 1;
28
- max = Math.max((_b = pattern.max) !== null && _b !== void 0 ? _b : 1, min);
29
- model = pattern.require;
30
- type = 'MISSING_NODE_REQUIRED';
31
- _type = 'require';
32
- }
33
- else if ((0, utils_1.isOptional)(pattern)) {
34
- min = 0;
35
- max = Math.max((_c = pattern.max) !== null && _c !== void 0 ? _c : 1, 1);
36
- model = pattern.optional;
37
- _type = 'optional';
38
- }
39
- else if ((0, utils_1.isOneOrMore)(pattern)) {
40
- min = 1;
41
- max = Math.max((_d = pattern.max) !== null && _d !== void 0 ? _d : Infinity, 1);
42
- model = pattern.oneOrMore;
43
- type = 'MISSING_NODE_ONE_OR_MORE';
44
- _type = 'one or more';
45
- }
46
- else if ((0, utils_1.isZeroOrMore)(pattern)) {
47
- min = 0;
48
- max = Math.max((_e = pattern.max) !== null && _e !== void 0 ? _e : Infinity, 1);
49
- model = pattern.zeroOrMore;
50
- _type = 'zero or more';
51
- }
52
- else {
53
- throw new Error('Unreachable code');
54
- }
55
- ptLog('%s: %o', _type, model);
22
+ const { model, min, max, repeat, missingType } = (0, utils_1.normalizeModel)(pattern);
23
+ ptLog('Model:\n RegEx: %s\n Schema: %o', (0, utils_1.modelLog)(model, repeat), pattern);
56
24
  let prevResult = null;
25
+ let barelyResult = null;
26
+ let loopCount = 0;
57
27
  // eslint-disable-next-line no-constant-condition
58
28
  while (true) {
59
- ptLog('%s', collection);
29
+ loopCount++;
30
+ ptLog('Check#%s: %s', loopCount, collection);
60
31
  const result = (0, recursive_branch_1.recursiveBranch)(model, collection.unmatched, specs, options, depth);
61
32
  const added = collection.addMatched(result.matched);
33
+ const { matchedCount } = collection;
62
34
  if (result.type === 'UNMATCHED_SELECTOR_BUT_MAY_EMPTY') {
63
- ptLog('MATCHED_ZERO: %s', result.query);
64
- return {
35
+ ptLog('MATCHED_ZERO:\n model: %s\n max: %s\n collection: %s\n matched element: %s', (0, utils_1.modelLog)(model, repeat), max, collection, matchedCount);
36
+ return compereResult({
65
37
  type: 'MATCHED_ZERO',
66
38
  matched: collection.matched,
67
39
  unmatched: collection.unmatched,
68
40
  zeroMatch: true,
69
41
  query: result.query,
70
42
  hint: result.hint,
71
- };
43
+ }, barelyResult);
72
44
  }
73
45
  if (max < collection.matchedCount) {
74
46
  collection.max(max);
75
- ptLog('UNEXPECTED_EXTRA_NODE (max: %s): %s', max, result.query);
76
- return {
47
+ ptLog('UNEXPECTED_EXTRA_NODE:\n model: %s\n max: %s\n collection: %s\n matched element: %s', (0, utils_1.modelLog)(model, repeat), max, collection, matchedCount);
48
+ return compereResult({
77
49
  type: 'UNEXPECTED_EXTRA_NODE',
78
50
  matched: collection.matched,
79
51
  unmatched: collection.unmatched,
80
52
  zeroMatch: result.zeroMatch,
81
53
  query: result.query,
82
- hint: {
83
- ...result.hint,
84
- max,
85
- },
86
- };
54
+ hint: (0, utils_1.mergeHints)(result.hint, { max }),
55
+ }, barelyResult);
87
56
  }
88
57
  if (prevResult) {
89
58
  if (result.type === 'MISSING_NODE_ONE_OR_MORE' ||
90
59
  result.type === 'MISSING_NODE_REQUIRED' ||
91
60
  result.type === 'TRANSPARENT_MODEL_DISALLOWS') {
92
- ptLog('%s(continued): %s', result.type, collection);
93
- return {
61
+ ptLog('%s(continued): %s; Needs', result.type, collection, result.query);
62
+ return compereResult({
94
63
  type: result.type,
95
64
  matched: collection.matched,
96
65
  unmatched: collection.unmatched,
97
66
  zeroMatch: result.zeroMatch,
98
67
  query: result.query,
99
68
  hint: result.hint,
100
- };
69
+ }, barelyResult);
101
70
  }
102
71
  ptLog('%s(continued): %s', prevResult.type, collection);
103
- return prevResult;
72
+ return compereResult(prevResult, barelyResult);
104
73
  }
105
74
  if (added && collection.unmatched.length > 0) {
75
+ if (result.type !== 'MISSING_NODE' && result.type !== 'UNMATCHED_SELECTORS') {
76
+ barelyResult = {
77
+ type: result.type,
78
+ matched: collection.matched,
79
+ unmatched: collection.unmatched,
80
+ zeroMatch: result.zeroMatch,
81
+ query: result.query,
82
+ hint: result.hint,
83
+ };
84
+ }
85
+ ptLog('continue⤴️');
106
86
  continue;
107
87
  }
108
88
  if (collection.matchedCount + (result.zeroMatch ? 1 : 0) < min) {
@@ -110,16 +90,21 @@ function countPattern(pattern, elements, specs, options, depth) {
110
90
  result.type === 'MISSING_NODE_ONE_OR_MORE' ||
111
91
  result.type === 'TRANSPARENT_MODEL_DISALLOWS'
112
92
  ? result.type
113
- : type !== null && type !== void 0 ? type : 'MISSING_NODE_REQUIRED';
114
- ptLog('%s(in %s): %s', resultType, type, result.query);
115
- return {
93
+ : missingType !== null && missingType !== void 0 ? missingType : 'MISSING_NODE_REQUIRED';
94
+ ptLog('%s(in %s); Needs %s', resultType, missingType, result.query);
95
+ return compereResult({
116
96
  type: resultType,
117
97
  matched: collection.matched,
118
98
  unmatched: collection.unmatched,
119
99
  zeroMatch: result.zeroMatch,
120
100
  query: result.query,
121
- hint: result.hint,
122
- };
101
+ hint: (0, utils_1.mergeHints)(result.hint, {
102
+ missing: {
103
+ barelyMatchedElements: collection.matched.length,
104
+ need: result.query,
105
+ },
106
+ }),
107
+ }, barelyResult);
123
108
  }
124
109
  const resultType = collection.matched.length === 0 ? 'MATCHED_ZERO' : 'MATCHED';
125
110
  const zeroMatch = result.zeroMatch || min === 0 || resultType === 'MATCHED_ZERO';
@@ -129,27 +114,49 @@ function countPattern(pattern, elements, specs, options, depth) {
129
114
  unmatched: collection.unmatched,
130
115
  zeroMatch,
131
116
  query: result.query,
132
- hint: result.hint,
117
+ hint: (0, utils_1.mergeHints)(result.hint, {
118
+ missing: {
119
+ barelyMatchedElements: collection.matched.length,
120
+ need: result.query,
121
+ },
122
+ }),
133
123
  };
134
- if (!prevResult && collection.unmatched.length) {
135
- ptLog('continue checking');
124
+ if (collection.unmatched.length > 0) {
136
125
  prevResult = matchedResult;
126
+ ptLog('continue⤴️ (add prev)');
137
127
  continue;
138
128
  }
139
- ptLog('%s: %s', resultType, collection);
129
+ ptLog('%s:\n model: %s\n max: %s\n collection: %s\n matched element: %s', resultType, (0, utils_1.modelLog)(model, repeat), max, collection, matchedCount);
140
130
  if (result.type === 'MISSING_NODE_REQUIRED' ||
141
131
  result.type === 'MISSING_NODE_ONE_OR_MORE' ||
142
132
  result.type === 'TRANSPARENT_MODEL_DISALLOWS') {
143
- return {
133
+ return compereResult({
144
134
  type: result.type,
145
135
  matched: collection.matched,
146
136
  unmatched: collection.unmatched,
147
137
  zeroMatch: result.zeroMatch,
148
138
  query: result.query,
149
139
  hint: result.hint,
150
- };
140
+ }, barelyResult);
151
141
  }
152
- return matchedResult;
142
+ return compereResult(matchedResult, barelyResult);
153
143
  }
154
144
  }
155
145
  exports.countPattern = countPattern;
146
+ const cLog = debug_1.cmLog.extend('countCompereResult');
147
+ function compereResult(
148
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
149
+ a,
150
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
151
+ b) {
152
+ var _a;
153
+ cLog('current: %s %O\nbarely: %s %O', a.type, a.hint, b === null || b === void 0 ? void 0 : b.type, b === null || b === void 0 ? void 0 : b.hint);
154
+ if (b == null) {
155
+ return a;
156
+ }
157
+ if (a.type === 'MATCHED' || a.type === 'MATCHED_ZERO' || a.type === 'UNEXPECTED_EXTRA_NODE') {
158
+ return a;
159
+ }
160
+ const result = (_a = [a, b].sort((a, b) => { var _a, _b, _c, _d; return ((_b = (_a = b.hint.missing) === null || _a === void 0 ? void 0 : _a.barelyMatchedElements) !== null && _b !== void 0 ? _b : 0) - ((_d = (_c = a.hint.missing) === null || _c === void 0 ? void 0 : _c.barelyMatchedElements) !== null && _d !== void 0 ? _d : 0); })[0]) !== null && _a !== void 0 ? _a : a;
161
+ return result;
162
+ }
@@ -0,0 +1,30 @@
1
+ /// <reference types="debug" />
2
+ export declare const cmLog: import('debug').Debugger;
3
+ export declare const bgGreen: {
4
+ (): void;
5
+ bold(): void;
6
+ };
7
+ export declare const green: {
8
+ (): void;
9
+ bold(): void;
10
+ };
11
+ export declare const bgRed: {
12
+ (): void;
13
+ bold(): void;
14
+ };
15
+ export declare const bgBlue: {
16
+ (): void;
17
+ bold(): void;
18
+ };
19
+ export declare const blue: {
20
+ (): void;
21
+ bold(): void;
22
+ };
23
+ export declare const bgMagenta: {
24
+ (): void;
25
+ bold(): void;
26
+ };
27
+ export declare const cyan: {
28
+ (): void;
29
+ bold(): void;
30
+ };
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cyan = exports.bgMagenta = exports.blue = exports.bgBlue = exports.bgRed = exports.green = exports.bgGreen = exports.cmLog = void 0;
4
+ const debug_1 = require("../debug");
5
+ exports.cmLog = debug_1.log.extend('content-model');
6
+ const fn = () => { };
7
+ fn.bold = () => { };
8
+ exports.bgGreen = fn;
9
+ exports.green = fn;
10
+ exports.bgRed = fn;
11
+ exports.bgBlue = fn;
12
+ exports.blue = fn;
13
+ exports.bgMagenta = fn;
14
+ exports.cyan = fn;
@@ -1,2 +1,3 @@
1
1
  /// <reference types="debug" />
2
- export declare const cmLog: import("debug").Debugger;
2
+ export declare const cmLog: import('debug').Debugger;
3
+ export { bgGreen, green, bgRed, bgBlue, blue, bgMagenta, cyan } from 'ansi-colors';
@@ -1,5 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.cmLog = void 0;
3
+ exports.cyan = exports.bgMagenta = exports.blue = exports.bgBlue = exports.bgRed = exports.green = exports.bgGreen = exports.cmLog = void 0;
4
4
  const debug_1 = require("../debug");
5
5
  exports.cmLog = debug_1.log.extend('content-model');
6
+ var ansi_colors_1 = require("ansi-colors");
7
+ Object.defineProperty(exports, "bgGreen", { enumerable: true, get: function () { return ansi_colors_1.bgGreen; } });
8
+ Object.defineProperty(exports, "green", { enumerable: true, get: function () { return ansi_colors_1.green; } });
9
+ Object.defineProperty(exports, "bgRed", { enumerable: true, get: function () { return ansi_colors_1.bgRed; } });
10
+ Object.defineProperty(exports, "bgBlue", { enumerable: true, get: function () { return ansi_colors_1.bgBlue; } });
11
+ Object.defineProperty(exports, "blue", { enumerable: true, get: function () { return ansi_colors_1.blue; } });
12
+ Object.defineProperty(exports, "bgMagenta", { enumerable: true, get: function () { return ansi_colors_1.bgMagenta; } });
13
+ Object.defineProperty(exports, "cyan", { enumerable: true, get: function () { return ansi_colors_1.cyan; } });