@markuplint/ml-core 4.13.2 → 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.
Files changed (92) hide show
  1. package/ARCHITECTURE.ja.md +524 -0
  2. package/ARCHITECTURE.md +524 -0
  3. package/CHANGELOG.md +52 -2
  4. package/README.md +5 -0
  5. package/SKILL.md +61 -0
  6. package/docs/linting-pipeline.ja.md +307 -0
  7. package/docs/linting-pipeline.md +307 -0
  8. package/docs/maintenance.ja.md +210 -0
  9. package/docs/maintenance.md +210 -0
  10. package/docs/ml-dom/attr.ja.md +103 -0
  11. package/docs/ml-dom/attr.md +103 -0
  12. package/docs/ml-dom/block.ja.md +272 -0
  13. package/docs/ml-dom/block.md +272 -0
  14. package/docs/ml-dom/document.ja.md +141 -0
  15. package/docs/ml-dom/document.md +141 -0
  16. package/docs/ml-dom/element.ja.md +176 -0
  17. package/docs/ml-dom/element.md +176 -0
  18. package/docs/ml-dom/helpers.ja.md +203 -0
  19. package/docs/ml-dom/helpers.md +203 -0
  20. package/docs/ml-dom/node.ja.md +199 -0
  21. package/docs/ml-dom/node.md +199 -0
  22. package/docs/ml-dom/others.ja.md +120 -0
  23. package/docs/ml-dom/others.md +120 -0
  24. package/docs/ml-dom/overview.ja.md +102 -0
  25. package/docs/ml-dom/overview.md +102 -0
  26. package/docs/ml-dom/pretender.ja.md +269 -0
  27. package/docs/ml-dom/pretender.md +269 -0
  28. package/docs/ml-dom/rule-mapping.ja.md +371 -0
  29. package/docs/ml-dom/rule-mapping.md +371 -0
  30. package/docs/ml-dom.ja.md +18 -0
  31. package/docs/ml-dom.md +18 -0
  32. package/docs/rule-system.ja.md +287 -0
  33. package/docs/rule-system.md +287 -0
  34. package/lib/convert-ruleset.d.ts +7 -0
  35. package/lib/convert-ruleset.js +7 -0
  36. package/lib/debug.d.ts +4 -0
  37. package/lib/debug.js +4 -0
  38. package/lib/index.d.ts +4 -3
  39. package/lib/index.js +1 -1
  40. package/lib/ml-core.d.ts +37 -1
  41. package/lib/ml-core.js +171 -82
  42. package/lib/ml-dom/helper/accname.d.ts +8 -0
  43. package/lib/ml-dom/helper/accname.js +71 -55
  44. package/lib/ml-dom/helper/create-node.js +1 -0
  45. package/lib/ml-dom/helper/get-indent.d.ts +4 -1
  46. package/lib/ml-dom/helper/get-indent.js +21 -30
  47. package/lib/ml-dom/node/attr.d.ts +65 -4
  48. package/lib/ml-dom/node/attr.js +151 -53
  49. package/lib/ml-dom/node/block.d.ts +23 -2
  50. package/lib/ml-dom/node/block.js +24 -1
  51. package/lib/ml-dom/node/child-node.d.ts +9 -0
  52. package/lib/ml-dom/node/child-node.js +9 -0
  53. package/lib/ml-dom/node/comment.d.ts +7 -0
  54. package/lib/ml-dom/node/comment.js +7 -0
  55. package/lib/ml-dom/node/document-fragment.d.ts +8 -0
  56. package/lib/ml-dom/node/document-fragment.js +8 -0
  57. package/lib/ml-dom/node/document-type.d.ts +22 -0
  58. package/lib/ml-dom/node/document-type.js +25 -0
  59. package/lib/ml-dom/node/document.d.ts +88 -7
  60. package/lib/ml-dom/node/document.js +128 -32
  61. package/lib/ml-dom/node/dom-token-list.js +17 -30
  62. package/lib/ml-dom/node/element-close-tag.js +1 -0
  63. package/lib/ml-dom/node/element.d.ts +151 -5
  64. package/lib/ml-dom/node/element.js +242 -50
  65. package/lib/ml-dom/node/node-store.js +6 -15
  66. package/lib/ml-dom/node/node.d.ts +19 -1
  67. package/lib/ml-dom/node/node.js +175 -166
  68. package/lib/ml-dom/node/parent-node.js +14 -30
  69. package/lib/ml-dom/node/rule-mapper.js +7 -20
  70. package/lib/ml-dom/node/text.d.ts +19 -0
  71. package/lib/ml-dom/node/text.js +21 -0
  72. package/lib/ml-dom/node/types.d.ts +68 -0
  73. package/lib/ml-dom/token/token.d.ts +42 -0
  74. package/lib/ml-dom/token/token.js +59 -39
  75. package/lib/ml-rule/create-rule.d.ts +17 -1
  76. package/lib/ml-rule/ml-rule-context.js +7 -11
  77. package/lib/ml-rule/ml-rule.d.ts +66 -1
  78. package/lib/ml-rule/ml-rule.js +95 -25
  79. package/lib/ml-rule/types.d.ts +41 -0
  80. package/lib/plugin/plugin.d.ts +8 -0
  81. package/lib/plugin/plugin.js +8 -0
  82. package/lib/plugin/types.d.ts +21 -0
  83. package/lib/ruleset/index.d.ts +10 -0
  84. package/lib/ruleset/index.js +13 -0
  85. package/lib/test/index.d.ts +42 -1
  86. package/lib/test/index.js +39 -2
  87. package/lib/types.d.ts +10 -1
  88. package/lib/violation-collector.d.ts +33 -0
  89. package/lib/violation-collector.js +48 -28
  90. package/lib/virtual-rule.d.ts +72 -0
  91. package/lib/virtual-rule.js +233 -0
  92. package/package.json +16 -13
