@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.
Files changed (57) hide show
  1. package/ARCHITECTURE.ja.md +92 -35
  2. package/ARCHITECTURE.md +85 -28
  3. package/CHANGELOG.md +50 -0
  4. package/docs/linting-pipeline.ja.md +18 -14
  5. package/docs/linting-pipeline.md +18 -14
  6. package/docs/maintenance.ja.md +1 -1
  7. package/docs/maintenance.md +1 -1
  8. package/docs/ml-dom/attr.ja.md +8 -0
  9. package/docs/ml-dom/attr.md +8 -0
  10. package/docs/ml-dom/block.ja.md +33 -33
  11. package/docs/ml-dom/block.md +33 -33
  12. package/docs/ml-dom/element.ja.md +17 -17
  13. package/docs/ml-dom/element.md +17 -17
  14. package/docs/ml-dom/node.ja.md +4 -5
  15. package/docs/ml-dom/node.md +4 -5
  16. package/docs/ml-dom/others.ja.md +2 -1
  17. package/docs/ml-dom/others.md +5 -4
  18. package/docs/rule-system.ja.md +23 -6
  19. package/docs/rule-system.md +23 -6
  20. package/lib/index.d.ts +4 -3
  21. package/lib/index.js +1 -1
  22. package/lib/ml-core.d.ts +1 -1
  23. package/lib/ml-core.js +142 -82
  24. package/lib/ml-dom/helper/accname.d.ts +8 -0
  25. package/lib/ml-dom/helper/accname.js +71 -55
  26. package/lib/ml-dom/helper/create-node.js +1 -0
  27. package/lib/ml-dom/helper/get-indent.js +17 -29
  28. package/lib/ml-dom/node/attr.js +122 -73
  29. package/lib/ml-dom/node/block.d.ts +3 -3
  30. package/lib/ml-dom/node/block.js +10 -1
  31. package/lib/ml-dom/node/document-type.js +12 -0
  32. package/lib/ml-dom/node/document.d.ts +14 -3
  33. package/lib/ml-dom/node/document.js +75 -34
  34. package/lib/ml-dom/node/dom-token-list.js +17 -30
  35. package/lib/ml-dom/node/element-close-tag.js +1 -0
  36. package/lib/ml-dom/node/element.d.ts +19 -7
  37. package/lib/ml-dom/node/element.js +134 -55
  38. package/lib/ml-dom/node/node-store.js +6 -15
  39. package/lib/ml-dom/node/node.d.ts +3 -1
  40. package/lib/ml-dom/node/node.js +159 -166
  41. package/lib/ml-dom/node/parent-node.js +14 -30
  42. package/lib/ml-dom/node/rule-mapper.js +7 -20
  43. package/lib/ml-dom/node/text.d.ts +7 -0
  44. package/lib/ml-dom/node/text.js +9 -0
  45. package/lib/ml-dom/token/token.js +23 -39
  46. package/lib/ml-rule/create-rule.d.ts +8 -1
  47. package/lib/ml-rule/create-rule.js +0 -9
  48. package/lib/ml-rule/ml-rule-context.js +7 -11
  49. package/lib/ml-rule/ml-rule.d.ts +33 -1
  50. package/lib/ml-rule/ml-rule.js +65 -25
  51. package/lib/ruleset/index.js +6 -0
  52. package/lib/test/index.js +4 -1
  53. package/lib/types.d.ts +2 -1
  54. package/lib/violation-collector.js +15 -28
  55. package/lib/virtual-rule.d.ts +72 -0
  56. package/lib/virtual-rule.js +233 -0
  57. package/package.json +16 -13
package/lib/ml-core.js CHANGED
@@ -1,18 +1,7 @@
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');
17
6
  /**
18
7
  * The core linting engine for markuplint.
@@ -21,38 +10,65 @@ const resultLog = log.extend('result');
21
10
  * and verifies it against configured rules to produce violations.
22
11
  */
