@markuplint/ml-core 2.0.0-dev.26 → 2.0.0-rc.5

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.
Files changed (36) hide show
  1. package/lib/convert-ruleset.d.ts +1 -1
  2. package/lib/convert-ruleset.js +1 -1
  3. package/lib/debug.js +8 -2
  4. package/lib/index.d.ts +1 -0
  5. package/lib/index.js +3 -1
  6. package/lib/ml-core.d.ts +2 -1
  7. package/lib/ml-core.js +25 -16
  8. package/lib/ml-dom/document.d.ts +5 -2
  9. package/lib/ml-dom/document.js +99 -51
  10. package/lib/ml-dom/helper/match-selector.d.ts +12 -1
  11. package/lib/ml-dom/helper/match-selector.js +108 -90
  12. package/lib/ml-dom/helper/selector.d.ts +5 -3
  13. package/lib/ml-dom/helper/selector.js +275 -79
  14. package/lib/ml-dom/helper/walkers.js +1 -1
  15. package/lib/ml-dom/rule-mapper.d.ts +16 -0
  16. package/lib/ml-dom/rule-mapper.js +57 -0
  17. package/lib/ml-dom/tokens/abstract-element.d.ts +1 -0
  18. package/lib/ml-dom/tokens/abstract-element.js +31 -6
  19. package/lib/ml-dom/tokens/attribute.d.ts +1 -0
  20. package/lib/ml-dom/tokens/attribute.js +7 -0
  21. package/lib/ml-dom/tokens/node.d.ts +8 -7
  22. package/lib/ml-dom/tokens/node.js +21 -13
  23. package/lib/ml-dom/tokens/preprocessor-specific-attribute.d.ts +1 -0
  24. package/lib/ml-dom/tokens/preprocessor-specific-attribute.js +3 -0
  25. package/lib/ml-dom/tokens/token.d.ts +0 -2
  26. package/lib/ml-dom/tokens/token.js +0 -1
  27. package/lib/ml-rule/ml-rule.d.ts +5 -2
  28. package/lib/ml-rule/ml-rule.js +19 -3
  29. package/lib/ml-rule/types.d.ts +3 -3
  30. package/lib/test/index.d.ts +8 -5
  31. package/lib/test/index.js +7 -7
  32. package/markuplint-recommended.json +150 -11
  33. package/package.json +7 -7
  34. package/tsconfig.tsbuildinfo +1 -1
  35. package/lib/create-rule.d.ts +0 -4
  36. package/lib/create-rule.js +0 -8
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- var _MLDOMAbstractElement_fixedNodeName;
2
+ var _MLDOMAbstractElement_fixedNodeName, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, _MLDOMAbstractElement_normalizedString;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const tslib_1 = require("tslib");
5
5
  const ml_spec_1 = require("@markuplint/ml-spec");
@@ -15,6 +15,8 @@ class MLDOMAbstractElement extends node_1.default {
15
15
  this.childModels = new Set();
16
16
  this.descendantModels = new Set();
17
17
  _MLDOMAbstractElement_fixedNodeName.set(this, void 0);
18
+ _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache.set(this, null);
19
+ _MLDOMAbstractElement_normalizedString.set(this, null);
18
20
  this.nodeName = astNode.nodeName;
19
21
  (0, tslib_1.__classPrivateFieldSet)(this, _MLDOMAbstractElement_fixedNodeName, astNode.nodeName, "f");
20
22
  this.namespaceURI = astNode.namespace;
@@ -113,7 +115,7 @@ class MLDOMAbstractElement extends node_1.default {
113
115
  if (selecor.match(el, this)) {
114
116
  return el;
115
117
  }
116
- el = el.parentNode;
118
+ el = el.getParentElement();
117
119
  } while (el !== null && el.type === 'Element');
118
120
  return null;
119
121
  }
@@ -145,12 +147,15 @@ class MLDOMAbstractElement extends node_1.default {
145
147
  return !!this.getAttributeToken(attrName).length;
146
148
  }
147
149
  matches(selector) {
148
- return (0, helper_1.createSelector)(selector).match(this);
150
+ return !!(0, helper_1.createSelector)(selector).match(this);
149
151
  }
150
152
  fixNodeName(name) {
151
153
  (0, tslib_1.__classPrivateFieldSet)(this, _MLDOMAbstractElement_fixedNodeName, name, "f");
152
154
  }