package/lib/ml-core.d.ts CHANGED
@@ -2,17 +2,53 @@ import type { MLFabric } from './types.js';
2
2
  import type { PlainData, RuleConfigValue, Violation } from '@markuplint/ml-config';
3
3
  import { ParserError } from '@markuplint/parser-utils';
4
4
  import { Document } from './ml-dom/index.js';
5
+ /**
6
+ * Parameters for constructing an {@link MLCore} instance.
7
+ * Extends {@link MLFabric} with the source code, filename, and debug flag.
8
+ */
5
9
  export type MLCoreParams = {
10
+ /** The markup source code to lint */
6
11
  readonly sourceCode: string;
12
+ /** The filename associated with the source code */
7
13
  readonly filename: string;
14
+ /** Whether to enable debug logging */
8
15
  readonly debug?: boolean;
9
16
  } & MLFabric;
17
+ /**
18
+ * The core linting engine for markuplint.
19
+ *
20
+ * Parses markup source code into an AST, constructs a DOM document,
21
+ * and verifies it against configured rules to produce violations.
22
+ */
10
23
  export declare class MLCore {
11
24
  #private;
12
- constructor({ parser, sourceCode, ruleset, rules, locale, schemas, parserOptions, severity, pretenders, filename, debug, configErrors, }: MLCoreParams);
25
+ constructor({ parser, sourceCode, ruleset, rules, locale, schemas, ruleCommonSettings, parserOptions, severity, pretenders, filename, debug, configErrors, }: MLCoreParams);
26
+ /**
27
+ * The parsed document, or a {@link ParserError} if parsing failed.
28
+ */
13
29
  get document(): ParserError | Document<RuleConfigValue, PlainData>;
30
+ /**
31
+ * Replaces the source code and re-parses the document.
32
+ *
33
+ * @param sourceCode - The new markup source code
34
+ */
14
35
  setCode(sourceCode: string): void;
36
+ /**
37
+ * Updates the linting configuration and re-creates the document.
38
+ * Only re-parses if parser options have changed.
39
+ *
40
+ * @param fabric - Partial fabric with the properties to update
41
+ */
15
42
  update({ parser, ruleset, rules, locale, schemas, parserOptions, configErrors }: Partial<MLFabric>): void;
43
+ /**
44
+ * Runs all configured rules against the parsed document and returns violations.
45
+ *
46
+ * If the document failed to parse, a single parse-error violation is returned
47
+ * (unless parse errors are suppressed via severity options).
48
+ *
49
+ * @param fix - Whether to attempt auto-fixing violations
50
+ * @returns An array of violations found during verification
51
+ */
16
52
  verify(fix?: boolean): Promise<Violation[]>;
17
53
  private _createDocument;
18
54
  private _createParseError;
package/lib/ml-core.js CHANGED
@@ -1,100 +1,169 @@
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 _MLCore_ast, _MLCore_document, _MLCore_filename, _MLCore_locale, _MLCore_parser, _MLCore_parserOptions, _MLCore_severity, _MLCore_pretenders, _MLCore_rules, _MLCore_ruleset, _MLCore_schemas, _MLCore_sourceCode, _MLCore_configErrors;
13
1
  import { ParserError } from '@markuplint/parser-utils';
14
2
  import { log, enableDebug } from './debug.js';
15
3
  import { Document } from './ml-dom/index.js';
4
+ import { expandNamedNodeRules, expandNamedRules } from './virtual-rule.js';
16
5
  const resultLog = log.extend('result');
6
+ /**
7
+ * The core linting engine for markuplint.
8
+ *
9
+ * Parses markup source code into an AST, constructs a DOM document,
10
+ * and verifies it against configured rules to produce violations.
11
+ */
17
12
  export class MLCore {
18
- constructor({ parser, sourceCode, ruleset, rules, locale, schemas, parserOptions, severity, pretenders, filename, debug, configErrors, }) {
19
- _MLCore_ast.set(this, null);
20
- _MLCore_document.set(this, void 0);
21
- _MLCore_filename.set(this, void 0);
22
- _MLCore_locale.set(this, void 0);
23
- _MLCore_parser.set(this, void 0);
24
- _MLCore_parserOptions.set(this, void 0);
25
- _MLCore_severity.set(this, void 0);
26
- _MLCore_pretenders.set(this, void 0);
27
- _MLCore_rules.set(this, void 0);
28
- _MLCore_ruleset.set(this, void 0);
29
- _MLCore_schemas.set(this, void 0);
30
- _MLCore_sourceCode.set(this, void 0);
31
- _MLCore_configErrors.set(this, void 0);
13
+ #ast = null;
14
+ #document;
15
+ #filename;
16
+ #locale;
17
+ #parser;
18
+ #parserOptions;
19
+ #severity;
20
+ #pretenders;
21
+ #rules;
22
+ #ruleset;
23
+ #schemas;
24
+ #ruleCommonSettings;
25
+ #sourceCode;
26
+ #configErrors;
27
+ /**
28
+ * Pre-expansion nodeRules preserved for hot-reload.
29
+ * When `update()` is called without a new ruleset, these are used as the
30
+ * source for `expandNamedNodeRules()` instead of the already-transformed
31
+ * `#ruleset.nodeRules` (which has alias keys and no `name` property).
32
+ */
33
+ #originalNodeRules;
34
+ #originalChildNodeRules;
35
+ /**
36
+ * Pre-computed namespace prefixes from wildcard disable entries.
37
+ * e.g., `rules["a11y/*"]: false` yields `"a11y/"`.
38
+ */
39
+ #disabledNamespaces;
40
+ constructor({ parser, sourceCode, ruleset, rules, locale, schemas, ruleCommonSettings, parserOptions, severity, pretenders, filename, debug, configErrors, }) {
32
41
  if (debug) {
33
42
  enableDebug();
34
43
  }
35
- __classPrivateFieldSet(this, _MLCore_parser, parser, "f");
36
- __classPrivateFieldSet(this, _MLCore_sourceCode, sourceCode, "f");
37
- __classPrivateFieldSet(this, _MLCore_parserOptions, parserOptions, "f");
38
- __classPrivateFieldSet(this, _MLCore_ruleset, {
39
- rules: ruleset.rules ?? {},
40
- nodeRules: ruleset.nodeRules ?? [],
41
- childNodeRules: ruleset.childNodeRules ?? [],
42
- }, "f");
43
- __classPrivateFieldSet(this, _MLCore_locale, locale, "f");
44
- __classPrivateFieldSet(this, _MLCore_schemas, schemas, "f");
45
- __classPrivateFieldSet(this, _MLCore_filename, filename, "f");
46
- __classPrivateFieldSet(this, _MLCore_rules, [...rules], "f");
47
- __classPrivateFieldSet(this, _MLCore_severity, severity, "f");
48
- __classPrivateFieldSet(this, _MLCore_pretenders, [...pretenders], "f");
49
- __classPrivateFieldSet(this, _MLCore_configErrors, [...(configErrors ?? [])], "f");
44
+ this.#parser = parser;
45
+ this.#sourceCode = sourceCode;
46
+ this.#parserOptions = parserOptions;
47
+ this.#locale = locale;
48
+ this.#schemas = schemas;
49
+ this.#ruleCommonSettings = ruleCommonSettings;
50
+ this.#filename = filename;
51
+ this.#severity = severity;
52
+ this.#pretenders = [...pretenders];
53
+ this.#configErrors = [...(configErrors ?? [])];
54
+ // Preserve pre-expansion nodeRules for hot-reload
55
+ this.#originalNodeRules = ruleset.nodeRules ?? [];
56
+ this.#originalChildNodeRules = ruleset.childNodeRules ?? [];
57
+ // Expand named rule groups in the rules section
58
+ const namedRulesResult = expandNamedRules(ruleset.rules ?? {}, rules);
59
+ // Expand named nodeRules into virtual rules (using expanded rules as base)
60
+ const allRulesForExpansion = [...rules, ...namedRulesResult.virtualRules];
61
+ const nodeRuleResult = expandNamedNodeRules(this.#originalNodeRules, allRulesForExpansion);
62
+ const childNodeRuleResult = expandNamedNodeRules(this.#originalChildNodeRules, allRulesForExpansion);
63
+ const resolvedRules = namedRulesResult.resolvedRules;
64
+ this.#rules = [...allRulesForExpansion, ...nodeRuleResult.virtualRules, ...childNodeRuleResult.virtualRules];
65
+ this.#ruleset = {
66
+ rules: resolvedRules,
67
+ nodeRules: nodeRuleResult.transformedNodeRules,
68
+ childNodeRules: childNodeRuleResult.transformedNodeRules,
69
+ };
70
+ this.#disabledNamespaces = extractDisabledNamespaces(resolvedRules);
71
+ this.#configErrors.push(...namedRulesResult.errors, ...nodeRuleResult.errors, ...childNodeRuleResult.errors);
50
72
  this._parse();
51
73
  this._createDocument();
52
74
  }
75
+ /**
76
+ * The parsed document, or a {@link ParserError} if parsing failed.
77
+ */
53
78
  get document() {
54
- return __classPrivateFieldGet(this, _MLCore_document, "f");
79
+ return this.#document;
55
80
  }
81
+ /**
82
+ * Replaces the source code and re-parses the document.
83
+ *
84
+ * @param sourceCode - The new markup source code
85
+ */
56
86
  setCode(sourceCode) {
57
- __classPrivateFieldSet(this, _MLCore_sourceCode, sourceCode, "f");
87
+ this.#sourceCode = sourceCode;
58
88
  this._parse();
59
89
  this._createDocument();
60
90
  }
91
+ /**
92
+ * Updates the linting configuration and re-creates the document.
93
+ * Only re-parses if parser options have changed.
94
+ *
95
+ * @param fabric - Partial fabric with the properties to update
96
+ */
61
97
  update({ parser, ruleset, rules, locale, schemas, parserOptions, configErrors }) {
62
- __classPrivateFieldSet(this, _MLCore_parser, parser ?? __classPrivateFieldGet(this, _MLCore_parser, "f"), "f");
63
- __classPrivateFieldSet(this, _MLCore_ruleset, {
64
- rules: ruleset?.rules ?? __classPrivateFieldGet(this, _MLCore_ruleset, "f").rules,
65
- nodeRules: ruleset?.nodeRules ?? __classPrivateFieldGet(this, _MLCore_ruleset, "f").nodeRules,
66
- childNodeRules: ruleset?.childNodeRules ?? __classPrivateFieldGet(this, _MLCore_ruleset, "f").childNodeRules,
67
- }, "f");
68
- __classPrivateFieldSet(this, _MLCore_rules, rules?.slice() ?? __classPrivateFieldGet(this, _MLCore_rules, "f"), "f");
69
- __classPrivateFieldSet(this, _MLCore_locale, locale ?? __classPrivateFieldGet(this, _MLCore_locale, "f"), "f");
70
- __classPrivateFieldSet(this, _MLCore_schemas, schemas ?? __classPrivateFieldGet(this, _MLCore_schemas, "f"), "f");
71
- __classPrivateFieldSet(this, _MLCore_configErrors, [...(configErrors ?? [])], "f");
98
+ this.#parser = parser ?? this.#parser;
99
+ this.#locale = locale ?? this.#locale;
100
+ this.#schemas = schemas ?? this.#schemas;
101
+ this.#configErrors = [...(configErrors ?? [])];
102
+ const baseRules = rules?.slice() ?? this.#rules.filter(r => !r.baseRuleId);
103
+ // Use pre-expansion originals as fallback when ruleset is not provided
104
+ const incomingNodeRules = ruleset?.nodeRules ?? this.#originalNodeRules;
105
+ const incomingChildNodeRules = ruleset?.childNodeRules ?? this.#originalChildNodeRules;
106
+ // Expand named rule groups in the rules section
107
+ const incomingRules = ruleset?.rules ?? this.#ruleset.rules;
108
+ const namedRulesResult = expandNamedRules(incomingRules, baseRules);
109
+ const allRulesForExpansion = [...baseRules, ...namedRulesResult.virtualRules];
110
+ const nodeRuleResult = expandNamedNodeRules(incomingNodeRules, allRulesForExpansion);
111
+ const childNodeRuleResult = expandNamedNodeRules(incomingChildNodeRules, allRulesForExpansion);
112
+ // Update originals if new data was provided
113
+ if (ruleset?.nodeRules) {
114
+ this.#originalNodeRules = ruleset.nodeRules;
115
+ }
116
+ if (ruleset?.childNodeRules) {
117
+ this.#originalChildNodeRules = ruleset.childNodeRules;
118
+ }
119
+ const resolvedRules = namedRulesResult.resolvedRules;
120
+ this.#rules = [...allRulesForExpansion, ...nodeRuleResult.virtualRules, ...childNodeRuleResult.virtualRules];
121
+ this.#ruleset = {
122
+ rules: resolvedRules,
123
+ nodeRules: nodeRuleResult.transformedNodeRules,
124
+ childNodeRules: childNodeRuleResult.transformedNodeRules,
125
+ };
126
+ this.#disabledNamespaces = extractDisabledNamespaces(resolvedRules);
127
+ this.#configErrors.push(...namedRulesResult.errors, ...nodeRuleResult.errors, ...childNodeRuleResult.errors);
72
128
  if (parserOptions &&
73
- (parserOptions.ignoreFrontMatter !== __classPrivateFieldGet(this, _MLCore_parserOptions, "f").ignoreFrontMatter ||
74
- parserOptions.authoredElementName !== __classPrivateFieldGet(this, _MLCore_parserOptions, "f").authoredElementName)) {
129
+ (parserOptions.ignoreFrontMatter !== this.#parserOptions.ignoreFrontMatter ||
130
+ parserOptions.authoredElementName !== this.#parserOptions.authoredElementName)) {
75
131
  this._parse();
76
132
  }
77
133
  this._createDocument();
78
134
  }
135
+ /**
136
+ * Runs all configured rules against the parsed document and returns violations.
137
+ *
138
+ * If the document failed to parse, a single parse-error violation is returned
139
+ * (unless parse errors are suppressed via severity options).
140
+ *
141
+ * @param fix - Whether to attempt auto-fixing violations
142
+ * @returns An array of violations found during verification
143
+ */
79
144
  async verify(fix = false) {
80
145
  log('verify: start');
81
146
  const violations = [];
82
- if (__classPrivateFieldGet(this, _MLCore_document, "f") instanceof ParserError) {
83
- const parseError = this._createParseError(__classPrivateFieldGet(this, _MLCore_document, "f").message, __classPrivateFieldGet(this, _MLCore_document, "f").line, __classPrivateFieldGet(this, _MLCore_document, "f").col, __classPrivateFieldGet(this, _MLCore_document, "f").raw);
147
+ if (this.#document instanceof ParserError) {
148
+ const parseError = this._createParseError(this.#document.message, this.#document.line, this.#document.col, this.#document.raw);
84
149
  if (!parseError) {
85
150
  return [];
86
151
  }
87
152
  violations.push(parseError);
88
- log('verify: error %o', __classPrivateFieldGet(this, _MLCore_document, "f").message);
153
+ log('verify: error %o', this.#document.message);
89
154
  return violations;
90
155
  }
91
- const definedRuleName = new Set(__classPrivateFieldGet(this, _MLCore_rules, "f").map(rule => rule.name));
156
+ const definedRuleName = new Set(this.#rules.map(rule => rule.name));
92
157
  const setRuleNames = new Set([
93
- ...Object.keys(__classPrivateFieldGet(this, _MLCore_ruleset, "f").rules),
94
- ...__classPrivateFieldGet(this, _MLCore_ruleset, "f").nodeRules.flatMap(nodeRule => Object.keys(nodeRule.rules ?? {})),
95
- ...__classPrivateFieldGet(this, _MLCore_ruleset, "f").childNodeRules.flatMap(childNodeRule => Object.keys(childNodeRule.rules ?? {})),
158
+ ...Object.keys(this.#ruleset.rules),
159
+ ...this.#ruleset.nodeRules.flatMap(nodeRule => Object.keys(nodeRule.rules ?? {})),
160
+ ...this.#ruleset.childNodeRules.flatMap(childNodeRule => Object.keys(childNodeRule.rules ?? {})),
96
161
  ]);
97
162
  for (const setRuleName of setRuleNames) {
163
+ // Skip wildcard patterns (e.g., "a11y/*") — they are namespace disable entries, not rule references
164
+ if (setRuleName.endsWith('/*')) {
165
+ continue;
166
+ }
98
167
  if (!definedRuleName.has(setRuleName)) {
99
168
  violations.push({
100
169
  ruleId: 'config-error',
@@ -106,7 +175,7 @@ export class MLCore {
106
175
  });
107
176
  }
108
177
  }
109
- for (const error of __classPrivateFieldGet(this, _MLCore_configErrors, "f")) {
178
+ for (const error of this.#configErrors) {
110
179
  violations.push({
111
180
  ruleId: 'config-error',
112
181
  severity: 'warning',
@@ -116,13 +185,25 @@ export class MLCore {
116
185
  raw: '',
117
186
  });
118
187
  }
119
- for (const rule of __classPrivateFieldGet(this, _MLCore_rules, "f")) {
120
- const ruleInfo = rule.getRuleInfo(__classPrivateFieldGet(this, _MLCore_ruleset, "f"), rule.name);
188
+ for (const rule of this.#rules) {
189
+ // For virtual rules, check disable conditions:
190
+ // 1. Exact name match: rules["alias/name"]: false
191
+ // 2. Group disable: rules["groupName"]: false (multi-entry named nodeRules)
192
+ // 3. Namespace wildcard: rules["scope/*"]: false
193
+ // Note: base rule name disable (rules["baseRuleName"]: false) is handled
194
+ // during expandNamedRules for named rule groups in the rules section.
195
+ if (rule.baseRuleId &&
196
+ (this.#ruleset.rules[rule.name] === false ||
197
+ (rule.groupName && this.#ruleset.rules[rule.groupName] === false) ||
198
+ this.#disabledNamespaces.some(ns => rule.name.startsWith(ns)))) {
199
+ continue;
200
+ }
201
+ const ruleInfo = rule.getRuleInfo(this.#ruleset, rule.name);
121
202
  if (ruleInfo.disabled && ruleInfo.nodeRules.length === 0 && ruleInfo.childNodeRules.length === 0) {
122
203
  continue;
123
204
  }
124
205
  log('%s Rule: verify', rule.name);
125
- const results = await rule.verify(__classPrivateFieldGet(this, _MLCore_document, "f"), __classPrivateFieldGet(this, _MLCore_locale, "f"), fix).catch(error => {
206
+ const results = await rule.verify(this.#document, this.#locale, fix).catch(error => {
126
207
  if (error instanceof ParserError) {
127
208
  return error;
128
209
  }
@@ -159,21 +240,21 @@ export class MLCore {
159
240
  return violations;
160
241
  }
161
242
  _createDocument() {
162
- if (!__classPrivateFieldGet(this, _MLCore_ast, "f")) {
243
+ if (!this.#ast) {
163
244
  return;
164
245
  }
165
246
  try {
166
- __classPrivateFieldSet(this, _MLCore_document, new Document(__classPrivateFieldGet(this, _MLCore_ast, "f"), __classPrivateFieldGet(this, _MLCore_ruleset, "f"), __classPrivateFieldGet(this, _MLCore_schemas, "f"), {
167
- filename: __classPrivateFieldGet(this, _MLCore_filename, "f"),
168
- endTag: __classPrivateFieldGet(this, _MLCore_parser, "f").endTag,
169
- booleanish: __classPrivateFieldGet(this, _MLCore_parser, "f").booleanish,
170
- tagNameCaseSensitive: __classPrivateFieldGet(this, _MLCore_parser, "f").tagNameCaseSensitive,
171
- pretenders: __classPrivateFieldGet(this, _MLCore_pretenders, "f"),
172
- }), "f");
247
+ this.#document = new Document(this.#ast, this.#ruleset, this.#schemas, this.#ruleCommonSettings, {
248
+ filename: this.#filename,
249
+ endTag: this.#parser.endTag,
250
+ booleanish: this.#parser.booleanish,
251
+ tagNameCaseSensitive: this.#parser.tagNameCaseSensitive,
252
+ pretenders: this.#pretenders,
253
+ });
173
254
  }
174
255
  catch (error) {
175
256
  if (error instanceof ParserError) {
176
- __classPrivateFieldSet(this, _MLCore_document, error, "f");
257
+ this.#document = error;
177
258
  }
178
259
  else {
179
260
  throw error;
@@ -181,13 +262,13 @@ export class MLCore {
181
262
  }
182
263
  }
183
264
  _createParseError(message, line, col, raw) {
184
- if (__classPrivateFieldGet(this, _MLCore_severity, "f").parseError === false || __classPrivateFieldGet(this, _MLCore_severity, "f").parseError === 'off') {
265
+ if (this.#severity.parseError === false || this.#severity.parseError === 'off') {
185
266
  return null;
186
267
  }
187
268
  // Default severity is 'error'
188
- const severity = __classPrivateFieldGet(this, _MLCore_severity, "f").parseError === true || __classPrivateFieldGet(this, _MLCore_severity, "f").parseError == null
269
+ const severity = this.#severity.parseError === true || this.#severity.parseError == null
189
270
  ? 'error'
190
- : __classPrivateFieldGet(this, _MLCore_severity, "f").parseError;
271
+ : this.#severity.parseError;
191
272
  return {
192
273
  ruleId: 'parse-error',
193
274
  severity,
@@ -199,13 +280,13 @@ export class MLCore {
199
280
  }
200
281
  _parse() {
201
282
  try {
202
- __classPrivateFieldSet(this, _MLCore_ast, __classPrivateFieldGet(this, _MLCore_parser, "f").parse(__classPrivateFieldGet(this, _MLCore_sourceCode, "f"), __classPrivateFieldGet(this, _MLCore_parserOptions, "f")), "f");
283
+ this.#ast = this.#parser.parse(this.#sourceCode, this.#parserOptions);
203
284
  }
204
285
  catch (error) {
205
286
  log('Caught the parse error: %O', error);
206
- __classPrivateFieldSet(this, _MLCore_ast, null, "f");
287
+ this.#ast = null;
207
288
  if (error instanceof ParserError) {
208
- __classPrivateFieldSet(this, _MLCore_document, error, "f");
289
+ this.#document = error;
209
290
  }
210
291
  else {
211
292
  throw error;
@@ -213,4 +294,12 @@ export class MLCore {
213
294
  }
214
295
  }
215
296
  }
216
- _MLCore_ast = new WeakMap(), _MLCore_document = new WeakMap(), _MLCore_filename = new WeakMap(), _MLCore_locale = new WeakMap(), _MLCore_parser = new WeakMap(), _MLCore_parserOptions = new WeakMap(), _MLCore_severity = new WeakMap(), _MLCore_pretenders = new WeakMap(), _MLCore_rules = new WeakMap(), _MLCore_ruleset = new WeakMap(), _MLCore_schemas = new WeakMap(), _MLCore_sourceCode = new WeakMap(), _MLCore_configErrors = new WeakMap();
297
+ /**
298
+ * Extracts namespace prefixes from wildcard disable entries in rules.
299
+ * e.g., `{ "a11y/*": false }` yields `["a11y/"]`.
300
+ */
301
+ function extractDisabledNamespaces(rules) {
302
+ return Object.entries(rules)
303
+ .filter(([key, value]) => key.endsWith('/*') && value === false)
304
+ .map(([key]) => key.slice(0, -1)); // "a11y/*" → "a11y/"
305
+ }
@@ -1,3 +1,11 @@
1
1
  import type { MLElement } from '../node/element.js';
2
2
  import type { ARIAVersion } from '@markuplint/ml-spec';
3
+ /**
4
+ * Computes the accessible name for an MLElement using the HTML-AAM algorithm.
5
+ * Creates an MLCore-specific resolver that bridges MLElement to the AccnameResolver interface.
6
+ *
7
+ * @param el - The MLElement to compute the accessible name for
8
+ * @param version - The ARIA specification version to use for role resolution
9
+ * @returns The computed accessible name string, or an empty string on error
10
+ */
3
11
  export declare function getAccname(el: MLElement<any, any>, version: ARIAVersion): string;
@@ -1,71 +1,87 @@
1
- import { getAccname as get, getComputedRole } from '@markuplint/ml-spec';
1
+ import { computeAccessibleName, escapeCSS, getComputedRole, EMBEDDED_CONTROL_ROLES, isNativeEmbeddedControl, } from '@markuplint/ml-spec';
2
2
  import { log } from '../../debug.js';
3
3
  const accnameLog = log.extend('accname');
4
+ /**
5
+ * Computes the accessible name for an MLElement using the HTML-AAM algorithm.
6
+ * Creates an MLCore-specific resolver that bridges MLElement to the AccnameResolver interface.
7
+ *
8
+ * @param el - The MLElement to compute the accessible name for
9
+ * @param version - The ARIA specification version to use for role resolution
10
+ * @returns The computed accessible name string, or an empty string on error
11
+ */
4
12
  export function getAccname(
5
13
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
6
14
  el, version) {
7
- let accname = safeGet(el);
8
- if (accname) {
9
- return accname;
10
- }
11
- accname = getAccnameFromPretender(el);
12
- if (accname) {
13
- return accname;
14
- }
15
- if (isHidden(el)) {
16
- return '';
17
- }
18
- if (isFromContent(el, version)) {
19
- return [...el.childNodes]
20
- .map(child => {
21
- if (child.is(child.ELEMENT_NODE)) {
22
- return getAccname(child, version);
23
- }
24
- if (child.is(child.TEXT_NODE)) {
25
- return child.textContent ?? '';
26
- }
27
- return '';
28
- })
29
- .join('');
30
- }
31
- return '';
32
- }
33
- function safeGet(
34
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
35
- el) {
15
+ // AccName computation involves DOM traversal, role resolution, and label
16
+ // lookup via querySelector. These may fail on unexpected DOM states (e.g.,
17
+ // detached nodes, malformed attributes) or incomplete spec data.
18
+ // A single element's failure must not abort the entire linting process,
19
+ // so we catch operational errors and return an empty name (= "unnamed").
20
+ // Programmer errors (TypeError, ReferenceError) are rethrown to avoid
21
+ // masking implementation bugs.
36
22
  try {
37
- const name = get(el);
38
- return name;
23
+ const resolver = createMLCoreResolver(el, version);
24
+ const result = computeAccessibleName(el, resolver);
25
+ return result.name;
39
26
  }
40
27
  catch (error) {
28
+ if (error instanceof TypeError || error instanceof ReferenceError) {
29
+ throw error;
30
+ }
41
31
  accnameLog('Raw: %s', el.raw);
42
32
  accnameLog('Error: %O', error);
43
33
  return '';
44
34
  }
45
35
  }
46
- function getAccnameFromPretender(
47
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
48
- el) {
49
- if (el.pretenderContext?.type === 'pretender' && el.pretenderContext.aria?.name != null) {
50
- if (typeof el.pretenderContext.aria.name === 'boolean') {
51
- return 'some-name(Pretender Options)';
52
- }
53
- const attrName = el.pretenderContext.aria.name.fromAttr;
54
- const attrValue = el.getAttributePretended(attrName);
55
- if (attrValue) {
56
- return attrValue;
57
- }
58
- }
59
- return '';
60
- }
61
- function isHidden(
62
- // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
63
- el) {
64
- return el.getAttribute('aria-hidden') === 'true' || el.hasAttribute('hidden');
65
- }
66
- function isFromContent(
36
+ function createMLCoreResolver(
67
37
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
68
38
  el, version) {
69
- const role = getComputedRole(el.ownerMLDocument.specs, el, version);
70
- return !!role.role?.accessibleNameFromContent;
39
+ const doc = el.ownerMLDocument;
40
+ return {
41
+ getElementById(id) {
42
+ const found = doc.querySelector(`#${escapeCSS(id)}`);
43
+ return found ?? null;
44
+ },
45
+ getLabelsForId(id) {
46
+ const labels = doc.querySelectorAll(`label[for="${escapeCSS(id)}"]`);
47
+ return [...labels];
48
+ },
49
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
50
+ allowsNameFromContent(target) {
51
+ // TODO: Remove cast when getComputedRole accepts AccnameElement (#3178)
52
+ const role = getComputedRole(doc.specs, target, version);
53
+ return !!role.role?.accessibleNameFromContent;
54
+ },
55
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
56
+ isHidden(target) {
57
+ return target.getAttribute('aria-hidden') === 'true' || target.hasAttribute('hidden');
58
+ },
59
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
60
+ isEmbeddedControl(target) {
61
+ // TODO: Remove cast when getComputedRole accepts AccnameElement (#3178)
62
+ const role = getComputedRole(doc.specs, target, version);
63
+ if (role.role?.name) {
64
+ return EMBEDDED_CONTROL_ROLES.has(role.role.name);
65
+ }
66
+ return isNativeEmbeddedControl(target);
67
+ },
68
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
69
+ getPrecomputedName(target) {
70
+ // TODO: Remove cast when AccnameResolver is generic (#3178)
71
+ const targetEl = target;
72
+ if (!targetEl.pretenderContext || targetEl.pretenderContext.type !== 'pretender') {
73
+ return null;
74
+ }
75
+ const ariaName = targetEl.pretenderContext.aria?.name;
76
+ if (ariaName == null) {
77
+ return null;
78
+ }
79
+ if (typeof ariaName === 'boolean') {
80
+ return 'some-name(Pretender Options)';
81
+ }
82
+ const attrName = ariaName.fromAttr;
83
+ const attrValue = targetEl.getAttributePretended(attrName);
84
+ return attrValue || null;
85
+ },
86
+ };
71
87
  }
@@ -33,6 +33,7 @@ document) {
33
33
  elementType: 'web-component',
34
34
  attributes: [],
35
35
  childNodes: [],
36
+ blockBehavior: null,
36
37
  pairNode: null,
37
38
  tagOpenChar: '',
38
39
  tagCloseChar: '',
@@ -1,9 +1,12 @@
1
1
  import type { MLNode } from '../node/node.js';
2
2
  import type { MLText } from '../node/text.js';
3
3
  /**
4
+ * Computes the indentation preceding the given node by analyzing
5
+ * the whitespace in adjacent text nodes.
4
6
  *
5
7
  * @deprecated
6
- * @param node
8
+ * @param node - The node whose indentation to determine
9
+ * @returns An indentation object describing the whitespace, or null if no indentation is found
7
10
  */
8
11
  export declare function getIndent(node: MLNode<any, any>): MLDOMIndentation | null;
9
12
  declare class MLDOMIndentation {