@markuplint/rules 2.4.2 → 3.0.0-alpha.0
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 -5
- package/lib/attr-duplication/index.js +10 -7
- package/lib/attr-equal-space-after/index.d.ts +1 -1
- package/lib/attr-equal-space-before/index.d.ts +1 -1
- package/lib/attr-spacing/index.d.ts +3 -3
- package/lib/attr-value-quotes/index.js +21 -25
- package/lib/case-sensitive-attr-name/index.d.ts +1 -5
- package/lib/case-sensitive-attr-name/index.js +51 -81
- package/lib/case-sensitive-tag-name/index.js +38 -30
- package/lib/character-reference/index.d.ts +2 -1
- package/lib/character-reference/index.js +6 -8
- package/lib/class-naming/index.js +11 -11
- package/lib/deprecated-attr/index.js +17 -18
- package/lib/deprecated-element/index.js +5 -7
- package/lib/disallowed-element/index.js +8 -9
- package/lib/doctype/index.js +7 -7
- package/lib/end-tag/index.js +4 -1
- package/lib/helper/aria/get-aria.d.ts +7 -0
- package/lib/helper/aria/get-aria.js +53 -0
- package/lib/helper/aria/get-computed-role.d.ts +10 -0
- package/lib/helper/aria/get-computed-role.js +24 -0
- package/lib/helper/aria/get-implicit-role.d.ts +7 -0
- package/lib/helper/aria/get-implicit-role.js +12 -0
- package/lib/helper/aria/get-permitted-roles.d.ts +14 -0
- package/lib/helper/aria/get-permitted-roles.js +26 -0
- package/lib/helper/aria/resolve-version.d.ts +2 -0
- package/lib/helper/aria/resolve-version.js +21 -0
- package/lib/helper/spec/aria-spec.d.ts +5 -0
- package/lib/helper/spec/aria-spec.js +12 -0
- package/lib/helper/spec/get-role-spec.d.ts +11 -0
- package/lib/helper/spec/get-role-spec.js +39 -0
- package/lib/helper/spec/html-spec.d.ts +2 -0
- package/lib/helper/spec/html-spec.js +14 -0
- package/lib/helpers.d.ts +10 -28
- package/lib/helpers.js +29 -149
- package/lib/id-duplication/index.js +4 -6
- package/lib/indentation/index.d.ts +3 -3
- package/lib/index.d.ts +14 -19
- package/lib/index.js +0 -8
- package/lib/ineffective-attr/index.js +19 -27
- package/lib/invalid-attr/index.js +83 -83
- package/lib/landmark-roles/index.d.ts +2 -2
- package/lib/landmark-roles/index.js +9 -9
- package/lib/no-boolean-attr-value/index.js +33 -34
- package/lib/no-default-value/index.js +20 -24
- package/lib/no-hard-code-id/index.js +9 -13
- package/lib/no-refer-to-non-existent-id/index.d.ts +4 -1
- package/lib/no-refer-to-non-existent-id/index.js +77 -83
- package/lib/no-use-event-handler-attr/index.js +20 -22
- package/lib/permitted-contents/index.d.ts +5 -2
- package/lib/permitted-contents/index.js +67 -68
- package/lib/permitted-contents/permitted-content.spec-to-regexp.d.ts +5 -5
- package/lib/permitted-contents/permitted-content.spec-to-regexp.js +19 -14
- package/lib/permitted-contents/unfold-content-models-to-tags.d.ts +2 -2
- package/lib/require-accessible-name/index.d.ts +5 -1
- package/lib/require-accessible-name/index.js +6 -3
- package/lib/required-attr/index.js +16 -25
- package/lib/required-element/index.d.ts +1 -1
- package/lib/required-element/index.js +6 -6
- package/lib/required-h1/index.d.ts +2 -2
- package/lib/required-h1/index.js +3 -3
- package/lib/wai-aria/checkings/abstract-role.d.ts +3 -0
- package/lib/wai-aria/checkings/abstract-role.js +22 -0
- package/lib/wai-aria/checkings/checking-required-owned-elements.d.ts +11 -0
- package/lib/wai-aria/checkings/checking-required-owned-elements.js +63 -0
- package/lib/wai-aria/checkings/default-value.d.ts +6 -0
- package/lib/wai-aria/checkings/default-value.js +17 -0
- package/lib/wai-aria/checkings/deprecated-props.d.ts +7 -0
- package/lib/wai-aria/checkings/deprecated-props.js +18 -0
- package/lib/wai-aria/checkings/disallowed-prop.d.ts +7 -0
- package/lib/wai-aria/checkings/disallowed-prop.js +22 -0
- package/lib/wai-aria/checkings/implicit-props.d.ts +7 -0
- package/lib/wai-aria/checkings/implicit-props.js +50 -0
- package/lib/wai-aria/checkings/implicit-role.d.ts +3 -0
- package/lib/wai-aria/checkings/implicit-role.js +21 -0
- package/lib/wai-aria/checkings/interaction-in-hidden.d.ts +11 -0
- package/lib/wai-aria/checkings/interaction-in-hidden.js +47 -0
- package/lib/wai-aria/checkings/no-global-prop.d.ts +6 -0
- package/lib/wai-aria/checkings/no-global-prop.js +14 -0
- package/lib/wai-aria/checkings/non-existant-role.d.ts +3 -0
- package/lib/wai-aria/checkings/non-existant-role.js +28 -0
- package/lib/wai-aria/checkings/permitted-roles.d.ts +3 -0
- package/lib/wai-aria/checkings/permitted-roles.js +28 -0
- package/lib/wai-aria/checkings/presentational-children.d.ts +29 -0
- package/lib/wai-aria/checkings/presentational-children.js +60 -0
- package/lib/wai-aria/checkings/required-owned-elements.d.ts +11 -0
- package/lib/wai-aria/checkings/required-owned-elements.js +63 -0
- package/lib/wai-aria/checkings/required-prop.d.ts +9 -0
- package/lib/wai-aria/checkings/required-prop.js +27 -0
- package/lib/wai-aria/checkings/value.d.ts +7 -0
- package/lib/wai-aria/checkings/value.js +21 -0
- package/lib/wai-aria/index.d.ts +1 -8
- package/lib/wai-aria/index.js +66 -207
- package/lib/wai-aria/types.d.ts +13 -0
- package/lib/wai-aria/types.js +2 -0
- package/package.json +6 -6
- package/schema.json +0 -12
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -1,106 +1,100 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const ml_core_1 = require("@markuplint/ml-core");
|
|
4
|
-
const helpers_1 = require("../helpers");
|
|
5
4
|
exports.default = (0, ml_core_1.createRule)({
|
|
5
|
+
defaultOptions: {
|
|
6
|
+
ariaVersion: '1.2',
|
|
7
|
+
},
|
|
6
8
|
async verify({ document, report, t }) {
|
|
7
|
-
const { ariaAttrs } = (0, helpers_1.ariaSpec)(document.specs);
|
|
8
9
|
const idList = new Set();
|
|
9
10
|
let hasDynamicId = false;
|
|
10
|
-
await document.walkOn('
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
idList.add(attr.getValue().potential);
|
|
20
|
-
}
|
|
21
|
-
else {
|
|
22
|
-
if (attr.valueType !== 'code') {
|
|
23
|
-
idList.add(attr.getValue().potential);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
11
|
+
await document.walkOn('Attr', attr => {
|
|
12
|
+
if (attr.name.toLowerCase() !== 'id') {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
if (attr.isDynamicValue) {
|
|
16
|
+
hasDynamicId = true;
|
|
17
|
+
}
|
|
18
|
+
if (attr.valueType !== 'code') {
|
|
19
|
+
idList.add(attr.value);
|
|
26
20
|
}
|
|
27
21
|
});
|
|
28
22
|
if (hasDynamicId) {
|
|
29
23
|
return;
|
|
30
24
|
}
|
|
31
|
-
document.walkOn('
|
|
32
|
-
|
|
25
|
+
await document.walkOn('Attr', attr => {
|
|
26
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
27
|
+
const attrSpec = (0, ml_core_1.getAttrSpecs)(attr.ownerElement, document.specs);
|
|
33
28
|
if (!attrSpec) {
|
|
34
29
|
return;
|
|
35
30
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
if (attr.isDynamicValue) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const name = attr.name;
|
|
35
|
+
if (name.toLowerCase() === 'id') {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const value = attr.value;
|
|
39
|
+
const spec = attrSpec.find(s => s.name === name);
|
|
40
|
+
if (spec) {
|
|
41
|
+
// DOMID and DOMID List do not become in the array type.
|
|
42
|
+
if (Array.isArray(spec.type)) {
|
|
43
|
+
return;
|
|
39
44
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
if (spec.type === 'DOMID' && !idList.has(value)) {
|
|
46
|
+
report({
|
|
47
|
+
scope: attr,
|
|
48
|
+
line: (_a = attr.valueNode) === null || _a === void 0 ? void 0 : _a.startLine,
|
|
49
|
+
col: (_b = attr.valueNode) === null || _b === void 0 ? void 0 : _b.startCol,
|
|
50
|
+
raw: (_c = attr.valueNode) === null || _c === void 0 ? void 0 : _c.raw,
|
|
51
|
+
message: t('Missing {0}', t('"{0*}" ID', value)),
|
|
52
|
+
});
|
|
43
53
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
if (typeof spec.type !== 'string' && 'token' in spec.type && spec.type.token === 'DOMID') {
|
|
61
|
-
const refs = value
|
|
62
|
-
.split(spec.type.separator === 'space' ? /\s/ : ',')
|
|
63
|
-
.map(id => id.trim())
|
|
64
|
-
.filter(_ => _);
|
|
65
|
-
for (const ref of refs) {
|
|
66
|
-
if (!idList.has(ref)) {
|
|
67
|
-
report({
|
|
68
|
-
scope: el,
|
|
69
|
-
line: attr.valueNode.startLine,
|
|
70
|
-
col: attr.valueNode.startCol,
|
|
71
|
-
raw: attr.valueNode.raw,
|
|
72
|
-
message: t('Missing {0}', t('"{0*}" ID', ref)),
|
|
73
|
-
});
|
|
74
|
-
}
|
|
54
|
+
if (typeof spec.type !== 'string' && 'token' in spec.type && spec.type.token === 'DOMID') {
|
|
55
|
+
const refs = value
|
|
56
|
+
.split(spec.type.separator === 'space' ? /\s/ : ',')
|
|
57
|
+
.map(id => id.trim())
|
|
58
|
+
.filter(_ => _);
|
|
59
|
+
for (const ref of refs) {
|
|
60
|
+
if (!idList.has(ref)) {
|
|
61
|
+
report({
|
|
62
|
+
scope: attr,
|
|
63
|
+
line: (_d = attr.valueNode) === null || _d === void 0 ? void 0 : _d.startLine,
|
|
64
|
+
col: (_e = attr.valueNode) === null || _e === void 0 ? void 0 : _e.startCol,
|
|
65
|
+
raw: (_f = attr.valueNode) === null || _f === void 0 ? void 0 : _f.raw,
|
|
66
|
+
message: t('Missing {0}', t('"{0*}" ID', ref)),
|
|
67
|
+
});
|
|
75
68
|
}
|
|
76
69
|
}
|
|
77
70
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
71
|
+
}
|
|
72
|
+
const { props } = (0, ml_core_1.ariaSpecs)(document.specs, attr.rule.option.ariaVersion);
|
|
73
|
+
const aria = props.find(prop => prop.name === name);
|
|
74
|
+
if (aria) {
|
|
75
|
+
if (aria.value === 'ID reference' && !idList.has(value)) {
|
|
76
|
+
report({
|
|
77
|
+
scope: attr,
|
|
78
|
+
line: (_g = attr.valueNode) === null || _g === void 0 ? void 0 : _g.startLine,
|
|
79
|
+
col: (_h = attr.valueNode) === null || _h === void 0 ? void 0 : _h.startCol,
|
|
80
|
+
raw: (_j = attr.valueNode) === null || _j === void 0 ? void 0 : _j.raw,
|
|
81
|
+
message: t('Missing {0}', t('"{0*}" ID', value)),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
else if (aria.value === 'ID reference list') {
|
|
85
|
+
const refs = value
|
|
86
|
+
.split(/\s/)
|
|
87
|
+
.map(id => id.trim())
|
|
88
|
+
.filter(_ => _);
|
|
89
|
+
for (const ref of refs) {
|
|
90
|
+
if (!idList.has(ref)) {
|
|
91
|
+
report({
|
|
92
|
+
scope: attr,
|
|
93
|
+
line: (_k = attr.valueNode) === null || _k === void 0 ? void 0 : _k.startLine,
|
|
94
|
+
col: (_l = attr.valueNode) === null || _l === void 0 ? void 0 : _l.startCol,
|
|
95
|
+
raw: (_m = attr.valueNode) === null || _m === void 0 ? void 0 : _m.raw,
|
|
96
|
+
message: t('Missing {0}', t('"{0*}" ID', ref)),
|
|
97
|
+
});
|
|
104
98
|
}
|
|
105
99
|
}
|
|
106
100
|
}
|
|
@@ -6,32 +6,30 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
6
6
|
defaultServerity: 'warning',
|
|
7
7
|
defaultOptions: {},
|
|
8
8
|
async verify({ document, report, t }) {
|
|
9
|
-
await document.walkOn('
|
|
10
|
-
if (
|
|
9
|
+
await document.walkOn('Attr', attr => {
|
|
10
|
+
if (attr.ownerElement.isCustomElement) {
|
|
11
11
|
return;
|
|
12
12
|
}
|
|
13
|
-
const ignoreList = Array.isArray(
|
|
14
|
-
?
|
|
15
|
-
:
|
|
16
|
-
? [
|
|
13
|
+
const ignoreList = Array.isArray(attr.rule.option.ignore)
|
|
14
|
+
? attr.rule.option.ignore
|
|
15
|
+
: attr.rule.option.ignore
|
|
16
|
+
? [attr.rule.option.ignore]
|
|
17
17
|
: [];
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
18
|
+
const name = attr.name;
|
|
19
|
+
for (const ignore of ignoreList) {
|
|
20
|
+
if ((0, helpers_1.match)(name, ignore)) {
|
|
21
|
+
return;
|
|
24
22
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
}
|
|
23
|
+
}
|
|
24
|
+
if (/^on/i.test(name)) {
|
|
25
|
+
report({
|
|
26
|
+
scope: attr,
|
|
27
|
+
raw: attr.raw,
|
|
28
|
+
line: attr.startLine,
|
|
29
|
+
col: attr.startCol,
|
|
30
|
+
message: t('{0} is disallowed', t('the "{0*}" {1}', name, 'attribute')),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
35
33
|
});
|
|
36
34
|
},
|
|
37
35
|
});
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ContentModel } from '@markuplint/ml-spec';
|
|
2
|
+
declare type TagRule = ContentModel & {
|
|
3
|
+
tag: string;
|
|
4
|
+
};
|
|
2
5
|
declare type Options = {
|
|
3
6
|
ignoreHasMutableChildren: boolean;
|
|
4
7
|
};
|
|
5
|
-
declare const _default: import("@markuplint/ml-core").RuleSeed<
|
|
8
|
+
declare const _default: import("@markuplint/ml-core").RuleSeed<TagRule[], Options>;
|
|
6
9
|
export default _default;
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const ml_core_1 = require("@markuplint/ml-core");
|
|
5
|
-
const
|
|
5
|
+
const ml_spec_1 = require("@markuplint/ml-spec");
|
|
6
6
|
const permitted_content_spec_to_regexp_1 = tslib_1.__importDefault(require("./permitted-content.spec-to-regexp"));
|
|
7
|
-
const unfold_content_models_to_tags_1 = tslib_1.__importDefault(require("./unfold-content-models-to-tags"));
|
|
8
7
|
const expMapOnNodeId = new Map();
|
|
9
8
|
exports.default = (0, ml_core_1.createRule)({
|
|
10
9
|
defaultValue: [],
|
|
@@ -13,51 +12,50 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
13
12
|
},
|
|
14
13
|
async verify({ document, report, t }) {
|
|
15
14
|
let idCounter = 0;
|
|
16
|
-
await document.walkOn('Element',
|
|
15
|
+
await document.walkOn('Element', el => {
|
|
17
16
|
var _a;
|
|
18
|
-
if (!
|
|
17
|
+
if (!el.rule.value) {
|
|
19
18
|
return;
|
|
20
19
|
}
|
|
21
|
-
if (
|
|
20
|
+
if (el.rule.option.ignoreHasMutableChildren && el.hasMutableChildren()) {
|
|
22
21
|
return;
|
|
23
22
|
}
|
|
24
|
-
const specType =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
const specType = el.namespaceURI === 'http://www.w3.org/1999/xhtml'
|
|
24
|
+
? 'HTML'
|
|
25
|
+
: el.namespaceURI === 'http://www.w3.org/2000/svg'
|
|
26
|
+
? 'SVG'
|
|
27
|
+
: 'Any Language';
|
|
28
|
+
const childNodes = el.getChildElementsAndTextNodeWithoutWhitespaces();
|
|
29
|
+
const spec = !el.isCustomElement && ((_a = (0, ml_core_1.getSpec)(el, document.specs)) === null || _a === void 0 ? void 0 : _a.contentModel);
|
|
30
|
+
const expGen = new permitted_content_spec_to_regexp_1.default(document.specs, idCounter++);
|
|
28
31
|
if (spec) {
|
|
29
|
-
if (spec.
|
|
32
|
+
if (spec.descendantOf && !el.closest(spec.descendantOf)) {
|
|
30
33
|
report({
|
|
31
|
-
scope:
|
|
32
|
-
message: t('{0} must be {1}', t('the "{0*}" {1}',
|
|
34
|
+
scope: el,
|
|
35
|
+
message: t('{0} must be {1}', t('the "{0*}" {1}', el.localName, 'element'), t('{0} of {1}', 'descendant', t('the "{0*}" {1}', spec.descendantOf, 'element'))),
|
|
33
36
|
});
|
|
34
37
|
return;
|
|
35
38
|
}
|
|
36
39
|
let matched = false;
|
|
37
40
|
if (spec.conditional) {
|
|
38
41
|
for (const conditional of spec.conditional) {
|
|
39
|
-
matched =
|
|
40
|
-
('hasAttr' in conditional.condition && node.hasAttribute(conditional.condition.hasAttr)) ||
|
|
41
|
-
('parent' in conditional.condition &&
|
|
42
|
-
!!node.parentNode &&
|
|
43
|
-
node.parentNode.type === 'Element' &&
|
|
44
|
-
node.parentNode.matches(conditional.condition.parent));
|
|
42
|
+
matched = el.matches(conditional.condition);
|
|
45
43
|
// console.log({ ...conditional, matched });
|
|
46
44
|
if (matched) {
|
|
47
45
|
try {
|
|
48
|
-
const parentExp = getRegExpFromParentNode(document.specs,
|
|
49
|
-
const exp = expGen.specToRegExp(conditional.contents, parentExp,
|
|
50
|
-
const conditionalResult = match(exp, childNodes,
|
|
46
|
+
const parentExp = getRegExpFromParentNode(document.specs, el, expGen);
|
|
47
|
+
const exp = expGen.specToRegExp(conditional.contents, parentExp, el.namespaceURI);
|
|
48
|
+
const conditionalResult = match(exp, childNodes, false, document.specs);
|
|
51
49
|
if (!conditionalResult) {
|
|
52
|
-
const message = t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'content'), t('the "{0*}" {1}',
|
|
50
|
+
const message = t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'content'), t('the "{0*}" {1}', el.localName, 'element')), 'invalid');
|
|
53
51
|
report({
|
|
54
|
-
scope:
|
|
52
|
+
scope: el,
|
|
55
53
|
message: specType !== 'Any Language'
|
|
56
54
|
? t('{0} according to {1}', message, `the ${specType} specification`)
|
|
57
55
|
: message,
|
|
58
|
-
line:
|
|
59
|
-
col:
|
|
60
|
-
raw:
|
|
56
|
+
line: el.startLine,
|
|
57
|
+
col: el.startCol,
|
|
58
|
+
raw: el.raw,
|
|
61
59
|
});
|
|
62
60
|
break;
|
|
63
61
|
}
|
|
@@ -65,7 +63,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
65
63
|
catch (e) {
|
|
66
64
|
if (e instanceof Error) {
|
|
67
65
|
// eslint-disable-next-line no-console
|
|
68
|
-
console.warn(
|
|
66
|
+
console.warn(el.raw, 'conditional', conditional, e.message);
|
|
69
67
|
}
|
|
70
68
|
else {
|
|
71
69
|
throw e;
|
|
@@ -76,12 +74,12 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
76
74
|
}
|
|
77
75
|
if (!matched) {
|
|
78
76
|
try {
|
|
79
|
-
const exp = getRegExpFromNode(document.specs,
|
|
80
|
-
const specResult = match(exp, childNodes,
|
|
77
|
+
const exp = getRegExpFromNode(document.specs, el, expGen);
|
|
78
|
+
const specResult = match(exp, childNodes, false, document.specs);
|
|
81
79
|
if (!specResult) {
|
|
82
|
-
const message = t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'content'), t('the "{0*}" {1}',
|
|
80
|
+
const message = t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'content'), t('the "{0*}" {1}', el.localName, 'element')), 'invalid');
|
|
83
81
|
report({
|
|
84
|
-
scope:
|
|
82
|
+
scope: el,
|
|
85
83
|
message: specType !== 'Any Language'
|
|
86
84
|
? t('{0} according to {1}', message, `the ${specType} specification`)
|
|
87
85
|
: message,
|
|
@@ -91,7 +89,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
91
89
|
catch (e) {
|
|
92
90
|
if (e instanceof Error) {
|
|
93
91
|
// eslint-disable-next-line no-console
|
|
94
|
-
console.warn(
|
|
92
|
+
console.warn(el.raw, 'HTML Spec', e.message);
|
|
95
93
|
}
|
|
96
94
|
else {
|
|
97
95
|
throw e;
|
|
@@ -99,22 +97,22 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
99
97
|
}
|
|
100
98
|
}
|
|
101
99
|
}
|
|
102
|
-
for (const rule of
|
|
103
|
-
if (rule.tag.toLowerCase() !==
|
|
100
|
+
for (const rule of el.rule.value) {
|
|
101
|
+
if (rule.tag.toLowerCase() !== el.localName.toLowerCase()) {
|
|
104
102
|
continue;
|
|
105
103
|
}
|
|
106
|
-
const parentExp = getRegExpFromParentNode(document.specs,
|
|
104
|
+
const parentExp = getRegExpFromParentNode(document.specs, el, expGen);
|
|
107
105
|
try {
|
|
108
|
-
const exp = expGen.specToRegExp(rule.contents, parentExp,
|
|
106
|
+
const exp = expGen.specToRegExp(rule.contents, parentExp, el.namespaceURI);
|
|
109
107
|
// Evaluate the custom element if the optional schema.
|
|
110
|
-
const r = match(exp, childNodes,
|
|
108
|
+
const r = match(exp, childNodes, el.isCustomElement, document.specs);
|
|
111
109
|
if (!r) {
|
|
112
110
|
report({
|
|
113
|
-
scope:
|
|
114
|
-
message: t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'content'), t('the "{0*}" {1}',
|
|
115
|
-
line:
|
|
116
|
-
col:
|
|
117
|
-
raw:
|
|
111
|
+
scope: el,
|
|
112
|
+
message: t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'content'), t('the "{0*}" {1}', el.localName, 'element')), 'invalid'),
|
|
113
|
+
line: el.startLine,
|
|
114
|
+
col: el.startCol,
|
|
115
|
+
raw: el.raw,
|
|
118
116
|
});
|
|
119
117
|
return;
|
|
120
118
|
}
|
|
@@ -122,7 +120,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
122
120
|
catch (e) {
|
|
123
121
|
if (e instanceof Error) {
|
|
124
122
|
// eslint-disable-next-line no-console
|
|
125
|
-
console.warn(
|
|
123
|
+
console.warn(el.raw, 'rule', rule, e.message);
|
|
126
124
|
}
|
|
127
125
|
else {
|
|
128
126
|
throw e;
|
|
@@ -133,53 +131,54 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
133
131
|
expMapOnNodeId.clear();
|
|
134
132
|
},
|
|
135
133
|
});
|
|
136
|
-
function normalization(
|
|
137
|
-
return
|
|
134
|
+
function normalization(el, evalCustomElement) {
|
|
135
|
+
return el
|
|
138
136
|
.map(node => {
|
|
139
137
|
// FIXME: https://github.com/markuplint/markuplint/issues/388
|
|
140
|
-
if (!evalCustomElement && node.
|
|
138
|
+
if (!evalCustomElement && node.is(node.ELEMENT_NODE) && node.isCustomElement) {
|
|
141
139
|
return '';
|
|
142
140
|
}
|
|
143
|
-
if (node.
|
|
141
|
+
if (!node.is(node.ELEMENT_NODE)) {
|
|
144
142
|
return '<#text>';
|
|
145
143
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return `<${name}>`;
|
|
144
|
+
const { localNameWithNS } = (0, ml_core_1.resolveNamespace)(node.localName, node.namespaceURI);
|
|
145
|
+
return `<${localNameWithNS}>`;
|
|
149
146
|
})
|
|
150
147
|
.join('');
|
|
151
148
|
}
|
|
152
|
-
function getRegExpFromNode(specs,
|
|
149
|
+
function getRegExpFromNode(specs, el, expGen) {
|
|
153
150
|
var _a;
|
|
154
151
|
// console.log({ n: node.nodeName });
|
|
155
|
-
if (expMapOnNodeId.has(
|
|
156
|
-
return expMapOnNodeId.get(
|
|
152
|
+
if (expMapOnNodeId.has(el.uuid)) {
|
|
153
|
+
return expMapOnNodeId.get(el.uuid);
|
|
157
154
|
}
|
|
158
|
-
const parentExp =
|
|
159
|
-
const spec = !
|
|
155
|
+
const parentExp = el.parentNode ? getRegExpFromNode(specs, el.parentNode, expGen) : null;
|
|
156
|
+
const spec = el.nodeType === el.ELEMENT_NODE && !el.isCustomElement && ((_a = (0, ml_core_1.getSpec)(el, specs)) === null || _a === void 0 ? void 0 : _a.contentModel);
|
|
160
157
|
const contentRule = spec ? spec.contents : true;
|
|
161
|
-
const exp = expGen.specToRegExp(contentRule, parentExp,
|
|
162
|
-
expMapOnNodeId.set(
|
|
158
|
+
const exp = expGen.specToRegExp(contentRule, parentExp, el.nodeType === el.ELEMENT_NODE ? el.namespaceURI : undefined);
|
|
159
|
+
expMapOnNodeId.set(el.uuid, exp);
|
|
163
160
|
return exp;
|
|
164
161
|
}
|
|
165
|
-
function getRegExpFromParentNode(specs,
|
|
162
|
+
function getRegExpFromParentNode(specs, el, expGen) {
|
|
166
163
|
// console.log({ p: node.nodeName });
|
|
167
|
-
const parentExp =
|
|
164
|
+
const parentExp = el.parentNode ? getRegExpFromNode(specs, el.parentNode, expGen) : null;
|
|
168
165
|
return parentExp;
|
|
169
166
|
}
|
|
170
167
|
const matchingCacheMap = new Map();
|
|
171
|
-
function match(exp, childNodes,
|
|
172
|
-
const target = normalization(childNodes,
|
|
173
|
-
const cacheKey = target +
|
|
168
|
+
function match(exp, childNodes, evalCustomElement, specs) {
|
|
169
|
+
const target = normalization(childNodes, evalCustomElement);
|
|
170
|
+
const cacheKey = target +
|
|
171
|
+
exp.source +
|
|
172
|
+
childNodes.map(n => (n.is(n.ELEMENT_NODE) ? n.toNormalizeString() : n.originRaw)).join('');
|
|
174
173
|
const res = matchingCacheMap.get(cacheKey);
|
|
175
174
|
if (res != null) {
|
|
176
175
|
return res;
|
|
177
176
|
}
|
|
178
|
-
const matched = _match(target, exp, childNodes);
|
|
177
|
+
const matched = _match(target, exp, childNodes, specs);
|
|
179
178
|
matchingCacheMap.set(cacheKey, matched);
|
|
180
179
|
return matched;
|
|
181
180
|
}
|
|
182
|
-
function _match(target, exp, childNodes) {
|
|
181
|
+
function _match(target, exp, childNodes, specs) {
|
|
183
182
|
const result = exp.exec(target);
|
|
184
183
|
if (!result) {
|
|
185
184
|
return false;
|
|
@@ -200,12 +199,12 @@ function _match(target, exp, childNodes) {
|
|
|
200
199
|
switch (type) {
|
|
201
200
|
case 'ACM': {
|
|
202
201
|
const [model, tag] = _selector;
|
|
203
|
-
const selectors = (0,
|
|
202
|
+
const selectors = (0, ml_spec_1.contentModelCategoryToTagNames)(`#${model}`, specs).filter(selector => {
|
|
204
203
|
const [, tagName] = /^([^[\]]+)(?:\[[^\]]+\])?$/.exec(selector) || [];
|
|
205
204
|
return tagName.toLowerCase() === tag.toLowerCase();
|
|
206
205
|
});
|
|
207
206
|
for (const node of childNodes) {
|
|
208
|
-
if (node.
|
|
207
|
+
if (node.is(node.TEXT_NODE)) {
|
|
209
208
|
continue;
|
|
210
209
|
}
|
|
211
210
|
if (node.nodeName.toLowerCase() !== tag.toLowerCase()) {
|
|
@@ -233,7 +232,7 @@ function _match(target, exp, childNodes) {
|
|
|
233
232
|
: null;
|
|
234
233
|
_selector.forEach(content => {
|
|
235
234
|
if (content[0] === '_') {
|
|
236
|
-
(0,
|
|
235
|
+
(0, ml_spec_1.contentModelCategoryToTagNames)(content.replace('_', '#'), specs).forEach(tag => contents.add(tag));
|
|
237
236
|
return;
|
|
238
237
|
}
|
|
239
238
|
contents.add(content);
|
|
@@ -251,7 +250,7 @@ function _match(target, exp, childNodes) {
|
|
|
251
250
|
// });
|
|
252
251
|
for (const node of childNodes) {
|
|
253
252
|
for (const target of targetsMaybeIncludesNotAllowedDescendants) {
|
|
254
|
-
if (node.
|
|
253
|
+
if (node.is(node.TEXT_NODE)) {
|
|
255
254
|
if (selectors.includes('#text')) {
|
|
256
255
|
return false;
|
|
257
256
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { NamespaceURI } from '@markuplint/ml-ast';
|
|
2
|
+
import type { MLMLSpec, PermittedContent } from '@markuplint/ml-spec';
|
|
2
3
|
export default class ExpGenerator {
|
|
3
|
-
private
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
specToRegExp(contentRule: PermittedContent[] | boolean, parentExp?: RegExp | null, ownNS?: string | null): RegExp;
|
|
4
|
+
#private;
|
|
5
|
+
constructor(specs: MLMLSpec, id: number);
|
|
6
|
+
specToRegExp(contentRule: PermittedContent[] | boolean, parentExp?: RegExp | null, ownNS?: NamespaceURI): RegExp;
|
|
7
7
|
private _toPattern;
|
|
8
8
|
private _interleavePattern;
|
|
9
9
|
private _targetTags;
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var _TagList_list;
|
|
2
|
+
var _ExpGenerator_id, _ExpGenerator_specs, _ExpGenerator_idCounter, _TagList_list;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
+
const ml_spec_1 = require("@markuplint/ml-spec");
|
|
5
6
|
const helpers_1 = require("../helpers");
|
|
6
7
|
const array_combination_1 = tslib_1.__importDefault(require("./array.combination"));
|
|
7
|
-
const unfold_content_models_to_tags_1 = tslib_1.__importDefault(require("./unfold-content-models-to-tags"));
|
|
8
8
|
const ALL = '(?:<[^>]+>)?';
|
|
9
9
|
const ___TRANSPARENT___ = '___TRANSPARENT___';
|
|
10
10
|
const ___InTRANSPARENT = '___InTRANSPARENT';
|
|
11
11
|
const CUSTOM_ELEMENT = `(?:<[a-z](?:${helpers_1.rePCENChar})*\\-(?:${helpers_1.rePCENChar})*>)`;
|
|
12
12
|
class ExpGenerator {
|
|
13
|
-
constructor(
|
|
14
|
-
this
|
|
15
|
-
this
|
|
13
|
+
constructor(specs, id) {
|
|
14
|
+
_ExpGenerator_id.set(this, void 0);
|
|
15
|
+
_ExpGenerator_specs.set(this, void 0);
|
|
16
|
+
_ExpGenerator_idCounter.set(this, 0);
|
|
17
|
+
tslib_1.__classPrivateFieldSet(this, _ExpGenerator_id, id, "f");
|
|
18
|
+
tslib_1.__classPrivateFieldSet(this, _ExpGenerator_specs, specs, "f");
|
|
16
19
|
}
|
|
17
|
-
specToRegExp(contentRule, parentExp = null, ownNS =
|
|
20
|
+
specToRegExp(contentRule, parentExp = null, ownNS = 'http://www.w3.org/1999/xhtml') {
|
|
18
21
|
if (contentRule === true) {
|
|
19
22
|
return new RegExp(`^(?:${ALL})+$`);
|
|
20
23
|
}
|
|
@@ -22,10 +25,11 @@ class ExpGenerator {
|
|
|
22
25
|
return new RegExp('^$');
|
|
23
26
|
}
|
|
24
27
|
const parentPattern = parentExp ? parentExp.source.replace(/^\^|\$$/g, '') : ALL;
|
|
25
|
-
const pattern = this._toPattern(contentRule, null, ownNS, 1, 1).replace(___TRANSPARENT___, () => `(?<TRANSPARENT_${this
|
|
28
|
+
const pattern = this._toPattern(contentRule, null, ownNS, 1, 1).replace(___TRANSPARENT___, () => { var _a, _b; return `(?<TRANSPARENT_${tslib_1.__classPrivateFieldGet(this, _ExpGenerator_id, "f")}${tslib_1.__classPrivateFieldSet(this, _ExpGenerator_idCounter, (_b = tslib_1.__classPrivateFieldGet(this, _ExpGenerator_idCounter, "f"), _a = _b++, _b), "f"), _a}>${parentPattern})`; });
|
|
26
29
|
return new RegExp(`^${pattern}$`, 'i');
|
|
27
30
|
}
|
|
28
31
|
_toPattern(contentRule, ignore, ownNS, min, max) {
|
|
32
|
+
var _a, _b;
|
|
29
33
|
if (isTarget(contentRule)) {
|
|
30
34
|
return this._targetTags(contentRule, ignore, ownNS, min, max);
|
|
31
35
|
}
|
|
@@ -87,7 +91,7 @@ class ExpGenerator {
|
|
|
87
91
|
}
|
|
88
92
|
const range = createRange(min, max);
|
|
89
93
|
const c = notAllowedDescendantsNamedCapture
|
|
90
|
-
? `?<NAD_${this
|
|
94
|
+
? `?<NAD_${tslib_1.__classPrivateFieldGet(this, _ExpGenerator_id, "f")}${tslib_1.__classPrivateFieldSet(this, _ExpGenerator_idCounter, (_b = tslib_1.__classPrivateFieldGet(this, _ExpGenerator_idCounter, "f"), _a = _b++, _b), "f"), _a}_${notAllowedDescendantsNamedCapture}>`
|
|
91
95
|
: '?:';
|
|
92
96
|
if (1 === exp.length && range === '') {
|
|
93
97
|
if (notAllowedDescendantsNamedCapture) {
|
|
@@ -117,6 +121,7 @@ class ExpGenerator {
|
|
|
117
121
|
return join(Array.from(tagList), createRange(min, max));
|
|
118
122
|
}
|
|
119
123
|
_resolveTags(target, ownNS) {
|
|
124
|
+
var _a, _b;
|
|
120
125
|
const list = Array.isArray(target) ? target : [target];
|
|
121
126
|
const tagList = new TagList();
|
|
122
127
|
for (const name of list) {
|
|
@@ -131,8 +136,8 @@ class ExpGenerator {
|
|
|
131
136
|
break;
|
|
132
137
|
}
|
|
133
138
|
default: {
|
|
134
|
-
const selectors = (0,
|
|
135
|
-
const counter = this.
|
|
139
|
+
const selectors = (0, ml_spec_1.contentModelCategoryToTagNames)(name, tslib_1.__classPrivateFieldGet(this, _ExpGenerator_specs, "f"));
|
|
140
|
+
const counter = (tslib_1.__classPrivateFieldSet(this, _ExpGenerator_idCounter, (_b = tslib_1.__classPrivateFieldGet(this, _ExpGenerator_idCounter, "f"), _a = _b++, _b), "f"), _a);
|
|
136
141
|
selectors.forEach(selector => {
|
|
137
142
|
if (selector === '#custom') {
|
|
138
143
|
tagList.addPattern(CUSTOM_ELEMENT);
|
|
@@ -141,7 +146,7 @@ class ExpGenerator {
|
|
|
141
146
|
if (/]$/i.test(selector)) {
|
|
142
147
|
const [, tagName] = /^([^[\]]+)\[[^\]]+\]$/.exec(selector) || [];
|
|
143
148
|
// ACM = Attributes by content model
|
|
144
|
-
const exp = `(?<ACM_${this
|
|
149
|
+
const exp = `(?<ACM_${tslib_1.__classPrivateFieldGet(this, _ExpGenerator_id, "f")}${counter}_${name.slice(1)}_${tagName}><${tagName}>)`;
|
|
145
150
|
tagList.addPattern(exp);
|
|
146
151
|
return;
|
|
147
152
|
}
|
|
@@ -157,6 +162,7 @@ class ExpGenerator {
|
|
|
157
162
|
}
|
|
158
163
|
}
|
|
159
164
|
exports.default = ExpGenerator;
|
|
165
|
+
_ExpGenerator_id = new WeakMap(), _ExpGenerator_specs = new WeakMap(), _ExpGenerator_idCounter = new WeakMap();
|
|
160
166
|
class TagList {
|
|
161
167
|
constructor() {
|
|
162
168
|
_TagList_list.set(this, new Set());
|
|
@@ -165,9 +171,8 @@ class TagList {
|
|
|
165
171
|
tslib_1.__classPrivateFieldGet(this, _TagList_list, "f").add(pattern);
|
|
166
172
|
}
|
|
167
173
|
addTag(tagNameOrSelector, ownNS) {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
174
|
+
tagNameOrSelector = tagNameOrSelector.replace(/\|/g, ':');
|
|
175
|
+
tagNameOrSelector = tagNameOrSelector.replace(/^html:/i, '');
|
|
171
176
|
tslib_1.__classPrivateFieldGet(this, _TagList_list, "f").add(`<${tagNameOrSelector}>`);
|
|
172
177
|
}
|
|
173
178
|
result() {
|