153
155
  getChildElementsAndTextNodeWithoutWhitespaces() {
156
+ if ((0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, "f")) {
157
+ return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, "f");
158
+ }
154
159
  const filteredNodes = [];
155
160
  this.childNodes.forEach(node => {
156
161
  if (node.type === 'Element') {
@@ -164,6 +169,7 @@ class MLDOMAbstractElement extends node_1.default {
164
169
  filteredNodes.push(...children);
165
170
  }
166
171
  });
172
+ (0, tslib_1.__classPrivateFieldSet)(this, _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache, filteredNodes, "f");
167
173
  return filteredNodes;
168
174
  }
169
175
  /**
@@ -173,7 +179,7 @@ class MLDOMAbstractElement extends node_1.default {
173
179
  return this.childNodes.some(node => node.type === 'PSBlock');
174
180
  }
175
181
  isDescendantByUUIDList(uuidList) {
176
- let el = this.parentNode;
182
+ let el = this.getParentElement();
177
183
  if (el === null) {
178
184
  return false;
179
185
  }
@@ -181,10 +187,29 @@ class MLDOMAbstractElement extends node_1.default {
181
187
  if (uuidList.includes(el.uuid)) {
182
188
  return true;
183
189
  }
184
- el = el.parentNode;
190
+ el = el.getParentElement();
185
191
  } while (el !== null && el.type === 'Element');
186
192
  return false;
187
193
  }
194
+ toNormalizeString() {
195
+ if ((0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_normalizedString, "f")) {
196
+ return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMAbstractElement_normalizedString, "f");
197
+ }
198
+ const children = this.getChildElementsAndTextNodeWithoutWhitespaces();
199
+ const attrs = this.attributes.map(attr => attr.toNormalizeString());
200
+ const attrString = attrs.length ? ' ' + attrs.join('') : '';
201
+ const startTag = `<${this.nodeName}${attrString}>`;
202
+ const childNodes = children.map(node => {
203
+ if (node.type === 'Element') {
204
+ return node.toNormalizeString();
205
+ }
206
+ return node.originRaw;
207
+ });
208
+ const endTag = `</${this.nodeName}>`;
209
+ const normalizedString = `${startTag}${childNodes.join('')}${endTag}`;
210
+ (0, tslib_1.__classPrivateFieldSet)(this, _MLDOMAbstractElement_normalizedString, normalizedString, "f");
211
+ return normalizedString;
212
+ }
188
213
  }
189
214
  exports.default = MLDOMAbstractElement;
190
- _MLDOMAbstractElement_fixedNodeName = new WeakMap();
215
+ _MLDOMAbstractElement_fixedNodeName = new WeakMap(), _MLDOMAbstractElement_getChildElementsAndTextNodeWithoutWhitespacesCache = new WeakMap(), _MLDOMAbstractElement_normalizedString = new WeakMap();
@@ -36,4 +36,5 @@ export default class MLDOMAttribute extends MLDOMToken<MLASTHTMLAttr> {
36
36
  raw: string;
37
37
  };
38
38
  toString(withSpace?: boolean): string;
39
+ toNormalizeString(): string;
39
40
  }
@@ -69,5 +69,12 @@ class MLDOMAttribute extends token_1.default {
69
69
  toString(withSpace = true) {
70
70
  return (withSpace ? this.spacesBeforeName.raw : '') + this.raw;
71
71
  }
72
+ toNormalizeString() {
73
+ return (this.name.originRaw +
74
+ this.equal.originRaw +
75
+ this.startQuote.originRaw +
76
+ this.value.originRaw +
77
+ this.endQuote.originRaw);
78
+ }
72
79
  }
73
80
  exports.default = MLDOMAttribute;
@@ -1,23 +1,24 @@
1
1
  import type { Document } from '../';
2
2
  import type { RuleInfo } from '../../';
3
3
  import type { AnonymousNode, IMLDOMNode, NodeType } from '../types';
4
- import type { MLDOMElement, MLDOMOmittedElement } from './';
4
+ import type { MLDOMElement, MLDOMOmittedElement, MLDOMPreprocessorSpecificBlock } from './';
5
5
  import type { MLASTAbstructNode } from '@markuplint/ml-ast';
6
- import type { RuleConfigValue } from '@markuplint/ml-config';
6
+ import type { AnyRule, RuleConfigValue } from '@markuplint/ml-config';
7
7
  import MLDOMToken from './token';
8
8
  export default abstract class MLDOMNode<T extends RuleConfigValue, O = null, A extends MLASTAbstructNode = MLASTAbstructNode> extends MLDOMToken<A> implements IMLDOMNode {
9
9
  #private;
10
10
  readonly type: NodeType;
11
+ readonly rules: Record<string, AnyRule>;
11
12
  protected _astToken: A;
12
13
  constructor(astNode: A, document: Document<T, O>);
13
- get parentNode(): MLDOMElement<T, O> | MLDOMOmittedElement<T, O> | null;
14
+ get parentNode(): MLDOMElement<T, O> | MLDOMOmittedElement<T, O> | MLDOMPreprocessorSpecificBlock<T, O> | null;
14
15
  get prevNode(): AnonymousNode<T, O> | null;
15
16
  get nextNode(): AnonymousNode<T, O> | null;
16
- get syntaxicalParentNode(): MLDOMElement<T, O> | null;
17
+ get syntaxicalParentNode(): MLDOMElement<T, O> | MLDOMPreprocessorSpecificBlock<T, O> | null;
17
18
  get prevToken(): AnonymousNode<T, O> | null;
18
19
  get nodeStore(): import("../helper").NodeStore;
19
- toString(): string;
20
- is(type: NodeType): boolean;
21
20
  get rule(): RuleInfo<T, O>;
22
- private _isFirstToken;
21
+ getParentElement(): MLDOMElement<T, O> | MLDOMOmittedElement<T, O> | null;
22
+ is(type: NodeType): boolean;
23
+ toString(): string;
23
24
  }
@@ -7,6 +7,7 @@ class MLDOMNode extends token_1.default {
7
7
  constructor(astNode, document) {
8
8
  super(astNode);
9
9
  this.type = 'Node';
10
+ this.rules = {};
10
11
  _MLDOMNode_doc.set(this, void 0);
11
12
  /**
12
13
  * prevToken cache props
@@ -69,26 +70,33 @@ class MLDOMNode extends token_1.default {
69
70
  get nodeStore() {
70
71
  return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMNode_doc, "f").nodeStore;
71
72
  }
72
- toString() {
73
- return this.raw;
74
- }
75
- is(type) {
76
- return this.type === type;
77
- }
78
73
  get rule() {
79
74
  if (!(0, tslib_1.__classPrivateFieldGet)(this, _MLDOMNode_doc, "f").currentRule) {
80
75
  throw new Error('Invalid call.');
81
76
  }
82
77
  const name = (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMNode_doc, "f").currentRule.name;
83
- // @ts-ignore
84
- const ruleConfig = this.rules[name];
85
- if (ruleConfig == null) {
86
- throw new Error('Invalid call "rule" property.');
78
+ const rule = this.rules[name];
79
+ return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMNode_doc, "f").currentRule.optimizeOption(rule);
80
+ }
81
+ getParentElement() {
82
+ let parent = this.parentNode;
83
+ if (!parent) {
84
+ return null;
87
85
  }
88
- return (0, tslib_1.__classPrivateFieldGet)(this, _MLDOMNode_doc, "f").currentRule.optimizeOption(ruleConfig);
86
+ while (parent) {
87
+ if (parent.type === 'PSBlock') {
88
+ parent = parent.parentNode;
89
+ continue;
90
+ }
91
+ return parent;
92
+ }
93
+ return null;
94
+ }
95
+ is(type) {
96
+ return this.type === type;
89
97
  }
90
- _isFirstToken() {
91
- return !this.prevToken;
98
+ toString() {
99
+ return this.raw;
92
100
  }
93
101
  }
94
102
  exports.default = MLDOMNode;
@@ -20,4 +20,5 @@ export default class MLDOMPreprocessorSpecificAttribute extends MLDOMToken<MLAST
20
20
  raw: string;
21
21
  };
22
22
  toString(): string;
23
+ toNormalizeString(): string;
23
24
  }
@@ -30,5 +30,8 @@ class MLDOMPreprocessorSpecificAttribute extends token_1.default {
30
30
  toString() {
31
31
  return this.raw;
32
32
  }
33
+ toNormalizeString() {
34
+ return this.raw;
35
+ }
33
36
  }
34
37
  exports.default = MLDOMPreprocessorSpecificAttribute;
@@ -1,9 +1,7 @@
1
1
  import type { MLToken } from '@markuplint/ml-ast';
2
- import type { Rules } from '@markuplint/ml-config';
3
2
  export default class MLDOMToken<A extends MLToken> {
4
3
  #private;
5
4
  readonly uuid: string;
6
- readonly rules: Rules;
7
5
  protected readonly _astToken: A;
8
6
  constructor(astToken: A);
9
7
  get originRaw(): string;
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const tslib_1 = require("tslib");
5
5
  class MLDOMToken {
6
6
  constructor(astToken) {
7
- this.rules = {};
8
7
  _MLDOMToken_startLine.set(this, void 0);
9
8
  _MLDOMToken_endLine.set(this, void 0);
10
9
  _MLDOMToken_startCol.set(this, void 0);
@@ -1,7 +1,8 @@
1
+ import type { Ruleset } from '..';
1
2
  import type Document from '../ml-dom/document';
2
3
  import type { RuleSeed } from './types';
3
4
  import type { LocaleSet } from '@markuplint/i18n';
4
- import type { RuleConfig, RuleConfigValue, RuleInfo, Severity, Violation } from '@markuplint/ml-config';
5
+ import type { GlobalRuleInfo, RuleConfig, RuleConfigValue, RuleInfo, Rules, Severity, Violation } from '@markuplint/ml-config';
5
6
  export declare class MLRule<T extends RuleConfigValue, O = null> {
6
7
  #private;
7
8
  readonly name: string;
@@ -22,6 +23,8 @@ export declare class MLRule<T extends RuleConfigValue, O = null> {
22
23
  name: string;
23
24
  });
24
25
  verify(document: Document<T, O>, locale: LocaleSet, globalRule: RuleInfo<T, O>, fix: boolean): Promise<Violation[]>;
25
- optimizeOption(configSettings: T | RuleConfig<T, O>): RuleInfo<T, O>;
26
+ getRuleInfo(ruleSet: Ruleset, ruleName: string): GlobalRuleInfo<T, O>;
27
+ _optimize(rules: Rules | undefined, ruleName: string): RuleInfo<T, O>;
28
+ optimizeOption(configSettings: T | RuleConfig<T, O> | undefined): RuleInfo<T, O>;
26
29
  }
27
30
  export declare type AnyMLRule = MLRule<RuleConfigValue, unknown>;
@@ -6,12 +6,13 @@ const tslib_1 = require("tslib");
6
6
  const ml_rule_context_1 = require("./ml-rule-context");
7
7
  class MLRule {
8
8
  constructor(o) {
9
+ var _a, _b;
9
10
  _MLRule_v.set(this, void 0);
10
11
  _MLRule_f.set(this, void 0);
11
12
  this.name = o.name;
12
13
  this.defaultServerity = o.defaultServerity || 'error';
13
- this.defaultValue = o.defaultValue;
14
- this.defaultOptions = o.defaultOptions;
14
+ this.defaultValue = (_a = o.defaultValue) !== null && _a !== void 0 ? _a : true;
15
+ this.defaultOptions = (_b = o.defaultOptions) !== null && _b !== void 0 ? _b : null;
15
16
  (0, tslib_1.__classPrivateFieldSet)(this, _MLRule_v, o.verify, "f");
16
17
  (0, tslib_1.__classPrivateFieldSet)(this, _MLRule_f, o.fix, "f");
17
18
  }
@@ -79,13 +80,27 @@ class MLRule {
79
80
  document.setRule(null);
80
81
  return violation;
81
82
  }
83
+ getRuleInfo(ruleSet, ruleName) {
84
+ const info = this._optimize(ruleSet.rules, ruleName);
85
+ return {
86
+ ...info,
87
+ nodeRules: ruleSet.nodeRules.map(r => this._optimize(r.rules, ruleName)).filter(r => !r.disabled),
88
+ childNodeRules: ruleSet.childNodeRules.map(r => this._optimize(r.rules, ruleName)).filter(r => !r.disabled),
89
+ };
90
+ }
91
+ _optimize(rules, ruleName) {
92
+ const rule = (rules && rules[ruleName]) || false;
93
+ const info = this.optimizeOption(rule);
94
+ return info;
95
+ }
82
96
  optimizeOption(configSettings) {
83
- if (typeof configSettings === 'boolean') {
97
+ if (configSettings === undefined || typeof configSettings === 'boolean') {
84
98
  return {
85
99
  disabled: !configSettings,
86
100
  severity: this.defaultServerity,
87
101
  value: this.defaultValue,
88
102
  option: this.defaultOptions,
103
+ reason: undefined,
89
104
  };
90
105
  }
91
106
  if (!Array.isArray(configSettings) && typeof configSettings === 'object' && configSettings !== null) {
@@ -114,6 +129,7 @@ class MLRule {
114
129
  // @ts-ignore TODO: Wait for fix to bug of type guards in TypeScript
115
130
  value: configSettings == null ? this.defaultValue : configSettings,
116
131
  option: this.defaultOptions,
132
+ reason: undefined,
117
133
  };
118
134
  }
119
135
  }
@@ -1,9 +1,9 @@
1
1
  import type { MLRuleContext } from './ml-rule-context';
2
2
  import type { RuleConfigValue, Severity } from '@markuplint/ml-config';
3
- export declare type RuleSeed<T extends RuleConfigValue, O = null> = {
3
+ export declare type RuleSeed<T extends RuleConfigValue = boolean, O = null> = {
4
4
  defaultServerity?: Severity;
5
- defaultValue: T;
6
- defaultOptions: O;
5
+ defaultValue?: T;
6
+ defaultOptions?: O;
7
7
  verify(context: ReturnType<MLRuleContext<T, O>['provide']>): void | Promise<void>;
8
8
  fix?(context: ReturnType<MLRuleContext<T, O>['provide']>): void | Promise<void>;
9
9
  };
@@ -1,12 +1,15 @@
1
+ import type { MLMarkupLanguageParser } from '@markuplint/ml-ast';
1
2
  import type { Config, RuleConfigValue } from '@markuplint/ml-config';
2
3
  import type { ExtendedSpec, MLMLSpec } from '@markuplint/ml-spec';
3
4
  import { Document } from '../ml-dom';
4
- export declare function createTestDocument<T extends RuleConfigValue = any, O = any>(sourceCode: string, config?: Config): Document<T, O>;
5
- export declare function createTestNodeList(sourceCode: string): readonly import("../ml-dom").AnonymousNode<any, any>[];
6
- export declare function createTestElement(sourceCode: `<${Alphabet}${string}>`): import("../ml-dom").Element<any, any>;
7
- declare type Alphabet = 'a' | 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'h' | 'i' | 'j' | 'k' | 'l' | 'm' | 'n' | 'o' | 'p' | 'q' | 'r' | 's' | 't' | 'u' | 'v' | 'w' | 'x' | 'y' | 'z';
5
+ export declare type CreateTestOptions = {
6
+ config?: Config;
7
+ parser?: MLMarkupLanguageParser;
8
+ };
9
+ export declare function createTestDocument<T extends RuleConfigValue = any, O = any>(sourceCode: string, options?: CreateTestOptions): Document<T, O>;
10
+ export declare function createTestNodeList(sourceCode: string, options?: CreateTestOptions): readonly import("../ml-dom").AnonymousNode<any, any>[];
11
+ export declare function createTestElement(sourceCode: string, options?: CreateTestOptions): import("../ml-dom").Element<any, any>;
8
12
  /**
9
13
  * for test suite
10
14
  */
