@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 CHANGED
@@ -8,86 +8,54 @@
8
8
 
9
9
  ### Conformance checking
10
10
 
11
- | Avaiable | Docs | Docs(ja) | Rule | Fixable |
12
- | -------- | ---- | -------- | ------------------------------------------------- | ------- |
13
- | ✅ | ✅ | ✅ | [attr-duplication](./src/attr-duplication/) | 🚧 |
14
- | ✅ | ✅ | ✅ | [character-reference](./src/character-reference/) | 🚧 |
15
- | 🚧 | 🚧 | 🚧 | [comment](./src/comment/) | |
16
- | ✅ | ✅ | ✅ | [deprecated-attr](./src/deprecated-attr/) | |
17
- | ✅ | ✅ | ✅ | [deprecated-element](./src/deprecated-element/) | |
18
- | ✅ | ✅ | ✅ | [doctype](./src/doctype/) | 🚧 |
19
- | ✅ | ✅ | ✅ | [id-duplication](./src/id-duplication/) | |
20
- | ✅ | ✅ | ✅ | [permitted-contents](./src/permitted-contents/) | |
21
- | ✅ | ✅ | ✅ | [required-attr](./src/required-attr/) | |
22
- | ✅ | ✅ | ✅ | [invalid-attr](./src/invalid-attr/) | |
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
- | Avaiable | Docs | Docs(ja) | Rule | Fixable |
27
- | -------- | ---- | -------- | --------------------------------------------------------------------- | ------- |
28
- | 🚧 | 🚧 | 🚧 | [accessible-text](./src/accessible-text/) | |
29
- | 🚧 | 🚧 | 🚧 | [attr-role](./src/permitted-role/) | |
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
- ### Usability
31
+ ### Naming Convention
38
32
 
39
- | Avaiable | Docs | Docs(ja) | Rule | Fixable |
40
- | -------- | ---- | -------- | ------------------------------------- | ------- |
41
- | 🚧 | 🚧 | 🚧 | [external-link](./src/external-link/) | 🚧 |
33
+ - [`class-naming`](./src/class-naming)
42
34
 
43
- ### Structure Design and Naming Convention
35
+ ### Maintenability
44
36
 
45
- | Avaiable | Docs | Docs(ja) | Rule | Fixable |
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
- | Avaiable | Docs | Docs(ja) | Rule | Fixable |
55
- | -------- | ---- | -------- | ----------------------------------------------------------- | ------- |
56
- | ✅ | ✅ | ✅ | [attr-equal-space-after](./src/attr-equal-space-after/) | ✅ |
57
- | ✅ | ✅ | ✅ | [attr-equal-space-before](./src/attr-equal-space-before/) | ✅ |
58
- | ✅ | ✅ | ✅ | [attr-spacing](./src/attr-spacing/) | ✅ |
59
- | ✅ | ✅ | ✅ | [attr-value-quotes](./src/attr-value-quotes/) | ✅ |
60
- | ✅ | ✅ | ✅ | [case-sensitive-attr-name](./src/case-sensitive-attr-name/) | ✅ |
61
- | ✅ | ✅ | ✅ | [case-sensitive-tag-name](./src/case-sensitive-tag-name/) | ✅ |
62
- | 🚧 | 🚧 | 🚧 | [comment-spasing](./src/comment-spasing/) | 🚧 |
63
- | 🚧 | 🚧 | 🚧 | [event-attr](./src/event-attr/) | |
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
- This package is **default** used that dependenced by [`markuplint`](https://www.npmjs.com/package/markuplint/).
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 = 'no-upper' | 'no-lower';
2
- declare const _default: import("@markuplint/ml-core").RuleSeed<Value, null>;
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: 'no-upper',
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 = node.rule.value === 'no-upper' ? /[A-Z]/ : /[a-z]/;
14
- const cases = node.rule.value === 'no-upper' ? 'lower' : 'upper';
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 (node.rule.value === 'no-upper') {
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,2 @@
1
+ declare const _default: import("@markuplint/ml-core").RuleSeed<boolean, null>;
2
+ export default _default;
@@ -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 = html_spec_1.specs.find(spec => spec.name === nameWithNS);
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 = html_spec_1.def['#roles'];
131
- const ariaAttrs = html_spec_1.def['#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 = html_spec_1.def['#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 = html_spec_1.def['#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;