@markuplint/ml-core 5.0.0-alpha.1 → 5.0.0-alpha.3

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 (46) hide show
  1. package/ARCHITECTURE.ja.md +158 -5
  2. package/ARCHITECTURE.md +209 -6
  3. package/CHANGELOG.md +25 -0
  4. package/docs/ml-dom/document.ja.md +7 -14
  5. package/docs/ml-dom/document.md +7 -14
  6. package/docs/ml-dom/element.ja.md +9 -24
  7. package/docs/ml-dom/element.md +9 -24
  8. package/docs/rule-system.ja.md +1 -1
  9. package/docs/rule-system.md +1 -1
  10. package/lib/cursor-offset.d.ts +13 -0
  11. package/lib/cursor-offset.js +34 -0
  12. package/lib/fix-applier.d.ts +32 -0
  13. package/lib/fix-applier.js +75 -0
  14. package/lib/index.d.ts +3 -0
  15. package/lib/index.js +2 -0
  16. package/lib/ml-core.d.ts +53 -8
  17. package/lib/ml-core.js +197 -59
  18. package/lib/ml-dom/helper/get-indent.d.ts +0 -1
  19. package/lib/ml-dom/helper/get-indent.js +5 -18
  20. package/lib/ml-dom/node/attr.d.ts +2 -13
  21. package/lib/ml-dom/node/attr.js +3 -35
  22. package/lib/ml-dom/node/block.js +2 -1
  23. package/lib/ml-dom/node/character-data.d.ts +35 -0
  24. package/lib/ml-dom/node/character-data.js +35 -6
  25. package/lib/ml-dom/node/document.d.ts +2 -27
  26. package/lib/ml-dom/node/document.js +7 -46
  27. package/lib/ml-dom/node/dom-token-list.d.ts +0 -1
  28. package/lib/ml-dom/node/dom-token-list.js +3 -3
  29. package/lib/ml-dom/node/element-close-tag.d.ts +1 -1
  30. package/lib/ml-dom/node/element-close-tag.js +2 -16
  31. package/lib/ml-dom/node/element.d.ts +2 -19
  32. package/lib/ml-dom/node/element.js +3 -65
  33. package/lib/ml-dom/token/token.d.ts +3 -18
  34. package/lib/ml-dom/token/token.js +7 -28
  35. package/lib/ml-rule/index.d.ts +1 -0
  36. package/lib/ml-rule/index.js +1 -0
  37. package/lib/ml-rule/ml-rule-context.d.ts +2 -31
  38. package/lib/ml-rule/ml-rule-context.js +18 -18
  39. package/lib/ml-rule/ml-rule.d.ts +6 -11
  40. package/lib/ml-rule/ml-rule.js +36 -37
  41. package/lib/ml-rule/rule-fixer.d.ts +20 -0
  42. package/lib/ml-rule/rule-fixer.js +38 -0
  43. package/lib/ml-rule/types.d.ts +1 -2
  44. package/lib/test/index.d.ts +1 -10
  45. package/lib/test/index.js +0 -11
  46. package/package.json +12 -12