11
15
  export declare function dummySchemas(): [MLMLSpec, ...ExtendedSpec[]];
12
- export {};
package/lib/test/index.js CHANGED
@@ -4,20 +4,20 @@ exports.dummySchemas = exports.createTestElement = exports.createTestNodeList =
4
4
  const html_parser_1 = require("@markuplint/html-parser");
5
5
  const convert_ruleset_1 = require("../convert-ruleset");
6
6
  const ml_dom_1 = require("../ml-dom");
7
- function createTestDocument(sourceCode, config = {}) {
8
- const ast = (0, html_parser_1.parse)(sourceCode);
9
- const ruleset = (0, convert_ruleset_1.convertRuleset)(config);
7
+ function createTestDocument(sourceCode, options) {
8
+ const ast = (options === null || options === void 0 ? void 0 : options.parser) ? options.parser.parse(sourceCode) : (0, html_parser_1.parse)(sourceCode);
9
+ const ruleset = (0, convert_ruleset_1.convertRuleset)(options === null || options === void 0 ? void 0 : options.config);
10
10
  const document = new ml_dom_1.Document(ast, ruleset, [{}, {}]);
11
11
  return document;
12
12
  }
13
13
  exports.createTestDocument = createTestDocument;
14
- function createTestNodeList(sourceCode) {
15
- const document = createTestDocument(sourceCode);
14
+ function createTestNodeList(sourceCode, options) {
15
+ const document = createTestDocument(sourceCode, options);
16
16
  return document.nodeList;
17
17
  }
18
18
  exports.createTestNodeList = createTestNodeList;
19
- function createTestElement(sourceCode) {
20
- const document = createTestDocument(sourceCode);
19
+ function createTestElement(sourceCode, options) {
20
+ const document = createTestDocument(sourceCode, options);
21
21
  const el = document.nodeList[0];
22
22
  if (el.type === 'Element') {
23
23
  return el;
@@ -1,54 +1,193 @@
1
1
  {
2
2
  "rules": {
3
3
  "attr-duplication": true,
4
+ "attr-equal-space-after": false,
5
+ "attr-equal-space-before": false,
6
+ "attr-spacing": false,
7
+ "attr-value-quotes": false,
8
+ "case-sensitive-attr-name": false,
9
+ "case-sensitive-tag-name": false,
4
10
  "character-reference": true,
11
+ "class-naming": false,
5
12
  "deprecated-attr": true,
6
13
  "deprecated-element": true,
14
+ "disallowed-element": ["hgroup"],
7
15
  "doctype": true,
8
16
  "id-duplication": true,
17
+ "indentation": false,
18
+ "ineffective-attr": true,
19
+ "invalid-attr": {
20
+ "option": {
21
+ "attrs": {
22
+ "autofocus": {
23
+ "disallowed": true
24
+ },
25
+ "accesskey": {
26
+ "disallowed": true
27
+ },
28
+ "tabindex": {
29
+ "enum": ["-1", "0"]
30
+ }
31
+ }
32
+ }
33
+ },
34
+ "landmark-roles": true,
35
+ "no-boolean-attr-value": true,
36
+ "no-default-value": false,
37
+ "no-hard-code-id": false,
38
+ "no-refer-to-non-existent-id": true,
39
+ "no-use-event-handler-attr": false,
9
40
  "permitted-contents": true,
10
41
  "required-attr": true,
11
- "invalid-attr": true,
12
- "landmark-roles": true,
42
+ "required-element": false,
13
43
  "required-h1": true,
14
44
  "wai-aria": true
15
45
  },
16
46
  "nodeRules": [
17
47
  {
18
- "selector": "html",
48
+ "selector": ":where(html)",
19
49
  "rules": {
20
50
  "required-attr": ["lang"]
21
51
  }
22
52
  },
23
53
  {
24
- "selector": "script[src]",
54
+ "selector": ":where(head)",
55
+ "rules": {
56
+ "required-element": ["meta[charset=\"UTF-8\" i]"]
57
+ }
58
+ },
59
+ {
60
+ "selector": ":where(meta[property])",
61
+ "rules": {
62
+ "invalid-attr": {
63
+ "option": {
64
+ "attrs": {
65
+ "property": {
66
+ "pattern": "/^og:[a-z_]+/"
67
+ },
68
+ "content": {
69
+ "type": "NoEmptyAny"
70
+ }
71
+ }
72
+ }
73
+ },
74
+ "required-attr": false
75
+ }
76
+ },
77
+ {
78
+ "selector": ":where(meta[itemprop])",
79
+ "rules": {
80
+ "invalid-attr": {
81
+ "option": {
82
+ "attrs": {
83
+ "content": {
84
+ "type": "NoEmptyAny"
85
+ }
86
+ }
87
+ }
88
+ }
89
+ }
90
+ },
91
+ {
92
+ "selector": ":where(script[src])",
25
93
  "rules": {
26
94
  "required-attr": ["defer"]
27
95
  }
28
96
  },
29
97
  {
30
- "selector": "img",
98
+ "selector": "h1, h2, h3, h4, h5, h6",
99
+ "rules": {
100
+ "disallowed-element": {
101
+ "value": ["small"],
102
+ "reason": "The small element must not be used for subheadings. https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-small-element"
103
+ }
104
+ }
105
+ },
106
+ {
107
+ "selector": ":where(img)",
31
108
  "rules": {
32
- "required-attr": ["decoding", "width", "height", "alt"]
109
+ "required-attr": [
110
+ "width",
111
+ "height",
112
+ "alt",
113
+ {
114
+ "name": "decoding",
115
+ "value": "async"
116
+ }
117
+ ]
33
118
  }
34
119
  },
35
120
  {
36
- "selector": "iframe",
121
+ "selector": ":where(iframe)",
37
122
  "rules": {
38
- "required-attr": ["loading", "title"]
123
+ "required-attr": [
124
+ "title",
125
+ {
126
+ "name": "loading",
127
+ "value": "lazy"
128
+ }
129
+ ]
39
130
  }
40
131
  },
41
132
  {
42
- "selector": "a[target=_blank], area[target=_blank]",
133
+ "selector": ":where(a[target=_blank], area[target=_blank])",
43
134
  "rules": {
44
- "required-attr": ["rel"]
135
+ "required-attr": {
136
+ "value": [
137
+ {
138
+ "name": "rel",
139
+ "value": "/(?<![^\\s]+)noreferrer(?![^\\s]+)/"
140
+ }
141
+ ],
142
+ "reason": "The \"rel\" attribute should be required with \"noreferrer\" if the \"a\" element has the \"target=_black\""
143
+ }
45
144
  }
46
145
  },
47
146
  {
48
- "selector": "abbr",
147
+ "selector": ":where(abbr)",
49
148
  "rules": {
50
149
  "required-attr": ["title"]
51
150
  }
151
+ },
152
+ {
153
+ "selector": ":where(video, audio)",
154
+ "rules": {
155
+ "required-element": ["track"]
156
+ }
157
+ },
158
+ {
159
+ "selector": ":where(video[autoplay])",
160
+ "rules": {
161
+ "required-attr": ["muted"]
162
+ }
163
+ },
164
+ {
165
+ "selector": ":where(th, td)",
166
+ "rules": {
167
+ "invalid-attr": {
168
+ "option": {
169
+ "attrs": {
170
+ "span": {
171
+ "disallowed": false
172
+ }
173
+ }
174
+ }
175
+ }
176
+ }
177
+ },
178
+ {
179
+ "selector": ":where(body :not(button))",
180
+ "rules": {
181
+ "invalid-attr": {
182
+ "option": {
183
+ "attrs": {
184
+ "onclick": {
185
+ "disallowed": false
186
+ }
187
+ }
188
+ }
189
+ }
190
+ }
52
191
  }
53
192
  ]
54
193
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/ml-core",
3
- "version": "2.0.0-dev.26+6a10aede",
3
+ "version": "2.0.0-rc.5",
4
4
  "description": "The core module of markuplint",
5
5
  "repository": "git@github.com:markuplint/markuplint.git",
6
6
  "author": "Yusuke Hirao <yusukehirao@me.com>",
@@ -17,17 +17,17 @@
17
17
  "clean": "tsc --build --clean"
18
18
  },
19
19
  "dependencies": {
20
- "@markuplint/i18n": "^1.2.0",
21
- "@markuplint/ml-ast": "^2.0.0-dev.36+6a10aede",
22
- "@markuplint/ml-config": "^1.3.0",
23
- "@markuplint/ml-spec": "^2.0.0-dev.26+6a10aede",
20
+ "@markuplint/i18n": "2.0.0-rc.4",
21
+ "@markuplint/ml-ast": "2.0.0-rc.5",
22
+ "@markuplint/ml-config": "2.0.0-rc.5",
23
+ "@markuplint/ml-spec": "2.0.0-rc.5",
24
24
  "debug": "^4.3.2",
25
25
  "postcss-selector-parser": "^6.0.6",
26
26
  "tslib": "^2.3.1"
27
27
  },
28
28
  "devDependencies": {
29
- "@markuplint/html-parser": "^2.0.0-dev.23+6a10aede",
29
+ "@markuplint/html-parser": "2.0.0-rc.5",
30
30
  "@types/debug": "^4.1.7"
31
31
  },
32
- "gitHead": "6a10aede7385e2e97d0ba2c272a2e8c08e5429fd"
32
+ "gitHead": "6faa4e54f16d3700d23953330abea89ccb9d6150"
33
33
  }