@markuplint/rules 3.0.0-alpha.66 → 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 (211) hide show
  1. package/README.md +10 -44
  2. package/lib/attr-check.d.ts +22 -10
  3. package/lib/attr-check.js +32 -23
  4. package/lib/attr-duplication/index.d.ts +3 -1
  5. package/lib/attr-duplication/index.js +3 -2
  6. package/lib/attr-value-quotes/index.d.ts +5 -5
  7. package/lib/attr-value-quotes/index.js +1 -1
  8. package/lib/case-sensitive-attr-name/index.d.ts +2 -2
  9. package/lib/case-sensitive-attr-name/index.js +3 -3
  10. package/lib/case-sensitive-tag-name/index.d.ts +2 -2
  11. package/lib/case-sensitive-tag-name/index.js +3 -3
  12. package/lib/character-reference/index.d.ts +1 -1
  13. package/lib/character-reference/index.js +1 -1
  14. package/lib/class-naming/index.d.ts +2 -2
  15. package/lib/class-naming/index.js +3 -5
  16. package/lib/create-message.d.ts +13 -2
  17. package/lib/create-message.js +9 -8
  18. package/lib/debug.d.ts +1 -1
  19. package/lib/debug.js +1 -1
  20. package/lib/deprecated-attr/index.d.ts +3 -1
  21. package/lib/deprecated-element/index.d.ts +3 -1
  22. package/lib/deprecated-element/index.js +4 -4
  23. package/lib/disallowed-element/index.d.ts +1 -1
  24. package/lib/doctype/index.d.ts +3 -3
  25. package/lib/doctype/index.js +2 -2
  26. package/lib/end-tag/index.d.ts +1 -1
  27. package/lib/end-tag/index.js +3 -18
  28. package/lib/helpers.d.ts +38 -47
  29. package/lib/helpers.js +42 -93
  30. package/lib/id-duplication/index.d.ts +3 -1
  31. package/lib/index.d.ts +242 -62
  32. package/lib/index.js +10 -1
  33. package/lib/ineffective-attr/index.d.ts +3 -1
  34. package/lib/ineffective-attr/index.js +3 -6
  35. package/lib/invalid-attr/index.d.ts +40 -12
  36. package/lib/invalid-attr/index.js +159 -28
  37. package/lib/label-has-control/index.d.ts +4 -0
  38. package/lib/label-has-control/index.js +28 -0
  39. package/lib/landmark-roles/index.d.ts +6 -6
  40. package/lib/landmark-roles/index.js +10 -6
  41. package/lib/no-boolean-attr-value/index.d.ts +3 -1
  42. package/lib/no-boolean-attr-value/index.js +1 -1
  43. package/lib/no-default-value/index.d.ts +3 -1
  44. package/lib/no-default-value/index.js +1 -1
  45. package/lib/no-empty-palpable-content/index.d.ts +6 -0
  46. package/lib/no-empty-palpable-content/index.js +42 -0
  47. package/lib/no-hard-code-id/index.d.ts +3 -1
  48. package/lib/no-hard-code-id/index.js +1 -1
  49. package/lib/no-refer-to-non-existent-id/index.d.ts +9 -4
  50. package/lib/no-refer-to-non-existent-id/index.js +65 -5
  51. package/lib/no-use-event-handler-attr/index.d.ts +3 -3
  52. package/lib/no-use-event-handler-attr/index.js +6 -6
  53. package/lib/permitted-contents/choice.d.ts +10 -0
  54. package/lib/permitted-contents/choice.js +72 -0
  55. package/lib/permitted-contents/complex-branch.d.ts +20 -0
  56. package/lib/permitted-contents/complex-branch.js +29 -0
  57. package/lib/permitted-contents/content-model.d.ts +2 -0
  58. package/lib/permitted-contents/content-model.js +53 -0
  59. package/lib/permitted-contents/count-pattern.d.ts +29 -0
  60. package/lib/permitted-contents/count-pattern.js +162 -0
  61. package/lib/permitted-contents/debug.browser.d.ts +30 -0
  62. package/lib/permitted-contents/debug.browser.js +14 -0
  63. package/lib/permitted-contents/debug.d.ts +3 -0
  64. package/lib/permitted-contents/debug.js +13 -0
  65. package/lib/permitted-contents/index.d.ts +2 -8
  66. package/lib/permitted-contents/index.js +76 -249
  67. package/lib/permitted-contents/matches-selector.d.ts +8 -0
  68. package/lib/permitted-contents/matches-selector.js +187 -0
  69. package/lib/permitted-contents/order.d.ts +20 -0
  70. package/lib/permitted-contents/order.js +99 -0
  71. package/lib/permitted-contents/recursive-branch.d.ts +11 -0
  72. package/lib/permitted-contents/recursive-branch.js +45 -0
  73. package/lib/permitted-contents/represent-transparent-nodes.d.ts +10 -0
  74. package/lib/permitted-contents/represent-transparent-nodes.js +78 -0
  75. package/lib/permitted-contents/start.d.ts +18 -0
  76. package/lib/permitted-contents/start.js +73 -0
  77. package/lib/permitted-contents/transparent.d.ts +2 -0
  78. package/lib/permitted-contents/transparent.js +31 -0
  79. package/lib/permitted-contents/types.d.ts +52 -0
  80. package/lib/permitted-contents/types.js +2 -0
  81. package/lib/permitted-contents/utils.d.ts +100 -0
  82. package/lib/permitted-contents/utils.js +302 -0
  83. package/lib/placeholder-label-option/index.d.ts +2 -0
  84. package/lib/placeholder-label-option/index.js +88 -0
  85. package/lib/require-accessible-name/index.d.ts +3 -3
  86. package/lib/require-accessible-name/index.js +12 -4
  87. package/lib/require-datetime/index.d.ts +6 -0
  88. package/lib/require-datetime/index.js +34 -0
  89. package/lib/require-datetime/types.d.ts +10 -0
  90. package/lib/require-datetime/types.js +2 -0
  91. package/lib/require-datetime/utils.d.ts +11 -0
  92. package/lib/require-datetime/utils.js +137 -0
  93. package/lib/required-attr/index.d.ts +5 -5
  94. package/lib/required-attr/index.js +20 -17
  95. package/lib/required-element/index.d.ts +3 -3
  96. package/lib/required-element/index.js +3 -2
  97. package/lib/required-h1/index.d.ts +3 -3
  98. package/lib/required-h1/index.js +4 -3
  99. package/lib/use-list/index.d.ts +4 -4
  100. package/lib/use-list/index.js +9 -7
  101. package/lib/wai-aria/checkings/_.d.ts +15 -0
  102. package/lib/wai-aria/checkings/_.js +27 -0
  103. package/lib/wai-aria/checkings/abstract-role.js +1 -1
  104. package/lib/wai-aria/checkings/default-value.d.ts +7 -3
  105. package/lib/wai-aria/checkings/deprecated-props.d.ts +8 -4
  106. package/lib/wai-aria/checkings/disallowed-prop.d.ts +8 -4
  107. package/lib/wai-aria/checkings/disallowed-prop.js +27 -3
  108. package/lib/wai-aria/checkings/implicit-props.d.ts +8 -4
  109. package/lib/wai-aria/checkings/implicit-props.js +3 -2
  110. package/lib/wai-aria/checkings/implicit-role.js +2 -2
  111. package/lib/wai-aria/checkings/interaction-in-hidden.d.ts +11 -0
  112. package/lib/wai-aria/checkings/interaction-in-hidden.js +49 -0
  113. package/lib/wai-aria/checkings/no-global-prop.d.ts +7 -3
  114. package/lib/wai-aria/checkings/{non-existant-role.d.ts → non-existent-role.d.ts} +1 -1
  115. package/lib/wai-aria/checkings/{non-existant-role.js → non-existent-role.js} +4 -4
  116. package/lib/wai-aria/checkings/permitted-roles.js +1 -1
  117. package/lib/wai-aria/checkings/presentational-children.d.ts +29 -0
  118. package/lib/wai-aria/checkings/presentational-children.js +62 -0
  119. package/lib/wai-aria/checkings/{checking-required-owned-elements.d.ts → required-owned-elements.d.ts} +7 -3
  120. package/lib/wai-aria/checkings/required-owned-elements.js +103 -0
  121. package/lib/wai-aria/checkings/required-prop copy.d.ts +15 -0
  122. package/lib/wai-aria/checkings/required-prop copy.js +27 -0
  123. package/lib/wai-aria/checkings/required-prop.d.ts +12 -6
  124. package/lib/wai-aria/checkings/unadopted-explicit-roles.d.ts +15 -0
  125. package/lib/wai-aria/checkings/unadopted-explicit-roles.js +17 -0
  126. package/lib/wai-aria/checkings/value.d.ts +19 -4
  127. package/lib/wai-aria/checkings/value.js +77 -5
  128. package/lib/wai-aria/index.d.ts +1 -1
  129. package/lib/wai-aria/index.js +27 -16
  130. package/lib/wai-aria/types.d.ts +11 -9
  131. package/lib/xxx/index.d.ts +2 -0
  132. package/lib/xxx/index.js +32 -0
  133. package/package.json +18 -12
  134. package/schema.json +12 -0
  135. package/test/attr-check.spec.js +77 -0
  136. package/test/attr-duplication/index.spec.js +159 -0
  137. package/test/attr-value-quotes/index.spec.js +133 -0
  138. package/test/case-sensitive-attr-name/index.spec.js +65 -0
  139. package/test/case-sensitive-tag-name/index.spec.js +80 -0
  140. package/test/character-reference/index.spec.js +57 -0
  141. package/test/class-naming/index.spec.js +470 -0
  142. package/test/create-message.spec.js +497 -0
  143. package/test/deprecated-attr/index.spec.js +48 -0
  144. package/test/deprecated-element/index.spec.js +48 -0
  145. package/test/disallowed-element/index.spec.js +90 -0
  146. package/test/doctype/index.spec.js +50 -0
  147. package/test/end-tag/index.spec.js +162 -0
  148. package/test/id-duplication/index.spec.js +47 -0
  149. package/test/ineffective-attr/index.spec.js +31 -0
  150. package/test/invalid-attr/index.spec.js +1632 -0
  151. package/test/label-has-control/index.spec.js +29 -0
  152. package/test/landmark-roles/index.spec.js +187 -0
  153. package/test/no-boolean-attr-value/index.spec.js +41 -0
  154. package/test/no-default-value/index.spec.js +74 -0
  155. package/test/no-empty-palpable-content/index.spec.js +115 -0
  156. package/test/no-hard-code-id/index.spec.js +100 -0
  157. package/test/no-refer-to-non-existent-id/index.spec.js +254 -0
  158. package/test/no-use-event-handler-attr/index.spec.js +57 -0
  159. package/test/permitted-contents/choice.spec.js +174 -0
  160. package/test/permitted-contents/count-pattern.spec.js +308 -0
  161. package/test/permitted-contents/index.spec.js +1371 -0
  162. package/test/permitted-contents/matches-selector.spec.js +59 -0
  163. package/test/permitted-contents/order.spec.js +351 -0
  164. package/test/permitted-contents/recursive-branch.spec.js +41 -0
  165. package/test/permitted-contents/represent-transparent-nodes.spec.js +24 -0
  166. package/test/permitted-contents/start.spec.js +67 -0
  167. package/test/placeholder-label-option/index.spec.js +113 -0
  168. package/test/require-accessible-name/index.spec.js +446 -0
  169. package/test/require-datetime/index.spec.js +54 -0
  170. package/test/require-datetime/utils.spec.js +52 -0
  171. package/test/required-attr/index.spec.js +414 -0
  172. package/test/required-element/index.spec.js +188 -0
  173. package/test/required-h1/index.spec.js +69 -0
  174. package/test/use-list/index.spec.js +109 -0
  175. package/test/wai-aria/checkings/value.spec.js +33 -0
  176. package/test/wai-aria/index.spec.js +1173 -0
  177. package/lib/__foo/index.d.ts +0 -2
  178. package/lib/__foo/index.js +0 -46
  179. package/lib/attr-equal-space-after/index.d.ts +0 -3
  180. package/lib/attr-equal-space-after/index.js +0 -90
  181. package/lib/attr-equal-space-before/index.d.ts +0 -3
  182. package/lib/attr-equal-space-before/index.js +0 -90
  183. package/lib/attr-spacing/index.d.ts +0 -6
  184. package/lib/attr-spacing/index.js +0 -100
  185. package/lib/helper/aria/get-aria.d.ts +0 -7
  186. package/lib/helper/aria/get-aria.js +0 -53
  187. package/lib/helper/aria/get-computed-role.d.ts +0 -10
  188. package/lib/helper/aria/get-computed-role.js +0 -24
  189. package/lib/helper/aria/get-implicit-role.d.ts +0 -7
  190. package/lib/helper/aria/get-implicit-role.js +0 -12
  191. package/lib/helper/aria/get-permitted-roles.d.ts +0 -14
  192. package/lib/helper/aria/get-permitted-roles.js +0 -26
  193. package/lib/helper/aria/resolve-version.d.ts +0 -2
  194. package/lib/helper/aria/resolve-version.js +0 -21
  195. package/lib/helper/spec/aria-spec.d.ts +0 -5
  196. package/lib/helper/spec/aria-spec.js +0 -12
  197. package/lib/helper/spec/get-role-spec.d.ts +0 -11
  198. package/lib/helper/spec/get-role-spec.js +0 -39
  199. package/lib/helper/spec/html-spec.d.ts +0 -2
  200. package/lib/helper/spec/html-spec.js +0 -14
  201. package/lib/indentation/index.d.ts +0 -7
  202. package/lib/indentation/index.js +0 -208
  203. package/lib/permitted-contents/array.combination.d.ts +0 -1
  204. package/lib/permitted-contents/array.combination.js +0 -33
  205. package/lib/permitted-contents/permitted-content.spec-to-regexp.d.ts +0 -11
  206. package/lib/permitted-contents/permitted-content.spec-to-regexp.js +0 -230
  207. package/lib/permitted-contents/unfold-content-models-to-tags.d.ts +0 -2
  208. package/lib/permitted-contents/unfold-content-models-to-tags.js +0 -8
  209. package/lib/wai-aria/checkings/checking-required-owned-elements.js +0 -63
  210. package/tsconfig.test.json +0 -3
  211. package/tsconfig.tsbuildinfo +0 -1
