@markuplint/rules 3.0.0-dev.25 → 3.0.0-dev.290

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/lib/attr-check.d.ts +3 -2
  2. package/lib/attr-check.js +42 -38
  3. package/lib/attr-duplication/index.d.ts +1 -1
  4. package/lib/attr-duplication/index.js +3 -5
  5. package/lib/attr-value-quotes/index.d.ts +1 -1
  6. package/lib/attr-value-quotes/index.js +5 -9
  7. package/lib/case-sensitive-attr-name/index.d.ts +1 -1
  8. package/lib/case-sensitive-attr-name/index.js +9 -13
  9. package/lib/case-sensitive-tag-name/index.d.ts +1 -1
  10. package/lib/case-sensitive-tag-name/index.js +2 -4
  11. package/lib/character-reference/index.d.ts +1 -1
  12. package/lib/character-reference/index.js +7 -9
  13. package/lib/class-naming/index.d.ts +1 -1
  14. package/lib/class-naming/index.js +9 -13
  15. package/lib/create-message.d.ts +3 -2
  16. package/lib/create-message.js +15 -17
  17. package/lib/debug.js +2 -6
  18. package/lib/deprecated-attr/index.d.ts +1 -1
  19. package/lib/deprecated-attr/index.js +6 -9
  20. package/lib/deprecated-element/index.d.ts +1 -1
  21. package/lib/deprecated-element/index.js +5 -7
  22. package/lib/disallowed-element/index.d.ts +1 -1
  23. package/lib/disallowed-element/index.js +2 -4
  24. package/lib/doctype/index.d.ts +1 -1
  25. package/lib/doctype/index.js +2 -4
  26. package/lib/end-tag/index.d.ts +1 -1
  27. package/lib/end-tag/index.js +5 -8
  28. package/lib/helpers.d.ts +10 -13
  29. package/lib/helpers.js +39 -37
  30. package/lib/id-duplication/index.d.ts +1 -1
  31. package/lib/id-duplication/index.js +2 -4
  32. package/lib/index.d.ts +102 -3
  33. package/lib/index.js +64 -62
  34. package/lib/ineffective-attr/index.d.ts +1 -1
  35. package/lib/ineffective-attr/index.js +5 -10
  36. package/lib/invalid-attr/index.d.ts +25 -5
  37. package/lib/invalid-attr/index.js +166 -43
  38. package/lib/label-has-control/index.d.ts +1 -1
  39. package/lib/label-has-control/index.js +2 -4
  40. package/lib/landmark-roles/index.d.ts +1 -1
  41. package/lib/landmark-roles/index.js +10 -9
  42. package/lib/no-boolean-attr-value/index.d.ts +1 -1
  43. package/lib/no-boolean-attr-value/index.js +6 -9
  44. package/lib/no-default-value/index.d.ts +1 -1
  45. package/lib/no-default-value/index.js +9 -12
  46. package/lib/no-empty-palpable-content/index.d.ts +1 -1
  47. package/lib/no-empty-palpable-content/index.js +13 -7
  48. package/lib/no-hard-code-id/index.d.ts +1 -1
  49. package/lib/no-hard-code-id/index.js +2 -4
  50. package/lib/no-refer-to-non-existent-id/index.d.ts +4 -4
  51. package/lib/no-refer-to-non-existent-id/index.js +79 -23
  52. package/lib/no-use-event-handler-attr/index.d.ts +1 -1
  53. package/lib/no-use-event-handler-attr/index.js +4 -6
  54. package/lib/permitted-contents/choice.d.ts +3 -2
  55. package/lib/permitted-contents/choice.js +54 -14
  56. package/lib/permitted-contents/complex-branch.d.ts +3 -2
  57. package/lib/permitted-contents/complex-branch.js +12 -14
  58. package/lib/permitted-contents/content-model.d.ts +2 -2
  59. package/lib/permitted-contents/content-model.js +11 -11
  60. package/lib/permitted-contents/count-pattern.d.ts +3 -2
  61. package/lib/permitted-contents/count-pattern.js +76 -74
  62. package/lib/permitted-contents/debug.browser.js +9 -12
  63. package/lib/permitted-contents/debug.d.ts +8 -1
  64. package/lib/permitted-contents/debug.js +12 -13
  65. package/lib/permitted-contents/index.d.ts +2 -2
  66. package/lib/permitted-contents/index.js +18 -24
  67. package/lib/permitted-contents/matches-selector.d.ts +2 -2
  68. package/lib/permitted-contents/matches-selector.js +17 -20
  69. package/lib/permitted-contents/order.d.ts +3 -2
  70. package/lib/permitted-contents/order.js +33 -28
  71. package/lib/permitted-contents/recursive-branch.d.ts +4 -3
  72. package/lib/permitted-contents/recursive-branch.js +11 -13
  73. package/lib/permitted-contents/represent-transparent-nodes.d.ts +1 -1
  74. package/lib/permitted-contents/represent-transparent-nodes.js +18 -21
  75. package/lib/permitted-contents/start.d.ts +3 -2
  76. package/lib/permitted-contents/start.js +16 -22
  77. package/lib/permitted-contents/transparent.d.ts +2 -2
  78. package/lib/permitted-contents/transparent.js +6 -9
  79. package/lib/permitted-contents/types.d.ts +20 -13
  80. package/lib/permitted-contents/types.js +1 -2
  81. package/lib/permitted-contents/utils.d.ts +27 -8
  82. package/lib/permitted-contents/utils.js +164 -65
  83. package/lib/placeholder-label-option/index.d.ts +2 -0
  84. package/lib/placeholder-label-option/index.js +84 -0
  85. package/lib/require-accessible-name/index.d.ts +1 -1
  86. package/lib/require-accessible-name/index.js +10 -13
  87. package/lib/require-datetime/index.d.ts +6 -0
  88. package/lib/require-datetime/index.js +32 -0
  89. package/lib/require-datetime/types.d.ts +10 -0
  90. package/lib/require-datetime/types.js +1 -0
  91. package/lib/require-datetime/utils.d.ts +11 -0
  92. package/lib/require-datetime/utils.js +130 -0
  93. package/lib/required-attr/index.d.ts +2 -2
  94. package/lib/required-attr/index.js +24 -24
  95. package/lib/required-element/index.d.ts +1 -1
  96. package/lib/required-element/index.js +3 -5
  97. package/lib/required-h1/index.d.ts +1 -1
  98. package/lib/required-h1/index.js +4 -6
  99. package/lib/use-list/index.d.ts +2 -2
  100. package/lib/use-list/index.js +7 -9
  101. package/lib/wai-aria/checkings/abstract-role.d.ts +1 -1
  102. package/lib/wai-aria/checkings/abstract-role.js +5 -10
  103. package/lib/wai-aria/checkings/default-value.d.ts +2 -2
  104. package/lib/wai-aria/checkings/default-value.js +2 -6
  105. package/lib/wai-aria/checkings/deprecated-props.d.ts +2 -2
  106. package/lib/wai-aria/checkings/deprecated-props.js +3 -8
  107. package/lib/wai-aria/checkings/disallowed-prop.d.ts +2 -2
  108. package/lib/wai-aria/checkings/disallowed-prop.js +27 -8
  109. package/lib/wai-aria/checkings/implicit-props.d.ts +3 -3
  110. package/lib/wai-aria/checkings/implicit-props.js +7 -11
  111. package/lib/wai-aria/checkings/implicit-role.d.ts +1 -1
  112. package/lib/wai-aria/checkings/implicit-role.js +4 -9
  113. package/lib/wai-aria/checkings/interaction-in-hidden.d.ts +1 -1
  114. package/lib/wai-aria/checkings/interaction-in-hidden.js +7 -9
  115. package/lib/wai-aria/checkings/no-global-prop.d.ts +2 -2
  116. package/lib/wai-aria/checkings/no-global-prop.js +2 -7
  117. package/lib/wai-aria/checkings/non-existent-role.d.ts +1 -1
  118. package/lib/wai-aria/checkings/non-existent-role.js +4 -9
  119. package/lib/wai-aria/checkings/permitted-roles.d.ts +1 -1
  120. package/lib/wai-aria/checkings/permitted-roles.js +4 -9
  121. package/lib/wai-aria/checkings/presentational-children.d.ts +1 -1
  122. package/lib/wai-aria/checkings/presentational-children.js +7 -10
  123. package/lib/wai-aria/checkings/required-owned-elements.d.ts +1 -1
  124. package/lib/wai-aria/checkings/required-owned-elements.js +10 -12
  125. package/lib/wai-aria/checkings/required-prop.d.ts +2 -2
  126. package/lib/wai-aria/checkings/required-prop.js +2 -7
  127. package/lib/wai-aria/checkings/value.d.ts +3 -3
  128. package/lib/wai-aria/checkings/value.js +7 -11
  129. package/lib/wai-aria/index.d.ts +2 -2
  130. package/lib/wai-aria/index.js +38 -39
  131. package/lib/wai-aria/types.js +1 -2
  132. package/package.json +21 -14
  133. package/schema.json +6 -0
  134. package/tsconfig.test.json +0 -3
  135. package/tsconfig.tsbuildinfo +0 -1