23
12
  export class MLCore {
24
- constructor({ parser, sourceCode, ruleset, rules, locale, schemas, parserOptions, severity, pretenders, filename, debug, configErrors, }) {
25
- _MLCore_ast.set(this, null);
26
- _MLCore_document.set(this, void 0);
27
- _MLCore_filename.set(this, void 0);
28
- _MLCore_locale.set(this, void 0);
29
- _MLCore_parser.set(this, void 0);
30
- _MLCore_parserOptions.set(this, void 0);
31
- _MLCore_severity.set(this, void 0);
32
- _MLCore_pretenders.set(this, void 0);
33
- _MLCore_rules.set(this, void 0);
34
- _MLCore_ruleset.set(this, void 0);
35
- _MLCore_schemas.set(this, void 0);
36
- _MLCore_sourceCode.set(this, void 0);
37
- _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, }) {
38
41
  if (debug) {
39
42
  enableDebug();
40
43
  }
41
- __classPrivateFieldSet(this, _MLCore_parser, parser, "f");
42
- __classPrivateFieldSet(this, _MLCore_sourceCode, sourceCode, "f");
43
- __classPrivateFieldSet(this, _MLCore_parserOptions, parserOptions, "f");
44
- __classPrivateFieldSet(this, _MLCore_ruleset, {
45
- rules: ruleset.rules ?? {},
46
- nodeRules: ruleset.nodeRules ?? [],
47
- childNodeRules: ruleset.childNodeRules ?? [],
48
- }, "f");
49
- __classPrivateFieldSet(this, _MLCore_locale, locale, "f");
50
- __classPrivateFieldSet(this, _MLCore_schemas, schemas, "f");
51
- __classPrivateFieldSet(this, _MLCore_filename, filename, "f");
52
- __classPrivateFieldSet(this, _MLCore_rules, [...rules], "f");
53
- __classPrivateFieldSet(this, _MLCore_severity, severity, "f");
54
- __classPrivateFieldSet(this, _MLCore_pretenders, [...pretenders], "f");
55
- __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);
56
72
  this._parse();
57
73
  this._createDocument();
58
74
  }
