@markuplint/rules 2.0.0-rc.3 → 2.1.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 +34 -66
- package/lib/attr-check.js +2 -2
- package/lib/case-sensitive-attr-name/index.d.ts +6 -2
- package/lib/case-sensitive-attr-name/index.js +28 -4
- package/lib/class-naming/index.js +1 -1
- package/lib/deprecated-attr/index.js +1 -1
- package/lib/deprecated-element/index.js +1 -1
- package/lib/end-tag/index.d.ts +2 -0
- package/lib/end-tag/index.js +43 -0
- package/lib/helpers.d.ts +8 -8
- package/lib/helpers.js +26 -27
- package/lib/id-duplication/index.js +1 -1
- package/lib/index.d.ts +2 -1
- package/lib/index.js +2 -0
- package/lib/ineffective-attr/index.js +1 -1
- package/lib/invalid-attr/index.js +10 -3
- package/lib/landmark-roles/index.js +1 -1
- package/lib/no-boolean-attr-value/index.js +2 -2
- package/lib/no-refer-to-non-existent-id/index.js +2 -2
- package/lib/no-use-event-handler-attr/index.js +1 -1
- package/lib/permitted-contents/index.js +14 -13
- package/lib/permitted-contents/types.d.ts +8 -0
- package/lib/permitted-contents/types.js +2 -0
- package/lib/require-element/index.d.ts +1 -1
- package/lib/required-attr/index.js +2 -2
- package/lib/required-h1/index.js +2 -2
- package/lib/wai-aria/index.js +21 -21
- package/package.json +7 -7
- package/schema.json +3 -0
- package/tsconfig.tsbuildinfo +1 -1
package/README.md
CHANGED
|
@@ -8,86 +8,54 @@
|
|
|
8
8
|
|
|
9
9
|
### Conformance checking
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
11
|
+
- [`attr-duplication`](./src/attr-duplication)
|
|
12
|
+
- [`character-reference`](./src/character-reference)
|
|
13
|
+
- [`deprecated-attr`](./src/deprecated-attr)
|
|
14
|
+
- [`deprecated-element`](./src/deprecated-element)
|
|
15
|
+
- [`disallowed-element`](./src/disallowed-element)
|
|
16
|
+
- [`doctype`](./src/doctype)
|
|
17
|
+
- [`id-duplication`](./src/id-duplication)
|
|
18
|
+
- [`ineffective-attr`](./src/ineffective-attr)
|
|
19
|
+
- [`invalid-attr`](./src/invalid-attr)
|
|
20
|
+
- [`permitted-contents`](./src/permitted-contents)
|
|
21
|
+
- [`required-attr`](./src/required-attr)
|
|
22
|
+
- [`required-element`](./src/required-element)
|
|
23
23
|
|
|
24
24
|
### Accessibility
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
| 🚧 | 🚧 | 🚧 | [heading-in-sectioning-content](./src/heading-in-sectioning-content/) | |
|
|
31
|
-
| 🚧 | 🚧 | 🚧 | [heading-in-sectioning-root](./src/heading-in-sectioning-root/) | |
|
|
32
|
-
| 🚧 | 🚧 | 🚧 | [heading-levels-skipping](./src/heading-levels-skipping/) | |
|
|
33
|
-
| 🚧 | 🚧 | 🚧 | [labeling-controls](./src/labeling-controls/) | |
|
|
34
|
-
| ✅ | ✅ | ✅ | [landmark-roles](./src/landmark-roles/) | |
|
|
35
|
-
| ✅ | ✅ | ✅ | [required-h1](./src/required-h1/) | |
|
|
26
|
+
- [`landmark-roles`](./src/landmark-roles)
|
|
27
|
+
- [`no-refer-to-non-existent-id`](./src/no-refer-to-non-existent-id)
|
|
28
|
+
- [`required-h1`](./src/required-h1)
|
|
29
|
+
- [`wai-aria`](./src/wai-aria)
|
|
36
30
|
|
|
37
|
-
###
|
|
31
|
+
### Naming Convention
|
|
38
32
|
|
|
39
|
-
|
|
40
|
-
| -------- | ---- | -------- | ------------------------------------- | ------- |
|
|
41
|
-
| 🚧 | 🚧 | 🚧 | [external-link](./src/external-link/) | 🚧 |
|
|
33
|
+
- [`class-naming`](./src/class-naming)
|
|
42
34
|
|
|
43
|
-
###
|
|
35
|
+
### Maintenability
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
| ✅ | ✅ | ✅ | [class-naming](./src/class-naming/) | |
|
|
48
|
-
| 🚧 | 🚧 | 🚧 | [custom-element-naming](./src/custom-element-naming/) | |
|
|
49
|
-
| 🚧 | 🚧 | 🚧 | [data-attr-naming](./src/data-attr-naming/) | |
|
|
50
|
-
| 🚧 | 🚧 | 🚧 | [required-element](./src/required-element/) | |
|
|
37
|
+
- [`no-hard-code-id`](./src/no-hard-code-id)
|
|
38
|
+
- [`no-use-event-handler-attr`](./src/no-use-event-handler-attr)
|
|
51
39
|
|
|
52
40
|
### Style
|
|
53
41
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
| ✅ | ✅ | ✅ | [indentation](./src/indentation/) | ✅ |
|
|
65
|
-
| 🚧 | 🚧 | 🚧 | [indentation-attr](./src/indentation-attr/) | 🚧 |
|
|
66
|
-
| 🚧 | 🚧 | 🚧 | [path](./src/path/) | |
|
|
67
|
-
| 🚧 | 🚧 | ✅ | [self-closing-tag](./src/self-closing-tag/) | 🚧 |
|
|
68
|
-
| 🚧 | 🚧 | 🚧 | [tag-omission](./src/tag-omission/) | 🚧 |
|
|
42
|
+
- [`attr-equal-space-after`](./src/attr-equal-space-after)
|
|
43
|
+
- [`attr-equal-space-before`](./src/attr-equal-space-before)
|
|
44
|
+
- [`attr-spacing`](./src/attr-spacing)
|
|
45
|
+
- [`attr-value-quotes`](./src/attr-value-quotes)
|
|
46
|
+
- [`case-sensitive-attr-name`](./src/case-sensitive-attr-name)
|
|
47
|
+
- [`case-sensitive-tag-name`](./src/case-sensitive-tag-name)
|
|
48
|
+
- [`end-tag`](./src/end-tag)
|
|
49
|
+
- [`indentation`](./src/indentation)
|
|
50
|
+
- [`no-boolean-attr-value`](./src/no-boolean-attr-value)
|
|
51
|
+
- [`no-default-value`](./src/no-default-value)
|
|
69
52
|
|
|
70
53
|
## Install
|
|
71
54
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Prerequisites: [Node.js](https://nodejs.org) (Version 12.4.0 or later)
|
|
55
|
+
Generally, you **don't have to install** this package because markuplint dependents it defaultly.
|
|
75
56
|
|
|
76
57
|
```sh
|
|
77
|
-
$ npm install @markuplint/rules
|
|
78
|
-
|
|
79
|
-
$ yarn add @markuplint/rules
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
## Contributing
|
|
58
|
+
$ npm install -D @markuplint/rules
|
|
83
59
|
|
|
60
|
+
$ yarn add -D @markuplint/rules
|
|
84
61
|
```
|
|
85
|
-
$ git clone git@github.com:markuplint/markuplint.git -b main
|
|
86
|
-
$ yarn
|
|
87
|
-
$ yarn build
|
|
88
|
-
$ yarn test
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
---
|
|
92
|
-
|
|
93
|
-
Copyright © 2021 markuplint. Under the MIT License.
|
package/lib/attr-check.js
CHANGED
|
@@ -28,7 +28,7 @@ function attrCheck(t, name, value, isCustomRule, spec) {
|
|
|
28
28
|
(0, debug_1.log)('The "%s" attribute DOES\'NT EXIST in the spec', name);
|
|
29
29
|
return {
|
|
30
30
|
invalidType: 'non-existent',
|
|
31
|
-
message: t('{0} is {1:c}', t('the "{0}" {1}', name, 'attribute'), 'disallowed'),
|
|
31
|
+
message: t('{0} is {1:c}', t('the "{0*}" {1}', name, 'attribute'), 'disallowed'),
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
const nameCaseSensitive = /[A-Z]/.test(spec.name);
|
|
@@ -36,7 +36,7 @@ function attrCheck(t, name, value, isCustomRule, spec) {
|
|
|
36
36
|
(0, debug_1.log)('The "%s" attribute name is unmatched in case-sensitive', name);
|
|
37
37
|
return {
|
|
38
38
|
invalidType: 'non-existent',
|
|
39
|
-
message: t('{0} is {1:c}', t('the "{0}" {1}', name, 'attribute'), 'disallowed') +
|
|
39
|
+
message: t('{0} is {1:c}', t('the "{0*}" {1}', name, 'attribute'), 'disallowed') +
|
|
40
40
|
t('. ') +
|
|
41
41
|
t('Did you mean "{0*}"?', spec.name),
|
|
42
42
|
};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
-
export declare type Value = '
|
|
2
|
-
|
|
1
|
+
export declare type Value = 'lower' | 'upper';
|
|
2
|
+
/**
|
|
3
|
+
* @deprecated
|
|
4
|
+
*/
|
|
5
|
+
export declare type Value_v1 = 'no-upper' | 'no-lower';
|
|
6
|
+
declare const _default: import("@markuplint/ml-core").RuleSeed<Value | Value_v1, null>;
|
|
3
7
|
export default _default;
|
|
@@ -3,15 +3,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const ml_core_1 = require("@markuplint/ml-core");
|
|
4
4
|
exports.default = (0, ml_core_1.createRule)({
|
|
5
5
|
defaultServerity: 'warning',
|
|
6
|
-
defaultValue: '
|
|
6
|
+
defaultValue: 'lower',
|
|
7
7
|
async verify({ document, report, t }) {
|
|
8
8
|
await document.walkOn('Element', async (node) => {
|
|
9
9
|
if (node.isForeignElement || node.isCustomElement) {
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
|
+
let value;
|
|
13
|
+
// Convert deprecated value
|
|
14
|
+
switch (node.rule.value) {
|
|
15
|
+
case 'no-lower':
|
|
16
|
+
value = 'upper';
|
|
17
|
+
break;
|
|
18
|
+
case 'no-upper':
|
|
19
|
+
value = 'lower';
|
|
20
|
+
break;
|
|
21
|
+
default:
|
|
22
|
+
value = node.rule.value;
|
|
23
|
+
}
|
|
12
24
|
const ms = node.rule.severity === 'error' ? 'must' : 'should';
|
|
13
|
-
const deny =
|
|
14
|
-
const cases =
|
|
25
|
+
const deny = value === 'lower' ? /[A-Z]/ : /[a-z]/;
|
|
26
|
+
const cases = value === 'lower' ? 'lower' : 'upper';
|
|
15
27
|
const message = t(`{0} ${ms} be {1}`, t('{0} of {1}', 'attribute names', 'HTML elements'), `${cases}case`);
|
|
16
28
|
const attrSpecs = (0, ml_core_1.getAttrSpecs)(node.nameWithNS, document.specs);
|
|
17
29
|
for (const attr of node.attributes) {
|
|
@@ -50,6 +62,18 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
50
62
|
return;
|
|
51
63
|
}
|
|
52
64
|
const attrSpecs = (0, ml_core_1.getAttrSpecs)(node.nameWithNS, document.specs);
|
|
65
|
+
let value;
|
|
66
|
+
// Convert deprecated value
|
|
67
|
+
switch (node.rule.value) {
|
|
68
|
+
case 'no-lower':
|
|
69
|
+
value = 'upper';
|
|
70
|
+
break;
|
|
71
|
+
case 'no-upper':
|
|
72
|
+
value = 'lower';
|
|
73
|
+
break;
|
|
74
|
+
default:
|
|
75
|
+
value = node.rule.value;
|
|
76
|
+
}
|
|
53
77
|
if (node.attributes) {
|
|
54
78
|
for (const attr of node.attributes) {
|
|
55
79
|
if (attr.attrType === 'ps-attr') {
|
|
@@ -69,7 +93,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
69
93
|
continue;
|
|
70
94
|
}
|
|
71
95
|
}
|
|
72
|
-
if (
|
|
96
|
+
if (value === 'lower') {
|
|
73
97
|
attr.name.fix(attr.name.raw.toLowerCase());
|
|
74
98
|
}
|
|
75
99
|
else {
|
|
@@ -25,7 +25,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
25
25
|
if (!classPatterns.some(pattern => (0, helpers_1.match)(className, pattern))) {
|
|
26
26
|
report({
|
|
27
27
|
scope: node,
|
|
28
|
-
message: t('{0} is unmatched with the below patterns: {1}', t('the "{0}" {1}', className, 'class name'), `"${classPatterns.join('", "')}"`),
|
|
28
|
+
message: t('{0} is unmatched with the below patterns: {1}', t('the "{0*}" {1}', className, 'class name'), `"${classPatterns.join('", "')}"`),
|
|
29
29
|
line: classAttr.line,
|
|
30
30
|
col: classAttr.col,
|
|
31
31
|
raw: classAttr.raw,
|
|
@@ -15,7 +15,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
if (attrSpec.deprecated || attrSpec.obsolete) {
|
|
18
|
-
const message = t('{0} is {1:c}', t('the "{0}" {1}', name.potential, 'attribute'), attrSpec.obsolete ? 'obsolete' : 'deprecated');
|
|
18
|
+
const message = t('{0} is {1:c}', t('the "{0*}" {1}', name.potential, 'attribute'), attrSpec.obsolete ? 'obsolete' : 'deprecated');
|
|
19
19
|
report({
|
|
20
20
|
scope: element,
|
|
21
21
|
message,
|
|
@@ -13,7 +13,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
13
13
|
}
|
|
14
14
|
const spec = (0, ml_spec_1.getSpecByTagName)(el.nameWithNS, html_spec_1.default);
|
|
15
15
|
if (spec && (spec.obsolete || spec.deprecated || spec.nonStandard)) {
|
|
16
|
-
const message = t('{0} is {1:c}', t('the "{0}" {1}', el.nodeName, 'element'), spec.deprecated ? 'deprecated' : spec.obsolete ? 'obsolete' : 'non-standard');
|
|
16
|
+
const message = t('{0} is {1:c}', t('the "{0*}" {1}', el.nodeName, 'element'), spec.deprecated ? 'deprecated' : spec.obsolete ? 'obsolete' : 'non-standard');
|
|
17
17
|
report({
|
|
18
18
|
scope: el,
|
|
19
19
|
message,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const ml_core_1 = require("@markuplint/ml-core");
|
|
4
|
+
const voidElements = [
|
|
5
|
+
'area',
|
|
6
|
+
'base',
|
|
7
|
+
'br',
|
|
8
|
+
'col',
|
|
9
|
+
'embed',
|
|
10
|
+
'hr',
|
|
11
|
+
'img',
|
|
12
|
+
'input',
|
|
13
|
+
'link',
|
|
14
|
+
'meta',
|
|
15
|
+
'param',
|
|
16
|
+
'source',
|
|
17
|
+
'track',
|
|
18
|
+
'wbr',
|
|
19
|
+
];
|
|
20
|
+
exports.default = (0, ml_core_1.createRule)({
|
|
21
|
+
defaultServerity: 'warning',
|
|
22
|
+
async verify({ document, report, t }) {
|
|
23
|
+
if (document.endTag === 'never') {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
await document.walkOn('Element', el => {
|
|
27
|
+
var _a;
|
|
28
|
+
if (voidElements.some(vE => vE === el.nodeName.toLowerCase())) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (el.closeTag != null) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (document.endTag === 'xml' && ((_a = el.selfClosingSolidus) === null || _a === void 0 ? void 0 : _a.raw)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
report({
|
|
38
|
+
scope: el,
|
|
39
|
+
message: t('Missing {0}', t('the {0}', 'end tag')),
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
});
|
package/lib/helpers.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Log } from './debug';
|
|
2
2
|
import type { Translator } from '@markuplint/i18n';
|
|
3
3
|
import type { Element, RuleConfigValue } from '@markuplint/ml-core';
|
|
4
|
-
import type { ARIRRoleAttribute, Attribute, PermittedRoles } from '@markuplint/ml-spec';
|
|
4
|
+
import type { ARIRRoleAttribute, Attribute, MLMLSpec, PermittedRoles } from '@markuplint/ml-spec';
|
|
5
5
|
export declare function attrMatches<T extends RuleConfigValue, R>(node: Element<T, R>, condition: Attribute['condition']): boolean;
|
|
6
6
|
export declare function match(needle: string, pattern: string): boolean;
|
|
7
7
|
/**
|
|
@@ -20,7 +20,7 @@ export declare function match(needle: string, pattern: string): boolean;
|
|
|
20
20
|
* Originally, it is not possible to define a name including ASCII upper alphas in the custom element, but it is not treated as illegal by the HTML parser.
|
|
21
21
|
*/
|
|
22
22
|
export declare const rePCENChar: string;
|
|
23
|
-
export declare function htmlSpec(nameWithNS: string): import("@markuplint/ml-spec").ElementSpec | null;
|
|
23
|
+
export declare function htmlSpec(specs: Readonly<MLMLSpec>, nameWithNS: string): import("@markuplint/ml-spec").ElementSpec | null;
|
|
24
24
|
export declare function isValidAttr(t: Translator, name: string, value: string, isDynamicValue: boolean, node: Element<any, any>, attrSpecs: Attribute[], log?: Log): false | {
|
|
25
25
|
invalidType: "non-existent" | "invalid-value";
|
|
26
26
|
message: string;
|
|
@@ -31,11 +31,11 @@ export declare function isValidAttr(t: Translator, name: string, value: string,
|
|
|
31
31
|
} | undefined;
|
|
32
32
|
};
|
|
33
33
|
export declare function toNormalizedValue(value: string, spec: Attribute): string;
|
|
34
|
-
export declare function ariaSpec(): {
|
|
34
|
+
export declare function ariaSpec(specs: Readonly<MLMLSpec>): {
|
|
35
35
|
roles: ARIRRoleAttribute[];
|
|
36
36
|
ariaAttrs: import("@markuplint/ml-spec").ARIAAttribute[];
|
|
37
37
|
};
|
|
38
|
-
export declare function getRoleSpec(roleName: string): {
|
|
38
|
+
export declare function getRoleSpec(specs: Readonly<MLMLSpec>, roleName: string): {
|
|
39
39
|
name: string;
|
|
40
40
|
isAbstract: boolean;
|
|
41
41
|
statesAndProps: import("@markuplint/ml-spec").ARIARoleOwnedPropOrState[];
|
|
@@ -48,9 +48,9 @@ export declare function getRoleSpec(roleName: string): {
|
|
|
48
48
|
* - If `true`, this mean is "Any".
|
|
49
49
|
* - If `false`, this mean is "No".
|
|
50
50
|
*/
|
|
51
|
-
export declare function getPermittedRoles(el: Element<any, any>): PermittedRoles;
|
|
52
|
-
export declare function getImplicitRole(el: Element<any, any>): string | false;
|
|
53
|
-
export declare function getComputedRole(el: Element<any, any>): {
|
|
51
|
+
export declare function getPermittedRoles(specs: Readonly<MLMLSpec>, el: Element<any, any>): PermittedRoles;
|
|
52
|
+
export declare function getImplicitRole(specs: Readonly<MLMLSpec>, el: Element<any, any>): string | false;
|
|
53
|
+
export declare function getComputedRole(specs: Readonly<MLMLSpec>, el: Element<any, any>): {
|
|
54
54
|
name: string;
|
|
55
55
|
isImplicit: boolean;
|
|
56
56
|
} | null;
|
|
@@ -63,7 +63,7 @@ export declare function getComputedRole(el: Element<any, any>): {
|
|
|
63
63
|
* @param tokenEnum
|
|
64
64
|
*/
|
|
65
65
|
export declare function checkAriaValue(type: string, value: string, tokenEnum: string[]): boolean;
|
|
66
|
-
export declare function checkAria(attrName: string, currentValue: string, role?: string): {
|
|
66
|
+
export declare function checkAria(specs: Readonly<MLMLSpec>, attrName: string, currentValue: string, role?: string): {
|
|
67
67
|
currentValue: string;
|
|
68
68
|
isValid: boolean;
|
|
69
69
|
} | {
|
package/lib/helpers.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.checkAria = exports.checkAriaValue = exports.getComputedRole = exports.getImplicitRole = exports.getPermittedRoles = exports.getRoleSpec = exports.ariaSpec = exports.toNormalizedValue = exports.isValidAttr = exports.htmlSpec = exports.rePCENChar = exports.match = exports.attrMatches = void 0;
|
|
4
|
-
const html_spec_1 = require("@markuplint/html-spec");
|
|
5
4
|
const attr_check_1 = require("./attr-check");
|
|
6
5
|
function attrMatches(node, condition) {
|
|
7
6
|
if (!condition) {
|
|
@@ -75,8 +74,8 @@ exports.rePCENChar = [
|
|
|
75
74
|
'[\uFDF0-\uFFFD]',
|
|
76
75
|
'[\uD800-\uDBFF][\uDC00-\uDFFF]',
|
|
77
76
|
].join('|');
|
|
78
|
-
function htmlSpec(nameWithNS) {
|
|
79
|
-
const spec =
|
|
77
|
+
function htmlSpec(specs, nameWithNS) {
|
|
78
|
+
const spec = specs.specs.find(spec => spec.name === nameWithNS);
|
|
80
79
|
return spec || null;
|
|
81
80
|
}
|
|
82
81
|
exports.htmlSpec = htmlSpec;
|
|
@@ -88,7 +87,7 @@ function isValidAttr(t, name, value, isDynamicValue, node, attrSpecs, log) {
|
|
|
88
87
|
if (!invalid && spec && spec.condition && !node.hasSpreadAttr && !attrMatches(node, spec.condition)) {
|
|
89
88
|
invalid = {
|
|
90
89
|
invalidType: 'non-existent',
|
|
91
|
-
message: t('{0} is {1}', t('the "{0}" {1}', name, 'attribute'), 'disallowed'),
|
|
90
|
+
message: t('{0} is {1}', t('the "{0*}" {1}', name, 'attribute'), 'disallowed'),
|
|
92
91
|
};
|
|
93
92
|
}
|
|
94
93
|
if (invalid && invalid.invalidType === 'invalid-value' && isDynamicValue) {
|
|
@@ -126,21 +125,21 @@ function toNormalizedValue(value, spec) {
|
|
|
126
125
|
return normalized;
|
|
127
126
|
}
|
|
128
127
|
exports.toNormalizedValue = toNormalizedValue;
|
|
129
|
-
function ariaSpec() {
|
|
130
|
-
const roles =
|
|
131
|
-
const ariaAttrs =
|
|
128
|
+
function ariaSpec(specs) {
|
|
129
|
+
const roles = specs.def['#roles'];
|
|
130
|
+
const ariaAttrs = specs.def['#ariaAttrs'];
|
|
132
131
|
return {
|
|
133
132
|
roles,
|
|
134
133
|
ariaAttrs,
|
|
135
134
|
};
|
|
136
135
|
}
|
|
137
136
|
exports.ariaSpec = ariaSpec;
|
|
138
|
-
function getRoleSpec(roleName) {
|
|
139
|
-
const role = getRoleByName(roleName);
|
|
137
|
+
function getRoleSpec(specs, roleName) {
|
|
138
|
+
const role = getRoleByName(specs, roleName);
|
|
140
139
|
if (!role) {
|
|
141
140
|
return null;
|
|
142
141
|
}
|
|
143
|
-
const superClassRoles = recursiveTraverseSuperClassRoles(roleName);
|
|
142
|
+
const superClassRoles = recursiveTraverseSuperClassRoles(specs, roleName);
|
|
144
143
|
return {
|
|
145
144
|
name: role.name,
|
|
146
145
|
isAbstract: !!role.isAbstract,
|
|
@@ -149,22 +148,22 @@ function getRoleSpec(roleName) {
|
|
|
149
148
|
};
|
|
150
149
|
}
|
|
151
150
|
exports.getRoleSpec = getRoleSpec;
|
|
152
|
-
function getRoleByName(roleName) {
|
|
153
|
-
const roles =
|
|
151
|
+
function getRoleByName(specs, roleName) {
|
|
152
|
+
const roles = specs.def['#roles'];
|
|
154
153
|
const role = roles.find(r => r.name === roleName);
|
|
155
154
|
return role;
|
|
156
155
|
}
|
|
157
|
-
function getSuperClassRoles(roleName) {
|
|
158
|
-
const role = getRoleByName(roleName);
|
|
159
|
-
return ((role === null || role === void 0 ? void 0 : role.generalization.map(roleName => getRoleByName(roleName)).filter((role) => !!role)) || null);
|
|
156
|
+
function getSuperClassRoles(specs, roleName) {
|
|
157
|
+
const role = getRoleByName(specs, roleName);
|
|
158
|
+
return ((role === null || role === void 0 ? void 0 : role.generalization.map(roleName => getRoleByName(specs, roleName)).filter((role) => !!role)) || null);
|
|
160
159
|
}
|
|
161
|
-
function recursiveTraverseSuperClassRoles(roleName) {
|
|
160
|
+
function recursiveTraverseSuperClassRoles(specs, roleName) {
|
|
162
161
|
const roles = [];
|
|
163
|
-
const superClassRoles = getSuperClassRoles(roleName);
|
|
162
|
+
const superClassRoles = getSuperClassRoles(specs, roleName);
|
|
164
163
|
if (superClassRoles) {
|
|
165
164
|
roles.push(...superClassRoles);
|
|
166
165
|
for (const superClassRole of superClassRoles) {
|
|
167
|
-
const ancestorRoles = recursiveTraverseSuperClassRoles(superClassRole.name);
|
|
166
|
+
const ancestorRoles = recursiveTraverseSuperClassRoles(specs, superClassRole.name);
|
|
168
167
|
roles.push(...ancestorRoles);
|
|
169
168
|
}
|
|
170
169
|
}
|
|
@@ -177,10 +176,10 @@ function recursiveTraverseSuperClassRoles(roleName) {
|
|
|
177
176
|
* - If `true`, this mean is "Any".
|
|
178
177
|
* - If `false`, this mean is "No".
|
|
179
178
|
*/
|
|
180
|
-
function getPermittedRoles(el) {
|
|
179
|
+
function getPermittedRoles(specs, el) {
|
|
181
180
|
var _a;
|
|
182
|
-
const implicitRole = getImplicitRole(el);
|
|
183
|
-
const spec = (_a = htmlSpec(el.nodeName)) === null || _a === void 0 ? void 0 : _a.permittedRoles;
|
|
181
|
+
const implicitRole = getImplicitRole(specs, el);
|
|
182
|
+
const spec = (_a = htmlSpec(specs, el.nodeName)) === null || _a === void 0 ? void 0 : _a.permittedRoles;
|
|
184
183
|
if (!spec) {
|
|
185
184
|
return true;
|
|
186
185
|
}
|
|
@@ -209,9 +208,9 @@ function mergeRoleList(implicitRole, permittedRoles) {
|
|
|
209
208
|
}
|
|
210
209
|
return permittedRoles;
|
|
211
210
|
}
|
|
212
|
-
function getImplicitRole(el) {
|
|
211
|
+
function getImplicitRole(specs, el) {
|
|
213
212
|
var _a;
|
|
214
|
-
const implicitRole = (_a = htmlSpec(el.nodeName)) === null || _a === void 0 ? void 0 : _a.implicitRole;
|
|
213
|
+
const implicitRole = (_a = htmlSpec(specs, el.nodeName)) === null || _a === void 0 ? void 0 : _a.implicitRole;
|
|
215
214
|
if (!implicitRole) {
|
|
216
215
|
return false;
|
|
217
216
|
}
|
|
@@ -225,7 +224,7 @@ function getImplicitRole(el) {
|
|
|
225
224
|
return implicitRole.role;
|
|
226
225
|
}
|
|
227
226
|
exports.getImplicitRole = getImplicitRole;
|
|
228
|
-
function getComputedRole(el) {
|
|
227
|
+
function getComputedRole(specs, el) {
|
|
229
228
|
const roleAttrTokens = el.getAttributeToken('role');
|
|
230
229
|
const roleAttr = roleAttrTokens[0];
|
|
231
230
|
if (roleAttr) {
|
|
@@ -235,7 +234,7 @@ function getComputedRole(el) {
|
|
|
235
234
|
isImplicit: false,
|
|
236
235
|
};
|
|
237
236
|
}
|
|
238
|
-
const implicitRole = getImplicitRole(el);
|
|
237
|
+
const implicitRole = getImplicitRole(specs, el);
|
|
239
238
|
if (implicitRole) {
|
|
240
239
|
return {
|
|
241
240
|
name: implicitRole,
|
|
@@ -287,8 +286,8 @@ function checkAriaValue(type, value, tokenEnum) {
|
|
|
287
286
|
return true;
|
|
288
287
|
}
|
|
289
288
|
exports.checkAriaValue = checkAriaValue;
|
|
290
|
-
function checkAria(attrName, currentValue, role) {
|
|
291
|
-
const ariaAttrs =
|
|
289
|
+
function checkAria(specs, attrName, currentValue, role) {
|
|
290
|
+
const ariaAttrs = specs.def['#ariaAttrs'];
|
|
292
291
|
const aria = ariaAttrs.find(a => a.name === attrName);
|
|
293
292
|
if (!aria) {
|
|
294
293
|
return {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const ml_core_1 = require("@markuplint/ml-core");
|
|
4
4
|
exports.default = (0, ml_core_1.createRule)({
|
|
5
5
|
async verify({ document, report, t }) {
|
|
6
|
-
const message = t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'value'), t('the "{0}" {1}', 'id', 'attribute')), 'duplicated');
|
|
6
|
+
const message = t('{0} is {1:c}', t('{0} of {1}', t('the {0}', 'value'), t('the "{0*}" {1}', 'id', 'attribute')), 'duplicated');
|
|
7
7
|
const idStack = [];
|
|
8
8
|
await document.walkOn('Element', async (node) => {
|
|
9
9
|
const idAttrs = node.getAttributeToken('id');
|
package/lib/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ declare const _default: {
|
|
|
4
4
|
'attr-equal-space-before': import("packages/@markuplint/ml-core/lib").RuleSeed<"always" | "never" | "always-single-line" | "never-single-line", null>;
|
|
5
5
|
'attr-spacing': import("packages/@markuplint/ml-core/lib").RuleSeed<boolean, import("./attr-spacing").AttrSpasingOptions>;
|
|
6
6
|
'attr-value-quotes': import("packages/@markuplint/ml-core/lib").RuleSeed<import("./attr-value-quotes").Type, null>;
|
|
7
|
-
'case-sensitive-attr-name': import("packages/@markuplint/ml-core/lib").RuleSeed<import("./case-sensitive-attr-name").Value, null>;
|
|
7
|
+
'case-sensitive-attr-name': import("packages/@markuplint/ml-core/lib").RuleSeed<import("./case-sensitive-attr-name").Value | import("./case-sensitive-attr-name").Value_v1, null>;
|
|
8
8
|
'case-sensitive-tag-name': import("packages/@markuplint/ml-core/lib").RuleSeed<import("./case-sensitive-tag-name").Value, null>;
|
|
9
9
|
'character-reference': import("packages/@markuplint/ml-core/lib").RuleSeed<import("packages/@markuplint/ml-config/src").RuleConfigValue, null>;
|
|
10
10
|
'class-naming': import("packages/@markuplint/ml-core/lib").RuleSeed<import("./class-naming").Value, null>;
|
|
@@ -14,6 +14,7 @@ declare const _default: {
|
|
|
14
14
|
doctype: import("packages/@markuplint/ml-core/lib").RuleSeed<"always", {
|
|
15
15
|
denyObsolateType: boolean;
|
|
16
16
|
}>;
|
|
17
|
+
'end-tag': import("packages/@markuplint/ml-core/lib").RuleSeed<boolean, null>;
|
|
17
18
|
'id-duplication': import("packages/@markuplint/ml-core/lib").RuleSeed<import("packages/@markuplint/ml-config/src").RuleConfigValue, null>;
|
|
18
19
|
indentation: import("packages/@markuplint/ml-core/lib").RuleSeed<import("./indentation").Value, import("./indentation").IndentationOptions>;
|
|
19
20
|
'ineffective-attr': import("packages/@markuplint/ml-core/lib").RuleSeed<import("packages/@markuplint/ml-config/src").RuleConfigValue, null>;
|
package/lib/index.js
CHANGED
|
@@ -14,6 +14,7 @@ const deprecated_attr_1 = (0, tslib_1.__importDefault)(require("./deprecated-att
|
|
|
14
14
|
const deprecated_element_1 = (0, tslib_1.__importDefault)(require("./deprecated-element"));
|
|
15
15
|
const disallowed_element_1 = (0, tslib_1.__importDefault)(require("./disallowed-element"));
|
|
16
16
|
const doctype_1 = (0, tslib_1.__importDefault)(require("./doctype"));
|
|
17
|
+
const end_tag_1 = (0, tslib_1.__importDefault)(require("./end-tag"));
|
|
17
18
|
const id_duplication_1 = (0, tslib_1.__importDefault)(require("./id-duplication"));
|
|
18
19
|
const indentation_1 = (0, tslib_1.__importDefault)(require("./indentation"));
|
|
19
20
|
const ineffective_attr_1 = (0, tslib_1.__importDefault)(require("./ineffective-attr"));
|
|
@@ -43,6 +44,7 @@ exports.default = {
|
|
|
43
44
|
'deprecated-element': deprecated_element_1.default,
|
|
44
45
|
'disallowed-element': disallowed_element_1.default,
|
|
45
46
|
doctype: doctype_1.default,
|
|
47
|
+
'end-tag': end_tag_1.default,
|
|
46
48
|
'id-duplication': id_duplication_1.default,
|
|
47
49
|
indentation: indentation_1.default,
|
|
48
50
|
'ineffective-attr': ineffective_attr_1.default,
|
|
@@ -29,7 +29,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
29
29
|
raw: attr.raw,
|
|
30
30
|
line: attr.startLine,
|
|
31
31
|
col: attr.startCol,
|
|
32
|
-
message: t('{0} is {1:c}', t('the "{0}" {1}', name, 'attribute'), 'ineffective') +
|
|
32
|
+
message: t('{0} is {1:c}', t('the "{0*}" {1}', name, 'attribute'), 'ineffective') +
|
|
33
33
|
t('. ') +
|
|
34
34
|
t("It doesn't need {0}", t('the {0}', 'attribute')),
|
|
35
35
|
});
|
|
@@ -18,7 +18,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
18
18
|
const attrName = attr.getName();
|
|
19
19
|
const name = attrName.potential;
|
|
20
20
|
if (!node.isCustomElement && attr.attrType === 'html-attr' && attr.candidate) {
|
|
21
|
-
const message = t('{0} is {1:c}', t('the "{0}" {1}', attrName.raw, 'attribute'), 'disallowed') +
|
|
21
|
+
const message = t('{0} is {1:c}', t('the "{0*}" {1}', attrName.raw, 'attribute'), 'disallowed') +
|
|
22
22
|
t('. ') +
|
|
23
23
|
t('Did you mean "{0}"?', attr.candidate);
|
|
24
24
|
report({
|
|
@@ -43,6 +43,9 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
43
43
|
let invalid = false;
|
|
44
44
|
const customRule = node.rule.option.attrs ? node.rule.option.attrs[name] : null;
|
|
45
45
|
if (customRule) {
|
|
46
|
+
if (attr.attrType === 'html-attr' && attr.isDynamicValue) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
46
49
|
if ('enum' in customRule) {
|
|
47
50
|
invalid = (0, attr_check_1.attrCheck)(t, name.toLowerCase(), value, true, {
|
|
48
51
|
name,
|
|
@@ -56,7 +59,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
56
59
|
if (!(0, helpers_1.match)(value, customRule.pattern)) {
|
|
57
60
|
invalid = {
|
|
58
61
|
invalidType: 'invalid-value',
|
|
59
|
-
message: t('{0} is unmatched with the below patterns: {1}', t('the "{0}" {1}', name, 'attribute'), customRule.pattern),
|
|
62
|
+
message: t('{0} is unmatched with the below patterns: {1}', t('the "{0*}" {1}', name, 'attribute'), customRule.pattern),
|
|
60
63
|
};
|
|
61
64
|
}
|
|
62
65
|
}
|
|
@@ -66,7 +69,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
66
69
|
else if ('disallowed' in customRule && customRule.disallowed) {
|
|
67
70
|
invalid = {
|
|
68
71
|
invalidType: 'non-existent',
|
|
69
|
-
message: t('{0} is disallowed', t('the "{0}" {1}', name, 'attribute')),
|
|
72
|
+
message: t('{0} is disallowed', t('the "{0*}" {1}', name, 'attribute')),
|
|
70
73
|
};
|
|
71
74
|
}
|
|
72
75
|
}
|
|
@@ -87,6 +90,10 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
87
90
|
break;
|
|
88
91
|
}
|
|
89
92
|
case 'non-existent': {
|
|
93
|
+
const spec = (0, helpers_1.htmlSpec)(document.specs, node.nameWithNS);
|
|
94
|
+
if (spec === null || spec === void 0 ? void 0 : spec.possibleToAddProperties) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
90
97
|
report({
|
|
91
98
|
scope: node,
|
|
92
99
|
message: invalid.message,
|
|
@@ -78,7 +78,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
78
78
|
if (el.isDescendantByUUIDList(uuidList)) {
|
|
79
79
|
report({
|
|
80
80
|
scope: el,
|
|
81
|
-
message: t('{0} should be {1}', t('the "{0}" {1}', role, 'role'), 'top level'),
|
|
81
|
+
message: t('{0} should be {1}', t('the "{0*}" {1}', role, 'role'), 'top level'),
|
|
82
82
|
});
|
|
83
83
|
}
|
|
84
84
|
}
|
|
@@ -10,7 +10,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
12
12
|
for (const attr of el.attributes) {
|
|
13
|
-
if (attr.attrType === 'ps-attr') {
|
|
13
|
+
if (attr.attrType === 'ps-attr' || attr.isDynamicValue) {
|
|
14
14
|
continue;
|
|
15
15
|
}
|
|
16
16
|
const name = attr.getName().potential;
|
|
@@ -36,7 +36,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
36
36
|
raw: extraRaw,
|
|
37
37
|
line: attr.spacesBeforeEqual.startLine,
|
|
38
38
|
col: attr.spacesBeforeEqual.startCol,
|
|
39
|
-
message: t('{0} is {1}', t('the "{0}" {1}', name, 'attribute'), t('a {0}', 'boolean attribute')) +
|
|
39
|
+
message: t('{0} is {1}', t('the "{0*}" {1}', name, 'attribute'), t('a {0}', 'boolean attribute')) +
|
|
40
40
|
t('. ') +
|
|
41
41
|
t("It doesn't need {0}", t('the {0}', 'value')),
|
|
42
42
|
});
|
|
@@ -4,7 +4,7 @@ const ml_core_1 = require("@markuplint/ml-core");
|
|
|
4
4
|
const helpers_1 = require("../helpers");
|
|
5
5
|
exports.default = (0, ml_core_1.createRule)({
|
|
6
6
|
async verify({ document, report, t }) {
|
|
7
|
-
const { ariaAttrs } = (0, helpers_1.ariaSpec)();
|
|
7
|
+
const { ariaAttrs } = (0, helpers_1.ariaSpec)(document.specs);
|
|
8
8
|
const idList = new Set();
|
|
9
9
|
let hasDynamicId = false;
|
|
10
10
|
await document.walkOn('Element', el => {
|
|
@@ -34,7 +34,7 @@ exports.default = (0, ml_core_1.createRule)({
|
|
|
34
34
|
return;
|
|
35
35
|
}
|
|
36
36
|
for (const attr of el.attributes) {
|
|
37
|
-
if (attr.attrType !== 'html-attr') {
|
|
37
|
+
if (attr.attrType !== 'html-attr' || attr.isDynamicValue) {
|
|
38
38
|
continue;
|
|
39
39
|
}
|
|
40
40
|
const name = attr.getName().potential;
|