@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
|
@@ -3,11 +3,10 @@ import type { MLNamedNodeMap } from './named-node-map.js';
|
|
|
3
3
|
import type { MLNode } from './node.js';
|
|
4
4
|
import type { MLText } from './text.js';
|
|
5
5
|
import type { ElementNodeType, PretenderContext } from './types.js';
|
|
6
|
-
import type { ElementType, MLASTElement, NamespaceURI } from '@markuplint/ml-ast';
|
|
6
|
+
import type { ElementType, MLASTBlockBehavior, MLASTElement, NamespaceURI } from '@markuplint/ml-ast';
|
|
7
7
|
import type { PlainData, Pretender, RegexSelector, RuleConfigValue, RuleInfo } from '@markuplint/ml-config';
|
|
8
8
|
import type { ARIAVersion } from '@markuplint/ml-spec';
|
|
9
9
|
import type { SelectorMatches } from '@markuplint/selector';
|
|
10
|
-
import { MLToken } from '../token/token.js';
|
|
11
10
|
import { MLAttr } from './attr.js';
|
|
12
11
|
import { MLDomTokenList } from './dom-token-list.js';
|
|
13
12
|
import { MLElementCloseTag } from './element-close-tag.js';
|
|
@@ -57,9 +56,9 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
|
|
|
57
56
|
*/
|
|
58
57
|
pretenderContext: PretenderContext<MLElement<T, O>, T, O> | null;
|
|
59
58
|
/**
|
|
60
|
-
*
|
|
59
|
+
* Block behavior associated with this element, if any.
|
|
61
60
|
*/
|
|
62
|
-
readonly
|
|
61
|
+
readonly blockBehavior: MLASTBlockBehavior | null;
|
|
63
62
|
/**
|
|
64
63
|
* The tag close character string (e.g., `>` or `/>` or `%>`).
|
|
65
64
|
*/
|
|
@@ -1836,12 +1835,25 @@ export declare class MLElement<T extends RuleConfigValue, O extends PlainData =
|
|
|
1836
1835
|
*/
|
|
1837
1836
|
focus(options?: FocusOptions): void;
|
|
1838
1837
|
/**
|
|
1839
|
-
*
|
|
1840
|
-
*
|
|
1838
|
+
* Returns the accessible name of this element, with per-element memoization.
|
|
1839
|
+
*
|
|
1840
|
+
* On the first call for a given ARIA version the full AccName Computation
|
|
1841
|
+
* algorithm runs (tree walk, `aria-labelledby` resolution, `<label>` lookup,
|
|
1842
|
+
* etc.) and the result is stored in {@link #accessibleNameCache}.
|
|
1843
|
+
* Subsequent calls for the same version return the cached value in O(1).
|
|
1844
|
+
*
|
|
1845
|
+
* **All callers that need the accessible name of an MLElement should use
|
|
1846
|
+
* this method** rather than importing `getAccname()` directly, so that
|
|
1847
|
+
* every consumer benefits from the shared cache. This includes the
|
|
1848
|
+
* `:aria(has name)` selector in `@markuplint/selector`, which uses
|
|
1849
|
+
* duck-typing to detect and call this method.
|
|
1850
|
+
*
|
|
1851
|
+
* Added as part of the fix for {@link https://github.com/markuplint/markuplint/issues/2179 | #2179}
|
|
1852
|
+
* to eliminate redundant AccName computation across rules.
|
|
1841
1853
|
*
|
|
1842
1854
|
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
1843
1855
|
* @param version - The ARIA specification version to use for computation
|
|
1844
|
-
* @returns The computed accessible name string
|
|
1856
|
+
* @returns The computed accessible name string (may be empty)
|
|
1845
1857
|
*/
|
|
1846
1858
|
getAccessibleName(version: ARIAVersion): string;
|
|
1847
1859
|
/**
|
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
/* global StylePropertyMap, StylePropertyMapReadOnly */
|
|
2
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
-
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");
|
|
6
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
-
};
|
|
8
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
-
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");
|
|
11
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
-
};
|
|
13
|
-
var _MLElement_attributes, _MLElement_fixedNodeName, _MLElement_getChildElementsAndTextNodeWithoutWhitespacesCache, _MLElement_localName, _MLElement_normalizedAttrs, _MLElement_normalizedString;
|
|
14
2
|
import { resolveNamespace } from '@markuplint/ml-spec';
|
|
15
3
|
import { matchSelector } from '@markuplint/selector';
|
|
16
4
|
import { getAccname } from '../helper/accname.js';
|
|
17
5
|
import { after, before, nextElementSibling, previousElementSibling, remove, replaceWith, } from '../manipulations/child-node-methods.js';
|
|
18
|
-
import { MLToken } from '../token/token.js';
|
|
19
6
|
import { MLAttr } from './attr.js';
|
|
20
7
|
import { MLDomTokenList } from './dom-token-list.js';
|
|
21
8
|
import { MLElementCloseTag } from './element-close-tag.js';
|
|
@@ -34,6 +21,91 @@ const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
|
34
21
|
* @template O - The rule options type
|
|
35
22
|
*/
|
|
36
23
|
export class MLElement extends MLParentNode {
|
|
24
|
+
#attributes;
|
|
25
|
+
/**
|
|
26
|
+
* The closing tag for this element, or null if the element is self-closing or void.
|
|
27
|
+
*/
|
|
28
|
+
closeTag;
|
|
29
|
+
/**
|
|
30
|
+
* Element type
|
|
31
|
+
*
|
|
32
|
+
* - `html`: From native HTML Standard
|
|
33
|
+
* - `web-component`: As the Web Component according to HTML Standard
|
|
34
|
+
* - `authored`: Authored element (JSX Element etc.) through the view framework or the template engine.
|
|
35
|
+
*/
|
|
36
|
+
elementType;
|
|
37
|
+
#fixedNodeName;
|
|
38
|
+
#getChildElementsAndTextNodeWithoutWhitespacesCache = null;
|
|
39
|
+
/**
|
|
40
|
+
* Whether this element belongs to a non-HTML namespace (e.g., SVG or MathML).
|
|
41
|
+
*/
|
|
42
|
+
isForeignElement;
|
|
43
|
+
/**
|
|
44
|
+
* Whether this element was implicitly created (e.g., an omitted `<body>` tag in HTML).
|
|
45
|
+
*/
|
|
46
|
+
isOmitted;
|
|
47
|
+
#localName;
|
|
48
|
+
/**
|
|
49
|
+
* The namespace URI of this element (e.g., `http://www.w3.org/1999/xhtml` for HTML elements).
|
|
50
|
+
*/
|
|
51
|
+
namespaceURI;
|
|
52
|
+
/**
|
|
53
|
+
* Memoization cache for the accessible name computation, keyed by ARIA version.
|
|
54
|
+
*
|
|
55
|
+
* ## Why this cache exists
|
|
56
|
+
*
|
|
57
|
+
* Multiple rules and the `:aria(has name)` selector evaluate the accessible name
|
|
58
|
+
* of every element during a single lint pass. Without caching, the same
|
|
59
|
+
* expensive tree-walking algorithm (AccName Computation 1.2) runs repeatedly
|
|
60
|
+
* for the same element — once per consumer. Benchmarks on a 500-element page
|
|
61
|
+
* show **14,626 total calls** of which **11,573 (79%) are cache hits**,
|
|
62
|
+
* eliminating redundant computation.
|
|
63
|
+
*
|
|
64
|
+
* ## Why memoization is safe
|
|
65
|
+
*
|
|
66
|
+
* The MLDOM is **immutable** once constructed — no attributes or child nodes
|
|
67
|
+
* change during a lint pass. Therefore the accessible name for a given ARIA
|
|
68
|
+
* version is deterministic and will never become stale. No invalidation
|
|
69
|
+
* logic is needed. The cache is garbage-collected together with the
|
|
70
|
+
* MLElement instance when the document is released.
|
|
71
|
+
*
|
|
72
|
+
* ## Consumers that benefit
|
|
73
|
+
*
|
|
74
|
+
* - `require-accessible-name` rule (direct call)
|
|
75
|
+
* - `wai-aria` rule via `:aria(has name)` selector
|
|
76
|
+
* - `neighbor-popovers` rule
|
|
77
|
+
* - `landmark-roles` rule
|
|
78
|
+
* - `MLDocument.getAccessibilityProp()` (accessibility tree builder)
|
|
79
|
+
*
|
|
80
|
+
* Introduced to resolve {@link https://github.com/markuplint/markuplint/issues/2179 | #2179}
|
|
81
|
+
* (AccName performance bottleneck).
|
|
82
|
+
*
|
|
83
|
+
* @see {@link getAccessibleName} — the public method that reads/writes this cache
|
|
84
|
+
*/
|
|
85
|
+
#accessibleNameCache = new Map();
|
|
86
|
+
#normalizedAttrs = new Map();
|
|
87
|
+
#normalizedString = null;
|
|
88
|
+
ontouchcancel;
|
|
89
|
+
ontouchend;
|
|
90
|
+
ontouchmove;
|
|
91
|
+
ontouchstart;
|
|
92
|
+
/**
|
|
93
|
+
* The pretender context if this element is participating in pretender behavior,
|
|
94
|
+
* or null if it is not a pretender or pretended element.
|
|
95
|
+
*/
|
|
96
|
+
pretenderContext = null;
|
|
97
|
+
/**
|
|
98
|
+
* Block behavior associated with this element, if any.
|
|
99
|
+
*/
|
|
100
|
+
blockBehavior;
|
|
101
|
+
/**
|
|
102
|
+
* The tag close character string (e.g., `>` or `/>` or `%>`).
|
|
103
|
+
*/
|
|
104
|
+
tagCloseChar;
|
|
105
|
+
/**
|
|
106
|
+
* The tag open character string (e.g., `<` or `<%`).
|
|
107
|
+
*/
|
|
108
|
+
tagOpenChar;
|
|
37
109
|
/**
|
|
38
110
|
* Creates a new MLElement instance from an AST element node.
|
|
39
111
|
*
|
|
@@ -44,29 +116,18 @@ export class MLElement extends MLParentNode {
|
|
|
44
116
|
// eslint-disable-next-line @typescript-eslint/prefer-readonly-parameter-types
|
|
45
117
|
document) {
|
|
46
118
|
super(astNode, document, astNode.isFragment);
|
|
47
|
-
|
|
48
|
-
_MLElement_fixedNodeName.set(this, void 0);
|
|
49
|
-
_MLElement_getChildElementsAndTextNodeWithoutWhitespacesCache.set(this, null);
|
|
50
|
-
_MLElement_localName.set(this, void 0);
|
|
51
|
-
_MLElement_normalizedAttrs.set(this, new Map());
|
|
52
|
-
_MLElement_normalizedString.set(this, null);
|
|
53
|
-
/**
|
|
54
|
-
* The pretender context if this element is participating in pretender behavior,
|
|
55
|
-
* or null if it is not a pretender or pretended element.
|
|
56
|
-
*/
|
|
57
|
-
this.pretenderContext = null;
|
|
58
|
-
__classPrivateFieldSet(this, _MLElement_attributes, astNode.attributes.map(attr => new MLAttr(attr, this)), "f");
|
|
59
|
-
this.selfClosingSolidus = astNode.selfClosingSolidus ? new MLToken(astNode.selfClosingSolidus) : null;
|
|
119
|
+
this.#attributes = astNode.attributes.map(attr => new MLAttr(attr, this));
|
|
60
120
|
this.closeTag = astNode.pairNode ? new MLElementCloseTag(astNode.pairNode, document, this) : null;
|
|
61
121
|
const ns = resolveNamespace(astNode.nodeName, astNode.namespace);
|
|
62
|
-
this.namespaceURI =
|
|
122
|
+
this.namespaceURI = astNode.namespace;
|
|
63
123
|
this.elementType = astNode.elementType;
|
|
64
|
-
|
|
124
|
+
this.#localName = ns.localName;
|
|
65
125
|
this.isForeignElement = this.namespaceURI !== HTML_NAMESPACE;
|
|
66
|
-
|
|
126
|
+
this.#fixedNodeName = astNode.nodeName;
|
|
67
127
|
this.isOmitted = astNode.isGhost;
|
|
68
128
|
this.tagOpenChar = astNode.tagOpenChar;
|
|
69
129
|
this.tagCloseChar = astNode.tagCloseChar;
|
|
130
|
+
this.blockBehavior = astNode.blockBehavior;
|
|
70
131
|
}
|
|
71
132
|
/**
|
|
72
133
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
@@ -622,9 +683,9 @@ export class MLElement extends MLParentNode {
|
|
|
622
683
|
* @see https://dom.spec.whatwg.org/#dom-element-attributes
|
|
623
684
|
*/
|
|
624
685
|
get attributes() {
|
|
625
|
-
const origin = this.pretenderContext?.type === 'pretender' ?
|
|
626
|
-
if (
|
|
627
|
-
return
|
|
686
|
+
const origin = this.pretenderContext?.type === 'pretender' ? this.pretenderContext.as.#attributes : this.#attributes;
|
|
687
|
+
if (this.#normalizedAttrs.has(origin)) {
|
|
688
|
+
return this.#normalizedAttrs.get(origin);
|
|
628
689
|
}
|
|
629
690
|
const names = new Set();
|
|
630
691
|
const attrs = [];
|
|
@@ -640,7 +701,7 @@ export class MLElement extends MLParentNode {
|
|
|
640
701
|
attrs.push(attr);
|
|
641
702
|
}
|
|
642
703
|
const map = toNamedNodeMap(attrs);
|
|
643
|
-
|
|
704
|
+
this.#normalizedAttrs.set(origin, map);
|
|
644
705
|
return map;
|
|
645
706
|
}
|
|
646
707
|
/**
|
|
@@ -796,13 +857,13 @@ export class MLElement extends MLParentNode {
|
|
|
796
857
|
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
797
858
|
*/
|
|
798
859
|
get fixedNodeName() {
|
|
799
|
-
return
|
|
860
|
+
return this.#fixedNodeName;
|
|
800
861
|
}
|
|
801
862
|
/**
|
|
802
863
|
* Whether this element has any spread attributes (e.g., `{...props}` in JSX).
|
|
803
864
|
*/
|
|
804
865
|
get hasSpreadAttr() {
|
|
805
|
-
return
|
|
866
|
+
return this.#attributes.some(attr => attr.localName === '#spread');
|
|
806
867
|
}
|
|
807
868
|
/**
|
|
808
869
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
@@ -902,12 +963,12 @@ export class MLElement extends MLParentNode {
|
|
|
902
963
|
return this.pretenderContext.as.localName;
|
|
903
964
|
}
|
|
904
965
|
if (this.isForeignElement || this.elementType !== 'html') {
|
|
905
|
-
return
|
|
966
|
+
return this.#localName;
|
|
906
967
|
}
|
|
907
968
|
if (this.ownerMLDocument.tagNameCaseSensitive) {
|
|
908
|
-
return
|
|
969
|
+
return this.#localName;
|
|
909
970
|
}
|
|
910
|
-
return
|
|
971
|
+
return this.#localName.toLowerCase();
|
|
911
972
|
}
|
|
912
973
|
/**
|
|
913
974
|
* The element immediately following the specified one in its parent's children list.
|
|
@@ -2331,7 +2392,7 @@ export class MLElement extends MLParentNode {
|
|
|
2331
2392
|
* @param name - The new node name to set
|
|
2332
2393
|
*/
|
|
2333
2394
|
fixNodeName(name) {
|
|
2334
|
-
|
|
2395
|
+
this.#fixedNodeName = name;
|
|
2335
2396
|
}
|
|
2336
2397
|
/**
|
|
2337
2398
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
@@ -2346,15 +2407,34 @@ export class MLElement extends MLParentNode {
|
|
|
2346
2407
|
throw new UnexpectedCallError('Not supported "focus" method');
|
|
2347
2408
|
}
|
|
2348
2409
|
/**
|
|
2349
|
-
*
|
|
2350
|
-
*
|
|
2410
|
+
* Returns the accessible name of this element, with per-element memoization.
|
|
2411
|
+
*
|
|
2412
|
+
* On the first call for a given ARIA version the full AccName Computation
|
|
2413
|
+
* algorithm runs (tree walk, `aria-labelledby` resolution, `<label>` lookup,
|
|
2414
|
+
* etc.) and the result is stored in {@link #accessibleNameCache}.
|
|
2415
|
+
* Subsequent calls for the same version return the cached value in O(1).
|
|
2416
|
+
*
|
|
2417
|
+
* **All callers that need the accessible name of an MLElement should use
|
|
2418
|
+
* this method** rather than importing `getAccname()` directly, so that
|
|
2419
|
+
* every consumer benefits from the shared cache. This includes the
|
|
2420
|
+
* `:aria(has name)` selector in `@markuplint/selector`, which uses
|
|
2421
|
+
* duck-typing to detect and call this method.
|
|
2422
|
+
*
|
|
2423
|
+
* Added as part of the fix for {@link https://github.com/markuplint/markuplint/issues/2179 | #2179}
|
|
2424
|
+
* to eliminate redundant AccName computation across rules.
|
|
2351
2425
|
*
|
|
2352
2426
|
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2353
2427
|
* @param version - The ARIA specification version to use for computation
|
|
2354
|
-
* @returns The computed accessible name string
|
|
2428
|
+
* @returns The computed accessible name string (may be empty)
|
|
2355
2429
|
*/
|
|
2356
2430
|
getAccessibleName(version) {
|
|
2357
|
-
|
|
2431
|
+
const cached = this.#accessibleNameCache.get(version);
|
|
2432
|
+
if (cached != null) {
|
|
2433
|
+
return cached;
|
|
2434
|
+
}
|
|
2435
|
+
const name = getAccname(this, version);
|
|
2436
|
+
this.#accessibleNameCache.set(version, name);
|
|
2437
|
+
return name;
|
|
2358
2438
|
}
|
|
2359
2439
|
/**
|
|
2360
2440
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
@@ -2423,7 +2503,7 @@ export class MLElement extends MLParentNode {
|
|
|
2423
2503
|
* @returns The attribute value, or null if the attribute is not found
|
|
2424
2504
|
*/
|
|
2425
2505
|
getAttributePretended(attrName) {
|
|
2426
|
-
for (const attr of
|
|
2506
|
+
for (const attr of this.#attributes) {
|
|
2427
2507
|
if (attr.name.toLowerCase() === attrName.toLowerCase()) {
|
|
2428
2508
|
return attr.value;
|
|
2429
2509
|
}
|
|
@@ -2456,7 +2536,7 @@ export class MLElement extends MLParentNode {
|
|
|
2456
2536
|
* @returns A frozen array of all attribute tokens
|
|
2457
2537
|
*/
|
|
2458
2538
|
getAttributeTokens() {
|
|
2459
|
-
return Object.freeze(this.pretenderContext?.type === 'pretender' ?
|
|
2539
|
+
return Object.freeze(this.pretenderContext?.type === 'pretender' ? this.pretenderContext.as.#attributes : this.#attributes);
|
|
2460
2540
|
}
|
|
2461
2541
|
/**
|
|
2462
2542
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
@@ -2476,8 +2556,8 @@ export class MLElement extends MLParentNode {
|
|
|
2476
2556
|
* @returns An array of child elements and non-whitespace text nodes
|
|
2477
2557
|
*/
|
|
2478
2558
|
getChildElementsAndTextNodeWithoutWhitespaces() {
|
|
2479
|
-
if (
|
|
2480
|
-
return
|
|
2559
|
+
if (this.#getChildElementsAndTextNodeWithoutWhitespacesCache) {
|
|
2560
|
+
return this.#getChildElementsAndTextNodeWithoutWhitespacesCache;
|
|
2481
2561
|
}
|
|
2482
2562
|
const filteredNodes = [];
|
|
2483
2563
|
for (const node of this.childNodes) {
|
|
@@ -2494,7 +2574,7 @@ export class MLElement extends MLParentNode {
|
|
|
2494
2574
|
filteredNodes.push(node);
|
|
2495
2575
|
}
|
|
2496
2576
|
}
|
|
2497
|
-
|
|
2577
|
+
this.#getChildElementsAndTextNodeWithoutWhitespacesCache = filteredNodes;
|
|
2498
2578
|
return filteredNodes;
|
|
2499
2579
|
}
|
|
2500
2580
|
/**
|
|
@@ -2616,7 +2696,7 @@ export class MLElement extends MLParentNode {
|
|
|
2616
2696
|
hasMutableChildren(attr = false) {
|
|
2617
2697
|
for (const child of this.getPureChildNodes()) {
|
|
2618
2698
|
if (child.is(child.MARKUPLINT_PREPROCESSOR_BLOCK)) {
|
|
2619
|
-
if (child.
|
|
2699
|
+
if (child.blockBehavior) {
|
|
2620
2700
|
continue;
|
|
2621
2701
|
}
|
|
2622
2702
|
return true;
|
|
@@ -2787,7 +2867,7 @@ export class MLElement extends MLParentNode {
|
|
|
2787
2867
|
return;
|
|
2788
2868
|
}
|
|
2789
2869
|
let nodeName;
|
|
2790
|
-
let namespace = '
|
|
2870
|
+
let namespace = 'http://www.w3.org/1999/xhtml';
|
|
2791
2871
|
const attributes = [];
|
|
2792
2872
|
let aria;
|
|
2793
2873
|
if (typeof pretenderElement === 'string') {
|
|
@@ -2795,7 +2875,7 @@ export class MLElement extends MLParentNode {
|
|
|
2795
2875
|
}
|
|
2796
2876
|
else {
|
|
2797
2877
|
nodeName = pretenderElement.element;
|
|
2798
|
-
namespace = pretenderElement.namespace
|
|
2878
|
+
namespace = pretenderElement.namespace === 'svg' ? 'http://www.w3.org/2000/svg' : namespace;
|
|
2799
2879
|
if (pretenderElement.inheritAttrs) {
|
|
2800
2880
|
attributes.push(...this._astToken.attributes);
|
|
2801
2881
|
}
|
|
@@ -3077,8 +3157,8 @@ export class MLElement extends MLParentNode {
|
|
|
3077
3157
|
* @returns The normalized HTML string of this element
|
|
3078
3158
|
*/
|
|
3079
3159
|
toNormalizeString() {
|
|
3080
|
-
if (
|
|
3081
|
-
return
|
|
3160
|
+
if (this.#normalizedString) {
|
|
3161
|
+
return this.#normalizedString;
|
|
3082
3162
|
}
|
|
3083
3163
|
const children = this.getChildElementsAndTextNodeWithoutWhitespaces();
|
|
3084
3164
|
const attrs = this.attributes.map(attr => attr.toNormalizeString());
|
|
@@ -3092,7 +3172,7 @@ export class MLElement extends MLParentNode {
|
|
|
3092
3172
|
});
|
|
3093
3173
|
const endTag = `</${this.nodeName}>`;
|
|
3094
3174
|
const normalizedString = `${startTag}${childNodes.join('')}${endTag}`;
|
|
3095
|
-
|
|
3175
|
+
this.#normalizedString = normalizedString;
|
|
3096
3176
|
return normalizedString;
|
|
3097
3177
|
}
|
|
3098
3178
|
/**
|
|
@@ -3174,4 +3254,3 @@ export class MLElement extends MLParentNode {
|
|
|
3174
3254
|
throw new UnexpectedCallError('Not supported "webkitMatchesSelector" method');
|
|
3175
3255
|
}
|
|
3176
3256
|
}
|
|
3177
|
-
_MLElement_attributes = new WeakMap(), _MLElement_fixedNodeName = new WeakMap(), _MLElement_getChildElementsAndTextNodeWithoutWhitespacesCache = new WeakMap(), _MLElement_localName = new WeakMap(), _MLElement_normalizedAttrs = new WeakMap(), _MLElement_normalizedString = new WeakMap();
|
|
@@ -1,29 +1,21 @@
|
|
|
1
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
-
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");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var _NodeStore_store;
|
|
7
1
|
import { TargetParserError } from '@markuplint/parser-utils';
|
|
8
2
|
import { log } from '../../debug.js';
|
|
9
3
|
const nodeStoreLog = log.extend('node-store');
|
|
10
4
|
const nodeStoreError = nodeStoreLog.extend('error');
|
|
11
5
|
class NodeStore {
|
|
12
|
-
|
|
13
|
-
_NodeStore_store.set(this, new Map());
|
|
14
|
-
}
|
|
6
|
+
#store = new Map();
|
|
15
7
|
getNode(astNode) {
|
|
16
8
|
// console.log(`Get: ${astNode.uuid} -> ${astNode.raw.trim()}(${astNode.type})`);
|
|
17
|
-
const node =
|
|
9
|
+
const node = this.#store.get(astNode.uuid);
|
|
18
10
|
if (!node) {
|
|
19
11
|
nodeStoreError('Ref ID: %s (%s: "%s")', astNode.uuid, astNode.nodeName, astNode.raw);
|
|
20
|
-
nodeStoreError('Map: %O', [...
|
|
12
|
+
nodeStoreError('Map: %O', [...this.#store.entries()].map(([id, node]) => ({
|
|
21
13
|
id,
|
|
22
14
|
name: node.nodeName,
|
|
23
15
|
})));
|
|
24
16
|
throw new TargetParserError('Broke mapping nodes.', {
|
|
25
|
-
line: astNode.
|
|
26
|
-
col: astNode.
|
|
17
|
+
line: astNode.line,
|
|
18
|
+
col: astNode.col,
|
|
27
19
|
raw: astNode.raw,
|
|
28
20
|
nodeName: astNode.nodeName,
|
|
29
21
|
});
|
|
@@ -41,10 +33,9 @@ class NodeStore {
|
|
|
41
33
|
nodeStoreError('Invalid node: %O', node);
|
|
42
34
|
}
|
|
43
35
|
nodeStoreLog('Mapped: %s (%s: "%s")', astNode.uuid, astNode.nodeName, astNode.raw.replaceAll('\n', '⏎').replaceAll('\t', '→'));
|
|
44
|
-
|
|
36
|
+
this.#store.set(astNode.uuid, node);
|
|
45
37
|
}
|
|
46
38
|
}
|
|
47
|
-
_NodeStore_store = new WeakMap();
|
|
48
39
|
/**
|
|
49
40
|
* `NodeStore` Singleton
|
|
50
41
|
*/
|
|
@@ -136,6 +136,8 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
|
|
|
136
136
|
get baseURI(): string;
|
|
137
137
|
/**
|
|
138
138
|
* The list of child nodes that contains `Element`, `Text`, and `Comment`.
|
|
139
|
+
* Fragment nodes and preprocessor blocks with `each` or `end` block behavior
|
|
140
|
+
* are transparent — their child nodes are flattened into this list.
|
|
139
141
|
*
|
|
140
142
|
* @readonly
|
|
141
143
|
* @implements DOM API: `Node`
|
|
@@ -376,7 +378,7 @@ export declare abstract class MLNode<T extends RuleConfigValue, O extends PlainD
|
|
|
376
378
|
/**
|
|
377
379
|
* Returns an array of NodeLists representing the conditional child nodes of the current node.
|
|
378
380
|
* Conditional child nodes are nodes that are nested within
|
|
379
|
-
* **preprocessor blocks** such as `if
|
|
381
|
+
* **preprocessor blocks** such as `if` or `switch`.
|
|
380
382
|
* Each NodeList represents a branch of conditional child nodes.
|
|
381
383
|
*
|
|
382
384
|
* Note: NodeList doesn't include whitespace nodes.
|