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