@markuplint/ml-core 4.13.3 → 5.0.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.ja.md +92 -35
- package/ARCHITECTURE.md +85 -28
- package/CHANGELOG.md +50 -0
- package/docs/linting-pipeline.ja.md +18 -14
- package/docs/linting-pipeline.md +18 -14
- package/docs/maintenance.ja.md +1 -1
- package/docs/maintenance.md +1 -1
- package/docs/ml-dom/attr.ja.md +8 -0
- package/docs/ml-dom/attr.md +8 -0
- package/docs/ml-dom/block.ja.md +33 -33
- package/docs/ml-dom/block.md +33 -33
- package/docs/ml-dom/element.ja.md +17 -17
- package/docs/ml-dom/element.md +17 -17
- package/docs/ml-dom/node.ja.md +4 -5
- package/docs/ml-dom/node.md +4 -5
- package/docs/ml-dom/others.ja.md +2 -1
- package/docs/ml-dom/others.md +5 -4
- package/docs/rule-system.ja.md +23 -6
- package/docs/rule-system.md +23 -6
- package/lib/index.d.ts +4 -3
- package/lib/index.js +1 -1
- package/lib/ml-core.d.ts +1 -1
- package/lib/ml-core.js +142 -82
- package/lib/ml-dom/helper/accname.d.ts +8 -0
- package/lib/ml-dom/helper/accname.js +71 -55
- package/lib/ml-dom/helper/create-node.js +1 -0
- package/lib/ml-dom/helper/get-indent.js +17 -29
- package/lib/ml-dom/node/attr.js +122 -73
- package/lib/ml-dom/node/block.d.ts +3 -3
- package/lib/ml-dom/node/block.js +10 -1
- package/lib/ml-dom/node/document-type.js +12 -0
- package/lib/ml-dom/node/document.d.ts +14 -3
- package/lib/ml-dom/node/document.js +75 -34
- package/lib/ml-dom/node/dom-token-list.js +17 -30
- package/lib/ml-dom/node/element-close-tag.js +1 -0
- package/lib/ml-dom/node/element.d.ts +19 -7
- package/lib/ml-dom/node/element.js +134 -55
- package/lib/ml-dom/node/node-store.js +6 -15
- package/lib/ml-dom/node/node.d.ts +3 -1
- package/lib/ml-dom/node/node.js +159 -166
- package/lib/ml-dom/node/parent-node.js +14 -30
- package/lib/ml-dom/node/rule-mapper.js +7 -20
- package/lib/ml-dom/node/text.d.ts +7 -0
- package/lib/ml-dom/node/text.js +9 -0
- package/lib/ml-dom/token/token.js +23 -39
- package/lib/ml-rule/create-rule.d.ts +8 -1
- package/lib/ml-rule/create-rule.js +0 -9
- package/lib/ml-rule/ml-rule-context.js +7 -11
- package/lib/ml-rule/ml-rule.d.ts +33 -1
- package/lib/ml-rule/ml-rule.js +65 -25
- package/lib/ruleset/index.js +6 -0
- package/lib/test/index.js +4 -1
- package/lib/types.d.ts +2 -1
- package/lib/violation-collector.js +15 -28
- package/lib/virtual-rule.d.ts +72 -0
- package/lib/virtual-rule.js +233 -0
- package/package.json +16 -13
package/lib/ml-dom/node/attr.js
CHANGED
|
@@ -1,16 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
-
|
|
93
|
+
this.#namespaceURI = ownElement.namespaceURI;
|
|
84
94
|
this.valueType = 'code';
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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
|
-
*
|
|
17
|
+
* Block behavior associated with this block, if any.
|
|
18
18
|
*/
|
|
19
|
-
readonly
|
|
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.
|
package/lib/ml-dom/node/block.js
CHANGED
|
@@ -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.
|
|
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,
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 (
|
|
2057
|
-
return
|
|
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.
|
|
2067
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 (
|
|
28
|
+
if (this.#set.has(token)) {
|
|
41
29
|
continue;
|
|
42
30
|
}
|
|
43
|
-
|
|
31
|
+
this.#set.add(token);
|
|
44
32
|
this.push(token);
|
|
45
33
|
}
|
|
46
|
-
|
|
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
|
|
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
|
|
109
|
+
for (const ownerAttr of this.#ownerAttrs) {
|
|
122
110
|
if (ownerAttr.isDynamicValue) {
|
|
123
111
|
continue;
|
|
124
112
|
}
|
|
125
|
-
const startOffset =
|
|
113
|
+
const startOffset = this.#origin.indexOf(token, _offset);
|
|
126
114
|
if (startOffset === -1) {
|
|
127
115
|
continue;
|
|
128
116
|
}
|
|
129
|
-
const position = getPosition(
|
|
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();
|