@@ -81,7 +81,6 @@ export class MLDocument extends MLParentNode {
81
81
  * Rules use this as a fallback when their own options do not specify a value.
82
82
  */
83
83
  ruleCommonSettings;
84
- #tokenList = null;
85
84
  /**
86
85
  * @param ast node list of markuplint AST
87
86
  * @param ruleset ruleset object
@@ -108,9 +107,9 @@ export class MLDocument extends MLParentNode {
108
107
  return createNode(astNode, this);
109
108
  })
110
109
  .filter((n) => !!n));
111
- this._pretending(options?.pretenders);
110
+ this.#pretending(options?.pretenders);
112
111
  try {
113
- this._ruleMapping(ruleset);
112
+ this.#ruleMapping(ruleset);
114
113
  }
115
114
  catch (error) {
116
115
  if (error instanceof InvalidSelectorError) {
@@ -2040,7 +2039,6 @@ export class MLDocument extends MLParentNode {
2040
2039
  * @implements DOM API: `Document`
2041
2040
  */
2042
2041
  getElementById(elementId) {
2043
- // TODO:
2044
2042
  return this.querySelector(`#${elementId}`);
2045
2043
  }
2046
2044
  /**
@@ -2088,27 +2086,6 @@ export class MLDocument extends MLParentNode {
2088
2086
  getSelection() {
2089
2087
  throw new UnexpectedCallError('Not supported "getSelection" method');
2090
2088
  }
2091
- /**
2092
- * Returns a flat, offset-sorted list of all tokens in the document,
2093
- * including element close tags. The result is cached after the first call.
2094
- *
2095
- * @implements `@markuplint/ml-core` API: `MLDocument`
2096
- * @returns A frozen array of tokens sorted by their starting offset
2097
- */
2098
- getTokenList() {
2099
- if (this.#tokenList) {
2100
- return this.#tokenList;
2101
- }
2102
- const tokens = [];
2103
- for (const node of this.nodeList) {
2104
- tokens.push(node);
2105
- if (node.is(node.ELEMENT_NODE) && node.closeTag) {
2106
- tokens.push(node.closeTag);
2107
- }
2108
- }
2109
- this.#tokenList = Object.freeze(tokens.toSorted((a, b) => a.startOffset - b.startOffset));
2110
- return this.#tokenList;
2111
- }
2112
2089
  /**
2113
2090
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
2114
2091
  *
@@ -2250,29 +2227,13 @@ export class MLDocument extends MLParentNode {
2250
2227
  throw new UnexpectedCallError('Not supported "startViewTransition" method');
2251
2228
  }
2252
2229
  /**
2253
- * Returns a string representation of the entire document. When `fixed` is true,
2254
- * returns the document with all lint fixes applied by substituting
2255
- * fixed token content at the appropriate offsets.
2230
+ * Returns the raw string representation of the document.
2256
2231
  *
2257
2232
  * @implements `@markuplint/ml-core` API: `MLDocument`
2258
- * @param fixed - When true, returns the fixed content; otherwise returns the original raw content
2259
2233
  * @returns The string content of the document
2260
2234
  */
2261
- toString(fixed = false) {
2262
- if (!fixed) {
2263
- return this.raw;
2264
- }
2265
- let raw = this.raw;
2266
- let offset = 0;
2267
- for (const node of this.getTokenList()) {
2268
- const nodeRaw = node.toString(true);
2269
- if (nodeRaw === node.raw) {
2270
- continue;
2271
- }
2272
- raw = raw.slice(0, node.startOffset + offset) + nodeRaw + raw.slice(node.endOffset + offset);
2273
- offset += nodeRaw.length - (node.endOffset - node.startOffset);
2274
- }
2275
- return raw;
2235
+ toString() {
2236
+ return this.raw;
2276
2237
  }
2277
2238
  walkOn(type, walker, skipWhenRuleIsDisabled = true) {
2278
2239
  return sequentialWalker(this.nodeList, node => {
@@ -2319,7 +2280,7 @@ export class MLDocument extends MLParentNode {
2319
2280
  *
2320
2281
  * @param pretenders - Optional pretender configurations from the document options
2321
2282
  */
2322
- _pretending(pretenders) {
2283
+ #pretending(pretenders) {
2323
2284
  if (docLog.enabled) {
2324
2285
  docLog('Pretending: %O', pretenders);
2325
2286
  }
@@ -2336,7 +2297,7 @@ export class MLDocument extends MLParentNode {
2336
2297
  *
2337
2298
  * @param ruleset - The ruleset containing rules, nodeRules, and childNodeRules
2338
2299
  */
2339
- _ruleMapping(ruleset) {
2300
+ #ruleMapping(ruleset) {
2340
2301
  if (docLog.enabled) {
2341
2302
  docLog('Rule Mapping: %O', Object.keys(ruleset.rules));
2342
2303
  }
@@ -28,6 +28,5 @@ export declare class MLDomTokenList extends Array<string> implements DOMTokenLis
28
28
  supports(token: string): boolean;
29
29
  toString(): string;
30
30
  toggle(token: string, force?: boolean): boolean;
31
- private _pick;
32
31
  }
33
32
  export {};
@@ -45,7 +45,7 @@ export class MLDomTokenList extends Array {
45
45
  if (!token) {
46
46
  break;
47
47
  }
48
- const loc = this._pick(token, offset);
48
+ const loc = this.#pick(token, offset);
49
49
  if (!loc) {
50
50
  offset = 0;
51
51
  continue;
@@ -74,7 +74,7 @@ export class MLDomTokenList extends Array {
74
74
  * @implements `@markuplint/ml-core` API: `MLDomTokenList`
75
75
  */
76
76
  pick(token) {
77
- const r = this._pick(token);
77
+ const r = this.#pick(token);
78
78
  if (!r) {
79
79
  return null;
80
80
  }
@@ -101,7 +101,7 @@ export class MLDomTokenList extends Array {
101
101
  toggle(token, force) {
102
102
  throw new UnexpectedCallError('Not supported "toggle" method');
103
103
  }
104
- _pick(token, _offset = 0) {
104
+ #pick(token, _offset = 0) {
105
105
  token = token.trim().split(/\s+/)[0] ?? '';
106
106
  if (!token) {
107
107
  return null;
@@ -16,5 +16,5 @@ export declare class MLElementCloseTag<T extends RuleConfigValue, O extends Plai
16
16
  * @implements `@markuplint/ml-core` API: `MLElement`
17
17
  */
18
18
  get rawName(): string;
19
- toString(fixed?: boolean): string;
19
+ toString(): string;
20
20
  }
@@ -23,21 +23,7 @@ export class MLElementCloseTag extends MLNode {
23
23
  get rawName() {
24
24
  return this._astToken.nodeName;
25
25
  }
26
- toString(fixed = false) {
27
- if (!fixed) {
28
- return this.raw;
29
- }
30
- if (this.nodeName.startsWith('#')) {
31
- return this.raw;
32
- }
33
- if (this.pair.isOmitted) {
34
- return this.raw;
35
- }
36
- return [
37
- this.pair.tagOpenChar,
38
- this.pair.tagOpenChar === '' ? '' : '/',
39
- this.pair.fixedNodeName === this.pair.rawName ? this.rawName : this.pair.fixedNodeName,
40
- this.pair.tagCloseChar,
41
- ].join('');
26
+ toString() {
27
+ return this.raw;
42
28
  }
43
29
  }
@@ -632,13 +632,6 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
632
632
  * @implements DOM API: `Element`
633
633
  */
634
634
  get enterKeyHint(): string;
635
- /**
636
- * Returns the fixed (potentially corrected) node name, which may differ from the
637
- * original node name after lint fixes such as case normalization.
638
- *
639
- * @implements `@markuplint/ml-core` API: `MLElement`
640
- */
641
- get fixedNodeName(): string;
642
635
  /**
643
636
  * Whether this element has any spread attributes (e.g., `{...props}` in JSX).
644
637
  */
@@ -1819,13 +1812,6 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
1819
1812
  * @implements DOM API: `Element`
1820
1813
  */
1821
1814
  computedStyleMap(): StylePropertyMapReadOnly;
1822
- /**
1823
- * Overrides the fixed node name for this element, used when the element's
1824
- * tag name needs to be corrected during linting (e.g., case normalization).
1825
- *
1826
- * @param name - The new node name to set
1827
- */
1828
- fixNodeName(name: string): void;
1829
1815
  /**
1830
1816
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
1831
1817
  *
@@ -2252,15 +2238,12 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
2252
2238
  */
2253
2239
  toNormalizeString(): string;
2254
2240
  /**
2255
- * Returns a string representation of this element. When `fixed` is true,
2256
- * returns the element with any lint fixes applied to the tag name,
2257
- * attributes, and embedded comment nodes.
2241
+ * Returns the raw string representation of this element.
2258
2242
  *
2259
2243
  * @implements `@markuplint/ml-core` API: `MLElement`
2260
- * @param fixed - When true, returns the fixed content; otherwise returns the original raw content
2261
2244
  * @returns The string content of this element
2262
2245
  */
2263
- toString(fixed?: boolean): string;
2246
+ toString(): string;
2264
2247
  /**
2265
2248
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
2266
2249
  *
@@ -34,7 +34,6 @@ export class MLElement extends MLParentNode {
34
34
  * - `authored`: Authored element (JSX Element etc.) through the view framework or the template engine.
35
35
  */
36
36
  elementType;
37
- #fixedNodeName;
38
37
  #getChildElementsAndTextNodeWithoutWhitespacesCache = null;
39
38
  /**
40
39
  * Whether this element belongs to a non-HTML namespace (e.g., SVG or MathML).
@@ -123,7 +122,6 @@ export class MLElement extends MLParentNode {
123
122
  this.elementType = astNode.elementType;
124
123
  this.#localName = ns.localName;
125
124
  this.isForeignElement = this.namespaceURI !== HTML_NAMESPACE;
126
- this.#fixedNodeName = astNode.nodeName;
127
125
  this.isOmitted = astNode.isGhost;
128
126
  this.tagOpenChar = astNode.tagOpenChar;
129
127
  this.tagCloseChar = astNode.tagCloseChar;
@@ -850,15 +848,6 @@ export class MLElement extends MLParentNode {
850
848
  get enterKeyHint() {
851
849
  throw new UnexpectedCallError('Not supported "enterKeyHint" property');
852
850
  }
853
- /**
854
- * Returns the fixed (potentially corrected) node name, which may differ from the
855
- * original node name after lint fixes such as case normalization.
856
- *
857
- * @implements `@markuplint/ml-core` API: `MLElement`
858
- */
859
- get fixedNodeName() {
860
- return this.#fixedNodeName;
861
- }
862
851
  /**
863
852
  * Whether this element has any spread attributes (e.g., `{...props}` in JSX).
864
853
  */
@@ -2385,15 +2374,6 @@ export class MLElement extends MLParentNode {
2385
2374
  computedStyleMap() {
2386
2375
  throw new UnexpectedCallError('Not supported "computedStyleMap" method');
2387
2376
  }
2388
- /**
2389
- * Overrides the fixed node name for this element, used when the element's
2390
- * tag name needs to be corrected during linting (e.g., case normalization).
2391
- *
2392
- * @param name - The new node name to set
2393
- */
2394
- fixNodeName(name) {
2395
- this.#fixedNodeName = name;
2396
- }
2397
2377
  /**
2398
2378
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
2399
2379
  *
@@ -2745,7 +2725,6 @@ export class MLElement extends MLParentNode {
2745
2725
  insertAdjacentElement(where,
2746
2726
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
2747
2727
  element) {
2748
- // TODO:
2749
2728
  throw new UnexpectedCallError('Does not implement "insertAdjacentElement" method yet');
2750
2729
  }
2751
2730
  /**
@@ -2756,7 +2735,6 @@ export class MLElement extends MLParentNode {
2756
2735
  * @see https://w3c.github.io/DOM-Parsing/#widl-Element-insertAdjacentHTML-void-DOMString-position-DOMString-text
2757
2736
  */
2758
2737
  insertAdjacentHTML(position, text) {
2759
- // TODO:
2760
2738
  throw new UnexpectedCallError('Does not implement "insertAdjacentHTML" method yet');
2761
2739
  }
2762
2740
  /**
@@ -2767,7 +2745,6 @@ export class MLElement extends MLParentNode {
2767
2745
  * @see https://dom.spec.whatwg.org/#dom-element-insertadjacenttext
2768
2746
  */
2769
2747
  insertAdjacentText(where, data) {
2770
- // TODO:
2771
2748
  throw new UnexpectedCallError('Does not implement "insertAdjacentText" method yet');
2772
2749
  }
2773
2750
  /**
@@ -3176,52 +3153,13 @@ export class MLElement extends MLParentNode {
3176
3153
  return normalizedString;
3177
3154
  }
3178
3155
  /**
3179
- * Returns a string representation of this element. When `fixed` is true,
3180
- * returns the element with any lint fixes applied to the tag name,
3181
- * attributes, and embedded comment nodes.
3156
+ * Returns the raw string representation of this element.
3182
3157
  *
3183
3158
  * @implements `@markuplint/ml-core` API: `MLElement`
3184
- * @param fixed - When true, returns the fixed content; otherwise returns the original raw content
3185
3159
  * @returns The string content of this element
3186
3160
  */
3187
- toString(fixed = false) {
3188
- if (!fixed) {
3189
- return this.raw;
3190
- }
3191
- if (this.pretenderContext?.type === 'pretender') {
3192
- return this.raw;
3193
- }
3194
- if (this.nodeName.startsWith('#')) {
3195
- return this.raw;
3196
- }
3197
- if (this.isOmitted) {
3198
- return this.raw;
3199
- }
3200
- let raw = this.raw;
3201
- let offset = 0;
3202
- const overriddenCommentNodes = this.ownerMLDocument.nodeList.filter(node => {
3203
- if (node.is(node.COMMENT_NODE)) {
3204
- return this.startOffset < node.startOffset && node.endOffset < this.endOffset;
3205
- }
3206
- return false;
3207
- });
3208
- const nodes = [
3209
- {
3210
- toString: () => this.tagOpenChar + this.fixedNodeName,
3211
- startOffset: this.startOffset,
3212
- endOffset: this.startOffset + this.tagOpenChar.length + this.nodeName.length,
3213
- },
3214
- ...overriddenCommentNodes,
3215
- ...this.attributes,
3216
- ];
3217
- for (const node of nodes) {
3218
- const before = raw.slice(0, node.startOffset + offset - this.startOffset);
3219
- const rawCode = node.toString(true);
3220
- const after = raw.slice(node.endOffset + offset - this.startOffset);
3221
- raw = before + rawCode + after;
3222
- offset += rawCode.length - (node.endOffset - node.startOffset);
3223
- }
3224
- return raw;
3161
+ toString() {
3162
+ return this.raw;
3225
3163
  }
3226
3164
  /**
3227
3165
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
@@ -2,7 +2,7 @@ import type { MLASTToken } from '@markuplint/ml-ast';
2
2
  /**
3
3
  * Represents a single token in the markuplint AST.
4
4
  * Wraps an AST token with positional information (line, column, offset)
5
- * and provides both raw and fixed string representations.
5
+ * and provides the raw string representation.
6
6
  *
7
7
  * @template A - The AST token type this token wraps
8
8
  */
@@ -40,12 +40,6 @@ export declare class MLToken<A extends MLASTToken = MLASTToken> {
40
40
  * @implements `@markuplint/ml-core` API: `MLDOMToken`
41
41
  */
42
42
  get endOffset(): number;
43
- /**
44
- * The fixed (potentially modified) string content of this token.
45
- *
46
- * @implements `@markuplint/ml-core` API: `MLDOMToken`
47
- */
48
- get fixed(): string;
49
43
  /**
50
44
  * The original raw string content of this token from the source.
51
45
  *
@@ -71,19 +65,10 @@ export declare class MLToken<A extends MLASTToken = MLASTToken> {
71
65
  */
72
66
  get startOffset(): number;
73
67
  /**
74
- * Replaces the fixed content of this token with the given string,
75
- * used when applying lint fixes.
76
- *
77
- * @implements `@markuplint/ml-core` API: `MLDOMToken`
78
- * @param raw - The new string content to set as the fixed value
79
- */
80
- fix(raw: string): void;
81
- /**
82
- * Returns the string representation of this token.
68
+ * Returns the raw string representation of this token.
83
69
  *
84
70
  * @implements `@markuplint/ml-core` API: `MLDOMToken`
85
- * @param fixed - When true, returns the fixed content; otherwise returns the original raw content
86
71
  * @returns The string content of this token
87
72
  */
88
- toString(fixed?: boolean): string;
73
+ toString(): string;
89
74
  }
@@ -2,12 +2,11 @@ import { getEndCol, getEndLine } from '@markuplint/parser-utils/location';
2
2
  /**
3
3
  * Represents a single token in the markuplint AST.
4
4
  * Wraps an AST token with positional information (line, column, offset)
5
- * and provides both raw and fixed string representations.
5
+ * and provides the raw string representation.
6
6
  *
7
7
  * @template A - The AST token type this token wraps
8
8
  */
9
9
  export class MLToken {
10
- #fixed;
11
10
  #raw;
12
11
  /**
13
12
  * The unique identifier for this token.
@@ -25,7 +24,6 @@ export class MLToken {
25
24
  constructor(astToken) {
26
25
  this._astToken = astToken;
27
26
  this.#raw = astToken.raw;
28
- this.#fixed = astToken.raw;
29
27
  this.uuid = astToken.uuid;
30
28
  }
31
29
  /**
@@ -34,7 +32,7 @@ export class MLToken {
34
32
  * @implements `@markuplint/ml-core` API: `MLDOMToken`
35
33
  */
36
34
  get endCol() {
37
- return getEndCol(this.fixed, this.startCol);
35
+ return getEndCol(this.raw, this.startCol);
38
36
  }
39
37
  /**
40
38
  * The ending line number (1-based) of this token in the source.
@@ -42,7 +40,7 @@ export class MLToken {
42
40
  * @implements `@markuplint/ml-core` API: `MLDOMToken`
43
41
  */
44
42
  get endLine() {
45
- return getEndLine(this.fixed, this.startLine);
43
+ return getEndLine(this.raw, this.startLine);
46
44
  }
47
45
  /**
48
46
  * The ending character offset (0-based) of this token in the source.
@@ -50,15 +48,7 @@ export class MLToken {
50
48
  * @implements `@markuplint/ml-core` API: `MLDOMToken`
51
49
  */
52
50
  get endOffset() {
53
- return this.startOffset + this.fixed.length;
54
- }
55
- /**
56
- * The fixed (potentially modified) string content of this token.
57
- *
58
- * @implements `@markuplint/ml-core` API: `MLDOMToken`
59
- */
60
- get fixed() {
61
- return this.#fixed;
51
+ return this.startOffset + this.raw.length;
62
52
  }
63
53
  /**
64
54
  * The original raw string content of this token from the source.
@@ -93,23 +83,12 @@ export class MLToken {
93
83
  return this._astToken.offset;
94
84
  }
95
85
  /**
96
- * Replaces the fixed content of this token with the given string,
97
- * used when applying lint fixes.
86
+ * Returns the raw string representation of this token.
98
87
  *
99
88
  * @implements `@markuplint/ml-core` API: `MLDOMToken`
100
- * @param raw - The new string content to set as the fixed value
101
- */
102
- fix(raw) {
103
- this.#fixed = raw;
104
- }
105
- /**
106
- * Returns the string representation of this token.
107
- *
108
- * @implements `@markuplint/ml-core` API: `MLDOMToken`
109
- * @param fixed - When true, returns the fixed content; otherwise returns the original raw content
110
89
  * @returns The string content of this token
111
90
  */
112
- toString(fixed = false) {
113
- return fixed ? this.#fixed : this.#raw;
91
+ toString() {
92
+ return this.#raw;
114
93
  }
115
94
  }
@@ -1,3 +1,4 @@
1
1
  export * from './create-rule.js';
2
2
  export * from './ml-rule.js';
3
+ export * from './rule-fixer.js';
3
4
  export * from './types.js';
@@ -1,3 +1,4 @@
1
1
  export * from './create-rule.js';
2
2
  export * from './ml-rule.js';
3
+ export * from './rule-fixer.js';
3
4
  export * from './types.js';
@@ -8,40 +8,12 @@ export declare class MLRuleContext<T extends RuleConfigValue, O extends PlainDat
8
8
  readonly locale: string;
9
9
  readonly translate: Translator;
10
10
  constructor(document: MLDocument<T, O>, locale: LocaleSet);
11
- get reports(): ({
12
- message: string;
13
- line: number;
14
- col: number;
15
- raw: string;
16
- } | {
17
- message: string;
18
- scope: import("@markuplint/ml-config").Scope<T, O>;
19
- } | {
20
- message: string;
21
- scope: import("@markuplint/ml-config").Scope<T, O>;
22
- line: number;
23
- col: number;
24
- raw: string;
25
- })[];
11
+ get reports(): Report<T, O>[];
26
12
  provide(): {
27
13
  document: MLDocument<T, O>;
28
14
  translate: Translator;
29
15
  t: Translator;
30
- reports: ({
31
- message: string;
32
- line: number;
33
- col: number;
34
- raw: string;
35
- } | {
36
- message: string;
37
- scope: import("@markuplint/ml-config").Scope<T, O>;
38
- } | {
39
- message: string;
40
- scope: import("@markuplint/ml-config").Scope<T, O>;
41
- line: number;
42
- col: number;
43
- raw: string;
44
- })[];
16
+ reports: Report<T, O>[];
45
17
  report: {
46
18
  (report: Report<T, O>): undefined;
47
19
  (report: CheckerReport<T, O>): boolean;
@@ -49,5 +21,4 @@ export declare class MLRuleContext<T extends RuleConfigValue, O extends PlainDat
49
21
  };
50
22
  report(report: Report<T, O>): undefined;
51
23
  report(report: CheckerReport<T, O>): boolean;
52
- private _push;
53
24
  }
@@ -3,6 +3,7 @@ export class MLRuleContext {
3
3
  document;
4
4
  locale;
5
5
  #reports = [];
6
+ #reportKeys = new Set();
6
7
  translate;
7
8
  constructor(
8
9
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
@@ -12,10 +13,7 @@ export class MLRuleContext {
12
13
  this.locale = locale.locale;
13
14
  }
14
15
  get reports() {
15
- return this.#reports.map(report => ({
16
- ...report,
17
- message: finish(report.message, this.locale),
18
- }));
16
+ return this.#reports;
19
17
  }
20
18
  provide() {
21
19
  return {
@@ -30,16 +28,21 @@ export class MLRuleContext {
30
28
  if (typeof report === 'function') {
31
29
  const r = report(this.translate);
32
30
  if (r) {
33
- this._push(r);
31
+ this.#push(r);
34
32
  return true;
35
33
  }
36
34
  return false;
37
35
  }
38
- this._push(report);
36
+ this.#push(report);
39
37
  }
40
- _push(report) {
41
- if (!this.#reports.some(r => is(r, report))) {
42
- this.#reports.push(report);
38
+ #push(report) {
39
+ const key = reportKey(report);
40
+ if (!this.#reportKeys.has(key)) {
41
+ this.#reportKeys.add(key);
42
+ this.#reports.push({
43
+ ...report,
44
+ message: finish(report.message, this.locale),
45
+ });
43
46
  }
44
47
  }
45
48
  }
@@ -51,15 +54,12 @@ function finish(message, locale = 'en') {
51
54
  }
52
55
  return message;
53
56
  }
54
- function is(r1, r2) {
55
- if ('col' in r1 && 'col' in r2) {
56
- return r1.col === r2.col && r1.line === r2.line && r1.message === r2.message && r1.raw === r2.raw;
57
+ function reportKey(report) {
58
+ if ('col' in report && report.col != null) {
59
+ return `${report.line}:${report.col}:${report.message}:${report.raw}`;
57
60
  }
58
- if ('scope' in r1) {
59
- if (!('scope' in r2)) {
60
- return false;
61
- }
62
- return r1.scope === r2.scope && r1.message === r2.message;
61
+ if ('scope' in report) {
62
+ return `${report.scope.startLine}:${report.scope.startCol}:${report.message}`;
63
63
  }
64
- return false;
64
+ return report.message;
65
65
  }
@@ -12,7 +12,7 @@ import type { GlobalRuleInfo, SpecConformance, PlainData, Rule, RuleConfigValue,
12
12
  export declare class MLRule<T extends RuleConfigValue, O extends PlainData = undefined> {
13
13
  #private;
14
14
  /**
15
- * For virtual rules, the name of the base rule whose verify/fix logic is reused.
15
+ * For virtual rules, the name of the base rule whose verify logic is reused.
16
16
  * When set, violations report this as `ruleId` for backwards compatibility.
17
17
  */
18
18
  readonly baseRuleId?: string;
@@ -37,24 +37,19 @@ export declare class MLRule<T extends RuleConfigValue, O extends PlainData = und
37
37
  readonly groupName?: string;
38
38
  });
39
39
  /**
40
- * Creates a virtual rule that reuses this rule's verify/fix logic
40
+ * Creates a virtual rule that reuses this rule's verify logic
41
41
  * under a different name (alias). Used by named nodeRules to produce
42
42
  * independent rule instances that can be individually configured.
43
43
  *
44
44
  * @param aliasName - The alias name (must contain `/`)
45
45
  * @param options - Override options for the virtual rule
46
- * @returns A new MLRule instance sharing the same verify/fix logic
46
+ * @returns A new MLRule instance sharing the same verify logic
47
47
  */
48
48
  createAlias(aliasName: string, options?: {
49
49
  readonly defaultSeverity?: Severity;
50
50
  readonly specConformance?: SpecConformance;
51
51
  readonly groupName?: string;
52
52
  }): MLRule<T, O>;
53
- /**
54
- * The following getter is unused internally,
55
- * only for extending from 3rd party library
56
- */
57
- protected get f(): RuleSeed<T, O>['fix'];
58
53
  /**
59
54
  * The following getter is unused internally,
60
55
  * only for extending from 3rd party library
@@ -78,16 +73,16 @@ export declare class MLRule<T extends RuleConfigValue, O extends PlainData = und
78
73
  */
79
74
  optimizeOption(configSettings: Rule<T, O> | null | undefined): RuleInfo<T, O>;
80
75
  /**
81
- * Executes this rule's verify (and optionally fix) function against a document,
76
+ * Executes this rule's verify function against a document,
82
77
  * then collects and returns the resulting violations.
78
+ * When `fix` is true, fix callbacks on reports are executed to produce {@link FixData}.
83
79
  *
84
80
  * @param document - The parsed document to verify
85
81
  * @param locale - The locale set for translating violation messages
86
- * @param fix - Whether to also run the fix function
82
+ * @param fix - Whether to execute fix callbacks and attach FixData to violations
87
83
  * @returns An array of violations found by this rule
88
84
  */
89
85
  verify(document: MLDocument<T, O>, locale: LocaleSet, fix: boolean): Promise<Violation[]>;
90
- private _optimize;
91
86
  }
92
87
  /**
93
88
  * An MLRule with any value and option types. Used when the specific types are not known.