@markuplint/ml-core 4.13.3 → 5.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/ARCHITECTURE.ja.md +92 -35
- package/ARCHITECTURE.md +85 -28
- package/CHANGELOG.md +50 -0
- package/docs/linting-pipeline.ja.md +18 -14
- package/docs/linting-pipeline.md +18 -14
- package/docs/maintenance.ja.md +1 -1
- package/docs/maintenance.md +1 -1
- package/docs/ml-dom/attr.ja.md +8 -0
- package/docs/ml-dom/attr.md +8 -0
- package/docs/ml-dom/block.ja.md +33 -33
- package/docs/ml-dom/block.md +33 -33
- package/docs/ml-dom/element.ja.md +17 -17
- package/docs/ml-dom/element.md +17 -17
- package/docs/ml-dom/node.ja.md +4 -5
- package/docs/ml-dom/node.md +4 -5
- package/docs/ml-dom/others.ja.md +2 -1
- package/docs/ml-dom/others.md +5 -4
- package/docs/rule-system.ja.md +23 -6
- package/docs/rule-system.md +23 -6
- package/lib/index.d.ts +4 -3
- package/lib/index.js +1 -1
- package/lib/ml-core.d.ts +1 -1
- package/lib/ml-core.js +142 -82
- package/lib/ml-dom/helper/accname.d.ts +8 -0
- package/lib/ml-dom/helper/accname.js +71 -55
- package/lib/ml-dom/helper/create-node.js +1 -0
- package/lib/ml-dom/helper/get-indent.js +17 -29
- package/lib/ml-dom/node/attr.js +122 -73
- package/lib/ml-dom/node/block.d.ts +3 -3
- package/lib/ml-dom/node/block.js +10 -1
- package/lib/ml-dom/node/document-type.js +12 -0
- package/lib/ml-dom/node/document.d.ts +14 -3
- package/lib/ml-dom/node/document.js +75 -34
- package/lib/ml-dom/node/dom-token-list.js +17 -30
- package/lib/ml-dom/node/element-close-tag.js +1 -0
- package/lib/ml-dom/node/element.d.ts +19 -7
- package/lib/ml-dom/node/element.js +134 -55
- package/lib/ml-dom/node/node-store.js +6 -15
- package/lib/ml-dom/node/node.d.ts +3 -1
- package/lib/ml-dom/node/node.js +159 -166
- package/lib/ml-dom/node/parent-node.js +14 -30
- package/lib/ml-dom/node/rule-mapper.js +7 -20
- package/lib/ml-dom/node/text.d.ts +7 -0
- package/lib/ml-dom/node/text.js +9 -0
- package/lib/ml-dom/token/token.js +23 -39
- package/lib/ml-rule/create-rule.d.ts +8 -1
- package/lib/ml-rule/create-rule.js +0 -9
- package/lib/ml-rule/ml-rule-context.js +7 -11
- package/lib/ml-rule/ml-rule.d.ts +33 -1
- package/lib/ml-rule/ml-rule.js +65 -25
- package/lib/ruleset/index.js +6 -0
- package/lib/test/index.js +4 -1
- package/lib/types.d.ts +2 -1
- package/lib/violation-collector.js +15 -28
- package/lib/virtual-rule.d.ts +72 -0
- package/lib/virtual-rule.js +233 -0
- package/package.json +16 -13
|
@@ -1,15 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _MLToken_endCol, _MLToken_endLine, _MLToken_endOffset, _MLToken_fixed, _MLToken_raw, _MLToken_startCol, _MLToken_startLine, _MLToken_startOffset;
|
|
1
|
+
import { getEndCol, getEndLine } from '@markuplint/parser-utils/location';
|
|
13
2
|
/**
|
|
14
3
|
* Represents a single token in the markuplint AST.
|
|
15
4
|
* Wraps an AST token with positional information (line, column, offset)
|
|
@@ -18,30 +7,26 @@ var _MLToken_endCol, _MLToken_endLine, _MLToken_endOffset, _MLToken_fixed, _MLTo
|
|
|
18
7
|
* @template A - The AST token type this token wraps
|
|
19
8
|
*/
|
|
20
9
|
export class MLToken {
|
|
10
|
+
#fixed;
|
|
11
|
+
#raw;
|
|
12
|
+
/**
|
|
13
|
+
* The unique identifier for this token.
|
|
14
|
+
*/
|
|
15
|
+
uuid;
|
|
16
|
+
/**
|
|
17
|
+
* The underlying AST token that this token wraps.
|
|
18
|
+
*/
|
|
19
|
+
_astToken;
|
|
21
20
|
/**
|
|
22
21
|
* Creates a new MLToken instance from an AST token.
|
|
23
22
|
*
|
|
24
23
|
* @param astToken - The AST token to wrap
|
|
25
24
|
*/
|
|
26
25
|
constructor(astToken) {
|
|
27
|
-
_MLToken_endCol.set(this, void 0);
|
|
28
|
-
_MLToken_endLine.set(this, void 0);
|
|
29
|
-
_MLToken_endOffset.set(this, void 0);
|
|
30
|
-
_MLToken_fixed.set(this, void 0);
|
|
31
|
-
_MLToken_raw.set(this, void 0);
|
|
32
|
-
_MLToken_startCol.set(this, void 0);
|
|
33
|
-
_MLToken_startLine.set(this, void 0);
|
|
34
|
-
_MLToken_startOffset.set(this, void 0);
|
|
35
26
|
this._astToken = astToken;
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
this.#raw = astToken.raw;
|
|
28
|
+
this.#fixed = astToken.raw;
|
|
38
29
|
this.uuid = astToken.uuid;
|
|
39
|
-
__classPrivateFieldSet(this, _MLToken_startLine, astToken.startLine, "f");
|
|
40
|
-
__classPrivateFieldSet(this, _MLToken_endLine, astToken.endLine, "f");
|
|
41
|
-
__classPrivateFieldSet(this, _MLToken_startCol, astToken.startCol, "f");
|
|
42
|
-
__classPrivateFieldSet(this, _MLToken_endCol, astToken.endCol, "f");
|
|
43
|
-
__classPrivateFieldSet(this, _MLToken_startOffset, astToken.startOffset, "f");
|
|
44
|
-
__classPrivateFieldSet(this, _MLToken_endOffset, astToken.endOffset, "f");
|
|
45
30
|
}
|
|
46
31
|
/**
|
|
47
32
|
* The ending column number (1-based) of this token in the source.
|
|
@@ -49,7 +34,7 @@ export class MLToken {
|
|
|
49
34
|
* @implements `@markuplint/ml-core` API: `MLDOMToken`
|
|
50
35
|
*/
|
|
51
36
|
get endCol() {
|
|
52
|
-
return
|
|
37
|
+
return getEndCol(this.fixed, this.startCol);
|
|
53
38
|
}
|
|
54
39
|
/**
|
|
55
40
|
* The ending line number (1-based) of this token in the source.
|
|
@@ -57,7 +42,7 @@ export class MLToken {
|
|
|
57
42
|
* @implements `@markuplint/ml-core` API: `MLDOMToken`
|
|
58
43
|
*/
|
|
59
44
|
get endLine() {
|
|
60
|
-
return
|
|
45
|
+
return getEndLine(this.fixed, this.startLine);
|
|
61
46
|
}
|
|
62
47
|
/**
|
|
63
48
|
* The ending character offset (0-based) of this token in the source.
|
|
@@ -65,7 +50,7 @@ export class MLToken {
|
|
|
65
50
|
* @implements `@markuplint/ml-core` API: `MLDOMToken`
|
|
66
51
|
*/
|
|
67
52
|
get endOffset() {
|
|
68
|
-
return
|
|
53
|
+
return this.startOffset + this.fixed.length;
|
|
69
54
|
}
|
|
70
55
|
/**
|
|
71
56
|
* The fixed (potentially modified) string content of this token.
|
|
@@ -73,7 +58,7 @@ export class MLToken {
|
|
|
73
58
|
* @implements `@markuplint/ml-core` API: `MLDOMToken`
|
|
74
59
|
*/
|
|
75
60
|
get fixed() {
|
|
76
|
-
return
|
|
61
|
+
return this.#fixed;
|
|
77
62
|
}
|
|
78
63
|
/**
|
|
79
64
|
* The original raw string content of this token from the source.
|
|
@@ -81,7 +66,7 @@ export class MLToken {
|
|
|
81
66
|
* @implements `@markuplint/ml-core` API: `MLDOMToken`
|
|
82
67
|
*/
|
|
83
68
|
get raw() {
|
|
84
|
-
return
|
|
69
|
+
return this.#raw;
|
|
85
70
|
}
|
|
86
71
|
/**
|
|
87
72
|
* The starting column number (1-based) of this token in the source.
|
|
@@ -89,7 +74,7 @@ export class MLToken {
|
|
|
89
74
|
* @implements `@markuplint/ml-core` API: `MLDOMToken`
|
|
90
75
|
*/
|
|
91
76
|
get startCol() {
|
|
92
|
-
return
|
|
77
|
+
return this._astToken.col;
|
|
93
78
|
}
|
|
94
79
|
/**
|
|
95
80
|
* The starting line number (1-based) of this token in the source.
|
|
@@ -97,7 +82,7 @@ export class MLToken {
|
|
|
97
82
|
* @implements `@markuplint/ml-core` API: `MLDOMToken`
|
|
98
83
|
*/
|
|
99
84
|
get startLine() {
|
|
100
|
-
return
|
|
85
|
+
return this._astToken.line;
|
|
101
86
|
}
|
|
102
87
|
/**
|
|
103
88
|
* The starting character offset (0-based) of this token in the source.
|
|
@@ -105,7 +90,7 @@ export class MLToken {
|
|
|
105
90
|
* @implements `@markuplint/ml-core` API: `MLDOMToken`
|
|
106
91
|
*/
|
|
107
92
|
get startOffset() {
|
|
108
|
-
return
|
|
93
|
+
return this._astToken.offset;
|
|
109
94
|
}
|
|
110
95
|
/**
|
|
111
96
|
* Replaces the fixed content of this token with the given string,
|
|
@@ -115,7 +100,7 @@ export class MLToken {
|
|
|
115
100
|
* @param raw - The new string content to set as the fixed value
|
|
116
101
|
*/
|
|
117
102
|
fix(raw) {
|
|
118
|
-
|
|
103
|
+
this.#fixed = raw;
|
|
119
104
|
}
|
|
120
105
|
/**
|
|
121
106
|
* Returns the string representation of this token.
|
|
@@ -125,7 +110,6 @@ export class MLToken {
|
|
|
125
110
|
* @returns The string content of this token
|
|
126
111
|
*/
|
|
127
112
|
toString(fixed = false) {
|
|
128
|
-
return fixed ?
|
|
113
|
+
return fixed ? this.#fixed : this.#raw;
|
|
129
114
|
}
|
|
130
115
|
}
|
|
131
|
-
_MLToken_endCol = new WeakMap(), _MLToken_endLine = new WeakMap(), _MLToken_endOffset = new WeakMap(), _MLToken_fixed = new WeakMap(), _MLToken_raw = new WeakMap(), _MLToken_startCol = new WeakMap(), _MLToken_startLine = new WeakMap(), _MLToken_startOffset = new WeakMap();
|
|
@@ -4,9 +4,16 @@ import type { PlainData, RuleConfigValue } from '@markuplint/ml-config';
|
|
|
4
4
|
* Factory function for creating a type-safe rule seed.
|
|
5
5
|
* Returns the seed object as-is; primarily used for type inference.
|
|
6
6
|
*
|
|
7
|
+
* When `T` is `boolean` (or includes `boolean`), `defaultValue` is optional because
|
|
8
|
+
* the runtime default is `true`. For all other value types, `defaultValue` is required
|
|
9
|
+
* so that the rule always has a well-typed default.
|
|
10
|
+
*
|
|
11
|
+
* @see https://github.com/markuplint/markuplint/issues/808
|
|
7
12
|
* @template T - The type of the rule's configuration value
|
|
8
13
|
* @template O - The type of the rule's options
|
|
9
14
|
* @param seed - The rule seed definition containing verify/fix functions and defaults
|
|
10
15
|
* @returns The same seed object, now fully typed
|
|
11
16
|
*/
|
|
12
|
-
export declare function createRule<T extends RuleConfigValue, O extends PlainData = undefined>(seed: Readonly<RuleSeed<T, O>>
|
|
17
|
+
export declare function createRule<T extends RuleConfigValue, O extends PlainData = undefined>(seed: Readonly<RuleSeed<T, O>> & (boolean extends T ? {} : {
|
|
18
|
+
readonly defaultValue: T;
|
|
19
|
+
})): Readonly<RuleSeed<T, O>>;
|
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Factory function for creating a type-safe rule seed.
|
|
3
|
-
* Returns the seed object as-is; primarily used for type inference.
|
|
4
|
-
*
|
|
5
|
-
* @template T - The type of the rule's configuration value
|
|
6
|
-
* @template O - The type of the rule's options
|
|
7
|
-
* @param seed - The rule seed definition containing verify/fix functions and defaults
|
|
8
|
-
* @returns The same seed object, now fully typed
|
|
9
|
-
*/
|
|
10
1
|
export function createRule(seed) {
|
|
11
2
|
return seed;
|
|
12
3
|
}
|
|
@@ -1,21 +1,18 @@
|
|
|
1
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var _MLRuleContext_reports;
|
|
7
1
|
import { translator } from '@markuplint/i18n';
|
|
8
2
|
export class MLRuleContext {
|
|
3
|
+
document;
|
|
4
|
+
locale;
|
|
5
|
+
#reports = [];
|
|
6
|
+
translate;
|
|
9
7
|
constructor(
|
|
10
8
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
11
9
|
document, locale) {
|
|
12
|
-
_MLRuleContext_reports.set(this, []);
|
|
13
10
|
this.document = document;
|
|
14
11
|
this.translate = translator(locale);
|
|
15
12
|
this.locale = locale.locale;
|
|
16
13
|
}
|
|
17
14
|
get reports() {
|
|
18
|
-
return
|
|
15
|
+
return this.#reports.map(report => ({
|
|
19
16
|
...report,
|
|
20
17
|
message: finish(report.message, this.locale),
|
|
21
18
|
}));
|
|
@@ -41,12 +38,11 @@ export class MLRuleContext {
|
|
|
41
38
|
this._push(report);
|
|
42
39
|
}
|
|
43
40
|
_push(report) {
|
|
44
|
-
if (!
|
|
45
|
-
|
|
41
|
+
if (!this.#reports.some(r => is(r, report))) {
|
|
42
|
+
this.#reports.push(report);
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
45
|
}
|
|
49
|
-
_MLRuleContext_reports = new WeakMap();
|
|
50
46
|
function finish(message, locale = 'en') {
|
|
51
47
|
switch (locale) {
|
|
52
48
|
case 'en': {
|
package/lib/ml-rule/ml-rule.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { RuleSeed } from './types.js';
|
|
|
2
2
|
import type { MLDocument } from '../ml-dom/node/document.js';
|
|
3
3
|
import type { Ruleset } from '../ruleset/index.js';
|
|
4
4
|
import type { LocaleSet } from '@markuplint/i18n';
|
|
5
|
-
import type { GlobalRuleInfo, PlainData, Rule, RuleConfigValue, RuleInfo, Severity, Violation } from '@markuplint/ml-config';
|
|
5
|
+
import type { GlobalRuleInfo, SpecConformance, PlainData, Rule, RuleConfigValue, RuleInfo, Severity, Violation } from '@markuplint/ml-config';
|
|
6
6
|
/**
|
|
7
7
|
* Represents a single markuplint rule that can verify documents and report violations.
|
|
8
8
|
*
|
|
@@ -11,13 +11,45 @@ import type { GlobalRuleInfo, PlainData, Rule, RuleConfigValue, RuleInfo, Severi
|
|
|
11
11
|
*/
|
|
12
12
|
export declare class MLRule<T extends RuleConfigValue, O extends PlainData = undefined> {
|
|
13
13
|
#private;
|
|
14
|
+
/**
|
|
15
|
+
* For virtual rules, the name of the base rule whose verify/fix logic is reused.
|
|
16
|
+
* When set, violations report this as `ruleId` for backwards compatibility.
|
|
17
|
+
*/
|
|
18
|
+
readonly baseRuleId?: string;
|
|
14
19
|
readonly defaultOptions: O;
|
|
15
20
|
readonly defaultSeverity: Severity;
|
|
16
21
|
readonly defaultValue: T;
|
|
22
|
+
/**
|
|
23
|
+
* For multi-entry named nodeRules, the group name shared by all derived virtual rules.
|
|
24
|
+
* Allows `rules["groupName"]: false` to disable all rules in the group.
|
|
25
|
+
*/
|
|
26
|
+
readonly groupName?: string;
|
|
17
27
|
readonly name: string;
|
|
28
|
+
/**
|
|
29
|
+
* The spec conformance classification of this rule, based on RFC 2119 keyword strength.
|
|
30
|
+
* Set on virtual rules derived from named nodeRules in presets.
|
|
31
|
+
*/
|
|
32
|
+
readonly specConformance?: SpecConformance;
|
|
18
33
|
constructor(o: Readonly<RuleSeed<T, O>> & {
|
|
19
34
|
readonly name: string;
|
|
35
|
+
readonly baseRuleId?: string;
|
|
36
|
+
readonly specConformance?: SpecConformance;
|
|
37
|
+
readonly groupName?: string;
|
|
20
38
|
});
|
|
39
|
+
/**
|
|
40
|
+
* Creates a virtual rule that reuses this rule's verify/fix logic
|
|
41
|
+
* under a different name (alias). Used by named nodeRules to produce
|
|
42
|
+
* independent rule instances that can be individually configured.
|
|
43
|
+
*
|
|
44
|
+
* @param aliasName - The alias name (must contain `/`)
|
|
45
|
+
* @param options - Override options for the virtual rule
|
|
46
|
+
* @returns A new MLRule instance sharing the same verify/fix logic
|
|
47
|
+
*/
|
|
48
|
+
createAlias(aliasName: string, options?: {
|
|
49
|
+
readonly defaultSeverity?: Severity;
|
|
50
|
+
readonly specConformance?: SpecConformance;
|
|
51
|
+
readonly groupName?: string;
|
|
52
|
+
}): MLRule<T, O>;
|
|
21
53
|
/**
|
|
22
54
|
* The following getter is unused internally,
|
|
23
55
|
* only for extending from 3rd party library
|
package/lib/ml-rule/ml-rule.js
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _MLRule_f, _MLRule_v;
|
|
13
1
|
import { deleteUndefProp } from '@markuplint/ml-config';
|
|
14
2
|
// @ts-ignore
|
|
15
3
|
import { isPlainObject } from 'is-plain-object';
|
|
@@ -21,30 +9,76 @@ import { MLRuleContext } from './ml-rule-context.js';
|
|
|
21
9
|
* @template O - The type of the rule's options
|
|
22
10
|
*/
|
|
23
11
|
export class MLRule {
|
|
12
|
+
/**
|
|
13
|
+
* For virtual rules, the name of the base rule whose verify/fix logic is reused.
|
|
14
|
+
* When set, violations report this as `ruleId` for backwards compatibility.
|
|
15
|
+
*/
|
|
16
|
+
baseRuleId;
|
|
17
|
+
defaultOptions;
|
|
18
|
+
defaultSeverity;
|
|
19
|
+
defaultValue;
|
|
20
|
+
#f;
|
|
21
|
+
/**
|
|
22
|
+
* For multi-entry named nodeRules, the group name shared by all derived virtual rules.
|
|
23
|
+
* Allows `rules["groupName"]: false` to disable all rules in the group.
|
|
24
|
+
*/
|
|
25
|
+
groupName;
|
|
26
|
+
name;
|
|
27
|
+
/**
|
|
28
|
+
* The spec conformance classification of this rule, based on RFC 2119 keyword strength.
|
|
29
|
+
* Set on virtual rules derived from named nodeRules in presets.
|
|
30
|
+
*/
|
|
31
|
+
specConformance;
|
|
32
|
+
#v;
|
|
24
33
|
constructor(o) {
|
|
25
|
-
_MLRule_f.set(this, void 0);
|
|
26
|
-
_MLRule_v.set(this, void 0);
|
|
27
34
|
this.name = o.name;
|
|
35
|
+
this.baseRuleId = o.baseRuleId;
|
|
36
|
+
this.groupName = o.groupName;
|
|
37
|
+
this.specConformance = o.specConformance;
|
|
28
38
|
this.defaultSeverity = o.defaultSeverity ?? 'error';
|
|
29
|
-
//
|
|
39
|
+
// When T is boolean, defaultValue is optional and the runtime default is `true`.
|
|
40
|
+
// For non-boolean T, the type system now enforces that defaultValue is provided.
|
|
41
|
+
// See: https://github.com/markuplint/markuplint/issues/808
|
|
30
42
|
this.defaultValue = (o.defaultValue === undefined ? true : o.defaultValue);
|
|
31
43
|
this.defaultOptions = o.defaultOptions;
|
|
32
|
-
|
|
33
|
-
|
|
44
|
+
this.#v = o.verify;
|
|
45
|
+
this.#f = o.fix;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Creates a virtual rule that reuses this rule's verify/fix logic
|
|
49
|
+
* under a different name (alias). Used by named nodeRules to produce
|
|
50
|
+
* independent rule instances that can be individually configured.
|
|
51
|
+
*
|
|
52
|
+
* @param aliasName - The alias name (must contain `/`)
|
|
53
|
+
* @param options - Override options for the virtual rule
|
|
54
|
+
* @returns A new MLRule instance sharing the same verify/fix logic
|
|
55
|
+
*/
|
|
56
|
+
createAlias(aliasName, options) {
|
|
57
|
+
return new MLRule({
|
|
58
|
+
name: aliasName,
|
|
59
|
+
baseRuleId: this.name,
|
|
60
|
+
groupName: options?.groupName,
|
|
61
|
+
specConformance: options?.specConformance,
|
|
62
|
+
defaultSeverity: options?.defaultSeverity ?? this.defaultSeverity,
|
|
63
|
+
defaultValue: this.defaultValue,
|
|
64
|
+
defaultOptions: this.defaultOptions,
|
|
65
|
+
verify: this.#v,
|
|
66
|
+
fix: this.#f,
|
|
67
|
+
});
|
|
34
68
|
}
|
|
35
69
|
/**
|
|
36
70
|
* The following getter is unused internally,
|
|
37
71
|
* only for extending from 3rd party library
|
|
38
72
|
*/
|
|
39
73
|
get f() {
|
|
40
|
-
return
|
|
74
|
+
return this.#f;
|
|
41
75
|
}
|
|
42
76
|
/**
|
|
43
77
|
* The following getter is unused internally,
|
|
44
78
|
* only for extending from 3rd party library
|
|
45
79
|
*/
|
|
46
80
|
get v() {
|
|
47
|
-
return
|
|
81
|
+
return this.#v;
|
|
48
82
|
}
|
|
49
83
|
/**
|
|
50
84
|
* Resolves the full rule information from a ruleset, including node-level
|
|
@@ -115,10 +149,13 @@ export class MLRule {
|
|
|
115
149
|
document.setRule(this);
|
|
116
150
|
const context = new MLRuleContext(document, locale);
|
|
117
151
|
const providableContext = context.provide();
|
|
118
|
-
await
|
|
119
|
-
if (
|
|
120
|
-
await
|
|
152
|
+
await this.#v(providableContext);
|
|
153
|
+
if (this.#f && fix) {
|
|
154
|
+
await this.#f(providableContext);
|
|
121
155
|
}
|
|
156
|
+
const ruleId = this.baseRuleId ?? this.name;
|
|
157
|
+
// Only include name and specConformance for virtual rules (named nodeRules)
|
|
158
|
+
const aliasName = this.baseRuleId ? this.name : undefined;
|
|
122
159
|
const violation = context.reports.map(report => {
|
|
123
160
|
if ('scope' in report) {
|
|
124
161
|
let line = report.scope.startLine;
|
|
@@ -135,7 +172,9 @@ export class MLRule {
|
|
|
135
172
|
line,
|
|
136
173
|
col,
|
|
137
174
|
raw,
|
|
138
|
-
ruleId
|
|
175
|
+
ruleId,
|
|
176
|
+
name: aliasName,
|
|
177
|
+
specConformance: this.specConformance,
|
|
139
178
|
reason: report.scope.rule.reason ?? document.rule.reason,
|
|
140
179
|
};
|
|
141
180
|
deleteUndefProp(violation);
|
|
@@ -147,7 +186,9 @@ export class MLRule {
|
|
|
147
186
|
line: report.line,
|
|
148
187
|
col: report.col,
|
|
149
188
|
raw: report.raw,
|
|
150
|
-
ruleId
|
|
189
|
+
ruleId,
|
|
190
|
+
name: aliasName,
|
|
191
|
+
specConformance: this.specConformance,
|
|
151
192
|
reason: document.rule.reason,
|
|
152
193
|
};
|
|
153
194
|
deleteUndefProp(violation);
|
|
@@ -162,7 +203,6 @@ export class MLRule {
|
|
|
162
203
|
return info;
|
|
163
204
|
}
|
|
164
205
|
}
|
|
165
|
-
_MLRule_f = new WeakMap(), _MLRule_v = new WeakMap();
|
|
166
206
|
function isRuleConfig(data) {
|
|
167
207
|
return isPlainObject(data);
|
|
168
208
|
}
|
package/lib/ruleset/index.js
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
* rule overrides extracted from a markuplint {@link Config}.
|
|
4
4
|
*/
|
|
5
5
|
export class Ruleset {
|
|
6
|
+
/** Rule overrides that apply to child nodes matching specific selectors */
|
|
7
|
+
childNodeRules;
|
|
8
|
+
/** Rule overrides that apply to nodes matching specific selectors */
|
|
9
|
+
nodeRules;
|
|
10
|
+
/** The global rule definitions */
|
|
11
|
+
rules;
|
|
6
12
|
/**
|
|
7
13
|
* @param config - The markuplint configuration to extract rules from
|
|
8
14
|
*/
|
package/lib/test/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ARIA_RECOMMENDED_VERSION } from '@markuplint/ml-spec';
|
|
1
2
|
import { parser } from '@markuplint/html-parser';
|
|
2
3
|
import spec from '@markuplint/html-spec';
|
|
3
4
|
import { convertRuleset } from '../convert-ruleset.js';
|
|
@@ -18,7 +19,9 @@ export function createTestDocument(sourceCode, options) {
|
|
|
18
19
|
: options.parser.parse(sourceCode, options.config?.parserOptions)
|
|
19
20
|
: parser.parse(sourceCode, options?.config?.parserOptions);
|
|
20
21
|
const ruleset = convertRuleset(options?.config);
|
|
21
|
-
const document = new MLDocument(ast, ruleset, [options?.specs ?? {}, {}]
|
|
22
|
+
const document = new MLDocument(ast, ruleset, [options?.specs ?? {}, {}], {
|
|
23
|
+
ariaVersion: ARIA_RECOMMENDED_VERSION,
|
|
24
|
+
});
|
|
22
25
|
return document;
|
|
23
26
|
}
|
|
24
27
|
/**
|
package/lib/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { AnyMLRule } from './ml-rule/index.js';
|
|
|
2
2
|
import type { Ruleset } from './ruleset/index.js';
|
|
3
3
|
import type { LocaleSet } from '@markuplint/i18n';
|
|
4
4
|
import type { MLParser, ParserOptions } from '@markuplint/ml-ast';
|
|
5
|
-
import type { Pretender, SeverityOptions } from '@markuplint/ml-config';
|
|
5
|
+
import type { Pretender, RuleCommonSettings, SeverityOptions } from '@markuplint/ml-config';
|
|
6
6
|
import type { ExtendedSpec, MLMLSpec } from '@markuplint/ml-spec';
|
|
7
7
|
/**
|
|
8
8
|
* A tuple of the base HTML/ARIA specification and zero or more
|
|
@@ -19,6 +19,7 @@ export type MLFabric = {
|
|
|
19
19
|
readonly rules: readonly Readonly<AnyMLRule>[];
|
|
20
20
|
readonly locale: LocaleSet;
|
|
21
21
|
readonly schemas: MLSchema;
|
|
22
|
+
readonly ruleCommonSettings: RuleCommonSettings;
|
|
22
23
|
readonly parserOptions: ParserOptions;
|
|
23
24
|
readonly severity: SeverityOptions;
|
|
24
25
|
readonly pretenders: readonly Pretender[];
|
|
@@ -1,29 +1,17 @@
|
|
|
1
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _ViolationCollector_violations, _ViolationCollector_maxCount, _ViolationCollector_locked;
|
|
13
1
|
/**
|
|
14
2
|
* Collects and manages lint violations across multiple files.
|
|
15
3
|
* Supports a maximum violation count to stop collecting early.
|
|
16
4
|
*/
|
|
17
5
|
export class ViolationCollector {
|
|
6
|
+
#violations = [];
|
|
7
|
+
#maxCount = 0;
|
|
8
|
+
#locked = false;
|
|
18
9
|
/**
|
|
19
10
|
* @param maxCount - Maximum number of violations to collect; 0 means unlimited
|
|
20
11
|
*/
|
|
21
12
|
constructor(maxCount = 0) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
_ViolationCollector_locked.set(this, false);
|
|
25
|
-
__classPrivateFieldSet(this, _ViolationCollector_maxCount, maxCount, "f");
|
|
26
|
-
__classPrivateFieldSet(this, _ViolationCollector_locked, false, "f");
|
|
13
|
+
this.#maxCount = maxCount;
|
|
14
|
+
this.#locked = false;
|
|
27
15
|
}
|
|
28
16
|
/**
|
|
29
17
|
* Adds violations associated with a specific file path.
|
|
@@ -34,23 +22,23 @@ export class ViolationCollector {
|
|
|
34
22
|
* @returns The current total number of collected violations
|
|
35
23
|
*/
|
|
36
24
|
pushWithFile(filePath, ...violations) {
|
|
37
|
-
if (
|
|
38
|
-
return
|
|
25
|
+
if (this.#locked) {
|
|
26
|
+
return this.#violations.length;
|
|
39
27
|
}
|
|
40
28
|
for (const violation of violations) {
|
|
41
|
-
|
|
42
|
-
if (
|
|
43
|
-
|
|
29
|
+
this.#violations.push({ ...violation, filePath });
|
|
30
|
+
if (this.#maxCount > 0 && this.#violations.length >= this.#maxCount) {
|
|
31
|
+
this.#locked = true;
|
|
44
32
|
break;
|
|
45
33
|
}
|
|
46
34
|
}
|
|
47
|
-
return
|
|
35
|
+
return this.#violations.length;
|
|
48
36
|
}
|
|
49
37
|
/**
|
|
50
38
|
* The total number of collected violations.
|
|
51
39
|
*/
|
|
52
40
|
get length() {
|
|
53
|
-
return
|
|
41
|
+
return this.#violations.length;
|
|
54
42
|
}
|
|
55
43
|
/**
|
|
56
44
|
* Whether the collector has reached its maximum count and will no longer accept violations.
|
|
@@ -58,7 +46,7 @@ export class ViolationCollector {
|
|
|
58
46
|
* @returns `true` if the collector is locked
|
|
59
47
|
*/
|
|
60
48
|
isLocked() {
|
|
61
|
-
return
|
|
49
|
+
return this.#locked;
|
|
62
50
|
}
|
|
63
51
|
/**
|
|
64
52
|
* Returns a copy of all collected violations as an array.
|
|
@@ -66,7 +54,7 @@ export class ViolationCollector {
|
|
|
66
54
|
* @returns An array of violations with their associated file paths
|
|
67
55
|
*/
|
|
68
56
|
toArray() {
|
|
69
|
-
return [...
|
|
57
|
+
return [...this.#violations];
|
|
70
58
|
}
|
|
71
59
|
/**
|
|
72
60
|
* Groups collected violations by their file path.
|
|
@@ -75,7 +63,7 @@ export class ViolationCollector {
|
|
|
75
63
|
*/
|
|
76
64
|
groupByFile() {
|
|
77
65
|
const grouped = new Map();
|
|
78
|
-
for (const violation of
|
|
66
|
+
for (const violation of this.#violations) {
|
|
79
67
|
const { filePath, ...violationWithoutPath } = violation;
|
|
80
68
|
if (!grouped.has(filePath)) {
|
|
81
69
|
grouped.set(filePath, []);
|
|
@@ -85,4 +73,3 @@ export class ViolationCollector {
|
|
|
85
73
|
return grouped;
|
|
86
74
|
}
|
|
87
75
|
}
|
|
88
|
-
_ViolationCollector_violations = new WeakMap(), _ViolationCollector_maxCount = new WeakMap(), _ViolationCollector_locked = new WeakMap();
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { AnyMLRule } from './ml-rule/index.js';
|
|
2
|
+
import type { Rules, SpecConformance } from '@markuplint/ml-config';
|
|
3
|
+
/**
|
|
4
|
+
* Common shape of a nodeRule/childNodeRule entry that supports naming.
|
|
5
|
+
*/
|
|
6
|
+
type NameableNodeRule = {
|
|
7
|
+
readonly name?: string;
|
|
8
|
+
readonly specConformance?: SpecConformance;
|
|
9
|
+
readonly rules?: {
|
|
10
|
+
readonly [ruleName: string]: unknown;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Result of expanding named nodeRules into virtual rules.
|
|
15
|
+
*/
|
|
16
|
+
export type ExpandResult<T> = {
|
|
17
|
+
/** Virtual MLRule instances created from named nodeRules */
|
|
18
|
+
readonly virtualRules: readonly AnyMLRule[];
|
|
19
|
+
/** NodeRules array with named entries transformed (rules key changed to alias name) */
|
|
20
|
+
readonly transformedNodeRules: readonly T[];
|
|
21
|
+
/** Validation errors encountered during expansion */
|
|
22
|
+
readonly errors: readonly Error[];
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Expands named nodeRules (or childNodeRules) into virtual MLRule instances.
|
|
26
|
+
*
|
|
27
|
+
* Named entries (those with a `name` property containing `/`) are converted into
|
|
28
|
+
* independent virtual rules that reuse the base rule's verify/fix logic but run
|
|
29
|
+
* under their own alias name. This enables per-check control:
|
|
30
|
+
* - Each virtual rule can be independently enabled/disabled via `rules["alias/name"]: false`
|
|
31
|
+
* - Violations report both the base `ruleId` and the alias `name`
|
|
32
|
+
*
|
|
33
|
+
* **false entry separation**: When a named nodeRule has `rules` entries set to `false`,
|
|
34
|
+
* those entries are separated into unnamed nodeRules. This preserves their semantics
|
|
35
|
+
* as base-rule specificity overrides (disabling the base rule on matching nodes)
|
|
36
|
+
* rather than creating virtual rules that would only disable themselves.
|
|
37
|
+
*
|
|
38
|
+
* **Multi-entry support**: When a named nodeRule has 2+ non-false entries, each
|
|
39
|
+
* entry gets its own virtual rule with a derived name (`name/baseRuleName`).
|
|
40
|
+
* A `groupName` is set so the entire group can be disabled via `rules["groupName"]: false`.
|
|
41
|
+
*
|
|
42
|
+
* Unnamed nodeRules pass through unchanged.
|
|
43
|
+
*
|
|
44
|
+
* @param nodeRules - The nodeRules (or childNodeRules) array from the config
|
|
45
|
+
* @param existingRules - All registered MLRule instances (for base rule lookup)
|
|
46
|
+
* @returns Virtual rules, transformed nodeRules, and any validation errors
|
|
47
|
+
*/
|
|
48
|
+
export declare function expandNamedNodeRules<T extends NameableNodeRule>(nodeRules: readonly T[], existingRules: readonly Readonly<AnyMLRule>[]): ExpandResult<T>;
|
|
49
|
+
/**
|
|
50
|
+
* Result of expanding named rule groups in the `rules` section.
|
|
51
|
+
*/
|
|
52
|
+
export type ExpandNamedRulesResult = {
|
|
53
|
+
/** Virtual MLRule instances created from named rule groups */
|
|
54
|
+
readonly virtualRules: readonly AnyMLRule[];
|
|
55
|
+
/** Rules dict with named groups expanded into their virtual rule entries */
|
|
56
|
+
readonly resolvedRules: Rules;
|
|
57
|
+
/** Validation errors encountered during expansion */
|
|
58
|
+
readonly errors: readonly Error[];
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Expands named rule groups in the `rules` section into virtual MLRule instances.
|
|
62
|
+
*
|
|
63
|
+
* Named rule groups (keys containing `/` whose values are {@link NamedRuleGroup} objects)
|
|
64
|
+
* are converted into independent virtual rules, similar to how `expandNamedNodeRules`
|
|
65
|
+
* handles named nodeRules. This enables per-check control at the global rules level.
|
|
66
|
+
*
|
|
67
|
+
* @param rules - The rules dict from the config
|
|
68
|
+
* @param existingRules - All registered MLRule instances (for base rule lookup)
|
|
69
|
+
* @returns Virtual rules, resolved rules dict, and any validation errors
|
|
70
|
+
*/
|
|
71
|
+
export declare function expandNamedRules(rules: Rules, existingRules: readonly Readonly<AnyMLRule>[]): ExpandNamedRulesResult;
|
|
72
|
+
export {};
|