@markuplint/rules 3.0.0-dev.24 → 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.
- package/lib/attr-check.d.ts +3 -2
- package/lib/attr-check.js +42 -38
- package/lib/attr-duplication/index.d.ts +1 -1
- package/lib/attr-duplication/index.js +3 -5
- package/lib/attr-value-quotes/index.d.ts +1 -1
- package/lib/attr-value-quotes/index.js +5 -9
- package/lib/case-sensitive-attr-name/index.d.ts +1 -1
- package/lib/case-sensitive-attr-name/index.js +9 -13
- package/lib/case-sensitive-tag-name/index.d.ts +1 -1
- package/lib/case-sensitive-tag-name/index.js +2 -4
- package/lib/character-reference/index.d.ts +1 -1
- package/lib/character-reference/index.js +7 -9
- package/lib/class-naming/index.d.ts +1 -1
- package/lib/class-naming/index.js +9 -13
- package/lib/create-message.d.ts +3 -2
- package/lib/create-message.js +15 -17
- package/lib/debug.js +2 -6
- package/lib/deprecated-attr/index.d.ts +1 -1
- package/lib/deprecated-attr/index.js +6 -9
- package/lib/deprecated-element/index.d.ts +1 -1
- package/lib/deprecated-element/index.js +5 -7
- package/lib/disallowed-element/index.d.ts +1 -1
- package/lib/disallowed-element/index.js +2 -4
- package/lib/doctype/index.d.ts +1 -1
- package/lib/doctype/index.js +2 -4
- package/lib/end-tag/index.d.ts +1 -1
- package/lib/end-tag/index.js +5 -8
- package/lib/helpers.d.ts +10 -13
- package/lib/helpers.js +39 -37
- package/lib/id-duplication/index.d.ts +1 -1
- package/lib/id-duplication/index.js +2 -4
- package/lib/index.d.ts +102 -3
- package/lib/index.js +64 -62
- package/lib/ineffective-attr/index.d.ts +1 -1
- package/lib/ineffective-attr/index.js +5 -10
- package/lib/invalid-attr/index.d.ts +25 -5
- package/lib/invalid-attr/index.js +166 -43
- package/lib/label-has-control/index.d.ts +1 -1
- package/lib/label-has-control/index.js +2 -4
- package/lib/landmark-roles/index.d.ts +1 -1
- package/lib/landmark-roles/index.js +10 -9
- package/lib/no-boolean-attr-value/index.d.ts +1 -1
- package/lib/no-boolean-attr-value/index.js +6 -9
- package/lib/no-default-value/index.d.ts +1 -1
- package/lib/no-default-value/index.js +9 -12
- package/lib/no-empty-palpable-content/index.d.ts +1 -1
- package/lib/no-empty-palpable-content/index.js +13 -7
- package/lib/no-hard-code-id/index.d.ts +1 -1
- package/lib/no-hard-code-id/index.js +2 -4
- package/lib/no-refer-to-non-existent-id/index.d.ts +4 -4
- package/lib/no-refer-to-non-existent-id/index.js +79 -23
- package/lib/no-use-event-handler-attr/index.d.ts +1 -1
- package/lib/no-use-event-handler-attr/index.js +4 -6
- package/lib/permitted-contents/choice.d.ts +3 -2
- package/lib/permitted-contents/choice.js +54 -14
- package/lib/permitted-contents/complex-branch.d.ts +3 -2
- package/lib/permitted-contents/complex-branch.js +12 -14
- package/lib/permitted-contents/content-model.d.ts +2 -2
- package/lib/permitted-contents/content-model.js +11 -11
- package/lib/permitted-contents/count-pattern.d.ts +3 -2
- package/lib/permitted-contents/count-pattern.js +76 -74
- package/lib/permitted-contents/debug.browser.js +9 -12
- package/lib/permitted-contents/debug.d.ts +8 -1
- package/lib/permitted-contents/debug.js +12 -13
- package/lib/permitted-contents/index.d.ts +2 -2
- package/lib/permitted-contents/index.js +18 -24
- package/lib/permitted-contents/matches-selector.d.ts +2 -2
- package/lib/permitted-contents/matches-selector.js +17 -20
- package/lib/permitted-contents/order.d.ts +3 -2
- package/lib/permitted-contents/order.js +33 -28
- package/lib/permitted-contents/recursive-branch.d.ts +4 -3
- package/lib/permitted-contents/recursive-branch.js +11 -13
- package/lib/permitted-contents/represent-transparent-nodes.d.ts +1 -1
- package/lib/permitted-contents/represent-transparent-nodes.js +18 -21
- package/lib/permitted-contents/start.d.ts +3 -2
- package/lib/permitted-contents/start.js +16 -22
- package/lib/permitted-contents/transparent.d.ts +2 -2
- package/lib/permitted-contents/transparent.js +6 -9
- package/lib/permitted-contents/types.d.ts +20 -13
- package/lib/permitted-contents/types.js +1 -2
- package/lib/permitted-contents/utils.d.ts +27 -8
- package/lib/permitted-contents/utils.js +164 -65
- package/lib/placeholder-label-option/index.d.ts +2 -0
- package/lib/placeholder-label-option/index.js +84 -0
- package/lib/require-accessible-name/index.d.ts +1 -1
- package/lib/require-accessible-name/index.js +10 -13
- package/lib/require-datetime/index.d.ts +6 -0
- package/lib/require-datetime/index.js +32 -0
- package/lib/require-datetime/types.d.ts +10 -0
- package/lib/require-datetime/types.js +1 -0
- package/lib/require-datetime/utils.d.ts +11 -0
- package/lib/require-datetime/utils.js +130 -0
- package/lib/required-attr/index.d.ts +2 -2
- package/lib/required-attr/index.js +24 -24
- package/lib/required-element/index.d.ts +1 -1
- package/lib/required-element/index.js +3 -5
- package/lib/required-h1/index.d.ts +1 -1
- package/lib/required-h1/index.js +4 -6
- package/lib/use-list/index.d.ts +2 -2
- package/lib/use-list/index.js +7 -9
- package/lib/wai-aria/checkings/abstract-role.d.ts +1 -1
- package/lib/wai-aria/checkings/abstract-role.js +5 -10
- package/lib/wai-aria/checkings/default-value.d.ts +2 -2
- package/lib/wai-aria/checkings/default-value.js +2 -6
- package/lib/wai-aria/checkings/deprecated-props.d.ts +2 -2
- package/lib/wai-aria/checkings/deprecated-props.js +3 -8
- package/lib/wai-aria/checkings/disallowed-prop.d.ts +2 -2
- package/lib/wai-aria/checkings/disallowed-prop.js +27 -8
- package/lib/wai-aria/checkings/implicit-props.d.ts +3 -3
- package/lib/wai-aria/checkings/implicit-props.js +7 -11
- package/lib/wai-aria/checkings/implicit-role.d.ts +1 -1
- package/lib/wai-aria/checkings/implicit-role.js +4 -9
- package/lib/wai-aria/checkings/interaction-in-hidden.d.ts +1 -1
- package/lib/wai-aria/checkings/interaction-in-hidden.js +7 -9
- package/lib/wai-aria/checkings/no-global-prop.d.ts +2 -2
- package/lib/wai-aria/checkings/no-global-prop.js +2 -7
- package/lib/wai-aria/checkings/non-existent-role.d.ts +1 -1
- package/lib/wai-aria/checkings/non-existent-role.js +4 -9
- package/lib/wai-aria/checkings/permitted-roles.d.ts +1 -1
- package/lib/wai-aria/checkings/permitted-roles.js +4 -9
- package/lib/wai-aria/checkings/presentational-children.d.ts +1 -1
- package/lib/wai-aria/checkings/presentational-children.js +7 -10
- package/lib/wai-aria/checkings/required-owned-elements.d.ts +1 -1
- package/lib/wai-aria/checkings/required-owned-elements.js +10 -12
- package/lib/wai-aria/checkings/required-prop.d.ts +2 -2
- package/lib/wai-aria/checkings/required-prop.js +2 -7
- package/lib/wai-aria/checkings/value.d.ts +3 -3
- package/lib/wai-aria/checkings/value.js +7 -11
- package/lib/wai-aria/index.d.ts +2 -2
- package/lib/wai-aria/index.js +38 -39
- package/lib/wai-aria/types.js +1 -2
- package/package.json +21 -14
- package/schema.json +6 -0
- package/tsconfig.test.json +0 -3
- package/tsconfig.tsbuildinfo +0 -1
|
@@ -1,30 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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:
|
|
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
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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:
|
|
49
|
-
col:
|
|
50
|
-
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:
|
|
64
|
-
col:
|
|
65
|
-
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 } =
|
|
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:
|
|
79
|
-
col:
|
|
80
|
-
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:
|
|
94
|
-
col:
|
|
95
|
-
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,8 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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:
|
|
16
|
-
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) =>
|
|
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
|
-
|
|
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
|
|
14
|
+
export declare function complexBranch(pattern: ReadonlyDeep<PermittedContentPattern>, elements: readonly ChildNode[], specs: Specs, options: Options, depth: number): Result;
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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,
|
|
19
|
-
|
|
20
|
-
|
|
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 (
|
|
23
|
-
return
|
|
21
|
+
if (isTransparent(pattern)) {
|
|
22
|
+
return transparent(elements);
|
|
24
23
|
}
|
|
25
|
-
return
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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 =
|
|
17
|
+
const result = start(model, el, el.ownerMLDocument.specs, options);
|
|
19
18
|
return result;
|
|
20
19
|
}
|
|
21
|
-
|
|
22
|
-
|
|
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 =
|
|
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 (
|
|
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
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const
|
|
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,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
-
|
|
60
|
-
|
|
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',
|
|
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
|
|
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
|
-
|
|
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
|
-
:
|
|
114
|
-
ptLog('%s(in %s)
|
|
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 (
|
|
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
|
-
|
|
2
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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;
|