@microsoft/webui-framework 0.0.6 → 0.0.7
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/dist/decorators.js +127 -85
- package/dist/element/diff.js +143 -100
- package/dist/element/markers.d.ts +20 -0
- package/dist/element/markers.js +117 -38
- package/dist/element/styles.d.ts +8 -4
- package/dist/element/styles.js +75 -27
- package/dist/element/types.js +7 -10
- package/dist/element.d.ts +8 -1
- package/dist/element.js +1198 -993
- package/dist/index.js +25 -12
- package/dist/lifecycle.js +46 -22
- package/dist/template-types.js +3 -0
- package/dist/template.js +4 -5
- package/package.json +6 -9
package/dist/decorators.js
CHANGED
|
@@ -1,95 +1,137 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
/**
|
|
4
|
+
* Reactive decorators for WebUIElement properties.
|
|
5
|
+
*
|
|
6
|
+
* Uses legacy/experimental TypeScript decorators (`experimentalDecorators: true`)
|
|
7
|
+
* for compatibility with the FAST ecosystem conventions.
|
|
8
|
+
*/
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Internal helpers
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/** Convert camelCase to kebab-case for attribute reflection. */
|
|
1
13
|
function toKebabCase(str) {
|
|
2
|
-
|
|
14
|
+
return str.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
|
3
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Shared logic for installing a reactive getter/setter on a class prototype.
|
|
18
|
+
* The backing value is stored in a private `_prop` field on the instance.
|
|
19
|
+
*/
|
|
4
20
|
function createReactiveProperty(proto, name) {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
const backingKey = `_${name}`;
|
|
22
|
+
const changedKey = `${name}Changed`;
|
|
23
|
+
Object.defineProperty(proto, name, {
|
|
24
|
+
get() {
|
|
25
|
+
return this[backingKey];
|
|
26
|
+
},
|
|
27
|
+
set(newValue) {
|
|
28
|
+
const oldValue = this[backingKey];
|
|
29
|
+
if (oldValue === newValue)
|
|
30
|
+
return;
|
|
31
|
+
this[backingKey] = newValue;
|
|
32
|
+
const cb = this[changedKey];
|
|
33
|
+
if (typeof cb === 'function') {
|
|
34
|
+
cb.call(this, oldValue, newValue);
|
|
35
|
+
}
|
|
36
|
+
if (this.isConnected) {
|
|
37
|
+
const upd = this['$update'];
|
|
38
|
+
if (upd)
|
|
39
|
+
upd.call(this, name);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
enumerable: true,
|
|
43
|
+
configurable: true,
|
|
44
|
+
});
|
|
27
45
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// @observable
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
/** Per-class registry of @observable property names. */
|
|
50
|
+
const observableRegistry = new WeakMap();
|
|
51
|
+
/**
|
|
52
|
+
* Get the set of @observable property names registered for a class.
|
|
53
|
+
*/
|
|
54
|
+
const EMPTY_SET = Object.freeze(new Set());
|
|
55
|
+
export function getObservableNames(ctor) {
|
|
56
|
+
return observableRegistry.get(ctor) ?? EMPTY_SET;
|
|
32
57
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Marks a property as observable. When the value changes the decorator will:
|
|
60
|
+
* 1. Call `this.<prop>Changed(oldValue, newValue)` if defined.
|
|
61
|
+
* 2. Call `this.$update(name)` if the element is connected, targeting
|
|
62
|
+
* only bindings that reference this property.
|
|
63
|
+
*/
|
|
64
|
+
export function observable(target, name) {
|
|
65
|
+
const ctor = target.constructor;
|
|
66
|
+
if (!observableRegistry.has(ctor)) {
|
|
67
|
+
observableRegistry.set(ctor, new Set());
|
|
68
|
+
}
|
|
69
|
+
observableRegistry.get(ctor).add(name);
|
|
70
|
+
createReactiveProperty(target, name);
|
|
40
71
|
}
|
|
41
|
-
|
|
42
|
-
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// @attr
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
/**
|
|
76
|
+
* Registry of attribute-name → property-name mappings per constructor.
|
|
77
|
+
* Used by `attributeChangedCallback` to route attribute changes to properties.
|
|
78
|
+
*/
|
|
79
|
+
const attrMap = new WeakMap();
|
|
80
|
+
/** Registry of boolean-mode attribute names per constructor. */
|
|
81
|
+
const boolAttrs = new WeakMap();
|
|
43
82
|
function applyAttr(target, name, options) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (options?.mode === "boolean") {
|
|
53
|
-
if (!boolAttrs.has(ctor)) {
|
|
54
|
-
boolAttrs.set(ctor, /* @__PURE__ */ new Set());
|
|
83
|
+
const proto = target;
|
|
84
|
+
const ctor = proto.constructor;
|
|
85
|
+
// 1. Install the reactive getter/setter (same as @observable).
|
|
86
|
+
createReactiveProperty(proto, name);
|
|
87
|
+
// 2. Register the attribute mapping.
|
|
88
|
+
const attrName = options?.attribute ?? toKebabCase(name);
|
|
89
|
+
if (!attrMap.has(ctor)) {
|
|
90
|
+
attrMap.set(ctor, new Map());
|
|
55
91
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
origACB
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
92
|
+
attrMap.get(ctor).set(attrName, name);
|
|
93
|
+
// Track boolean-mode attrs.
|
|
94
|
+
if (options?.mode === 'boolean') {
|
|
95
|
+
if (!boolAttrs.has(ctor)) {
|
|
96
|
+
boolAttrs.set(ctor, new Set());
|
|
97
|
+
}
|
|
98
|
+
boolAttrs.get(ctor).add(attrName);
|
|
99
|
+
}
|
|
100
|
+
// 3. Accumulate observed attributes on the constructor.
|
|
101
|
+
if (!ctor._observedAttrs) {
|
|
102
|
+
ctor._observedAttrs = [];
|
|
103
|
+
// Define the static getter that `customElements.define` inspects.
|
|
104
|
+
Object.defineProperty(ctor, 'observedAttributes', {
|
|
105
|
+
get() {
|
|
106
|
+
return ctor._observedAttrs ?? [];
|
|
107
|
+
},
|
|
108
|
+
configurable: true,
|
|
109
|
+
});
|
|
110
|
+
// Patch `attributeChangedCallback` once per class.
|
|
111
|
+
const origACB = proto['attributeChangedCallback'];
|
|
112
|
+
proto['attributeChangedCallback'] = function (attribute, oldVal, newVal) {
|
|
113
|
+
// Route the attribute change to the corresponding property.
|
|
114
|
+
const map = attrMap.get(this.constructor);
|
|
115
|
+
const propName = map?.get(attribute);
|
|
116
|
+
if (propName !== undefined) {
|
|
117
|
+
const isBool = boolAttrs.get(this.constructor)?.has(attribute);
|
|
118
|
+
this[propName] = isBool ? newVal !== null : newVal;
|
|
119
|
+
}
|
|
120
|
+
// Preserve any pre-existing attributeChangedCallback.
|
|
121
|
+
if (origACB) {
|
|
122
|
+
origACB.call(this, attribute, oldVal, newVal);
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
ctor._observedAttrs.push(attrName);
|
|
80
127
|
}
|
|
81
|
-
function attr(targetOrOptions, name) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
128
|
+
export function attr(targetOrOptions, name) {
|
|
129
|
+
if (typeof name === 'string') {
|
|
130
|
+
applyAttr(targetOrOptions, name);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const options = targetOrOptions;
|
|
134
|
+
return (target, propName) => {
|
|
135
|
+
applyAttr(target, propName, options);
|
|
136
|
+
};
|
|
90
137
|
}
|
|
91
|
-
export {
|
|
92
|
-
attr,
|
|
93
|
-
getObservableNames,
|
|
94
|
-
observable
|
|
95
|
-
};
|
package/dist/element/diff.js
CHANGED
|
@@ -1,117 +1,160 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT license.
|
|
3
|
+
// ── Helpers ─────────────────────────────────────────────────────────
|
|
1
4
|
function asParent(node) {
|
|
2
|
-
|
|
3
|
-
|
|
5
|
+
if (!node)
|
|
6
|
+
return null;
|
|
7
|
+
return 'childNodes' in node ? node : null;
|
|
4
8
|
}
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
/** Resolve a dotted path from a start offset without allocating. */
|
|
10
|
+
export function dotWalk(cursor, path, from) {
|
|
11
|
+
let start = from;
|
|
12
|
+
for (let i = from; i <= path.length; i++) {
|
|
13
|
+
if (i === path.length || path.charCodeAt(i) === 46 /* . */) {
|
|
14
|
+
if (cursor == null || typeof cursor !== 'object')
|
|
15
|
+
return undefined;
|
|
16
|
+
cursor = cursor[path.slice(start, i)];
|
|
17
|
+
start = i + 1;
|
|
18
|
+
}
|
|
12
19
|
}
|
|
13
|
-
|
|
14
|
-
return cursor;
|
|
20
|
+
return cursor;
|
|
15
21
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Resolve a dotted path against a repeat scope variable.
|
|
24
|
+
*
|
|
25
|
+
* When a binding inside `@for(item of items)` references `item.title`,
|
|
26
|
+
* this function looks up `title` on the current scope value.
|
|
27
|
+
*/
|
|
28
|
+
export function resolveRepeatValue(scopeVar, scope, path) {
|
|
29
|
+
if (path === scopeVar)
|
|
30
|
+
return scope;
|
|
31
|
+
if (!path.startsWith(`${scopeVar}.`))
|
|
32
|
+
return undefined;
|
|
33
|
+
return dotWalk(scope, path, scopeVar.length + 1);
|
|
20
34
|
}
|
|
35
|
+
/** Compute a key for an item using the cached key path, or null. */
|
|
21
36
|
function itemKey(item, keyPath) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
if (keyPath === undefined || keyPath === '')
|
|
38
|
+
return null;
|
|
39
|
+
const v = dotWalk(item, keyPath, 0);
|
|
40
|
+
return v != null ? String(v) : '';
|
|
25
41
|
}
|
|
42
|
+
/** Build a scope frame for a repeat item. */
|
|
26
43
|
function itemScope(rep, item) {
|
|
27
|
-
|
|
44
|
+
return { name: rep.itemVar, value: item, parent: rep.scope };
|
|
28
45
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
46
|
+
// ── Reconciliation ──────────────────────────────────────────────────
|
|
47
|
+
/**
|
|
48
|
+
* Reconcile a repeat binding against its current collection value.
|
|
49
|
+
*
|
|
50
|
+
* Called by `$updateInstance` on every reactive update. Resolves the
|
|
51
|
+
* collection path, diffs old vs. new items by key, and patches the DOM.
|
|
52
|
+
*/
|
|
53
|
+
export function syncRepeat(host, rep) {
|
|
54
|
+
const resolved = host.$resolveValue(rep.collection, rep.scope);
|
|
55
|
+
const items = Array.isArray(resolved) ? resolved : [];
|
|
56
|
+
// Locate the container once and cache it.
|
|
57
|
+
let container = rep.container
|
|
58
|
+
?? (rep.start ? asParent(rep.start.parentNode) : null)
|
|
59
|
+
?? (rep.owner.nodes[0] ? asParent(rep.owner.nodes[0].parentNode) : null);
|
|
60
|
+
if (!container)
|
|
61
|
+
return;
|
|
62
|
+
rep.container = container;
|
|
63
|
+
// Before the first client-side sync, bail if the collection hasn't
|
|
64
|
+
// been explicitly set but SSR children already exist.
|
|
65
|
+
if (!rep.synced && items.length === 0 && rep.instances.length > 0)
|
|
66
|
+
return;
|
|
67
|
+
rep.synced = true;
|
|
68
|
+
// If there are no items, just tear down everything.
|
|
69
|
+
if (items.length === 0) {
|
|
70
|
+
for (let i = 0; i < rep.instances.length; i += 1) {
|
|
71
|
+
host.$removeInstance(rep.instances[i].instance);
|
|
72
|
+
}
|
|
73
|
+
rep.instances = [];
|
|
74
|
+
return;
|
|
40
75
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
76
|
+
const keyPath = Object.values(rep.attrMap)[0];
|
|
77
|
+
const hasKeys = keyPath !== undefined && keyPath !== '';
|
|
78
|
+
const oldInstances = rep.instances;
|
|
79
|
+
// ── Fast path for unkeyed (index-based) repeats ────────────────
|
|
80
|
+
if (!hasKeys) {
|
|
81
|
+
const next = [];
|
|
82
|
+
const reuseCount = Math.min(oldInstances.length, items.length);
|
|
83
|
+
// Reuse existing instances by index
|
|
84
|
+
for (let i = 0; i < reuseCount; i += 1) {
|
|
85
|
+
const entry = oldInstances[i];
|
|
86
|
+
entry.value = items[i];
|
|
87
|
+
if (entry.instance.scope)
|
|
88
|
+
entry.instance.scope.value = items[i];
|
|
89
|
+
next.push(entry);
|
|
90
|
+
}
|
|
91
|
+
// Create new instances for items beyond old length
|
|
92
|
+
for (let i = reuseCount; i < items.length; i += 1) {
|
|
93
|
+
const scope = itemScope(rep, items[i]);
|
|
94
|
+
const instance = host.$createBlockInstance(rep.blockIndex, scope);
|
|
95
|
+
if (instance) {
|
|
96
|
+
next.push({ key: null, value: items[i], instance });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Remove excess old instances
|
|
100
|
+
for (let i = reuseCount; i < oldInstances.length; i += 1) {
|
|
101
|
+
host.$removeInstance(oldInstances[i].instance);
|
|
102
|
+
}
|
|
103
|
+
rep.instances = next;
|
|
104
|
+
// Reorder + update
|
|
105
|
+
let cursor = rep.start;
|
|
106
|
+
for (let i = 0; i < next.length; i += 1) {
|
|
107
|
+
cursor = host.$insertInstanceAfter(cursor, container, next[i].instance);
|
|
108
|
+
}
|
|
109
|
+
for (let i = 0; i < next.length; i += 1) {
|
|
110
|
+
host.$updateInstance(next[i].instance);
|
|
111
|
+
}
|
|
112
|
+
return;
|
|
55
113
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
114
|
+
// ── Keyed diff ─────────────────────────────────────────────────
|
|
115
|
+
// ── Build old-key → instance map ────────────────────────────────
|
|
116
|
+
const oldByKey = new Map();
|
|
117
|
+
for (let i = 0; i < oldInstances.length; i += 1) {
|
|
118
|
+
const entry = oldInstances[i];
|
|
119
|
+
const k = entry.key;
|
|
120
|
+
if (k != null)
|
|
121
|
+
oldByKey.set(k, entry);
|
|
62
122
|
}
|
|
63
|
-
|
|
64
|
-
|
|
123
|
+
// ── Match / create ──────────────────────────────────────────────
|
|
124
|
+
const next = [];
|
|
125
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
126
|
+
const item = items[i];
|
|
127
|
+
const key = itemKey(item, keyPath);
|
|
128
|
+
const existing = key != null ? oldByKey.get(key) : undefined;
|
|
129
|
+
if (existing) {
|
|
130
|
+
oldByKey.delete(key);
|
|
131
|
+
existing.value = item;
|
|
132
|
+
existing.key = key;
|
|
133
|
+
if (existing.instance.scope)
|
|
134
|
+
existing.instance.scope.value = item;
|
|
135
|
+
next.push(existing);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const scope = itemScope(rep, item);
|
|
139
|
+
const instance = host.$createBlockInstance(rep.blockIndex, scope);
|
|
140
|
+
if (instance) {
|
|
141
|
+
next.push({ key: key ?? null, value: item, instance });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
65
144
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
cursor2 = host.$insertInstanceAfter(cursor2, container, next2[i].instance);
|
|
145
|
+
// ── Remove unmatched old instances ──────────────────────────────
|
|
146
|
+
for (const leftover of oldByKey.values()) {
|
|
147
|
+
host.$removeInstance(leftover.instance);
|
|
70
148
|
}
|
|
71
|
-
|
|
72
|
-
|
|
149
|
+
rep.instances = next;
|
|
150
|
+
// ── Reorder DOM (forward pass) ──────────────────────────────────
|
|
151
|
+
// Walk forward, skip nodes already in position.
|
|
152
|
+
let cursor = rep.start;
|
|
153
|
+
for (let i = 0; i < next.length; i += 1) {
|
|
154
|
+
cursor = host.$insertInstanceAfter(cursor, container, next[i].instance);
|
|
73
155
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
for (let i = 0; i < oldInstances.length; i += 1) {
|
|
78
|
-
const entry = oldInstances[i];
|
|
79
|
-
const k = entry.key;
|
|
80
|
-
if (k != null) oldByKey.set(k, entry);
|
|
81
|
-
}
|
|
82
|
-
const next = [];
|
|
83
|
-
for (let i = 0; i < items.length; i += 1) {
|
|
84
|
-
const item = items[i];
|
|
85
|
-
const key = itemKey(item, keyPath);
|
|
86
|
-
const existing = key != null ? oldByKey.get(key) : void 0;
|
|
87
|
-
if (existing) {
|
|
88
|
-
oldByKey.delete(key);
|
|
89
|
-
existing.value = item;
|
|
90
|
-
existing.key = key;
|
|
91
|
-
if (existing.instance.scope) existing.instance.scope.value = item;
|
|
92
|
-
next.push(existing);
|
|
93
|
-
} else {
|
|
94
|
-
const scope = itemScope(rep, item);
|
|
95
|
-
const instance = host.$createBlockInstance(rep.blockIndex, scope);
|
|
96
|
-
if (instance) {
|
|
97
|
-
next.push({ key: key ?? null, value: item, instance });
|
|
98
|
-
}
|
|
156
|
+
// ── Update bindings ─────────────────────────────────────────────
|
|
157
|
+
for (let i = 0; i < next.length; i += 1) {
|
|
158
|
+
host.$updateInstance(next[i].instance);
|
|
99
159
|
}
|
|
100
|
-
}
|
|
101
|
-
for (const leftover of oldByKey.values()) {
|
|
102
|
-
host.$removeInstance(leftover.instance);
|
|
103
|
-
}
|
|
104
|
-
rep.instances = next;
|
|
105
|
-
let cursor = rep.start;
|
|
106
|
-
for (let i = 0; i < next.length; i += 1) {
|
|
107
|
-
cursor = host.$insertInstanceAfter(cursor, container, next[i].instance);
|
|
108
|
-
}
|
|
109
|
-
for (let i = 0; i < next.length; i += 1) {
|
|
110
|
-
host.$updateInstance(next[i].instance);
|
|
111
|
-
}
|
|
112
160
|
}
|
|
113
|
-
export {
|
|
114
|
-
dotWalk,
|
|
115
|
-
resolveRepeatValue,
|
|
116
|
-
syncRepeat
|
|
117
|
-
};
|
|
@@ -32,3 +32,23 @@ export declare function collectItemMarkers(repeatStart: Comment): {
|
|
|
32
32
|
* whitespace text nodes and other comments.
|
|
33
33
|
*/
|
|
34
34
|
export declare function nextElement(marker: Comment): Element | null;
|
|
35
|
+
/**
|
|
36
|
+
* Find the Nth child of a given nodeType, skipping structural block ranges.
|
|
37
|
+
*
|
|
38
|
+
* The compiled template static HTML (`meta.h`) does not contain conditional
|
|
39
|
+
* or repeat block content — those are stored as separate block metadata.
|
|
40
|
+
* But the SSR DOM has this content rendered inline between marker pairs
|
|
41
|
+
* (`<!--wc-->...<!--/wc-->` and `<!--wr-->...<!--/wr-->`).
|
|
42
|
+
*
|
|
43
|
+
* This function walks `parent.firstChild` → siblings, counting only
|
|
44
|
+
* children of the requested `nodeType` that are NOT inside a structural
|
|
45
|
+
* block range. Nested blocks of the same type are handled via depth
|
|
46
|
+
* tracking. Returns the child at the given `ordinal`, or null.
|
|
47
|
+
*
|
|
48
|
+
* Used by `$resolveSSR` (element ordinals) and `$findSSRText` (text
|
|
49
|
+
* ordinals) to keep SSR DOM ordinals aligned with template metadata.
|
|
50
|
+
*
|
|
51
|
+
* **Requires closing markers to still be in the DOM** — caller must
|
|
52
|
+
* not remove `<!--/wc-->` or `<!--/wr-->` before all resolution is done.
|
|
53
|
+
*/
|
|
54
|
+
export declare function findByOrdinal(parent: Node, nodeType: number, ordinal: number): Node | null;
|