@@ -1,281 +1,108 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
3
  const ml_core_1 = require("@markuplint/ml-core");
5
- const ml_spec_1 = require("@markuplint/ml-spec");
6
- const permitted_content_spec_to_regexp_1 = tslib_1.__importDefault(require("./permitted-content.spec-to-regexp"));
7
- const expMapOnNodeId = new Map();
4
+ const content_model_1 = require("./content-model");
5
+ const represent_transparent_nodes_1 = require("./represent-transparent-nodes");
8
6
  exports.default = (0, ml_core_1.createRule)({
9
7
  defaultValue: [],
10
8
  defaultOptions: {
11
9
  ignoreHasMutableChildren: true,
12
10
  },
13
11
  async verify({ document, report, t }) {
14
- let idCounter = 0;
15
12
  await document.walkOn('Element', el => {
16
- var _a;
17
- if (!el.rule.value) {
13
+ var _a, _b, _c;
14
+ if (el.rule.options.ignoreHasMutableChildren && el.hasMutableChildren()) {
18
15
  return;
19
16
  }
20
- if (el.rule.option.ignoreHasMutableChildren && el.hasMutableChildren()) {
21
- return;
22
- }
23
- const specType = el.namespaceURI === 'http://www.w3.org/1999/xhtml'
24
- ? 'HTML'
25
- : el.namespaceURI === 'http://www.w3.org/2000/svg'
26
- ? 'SVG'
27
- : 'Any Language';
28
- const childNodes = el.getChildElementsAndTextNodeWithoutWhitespaces();
29
- const spec = !el.isCustomElement && ((_a = (0, ml_core_1.getSpec)(el, document.specs)) === null || _a === void 0 ? void 0 : _a.contentModel);
30
- const expGen = new permitted_content_spec_to_regexp_1.default(document.specs, idCounter++);
31
- if (spec) {
32
- if (spec.descendantOf && !el.closest(spec.descendantOf)) {
33
- report({
34
- scope: el,
35
- message: t('{0} must be {1}', t('the "{0*}" {1}', el.localName, 'element'), t('{0} of {1}', 'descendant', t('the "{0*}" {1}', spec.descendantOf, 'element'))),
36
- });
37
- return;
17
+ const results = (0, content_model_1.contentModel)(el, el.rule.value, el.rule.options);
18
+ for (const { type, scope, query, hint } of results) {
19
+ let message = '';
20
+ if (hint.max != null) {
21
+ message =
22
+ t('there is more content than it needs') +
23
+ t('. ') +
24
+ t('the max number of elements required is {0}', `${hint.max}`);
38
25
  }
39
- let matched = false;
40
- if (spec.conditional) {
41
- for (const conditional of spec.conditional) {
42
- matched = el.matches(conditional.condition);
43
- // console.log({ ...conditional, matched });
44
- if (matched) {
45
- try {
46
- const parentExp = getRegExpFromParentNode(document.specs, el, expGen);
47
- const exp = expGen.specToRegExp(conditional.contents, parentExp, el.namespaceURI);
48
- const conditionalResult = match(exp, childNodes, false, document.specs);
49
- if (!conditionalResult) {
50
- const message = t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'content'), t('the "{0*}" {1}', el.localName, 'element')), 'invalid');
51
- report({
52
- scope: el,
53
- message: specType !== 'Any Language'
54
- ? t('{0} according to {1}', message, `the ${specType} specification`)
55
- : message,
56
- line: el.startLine,
57
- col: el.startCol,
58
- raw: el.raw,
59
- });
60
- break;
61
- }
62
- }
63
- catch (e) {
64
- if (e instanceof Error) {
65
- // eslint-disable-next-line no-console
66
- console.warn(el.raw, 'conditional', conditional, e.message);
67
- }
68
- else {
69
- throw e;
70
- }
71
- }
72
- }
26
+ switch (type) {
27
+ case 'MATCHED':
28
+ case 'MATCHED_ZERO': {
29
+ break;
73
30
  }
74
- }
75
- if (!matched) {
76
- try {
77
- const exp = getRegExpFromNode(document.specs, el, expGen);
78
- const specResult = match(exp, childNodes, false, document.specs);
79
- if (!specResult) {
80
- const message = t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'content'), t('the "{0*}" {1}', el.localName, 'element')), 'invalid');
81
- report({
82
- scope: el,
83
- message: specType !== 'Any Language'
84
- ? t('{0} according to {1}', message, `the ${specType} specification`)
85
- : message,
86
- });
87
- }
31
+ case 'MISSING_NODE_ONE_OR_MORE': {
32
+ message =
33
+ message ||
34
+ t('Require {0}', t('one or more elements')) + t('. ') + '(' + t('Need "{0*}"', query) + ')';
35
+ report({
36
+ scope,
37
+ message,
38
+ });
39
+ break;
88
40
  }
89
- catch (e) {
90
- if (e instanceof Error) {
91
- // eslint-disable-next-line no-console
92
- console.warn(el.raw, 'HTML Spec', e.message);
93
- }
94
- else {
95
- throw e;
96
- }
41
+ case 'MISSING_NODE_REQUIRED': {
42
+ message =
43
+ message ||
44
+ t('Require {0}', t('an {0}', 'element')) + t('. ') + '(' + t('Need "{0*}"', query) + ')';
45
+ report({
46
+ scope,
47
+ message,
48
+ });
49
+ break;
97
50
  }
98
- }
99
- }
100
- for (const rule of el.rule.value) {
101
- if (rule.tag.toLowerCase() !== el.localName.toLowerCase()) {
102
- continue;
103
- }
104
- const parentExp = getRegExpFromParentNode(document.specs, el, expGen);
105
- try {
106
- const exp = expGen.specToRegExp(rule.contents, parentExp, el.namespaceURI);
107
- // Evaluate the custom element if the optional schema.
108
- const r = match(exp, childNodes, el.isCustomElement, document.specs);
109
- if (!r) {
51
+ case 'UNEXPECTED_EXTRA_NODE': {
52
+ const not = (_a = hint.not) !== null && _a !== void 0 ? _a : scope;
53
+ message =
54
+ message ||
55
+ (represent_transparent_nodes_1.transparentMode.has(scope)
56
+ ? t('{0} is not allowed in {1} through the transparent model in this context', name(not, t), name(el, t))
57
+ : t('{0} is not allowed in {1} in this context', name(not, t), name(el, t)));
110
58
  report({
111
- scope: el,
112
- message: t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'content'), t('the "{0*}" {1}', el.localName, 'element')), 'invalid'),
113
- line: el.startLine,
114
- col: el.startCol,
115
- raw: el.raw,
59
+ scope: not,
60
+ message,
116
61
  });
117
- return;
62
+ break;
118
63
  }
119
- }
120
- catch (e) {
121
- if (e instanceof Error) {
122
- // eslint-disable-next-line no-console
123
- console.warn(el.raw, 'rule', rule, e.message);
64
+ case 'TRANSPARENT_MODEL_DISALLOWS': {
65
+ const not = (_b = hint.not) !== null && _b !== void 0 ? _b : scope;
66
+ const tp = (_c = hint.transparent) !== null && _c !== void 0 ? _c : el;
67
+ report({
68
+ scope: not,
69
+ message: t('{0} is {1} but {2}', name(tp, t), t('a {0}', 'transparent model'), t('also disallows {0} in this context', name(not, t))),
70
+ });
71
+ break;
124
72
  }
125
- else {
126
- throw e;
73
+ case 'NOTHING': {
74
+ report({
75
+ scope: el,
76
+ message: t('{0} disallows {1}', t('the {0}', 'element'), 'contents'),
77
+ });
78
+ break;
79
+ }
80
+ default: {
81
+ throw new Error('Unreachable code');
127
82
  }
128
83
  }
129
84
  }
85
+ represent_transparent_nodes_1.transparentMode.clear();
130
86
  });
131
- expMapOnNodeId.clear();
132
87
  },
133
88
  });
134
- function normalization(el, evalCustomElement) {
135
- return el
136
- .map(node => {
137
- // FIXME: https://github.com/markuplint/markuplint/issues/388
138
- if (!evalCustomElement && node.is(node.ELEMENT_NODE) && node.isCustomElement) {
139
- return '';
140
- }
141
- if (!node.is(node.ELEMENT_NODE)) {
142
- return '<#text>';
143
- }
144
- const { localNameWithNS } = (0, ml_core_1.resolveNamespace)(node.localName, node.namespaceURI);
145
- return `<${localNameWithNS}>`;
146
- })
147
- .join('');
148
- }
149
- function getRegExpFromNode(specs, el, expGen) {
150
- var _a;
151
- // console.log({ n: node.nodeName });
152
- if (expMapOnNodeId.has(el.uuid)) {
153
- return expMapOnNodeId.get(el.uuid);
89
+ function name(
90
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
91
+ scope, t) {
92
+ if (scope.is(scope.ELEMENT_NODE)) {
93
+ return t('the "{0}" {1}', scope.localName, 'element');
154
94
  }
155
- const parentExp = el.parentNode ? getRegExpFromNode(specs, el.parentNode, expGen) : null;
156
- const spec = el.nodeType === el.ELEMENT_NODE && !el.isCustomElement && ((_a = (0, ml_core_1.getSpec)(el, specs)) === null || _a === void 0 ? void 0 : _a.contentModel);
157
- const contentRule = spec ? spec.contents : true;
158
- const exp = expGen.specToRegExp(contentRule, parentExp, el.nodeType === el.ELEMENT_NODE ? el.namespaceURI : undefined);
159
- expMapOnNodeId.set(el.uuid, exp);
160
- return exp;
161
- }
162
- function getRegExpFromParentNode(specs, el, expGen) {
163
- // console.log({ p: node.nodeName });
164
- const parentExp = el.parentNode ? getRegExpFromNode(specs, el.parentNode, expGen) : null;
165
- return parentExp;
166
- }
167
- const matchingCacheMap = new Map();
168
- function match(exp, childNodes, evalCustomElement, specs) {
169
- const target = normalization(childNodes, evalCustomElement);
170
- const cacheKey = target +
171
- exp.source +
172
- childNodes.map(n => (n.is(n.ELEMENT_NODE) ? n.toNormalizeString() : n.originRaw)).join('');
173
- const res = matchingCacheMap.get(cacheKey);
174
- if (res != null) {
175
- return res;
95
+ if (scope.is(scope.TEXT_NODE)) {
96
+ return t('the {0}', 'text node');
176
97
  }
177
- const matched = _match(target, exp, childNodes, specs);
178
- matchingCacheMap.set(cacheKey, matched);
179
- return matched;
180
- }
181
- function _match(target, exp, childNodes, specs) {
182
- const result = exp.exec(target);
183
- if (!result) {
184
- return false;
98
+ if (scope.is(scope.CDATA_SECTION_NODE)) {
99
+ return t('the {0}', 'comment');
185
100
  }
186
- const capGroups = result.groups;
187
- // console.log({ exp, target, capGroups });
188
- if (!capGroups) {
189
- return true;
101
+ if (scope.is(scope.DOCUMENT_TYPE_NODE)) {
102
+ return t('the {0}', 'doctype');
190
103
  }
191
- const groupNames = Object.keys(capGroups);
192
- for (const groupName of groupNames) {
193
- const matched = capGroups[groupName];
194
- if (!matched) {
195
- continue;
196
- }
197
- const [type, , ..._selector] = groupName.split(/(?<=[a-z0-9])_/gi);
198
- // console.log({ type, _selector });
199
- switch (type) {
200
- case 'ACM': {
201
- const [model, tag] = _selector;
202
- const selectors = (0, ml_spec_1.contentModelCategoryToTagNames)(`#${model}`, specs).filter(selector => {
203
- const [, tagName] = /^([^[\]]+)(?:\[[^\]]+\])?$/.exec(selector) || [];
204
- return tagName.toLowerCase() === tag.toLowerCase();
205
- });
206
- for (const node of childNodes) {
207
- if (node.is(node.TEXT_NODE)) {
208
- continue;
209
- }
210
- if (node.nodeName.toLowerCase() !== tag.toLowerCase()) {
211
- continue;
212
- }
213
- for (const selector of selectors) {
214
- const matched = node.matches(selector);
215
- // console.log({ raw: node.raw, selector, matched });
216
- if (matched) {
217
- return true;
218
- }
219
- }
220
- return false;
221
- }
222
- break;
223
- }
224
- case 'NAD': {
225
- let targetsMaybeIncludesNotAllowedDescendants = Array.from(new Set(matched.split(/><|<|>/g).filter(_ => _)));
226
- const contents = new Set();
227
- const transparentGroupName = groupNames.find(name => /^TRANSPARENT_[0-9]+$/.test(name));
228
- const inTransparent = _selector.includes('__InTRANSPARENT')
229
- ? transparentGroupName && capGroups[transparentGroupName]
230
- ? capGroups[transparentGroupName].split(/><|<|>/g).filter(_ => _)
231
- : null
232
- : null;
233
- _selector.forEach(content => {
234
- if (content[0] === '_') {
235
- (0, ml_spec_1.contentModelCategoryToTagNames)(content.replace('_', '#'), specs).forEach(tag => contents.add(tag));
236
- return;
237
- }
238
- contents.add(content);
239
- });
240
- const selectors = Array.from(contents);
241
- targetsMaybeIncludesNotAllowedDescendants = targetsMaybeIncludesNotAllowedDescendants.filter(content => inTransparent ? inTransparent.includes(content) : true);
242
- // console.log({
243
- // groupName,
244
- // matched,
245
- // _selector,
246
- // type,
247
- // selectors,
248
- // inTransparent,
249
- // targetsMaybeIncludesNotAllowedDescendants,
250
- // });
251
- for (const node of childNodes) {
252
- for (const target of targetsMaybeIncludesNotAllowedDescendants) {
253
- if (node.is(node.TEXT_NODE)) {
254
- if (selectors.includes('#text')) {
255
- return false;
256
- }
257
- continue;
258
- }
259
- if (node.nodeName.toLowerCase() !== target.toLowerCase()) {
260
- continue;
261
- }
262
- for (const selector of selectors) {
263
- // console.log({ selector, target });
264
- // Self
265
- if (node.matches(selector)) {
266
- return false;
267
- }
268
- // Descendants
269
- const nodeList = node.querySelectorAll(selector);
270
- if (nodeList.length) {
271
- return false;
272
- }
273
- }
274
- }
275
- }
276
- break;
277
- }
278
- }
104
+ if (scope.is(scope.MARKUPLINT_PREPROCESSOR_BLOCK)) {
105
+ return t('the {0}', 'code block');
279
106
  }
280
- return true;
107
+ return t('the {0}', 'node');
281
108
  }
@@ -0,0 +1,8 @@
1
+ import type { ChildNode, Result, Specs } from './types';
2
+ export type SelectorResult = Result<'UNMATCHED_SELECTOR_BUT_MAY_EMPTY' | 'MISSING_NODE' | 'UNMATCHED_SELECTORS'>;
3
+ export declare function matchesSelector(
4
+ query: string,
5
+ node: ChildNode | undefined,
6
+ specs: Specs,
7
+ depth: number,
8
+ ): SelectorResult;
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.matchesSelector = void 0;
4
+ const ml_spec_1 = require("@markuplint/ml-spec");
5
+ const debug_1 = require("./debug");
6
+ const utils_1 = require("./utils");
7
+ function matchesSelector(query,
8
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
9
+ node, specs, depth) {
10
+ const nodeLog = debug_1.cmLog.extend(`node#${depth}`);
11
+ const { selector, hasText, hasCustom } = optCondition(query, specs);
12
+ if (node == null) {
13
+ if (hasText) {
14
+ nodeLog('<#text>.matches(%s) => ""', query);
15
+ return {
16
+ type: 'MATCHED_ZERO',
17
+ matched: [],
18
+ unmatched: [],
19
+ zeroMatch: true,
20
+ query,
21
+ hint: {},
22
+ };
23
+ }
24
+ return {
25
+ type: 'MISSING_NODE',
26
+ matched: [],
27
+ unmatched: [],
28
+ zeroMatch: false,
29
+ query,
30
+ hint: {},
31
+ };
32
+ }
33
+ if (node.is(node.TEXT_NODE)) {
34
+ if (hasText) {
35
+ nodeLog('<#text>.matches(%s) => "%s"', query, node.raw.trim());
36
+ return {
37
+ type: 'MATCHED',
38
+ matched: [node],
39
+ unmatched: [],
40
+ zeroMatch: true,
41
+ query,
42
+ hint: {},
43
+ };
44
+ }
45
+ if (node.isWhitespace()) {
46
+ nodeLog('<#text>.matches(%s) => WHITESPACE', query);
47
+ return {
48
+ type: 'MATCHED',
49
+ matched: [node],
50
+ unmatched: [],
51
+ zeroMatch: true,
52
+ query,
53
+ hint: {},
54
+ };
55
+ }
56
+ // Disallows a text node
57
+ return {
58
+ type: 'UNEXPECTED_EXTRA_NODE',
59
+ matched: [],
60
+ unmatched: [node],
61
+ zeroMatch: false,
62
+ query,
63
+ hint: {},
64
+ };
65
+ }
66
+ if (node.is(node.MARKUPLINT_PREPROCESSOR_BLOCK)) {
67
+ nodeLog('%s.matches(%s) => PBlock', node.raw, query);
68
+ return {
69
+ type: 'MATCHED',
70
+ matched: [node],
71
+ unmatched: [],
72
+ zeroMatch: !!hasText,
73
+ query,
74
+ hint: {},
75
+ };
76
+ }
77
+ if (node.is(node.ELEMENT_NODE)) {
78
+ if (node.elementType !== 'html' && hasCustom) {
79
+ nodeLog('%s.matches(%s) => CustomElement', node.raw, query);
80
+ return {
81
+ type: 'MATCHED',
82
+ matched: [node],
83
+ unmatched: [],
84
+ zeroMatch: !!hasText,
85
+ query,
86
+ hint: {},
87
+ };
88
+ }
89
+ const result = (0, utils_1.matches)(selector, node, specs);
90
+ nodeLog('%s.matches(%s) => %s', node.raw, query, result.matched);
91
+ if (result.matched) {
92
+ return {
93
+ type: 'MATCHED',
94
+ matched: [node],
95
+ unmatched: [],
96
+ zeroMatch: !!hasText,
97
+ query,
98
+ hint: {},
99
+ };
100
+ }
101
+ if (hasText) {
102
+ return {
103
+ type: 'UNMATCHED_SELECTOR_BUT_MAY_EMPTY',
104
+ matched: [],
105
+ unmatched: [node],
106
+ zeroMatch: true,
107
+ query,
108
+ hint: (0, utils_1.cleanObject)({
109
+ not: result.not,
110
+ }),
111
+ };
112
+ }
113
+ return {
114
+ type: 'UNMATCHED_SELECTORS',
115
+ matched: [],
116
+ unmatched: [node],
117
+ zeroMatch: false,
118
+ query,
119
+ hint: (0, utils_1.cleanObject)({
120
+ not: result.not,
121
+ }),
122
+ };
123
+ }
124
+ return {
125
+ type: 'MATCHED',
126
+ matched: [node],
127
+ unmatched: [],
128
+ zeroMatch: !!hasText,
129
+ query,
130
+ hint: {},
131
+ };
132
+ }
133
+ exports.matchesSelector = matchesSelector;
134
+ const conditionWithoutSpecs = {
135
+ '#custom': {
136
+ selector: '#custom',
137
+ hasCustom: true,
138
+ hasText: false,
139
+ },
140
+ '#text': {
141
+ selector: '#text',
142
+ hasCustom: false,
143
+ hasText: true,
144
+ },
145
+ };
146
+ const optConditionSpecsBaseCaches = new Map();
147
+ function optCondition(query, specs) {
148
+ var _a;
149
+ const condWithoutSpecs = conditionWithoutSpecs[query];
150
+ if (condWithoutSpecs) {
151
+ return condWithoutSpecs;
152
+ }
153
+ const queryCaches = (_a = optConditionSpecsBaseCaches.get(specs)) !== null && _a !== void 0 ? _a : new Map();
154
+ const cached = queryCaches.get(query);
155
+ if (cached) {
156
+ return cached;
157
+ }
158
+ let hasCustom = false;
159
+ let hasText = false;
160
+ const selector = query.replace(/^(?::model\(([^)]+)\)|#([a-z-]+))(.*)$/, (_, $model, _model, $last) => {
161
+ const _selectors = (0, ml_spec_1.contentModelCategoryToTagNames)(`#${$model !== null && $model !== void 0 ? $model : _model}`, specs.def);
162
+ if (_selectors.length === 0) {
163
+ throw new Error(`${$model !== null && $model !== void 0 ? $model : _model} is empty`);
164
+ }
165
+ const selectors = [];
166
+ for (const selector of _selectors) {
167
+ if (selector === '#custom') {
168
+ hasCustom = true;
169
+ continue;
170
+ }
171
+ if (selector === '#text') {
172
+ hasText = true;
173
+ continue;
174
+ }
175
+ selectors.push(selector);
176
+ }
177
+ return `:is(${selectors.join(',')})${$last !== null && $last !== void 0 ? $last : ''}`;
178
+ });
179
+ const result = {
180
+ selector,
181
+ hasCustom,
182
+ hasText,
183
+ };
184
+ queryCaches.set(query, result);
185
+ optConditionSpecsBaseCaches.set(specs, queryCaches);
186
+ return result;
187
+ }
@@ -0,0 +1,20 @@
1
+ import type { ChildNode, Options, Result, Specs } from './types';
2
+ import type { PermittedContentPattern } from '@markuplint/ml-spec';
3
+ import type { ReadonlyDeep } from 'type-fest';
4
+ /**
5
+ * Check ordered array
6
+ *
7
+ * @param contents
8
+ * @param nodes
9
+ * @param specs
10
+ * @param options
11
+ * @param depth
12
+ * @returns
13
+ */
14
+ export declare function order(
15
+ contents: ReadonlyDeep<PermittedContentPattern[]>,
16
+ nodes: readonly ChildNode[],
17
+ specs: Specs,
18
+ options: Options,
19
+ depth: number,
20
+ ): Result;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.order = void 0;
4
+ const helpers_1 = require("../helpers");
5
+ const complex_branch_1 = require("./complex-branch");
6
+ const debug_1 = require("./debug");
7
+ const utils_1 = require("./utils");
8
+ /**
9
+ * Check ordered array
10
+ *
11
+ * @param contents
12
+ * @param nodes
13
+ * @param specs
14
+ * @param options
15
+ * @param depth
16
+ * @returns
17
+ */
18
+ function order(contents,
19
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
20
+ nodes, specs, options, depth) {
21
+ var _a, _b, _c, _d, _e;
22
+ const orderLog = debug_1.cmLog.extend(`order#${depth}`);
23
+ const btLog = debug_1.cmLog.extend(`backtrack#${depth}`);
24
+ const patterns = (0, helpers_1.deepCopy)(contents);
25
+ const collection = new utils_1.Collection(nodes);
26
+ orderLog('Model:\n RegEx: %s\n Schema: %o', (0, utils_1.modelLog)(patterns, ''), patterns);
27
+ orderLog('Starts: %s', collection);
28
+ let result = undefined;
29
+ let backtrackMode = false;
30
+ let afterBacktrack = false;
31
+ const unmatchedResults = [];
32
+ while (patterns.length > 0 && patterns[0]) {
33
+ result = (0, complex_branch_1.complexBranch)(patterns[0], collection.unmatched, specs, options, depth);
34
+ collection.addMatched(result.matched);
35
+ if (result.type !== 'UNEXPECTED_EXTRA_NODE' && result.type !== 'MATCHED' && result.type !== 'MATCHED_ZERO') {
36
+ unmatchedResults.push(result);
37
+ if (backtrackMode) {
38
+ collection.back();
39
+ btLog('🔙◀BACK');
40
+ backtrackMode = false;
41
+ afterBacktrack = true;
42
+ continue;
43
+ }
44
+ const barelyMatchedResult = unmatchedResults.sort((a, b) => b.matched.length - a.matched.length)[0];
45
+ if (!barelyMatchedResult) {
46
+ throw new Error('Unreachable code');
47
+ }
48
+ orderLog('Result (%s): %s%s', result.type, collection.toString(true), ((_a = barelyMatchedResult.hint.missing) === null || _a === void 0 ? void 0 : _a.barelyMatchedElements) != null
49
+ ? `; But ${barelyMatchedResult.hint.missing.barelyMatchedElements} elements hit out of pattern`
50
+ : '');
51
+ return {
52
+ type: barelyMatchedResult.type,
53
+ matched: collection.matched,
54
+ unmatched: collection.unmatched,
55
+ zeroMatch: barelyMatchedResult.zeroMatch,
56
+ query: barelyMatchedResult.query,
57
+ hint: (0, utils_1.mergeHints)(barelyMatchedResult.hint, {
58
+ missing: {
59
+ barelyMatchedElements: collection.matched.length,
60
+ need: barelyMatchedResult.query,
61
+ },
62
+ }),
63
+ };
64
+ }
65
+ if (afterBacktrack) {
66
+ collection.lock();
67
+ afterBacktrack = false;
68
+ }
69
+ if (result.zeroMatch) {
70
+ backtrackMode = true;
71
+ }
72
+ else {
73
+ backtrackMode = false;
74
+ }
75
+ patterns.shift();
76
+ }
77
+ if (collection.unmatched.length > 0) {
78
+ orderLog('Result (UNEXPECTED_EXTRA_NODE): %s', collection.toString(true));
79
+ return {
80
+ type: 'UNEXPECTED_EXTRA_NODE',
81
+ matched: collection.matched,
82
+ unmatched: collection.unmatched,
83
+ zeroMatch: false,
84
+ query: (_b = result === null || result === void 0 ? void 0 : result.query) !== null && _b !== void 0 ? _b : 'N/A',
85
+ hint: (_c = result === null || result === void 0 ? void 0 : result.hint) !== null && _c !== void 0 ? _c : {},
86
+ };
87
+ }
88
+ const resultType = collection.matched.length > 0 ? 'MATCHED' : 'MATCHED_ZERO';
89
+ orderLog('Result (%s): %s', resultType, collection.toString(true));
90
+ return {
91
+ type: resultType,
92
+ matched: collection.matched,
93
+ unmatched: collection.unmatched,
94
+ zeroMatch: false,
95
+ query: (_d = result === null || result === void 0 ? void 0 : result.query) !== null && _d !== void 0 ? _d : 'N/A',
96
+ hint: (_e = result === null || result === void 0 ? void 0 : result.hint) !== null && _e !== void 0 ? _e : {},
97
+ };
98
+ }
99
+ exports.order = order;