@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
@@ -1,16 +1,5 @@
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 _MLAttr_localName, _MLAttr_namespaceURI, _MLAttr_potentialName, _MLAttr_potentialValue;
13
- import { resolveNamespace } from '@markuplint/ml-spec';
1
+ import { resolveNamespace, compileDirectivePatterns, resolveDirective } from '@markuplint/ml-spec';
2
+ import { searchIDLAttribute } from '@markuplint/parser-utils';
14
3
  import { MLToken } from '../token/token.js';
15
4
  import { MLDomTokenList } from './dom-token-list.js';
16
5
  import { MLNode } from './node.js';
@@ -25,6 +14,70 @@ import { UnexpectedCallError } from './unexpected-call-error.js';
25
14
  * @template O - The rule options type
26
15
  */
27
16
  export class MLAttr extends MLNode {
17
+ /**
18
+ * A candidate attribute name suggested by the parser, if available.
19
+ */
20
+ candidate;
21
+ /**
22
+ * The end quote token of the attribute value, or null if the attribute has no value or quotes.
23
+ */
24
+ endQuote = null;
25
+ /**
26
+ * The equal sign token between the attribute name and value, or null if absent.
27
+ */
28
+ equal = null;
29
+ /**
30
+ * Whether this attribute is a directive (e.g., framework-specific attributes like `v-if` or `@click`).
31
+ */
32
+ isDirective;
33
+ /**
34
+ * Whether this attribute can be duplicated on the same element.
35
+ */
36
+ isDuplicatable;
37
+ /**
38
+ * Whether this attribute has a dynamic value (e.g., a template expression rather than a static string).
39
+ */
40
+ isDynamicValue;
41
+ #localName;
42
+ /**
43
+ * The token representing the attribute name, or null for spread attributes.
44
+ */
45
+ nameNode = null;
46
+ #namespaceURI;
47
+ /**
48
+ * @implements DOM API: `Attr`
49
+ * @see https://dom.spec.whatwg.org/#ref-for-dom-node-previoussibling%E2%91%A0
50
+ */
51
+ ownerElement;
52
+ #potentialName;
53
+ #potentialValue;
54
+ /**
55
+ * The whitespace token after the equal sign, or null if absent.
56
+ */
57
+ spacesAfterEqual = null;
58
+ /**
59
+ * The whitespace token before the equal sign, or null if absent.
60
+ */
61
+ spacesBeforeEqual = null;
62
+ /**
63
+ * The whitespace token before the attribute name, or null if absent.
64
+ */
65
+ spacesBeforeName = null;
66
+ /**
67
+ * The start quote token of the attribute value, or null if the attribute has no value or quotes.
68
+ */
69
+ startQuote = null;
70
+ /**
71
+ * The token representing the attribute value, or null if the attribute has no value.
72
+ */
73
+ valueNode = null;
74
+ /**
75
+ * Returns the "string" if HTML syntax. Otherwise, returns a type in its syntax.
76
+ *
77
+ * @default "string"
78
+ * @implements `@markuplint/ml-core` API: `MLAttr`
79
+ */
80
+ valueType = 'string';
28
81
  /**
29
82
  * Creates a new MLAttr instance from an AST attribute token.
30
83
  *
@@ -35,56 +88,13 @@ export class MLAttr extends MLNode {
35
88
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
36
89
  ownElement) {
37
90
  super(astToken, ownElement.ownerMLDocument);
38
- /**
39
- * The end quote token of the attribute value, or null if the attribute has no value or quotes.
40
- */
41
- this.endQuote = null;
42
- /**
43
- * The equal sign token between the attribute name and value, or null if absent.
44
- */
45
- this.equal = null;
46
- _MLAttr_localName.set(this, void 0);
47
- /**
48
- * The token representing the attribute name, or null for spread attributes.
49
- */
50
- this.nameNode = null;
51
- _MLAttr_namespaceURI.set(this, void 0);
52
- _MLAttr_potentialName.set(this, void 0);
53
- _MLAttr_potentialValue.set(this, void 0);
54
- /**
55
- * The whitespace token after the equal sign, or null if absent.
56
- */
57
- this.spacesAfterEqual = null;
58
- /**
59
- * The whitespace token before the equal sign, or null if absent.
60
- */
61
- this.spacesBeforeEqual = null;
62
- /**
63
- * The whitespace token before the attribute name, or null if absent.
64
- */
65
- this.spacesBeforeName = null;
66
- /**
67
- * The start quote token of the attribute value, or null if the attribute has no value or quotes.
68
- */
69
- this.startQuote = null;
70
- /**
71
- * The token representing the attribute value, or null if the attribute has no value.
72
- */
73
- this.valueNode = null;
74
- /**
75
- * Returns the "string" if HTML syntax. Otherwise, returns a type in its syntax.
76
- *
77
- * @default "string"
78
- * @implements `@markuplint/ml-core` API: `MLAttr`
79
- */
80
- this.valueType = 'string';
81
91
  this.ownerElement = ownElement;
82
92
  if (this._astToken.type === 'spread') {
83
- __classPrivateFieldSet(this, _MLAttr_namespaceURI, ownElement.namespaceURI, "f");
93
+ this.#namespaceURI = ownElement.namespaceURI;
84
94
  this.valueType = 'code';
85
- __classPrivateFieldSet(this, _MLAttr_localName, '#spread', "f");
86
- __classPrivateFieldSet(this, _MLAttr_potentialName, '#spread', "f");
87
- __classPrivateFieldSet(this, _MLAttr_potentialValue, this._astToken.raw, "f");
95
+ this.#localName = '#spread';
96
+ this.#potentialName = '#spread';
97
+ this.#potentialValue = this._astToken.raw;
88
98
  this.isDirective = true;
89
99
  this.isDynamicValue = true;
90
100
  this.isDuplicatable = true;
@@ -98,15 +108,55 @@ export class MLAttr extends MLNode {
98
108
  this.startQuote = new MLToken(this._astToken.startQuote);
99
109
  this.valueNode = new MLToken(this._astToken.value);
100
110
  this.endQuote = new MLToken(this._astToken.endQuote);
101
- this.isDynamicValue = this._astToken.isDynamicValue;
102
- this.isDirective = this._astToken.isDirective;
103
111
  this.candidate = this._astToken.candidate;
104
- __classPrivateFieldSet(this, _MLAttr_potentialName, this._astToken.potentialName ?? this.nameNode?.raw ?? '', "f");
105
- __classPrivateFieldSet(this, _MLAttr_potentialValue, this._astToken.potentialValue ?? this.valueNode?.raw ?? '', "f");
106
- this.isDuplicatable = this._astToken.isDuplicatable;
107
- const ns = resolveNamespace(__classPrivateFieldGet(this, _MLAttr_potentialName, "f"), ownElement.namespaceURI);
108
- __classPrivateFieldSet(this, _MLAttr_localName, ns.localName, "f");
109
- __classPrivateFieldSet(this, _MLAttr_namespaceURI, ns.namespaceURI, "f");
112
+ this.#potentialValue = this._astToken.potentialValue ?? this.valueNode?.raw ?? '';
113
+ if (this._astToken.potentialName == null) {
114
+ // Try declarative directive pattern resolution from spec
115
+ const patterns = ownElement.ownerMLDocument.specs.directivePatterns ?? [];
116
+ const resolution = patterns.length > 0
117
+ ? resolveDirective(this.nameNode?.raw ?? '', compileDirectivePatterns(patterns))
118
+ : null;
119
+ if (resolution) {
120
+ this.#potentialName = resolution.potentialName ?? this.nameNode?.raw ?? '';
121
+ this.isDynamicValue = resolution.isDynamicValue;
122
+ this.isDirective = resolution.isDirective;
123
+ this.isDuplicatable = resolution.isDuplicatable ?? this._astToken.isDuplicatable;
124
+ if (resolution.valueType) {
125
+ this.valueType = resolution.valueType;
126
+ }
127
+ }
128
+ else {
129
+ this.#potentialName = this.nameNode?.raw ?? '';
130
+ this.isDynamicValue = this._astToken.isDynamicValue;
131
+ this.isDirective = this._astToken.isDirective;
132
+ this.isDuplicatable = this._astToken.isDuplicatable;
133
+ }
134
+ // IDL attribute resolution (after directivePatterns)
135
+ if (ownElement.ownerMLDocument.specs.useIDLAttributeNames && !this.isDirective) {
136
+ const { contentAttrName, idlPropName } = searchIDLAttribute(this.#potentialName);
137
+ if (contentAttrName && contentAttrName !== this.#potentialName) {
138
+ this.#potentialName = contentAttrName;
139
+ }
140
+ // Set candidate for IDL naming suggestions (e.g., tabindex → tabIndex in JSX).
141
+ // Only when no directive pattern transformed the name.
142
+ if (!resolution && idlPropName) {
143
+ const rawName = this.nameNode?.raw ?? '';
144
+ if (rawName !== idlPropName) {
145
+ this.candidate = idlPropName;
146
+ }
147
+ }
148
+ }
149
+ }
150
+ else {
151
+ // Parser-set potentialName takes precedence
152
+ this.#potentialName = this._astToken.potentialName;
153
+ this.isDynamicValue = this._astToken.isDynamicValue;
154
+ this.isDirective = this._astToken.isDirective;
155
+ this.isDuplicatable = this._astToken.isDuplicatable;
156
+ }
157
+ const ns = resolveNamespace(this.#potentialName, ownElement.namespaceURI);
158
+ this.#localName = ns.localName;
159
+ this.#namespaceURI = ns.namespaceURI;
110
160
  }
111
161
  /**
112
162
  * Returns the local name portion of the attribute (without namespace prefix).
@@ -115,7 +165,7 @@ export class MLAttr extends MLNode {
115
165
  * @see https://dom.spec.whatwg.org/#ref-for-dom-attr-localname
116
166
  */
117
167
  get localName() {
118
- return __classPrivateFieldGet(this, _MLAttr_localName, "f");
168
+ return this.#localName;
119
169
  }
120
170
  /**
121
171
  * Returns the qualified attribute name (the potential name resolved by the parser).
@@ -124,7 +174,7 @@ export class MLAttr extends MLNode {
124
174
  * @see https://dom.spec.whatwg.org/#dom-attr-name
125
175
  */
126
176
  get name() {
127
- return __classPrivateFieldGet(this, _MLAttr_potentialName, "f");
177
+ return this.#potentialName;
128
178
  }
129
179
  /**
130
180
  * Returns the namespace URI of this attribute, resolved from the attribute name.
@@ -133,7 +183,7 @@ export class MLAttr extends MLNode {
133
183
  * @see https://dom.spec.whatwg.org/#ref-for-dom-attr-namespaceuri
134
184
  */
135
185
  get namespaceURI() {
136
- return __classPrivateFieldGet(this, _MLAttr_namespaceURI, "f");
186
+ return this.#namespaceURI;
137
187
  }
138
188
  /**
139
189
  * Returns a string appropriate for the type of node as `Attr`
@@ -199,7 +249,7 @@ export class MLAttr extends MLNode {
199
249
  * @see https://dom.spec.whatwg.org/#dom-attr-value
200
250
  */
201
251
  get value() {
202
- return __classPrivateFieldGet(this, _MLAttr_potentialValue, "f");
252
+ return this.#potentialValue;
203
253
  }
204
254
  /**
205
255
  * Fixes the attribute value.
@@ -257,4 +307,3 @@ export class MLAttr extends MLNode {
257
307
  return tokens.join('');
258
308
  }
259
309
  }
260
- _MLAttr_localName = new WeakMap(), _MLAttr_namespaceURI = new WeakMap(), _MLAttr_potentialName = new WeakMap(), _MLAttr_potentialValue = new WeakMap();
@@ -1,7 +1,7 @@
1
1
  import type { MLDocument } from './document.js';
2
2
  import type { MLElement } from './element.js';
3
3
  import type { MarkuplintPreprocessorBlockType } from './types.js';
4
- import type { MLASTPreprocessorSpecificBlock, MLASTPreprocessorSpecificBlockConditionalType } from '@markuplint/ml-ast';
4
+ import type { MLASTPreprocessorSpecificBlock, MLASTBlockBehavior } from '@markuplint/ml-ast';
5
5
  import type { PlainData, RuleConfigValue } from '@markuplint/ml-config';
6
6
  import { MLNode } from './node.js';
7
7
  /**
@@ -14,9 +14,9 @@ import { MLNode } from './node.js';
14
14
  */
15
15
  export declare class MLBlock<T extends RuleConfigValue, O extends PlainData = undefined> extends MLNode<T, O, MLASTPreprocessorSpecificBlock> {
16
16
  /**
17
- * The type of conditional this block represents (e.g., `if`, `each`, `switch:case`).
17
+ * Block behavior associated with this block, if any.
18
18
  */
19
- readonly conditionalType: MLASTPreprocessorSpecificBlockConditionalType;
19
+ readonly blockBehavior: MLASTBlockBehavior | null;
20
20
  /**
21
21
  * Whether this block is transparent, meaning its children are treated
22
22
  * as belonging to the parent node for tree traversal purposes.
@@ -9,6 +9,15 @@ import { MLNode } from './node.js';
9
9
  * @template O - The rule options type
10
10
  */
11
11
  export class MLBlock extends MLNode {
12
+ /**
13
+ * Block behavior associated with this block, if any.
14
+ */
15
+ blockBehavior;
16
+ /**
17
+ * Whether this block is transparent, meaning its children are treated
18
+ * as belonging to the parent node for tree traversal purposes.
19
+ */
20
+ isTransparent;
12
21
  /**
13
22
  * Creates a new MLBlock instance.
14
23
  *
@@ -21,7 +30,7 @@ export class MLBlock extends MLNode {
21
30
  super(astNode, document, astNode.isFragment);
22
31
  // TODO:
23
32
  this.isTransparent = true;
24
- this.conditionalType = astNode.conditionalType;
33
+ this.blockBehavior = astNode.blockBehavior;
25
34
  }
26
35
  /**
27
36
  * Returns a string appropriate for the type of node as `MLBlock`
@@ -8,6 +8,18 @@ import { MLNode } from './node.js';
8
8
  * @template O - The rule options type
9
9
  */
10
10
  export class MLDocumentType extends MLNode {
11
+ /**
12
+ * The name of the document type (e.g., `"html"`).
13
+ */
14
+ name;
15
+ /**
16
+ * The public identifier of the document type, or an empty string if not specified.
17
+ */
18
+ publicId;
19
+ /**
20
+ * The system identifier of the document type, or an empty string if not specified.
21
+ */
22
+ systemId;
11
23
  /**
12
24
  * Creates a new MLDocumentType instance.
13
25
  *
@@ -11,7 +11,7 @@ import type { MLSchema } from '../../types.js';
11
11
  import type { Walker } from '../helper/walkers.js';
12
12
  import type { MLToken } from '../token/token.js';
13
13
  import type { EndTagType, MLASTDocument } from '@markuplint/ml-ast';
14
- import type { PlainData, Pretender, RuleConfigValue } from '@markuplint/ml-config';
14
+ import type { PlainData, Pretender, RuleCommonSettings, RuleConfigValue } from '@markuplint/ml-config';
15
15
  import type { ARIAVersion, MLMLSpec } from '@markuplint/ml-spec';
16
16
  import { MLParentNode } from './parent-node.js';
17
17
  /**
@@ -75,11 +75,18 @@ export declare class MLDocument<T extends RuleConfigValue, O extends PlainData =
75
75
  */
76
76
  readonly tagNameCaseSensitive: boolean;
77
77
  /**
78
- *
78
+ * Common settings applied globally to all rules, such as the ARIA version.
79
+ * Rules use this as a fallback when their own options do not specify a value.
80
+ */
81
+ readonly ruleCommonSettings: RuleCommonSettings;
82
+ /**
79
83
  * @param ast node list of markuplint AST
80
84
  * @param ruleset ruleset object
85
+ * @param schemas base HTML/ARIA spec with optional framework-specific extensions
86
+ * @param ruleCommonSettings common settings applied globally to all rules
87
+ * @param options optional configuration for document behavior
81
88
  */
82
- constructor(ast: MLASTDocument, ruleset: Ruleset, schemas: MLSchema, options?: {
89
+ constructor(ast: MLASTDocument, ruleset: Ruleset, schemas: MLSchema, ruleCommonSettings: RuleCommonSettings, options?: {
83
90
  readonly filename?: string;
84
91
  readonly endTag?: 'xml' | 'omittable' | 'never';
85
92
  readonly booleanish?: boolean;
@@ -1515,6 +1522,10 @@ export declare class MLDocument<T extends RuleConfigValue, O extends PlainData =
1515
1522
  * ARIA role, accessible name, focusability, and ARIA property values.
1516
1523
  * Returns null for non-element nodes.
1517
1524
  *
1525
+ * The accessible name is obtained via `node.getAccessibleName()` so that
1526
+ * the per-element memoization cache is shared with other consumers
1527
+ * (rules, selectors). See {@link MLElement.getAccessibleName}.
1528
+ *
1518
1529
  * @param node - The node to compute accessibility properties for
1519
1530
  * @param ariaVersion - The ARIA specification version to use for computation
1520
1531
  * @returns The computed accessibility properties, or null for non-element nodes
@@ -1,17 +1,5 @@
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 _MLDocument_filename, _MLDocument_tokenList;
13
1
  import { exchangeValueOnRule, mergeRule } from '@markuplint/ml-config';
14
- import { schemaToSpec, getAccname, getComputedRole, mayBeFocusable, getComputedAriaProps, isExposed, ARIA_RECOMMENDED_VERSION, } from '@markuplint/ml-spec';
2
+ import { schemaToSpec, getComputedRole, mayBeFocusable, getComputedAriaProps, isExposed, ARIA_RECOMMENDED_VERSION, } from '@markuplint/ml-spec';
15
3
  import { ConfigParserError } from '@markuplint/parser-utils';
16
4
  import { InvalidSelectorError } from '@markuplint/selector';
17
5
  import { log as coreLog } from '../../debug.js';
@@ -36,30 +24,81 @@ const ruleLog = docLog.extend('rule');
36
24
  */
37
25
  export class MLDocument extends MLParentNode {
38
26
  /**
27
+ * Detect value as a true if its attribute is booleanish value and omitted.
39
28
  *
29
+ * Ex:
30
+ * ```jsx
31
+ * <Component aria-hidden />
32
+ * ```
33
+ *
34
+ * In the above, the `aria-hidden` is `true`.
35
+ *
36
+ * @default false
37
+ */
38
+ booleanish;
39
+ /**
40
+ * The rule currently being evaluated during a lint pass.
41
+ * Set by the linting engine before rule evaluation begins and used by nodes
42
+ * to retrieve their rule configuration. Null when no rule is being evaluated.
43
+ */
44
+ currentRule = null;
45
+ /**
46
+ * This is defined by the parser.
47
+ *
48
+ * @default "omittable"
49
+ */
50
+ endTag;
51
+ /**
52
+ * The file path of the source document, if available.
53
+ */
54
+ #filename;
55
+ /**
56
+ * Whether this document represents a fragment rather than a complete document.
57
+ * Fragment documents may lack root-level elements like `<html>`, `<head>`, or `<body>`.
58
+ */
59
+ isFragment;
60
+ /**
61
+ * A flat, ordered array of all markuplint DOM nodes in the document,
62
+ * used for sequential traversal and token-level operations.
63
+ */
64
+ nodeList;
65
+ ontouchcancel;
66
+ ontouchend;
67
+ ontouchmove;
68
+ ontouchstart;
69
+ /**
70
+ * The ML specification data used for element/attribute lookups, ARIA role resolution,
71
+ * and other spec-driven computations.
72
+ */
73
+ specs;
74
+ /**
75
+ * Whether tag name comparisons should be case-sensitive.
76
+ * When false (default for HTML), tag names are compared case-insensitively.
77
+ */
78
+ tagNameCaseSensitive;
79
+ /**
80
+ * Common settings applied globally to all rules, such as the ARIA version.
81
+ * Rules use this as a fallback when their own options do not specify a value.
82
+ */
83
+ ruleCommonSettings;
84
+ #tokenList = null;
85
+ /**
40
86
  * @param ast node list of markuplint AST
41
87
  * @param ruleset ruleset object
88
+ * @param schemas base HTML/ARIA spec with optional framework-specific extensions
89
+ * @param ruleCommonSettings common settings applied globally to all rules
90
+ * @param options optional configuration for document behavior
42
91
  */
43
- constructor(ast, ruleset, schemas, options) {
92
+ constructor(ast, ruleset, schemas, ruleCommonSettings, options) {
44
93
  // @ts-ignore
45
94
  super(ast, null);
46
- /**
47
- * The rule currently being evaluated during a lint pass.
48
- * Set by the linting engine before rule evaluation begins and used by nodes
49
- * to retrieve their rule configuration. Null when no rule is being evaluated.
50
- */
51
- this.currentRule = null;
52
- /**
53
- * The file path of the source document, if available.
54
- */
55
- _MLDocument_filename.set(this, void 0);
56
- _MLDocument_tokenList.set(this, null);
57
95
  this.isFragment = ast.isFragment;
58
96
  this.specs = schemaToSpec(schemas);
59
97
  this.booleanish = options?.booleanish ?? false;
60
98
  this.endTag = options?.endTag ?? 'omittable';
61
- __classPrivateFieldSet(this, _MLDocument_filename, options?.filename, "f");
99
+ this.#filename = options?.filename;
62
100
  this.tagNameCaseSensitive = options?.tagNameCaseSensitive ?? false;
101
+ this.ruleCommonSettings = ruleCommonSettings;
63
102
  // console.log(ast.nodeList.map((n, i) => `${i}: ${n.uuid} "${n.raw.trim()}"(${n.type})`));
64
103
  this.nodeList = Object.freeze(ast.nodeList
65
104
  .map(astNode => {
@@ -325,7 +364,7 @@ export class MLDocument extends MLParentNode {
325
364
  * @implements `@markuplint/ml-core` API: `MLDOMDocument`
326
365
  */
327
366
  get filename() {
328
- return __classPrivateFieldGet(this, _MLDocument_filename, "f");
367
+ return this.#filename;
329
368
  }
330
369
  /**
331
370
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
@@ -1928,6 +1967,10 @@ export class MLDocument extends MLParentNode {
1928
1967
  * ARIA role, accessible name, focusability, and ARIA property values.
1929
1968
  * Returns null for non-element nodes.
1930
1969
  *
1970
+ * The accessible name is obtained via `node.getAccessibleName()` so that
1971
+ * the per-element memoization cache is shared with other consumers
1972
+ * (rules, selectors). See {@link MLElement.getAccessibleName}.
1973
+ *
1931
1974
  * @param node - The node to compute accessibility properties for
1932
1975
  * @param ariaVersion - The ARIA specification version to use for computation
1933
1976
  * @returns The computed accessibility properties, or null for non-element nodes
@@ -1955,7 +1998,7 @@ export class MLDocument extends MLParentNode {
1955
1998
  exposedToTree: true,
1956
1999
  };
1957
2000
  const role = getComputedRole(node.ownerMLDocument.specs, node, ariaVersion);
1958
- const name = getAccname(node).trim();
2001
+ const name = node.getAccessibleName(ariaVersion).trim();
1959
2002
  const focusable = mayBeFocusable(node, node.ownerMLDocument.specs);
1960
2003
  const nameRequired = role.role?.accessibleNameRequired ?? false;
1961
2004
  const nameProhibited = role.role?.accessibleNameProhibited ?? false;
@@ -2053,8 +2096,8 @@ export class MLDocument extends MLParentNode {
2053
2096
  * @returns A frozen array of tokens sorted by their starting offset
2054
2097
  */
2055
2098
  getTokenList() {
2056
- if (__classPrivateFieldGet(this, _MLDocument_tokenList, "f")) {
2057
- return __classPrivateFieldGet(this, _MLDocument_tokenList, "f");
2099
+ if (this.#tokenList) {
2100
+ return this.#tokenList;
2058
2101
  }
2059
2102
  const tokens = [];
2060
2103
  for (const node of this.nodeList) {
@@ -2063,9 +2106,8 @@ export class MLDocument extends MLParentNode {
2063
2106
  tokens.push(node.closeTag);
2064
2107
  }
2065
2108
  }
2066
- tokens.sort((a, b) => a.startOffset - b.startOffset);
2067
- __classPrivateFieldSet(this, _MLDocument_tokenList, Object.freeze(tokens), "f");
2068
- return __classPrivateFieldGet(this, _MLDocument_tokenList, "f");
2109
+ this.#tokenList = Object.freeze(tokens.toSorted((a, b) => a.startOffset - b.startOffset));
2110
+ return this.#tokenList;
2069
2111
  }
2070
2112
  /**
2071
2113
  * **IT THROWS AN ERROR WHEN CALLING THIS.**
@@ -2418,4 +2460,3 @@ export class MLDocument extends MLParentNode {
2418
2460
  ruleMapper.apply();
2419
2461
  }
2420
2462
  }
2421
- _MLDocument_filename = new WeakMap(), _MLDocument_tokenList = new WeakMap();
@@ -1,18 +1,13 @@
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 _MLDomTokenList_origin, _MLDomTokenList_ownerAttrs, _MLDomTokenList_set;
13
1
  import { getPosition } from '@markuplint/parser-utils/location';
14
2
  import { UnexpectedCallError } from './unexpected-call-error.js';
15
3
  export class MLDomTokenList extends Array {
4
+ #origin;
5
+ /**
6
+ * In some cases, an author specifies multiple attributes or directives.
7
+ * The reference is not always one.
8
+ */
9
+ #ownerAttrs;
10
+ #set;
16
11
  constructor(tokens,
17
12
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
18
13
  ownerAttrs) {
@@ -21,29 +16,22 @@ export class MLDomTokenList extends Array {
21
16
  .map(t => t.trim())
22
17
  .filter(t => !!t);
23
18
  super(...list);
24
- _MLDomTokenList_origin.set(this, void 0);
25
- /**
26
- * In some cases, an author specifies multiple attributes or directives.
27
- * The reference is not always one.
28
- */
29
- _MLDomTokenList_ownerAttrs.set(this, void 0);
30
- _MLDomTokenList_set.set(this, void 0);
31
- __classPrivateFieldSet(this, _MLDomTokenList_origin, tokens, "f");
32
- __classPrivateFieldSet(this, _MLDomTokenList_ownerAttrs, ownerAttrs, "f");
33
- __classPrivateFieldSet(this, _MLDomTokenList_set, new Set(list), "f");
19
+ this.#origin = tokens;
20
+ this.#ownerAttrs = ownerAttrs;
21
+ this.#set = new Set(list);
34
22
  }
35
23
  get value() {
36
24
  return this.join(' ');
37
25
  }
38
26
  add(...tokens) {
39
27
  for (const token of tokens) {
40
- if (__classPrivateFieldGet(this, _MLDomTokenList_set, "f").has(token)) {
28
+ if (this.#set.has(token)) {
41
29
  continue;
42
30
  }
43
- __classPrivateFieldGet(this, _MLDomTokenList_set, "f").add(token);
31
+ this.#set.add(token);
44
32
  this.push(token);
45
33
  }
46
- __classPrivateFieldSet(this, _MLDomTokenList_origin, __classPrivateFieldGet(this, _MLDomTokenList_origin, "f") + tokens.join(' '), "f");
34
+ this.#origin += tokens.join(' ');
47
35
  }
48
36
  /**
49
37
  * @implements `@markuplint/ml-core` API: `MLDomTokenList`
@@ -74,7 +62,7 @@ export class MLDomTokenList extends Array {
74
62
  return locs;
75
63
  }
76
64
  contains(token) {
77
- return __classPrivateFieldGet(this, _MLDomTokenList_set, "f").has(token);
65
+ return this.#set.has(token);
78
66
  }
79
67
  forEach(callbackfn, thisArg) {
80
68
  this.forEach.bind(this)((v, i) => callbackfn(v, i, thisArg ?? this));
@@ -118,15 +106,15 @@ export class MLDomTokenList extends Array {
118
106
  if (!token) {
119
107
  return null;
120
108
  }
121
- for (const ownerAttr of __classPrivateFieldGet(this, _MLDomTokenList_ownerAttrs, "f")) {
109
+ for (const ownerAttr of this.#ownerAttrs) {
122
110
  if (ownerAttr.isDynamicValue) {
123
111
  continue;
124
112
  }
125
- const startOffset = __classPrivateFieldGet(this, _MLDomTokenList_origin, "f").indexOf(token, _offset);
113
+ const startOffset = this.#origin.indexOf(token, _offset);
126
114
  if (startOffset === -1) {
127
115
  continue;
128
116
  }
129
- const position = getPosition(__classPrivateFieldGet(this, _MLDomTokenList_origin, "f"), startOffset);
117
+ const position = getPosition(this.#origin, startOffset);
130
118
  const startLine = position.line;
131
119
  const startCol = position.column;
132
120
  return {
@@ -141,4 +129,3 @@ export class MLDomTokenList extends Array {
141
129
  return null;
142
130
  }
143
131
  }
144
- _MLDomTokenList_origin = new WeakMap(), _MLDomTokenList_ownerAttrs = new WeakMap(), _MLDomTokenList_set = new WeakMap();
@@ -1,5 +1,6 @@
1
1
  import { MLNode } from './node.js';
2
2
  export class MLElementCloseTag extends MLNode {
3
+ pair;
3
4
  constructor(astNode,
4
5
  // eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
5
6
  document,