@markuplint/rules 3.12.0 → 4.0.0-alpha.1
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.js +14 -19
- package/lib/attr-duplication/index.js +3 -6
- package/lib/attr-value-quotes/index.js +5 -9
- package/lib/case-sensitive-attr-name/index.js +9 -13
- package/lib/case-sensitive-tag-name/index.js +2 -4
- package/lib/character-reference/index.js +6 -8
- package/lib/class-naming/index.js +9 -11
- package/lib/create-message.js +9 -16
- package/lib/debug.js +2 -6
- package/lib/deprecated-attr/index.js +6 -9
- package/lib/deprecated-element/index.js +3 -5
- package/lib/disallowed-element/index.js +2 -4
- package/lib/doctype/index.js +2 -4
- package/lib/end-tag/index.js +5 -8
- package/lib/helpers.d.ts +1 -1
- package/lib/helpers.js +17 -28
- package/lib/id-duplication/index.js +2 -4
- package/lib/index.d.ts +15 -15
- package/lib/index.js +63 -66
- package/lib/ineffective-attr/index.js +5 -7
- package/lib/invalid-attr/index.js +30 -33
- package/lib/label-has-control/index.js +2 -4
- package/lib/landmark-roles/index.js +3 -6
- package/lib/no-boolean-attr-value/index.js +6 -9
- package/lib/no-default-value/index.js +9 -12
- package/lib/no-empty-palpable-content/index.js +5 -7
- package/lib/no-hard-code-id/index.js +2 -4
- package/lib/no-refer-to-non-existent-id/index.js +26 -30
- package/lib/no-use-event-handler-attr/index.js +4 -6
- package/lib/permitted-contents/choice.d.ts +1 -1
- package/lib/permitted-contents/choice.js +13 -18
- package/lib/permitted-contents/complex-branch.d.ts +1 -1
- package/lib/permitted-contents/complex-branch.js +10 -14
- package/lib/permitted-contents/content-model.d.ts +1 -1
- package/lib/permitted-contents/content-model.js +5 -9
- package/lib/permitted-contents/count-pattern.d.ts +1 -1
- package/lib/permitted-contents/count-pattern.js +19 -24
- 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 +1 -1
- package/lib/permitted-contents/index.js +10 -13
- package/lib/permitted-contents/matches-selector.d.ts +1 -1
- package/lib/permitted-contents/matches-selector.js +12 -17
- package/lib/permitted-contents/order.d.ts +1 -1
- package/lib/permitted-contents/order.js +17 -22
- package/lib/permitted-contents/recursive-branch.d.ts +2 -2
- package/lib/permitted-contents/recursive-branch.js +9 -13
- package/lib/permitted-contents/represent-transparent-nodes.d.ts +1 -1
- package/lib/permitted-contents/represent-transparent-nodes.js +15 -20
- package/lib/permitted-contents/start.d.ts +1 -1
- package/lib/permitted-contents/start.js +13 -21
- package/lib/permitted-contents/transparent.d.ts +1 -1
- package/lib/permitted-contents/transparent.js +4 -9
- package/lib/permitted-contents/types.js +1 -2
- package/lib/permitted-contents/utils.d.ts +1 -1
- package/lib/permitted-contents/utils.js +55 -74
- package/lib/placeholder-label-option/index.js +5 -9
- package/lib/require-accessible-name/index.js +10 -13
- package/lib/require-datetime/index.d.ts +1 -1
- package/lib/require-datetime/index.js +6 -8
- package/lib/require-datetime/types.js +1 -2
- package/lib/require-datetime/utils.d.ts +1 -1
- package/lib/require-datetime/utils.js +4 -11
- package/lib/required-attr/index.js +8 -11
- package/lib/required-element/index.js +3 -6
- package/lib/required-h1/index.js +3 -6
- package/lib/use-list/index.js +6 -10
- 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 +1 -1
- package/lib/wai-aria/checkings/default-value.js +2 -6
- package/lib/wai-aria/checkings/deprecated-props.d.ts +1 -1
- package/lib/wai-aria/checkings/deprecated-props.js +3 -8
- package/lib/wai-aria/checkings/disallowed-prop.d.ts +1 -1
- package/lib/wai-aria/checkings/disallowed-prop.js +7 -12
- package/lib/wai-aria/checkings/implicit-props.d.ts +1 -1
- package/lib/wai-aria/checkings/implicit-props.js +6 -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 +4 -8
- package/lib/wai-aria/checkings/no-global-prop.d.ts +1 -1
- 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 +4 -9
- package/lib/wai-aria/checkings/required-owned-elements.d.ts +1 -1
- package/lib/wai-aria/checkings/required-owned-elements.js +4 -8
- package/lib/wai-aria/checkings/required-prop.d.ts +1 -1
- package/lib/wai-aria/checkings/required-prop.js +2 -7
- package/lib/wai-aria/checkings/value.d.ts +1 -1
- package/lib/wai-aria/checkings/value.js +4 -10
- package/lib/wai-aria/index.d.ts +1 -1
- package/lib/wai-aria/index.js +38 -40
- package/lib/wai-aria/types.js +1 -2
- package/package.json +17 -12
- package/test/attr-check.spec.js +0 -77
- package/test/attr-duplication/index.spec.js +0 -159
- package/test/attr-value-quotes/index.spec.js +0 -133
- package/test/case-sensitive-attr-name/index.spec.js +0 -65
- package/test/case-sensitive-tag-name/index.spec.js +0 -80
- package/test/character-reference/index.spec.js +0 -72
- package/test/class-naming/index.spec.js +0 -470
- package/test/create-message.spec.js +0 -497
- package/test/deprecated-attr/index.spec.js +0 -48
- package/test/deprecated-element/index.spec.js +0 -35
- package/test/disallowed-element/index.spec.js +0 -90
- package/test/doctype/index.spec.js +0 -50
- package/test/end-tag/index.spec.js +0 -162
- package/test/id-duplication/index.spec.js +0 -47
- package/test/ineffective-attr/index.spec.js +0 -31
- package/test/invalid-attr/index.spec.js +0 -1641
- package/test/label-has-control/index.spec.js +0 -29
- package/test/landmark-roles/index.spec.js +0 -187
- package/test/no-boolean-attr-value/index.spec.js +0 -41
- package/test/no-default-value/index.spec.js +0 -74
- package/test/no-empty-palpable-content/index.spec.js +0 -115
- package/test/no-hard-code-id/index.spec.js +0 -100
- package/test/no-refer-to-non-existent-id/index.spec.js +0 -254
- package/test/no-use-event-handler-attr/index.spec.js +0 -57
- package/test/permitted-contents/choice.spec.js +0 -174
- package/test/permitted-contents/count-pattern.spec.js +0 -308
- package/test/permitted-contents/index.spec.js +0 -1395
- package/test/permitted-contents/matches-selector.spec.js +0 -59
- package/test/permitted-contents/order.spec.js +0 -351
- package/test/permitted-contents/recursive-branch.spec.js +0 -41
- package/test/permitted-contents/represent-transparent-nodes.spec.js +0 -24
- package/test/permitted-contents/start.spec.js +0 -67
- package/test/placeholder-label-option/index.spec.js +0 -113
- package/test/require-accessible-name/index.spec.js +0 -444
- package/test/require-datetime/index.spec.js +0 -54
- package/test/require-datetime/utils.spec.js +0 -52
- package/test/required-attr/index.spec.js +0 -414
- package/test/required-element/index.spec.js +0 -188
- package/test/required-h1/index.spec.js +0 -69
- package/test/use-list/index.spec.js +0 -109
- package/test/wai-aria/checkings/value.spec.js +0 -33
- package/test/wai-aria/index.spec.js +0 -1203
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const ml_spec_1 = require("@markuplint/ml-spec");
|
|
5
|
-
const shared_1 = require("@markuplint/shared");
|
|
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';
|
|
6
4
|
const HYPERLINK_SELECTOR = 'a[href], area[href]';
|
|
7
|
-
|
|
5
|
+
export default createRule({
|
|
8
6
|
defaultOptions: {
|
|
9
|
-
ariaVersion:
|
|
7
|
+
ariaVersion: ARIA_RECOMMENDED_VERSION,
|
|
10
8
|
fragmentRefersNameAttr: false,
|
|
11
9
|
},
|
|
12
10
|
async verify({ document, report, t }) {
|
|
@@ -27,7 +25,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
27
25
|
hasDynamicId = true;
|
|
28
26
|
}
|
|
29
27
|
if (attr.valueType !== 'code') {
|
|
30
|
-
idList.add(
|
|
28
|
+
idList.add(decodeEntities(attr.value));
|
|
31
29
|
}
|
|
32
30
|
});
|
|
33
31
|
if (hasDynamicId) {
|
|
@@ -42,12 +40,11 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
42
40
|
hasDynamicName = true;
|
|
43
41
|
}
|
|
44
42
|
if (attr.valueType !== 'code') {
|
|
45
|
-
nameList.add(
|
|
43
|
+
nameList.add(decodeEntities(attr.value));
|
|
46
44
|
}
|
|
47
45
|
});
|
|
48
46
|
await document.walkOn('Attr', attr => {
|
|
49
|
-
|
|
50
|
-
const attrSpec = (0, ml_core_1.getAttrSpecs)(attr.ownerElement, document.specs);
|
|
47
|
+
const attrSpec = getAttrSpecs(attr.ownerElement, document.specs);
|
|
51
48
|
if (!attrSpec) {
|
|
52
49
|
return;
|
|
53
50
|
}
|
|
@@ -68,9 +65,9 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
68
65
|
if (spec.type === 'DOMID' && !idList.has(value)) {
|
|
69
66
|
report({
|
|
70
67
|
scope: attr,
|
|
71
|
-
line:
|
|
72
|
-
col:
|
|
73
|
-
raw:
|
|
68
|
+
line: attr.valueNode?.startLine,
|
|
69
|
+
col: attr.valueNode?.startCol,
|
|
70
|
+
raw: attr.valueNode?.raw,
|
|
74
71
|
message: t('Missing {0}', t('"{0*}" ID', value)),
|
|
75
72
|
});
|
|
76
73
|
}
|
|
@@ -83,24 +80,24 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
83
80
|
if (!idList.has(ref)) {
|
|
84
81
|
report({
|
|
85
82
|
scope: attr,
|
|
86
|
-
line:
|
|
87
|
-
col:
|
|
88
|
-
raw:
|
|
83
|
+
line: attr.valueNode?.startLine,
|
|
84
|
+
col: attr.valueNode?.startCol,
|
|
85
|
+
raw: attr.valueNode?.raw,
|
|
89
86
|
message: t('Missing {0}', t('"{0*}" ID', ref)),
|
|
90
87
|
});
|
|
91
88
|
}
|
|
92
89
|
}
|
|
93
90
|
}
|
|
94
91
|
}
|
|
95
|
-
const { props } =
|
|
92
|
+
const { props } = ariaSpecs(document.specs, attr.rule.options.ariaVersion);
|
|
96
93
|
const aria = props.find(prop => prop.name === name);
|
|
97
94
|
if (aria) {
|
|
98
95
|
if (aria.value === 'ID reference' && !idList.has(value)) {
|
|
99
96
|
report({
|
|
100
97
|
scope: attr,
|
|
101
|
-
line:
|
|
102
|
-
col:
|
|
103
|
-
raw:
|
|
98
|
+
line: attr.valueNode?.startLine,
|
|
99
|
+
col: attr.valueNode?.startCol,
|
|
100
|
+
raw: attr.valueNode?.raw,
|
|
104
101
|
message: t('Missing {0}', t('"{0*}" ID', value)),
|
|
105
102
|
});
|
|
106
103
|
}
|
|
@@ -113,9 +110,9 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
113
110
|
if (!idList.has(ref)) {
|
|
114
111
|
report({
|
|
115
112
|
scope: attr,
|
|
116
|
-
line:
|
|
117
|
-
col:
|
|
118
|
-
raw:
|
|
113
|
+
line: attr.valueNode?.startLine,
|
|
114
|
+
col: attr.valueNode?.startCol,
|
|
115
|
+
raw: attr.valueNode?.raw,
|
|
119
116
|
message: t('Missing {0}', t('"{0*}" ID', ref)),
|
|
120
117
|
});
|
|
121
118
|
}
|
|
@@ -127,7 +124,6 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
127
124
|
* @see https://html.spec.whatwg.org/multipage/browsing-the-web.html#scrolling-to-a-fragment
|
|
128
125
|
*/
|
|
129
126
|
await document.walkOn('Element', el => {
|
|
130
|
-
var _a, _b, _c, _d;
|
|
131
127
|
if (el.rule.options.fragmentRefersNameAttr && hasDynamicName) {
|
|
132
128
|
return;
|
|
133
129
|
}
|
|
@@ -138,11 +134,11 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
138
134
|
if (!href) {
|
|
139
135
|
return;
|
|
140
136
|
}
|
|
141
|
-
const rawFragment =
|
|
137
|
+
const rawFragment = href.value.match(/^#(.+)/)?.[1];
|
|
142
138
|
if (rawFragment == null) {
|
|
143
139
|
return;
|
|
144
140
|
}
|
|
145
|
-
const decodedFragment =
|
|
141
|
+
const decodedFragment = decodeHref(rawFragment);
|
|
146
142
|
// > 2. If fragment is the empty string, then return the special value top of the document.
|
|
147
143
|
// >
|
|
148
144
|
// > 9. If decodedFragment is an ASCII case-insensitive match for the string top, then return the top of the document.
|
|
@@ -153,9 +149,9 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
153
149
|
(el.rule.options.fragmentRefersNameAttr ? !nameList.has(decodedFragment) : true)) {
|
|
154
150
|
report({
|
|
155
151
|
scope: href,
|
|
156
|
-
line:
|
|
157
|
-
col:
|
|
158
|
-
raw:
|
|
152
|
+
line: href.valueNode?.startLine,
|
|
153
|
+
col: href.valueNode?.startCol,
|
|
154
|
+
raw: href.valueNode?.raw,
|
|
159
155
|
message: t('Missing {0}', t('"{0*}" ID', decodedFragment)),
|
|
160
156
|
});
|
|
161
157
|
}
|
|
@@ -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,4 +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
4
|
export declare function choice(pattern: ReadonlyDeep<PermittedContentChoice>, elements: readonly ChildNode[], specs: Specs, options: Options, depth: number): Result;
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const debug_1 = require("./debug");
|
|
5
|
-
const order_1 = require("./order");
|
|
6
|
-
const utils_1 = require("./utils");
|
|
1
|
+
import { bgBlue, bgGreen, cmLog } from './debug.js';
|
|
2
|
+
import { order } from './order.js';
|
|
3
|
+
import { Collection, modelLog } from './utils.js';
|
|
7
4
|
const indexes = new WeakMap();
|
|
8
|
-
function choice(pattern,
|
|
5
|
+
export function choice(pattern,
|
|
9
6
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
10
7
|
elements, specs, options, depth) {
|
|
11
|
-
const choiceLog =
|
|
12
|
-
const collection = new
|
|
8
|
+
const choiceLog = cmLog.extend(`choice#${depth}`);
|
|
9
|
+
const collection = new Collection(elements);
|
|
13
10
|
const unmatchedResults = [];
|
|
14
11
|
let i = 0;
|
|
15
12
|
for (const some of pattern.choice) {
|
|
16
|
-
choiceLog('Patterns[%s]: %s', i,
|
|
17
|
-
const result =
|
|
13
|
+
choiceLog('Patterns[%s]: %s', i, modelLog(some, ''));
|
|
14
|
+
const result = order(some, collection.unmatched, specs, options, depth + 1);
|
|
18
15
|
if (result.type === 'UNEXPECTED_EXTRA_NODE' || result.type === 'MATCHED' || result.type === 'MATCHED_ZERO') {
|
|
19
16
|
choiceLog('Results[%s]: %s', i, choiceLogString(pattern.choice, i));
|
|
20
17
|
collection.addMatched(result.matched);
|
|
@@ -32,13 +29,12 @@ elements, specs, options, depth) {
|
|
|
32
29
|
i++;
|
|
33
30
|
}
|
|
34
31
|
const barelyMatchedResult = unmatchedResults.sort((a, b) => {
|
|
35
|
-
var _c, _d, _e, _f;
|
|
36
32
|
const computed1 = b.matched.length - a.matched.length;
|
|
37
33
|
if (computed1 !== 0) {
|
|
38
34
|
return computed1;
|
|
39
35
|
}
|
|
40
|
-
const _a =
|
|
41
|
-
const _b =
|
|
36
|
+
const _a = a.hint.missing?.barelyMatchedElements ?? 0;
|
|
37
|
+
const _b = b.hint.missing?.barelyMatchedElements ?? 0;
|
|
42
38
|
const computed2 = _b - _a;
|
|
43
39
|
return computed2;
|
|
44
40
|
})[0];
|
|
@@ -58,15 +54,14 @@ elements, specs, options, depth) {
|
|
|
58
54
|
hint: barelyMatchedResult.hint,
|
|
59
55
|
};
|
|
60
56
|
}
|
|
61
|
-
exports.choice = choice;
|
|
62
57
|
function choiceLogString(choice, index, barely = false) {
|
|
63
|
-
const colorFn = barely ?
|
|
58
|
+
const colorFn = barely ? bgBlue : bgGreen;
|
|
64
59
|
return choice
|
|
65
60
|
.map((pattern, i) => {
|
|
66
61
|
if (i === index) {
|
|
67
|
-
return colorFn(
|
|
62
|
+
return colorFn(modelLog(pattern, ''));
|
|
68
63
|
}
|
|
69
|
-
return
|
|
64
|
+
return modelLog(pattern, '');
|
|
70
65
|
})
|
|
71
66
|
.join(', ');
|
|
72
67
|
}
|
|
@@ -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,15 +12,14 @@ const utils_1 = require("./utils");
|
|
|
15
12
|
* @param depth
|
|
16
13
|
* @returns
|
|
17
14
|
*/
|
|
18
|
-
function complexBranch(pattern,
|
|
15
|
+
export function complexBranch(pattern,
|
|
19
16
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
20
17
|
elements, specs, options, depth) {
|
|
21
|
-
if (
|
|
22
|
-
return
|
|
18
|
+
if (isChoice(pattern)) {
|
|
19
|
+
return choice(pattern, elements, specs, options, depth);
|
|
23
20
|
}
|
|
24
|
-
if (
|
|
25
|
-
return
|
|
21
|
+
if (isTransparent(pattern)) {
|
|
22
|
+
return transparent(elements);
|
|
26
23
|
}
|
|
27
|
-
return
|
|
24
|
+
return countPattern(pattern, elements, specs, options, depth);
|
|
28
25
|
}
|
|
29
|
-
exports.complexBranch = complexBranch;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { ContentModelResult, Element, Options, TagRule } from './types';
|
|
1
|
+
import type { ContentModelResult, Element, Options, TagRule } from './types.js';
|
|
2
2
|
export declare function contentModel(el: Element, rules: readonly TagRule[], options: Options): ContentModelResult[];
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const ml_spec_1 = require("@markuplint/ml-spec");
|
|
5
|
-
const start_1 = require("./start");
|
|
6
|
-
function contentModel(
|
|
1
|
+
import { getContentModel } from '@markuplint/ml-spec';
|
|
2
|
+
import { start } from './start.js';
|
|
3
|
+
export function contentModel(
|
|
7
4
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
8
5
|
el, rules, options) {
|
|
9
6
|
const model = createModel(el, rules);
|
|
@@ -17,15 +14,14 @@ el, rules, options) {
|
|
|
17
14
|
},
|
|
18
15
|
];
|
|
19
16
|
}
|
|
20
|
-
const result =
|
|
17
|
+
const result = start(model, el, el.ownerMLDocument.specs, options);
|
|
21
18
|
return result;
|
|
22
19
|
}
|
|
23
|
-
exports.contentModel = contentModel;
|
|
24
20
|
function createModel(
|
|
25
21
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
26
22
|
el, rules) {
|
|
27
23
|
const specs = cachedSpecs(el.ownerMLDocument.specs, rules);
|
|
28
|
-
const model =
|
|
24
|
+
const model = getContentModel(el, specs.specs);
|
|
29
25
|
return model;
|
|
30
26
|
}
|
|
31
27
|
const caches = new Map();
|
|
@@ -1,4 +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 { PermittedContentOneOrMore, PermittedContentOptional, PermittedContentRequire, PermittedContentZeroOrMore } from '@markuplint/ml-spec';
|
|
3
3
|
import type { ReadonlyDeep } from 'type-fest';
|
|
4
4
|
/**
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
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';
|
|
7
4
|
/**
|
|
8
5
|
* Check count
|
|
9
6
|
*
|
|
@@ -14,13 +11,13 @@ const utils_1 = require("./utils");
|
|
|
14
11
|
* @param depth
|
|
15
12
|
* @returns
|
|
16
13
|
*/
|
|
17
|
-
function countPattern(pattern,
|
|
14
|
+
export function countPattern(pattern,
|
|
18
15
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
19
16
|
elements, specs, options, depth) {
|
|
20
|
-
const ptLog =
|
|
21
|
-
const collection = new
|
|
22
|
-
const { model, min, max, repeat, missingType } =
|
|
23
|
-
ptLog('Model:\n RegEx: %s\n Schema: %o',
|
|
17
|
+
const ptLog = cmLog.extend(`countPattern#${depth}`);
|
|
18
|
+
const collection = new Collection(elements);
|
|
19
|
+
const { model, min, max, repeat, missingType } = normalizeModel(pattern);
|
|
20
|
+
ptLog('Model:\n RegEx: %s\n Schema: %o', modelLog(model, repeat), pattern);
|
|
24
21
|
let prevResult = null;
|
|
25
22
|
let barelyResult = null;
|
|
26
23
|
let loopCount = 0;
|
|
@@ -28,11 +25,11 @@ elements, specs, options, depth) {
|
|
|
28
25
|
while (true) {
|
|
29
26
|
loopCount++;
|
|
30
27
|
ptLog('Check#%s: %s', loopCount, collection);
|
|
31
|
-
const result =
|
|
28
|
+
const result = recursiveBranch(model, collection.unmatched, specs, options, depth);
|
|
32
29
|
const added = collection.addMatched(result.matched);
|
|
33
30
|
const { matchedCount } = collection;
|
|
34
31
|
if (result.type === 'UNMATCHED_SELECTOR_BUT_MAY_EMPTY') {
|
|
35
|
-
ptLog('MATCHED_ZERO:\n model: %s\n max: %s\n collection: %s\n matched element: %s',
|
|
32
|
+
ptLog('MATCHED_ZERO:\n model: %s\n max: %s\n collection: %s\n matched element: %s', modelLog(model, repeat), max, collection, matchedCount);
|
|
36
33
|
return compereResult({
|
|
37
34
|
type: 'MATCHED_ZERO',
|
|
38
35
|
matched: collection.matched,
|
|
@@ -44,14 +41,14 @@ elements, specs, options, depth) {
|
|
|
44
41
|
}
|
|
45
42
|
if (max < collection.matchedCount) {
|
|
46
43
|
collection.max(max);
|
|
47
|
-
ptLog('UNEXPECTED_EXTRA_NODE:\n model: %s\n max: %s\n collection: %s\n matched element: %s',
|
|
44
|
+
ptLog('UNEXPECTED_EXTRA_NODE:\n model: %s\n max: %s\n collection: %s\n matched element: %s', modelLog(model, repeat), max, collection, matchedCount);
|
|
48
45
|
return compereResult({
|
|
49
46
|
type: 'UNEXPECTED_EXTRA_NODE',
|
|
50
47
|
matched: collection.matched,
|
|
51
48
|
unmatched: collection.unmatched,
|
|
52
49
|
zeroMatch: result.zeroMatch,
|
|
53
50
|
query: result.query,
|
|
54
|
-
hint:
|
|
51
|
+
hint: mergeHints(result.hint, { max }),
|
|
55
52
|
}, barelyResult);
|
|
56
53
|
}
|
|
57
54
|
if (prevResult) {
|
|
@@ -90,7 +87,7 @@ elements, specs, options, depth) {
|
|
|
90
87
|
result.type === 'MISSING_NODE_ONE_OR_MORE' ||
|
|
91
88
|
result.type === 'TRANSPARENT_MODEL_DISALLOWS'
|
|
92
89
|
? result.type
|
|
93
|
-
: missingType
|
|
90
|
+
: missingType ?? 'MISSING_NODE_REQUIRED';
|
|
94
91
|
ptLog('%s(in %s); Needs %s', resultType, missingType, result.query);
|
|
95
92
|
return compereResult({
|
|
96
93
|
type: resultType,
|
|
@@ -98,7 +95,7 @@ elements, specs, options, depth) {
|
|
|
98
95
|
unmatched: collection.unmatched,
|
|
99
96
|
zeroMatch: result.zeroMatch,
|
|
100
97
|
query: result.query,
|
|
101
|
-
hint:
|
|
98
|
+
hint: mergeHints(result.hint, {
|
|
102
99
|
missing: {
|
|
103
100
|
barelyMatchedElements: collection.matched.length,
|
|
104
101
|
need: result.query,
|
|
@@ -114,7 +111,7 @@ elements, specs, options, depth) {
|
|
|
114
111
|
unmatched: collection.unmatched,
|
|
115
112
|
zeroMatch,
|
|
116
113
|
query: result.query,
|
|
117
|
-
hint:
|
|
114
|
+
hint: mergeHints(result.hint, {
|
|
118
115
|
missing: {
|
|
119
116
|
barelyMatchedElements: collection.matched.length,
|
|
120
117
|
need: result.query,
|
|
@@ -126,7 +123,7 @@ elements, specs, options, depth) {
|
|
|
126
123
|
ptLog('continue⤴️ (add prev)');
|
|
127
124
|
continue;
|
|
128
125
|
}
|
|
129
|
-
ptLog('%s:\n model: %s\n max: %s\n collection: %s\n matched element: %s', resultType,
|
|
126
|
+
ptLog('%s:\n model: %s\n max: %s\n collection: %s\n matched element: %s', resultType, modelLog(model, repeat), max, collection, matchedCount);
|
|
130
127
|
if (result.type === 'MISSING_NODE_REQUIRED' ||
|
|
131
128
|
result.type === 'MISSING_NODE_ONE_OR_MORE' ||
|
|
132
129
|
result.type === 'TRANSPARENT_MODEL_DISALLOWS') {
|
|
@@ -142,21 +139,19 @@ elements, specs, options, depth) {
|
|
|
142
139
|
return compereResult(matchedResult, barelyResult);
|
|
143
140
|
}
|
|
144
141
|
}
|
|
145
|
-
|
|
146
|
-
const cLog = debug_1.cmLog.extend('countCompereResult');
|
|
142
|
+
const cLog = cmLog.extend('countCompereResult');
|
|
147
143
|
function compereResult(
|
|
148
144
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
149
145
|
a,
|
|
150
146
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
151
147
|
b) {
|
|
152
|
-
|
|
153
|
-
cLog('current: %s %O\nbarely: %s %O', a.type, a.hint, b === null || b === void 0 ? void 0 : b.type, b === null || b === void 0 ? void 0 : b.hint);
|
|
148
|
+
cLog('current: %s %O\nbarely: %s %O', a.type, a.hint, b?.type, b?.hint);
|
|
154
149
|
if (b == null) {
|
|
155
150
|
return a;
|
|
156
151
|
}
|
|
157
152
|
if (a.type === 'MATCHED' || a.type === 'MATCHED_ZERO' || a.type === 'UNEXPECTED_EXTRA_NODE') {
|
|
158
153
|
return a;
|
|
159
154
|
}
|
|
160
|
-
const result =
|
|
155
|
+
const result = [a, b].sort((a, b) => (b.hint.missing?.barelyMatchedElements ?? 0) - (a.hint.missing?.barelyMatchedElements ?? 0))[0] ?? a;
|
|
161
156
|
return result;
|
|
162
157
|
}
|
|
@@ -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;
|
|
@@ -1,3 +1,10 @@
|
|
|
1
1
|
/// <reference types="debug" />
|
|
2
|
+
import color from 'ansi-colors';
|
|
2
3
|
export declare const cmLog: import("debug").Debugger;
|
|
3
|
-
export
|
|
4
|
+
export declare const bgGreen: color.StyleFunction;
|
|
5
|
+
export declare const green: color.StyleFunction;
|
|
6
|
+
export declare const bgRed: color.StyleFunction;
|
|
7
|
+
export declare const bgBlue: color.StyleFunction;
|
|
8
|
+
export declare const blue: color.StyleFunction;
|
|
9
|
+
export declare const bgMagenta: color.StyleFunction;
|
|
10
|
+
export declare const cyan: color.StyleFunction;
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Object.defineProperty(exports, "cyan", { enumerable: true, get: function () { return ansi_colors_1.cyan; } });
|
|
1
|
+
import color from 'ansi-colors';
|
|
2
|
+
import { log } from '../debug.js';
|
|
3
|
+
export const cmLog = log.extend('content-model');
|
|
4
|
+
/* eslint-disable import/no-named-as-default-member */
|
|
5
|
+
export const bgGreen = color.bgGreen;
|
|
6
|
+
export const green = color.green;
|
|
7
|
+
export const bgRed = color.bgRed;
|
|
8
|
+
export const bgBlue = color.bgBlue;
|
|
9
|
+
export const blue = color.blue;
|
|
10
|
+
export const bgMagenta = color.bgMagenta;
|
|
11
|
+
export const cyan = color.cyan;
|
|
12
|
+
/* eslint-enable import/no-named-as-default-member */
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const represent_transparent_nodes_1 = require("./represent-transparent-nodes");
|
|
6
|
-
exports.default = (0, ml_core_1.createRule)({
|
|
1
|
+
import { createRule } from '@markuplint/ml-core';
|
|
2
|
+
import { contentModel } from './content-model.js';
|
|
3
|
+
import { transparentMode } from './represent-transparent-nodes.js';
|
|
4
|
+
export default createRule({
|
|
7
5
|
defaultValue: [],
|
|
8
6
|
defaultOptions: {
|
|
9
7
|
ignoreHasMutableChildren: true,
|
|
10
8
|
},
|
|
11
9
|
async verify({ document, report, t }) {
|
|
12
10
|
await document.walkOn('Element', el => {
|
|
13
|
-
var _a, _b, _c;
|
|
14
11
|
if (el.rule.options.ignoreHasMutableChildren && el.hasMutableChildren()) {
|
|
15
12
|
return;
|
|
16
13
|
}
|
|
17
|
-
const results =
|
|
14
|
+
const results = contentModel(el, el.rule.value, el.rule.options);
|
|
18
15
|
for (const { type, scope, query, hint } of results) {
|
|
19
16
|
let message = '';
|
|
20
17
|
if (hint.max != null) {
|
|
@@ -49,10 +46,10 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
49
46
|
break;
|
|
50
47
|
}
|
|
51
48
|
case 'UNEXPECTED_EXTRA_NODE': {
|
|
52
|
-
const not =
|
|
49
|
+
const not = hint.not ?? scope;
|
|
53
50
|
message =
|
|
54
51
|
message ||
|
|
55
|
-
(
|
|
52
|
+
(transparentMode.has(scope)
|
|
56
53
|
? t('{0} is not allowed in {1} through the transparent model in this context', name(not, t), name(el, t))
|
|
57
54
|
: t('{0} is not allowed in {1} in this context', name(not, t), name(el, t)));
|
|
58
55
|
report({
|
|
@@ -62,8 +59,8 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
62
59
|
break;
|
|
63
60
|
}
|
|
64
61
|
case 'TRANSPARENT_MODEL_DISALLOWS': {
|
|
65
|
-
const not =
|
|
66
|
-
const tp =
|
|
62
|
+
const not = hint.not ?? scope;
|
|
63
|
+
const tp = hint.transparent ?? el;
|
|
67
64
|
report({
|
|
68
65
|
scope: not,
|
|
69
66
|
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))),
|
|
@@ -82,7 +79,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
82
79
|
}
|
|
83
80
|
}
|
|
84
81
|
}
|
|
85
|
-
|
|
82
|
+
transparentMode.clear();
|
|
86
83
|
});
|
|
87
84
|
},
|
|
88
85
|
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { ChildNode, Result, Specs } from './types';
|
|
1
|
+
import type { ChildNode, Result, Specs } from './types.js';
|
|
2
2
|
export type SelectorResult = Result<'UNMATCHED_SELECTOR_BUT_MAY_EMPTY' | 'MISSING_NODE' | 'UNMATCHED_SELECTORS'>;
|
|
3
3
|
export declare function matchesSelector(query: string, node: ChildNode | undefined, specs: Specs, depth: number): SelectorResult;
|