@@ -1,30 +1,50 @@
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)({
1
+ import { createRule, getAttrSpecs, ariaSpecs } from '@markuplint/ml-core';
2
+ import { ARIA_RECOMMENDED_VERSION } from '@markuplint/ml-spec';
3
+ import { decodeEntities, decodeHref } from '@markuplint/shared';
4
+ const HYPERLINK_SELECTOR = 'a[href], area[href]';
5
+ export default createRule({
5
6
  defaultOptions: {
6
- ariaVersion: '1.2',
7
+ ariaVersion: ARIA_RECOMMENDED_VERSION,
8
+ fragmentRefersNameAttr: false,
7
9
  },
8
10
  async verify({ document, report, t }) {
9
11
  const idList = new Set();
12
+ const nameList = new Set();
10
13
  let hasDynamicId = false;
11
- await document.walkOn('Attr', attr => {
12
- if (attr.name.toLowerCase() !== 'id') {
14
+ let hasDynamicName = false;
15
+ const isMutable = document.nodeList.some(node => node.is(node.MARKUPLINT_PREPROCESSOR_BLOCK));
16
+ if (isMutable) {
17
+ return;
18
+ }
19
+ document.querySelectorAll('[id]').forEach(el => {
20
+ const attr = el.getAttributeNode('id');
21
+ if (!attr) {
13
22
  return;
14
23
  }
15
24
  if (attr.isDynamicValue) {
16
25
  hasDynamicId = true;
17
26
  }
18
27
  if (attr.valueType !== 'code') {
19
- idList.add(attr.value);
28
+ idList.add(decodeEntities(attr.value));
20
29
  }
21
30
  });
22
31
  if (hasDynamicId) {
23
32
  return;
24
33
  }
34
+ document.querySelectorAll('[name]').forEach(el => {
35
+ const attr = el.getAttributeNode('name');
36
+ if (!attr) {
37
+ return;
38
+ }
39
+ if (attr.isDynamicValue) {
40
+ hasDynamicName = true;
41
+ }
42
+ if (attr.valueType !== 'code') {
43
+ nameList.add(decodeEntities(attr.value));
44
+ }
45
+ });
25
46
  await document.walkOn('Attr', attr => {
26
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
27
- const attrSpec = (0, ml_core_1.getAttrSpecs)(attr.ownerElement, document.specs);
47
+ const attrSpec = getAttrSpecs(attr.ownerElement, document.specs);
28
48
  if (!attrSpec) {
29
49
  return;
30
50
  }
@@ -45,9 +65,9 @@ exports.default = (0, ml_core_1.createRule)({
45
65
  if (spec.type === 'DOMID' && !idList.has(value)) {
46
66
  report({
47
67
  scope: attr,
48
- line: (_a = attr.valueNode) === null || _a === void 0 ? void 0 : _a.startLine,
49
- col: (_b = attr.valueNode) === null || _b === void 0 ? void 0 : _b.startCol,
50
- raw: (_c = attr.valueNode) === null || _c === void 0 ? void 0 : _c.raw,
68
+ line: attr.valueNode?.startLine,
69
+ col: attr.valueNode?.startCol,
70
+ raw: attr.valueNode?.raw,
51
71
  message: t('Missing {0}', t('"{0*}" ID', value)),
52
72
  });
53
73
  }
@@ -60,24 +80,24 @@ exports.default = (0, ml_core_1.createRule)({
60
80
  if (!idList.has(ref)) {
61
81
  report({
62
82
  scope: attr,
63
- line: (_d = attr.valueNode) === null || _d === void 0 ? void 0 : _d.startLine,
64
- col: (_e = attr.valueNode) === null || _e === void 0 ? void 0 : _e.startCol,
65
- raw: (_f = attr.valueNode) === null || _f === void 0 ? void 0 : _f.raw,
83
+ line: attr.valueNode?.startLine,
84
+ col: attr.valueNode?.startCol,
85
+ raw: attr.valueNode?.raw,
66
86
  message: t('Missing {0}', t('"{0*}" ID', ref)),
67
87
  });
68
88
  }
69
89
  }
70
90
  }
71
91
  }
72
- const { props } = (0, ml_core_1.ariaSpecs)(document.specs, attr.rule.options.ariaVersion);
92
+ const { props } = ariaSpecs(document.specs, attr.rule.options.ariaVersion);
73
93
  const aria = props.find(prop => prop.name === name);
74
94
  if (aria) {
75
95
  if (aria.value === 'ID reference' && !idList.has(value)) {
76
96
  report({
77
97
  scope: attr,
78
- line: (_g = attr.valueNode) === null || _g === void 0 ? void 0 : _g.startLine,
79
- col: (_h = attr.valueNode) === null || _h === void 0 ? void 0 : _h.startCol,
80
- raw: (_j = attr.valueNode) === null || _j === void 0 ? void 0 : _j.raw,
98
+ line: attr.valueNode?.startLine,
99
+ col: attr.valueNode?.startCol,
100
+ raw: attr.valueNode?.raw,
81
101
  message: t('Missing {0}', t('"{0*}" ID', value)),
82
102
  });
83
103
  }
@@ -90,9 +110,9 @@ exports.default = (0, ml_core_1.createRule)({
90
110
  if (!idList.has(ref)) {
91
111
  report({
92
112
  scope: attr,
93
- line: (_k = attr.valueNode) === null || _k === void 0 ? void 0 : _k.startLine,
94
- col: (_l = attr.valueNode) === null || _l === void 0 ? void 0 : _l.startCol,
95
- raw: (_m = attr.valueNode) === null || _m === void 0 ? void 0 : _m.raw,
113
+ line: attr.valueNode?.startLine,
114
+ col: attr.valueNode?.startCol,
115
+ raw: attr.valueNode?.raw,
96
116
  message: t('Missing {0}', t('"{0*}" ID', ref)),
97
117
  });
98
118
  }
@@ -100,5 +120,41 @@ exports.default = (0, ml_core_1.createRule)({
100
120
  }
101
121
  }
102
122
  });
123
+ /**
124
+ * @see https://html.spec.whatwg.org/multipage/browsing-the-web.html#scrolling-to-a-fragment
125
+ */
126
+ await document.walkOn('Element', el => {
127
+ if (el.rule.options.fragmentRefersNameAttr && hasDynamicName) {
128
+ return;
129
+ }
130
+ if (!el.matches(HYPERLINK_SELECTOR)) {
131
+ return;
132
+ }
133
+ const href = el.getAttributeNode('href');
134
+ if (!href) {
135
+ return;
136
+ }
137
+ const rawFragment = href.value.match(/^#(.+)/)?.[1];
138
+ if (rawFragment == null) {
139
+ return;
140
+ }
141
+ const decodedFragment = decodeHref(rawFragment);
142
+ // > 2. If fragment is the empty string, then return the special value top of the document.
143
+ // >
144
+ // > 9. If decodedFragment is an ASCII case-insensitive match for the string top, then return the top of the document.
145
+ if (decodedFragment === '' || /^top$/i.test(decodedFragment)) {
146
+ return;
147
+ }
148
+ if (!idList.has(decodedFragment) &&
149
+ (el.rule.options.fragmentRefersNameAttr ? !nameList.has(decodedFragment) : true)) {
150
+ report({
151
+ scope: href,
152
+ line: href.valueNode?.startLine,
153
+ col: href.valueNode?.startCol,
154
+ raw: href.valueNode?.raw,
155
+ message: t('Missing {0}', t('"{0*}" ID', decodedFragment)),
156
+ });
157
+ }
158
+ });
103
159
  },
104
160
  });
@@ -1,5 +1,5 @@
1
1
  type Options = {
2
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;
@@ -1,8 +1,6 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const ml_core_1 = require("@markuplint/ml-core");
4
- const helpers_1 = require("../helpers");
5
- exports.default = (0, ml_core_1.createRule)({
1
+ import { createRule } from '@markuplint/ml-core';
2
+ import { match } from '../helpers.js';
3
+ export default createRule({
6
4
  defaultSeverity: 'warning',
7
5
  defaultOptions: {},
8
6
  async verify({ document, report, t }) {
@@ -17,7 +15,7 @@ exports.default = (0, ml_core_1.createRule)({
17
15
  : [];
18
16
  const name = attr.name;
19
17
  for (const ignore of ignoreList) {
20
- if ((0, helpers_1.match)(name, ignore)) {
18
+ if (match(name, ignore)) {
21
19
  return;
22
20
  }
23
21
  }
@@ -1,3 +1,4 @@
1
- import type { ChildNode, Options, Result, Specs } from './types';
1
+ import type { ChildNode, Options, Result, Specs } from './types.js';
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(pattern: ReadonlyDeep<PermittedContentChoice>, elements: readonly ChildNode[], specs: Specs, options: Options, depth: number): Result;
@@ -1,30 +1,60 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.choice = void 0;
4
- const order_1 = require("./order");
5
- const utils_1 = require("./utils");
6
- function choice(pattern, elements, specs, options, depth) {
7
- const collection = new utils_1.Collection(elements);
1
+ import { bgBlue, bgGreen, cmLog } from './debug.js';
2
+ import { order } from './order.js';
3
+ import { Collection, modelLog } from './utils.js';
4
+ const indexes = new WeakMap();
5
+ export function choice(pattern,
6
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
7
+ elements, specs, options, depth) {
8
+ const choiceLog = cmLog.extend(`choice#${depth}`);
9
+ const collection = new Collection(elements);
8
10
  const unmatchedResults = [];
11
+ let i = 0;
9
12
  for (const some of pattern.choice) {
10
- const result = (0, order_1.order)(some, collection.unmatched, specs, options, depth + 1);
11
- if (result.type === 'UNEXPECTED_EXTRA_NODE' || result.type === 'MATCHED' || result.type === 'MATCHED_ZERO') {
12
- collection.addMatched(result.matched);
13
+ choiceLog('Patterns[%s]: %s', i, modelLog(some, ''));
14
+ const result = order(some, collection.unmatched, specs, options, depth + 1);
15
+ if (result.type === 'MATCHED' ||
16
+ result.type === 'MATCHED_ZERO' ||
17
+ (result.type === 'UNEXPECTED_EXTRA_NODE' && result.matched.length >= 1)) {
18
+ choiceLog('Results[%s]: %s', i, choiceLogString(pattern.choice, i));
13
19
  return {
14
20
  type: result.type,
15
- matched: collection.matched,
16
- unmatched: collection.unmatched,
21
+ matched: result.matched,
22
+ unmatched: result.unmatched,
17
23
  zeroMatch: result.zeroMatch,
18
24
  query: result.query,
19
25
  hint: result.hint,
20
26
  };
21
27
  }
22
28
  unmatchedResults.push(result);
29
+ collection.addMatched(result.matched);
30
+ indexes.set(result, i);
31
+ i++;
23
32
  }
24
- const barelyMatchedResult = unmatchedResults.sort((a, b) => b.matched.length - a.matched.length)[0];
33
+ const barelyMatchedResult = unmatchedResults.sort((a, b) => {
34
+ if (a.type !== b.type) {
35
+ if (a.type === 'UNEXPECTED_EXTRA_NODE') {
36
+ return -1;
37
+ }
38
+ if (b.type === 'UNEXPECTED_EXTRA_NODE') {
39
+ return 1;
40
+ }
41
+ }
42
+ const computed1 = b.matched.length - a.matched.length;
43
+ if (computed1 !== 0) {
44
+ return computed1;
45
+ }
46
+ const _a = a.hint.missing?.barelyMatchedElements ?? 0;
47
+ const _b = b.hint.missing?.barelyMatchedElements ?? 0;
48
+ const computed2 = _b - _a;
49
+ return computed2;
50
+ })[0];
25
51
  if (!barelyMatchedResult) {
26
52
  throw new Error('Unreachable code');
27
53
  }
54
+ const index = indexes.get(barelyMatchedResult);
55
+ if (index != null) {
56
+ choiceLog('Results[%s]: %s', index, choiceLogString(pattern.choice, index, true));
57
+ }
28
58
  return {
29
59
  type: barelyMatchedResult.type,
30
60
  matched: collection.matched,
@@ -34,4 +64,14 @@ function choice(pattern, elements, specs, options, depth) {
34
64
  hint: barelyMatchedResult.hint,
35
65
  };
36
66
  }
37
- exports.choice = choice;
67
+ function choiceLogString(choice, index, barely = false) {
68
+ const colorFn = barely ? bgBlue : bgGreen;
69
+ return choice
70
+ .map((pattern, i) => {
71
+ if (i === index) {
72
+ return colorFn(modelLog(pattern, ''));
73
+ }
74
+ return modelLog(pattern, '');
75
+ })
76
+ .join(', ');
77
+ }
@@ -1,5 +1,6 @@
1
- import type { ChildNode, Options, Result, Specs } from './types';
1
+ import type { ChildNode, Options, Result, Specs } from './types.js';
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,4 @@ 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(pattern: ReadonlyDeep<PermittedContentPattern>, elements: readonly ChildNode[], specs: Specs, options: Options, depth: number): Result;
@@ -1,10 +1,7 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.complexBranch = void 0;
4
- const choice_1 = require("./choice");
5
- const count_pattern_1 = require("./count-pattern");
6
- const transparent_1 = require("./transparent");
7
- const utils_1 = require("./utils");
1
+ import { choice } from './choice.js';
2
+ import { countPattern } from './count-pattern.js';
3
+ import { transparent } from './transparent.js';
4
+ import { isChoice, isTransparent } from './utils.js';
8
5
  /**
9
6
  * Check content condition
10
7
  *
@@ -15,13 +12,14 @@ const utils_1 = require("./utils");
15
12
  * @param depth
16
13
  * @returns
17
14
  */
18
- function complexBranch(pattern, elements, specs, options, depth) {
19
- if ((0, utils_1.isChoice)(pattern)) {
20
- return (0, choice_1.choice)(pattern, elements, specs, options, depth);
15
+ export function complexBranch(pattern,
16
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
17
+ elements, specs, options, depth) {
18
+ if (isChoice(pattern)) {
19
+ return choice(pattern, elements, specs, options, depth);
21
20
  }
22
- if ((0, utils_1.isTransparent)(pattern)) {
23
- return (0, transparent_1.transparent)(elements, specs, options, depth);
21
+ if (isTransparent(pattern)) {
22
+ return transparent(elements);
24
23
  }
25
- return (0, count_pattern_1.countPattern)(pattern, elements, specs, options, depth);
24
+ return countPattern(pattern, elements, specs, options, depth);
26
25
  }
27
- exports.complexBranch = complexBranch;
@@ -1,2 +1,2 @@
1
- import type { ContentModelResult, Element, Options, TagRule } from './types';
2
- export declare function contentModel(el: Element, rules: TagRule[], options: Options): ContentModelResult[];
1
+ import type { ContentModelResult, Element, Options, TagRule } from './types.js';
2
+ export declare function contentModel(el: Element, rules: readonly TagRule[], options: Options): ContentModelResult[];
@@ -1,9 +1,8 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.contentModel = void 0;
4
- const ml_spec_1 = require("@markuplint/ml-spec");
5
- const start_1 = require("./start");
6
- function contentModel(el, rules, options) {
1
+ import { getContentModel } from '@markuplint/ml-spec';
2
+ import { start } from './start.js';
3
+ export function contentModel(
4
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
5
+ el, rules, options) {
7
6
  const model = createModel(el, rules);
8
7
  if (model == null) {
9
8
  return [
@@ -15,18 +14,19 @@ function contentModel(el, rules, options) {
15
14
  },
16
15
  ];
17
16
  }
18
- const result = (0, start_1.start)(model, el, el.ownerMLDocument.specs, options);
17
+ const result = start(model, el, el.ownerMLDocument.specs, options);
19
18
  return result;
20
19
  }
21
- exports.contentModel = contentModel;
22
- function createModel(el, rules) {
20
+ function createModel(
21
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
22
+ el, rules) {
23
23
  const specs = cachedSpecs(el.ownerMLDocument.specs, rules);
24
- const model = (0, ml_spec_1.getContentModel)(el, specs.specs);
24
+ const model = getContentModel(el, specs.specs);
25
25
  return model;
26
26
  }
27
27
  const caches = new Map();
28
28
  function cachedSpecs(specs, rules) {
29
- if (!rules.length) {
29
+ if (rules.length === 0) {
30
30
  return specs;
31
31
  }
32
32
  const key = JSON.stringify(rules);
@@ -1,5 +1,6 @@
1
- import type { ChildNode, Options, Result, Specs } from './types';
1
+ import type { ChildNode, Options, Result, Specs } from './types.js';
2
2
  import type { PermittedContentOneOrMore, PermittedContentOptional, PermittedContentRequire, PermittedContentZeroOrMore } from '@markuplint/ml-spec';
3
+ import type { ReadonlyDeep } from 'type-fest';
3
4
  /**
4
5
  * Check count
5
6
  *
@@ -10,4 +11,4 @@ import type { PermittedContentOneOrMore, PermittedContentOptional, PermittedCont
10
11
  * @param depth
11
12
  * @returns
12
13
  */
13
- export declare function countPattern(pattern: PermittedContentOneOrMore | PermittedContentOptional | PermittedContentRequire | PermittedContentZeroOrMore, elements: readonly ChildNode[], specs: Specs, options: Options, depth: number): Result;
14
+ export declare function countPattern(pattern: ReadonlyDeep<PermittedContentOneOrMore> | ReadonlyDeep<PermittedContentOptional> | ReadonlyDeep<PermittedContentRequire> | ReadonlyDeep<PermittedContentZeroOrMore>, elements: readonly ChildNode[], specs: Specs, options: Options, depth: number): Result;
@@ -1,9 +1,7 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.countPattern = void 0;
4
- const debug_1 = require("./debug");
5
- const recursive_branch_1 = require("./recursive-branch");
6
- const utils_1 = require("./utils");
1
+ import { cmLog } from './debug.js';
2
+ import { recursiveBranch } from './recursive-branch.js';
3
+ import { Collection, mergeHints, modelLog, normalizeModel } from './utils.js';
4
+ const cLog = cmLog.extend('countCompereResult');
7
5
  /**
8
6
  * Check count
9
7
  *
@@ -14,95 +12,75 @@ const utils_1 = require("./utils");
14
12
  * @param depth
15
13
  * @returns
16
14
  */
17
- function countPattern(pattern, elements, specs, options, depth) {
18
- var _a, _b, _c, _d, _e;
19
- const ptLog = debug_1.cmLog.extend(`countPattern#${depth}`);
20
- 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);
15
+ export function countPattern(pattern,
16
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
17
+ elements, specs, options, depth) {
18
+ const ptLog = cmLog.extend(`countPattern#${depth}`);
19
+ const collection = new Collection(elements);
20
+ const { model, min, max, repeat, missingType } = normalizeModel(pattern);
21
+ ptLog('Model:\n RegEx: %s\n Schema: %o', modelLog(model, repeat), pattern);
56
22
  let prevResult = null;
23
+ let barelyResult = null;
24
+ let loopCount = 0;
57
25
  // eslint-disable-next-line no-constant-condition
58
26
  while (true) {
59
- ptLog('%s', collection);
60
- const result = (0, recursive_branch_1.recursiveBranch)(model, collection.unmatched, specs, options, depth);
27
+ loopCount++;
28
+ ptLog('Check#%s: %s', loopCount, collection);
29
+ const result = recursiveBranch(model, collection.unmatched, specs, options, depth);
61
30
  const added = collection.addMatched(result.matched);
31
+ const { matchedCount } = collection;
62
32
  if (result.type === 'UNMATCHED_SELECTOR_BUT_MAY_EMPTY') {
63
- ptLog('MATCHED_ZERO: %s', result.query);
64
- return {
33
+ ptLog('MATCHED_ZERO:\n model: %s\n max: %s\n collection: %s\n matched element: %s', modelLog(model, repeat), max, collection, matchedCount);
34
+ return compereResult({
65
35
  type: 'MATCHED_ZERO',
66
36
  matched: collection.matched,
67
37
  unmatched: collection.unmatched,
68
38
  zeroMatch: true,
69
39
  query: result.query,
70
40
  hint: result.hint,
71
- };
41
+ }, barelyResult);
72
42
  }
73
43
  if (max < collection.matchedCount) {
74
44
  collection.max(max);
75
- ptLog('UNEXPECTED_EXTRA_NODE (max: %s): %s', max, result.query);
76
- return {
45
+ ptLog('UNEXPECTED_EXTRA_NODE:\n model: %s\n max: %s\n collection: %s\n matched element: %s', modelLog(model, repeat), max, collection, matchedCount);
46
+ return compereResult({
77
47
  type: 'UNEXPECTED_EXTRA_NODE',
78
48
  matched: collection.matched,
79
49
  unmatched: collection.unmatched,
80
50
  zeroMatch: result.zeroMatch,
81
51
  query: result.query,
82
- hint: {
83
- ...result.hint,
84
- max,
85
- },
86
- };
52
+ hint: mergeHints(result.hint, { max }),
53
+ }, barelyResult);
87
54
  }
88
55
  if (prevResult) {
89
56
  if (result.type === 'MISSING_NODE_ONE_OR_MORE' ||
90
57
  result.type === 'MISSING_NODE_REQUIRED' ||
91
58
  result.type === 'TRANSPARENT_MODEL_DISALLOWS') {
92
- ptLog('%s(continued): %s', result.type, collection);
93
- return {
59
+ ptLog('%s(continued): %s; Needs', result.type, collection, result.query);
60
+ return compereResult({
94
61
  type: result.type,
95
62
  matched: collection.matched,
96
63
  unmatched: collection.unmatched,
97
64
  zeroMatch: result.zeroMatch,
98
65
  query: result.query,
99
66
  hint: result.hint,
100
- };
67
+ }, barelyResult);
101
68
  }
102
69
  ptLog('%s(continued): %s', prevResult.type, collection);
103
- return prevResult;
70
+ return compereResult(prevResult, barelyResult);
104
71
  }
105
72
  if (added && collection.unmatched.length > 0) {
73
+ if (result.type !== 'MISSING_NODE' && result.type !== 'UNMATCHED_SELECTORS') {
74
+ barelyResult = {
75
+ type: result.type,
76
+ matched: collection.matched,
77
+ unmatched: collection.unmatched,
78
+ zeroMatch: result.zeroMatch,
79
+ query: result.query,
80
+ hint: result.hint,
81
+ };
82
+ }
83
+ ptLog('continue⤴️');
106
84
  continue;
107
85
  }
108
86
  if (collection.matchedCount + (result.zeroMatch ? 1 : 0) < min) {
@@ -110,16 +88,21 @@ function countPattern(pattern, elements, specs, options, depth) {
110
88
  result.type === 'MISSING_NODE_ONE_OR_MORE' ||
111
89
  result.type === 'TRANSPARENT_MODEL_DISALLOWS'
112
90
  ? result.type
113
- : type !== null && type !== void 0 ? type : 'MISSING_NODE_REQUIRED';
114
- ptLog('%s(in %s): %s', resultType, type, result.query);
115
- return {
91
+ : missingType ?? 'MISSING_NODE_REQUIRED';
92
+ ptLog('%s(in %s); Needs %s', resultType, missingType, result.query);
93
+ return compereResult({
116
94
  type: resultType,
117
95
  matched: collection.matched,
118
96
  unmatched: collection.unmatched,
119
97
  zeroMatch: result.zeroMatch,
120
98
  query: result.query,
121
- hint: result.hint,
122
- };
99
+ hint: mergeHints(result.hint, {
100
+ missing: {
101
+ barelyMatchedElements: collection.matched.length,
102
+ need: result.query,
103
+ },
104
+ }),
105
+ }, barelyResult);
123
106
  }
124
107
  const resultType = collection.matched.length === 0 ? 'MATCHED_ZERO' : 'MATCHED';
125
108
  const zeroMatch = result.zeroMatch || min === 0 || resultType === 'MATCHED_ZERO';
@@ -129,27 +112,46 @@ function countPattern(pattern, elements, specs, options, depth) {
129
112
  unmatched: collection.unmatched,
130
113
  zeroMatch,
131
114
  query: result.query,
132
- hint: result.hint,
115
+ hint: mergeHints(result.hint, {
116
+ missing: {
117
+ barelyMatchedElements: collection.matched.length,
118
+ need: result.query,
119
+ },
120
+ }),
133
121
  };
134
- if (!prevResult && collection.unmatched.length) {
135
- ptLog('continue checking');
122
+ if (collection.unmatched.length > 0) {
136
123
  prevResult = matchedResult;
124
+ ptLog('continue⤴️ (add prev)');
137
125
  continue;
138
126
  }
139
- ptLog('%s: %s', resultType, collection);
127
+ ptLog('%s:\n model: %s\n max: %s\n collection: %s\n matched element: %s', resultType, modelLog(model, repeat), max, collection, matchedCount);
140
128
  if (result.type === 'MISSING_NODE_REQUIRED' ||
141
129
  result.type === 'MISSING_NODE_ONE_OR_MORE' ||
142
130
  result.type === 'TRANSPARENT_MODEL_DISALLOWS') {
143
- return {
131
+ return compereResult({
144
132
  type: result.type,
145
133
  matched: collection.matched,
146
134
  unmatched: collection.unmatched,
147
135
  zeroMatch: result.zeroMatch,
148
136
  query: result.query,
149
137
  hint: result.hint,
150
- };
138
+ }, barelyResult);
151
139
  }
152
- return matchedResult;
140
+ return compereResult(matchedResult, barelyResult);
141
+ }
142
+ }
143
+ function compereResult(
144
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
145
+ a,
146
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
147
+ b) {
148
+ cLog('current: %s %O\nbarely: %s %O', a.type, a.hint, b?.type, b?.hint);
149
+ if (b == null) {
150
+ return a;
151
+ }
152
+ if (a.type === 'MATCHED' || a.type === 'MATCHED_ZERO' || a.type === 'UNEXPECTED_EXTRA_NODE') {
153
+ return a;
153
154
  }
155
+ const result = [a, b].sort((a, b) => (b.hint.missing?.barelyMatchedElements ?? 0) - (a.hint.missing?.barelyMatchedElements ?? 0))[0] ?? a;
156
+ return result;
154
157
  }
155
- exports.countPattern = countPattern;
@@ -1,14 +1,11 @@
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');
1
+ import { log } from '../debug.js';
2
+ export const cmLog = log.extend('content-model');
6
3
  const fn = () => { };
7
4
  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;
5
+ export const bgGreen = fn;
6
+ export const green = fn;
7
+ export const bgRed = fn;
8
+ export const bgBlue = fn;
9
+ export const blue = fn;
10
+ export const bgMagenta = fn;
11
+ export const cyan = fn;