@@ -60,7 +76,7 @@ export class MLCore {
60
76
  * The parsed document, or a {@link ParserError} if parsing failed.
61
77
  */
62
78
  get document() {
63
- return __classPrivateFieldGet(this, _MLCore_document, "f");
79
+ return this.#document;
64
80
  }
65
81
  /**
66
82
  * Replaces the source code and re-parses the document.
@@ -68,7 +84,7 @@ export class MLCore {
68
84
  * @param sourceCode - The new markup source code
69
85
  */
70
86
  setCode(sourceCode) {
71
- __classPrivateFieldSet(this, _MLCore_sourceCode, sourceCode, "f");
87
+ this.#sourceCode = sourceCode;
72
88
  this._parse();
73
89
  this._createDocument();
74
90
  }
@@ -79,19 +95,39 @@ export class MLCore {
79
95
  * @param fabric - Partial fabric with the properties to update
80
96
  */
81
97
  update({ parser, ruleset, rules, locale, schemas, parserOptions, configErrors }) {
82
- __classPrivateFieldSet(this, _MLCore_parser, parser ?? __classPrivateFieldGet(this, _MLCore_parser, "f"), "f");
83
- __classPrivateFieldSet(this, _MLCore_ruleset, {
84
- rules: ruleset?.rules ?? __classPrivateFieldGet(this, _MLCore_ruleset, "f").rules,
85
- nodeRules: ruleset?.nodeRules ?? __classPrivateFieldGet(this, _MLCore_ruleset, "f").nodeRules,
86
- childNodeRules: ruleset?.childNodeRules ?? __classPrivateFieldGet(this, _MLCore_ruleset, "f").childNodeRules,
87
- }, "f");
88
- __classPrivateFieldSet(this, _MLCore_rules, rules?.slice() ?? __classPrivateFieldGet(this, _MLCore_rules, "f"), "f");
89
- __classPrivateFieldSet(this, _MLCore_locale, locale ?? __classPrivateFieldGet(this, _MLCore_locale, "f"), "f");
90
- __classPrivateFieldSet(this, _MLCore_schemas, schemas ?? __classPrivateFieldGet(this, _MLCore_schemas, "f"), "f");
91
- __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);
92
128
  if (parserOptions &&
93
- (parserOptions.ignoreFrontMatter !== __classPrivateFieldGet(this, _MLCore_parserOptions, "f").ignoreFrontMatter ||
94
- parserOptions.authoredElementName !== __classPrivateFieldGet(this, _MLCore_parserOptions, "f").authoredElementName)) {
129
+ (parserOptions.ignoreFrontMatter !== this.#parserOptions.ignoreFrontMatter ||
130
+ parserOptions.authoredElementName !== this.#parserOptions.authoredElementName)) {
95
131
  this._parse();
96
132
  }
97
133
  this._createDocument();
@@ -108,22 +144,26 @@ export class MLCore {
108
144
  async verify(fix = false) {
109
145
  log('verify: start');
110
146
  const violations = [];
111
- if (__classPrivateFieldGet(this, _MLCore_document, "f") instanceof ParserError) {
112
- 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);
113
149
  if (!parseError) {
114
150
  return [];
115
151
  }
116
152
  violations.push(parseError);
117
- log('verify: error %o', __classPrivateFieldGet(this, _MLCore_document, "f").message);
153
+ log('verify: error %o', this.#document.message);
118
154
  return violations;
119
155
  }
120
- const definedRuleName = new Set(__classPrivateFieldGet(this, _MLCore_rules, "f").map(rule => rule.name));
156
+ const definedRuleName = new Set(this.#rules.map(rule => rule.name));
121
157
  const setRuleNames = new Set([
122
- ...Object.keys(__classPrivateFieldGet(this, _MLCore_ruleset, "f").rules),
123
- ...__classPrivateFieldGet(this, _MLCore_ruleset, "f").nodeRules.flatMap(nodeRule => Object.keys(nodeRule.rules ?? {})),
124
- ...__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 ?? {})),
125
161
  ]);
126
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
+ }
127
167
  if (!definedRuleName.has(setRuleName)) {
128
168
  violations.push({
129
169
  ruleId: 'config-error',
@@ -135,7 +175,7 @@ export class MLCore {
135
175
  });
136
176
  }
137
177
  }
138
- for (const error of __classPrivateFieldGet(this, _MLCore_configErrors, "f")) {
178
+ for (const error of this.#configErrors) {
139
179
  violations.push({
140
180
  ruleId: 'config-error',
141
181
  severity: 'warning',
@@ -145,13 +185,25 @@ export class MLCore {
145
185
  raw: '',
146
186
  });
147
187
  }
148
- for (const rule of __classPrivateFieldGet(this, _MLCore_rules, "f")) {
149
- 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);
150
202
  if (ruleInfo.disabled && ruleInfo.nodeRules.length === 0 && ruleInfo.childNodeRules.length === 0) {
151
203
  continue;
152
204
  }
153
205
  log('%s Rule: verify', rule.name);
154
- 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 => {
155
207
  if (error instanceof ParserError) {
156
208
  return error;
157
209
  }
@@ -188,21 +240,21 @@ export class MLCore {
188
240
  return violations;
189
241
  }
190
242
  _createDocument() {
191
- if (!__classPrivateFieldGet(this, _MLCore_ast, "f")) {
243
+ if (!this.#ast) {
192
244
  return;
193
245
  }
194
246
  try {
195
- __classPrivateFieldSet(this, _MLCore_document, new Document(__classPrivateFieldGet(this, _MLCore_ast, "f"), __classPrivateFieldGet(this, _MLCore_ruleset, "f"), __classPrivateFieldGet(this, _MLCore_schemas, "f"), {
196
- filename: __classPrivateFieldGet(this, _MLCore_filename, "f"),
197
- endTag: __classPrivateFieldGet(this, _MLCore_parser, "f").endTag,
198
- booleanish: __classPrivateFieldGet(this, _MLCore_parser, "f").booleanish,
199
- tagNameCaseSensitive: __classPrivateFieldGet(this, _MLCore_parser, "f").tagNameCaseSensitive,
200
- pretenders: __classPrivateFieldGet(this, _MLCore_pretenders, "f"),
201
- }), "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
+ });
202
254
  }
203
255
  catch (error) {
204
256
  if (error instanceof ParserError) {
205
- __classPrivateFieldSet(this, _MLCore_document, error, "f");
257
+ this.#document = error;
206
258
  }
207
259
  else {
208
260
  throw error;
@@ -210,13 +262,13 @@ export class MLCore {
210
262
  }
211
263
  }
212
264
  _createParseError(message, line, col, raw) {
213
- 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') {
214
266
  return null;
215
267
  }
216
268
  // Default severity is 'error'
217
- 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
218
270
  ? 'error'
219
- : __classPrivateFieldGet(this, _MLCore_severity, "f").parseError;
271
+ : this.#severity.parseError;
220
272
  return {
221
273
  ruleId: 'parse-error',
222
274
  severity,
@@ -228,13 +280,13 @@ export class MLCore {
228
280
  }
229
281
  _parse() {
230
282
  try {
231
- __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);
232
284
  }
233
285
  catch (error) {
234
286
  log('Caught the parse error: %O', error);
235
- __classPrivateFieldSet(this, _MLCore_ast, null, "f");
287
+ this.#ast = null;
236
288
  if (error instanceof ParserError) {
237
- __classPrivateFieldSet(this, _MLCore_document, error, "f");
289
+ this.#document = error;
238
290
  }
239
291
  else {
240
292
  throw error;
@@ -242,4 +294,12 @@ export class MLCore {
242
294
  }
243
295
  }
244
296
  }
245
- _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,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 _MLDOMIndentation_fixed, _MLDOMIndentation_node, _MLDOMIndentation_parent;
13
1
  /**
14
2
  * Computes the indentation preceding the given node by analyzing
15
3
  * the whitespace in adjacent text nodes.
@@ -62,52 +50,52 @@ node) {
62
50
  return null;
63
51
  }
64
52
  class MLDOMIndentation {
53
+ #fixed;
54
+ line;
55
+ #node;
56
+ #parent;
65
57
  constructor(
66
58
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
67
59
  originTextNode, raw, line,
68
60
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
69
61
  parentNode) {
70
- _MLDOMIndentation_fixed.set(this, void 0);
71
- _MLDOMIndentation_node.set(this, void 0);
72
- _MLDOMIndentation_parent.set(this, void 0);
73
62
  this.line = line;
74
- __classPrivateFieldSet(this, _MLDOMIndentation_node, originTextNode, "f");
75
- __classPrivateFieldSet(this, _MLDOMIndentation_parent, parentNode, "f");
76
- __classPrivateFieldSet(this, _MLDOMIndentation_fixed, raw, "f");
63
+ this.#node = originTextNode;
64
+ this.#parent = parentNode;
65
+ this.#fixed = raw;
77
66
  }
78
67
  get raw() {
79
- if (!__classPrivateFieldGet(this, _MLDOMIndentation_parent, "f").is(__classPrivateFieldGet(this, _MLDOMIndentation_parent, "f").TEXT_NODE) && this.line !== __classPrivateFieldGet(this, _MLDOMIndentation_node, "f").endLine) {
68
+ if (!this.#parent.is(this.#parent.TEXT_NODE) && this.line !== this.#node.endLine) {
80
69
  return '';
81
70
  }
82
- return __classPrivateFieldGet(this, _MLDOMIndentation_fixed, "f");
71
+ return this.#fixed;
83
72
  }
84
73
  get type() {
85
- if (!__classPrivateFieldGet(this, _MLDOMIndentation_parent, "f").is(__classPrivateFieldGet(this, _MLDOMIndentation_parent, "f").TEXT_NODE) && this.line !== __classPrivateFieldGet(this, _MLDOMIndentation_node, "f").endLine) {
74
+ if (!this.#parent.is(this.#parent.TEXT_NODE) && this.line !== this.#node.endLine) {
86
75
  return 'none';
87
76
  }
88
- const raw = __classPrivateFieldGet(this, _MLDOMIndentation_fixed, "f");
77
+ const raw = this.#fixed;
89
78
  return raw === '' ? 'none' : /^\t+$/.test(raw) ? 'tab' : /^[^\t]+$/.test(raw) ? 'space' : 'mixed';
90
79
  }
91
80
  get width() {
92
- if (!__classPrivateFieldGet(this, _MLDOMIndentation_parent, "f").is(__classPrivateFieldGet(this, _MLDOMIndentation_parent, "f").TEXT_NODE) && this.line !== __classPrivateFieldGet(this, _MLDOMIndentation_node, "f").endLine) {
81
+ if (!this.#parent.is(this.#parent.TEXT_NODE) && this.line !== this.#node.endLine) {
93
82
  return 0;
94
83
  }
95
- return __classPrivateFieldGet(this, _MLDOMIndentation_fixed, "f").length;
84
+ return this.#fixed.length;
96
85
  }
97
86
  fix(raw) {
98
- const current = __classPrivateFieldGet(this, _MLDOMIndentation_fixed, "f");
99
- __classPrivateFieldSet(this, _MLDOMIndentation_fixed, raw, "f");
100
- const node = __classPrivateFieldGet(this, _MLDOMIndentation_node, "f");
87
+ const current = this.#fixed;
88
+ this.#fixed = raw;
89
+ const node = this.#node;
101
90
  const line = node.startLine;
102
91
  const lines = node.raw.split(/\r?\n/);
103
92
  const index = this.line - line;
104
93
  if (lines[index] != null) {
105
- lines[index] = lines[index].replace(current, __classPrivateFieldGet(this, _MLDOMIndentation_fixed, "f"));
94
+ lines[index] = lines[index].replace(current, this.#fixed);
106
95
  }
107
96
  node.fix(lines.join('\n'));
108
97
  }
109
98
  }
110
- _MLDOMIndentation_fixed = new WeakMap(), _MLDOMIndentation_node = new WeakMap(), _MLDOMIndentation_parent = new WeakMap();
111
99
  function isFirstToken(
112
100
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
113
101
  node) {