@markuplint/ml-core 5.4.0 → 5.6.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/CHANGELOG.md +29 -0
- package/lib/ml-core.js +3 -0
- package/lib/ml-dom/helper/accname.d.ts +35 -0
- package/lib/ml-dom/helper/accname.js +69 -17
- package/lib/ml-dom/helper/debug.js +1 -1
- package/lib/ml-dom/helper/literal-values.d.ts +102 -0
- package/lib/ml-dom/helper/literal-values.js +193 -0
- package/lib/ml-dom/helper/pretended-attrs.d.ts +16 -0
- package/lib/ml-dom/helper/pretended-attrs.js +21 -0
- package/lib/ml-dom/helper/slot-assignment.d.ts +48 -0
- package/lib/ml-dom/helper/slot-assignment.js +292 -0
- package/lib/ml-dom/helper/tree-scope.d.ts +130 -0
- package/lib/ml-dom/helper/tree-scope.js +341 -0
- package/lib/ml-dom/helper/walkers.d.ts +15 -0
- package/lib/ml-dom/helper/walkers.js +34 -0
- package/lib/ml-dom/index.d.ts +4 -0
- package/lib/ml-dom/index.js +2 -0
- package/lib/ml-dom/node/attr.d.ts +77 -10
- package/lib/ml-dom/node/attr.js +209 -20
- package/lib/ml-dom/node/block.d.ts +15 -6
- package/lib/ml-dom/node/block.js +17 -8
- package/lib/ml-dom/node/document-fragment.d.ts +18 -6
- package/lib/ml-dom/node/document-fragment.js +19 -7
- package/lib/ml-dom/node/document.d.ts +36 -7
- package/lib/ml-dom/node/document.js +60 -17
- package/lib/ml-dom/node/dom-token-list.js +28 -3
- package/lib/ml-dom/node/element.d.ts +148 -24
- package/lib/ml-dom/node/element.js +258 -48
- package/lib/ml-dom/node/named-node-map.js +2 -1
- package/lib/ml-dom/node/node-list.js +2 -3
- package/lib/ml-dom/node/node.d.ts +55 -10
- package/lib/ml-dom/node/node.js +98 -13
- package/lib/ml-dom/node/parent-node.d.ts +39 -8
- package/lib/ml-dom/node/parent-node.js +79 -12
- package/lib/ml-dom/node/shadow-root.d.ts +162 -0
- package/lib/ml-dom/node/shadow-root.js +219 -0
- package/lib/ml-dom/node/text.d.ts +12 -3
- package/lib/ml-dom/node/text.js +14 -3
- package/lib/ml-rule/ml-rule.js +4 -1
- package/lib/test/index.js +3 -0
- package/lib/utils/index.d.ts +2 -0
- package/lib/utils/index.js +1 -0
- package/lib/utils/search-node-at-location.d.ts +39 -0
- package/lib/utils/search-node-at-location.js +53 -0
- package/package.json +12 -15
|
@@ -2,17 +2,23 @@
|
|
|
2
2
|
var _a;
|
|
3
3
|
import { getSpecByTagName, resolveNamespace } from '@markuplint/ml-spec';
|
|
4
4
|
import { pretenderNamespaceToURI } from '@markuplint/ml-config';
|
|
5
|
-
import { getChildNamespace } from '@markuplint/parser-utils';
|
|
6
|
-
import { matchSelector, typeNameMatches } from '@markuplint/selector';
|
|
5
|
+
import { ConfigParserError, getChildNamespace } from '@markuplint/parser-utils';
|
|
6
|
+
import { InvalidSelectorError, matchSelector, typeNameMatches } from '@markuplint/selector';
|
|
7
7
|
import { getAccname, getAccnameVisits, getHostLanguageLabelSource, getReferencedTextAlternative, } from '../helper/accname.js';
|
|
8
|
+
import { literalToExpression } from '../helper/literal-values.js';
|
|
9
|
+
import { markPretendedAttr } from '../helper/pretended-attrs.js';
|
|
10
|
+
import { getAssignedNodes, getAssignedSlot, getFlatTreeSlot, getFlattenedSlottables, } from '../helper/slot-assignment.js';
|
|
11
|
+
import { getDeclarativeShadowRootAttachment, getDeclarativeShadowRootMode, getShadowRootTemplate, isHTMLTemplateElement, } from '../helper/tree-scope.js';
|
|
8
12
|
import { after, before, nextElementSibling, previousElementSibling, remove, replaceWith, } from '../manipulations/child-node-methods.js';
|
|
9
13
|
import { MLAttr } from './attr.js';
|
|
14
|
+
import { MLDocumentFragment } from './document-fragment.js';
|
|
10
15
|
import { MLDomTokenList } from './dom-token-list.js';
|
|
11
16
|
import { MLElementCloseTag } from './element-close-tag.js';
|
|
12
17
|
import { toNamedNodeMap } from './named-node-map.js';
|
|
13
18
|
import { toHTMLCollection, toNodeList } from './node-list.js';
|
|
14
19
|
import { MLParentNode } from './parent-node.js';
|
|
15
|
-
import {
|
|
20
|
+
import { MLShadowRoot } from './shadow-root.js';
|
|
21
|
+
import { UnexpectedCallError, asciiLowerCase, isFatalError, vuePropKey } from '@markuplint/shared';
|
|
16
22
|
const HTML_NAMESPACE = 'http://www.w3.org/1999/xhtml';
|
|
17
23
|
/**
|
|
18
24
|
* Represents a DOM Element node wrapper in the markuplint DOM tree.
|
|
@@ -830,14 +836,15 @@ export class MLElement extends MLParentNode {
|
|
|
830
836
|
throw new UnexpectedCallError('Not supported "ariaValueText" property');
|
|
831
837
|
}
|
|
832
838
|
/**
|
|
833
|
-
*
|
|
839
|
+
* The slot of an open declarative shadow root that this element is assigned to, or `null` when no
|
|
840
|
+
* slot takes it, the root is closed (the DOM does not expose it), or the assignment is not known
|
|
841
|
+
* (see {@link MLElement.getFlatTreeSlot}).
|
|
834
842
|
*
|
|
835
|
-
* @unsupported
|
|
836
843
|
* @implements DOM API: `Element`
|
|
837
844
|
* @see https://dom.spec.whatwg.org/#dom-slotable-assignedslot
|
|
838
845
|
*/
|
|
839
846
|
get assignedSlot() {
|
|
840
|
-
|
|
847
|
+
return getAssignedSlot(this);
|
|
841
848
|
}
|
|
842
849
|
/**
|
|
843
850
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
@@ -918,7 +925,7 @@ export class MLElement extends MLParentNode {
|
|
|
918
925
|
*
|
|
919
926
|
* @unsupported
|
|
920
927
|
* @implements DOM API: `Element`
|
|
921
|
-
* @see https://www.w3.org/TR/cssom-view-1/#
|
|
928
|
+
* @see https://www.w3.org/TR/cssom-view-1/#dom-element-clientheight
|
|
922
929
|
*/
|
|
923
930
|
get clientHeight() {
|
|
924
931
|
throw new UnexpectedCallError('Not supported "clientHeight" property');
|
|
@@ -928,7 +935,7 @@ export class MLElement extends MLParentNode {
|
|
|
928
935
|
*
|
|
929
936
|
* @unsupported
|
|
930
937
|
* @implements DOM API: `Element`
|
|
931
|
-
* @see https://www.w3.org/TR/cssom-view-1/#
|
|
938
|
+
* @see https://www.w3.org/TR/cssom-view-1/#dom-element-clientleft
|
|
932
939
|
*/
|
|
933
940
|
get clientLeft() {
|
|
934
941
|
throw new UnexpectedCallError('Not supported "clientLeft" property');
|
|
@@ -938,7 +945,7 @@ export class MLElement extends MLParentNode {
|
|
|
938
945
|
*
|
|
939
946
|
* @unsupported
|
|
940
947
|
* @implements DOM API: `Element`
|
|
941
|
-
* @see https://www.w3.org/TR/cssom-view-1/#
|
|
948
|
+
* @see https://www.w3.org/TR/cssom-view-1/#dom-element-clienttop
|
|
942
949
|
*/
|
|
943
950
|
get clientTop() {
|
|
944
951
|
throw new UnexpectedCallError('Not supported "clientTop" property');
|
|
@@ -948,7 +955,7 @@ export class MLElement extends MLParentNode {
|
|
|
948
955
|
*
|
|
949
956
|
* @unsupported
|
|
950
957
|
* @implements DOM API: `Element`
|
|
951
|
-
* @see https://www.w3.org/TR/cssom-view-1/#
|
|
958
|
+
* @see https://www.w3.org/TR/cssom-view-1/#dom-element-clientwidth
|
|
952
959
|
*/
|
|
953
960
|
get clientWidth() {
|
|
954
961
|
throw new UnexpectedCallError('Not supported "clientWidth" property');
|
|
@@ -1052,7 +1059,7 @@ export class MLElement extends MLParentNode {
|
|
|
1052
1059
|
* @deprecated
|
|
1053
1060
|
* @unsupported
|
|
1054
1061
|
* @implements DOM API: `Element`
|
|
1055
|
-
* @see https://html.spec.whatwg.org/multipage/
|
|
1062
|
+
* @see https://html.spec.whatwg.org/multipage/dom.html#dom-inert
|
|
1056
1063
|
*/
|
|
1057
1064
|
get inert() {
|
|
1058
1065
|
throw new UnexpectedCallError('Not supported "inert" property');
|
|
@@ -1062,7 +1069,7 @@ export class MLElement extends MLParentNode {
|
|
|
1062
1069
|
*
|
|
1063
1070
|
* @unsupported
|
|
1064
1071
|
* @implements DOM API: `Element`
|
|
1065
|
-
* @see https://w3c.github.io/DOM-Parsing/#dom-innerhtml
|
|
1072
|
+
* @see https://w3c.github.io/DOM-Parsing/#dom-innerhtml
|
|
1066
1073
|
*/
|
|
1067
1074
|
get innerHTML() {
|
|
1068
1075
|
throw new UnexpectedCallError('Not supported "innerHTML" property');
|
|
@@ -2304,7 +2311,7 @@ export class MLElement extends MLParentNode {
|
|
|
2304
2311
|
*
|
|
2305
2312
|
* @unsupported
|
|
2306
2313
|
* @implements DOM API: `Element`
|
|
2307
|
-
* @see https://www.w3.org/TR/cssom-view-1/#
|
|
2314
|
+
* @see https://www.w3.org/TR/cssom-view-1/#dom-element-scrollheight
|
|
2308
2315
|
*/
|
|
2309
2316
|
get scrollHeight() {
|
|
2310
2317
|
throw new UnexpectedCallError('Not supported "scrollHeight" property');
|
|
@@ -2314,7 +2321,7 @@ export class MLElement extends MLParentNode {
|
|
|
2314
2321
|
*
|
|
2315
2322
|
* @unsupported
|
|
2316
2323
|
* @implements DOM API: `Element`
|
|
2317
|
-
* @see https://www.w3.org/TR/cssom-view-1/#
|
|
2324
|
+
* @see https://www.w3.org/TR/cssom-view-1/#dom-element-scrollleft
|
|
2318
2325
|
*/
|
|
2319
2326
|
get scrollLeft() {
|
|
2320
2327
|
throw new UnexpectedCallError('Not supported "scrollLeft" property');
|
|
@@ -2324,7 +2331,7 @@ export class MLElement extends MLParentNode {
|
|
|
2324
2331
|
*
|
|
2325
2332
|
* @unsupported
|
|
2326
2333
|
* @implements DOM API: `Element`
|
|
2327
|
-
* @see https://www.w3.org/TR/cssom-view-1/#
|
|
2334
|
+
* @see https://www.w3.org/TR/cssom-view-1/#dom-element-scrolltop
|
|
2328
2335
|
*/
|
|
2329
2336
|
get scrollTop() {
|
|
2330
2337
|
throw new UnexpectedCallError('Not supported "scrollTop" property');
|
|
@@ -2334,21 +2341,114 @@ export class MLElement extends MLParentNode {
|
|
|
2334
2341
|
*
|
|
2335
2342
|
* @unsupported
|
|
2336
2343
|
* @implements DOM API: `Element`
|
|
2337
|
-
* @see https://www.w3.org/TR/cssom-view-1/#
|
|
2344
|
+
* @see https://www.w3.org/TR/cssom-view-1/#dom-element-scrollwidth
|
|
2338
2345
|
*/
|
|
2339
2346
|
get scrollWidth() {
|
|
2340
2347
|
throw new UnexpectedCallError('Not supported "scrollWidth" property');
|
|
2341
2348
|
}
|
|
2342
2349
|
/**
|
|
2343
|
-
*
|
|
2350
|
+
* The shadow root that a `template` element with `shadowrootmode="open"` declares on this element:
|
|
2351
|
+
* `null` for a `closed` one, which the DOM does not expose, and when there is none. See
|
|
2352
|
+
* {@link MLElement.declarativeShadowRoot} for one of either mode.
|
|
2344
2353
|
*
|
|
2345
|
-
* @unsupported
|
|
2346
2354
|
* @implements DOM API: `Element`
|
|
2347
2355
|
* @see https://dom.spec.whatwg.org/#ref-for-dom-element-shadowroot%E2%91%A0
|
|
2348
2356
|
*/
|
|
2349
2357
|
get shadowRoot() {
|
|
2350
|
-
|
|
2358
|
+
const shadowRoot = this.declarativeShadowRoot;
|
|
2359
|
+
return shadowRoot?.mode === 'open' ? shadowRoot : null;
|
|
2360
|
+
}
|
|
2361
|
+
/**
|
|
2362
|
+
* Whether this element is an HTML `template` element, which holds its children in {@link MLElement.content},
|
|
2363
|
+
* a tree of its own. A `template` of a framework that puts the children in the element itself
|
|
2364
|
+
* (`templateContent: 'in-place'` in the spec: React, Vue), a fragment, and a `template` with a
|
|
2365
|
+
* directive of a template engine are not.
|
|
2366
|
+
*
|
|
2367
|
+
* The getters about trees (this one, {@link MLElement.declarativeShadowRootAttachment},
|
|
2368
|
+
* {@link MLElement.content}, `getRootNode()` and the id lookups) keep what they first return: they
|
|
2369
|
+
* depend on the pretenders, the spec and the whole syntax tree, which are all in place once the
|
|
2370
|
+
* document is built, and the document does not change after that.
|
|
2371
|
+
*
|
|
2372
|
+
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2373
|
+
* @see https://html.spec.whatwg.org/multipage/scripting.html#the-template-element
|
|
2374
|
+
*/
|
|
2375
|
+
get isHTMLTemplateElement() {
|
|
2376
|
+
this.#isHTMLTemplateElement ??= isHTMLTemplateElement(this);
|
|
2377
|
+
return this.#isHTMLTemplateElement;
|
|
2378
|
+
}
|
|
2379
|
+
#isHTMLTemplateElement;
|
|
2380
|
+
/**
|
|
2381
|
+
* Whether this element is in no tree: an HTML `template` element that a declarative shadow root is
|
|
2382
|
+
* attached to, or that may have one (see {@link DeclarativeShadowRootAttachment}). The parser inserts
|
|
2383
|
+
* such an element into no tree, so it has no parent in a tree, is its own root, and is found by no
|
|
2384
|
+
* query of a tree. Unknown is treated as in no tree, as markuplint does not count what it cannot tell.
|
|
2385
|
+
*
|
|
2386
|
+
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2387
|
+
*/
|
|
2388
|
+
get isInNoTree() {
|
|
2389
|
+
return this.isHTMLTemplateElement && this.declarativeShadowRootAttachment !== 'not-attached';
|
|
2390
|
+
}
|
|
2391
|
+
/**
|
|
2392
|
+
* The mode that the `shadowrootmode` attribute of an HTML `template` element asks for. `null` when the
|
|
2393
|
+
* element is not one, the attribute is missing or invalid, or its value is dynamic.
|
|
2394
|
+
*
|
|
2395
|
+
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2396
|
+
*/
|
|
2397
|
+
get declarativeShadowRootMode() {
|
|
2398
|
+
return getDeclarativeShadowRootMode(this);
|
|
2399
|
+
}
|
|
2400
|
+
/**
|
|
2401
|
+
* Whether the HTML parser attaches a declarative shadow root to the parent of this `template`
|
|
2402
|
+
* element (see {@link DeclarativeShadowRootAttachment}).
|
|
2403
|
+
*
|
|
2404
|
+
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2405
|
+
*/
|
|
2406
|
+
get declarativeShadowRootAttachment() {
|
|
2407
|
+
this.#declarativeShadowRootAttachment ??= getDeclarativeShadowRootAttachment(this);
|
|
2408
|
+
return this.#declarativeShadowRootAttachment;
|
|
2409
|
+
}
|
|
2410
|
+
#declarativeShadowRootAttachment;
|
|
2411
|
+
/**
|
|
2412
|
+
* The content of an HTML `template` element: a document fragment that is a tree of its own, holding
|
|
2413
|
+
* the same nodes as the children of the element. When a declarative shadow root is attached to the
|
|
2414
|
+
* parent of the `template`, it is the shadow root. `null` for an element that is not an HTML
|
|
2415
|
+
* `template` element.
|
|
2416
|
+
*
|
|
2417
|
+
* @implements DOM API: `HTMLTemplateElement`
|
|
2418
|
+
* @see https://html.spec.whatwg.org/multipage/scripting.html#dom-template-content
|
|
2419
|
+
*/
|
|
2420
|
+
get content() {
|
|
2421
|
+
if (this.#content !== undefined) {
|
|
2422
|
+
return this.#content;
|
|
2423
|
+
}
|
|
2424
|
+
if (!this.isHTMLTemplateElement) {
|
|
2425
|
+
this.#content = null;
|
|
2426
|
+
return null;
|
|
2427
|
+
}
|
|
2428
|
+
const astNode = { ...this._astToken, uuid: `${this.uuid}:content` };
|
|
2429
|
+
this.#content =
|
|
2430
|
+
this.declarativeShadowRootAttachment === 'attached'
|
|
2431
|
+
? new MLShadowRoot(astNode, this.ownerMLDocument, this)
|
|
2432
|
+
: new MLDocumentFragment(astNode, this.ownerMLDocument);
|
|
2433
|
+
return this.#content;
|
|
2434
|
+
}
|
|
2435
|
+
#content;
|
|
2436
|
+
/**
|
|
2437
|
+
* The shadow root that a `template` element declares on this element, whatever its mode.
|
|
2438
|
+
*
|
|
2439
|
+
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2440
|
+
* @returns The shadow root, or `null` when none is attached to this element
|
|
2441
|
+
*/
|
|
2442
|
+
get declarativeShadowRoot() {
|
|
2443
|
+
if (this.#declarativeShadowRoot !== undefined) {
|
|
2444
|
+
return this.#declarativeShadowRoot;
|
|
2445
|
+
}
|
|
2446
|
+
const template = getShadowRootTemplate(this);
|
|
2447
|
+
const content = template?.content;
|
|
2448
|
+
this.#declarativeShadowRoot = content instanceof MLShadowRoot ? content : null;
|
|
2449
|
+
return this.#declarativeShadowRoot;
|
|
2351
2450
|
}
|
|
2451
|
+
#declarativeShadowRoot;
|
|
2352
2452
|
/**
|
|
2353
2453
|
* @implements DOM API: `Element`
|
|
2354
2454
|
* @see https://dom.spec.whatwg.org/#ref-for-dom-element-slot%E2%91%A0
|
|
@@ -2468,13 +2568,43 @@ export class MLElement extends MLParentNode {
|
|
|
2468
2568
|
throw new UnexpectedCallError('Not supported "animate" method');
|
|
2469
2569
|
}
|
|
2470
2570
|
/**
|
|
2571
|
+
* The nodes this `slot` places, in tree order, or with `flatten` what it renders in the flat tree. It
|
|
2572
|
+
* is empty when the assignment is not known; use {@link MLElement.getAssignedNodes} to tell the two apart.
|
|
2573
|
+
*
|
|
2574
|
+
* @implements DOM API: `HTMLSlotElement`
|
|
2471
2575
|
* @see https://html.spec.whatwg.org/multipage/scripting.html#dom-slot-assignednodes
|
|
2472
2576
|
*/
|
|
2473
|
-
assignedNodes() {
|
|
2577
|
+
assignedNodes(options) {
|
|
2474
2578
|
if (this.localName !== 'slot') {
|
|
2475
2579
|
throw new TypeError('assignedNodes is not a function');
|
|
2476
2580
|
}
|
|
2477
|
-
return [];
|
|
2581
|
+
return [...(options?.flatten ? getFlattenedSlottables(this) : (this.getAssignedNodes() ?? []))];
|
|
2582
|
+
}
|
|
2583
|
+
/**
|
|
2584
|
+
* The nodes this `slot` places, by the named slot assignment of the declarative shadow root it is in.
|
|
2585
|
+
* A slot that takes none has `[]`, and renders its own children. `null` when the assignment is not
|
|
2586
|
+
* known; what makes it so is in the module comment of `helper/slot-assignment.ts`.
|
|
2587
|
+
*
|
|
2588
|
+
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2589
|
+
* @see https://dom.spec.whatwg.org/#find-slotables
|
|
2590
|
+
*/
|
|
2591
|
+
getAssignedNodes() {
|
|
2592
|
+
return getAssignedNodes(this);
|
|
2593
|
+
}
|
|
2594
|
+
/**
|
|
2595
|
+
* The slot that places this element in the flat tree: the slot of the shadow root of its parent that
|
|
2596
|
+
* takes it. `null` when its parent is a shadow host and no slot takes it, so that it is not rendered;
|
|
2597
|
+
* `undefined` when its parent is not a shadow host, or the assignment is not known
|
|
2598
|
+
* (see {@link MLElement.getAssignedNodes}).
|
|
2599
|
+
*
|
|
2600
|
+
* `getComposedParentElement` of `@markuplint/ml-spec` reads it as the parent that the accessibility
|
|
2601
|
+
* tree has for the element.
|
|
2602
|
+
*
|
|
2603
|
+
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2604
|
+
* @see https://dom.spec.whatwg.org/#find-a-slot
|
|
2605
|
+
*/
|
|
2606
|
+
getFlatTreeSlot() {
|
|
2607
|
+
return getFlatTreeSlot(this);
|
|
2478
2608
|
}
|
|
2479
2609
|
/**
|
|
2480
2610
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
@@ -2549,7 +2679,7 @@ export class MLElement extends MLParentNode {
|
|
|
2549
2679
|
if (el.matches(selectors)) {
|
|
2550
2680
|
return el;
|
|
2551
2681
|
}
|
|
2552
|
-
el = el.
|
|
2682
|
+
el = el.treeParentElement;
|
|
2553
2683
|
} while (el !== null && el.is(el.ELEMENT_NODE));
|
|
2554
2684
|
return null;
|
|
2555
2685
|
}
|
|
@@ -2690,7 +2820,8 @@ export class MLElement extends MLParentNode {
|
|
|
2690
2820
|
let first = null;
|
|
2691
2821
|
let lastSetByCode = null;
|
|
2692
2822
|
for (const attr of this.attributes) {
|
|
2693
|
-
|
|
2823
|
+
// An attribute that the framework does not output (`disabled={false}`) is not an attribute of the element
|
|
2824
|
+
if (attr.isAbsent || !attr.isNamedBy(attrName)) {
|
|
2694
2825
|
continue;
|
|
2695
2826
|
}
|
|
2696
2827
|
first ??= attr;
|
|
@@ -2720,7 +2851,7 @@ export class MLElement extends MLParentNode {
|
|
|
2720
2851
|
* @see https://dom.spec.whatwg.org/#ref-for-dom-element-getattributenames%E2%91%A0
|
|
2721
2852
|
*/
|
|
2722
2853
|
getAttributeNames() {
|
|
2723
|
-
return [...this.attributes].map(attr => attr.name);
|
|
2854
|
+
return [...this.attributes].filter(attr => !attr.isAbsent).map(attr => attr.name);
|
|
2724
2855
|
}
|
|
2725
2856
|
/**
|
|
2726
2857
|
* @implements DOM API: `Element`
|
|
@@ -2753,7 +2884,7 @@ export class MLElement extends MLParentNode {
|
|
|
2753
2884
|
*/
|
|
2754
2885
|
getAttributePretended(attrName) {
|
|
2755
2886
|
for (const attr of this.#attributes) {
|
|
2756
|
-
if (attr.isNamedBy(attrName)) {
|
|
2887
|
+
if (!attr.isAbsent && attr.isNamedBy(attrName)) {
|
|
2757
2888
|
return attr.value;
|
|
2758
2889
|
}
|
|
2759
2890
|
}
|
|
@@ -2761,7 +2892,8 @@ export class MLElement extends MLParentNode {
|
|
|
2761
2892
|
}
|
|
2762
2893
|
/**
|
|
2763
2894
|
* Returns all attribute tokens matching the given name, including duplicates.
|
|
2764
|
-
* Unlike `getAttribute`, this returns the full `MLAttr` token objects.
|
|
2895
|
+
* Unlike `getAttribute`, this returns the full `MLAttr` token objects. An attribute that the
|
|
2896
|
+
* framework does not output (`MLAttr#isAbsent`) is not one of them, as it is not for `getAttribute`.
|
|
2765
2897
|
*
|
|
2766
2898
|
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2767
2899
|
* @param attrName - The attribute name to look up (see {@link MLAttr.isNamedBy} for how its case is compared)
|
|
@@ -2770,7 +2902,7 @@ export class MLElement extends MLParentNode {
|
|
|
2770
2902
|
getAttributeToken(attrName) {
|
|
2771
2903
|
const attrs = [];
|
|
2772
2904
|
for (const attr of this.getAttributeTokens()) {
|
|
2773
|
-
if (attr.isNamedBy(attrName)) {
|
|
2905
|
+
if (!attr.isAbsent && attr.isNamedBy(attrName)) {
|
|
2774
2906
|
attrs.push(attr);
|
|
2775
2907
|
}
|
|
2776
2908
|
}
|
|
@@ -2779,6 +2911,9 @@ export class MLElement extends MLParentNode {
|
|
|
2779
2911
|
/**
|
|
2780
2912
|
* Returns all attribute tokens for this element, respecting the pretender context.
|
|
2781
2913
|
* If the element is pretending to be another, returns the pretender's attributes.
|
|
2914
|
+
* It lists every attribute that is written, those that the framework does not output
|
|
2915
|
+
* (`MLAttr#isAbsent`) too, as a name that is misspelled is so whatever its value is; what is asked
|
|
2916
|
+
* of the DOM by name (`getAttributeToken`, `getAttribute`) does not find them.
|
|
2782
2917
|
*
|
|
2783
2918
|
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2784
2919
|
* @returns A frozen array of all attribute tokens
|
|
@@ -2791,7 +2926,7 @@ export class MLElement extends MLParentNode {
|
|
|
2791
2926
|
*
|
|
2792
2927
|
* @unsupported
|
|
2793
2928
|
* @implements DOM API: `Element`
|
|
2794
|
-
* @see https://www.w3.org/TR/cssom-view-1/#
|
|
2929
|
+
* @see https://www.w3.org/TR/cssom-view-1/#dom-element-getboundingclientrect
|
|
2795
2930
|
*/
|
|
2796
2931
|
getBoundingClientRect() {
|
|
2797
2932
|
throw new UnexpectedCallError('Not supported "getBoundingClientRect" method');
|
|
@@ -2830,7 +2965,7 @@ export class MLElement extends MLParentNode {
|
|
|
2830
2965
|
*
|
|
2831
2966
|
* @unsupported
|
|
2832
2967
|
* @implements DOM API: `Element`
|
|
2833
|
-
* @see https://www.w3.org/TR/cssom-view-1/#
|
|
2968
|
+
* @see https://www.w3.org/TR/cssom-view-1/#dom-element-getclientrects
|
|
2834
2969
|
*/
|
|
2835
2970
|
getClientRects() {
|
|
2836
2971
|
throw new UnexpectedCallError('Not supported "getClientRects" method');
|
|
@@ -2912,7 +3047,7 @@ export class MLElement extends MLParentNode {
|
|
|
2912
3047
|
* @see https://dom.spec.whatwg.org/#dom-element-hasattributes
|
|
2913
3048
|
*/
|
|
2914
3049
|
hasAttributes() {
|
|
2915
|
-
return this.attributes.
|
|
3050
|
+
return this.attributes.some(attr => !attr.isAbsent);
|
|
2916
3051
|
}
|
|
2917
3052
|
/**
|
|
2918
3053
|
* Checks whether this element has any mutable attributes, such as spread
|
|
@@ -2926,16 +3061,37 @@ export class MLElement extends MLParentNode {
|
|
|
2926
3061
|
if (!attr.nameNode) {
|
|
2927
3062
|
return true;
|
|
2928
3063
|
}
|
|
2929
|
-
|
|
3064
|
+
// An attribute that is known not to be output does not change
|
|
3065
|
+
if (attr.isDynamicValue && !attr.isAbsent) {
|
|
2930
3066
|
return true;
|
|
2931
3067
|
}
|
|
2932
3068
|
}
|
|
2933
3069
|
return false;
|
|
2934
3070
|
}
|
|
3071
|
+
/**
|
|
3072
|
+
* Checks whether this element is a component whose rendered output is not known: the
|
|
3073
|
+
* parser sees it as a component (`elementType` is `'authored'`) and no pretender stands
|
|
3074
|
+
* for an element of HTML, which includes a pretender whose own target is a component
|
|
3075
|
+
* (the chain of `pretenders.auto` stopped at `Tabs.Root`).
|
|
3076
|
+
*
|
|
3077
|
+
* A web component is not one: it is the element that it is written as. The criterion of
|
|
3078
|
+
* `isTreatedAsCustom` in `@markuplint/rules` (`permitted-contents`) includes it, because
|
|
3079
|
+
* a content model accepts a custom element wherever it accepts flow content; change them
|
|
3080
|
+
* together.
|
|
3081
|
+
*
|
|
3082
|
+
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
3083
|
+
* @returns True if what this element renders is not known
|
|
3084
|
+
*/
|
|
3085
|
+
isUnresolvedComponent() {
|
|
3086
|
+
if (this.pretenderContext?.type === 'pretender') {
|
|
3087
|
+
return this.pretenderContext.as.elementType === 'authored';
|
|
3088
|
+
}
|
|
3089
|
+
return this.elementType === 'authored';
|
|
3090
|
+
}
|
|
2935
3091
|
/**
|
|
2936
3092
|
* Checks whether this element has children that are potentially mutable,
|
|
2937
3093
|
* such as preprocessor-specific blocks, slot elements, or (optionally) elements
|
|
2938
|
-
* with dynamic attributes.
|
|
3094
|
+
* with dynamic attributes and components that no pretender resolved.
|
|
2939
3095
|
*
|
|
2940
3096
|
* Blocks that carry a `blockBehavior` are not treated as mutable because
|
|
2941
3097
|
* their branches are deterministically enumerable via
|
|
@@ -2943,11 +3099,29 @@ export class MLElement extends MLParentNode {
|
|
|
2943
3099
|
* like `{value}`) can produce arbitrary content, so the children are
|
|
2944
3100
|
* considered mutable.
|
|
2945
3101
|
*
|
|
3102
|
+
* A component that no pretender resolved renders something that is not known, however its
|
|
3103
|
+
* attributes are written (`size={size}`, `size={16}` and `size="16"` tell the same): a rule that
|
|
3104
|
+
* asks about what the descendants are or have (the accessible name, the owned elements) cannot decide
|
|
3105
|
+
* on it, so it is mutable when `attr` is true ({@link MLElement.isUnresolvedComponent}). A component
|
|
3106
|
+
* that a pretender resolved is the element that it renders, which the rules read themselves.
|
|
3107
|
+
*
|
|
3108
|
+
* A rule that asks which children an element has (the required children, the elements that
|
|
3109
|
+
* satisfy a query) does not pass `attr`: an attribute that cannot be known does not decide whether
|
|
3110
|
+
* a child exists. It looks for the unresolved components among the children itself.
|
|
3111
|
+
*
|
|
3112
|
+
* Every `slot` is mutable by default, as a `slot` that is not in a declarative shadow root renders what
|
|
3113
|
+
* is given to it, and a rule that reads the children as they are written (`require-datetime`) cannot
|
|
3114
|
+
* know it. A rule that reads what a slot of a shadow root renders by `MLElement#getAssignedNodes` says
|
|
3115
|
+
* which slots are with `isMutableSlot`.
|
|
3116
|
+
*
|
|
3117
|
+
*
|
|
2946
3118
|
* @implements `@markuplint/ml-core` API: `MLElement`
|
|
2947
|
-
* @param attr - When true, also considers children with mutable attributes
|
|
3119
|
+
* @param attr - When true, also considers children with mutable attributes, and components that no
|
|
3120
|
+
* pretender resolved, as mutable
|
|
3121
|
+
* @param isMutableSlot - Whether a `slot` among the descendants may render what is not known
|
|
2948
3122
|
* @returns True if this element has potentially mutable children
|
|
2949
3123
|
*/
|
|
2950
|
-
hasMutableChildren(attr = false) {
|
|
3124
|
+
hasMutableChildren(attr = false, isMutableSlot = () => true) {
|
|
2951
3125
|
for (const child of this.getPureChildNodes()) {
|
|
2952
3126
|
if (child.is(child.MARKUPLINT_PREPROCESSOR_BLOCK)) {
|
|
2953
3127
|
if (child.blockBehavior) {
|
|
@@ -2959,10 +3133,13 @@ export class MLElement extends MLParentNode {
|
|
|
2959
3133
|
if (attr && child.hasMutableAttributes()) {
|
|
2960
3134
|
return true;
|
|
2961
3135
|
}
|
|
2962
|
-
if (child.
|
|
3136
|
+
if (attr && child.isUnresolvedComponent()) {
|
|
3137
|
+
return true;
|
|
3138
|
+
}
|
|
3139
|
+
if (child.hasMutableChildren(attr, isMutableSlot)) {
|
|
2963
3140
|
return true;
|
|
2964
3141
|
}
|
|
2965
|
-
if (child.localName === 'slot') {
|
|
3142
|
+
if (child.localName === 'slot' && isMutableSlot(child)) {
|
|
2966
3143
|
return true;
|
|
2967
3144
|
}
|
|
2968
3145
|
}
|
|
@@ -3006,7 +3183,7 @@ export class MLElement extends MLParentNode {
|
|
|
3006
3183
|
*
|
|
3007
3184
|
* @unsupported
|
|
3008
3185
|
* @implements DOM API: `Element`
|
|
3009
|
-
* @see https://w3c.github.io/DOM-Parsing/#
|
|
3186
|
+
* @see https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml
|
|
3010
3187
|
*/
|
|
3011
3188
|
insertAdjacentHTML(position, text) {
|
|
3012
3189
|
throw new UnexpectedCallError('Does not implement "insertAdjacentHTML" method yet');
|
|
@@ -3221,7 +3398,19 @@ export class MLElement extends MLParentNode {
|
|
|
3221
3398
|
if (this.elementType === 'html' && this.ownerMLDocument.isNativeTag(this.localName, this.namespaceURI)) {
|
|
3222
3399
|
return;
|
|
3223
3400
|
}
|
|
3224
|
-
const pretenderConfig = pretenders?.find(option =>
|
|
3401
|
+
const pretenderConfig = pretenders?.find(option => {
|
|
3402
|
+
try {
|
|
3403
|
+
return this.matches(option.selector);
|
|
3404
|
+
}
|
|
3405
|
+
catch (error) {
|
|
3406
|
+
// An entry that a scan or a file made names the file that declares its component, which is
|
|
3407
|
+
// where the user fixes it. `MLDocument` names the configuration for any other.
|
|
3408
|
+
if (!isFatalError(error) && error instanceof InvalidSelectorError && option.filePath) {
|
|
3409
|
+
throw new ConfigParserError(error.message, { filePath: option.filePath, raw: option.selector });
|
|
3410
|
+
}
|
|
3411
|
+
throw error;
|
|
3412
|
+
}
|
|
3413
|
+
});
|
|
3225
3414
|
const asAttrValue = this.getAttribute('as');
|
|
3226
3415
|
const pretenderElement = pretenderConfig?.as ??
|
|
3227
3416
|
(this.elementType === 'html' || !asAttrValue
|
|
@@ -3286,9 +3475,19 @@ export class MLElement extends MLParentNode {
|
|
|
3286
3475
|
};
|
|
3287
3476
|
}
|
|
3288
3477
|
/**
|
|
3289
|
-
* Converts pretender attributes to AST attributes for a virtual element.
|
|
3478
|
+
* Converts pretender attributes to AST attributes for a virtual element. They are marked as made by
|
|
3479
|
+
* a pretender (`MLAttr#isPretended`): the position of their tokens is the element's, not the source's.
|
|
3480
|
+
*
|
|
3481
|
+
* `{ expression }` is the code that the attribute is written as in the component. It becomes an
|
|
3482
|
+
* attribute written as code (`{` and `}` are its quotes), which `MLAttr` reads as JavaScript
|
|
3483
|
+
* whatever the language of the document is: a literal in it is rendered as the framework renders
|
|
3484
|
+
* it, and any other code is dynamic.
|
|
3290
3485
|
*
|
|
3291
|
-
* `{ fromAttr }` reads this (the origin) element's attribute. When that attribute is
|
|
3486
|
+
* `{ fromAttr }` reads this (the origin) element's attribute. When that attribute is a literal
|
|
3487
|
+
* (`kind={false}`, `kind`), the literal is rendered under the name of the attribute that the
|
|
3488
|
+
* virtual element gets, which is what the component does with the prop it forwards
|
|
3489
|
+
* (`<Button disabled={false}>` forwarded to `<button disabled={props.disabled}>` has no `disabled`,
|
|
3490
|
+
* and forwarded to `aria-label={props.disabled}` it has `aria-label="false"`). When it is any other
|
|
3292
3491
|
* expression (`kind={kind}`), the result is dynamic as well: the value the component
|
|
3293
3492
|
* receives is unknown, and the source text of the expression is not a value to validate.
|
|
3294
3493
|
* When the attribute is not written and `omitIfMissing` is set, the virtual element gets
|
|
@@ -3302,9 +3501,14 @@ export class MLElement extends MLParentNode {
|
|
|
3302
3501
|
return attrs.flatMap(({ name, value }, i) => {
|
|
3303
3502
|
let isDynamic = value != null && typeof value === 'object' && 'dynamic' in value;
|
|
3304
3503
|
let _value = '';
|
|
3504
|
+
// The code that the attribute is written as, when it is known
|
|
3505
|
+
let expression;
|
|
3305
3506
|
if (typeof value === 'string') {
|
|
3306
3507
|
_value = value;
|
|
3307
3508
|
}
|
|
3509
|
+
else if (value != null && 'expression' in value) {
|
|
3510
|
+
expression = value.expression;
|
|
3511
|
+
}
|
|
3308
3512
|
else if (value != null && 'fromAttr' in value) {
|
|
3309
3513
|
// The attribute of the usage site that the prop is. Vue gives a prop the attribute by the camelCase
|
|
3310
3514
|
// of its name (`my-label` and `myLabel` are the prop `myLabel`, and `mylabel` is another),
|
|
@@ -3316,15 +3520,21 @@ export class MLElement extends MLParentNode {
|
|
|
3316
3520
|
if (!origin && value.omitIfMissing) {
|
|
3317
3521
|
return [];
|
|
3318
3522
|
}
|
|
3319
|
-
if (origin?.
|
|
3523
|
+
if (origin?.literal) {
|
|
3524
|
+
// The key of a literal is the code that it is written as (`true`, `"x"`, `0`), except for a number that
|
|
3525
|
+
// is not finite (`Infinity` is a name, which is not a literal)
|
|
3526
|
+
expression = literalToExpression(origin.literal);
|
|
3527
|
+
}
|
|
3528
|
+
else if (origin?.isDynamicValue) {
|
|
3320
3529
|
isDynamic = true;
|
|
3321
3530
|
}
|
|
3322
3531
|
else {
|
|
3323
3532
|
_value = origin?.value ?? '';
|
|
3324
3533
|
}
|
|
3325
3534
|
}
|
|
3535
|
+
const isExpression = expression !== undefined;
|
|
3326
3536
|
return [
|
|
3327
|
-
{
|
|
3537
|
+
markPretendedAttr({
|
|
3328
3538
|
...this._astToken,
|
|
3329
3539
|
uuid: `${uuidBase}_attr_${i}`,
|
|
3330
3540
|
type: 'attr',
|
|
@@ -3351,24 +3561,24 @@ export class MLElement extends MLParentNode {
|
|
|
3351
3561
|
},
|
|
3352
3562
|
startQuote: {
|
|
3353
3563
|
...this._astToken,
|
|
3354
|
-
raw: '',
|
|
3564
|
+
raw: isExpression ? '{' : '',
|
|
3355
3565
|
},
|
|
3356
3566
|
value: {
|
|
3357
3567
|
...this._astToken,
|
|
3358
|
-
raw: _value,
|
|
3568
|
+
raw: expression ?? _value,
|
|
3359
3569
|
},
|
|
3360
3570
|
endQuote: {
|
|
3361
3571
|
...this._astToken,
|
|
3362
|
-
raw: '',
|
|
3572
|
+
raw: isExpression ? '}' : '',
|
|
3363
3573
|
},
|
|
3364
3574
|
isDuplicatable: true,
|
|
3365
|
-
...(isDynamic ? { isDynamicValue: true } : {}),
|
|
3575
|
+
...(isDynamic || isExpression ? { isDynamicValue: true } : {}),
|
|
3366
3576
|
parentNode: null,
|
|
3367
3577
|
nextNode: null,
|
|
3368
3578
|
prevNode: null,
|
|
3369
3579
|
isFragment: false,
|
|
3370
3580
|
isGhost: false,
|
|
3371
|
-
},
|
|
3581
|
+
}),
|
|
3372
3582
|
];
|
|
3373
3583
|
});
|
|
3374
3584
|
}
|
|
@@ -3543,7 +3753,7 @@ export class MLElement extends MLParentNode {
|
|
|
3543
3753
|
*
|
|
3544
3754
|
* @unsupported
|
|
3545
3755
|
* @implements DOM API: `Element`
|
|
3546
|
-
* @see https://
|
|
3756
|
+
* @see https://www.w3.org/TR/pointerlock-2/#dom-element-requestpointerlock
|
|
3547
3757
|
*/
|
|
3548
3758
|
requestPointerLock(options) {
|
|
3549
3759
|
throw new UnexpectedCallError('Not supported "requestPointerLock" method');
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { UnexpectedCallError } from '@markuplint/shared';
|
|
2
2
|
export class MLNamedNodeMap extends Array {
|
|
3
3
|
getNamedItem(qualifiedName) {
|
|
4
|
-
|
|
4
|
+
// An attribute that the framework does not output (`MLAttr#isAbsent`) is not found, as the DOM has none
|
|
5
|
+
return this.find(attr => !attr.isAbsent && attr.isNamedBy(qualifiedName)) ?? null;
|
|
5
6
|
}
|
|
6
7
|
/**
|
|
7
8
|
* **IT THROWS AN ERROR WHEN CALLING THIS.**
|
|
@@ -10,10 +10,9 @@ class MLNodeList extends Array {
|
|
|
10
10
|
*
|
|
11
11
|
* It throws an error because it should return null but the `item` method of `NodeListOf` doesn't return null in the type definition of TypeScript.
|
|
12
12
|
*
|
|
13
|
-
* @see https://
|
|
13
|
+
* @see https://dom.spec.whatwg.org/#dom-nodelist-item
|
|
14
14
|
*
|
|
15
|
-
* > item
|
|
16
|
-
* > Returns the index-th item in the collection. If index is greater than or equal to the number of nodes in the list, this returns null.
|
|
15
|
+
* > The item(index) method must return the indexth node in the collection. If there is no indexth node in the collection, then the method must return null.
|
|
17
16
|
*/
|
|
18
17
|
throw new ReferenceError('Invalid index');
|
|
19
18
|
}
|