@mml-io/observable-dom 0.1.3 → 0.2.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/build/index.js +79 -111
- package/build/index.js.map +3 -3
- package/package.json +2 -2
- package/src/JSDOMRunner.ts +8 -53
- package/src/{ObservableDom.ts → ObservableDOM.ts} +89 -82
- package/src/index.ts +1 -1
- package/src/utils.ts +4 -4
package/build/index.js
CHANGED
|
@@ -31,23 +31,23 @@ var src_exports = {};
|
|
|
31
31
|
__export(src_exports, {
|
|
32
32
|
JSDOMRunner: () => JSDOMRunner,
|
|
33
33
|
JSDOMRunnerFactory: () => JSDOMRunnerFactory,
|
|
34
|
-
|
|
34
|
+
ObservableDOM: () => ObservableDOM
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(src_exports);
|
|
37
37
|
|
|
38
38
|
// src/utils.ts
|
|
39
|
-
function
|
|
39
|
+
function virtualDOMElementToStatic(el) {
|
|
40
40
|
return {
|
|
41
41
|
nodeId: el.nodeId,
|
|
42
42
|
tag: el.tag,
|
|
43
43
|
attributes: el.attributes,
|
|
44
|
-
childNodes: el.childNodes.map((child) =>
|
|
44
|
+
childNodes: el.childNodes.map((child) => virtualDOMElementToStatic(child)),
|
|
45
45
|
textContent: el.textContent
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
// src/
|
|
50
|
-
var
|
|
49
|
+
// src/ObservableDOM.ts
|
|
50
|
+
var ObservableDOM = class {
|
|
51
51
|
constructor(observableDOMParameters, callback, runnerFactory) {
|
|
52
52
|
this.nodeToNodeId = /* @__PURE__ */ new Map();
|
|
53
53
|
this.nodeIdToNode = /* @__PURE__ */ new Map();
|
|
@@ -58,9 +58,12 @@ var ObservableDom = class {
|
|
|
58
58
|
this.ignoreTextNodes = observableDOMParameters.ignoreTextNodes;
|
|
59
59
|
this.callback = callback;
|
|
60
60
|
this.documentTimeIntervalTimer = setInterval(() => {
|
|
61
|
-
this.callback(
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
this.callback(
|
|
62
|
+
{
|
|
63
|
+
documentTime: this.getDocumentTime()
|
|
64
|
+
},
|
|
65
|
+
this
|
|
66
|
+
);
|
|
64
67
|
}, observableDOMParameters.pingIntervalMilliseconds || 5e3);
|
|
65
68
|
this.domRunner = runnerFactory(
|
|
66
69
|
observableDOMParameters.htmlPath,
|
|
@@ -68,33 +71,36 @@ var ObservableDom = class {
|
|
|
68
71
|
observableDOMParameters.params,
|
|
69
72
|
(domRunnerMessage) => {
|
|
70
73
|
if (domRunnerMessage.loaded) {
|
|
71
|
-
this.
|
|
74
|
+
this.createVirtualDOMElementWithChildren(
|
|
72
75
|
this.domRunner.getDocument(),
|
|
73
76
|
null
|
|
74
77
|
);
|
|
75
|
-
const snapshot =
|
|
76
|
-
this.
|
|
78
|
+
const snapshot = virtualDOMElementToStatic(
|
|
79
|
+
this.getVirtualDOMElementForRealElementOrThrow(
|
|
77
80
|
this.domRunner.getDocument()
|
|
78
81
|
)
|
|
79
82
|
);
|
|
80
|
-
this.callback(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
this.callback(
|
|
84
|
+
{
|
|
85
|
+
snapshot,
|
|
86
|
+
documentTime: this.getDocumentTime()
|
|
87
|
+
},
|
|
88
|
+
this
|
|
89
|
+
);
|
|
84
90
|
} else if (domRunnerMessage.mutationList) {
|
|
85
91
|
this.processModificationList(domRunnerMessage.mutationList);
|
|
86
92
|
} else if (domRunnerMessage.logMessage) {
|
|
87
|
-
this.callback(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
this.callback(
|
|
94
|
+
{
|
|
95
|
+
logMessage: domRunnerMessage.logMessage,
|
|
96
|
+
documentTime: this.getDocumentTime()
|
|
97
|
+
},
|
|
98
|
+
this
|
|
99
|
+
);
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
102
|
);
|
|
94
103
|
}
|
|
95
|
-
addIPCWebsocket(webSocket) {
|
|
96
|
-
return this.domRunner.addIPCWebsocket(webSocket);
|
|
97
|
-
}
|
|
98
104
|
addConnectedUserId(connectionId) {
|
|
99
105
|
this.domRunner.getWindow().dispatchEvent(
|
|
100
106
|
new (this.domRunner.getWindow()).CustomEvent("connected", {
|
|
@@ -111,8 +117,8 @@ var ObservableDom = class {
|
|
|
111
117
|
}
|
|
112
118
|
processModificationList(mutationList) {
|
|
113
119
|
const documentEl = this.domRunner.getDocument();
|
|
114
|
-
const
|
|
115
|
-
if (!
|
|
120
|
+
const documentVirtualDOMElement = this.realElementToVirtualElement.get(documentEl);
|
|
121
|
+
if (!documentVirtualDOMElement) {
|
|
116
122
|
throw new Error(`document not created in processModificationList`);
|
|
117
123
|
}
|
|
118
124
|
if (mutationList.length > 1) {
|
|
@@ -130,7 +136,7 @@ var ObservableDom = class {
|
|
|
130
136
|
}
|
|
131
137
|
this.addKnownNodesInMutation(mutation);
|
|
132
138
|
const firstNonIgnoredPreviousSibling = mutation.previousSibling ? this.getFirstNonIgnoredPreviousSibling(mutation.previousSibling) : null;
|
|
133
|
-
const targetElement = this.
|
|
139
|
+
const targetElement = this.getVirtualDOMElementForRealElementOrThrow(
|
|
134
140
|
mutation.target
|
|
135
141
|
);
|
|
136
142
|
const addedNodes = [];
|
|
@@ -138,43 +144,46 @@ var ObservableDom = class {
|
|
|
138
144
|
if (this.isIgnoredElement(node)) {
|
|
139
145
|
continue;
|
|
140
146
|
}
|
|
141
|
-
const
|
|
147
|
+
const virtualDOMElement = this.getVirtualDOMElementForRealElementOrThrow(
|
|
142
148
|
node
|
|
143
149
|
);
|
|
144
|
-
addedNodes.push(
|
|
150
|
+
addedNodes.push(virtualDOMElementToStatic(virtualDOMElement));
|
|
145
151
|
}
|
|
146
152
|
const removedNodeIds = [];
|
|
147
153
|
for (const node of mutation.removedNodes) {
|
|
148
154
|
if (this.isIgnoredElement(node)) {
|
|
149
155
|
continue;
|
|
150
156
|
}
|
|
151
|
-
const
|
|
157
|
+
const virtualDOMElement = this.getVirtualDOMElementForRealElementOrThrow(
|
|
152
158
|
node
|
|
153
159
|
);
|
|
154
|
-
removedNodeIds.push(
|
|
160
|
+
removedNodeIds.push(virtualDOMElement.nodeId);
|
|
155
161
|
}
|
|
156
162
|
const mutationRecord = {
|
|
157
163
|
type: mutation.type,
|
|
158
164
|
targetId: targetElement.nodeId,
|
|
159
165
|
addedNodes,
|
|
160
166
|
removedNodeIds,
|
|
161
|
-
previousSiblingId: firstNonIgnoredPreviousSibling !== null ? this.
|
|
167
|
+
previousSiblingId: firstNonIgnoredPreviousSibling !== null ? this.getVirtualDOMElementForRealElementOrThrow(firstNonIgnoredPreviousSibling).nodeId : null,
|
|
162
168
|
attribute: mutation.attributeName ? {
|
|
163
169
|
attributeName: mutation.attributeName,
|
|
164
170
|
value: mutation.target.getAttribute(mutation.attributeName)
|
|
165
171
|
} : null
|
|
166
172
|
};
|
|
167
|
-
this.callback(
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
173
|
+
this.callback(
|
|
174
|
+
{
|
|
175
|
+
mutation: mutationRecord,
|
|
176
|
+
documentTime: this.getDocumentTime()
|
|
177
|
+
},
|
|
178
|
+
this
|
|
179
|
+
);
|
|
171
180
|
this.removeKnownNodesInMutation(mutation);
|
|
172
181
|
}
|
|
173
182
|
}
|
|
174
183
|
addKnownNodesInMutation(mutation) {
|
|
175
184
|
const targetNode = mutation.target;
|
|
176
|
-
const
|
|
177
|
-
if (!
|
|
185
|
+
const virtualDOMElement = this.realElementToVirtualElement.get(targetNode);
|
|
186
|
+
if (!virtualDOMElement) {
|
|
178
187
|
throw new Error(
|
|
179
188
|
"Unknown node in addKnownNodesInMutation:" + targetNode + "," + mutation.type
|
|
180
189
|
);
|
|
@@ -192,7 +201,7 @@ var ObservableDom = class {
|
|
|
192
201
|
if (!previousSiblingElement) {
|
|
193
202
|
throw new Error("Unknown previous sibling");
|
|
194
203
|
}
|
|
195
|
-
index =
|
|
204
|
+
index = virtualDOMElement.childNodes.indexOf(previousSiblingElement);
|
|
196
205
|
if (index === -1) {
|
|
197
206
|
throw new Error("Previous sibling is not currently a child of the parent element");
|
|
198
207
|
}
|
|
@@ -200,13 +209,13 @@ var ObservableDom = class {
|
|
|
200
209
|
}
|
|
201
210
|
mutation.addedNodes.forEach((node) => {
|
|
202
211
|
const asElementOrText = node;
|
|
203
|
-
const
|
|
212
|
+
const childVirtualDOMElement = this.createVirtualDOMElementWithChildren(
|
|
204
213
|
asElementOrText,
|
|
205
|
-
|
|
214
|
+
virtualDOMElement
|
|
206
215
|
);
|
|
207
|
-
if (
|
|
208
|
-
if (
|
|
209
|
-
|
|
216
|
+
if (childVirtualDOMElement) {
|
|
217
|
+
if (virtualDOMElement.childNodes.indexOf(childVirtualDOMElement) === -1) {
|
|
218
|
+
virtualDOMElement.childNodes.splice(index, 0, childVirtualDOMElement);
|
|
210
219
|
index++;
|
|
211
220
|
}
|
|
212
221
|
}
|
|
@@ -218,18 +227,18 @@ var ObservableDom = class {
|
|
|
218
227
|
}
|
|
219
228
|
const attributeValue = targetNode.getAttribute(attributeName);
|
|
220
229
|
if (attributeValue === null) {
|
|
221
|
-
delete
|
|
230
|
+
delete virtualDOMElement.attributes[attributeName];
|
|
222
231
|
} else {
|
|
223
|
-
|
|
232
|
+
virtualDOMElement.attributes[attributeName] = attributeValue;
|
|
224
233
|
}
|
|
225
234
|
} else if (mutation.type === "characterData") {
|
|
226
|
-
|
|
235
|
+
virtualDOMElement.textContent = targetNode.textContent ? targetNode.textContent : void 0;
|
|
227
236
|
}
|
|
228
237
|
}
|
|
229
238
|
removeKnownNodesInMutation(mutation) {
|
|
230
239
|
const targetNode = mutation.target;
|
|
231
|
-
const
|
|
232
|
-
if (!
|
|
240
|
+
const virtualDOMElement = this.realElementToVirtualElement.get(targetNode);
|
|
241
|
+
if (!virtualDOMElement) {
|
|
233
242
|
throw new Error("Unknown node in mutation list:" + targetNode + ", " + mutation.type);
|
|
234
243
|
}
|
|
235
244
|
if (mutation.type === "childList") {
|
|
@@ -238,36 +247,36 @@ var ObservableDom = class {
|
|
|
238
247
|
if (this.isIgnoredElement(asElementOrText)) {
|
|
239
248
|
continue;
|
|
240
249
|
}
|
|
241
|
-
const
|
|
242
|
-
if (!
|
|
250
|
+
const childDOMElement = this.realElementToVirtualElement.get(asElementOrText);
|
|
251
|
+
if (!childDOMElement) {
|
|
243
252
|
console.warn(this.htmlPath, "Unknown node in removeKnownNodesInMutation");
|
|
244
253
|
continue;
|
|
245
254
|
} else {
|
|
246
|
-
this.
|
|
247
|
-
const index =
|
|
248
|
-
|
|
255
|
+
this.removeVirtualDOMElement(childDOMElement);
|
|
256
|
+
const index = virtualDOMElement.childNodes.indexOf(childDOMElement);
|
|
257
|
+
virtualDOMElement.childNodes.splice(index, 1);
|
|
249
258
|
}
|
|
250
259
|
}
|
|
251
260
|
return;
|
|
252
261
|
}
|
|
253
262
|
}
|
|
254
|
-
|
|
255
|
-
this.nodeIdToNode.delete(
|
|
256
|
-
this.nodeToNodeId.delete(
|
|
257
|
-
this.realElementToVirtualElement.delete(
|
|
258
|
-
for (const child of
|
|
259
|
-
this.
|
|
263
|
+
removeVirtualDOMElement(virtualDOMElement) {
|
|
264
|
+
this.nodeIdToNode.delete(virtualDOMElement.nodeId);
|
|
265
|
+
this.nodeToNodeId.delete(virtualDOMElement);
|
|
266
|
+
this.realElementToVirtualElement.delete(virtualDOMElement.realElement);
|
|
267
|
+
for (const child of virtualDOMElement.childNodes) {
|
|
268
|
+
this.removeVirtualDOMElement(child);
|
|
260
269
|
}
|
|
261
270
|
}
|
|
262
|
-
|
|
263
|
-
const virtualElement = this.
|
|
271
|
+
createVirtualDOMElementWithChildren(node, parent) {
|
|
272
|
+
const virtualElement = this.createVirtualDOMElement(node, parent);
|
|
264
273
|
if (!virtualElement) {
|
|
265
274
|
return null;
|
|
266
275
|
}
|
|
267
276
|
if (node.childNodes) {
|
|
268
277
|
for (let i = 0; i < node.childNodes.length; i++) {
|
|
269
278
|
const child = node.childNodes[i];
|
|
270
|
-
const childVirtualElement = this.
|
|
279
|
+
const childVirtualElement = this.createVirtualDOMElementWithChildren(
|
|
271
280
|
child,
|
|
272
281
|
virtualElement
|
|
273
282
|
);
|
|
@@ -278,7 +287,7 @@ var ObservableDom = class {
|
|
|
278
287
|
}
|
|
279
288
|
return virtualElement;
|
|
280
289
|
}
|
|
281
|
-
|
|
290
|
+
createVirtualDOMElement(node, parent) {
|
|
282
291
|
if (this.isIgnoredElement(node)) {
|
|
283
292
|
return null;
|
|
284
293
|
}
|
|
@@ -332,7 +341,7 @@ var ObservableDom = class {
|
|
|
332
341
|
}
|
|
333
342
|
return null;
|
|
334
343
|
}
|
|
335
|
-
|
|
344
|
+
getVirtualDOMElementForRealElementOrThrow(realElement) {
|
|
336
345
|
const virtualElement = this.realElementToVirtualElement.get(realElement);
|
|
337
346
|
if (!virtualElement) {
|
|
338
347
|
throw new Error(`Virtual element not found for real element`);
|
|
@@ -382,7 +391,7 @@ var import_vm = __toESM(require("vm"));
|
|
|
382
391
|
var import_jsdom = require("jsdom");
|
|
383
392
|
var nodeFetch = __toESM(require("node-fetch"));
|
|
384
393
|
var import_node_fetch = __toESM(require("node-fetch"));
|
|
385
|
-
var
|
|
394
|
+
var ErrDOMWindowNotInitialized = "DOMWindow not initialized";
|
|
386
395
|
var monkeyPatchedMutationRecordCallbacks = /* @__PURE__ */ new Set();
|
|
387
396
|
function installMutationObserverMonkeyPatch() {
|
|
388
397
|
const MutationRecordExports = require("jsdom/lib/jsdom/living/generated/MutationRecord");
|
|
@@ -406,10 +415,8 @@ var RejectionResourceLoader = class extends import_jsdom.ResourceLoader {
|
|
|
406
415
|
};
|
|
407
416
|
var JSDOMRunner = class {
|
|
408
417
|
constructor(htmlPath, htmlContents, params, callback) {
|
|
409
|
-
this.ipcWebsockets = /* @__PURE__ */ new Set();
|
|
410
418
|
this.domWindow = null;
|
|
411
419
|
this.mutationObserver = null;
|
|
412
|
-
this.ipcListeners = /* @__PURE__ */ new Set();
|
|
413
420
|
this.documentStartTime = Date.now();
|
|
414
421
|
this.isLoaded = false;
|
|
415
422
|
this.logBuffer = [];
|
|
@@ -433,7 +440,7 @@ var JSDOMRunner = class {
|
|
|
433
440
|
}
|
|
434
441
|
};
|
|
435
442
|
monkeyPatchedMutationRecordCallbacks.add(this.monkeyPatchMutationRecordCallback);
|
|
436
|
-
this.
|
|
443
|
+
this.jsdom = new import_jsdom.JSDOM(htmlContents, {
|
|
437
444
|
runScripts: "dangerously",
|
|
438
445
|
resources: new RejectionResourceLoader(),
|
|
439
446
|
url: this.htmlPath,
|
|
@@ -452,22 +459,6 @@ var JSDOMRunner = class {
|
|
|
452
459
|
});
|
|
453
460
|
window.document.timeline = timeline;
|
|
454
461
|
window.params = JSON.parse(JSON.stringify(params));
|
|
455
|
-
const oldDocumentAddEventListener = window.document.addEventListener;
|
|
456
|
-
window.document.addEventListener = (...args) => {
|
|
457
|
-
const [eventName, listener] = args;
|
|
458
|
-
if (eventName === "ipc") {
|
|
459
|
-
this.ipcListeners.add(listener);
|
|
460
|
-
}
|
|
461
|
-
return oldDocumentAddEventListener.call(window.document, ...args);
|
|
462
|
-
};
|
|
463
|
-
const oldDocumentRemoveEventListener = window.document.addEventListener;
|
|
464
|
-
window.document.removeEventListener = (...args) => {
|
|
465
|
-
const [eventName, listener] = args;
|
|
466
|
-
if (eventName === "ipc") {
|
|
467
|
-
this.ipcListeners.delete(listener);
|
|
468
|
-
}
|
|
469
|
-
return oldDocumentRemoveEventListener.call(window.document, ...args);
|
|
470
|
-
};
|
|
471
462
|
this.mutationObserver = new window.MutationObserver((mutationList) => {
|
|
472
463
|
this.callback({
|
|
473
464
|
mutationList
|
|
@@ -509,36 +500,13 @@ var JSDOMRunner = class {
|
|
|
509
500
|
}
|
|
510
501
|
getDocument() {
|
|
511
502
|
if (!this.domWindow) {
|
|
512
|
-
throw new Error(
|
|
503
|
+
throw new Error(ErrDOMWindowNotInitialized);
|
|
513
504
|
}
|
|
514
505
|
return this.domWindow.document;
|
|
515
506
|
}
|
|
516
507
|
getWindow() {
|
|
517
508
|
return this.domWindow;
|
|
518
509
|
}
|
|
519
|
-
addIPCWebsocket(webSocket) {
|
|
520
|
-
if (!this.domWindow) {
|
|
521
|
-
throw new Error(ErrDomWindowNotInitialized);
|
|
522
|
-
}
|
|
523
|
-
if (this.ipcListeners.size === 0) {
|
|
524
|
-
console.error("ipc requested, but no ipc listeners registered on document:", this.htmlPath);
|
|
525
|
-
webSocket.close();
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
this.ipcWebsockets.add(webSocket);
|
|
529
|
-
const event = new this.domWindow.CustomEvent("ipc", {
|
|
530
|
-
detail: {
|
|
531
|
-
webSocket
|
|
532
|
-
}
|
|
533
|
-
});
|
|
534
|
-
for (const ipcListener of this.ipcListeners) {
|
|
535
|
-
ipcListener(event);
|
|
536
|
-
}
|
|
537
|
-
webSocket.addEventListener("close", () => {
|
|
538
|
-
this.ipcWebsockets.delete(webSocket);
|
|
539
|
-
});
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
510
|
dispose() {
|
|
543
511
|
var _a, _b;
|
|
544
512
|
const records = (_a = this.mutationObserver) == null ? void 0 : _a.takeRecords();
|
|
@@ -547,14 +515,14 @@ var JSDOMRunner = class {
|
|
|
547
515
|
});
|
|
548
516
|
monkeyPatchedMutationRecordCallbacks.delete(this.monkeyPatchMutationRecordCallback);
|
|
549
517
|
(_b = this.mutationObserver) == null ? void 0 : _b.disconnect();
|
|
550
|
-
this.
|
|
518
|
+
this.jsdom.window.close();
|
|
551
519
|
}
|
|
552
520
|
getDocumentTime() {
|
|
553
521
|
return Date.now() - this.documentStartTime;
|
|
554
522
|
}
|
|
555
523
|
dispatchRemoteEventFromConnectionId(connectionId, domNode, remoteEvent) {
|
|
556
524
|
if (!this.domWindow) {
|
|
557
|
-
throw new Error(
|
|
525
|
+
throw new Error(ErrDOMWindowNotInitialized);
|
|
558
526
|
}
|
|
559
527
|
const bubbles = remoteEvent.bubbles || false;
|
|
560
528
|
const remoteEventObject = new this.domWindow.CustomEvent(remoteEvent.name, {
|
|
@@ -567,7 +535,7 @@ var JSDOMRunner = class {
|
|
|
567
535
|
const handlerAttributeValue = domNode.getAttribute(handlerAttributeName);
|
|
568
536
|
if (handlerAttributeValue) {
|
|
569
537
|
const script = handlerAttributeValue;
|
|
570
|
-
const vmContext = this.
|
|
538
|
+
const vmContext = this.jsdom.getInternalVMContext();
|
|
571
539
|
try {
|
|
572
540
|
const invoke = import_vm.default.runInContext(`(function(event){ ${script} })`, vmContext);
|
|
573
541
|
Reflect.apply(invoke, domNode, [remoteEventObject]);
|
|
@@ -617,6 +585,6 @@ var JSDOMRunner = class {
|
|
|
617
585
|
0 && (module.exports = {
|
|
618
586
|
JSDOMRunner,
|
|
619
587
|
JSDOMRunnerFactory,
|
|
620
|
-
|
|
588
|
+
ObservableDOM
|
|
621
589
|
});
|
|
622
590
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../src/index.ts", "../src/utils.ts", "../src/
|
|
4
|
-
"sourcesContent": ["export * from \"./ObservableDom\";\nexport * from \"./JSDOMRunner\";\n", "import { StaticVirtualDomElement } from \"@mml-io/observable-dom-common\";\n\nimport { LiveVirtualDomElement } from \"./ObservableDom\";\n\nexport function virtualDomElementToStatic(el: LiveVirtualDomElement): StaticVirtualDomElement {\n return {\n nodeId: el.nodeId,\n tag: el.tag,\n attributes: el.attributes,\n childNodes: el.childNodes.map((child) => virtualDomElementToStatic(child)),\n textContent: el.textContent,\n };\n}\n", "import {\n LogMessage,\n ObservableDomInterface,\n ObservableDomMessage,\n ObservableDOMParameters,\n RemoteEvent,\n StaticVirtualDomElement,\n StaticVirtualDomMutationIdsRecord,\n} from \"@mml-io/observable-dom-common\";\n\nimport { virtualDomElementToStatic } from \"./utils\";\n\nexport type DOMRunnerMessage = {\n loaded?: boolean;\n mutationList?: Array<MutationRecord>;\n logMessage?: LogMessage;\n};\n\nexport type DOMRunnerInterface = {\n getDocument(): Document;\n getWindow(): Window & {\n CustomEvent: typeof CustomEvent;\n Text: typeof Text;\n HTMLScriptElement: typeof HTMLScriptElement;\n Comment: typeof Comment;\n }; // TODO - Define this without using JSDOM types\n addIPCWebsocket(webSocket: WebSocket): void;\n dispatchRemoteEventFromConnectionId(\n connectionId: number,\n realElement: Element,\n remoteEvent: RemoteEvent,\n ): void;\n dispose(): void;\n getDocumentTime(): number;\n};\n\nexport type DOMRunnerFactory = (\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (domRunnerMessage: DOMRunnerMessage) => void,\n) => DOMRunnerInterface;\n\nexport type LiveVirtualDomElement = Omit<StaticVirtualDomElement, \"childNodes\"> & {\n realElement: Element | Text;\n childNodes: Array<LiveVirtualDomElement>;\n parent: LiveVirtualDomElement | null;\n};\n\nexport class ObservableDom implements ObservableDomInterface {\n private nodeToNodeId = new Map<LiveVirtualDomElement, number>();\n private nodeIdToNode = new Map<number, LiveVirtualDomElement>();\n private realElementToVirtualElement = new Map<Element | Text, LiveVirtualDomElement>();\n private ignoreTextNodes = true;\n private callback: (message: ObservableDomMessage) => void;\n private nextNodeId = 1;\n private htmlPath: string;\n private domRunner: DOMRunnerInterface;\n\n private documentTimeIntervalTimer: NodeJS.Timer;\n\n constructor(\n observableDOMParameters: ObservableDOMParameters,\n callback: (message: ObservableDomMessage) => void,\n runnerFactory: DOMRunnerFactory,\n ) {\n this.htmlPath = observableDOMParameters.htmlPath;\n this.ignoreTextNodes = observableDOMParameters.ignoreTextNodes;\n this.callback = callback;\n\n this.documentTimeIntervalTimer = setInterval(() => {\n this.callback({\n documentTime: this.getDocumentTime(),\n });\n }, observableDOMParameters.pingIntervalMilliseconds || 5000);\n\n this.domRunner = runnerFactory(\n observableDOMParameters.htmlPath,\n observableDOMParameters.htmlContents,\n observableDOMParameters.params,\n (domRunnerMessage: DOMRunnerMessage) => {\n if (domRunnerMessage.loaded) {\n this.createVirtualDomElementWithChildren(\n this.domRunner.getDocument() as unknown as Element,\n null,\n );\n\n const snapshot = virtualDomElementToStatic(\n this.getVirtualDomElementForRealElementOrThrow(\n this.domRunner.getDocument() as unknown as Element,\n ),\n );\n\n this.callback({\n snapshot,\n documentTime: this.getDocumentTime(),\n });\n } else if (domRunnerMessage.mutationList) {\n this.processModificationList(domRunnerMessage.mutationList);\n } else if (domRunnerMessage.logMessage) {\n this.callback({\n logMessage: domRunnerMessage.logMessage,\n documentTime: this.getDocumentTime(),\n });\n }\n },\n );\n }\n\n public addIPCWebsocket(webSocket: WebSocket) {\n return this.domRunner.addIPCWebsocket(webSocket);\n }\n\n public addConnectedUserId(connectionId: number): void {\n this.domRunner.getWindow().dispatchEvent(\n new (this.domRunner.getWindow().CustomEvent)(\"connected\", {\n detail: { connectionId },\n }),\n );\n }\n\n public removeConnectedUserId(connectionId: number): void {\n this.domRunner.getWindow().dispatchEvent(\n new (this.domRunner.getWindow().CustomEvent)(\"disconnected\", {\n detail: { connectionId },\n }),\n );\n }\n\n private processModificationList(mutationList: Array<MutationRecord>): void {\n const documentEl = this.domRunner.getDocument() as unknown as Element;\n const documentVirtualDomElement = this.realElementToVirtualElement.get(documentEl);\n if (!documentVirtualDomElement) {\n throw new Error(`document not created in processModificationList`);\n }\n\n if (mutationList.length > 1) {\n // TODO - walk back through the records to derive the intermediate states (e.g. if an attribute is later added to\n // an element created in an earlier record then it should not have that attribute when the element is added.\n // This is important as incorrect attribute sets can affect visibility and expected client performance.\n console.error(\n \"More than one mutation record received. It is possible that intermediate states are incorrect.\",\n );\n }\n\n for (const mutation of mutationList) {\n if (this.isIgnoredElement(mutation.target as Element | Text)) {\n continue;\n }\n\n if (\n mutation.type === \"attributes\" &&\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.isIgnoredAttribute(mutation.target as Element | Text, mutation.attributeName!)\n ) {\n continue;\n }\n\n this.addKnownNodesInMutation(mutation);\n\n // Convert the \"real\" DOM MutationRecord into a \"virtual\" DOM MutationRecord that references the VirtualDOMElements\n // This is done so that the same process for handling mutations can be used for both changes to a live DOM and also\n // to diffs between DOM snapshots when reloading\n const firstNonIgnoredPreviousSibling = mutation.previousSibling\n ? this.getFirstNonIgnoredPreviousSibling(mutation.previousSibling as Element | Text)\n : null;\n const targetElement = this.getVirtualDomElementForRealElementOrThrow(\n mutation.target as Element | Text,\n );\n const addedNodes: Array<StaticVirtualDomElement> = [];\n for (const node of mutation.addedNodes) {\n if (this.isIgnoredElement(node as Element | Text)) {\n continue;\n }\n const virtualDomElement = this.getVirtualDomElementForRealElementOrThrow(\n node as Element | Text,\n );\n addedNodes.push(virtualDomElementToStatic(virtualDomElement));\n }\n\n const removedNodeIds: Array<number> = [];\n for (const node of mutation.removedNodes) {\n if (this.isIgnoredElement(node as Element | Text)) {\n continue;\n }\n const virtualDomElement = this.getVirtualDomElementForRealElementOrThrow(\n node as Element | Text,\n );\n removedNodeIds.push(virtualDomElement.nodeId);\n }\n\n const mutationRecord: StaticVirtualDomMutationIdsRecord = {\n type: mutation.type,\n targetId: targetElement.nodeId,\n addedNodes,\n removedNodeIds,\n previousSiblingId:\n firstNonIgnoredPreviousSibling !== null\n ? this.getVirtualDomElementForRealElementOrThrow(firstNonIgnoredPreviousSibling).nodeId\n : null,\n attribute: mutation.attributeName\n ? {\n attributeName: mutation.attributeName,\n value: (mutation.target as Element).getAttribute(mutation.attributeName),\n }\n : null,\n };\n\n this.callback({\n mutation: mutationRecord,\n documentTime: this.getDocumentTime(),\n });\n\n this.removeKnownNodesInMutation(mutation);\n }\n }\n\n private addKnownNodesInMutation(mutation: MutationRecord): void {\n const targetNode = mutation.target as Element | Text;\n const virtualDomElement = this.realElementToVirtualElement.get(targetNode);\n if (!virtualDomElement) {\n throw new Error(\n \"Unknown node in addKnownNodesInMutation:\" + targetNode + \",\" + mutation.type,\n );\n }\n if (mutation.type === \"childList\") {\n let previousSibling = mutation.previousSibling;\n let index = 0;\n while (previousSibling && this.isIgnoredElement(previousSibling as Element | Text)) {\n previousSibling = previousSibling.previousSibling;\n }\n if (previousSibling) {\n const previousSiblingElement = this.realElementToVirtualElement.get(\n previousSibling as Element | Text,\n );\n if (!previousSiblingElement) {\n throw new Error(\"Unknown previous sibling\");\n }\n index = virtualDomElement.childNodes.indexOf(previousSiblingElement);\n if (index === -1) {\n throw new Error(\"Previous sibling is not currently a child of the parent element\");\n }\n index += 1;\n }\n mutation.addedNodes.forEach((node: Node) => {\n const asElementOrText = node as Element | Text;\n const childVirtualDomElement = this.createVirtualDomElementWithChildren(\n asElementOrText,\n virtualDomElement,\n );\n if (childVirtualDomElement) {\n if (virtualDomElement.childNodes.indexOf(childVirtualDomElement) === -1) {\n virtualDomElement.childNodes.splice(index, 0, childVirtualDomElement);\n index++;\n }\n }\n });\n } else if (mutation.type === \"attributes\") {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const attributeName = mutation.attributeName!;\n if (this.isIgnoredAttribute(targetNode, attributeName)) {\n return;\n }\n const attributeValue = (targetNode as Element).getAttribute(attributeName);\n if (attributeValue === null) {\n delete virtualDomElement.attributes[attributeName];\n } else {\n virtualDomElement.attributes[attributeName] = attributeValue;\n }\n } else if (mutation.type === \"characterData\") {\n virtualDomElement.textContent = targetNode.textContent ? targetNode.textContent : undefined;\n }\n }\n\n private removeKnownNodesInMutation(mutation: MutationRecord): void {\n const targetNode = mutation.target as Element | Text;\n const virtualDomElement = this.realElementToVirtualElement.get(targetNode);\n if (!virtualDomElement) {\n throw new Error(\"Unknown node in mutation list:\" + targetNode + \", \" + mutation.type);\n }\n if (mutation.type === \"childList\") {\n for (const node of mutation.removedNodes) {\n const asElementOrText = node as Element | Text;\n if (this.isIgnoredElement(asElementOrText)) {\n continue;\n }\n const childDomElement = this.realElementToVirtualElement.get(asElementOrText);\n if (!childDomElement) {\n console.warn(this.htmlPath, \"Unknown node in removeKnownNodesInMutation\");\n continue;\n } else {\n this.removeVirtualDomElement(childDomElement);\n const index = virtualDomElement.childNodes.indexOf(childDomElement);\n virtualDomElement.childNodes.splice(index, 1);\n }\n }\n return;\n }\n }\n\n private removeVirtualDomElement(virtualDomElement: LiveVirtualDomElement): void {\n this.nodeIdToNode.delete(virtualDomElement.nodeId);\n this.nodeToNodeId.delete(virtualDomElement);\n this.realElementToVirtualElement.delete(virtualDomElement.realElement);\n for (const child of virtualDomElement.childNodes) {\n this.removeVirtualDomElement(child);\n }\n }\n\n private createVirtualDomElementWithChildren(\n node: Element | Text,\n parent: LiveVirtualDomElement | null,\n ): LiveVirtualDomElement | null {\n const virtualElement = this.createVirtualDomElement(node, parent);\n if (!virtualElement) {\n return null;\n }\n if ((node as Element).childNodes) {\n for (let i = 0; i < (node as Element).childNodes.length; i++) {\n const child = (node as Element).childNodes[i];\n const childVirtualElement = this.createVirtualDomElementWithChildren(\n child as Element | Text,\n virtualElement,\n );\n if (childVirtualElement) {\n virtualElement.childNodes.push(childVirtualElement);\n }\n }\n }\n\n return virtualElement;\n }\n\n private createVirtualDomElement(\n node: Element | Text,\n parent: LiveVirtualDomElement | null,\n ): LiveVirtualDomElement | null {\n if (this.isIgnoredElement(node)) {\n return null;\n }\n const existingValue = this.realElementToVirtualElement.get(node);\n if (existingValue !== undefined) {\n throw new Error(\"Node already has a virtual element: \" + node.nodeName);\n }\n if (!node) {\n throw new Error(\"Cannot assign node id to null\");\n }\n\n const attributes: { [key: string]: string } = {};\n if ((node as any).attributes) {\n const asHTMLElement = node as HTMLElement;\n for (const key of asHTMLElement.getAttributeNames()) {\n const value = asHTMLElement.getAttribute(key);\n if (value === null) {\n throw new Error(\"Null attribute value for key: \" + key);\n }\n if (!this.isIgnoredAttribute(node, key)) {\n attributes[key] = value;\n }\n }\n }\n\n const nodeId = this.nextNodeId++;\n const virtualElement: LiveVirtualDomElement = {\n nodeId,\n tag: node.nodeName,\n attributes,\n childNodes: [],\n realElement: node,\n parent,\n };\n if (node instanceof this.domRunner.getWindow().Text && node.textContent) {\n virtualElement.textContent = node.textContent;\n }\n this.nodeToNodeId.set(virtualElement, nodeId);\n this.nodeIdToNode.set(nodeId, virtualElement);\n this.realElementToVirtualElement.set(node, virtualElement);\n return virtualElement;\n }\n\n private getFirstNonIgnoredPreviousSibling(node: Element | Text): Element | Text | null {\n let currentNode = node;\n if (!this.isIgnoredElement(currentNode)) {\n return currentNode;\n }\n while (currentNode && currentNode.previousSibling) {\n currentNode = currentNode.previousSibling as Element | Text;\n if (!this.isIgnoredElement(currentNode)) {\n return currentNode;\n }\n }\n return null;\n }\n\n private getVirtualDomElementForRealElementOrThrow(\n realElement: Element | Text,\n ): LiveVirtualDomElement {\n const virtualElement = this.realElementToVirtualElement.get(realElement);\n if (!virtualElement) {\n throw new Error(`Virtual element not found for real element`);\n }\n return virtualElement;\n }\n\n private isIgnoredElement(node: Element | Text): boolean {\n if (this.ignoreTextNodes && node instanceof this.domRunner.getWindow().Text) {\n return true;\n } else if (node instanceof this.domRunner.getWindow().HTMLScriptElement) {\n return true;\n } else if (node instanceof this.domRunner.getWindow().Comment) {\n return true;\n }\n return false;\n }\n\n private isIgnoredAttribute(node: Element | Text, attributeName: string): boolean {\n return attributeName.startsWith(\"on\");\n }\n\n public dispatchRemoteEventFromConnectionId(connectionId: number, remoteEvent: RemoteEvent): void {\n const domNode = this.nodeIdToNode.get(remoteEvent.nodeId);\n if (!domNode) {\n console.error(\"Unknown node ID in remote event: \" + remoteEvent.nodeId);\n return;\n }\n\n if (domNode instanceof this.domRunner.getWindow().Text) {\n console.warn(\"Cannot dispatch remote event to text node\");\n return;\n }\n\n this.domRunner.dispatchRemoteEventFromConnectionId(\n connectionId,\n domNode.realElement as Element,\n remoteEvent,\n );\n }\n\n public dispose() {\n clearInterval(this.documentTimeIntervalTimer);\n this.domRunner.dispose();\n }\n\n private getDocumentTime() {\n return this.domRunner.getDocumentTime();\n }\n}\n", "import vm from \"vm\";\n\nimport { LogMessage, RemoteEvent } from \"@mml-io/observable-dom-common\";\nimport { AbortablePromise, DOMWindow, JSDOM, ResourceLoader, VirtualConsole } from \"jsdom\";\nimport * as nodeFetch from \"node-fetch\";\nimport nodeFetchFn from \"node-fetch\";\n\nimport { DOMRunnerFactory, DOMRunnerInterface, DOMRunnerMessage } from \"./ObservableDom\";\n\nconst ErrDomWindowNotInitialized = \"DOMWindow not initialized\";\n\n// TODO - remove this monkeypatching if it's possible to avoid the race conditions in naive MutationObserver usage\nconst monkeyPatchedMutationRecordCallbacks = new Set<() => void>();\nfunction installMutationObserverMonkeyPatch() {\n /*\n This monkey patch replaces the `createImpl` exported function implementation in the `MutationRecord` class in JSDOM\n to insert an iteration through callbacks that are therefore fired before a subsequent MutationRecord is created.\n This provides an opportunity to invoke the MutationObservers with a single MutationRecord rather than multiple.\n\n This is necessary as (at least intuitive) usage of the MutationObserver API does not enable creating accurate\n incremental diffs as the handling of all-but-the-last MutationRecord in a list requires collecting state from the\n DOM that has been since been mutated further. (e.g. if an attribute is changed twice in a row the first event cannot\n discover the intermediate value of the attribute as it can only query the latest DOM state). Whilst this simple case\n is solvable by walking backwards through the list of MutationRecords and using `oldValue` there are cases where adding\n child elements with the correct attributes is not possible when handling intermediate diffs.\n */\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const MutationRecordExports = require(\"jsdom/lib/jsdom/living/generated/MutationRecord\");\n const originalCreateImpl = MutationRecordExports.createImpl;\n // This overwrites the function property on the exports that mutation-observers.js uses to create MutationRecords.\n MutationRecordExports.createImpl = (...args: any[]) => {\n for (const callback of monkeyPatchedMutationRecordCallbacks) {\n callback();\n }\n return originalCreateImpl.call(null, ...args);\n };\n}\nlet monkeyPatchInstalled = false;\n\nexport const JSDOMRunnerFactory: DOMRunnerFactory = (\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (mutationList: DOMRunnerMessage) => void,\n): DOMRunnerInterface => {\n return new JSDOMRunner(htmlPath, htmlContents, params, callback);\n};\n\n// This is used to stop JSDOM trying to load resources\nclass RejectionResourceLoader extends ResourceLoader {\n public fetch(url: string): AbortablePromise<Buffer> | null {\n console.error(\"RejectionResourceLoader.fetch\", url);\n return null;\n }\n}\n\nexport class JSDOMRunner {\n private monkeyPatchMutationRecordCallback: () => void;\n\n private ipcWebsockets = new Set<WebSocket>();\n\n public domWindow: DOMWindow | null = null;\n private jsDom: JSDOM;\n\n private callback: (message: DOMRunnerMessage) => void;\n private mutationObserver: MutationObserver | null = null;\n private htmlPath: string;\n private ipcListeners = new Set<(event: any) => void>();\n\n private documentStartTime = Date.now();\n\n private isLoaded = false;\n private logBuffer: LogMessage[] = [];\n\n constructor(\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (domRunnerMessage: DOMRunnerMessage) => void,\n ) {\n this.htmlPath = htmlPath;\n this.callback = callback;\n\n if (!monkeyPatchInstalled) {\n installMutationObserverMonkeyPatch();\n monkeyPatchInstalled = true;\n }\n\n this.monkeyPatchMutationRecordCallback = () => {\n /*\n This is called before every creation of a MutationRecord so that it can be used to process an existing record to\n avoid handling multiple MutationRecords at a time (see comment at the top of this file).\n */\n const records = this.mutationObserver?.takeRecords();\n if (records && records.length > 1) {\n throw new Error(\n \"The monkey patching should have prevented more than one record being handled at a time\",\n );\n } else if (records && records.length > 0) {\n this.callback({\n mutationList: records,\n });\n }\n };\n monkeyPatchedMutationRecordCallbacks.add(this.monkeyPatchMutationRecordCallback);\n\n this.jsDom = new JSDOM(htmlContents, {\n runScripts: \"dangerously\",\n resources: new RejectionResourceLoader(),\n url: this.htmlPath,\n virtualConsole: this.createVirtualConsole(),\n beforeParse: (window) => {\n this.domWindow = window;\n\n this.domWindow.fetch = nodeFetchFn as unknown as typeof fetch;\n this.domWindow.Headers = nodeFetch.Headers as unknown as typeof Headers;\n this.domWindow.Request = nodeFetch.Request as unknown as typeof Request;\n this.domWindow.Response = nodeFetch.Response as unknown as typeof Response;\n\n // This is a polyfill for https://developer.mozilla.org/en-US/docs/Web/API/Document/timeline\n const timeline = {};\n Object.defineProperty(timeline, \"currentTime\", {\n get: () => {\n return this.getDocumentTime();\n },\n });\n (window.document as any).timeline = timeline;\n\n // JSON stringify and parse to avoid potential reference leaks from the params object\n window.params = JSON.parse(JSON.stringify(params));\n\n const oldDocumentAddEventListener = window.document.addEventListener;\n window.document.addEventListener = (...args: [string, EventListener, ...any[]]) => {\n const [eventName, listener] = args;\n if (eventName === \"ipc\") {\n this.ipcListeners.add(listener);\n }\n return oldDocumentAddEventListener.call(window.document, ...args);\n };\n\n const oldDocumentRemoveEventListener = window.document.addEventListener;\n window.document.removeEventListener = (...args: [string, EventListener, ...any[]]) => {\n const [eventName, listener] = args;\n if (eventName === \"ipc\") {\n this.ipcListeners.delete(listener);\n }\n return oldDocumentRemoveEventListener.call(window.document, ...args);\n };\n\n this.mutationObserver = new window.MutationObserver((mutationList) => {\n this.callback({\n mutationList,\n });\n });\n\n window.addEventListener(\"load\", () => {\n this.mutationObserver?.observe(window.document, {\n attributes: true,\n childList: true,\n subtree: true,\n characterData: true,\n });\n\n this.isLoaded = true;\n\n this.callback({\n loaded: true,\n });\n\n this.flushLogBuffer();\n });\n },\n });\n }\n\n private flushLogBuffer() {\n for (const logMessage of this.logBuffer) {\n this.callback({\n logMessage,\n });\n }\n\n this.logBuffer = [];\n }\n\n private log(message: LogMessage) {\n if (!this.isLoaded) {\n this.logBuffer.push(message);\n return;\n }\n\n this.callback({\n logMessage: message,\n });\n }\n\n public getDocument(): Document {\n if (!this.domWindow) {\n throw new Error(ErrDomWindowNotInitialized);\n }\n\n return this.domWindow.document;\n }\n\n public getWindow(): any {\n return this.domWindow;\n }\n\n public addIPCWebsocket(webSocket: WebSocket) {\n if (!this.domWindow) {\n throw new Error(ErrDomWindowNotInitialized);\n }\n if (this.ipcListeners.size === 0) {\n console.error(\"ipc requested, but no ipc listeners registered on document:\", this.htmlPath);\n webSocket.close();\n return;\n }\n this.ipcWebsockets.add(webSocket);\n const event = new this.domWindow.CustomEvent(\"ipc\", {\n detail: {\n webSocket,\n },\n });\n for (const ipcListener of this.ipcListeners) {\n ipcListener(event);\n }\n webSocket.addEventListener(\"close\", () => {\n this.ipcWebsockets.delete(webSocket);\n });\n return;\n }\n\n public dispose() {\n const records = this.mutationObserver?.takeRecords();\n this.callback({\n mutationList: records,\n });\n monkeyPatchedMutationRecordCallbacks.delete(this.monkeyPatchMutationRecordCallback);\n this.mutationObserver?.disconnect();\n this.jsDom.window.close();\n }\n\n public getDocumentTime() {\n return Date.now() - this.documentStartTime;\n }\n\n public dispatchRemoteEventFromConnectionId(\n connectionId: number,\n domNode: Element,\n remoteEvent: RemoteEvent,\n ) {\n if (!this.domWindow) {\n throw new Error(ErrDomWindowNotInitialized);\n }\n\n const bubbles = remoteEvent.bubbles || false;\n const remoteEventObject = new this.domWindow.CustomEvent(remoteEvent.name, {\n bubbles,\n detail: { ...remoteEvent.params, connectionId },\n });\n\n const eventTypeLowerCase = remoteEvent.name.toLowerCase();\n\n // TODO - check if there are other events that automatically wire up similarly to click->onclick and avoid those too\n if (eventTypeLowerCase !== \"click\") {\n const handlerAttributeName = \"on\" + eventTypeLowerCase;\n const handlerAttributeValue = domNode.getAttribute(handlerAttributeName);\n if (handlerAttributeValue) {\n // This event is defined as an HTML event attribute.\n const script = handlerAttributeValue;\n const vmContext = this.jsDom.getInternalVMContext();\n try {\n const invoke = vm.runInContext(`(function(event){ ${script} })`, vmContext);\n Reflect.apply(invoke, domNode, [remoteEventObject]);\n } catch (e) {\n console.error(\"Error running event handler:\", e);\n }\n }\n }\n\n // Dispatch the event via JavaScript.\n domNode.dispatchEvent(remoteEventObject);\n }\n\n private createVirtualConsole(): VirtualConsole {\n const virtualConsole = new VirtualConsole();\n virtualConsole.on(\"jsdomError\", (...args) => {\n this.log({\n level: \"system\",\n content: args,\n });\n });\n virtualConsole.on(\"error\", (...args) => {\n this.log({\n level: \"error\",\n content: args,\n });\n });\n virtualConsole.on(\"warn\", (...args) => {\n this.log({\n level: \"warn\",\n content: args,\n });\n });\n virtualConsole.on(\"log\", (...args) => {\n this.log({\n level: \"log\",\n content: args,\n });\n });\n virtualConsole.on(\"info\", (...args) => {\n this.log({\n level: \"info\",\n content: args,\n });\n });\n return virtualConsole;\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
3
|
+
"sources": ["../src/index.ts", "../src/utils.ts", "../src/ObservableDOM.ts", "../src/JSDOMRunner.ts"],
|
|
4
|
+
"sourcesContent": ["export * from \"./ObservableDOM\";\nexport * from \"./JSDOMRunner\";\n", "import { StaticVirtualDOMElement } from \"@mml-io/observable-dom-common\";\n\nimport { LiveVirtualDOMElement } from \"./ObservableDOM\";\n\nexport function virtualDOMElementToStatic(el: LiveVirtualDOMElement): StaticVirtualDOMElement {\n return {\n nodeId: el.nodeId,\n tag: el.tag,\n attributes: el.attributes,\n childNodes: el.childNodes.map((child) => virtualDOMElementToStatic(child)),\n textContent: el.textContent,\n };\n}\n", "import {\n LogMessage,\n ObservableDOMInterface,\n ObservableDOMMessage,\n ObservableDOMParameters,\n RemoteEvent,\n StaticVirtualDOMElement,\n StaticVirtualDOMMutationIdsRecord,\n} from \"@mml-io/observable-dom-common\";\n\nimport { virtualDOMElementToStatic } from \"./utils\";\n\nexport type DOMRunnerMessage = {\n loaded?: boolean;\n mutationList?: Array<MutationRecord>;\n logMessage?: LogMessage;\n};\n\nexport type DOMRunnerInterface = {\n getDocument(): Document;\n getWindow(): Window & {\n CustomEvent: typeof CustomEvent;\n Text: typeof Text;\n HTMLScriptElement: typeof HTMLScriptElement;\n Comment: typeof Comment;\n }; // TODO - Define this without using JSDOM types\n dispatchRemoteEventFromConnectionId(\n connectionId: number,\n realElement: Element,\n remoteEvent: RemoteEvent,\n ): void;\n dispose(): void;\n getDocumentTime(): number;\n};\n\nexport type DOMRunnerFactory = (\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (domRunnerMessage: DOMRunnerMessage) => void,\n) => DOMRunnerInterface;\n\nexport type LiveVirtualDOMElement = Omit<StaticVirtualDOMElement, \"childNodes\"> & {\n realElement: Element | Text;\n childNodes: Array<LiveVirtualDOMElement>;\n parent: LiveVirtualDOMElement | null;\n};\n\nexport class ObservableDOM implements ObservableDOMInterface {\n private nodeToNodeId = new Map<LiveVirtualDOMElement, number>();\n private nodeIdToNode = new Map<number, LiveVirtualDOMElement>();\n private realElementToVirtualElement = new Map<Element | Text, LiveVirtualDOMElement>();\n private ignoreTextNodes = true;\n private callback: (message: ObservableDOMMessage, observableDOM: ObservableDOMInterface) => void;\n private nextNodeId = 1;\n private htmlPath: string;\n private domRunner: DOMRunnerInterface;\n\n private documentTimeIntervalTimer: NodeJS.Timer;\n\n constructor(\n observableDOMParameters: ObservableDOMParameters,\n callback: (message: ObservableDOMMessage, observableDOM: ObservableDOMInterface) => void,\n runnerFactory: DOMRunnerFactory,\n ) {\n this.htmlPath = observableDOMParameters.htmlPath;\n this.ignoreTextNodes = observableDOMParameters.ignoreTextNodes;\n this.callback = callback;\n\n this.documentTimeIntervalTimer = setInterval(() => {\n this.callback(\n {\n documentTime: this.getDocumentTime(),\n },\n this,\n );\n }, observableDOMParameters.pingIntervalMilliseconds || 5000);\n\n this.domRunner = runnerFactory(\n observableDOMParameters.htmlPath,\n observableDOMParameters.htmlContents,\n observableDOMParameters.params,\n (domRunnerMessage: DOMRunnerMessage) => {\n if (domRunnerMessage.loaded) {\n this.createVirtualDOMElementWithChildren(\n this.domRunner.getDocument() as unknown as Element,\n null,\n );\n\n const snapshot = virtualDOMElementToStatic(\n this.getVirtualDOMElementForRealElementOrThrow(\n this.domRunner.getDocument() as unknown as Element,\n ),\n );\n\n this.callback(\n {\n snapshot,\n documentTime: this.getDocumentTime(),\n },\n this,\n );\n } else if (domRunnerMessage.mutationList) {\n this.processModificationList(domRunnerMessage.mutationList);\n } else if (domRunnerMessage.logMessage) {\n this.callback(\n {\n logMessage: domRunnerMessage.logMessage,\n documentTime: this.getDocumentTime(),\n },\n this,\n );\n }\n },\n );\n }\n\n public addConnectedUserId(connectionId: number): void {\n this.domRunner.getWindow().dispatchEvent(\n new (this.domRunner.getWindow().CustomEvent)(\"connected\", {\n detail: { connectionId },\n }),\n );\n }\n\n public removeConnectedUserId(connectionId: number): void {\n this.domRunner.getWindow().dispatchEvent(\n new (this.domRunner.getWindow().CustomEvent)(\"disconnected\", {\n detail: { connectionId },\n }),\n );\n }\n\n private processModificationList(mutationList: Array<MutationRecord>): void {\n const documentEl = this.domRunner.getDocument() as unknown as Element;\n const documentVirtualDOMElement = this.realElementToVirtualElement.get(documentEl);\n if (!documentVirtualDOMElement) {\n throw new Error(`document not created in processModificationList`);\n }\n\n if (mutationList.length > 1) {\n // TODO - walk back through the records to derive the intermediate states (e.g. if an attribute is later added to\n // an element created in an earlier record then it should not have that attribute when the element is added.\n // This is important as incorrect attribute sets can affect visibility and expected client performance.\n console.error(\n \"More than one mutation record received. It is possible that intermediate states are incorrect.\",\n );\n }\n\n for (const mutation of mutationList) {\n if (this.isIgnoredElement(mutation.target as Element | Text)) {\n continue;\n }\n\n if (\n mutation.type === \"attributes\" &&\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n this.isIgnoredAttribute(mutation.target as Element | Text, mutation.attributeName!)\n ) {\n continue;\n }\n\n this.addKnownNodesInMutation(mutation);\n\n // Convert the \"real\" DOM MutationRecord into a \"virtual\" DOM MutationRecord that references the VirtualDOMElements\n // This is done so that the same process for handling mutations can be used for both changes to a live DOM and also\n // to diffs between DOM snapshots when reloading\n const firstNonIgnoredPreviousSibling = mutation.previousSibling\n ? this.getFirstNonIgnoredPreviousSibling(mutation.previousSibling as Element | Text)\n : null;\n const targetElement = this.getVirtualDOMElementForRealElementOrThrow(\n mutation.target as Element | Text,\n );\n const addedNodes: Array<StaticVirtualDOMElement> = [];\n for (const node of mutation.addedNodes) {\n if (this.isIgnoredElement(node as Element | Text)) {\n continue;\n }\n const virtualDOMElement = this.getVirtualDOMElementForRealElementOrThrow(\n node as Element | Text,\n );\n addedNodes.push(virtualDOMElementToStatic(virtualDOMElement));\n }\n\n const removedNodeIds: Array<number> = [];\n for (const node of mutation.removedNodes) {\n if (this.isIgnoredElement(node as Element | Text)) {\n continue;\n }\n const virtualDOMElement = this.getVirtualDOMElementForRealElementOrThrow(\n node as Element | Text,\n );\n removedNodeIds.push(virtualDOMElement.nodeId);\n }\n\n const mutationRecord: StaticVirtualDOMMutationIdsRecord = {\n type: mutation.type,\n targetId: targetElement.nodeId,\n addedNodes,\n removedNodeIds,\n previousSiblingId:\n firstNonIgnoredPreviousSibling !== null\n ? this.getVirtualDOMElementForRealElementOrThrow(firstNonIgnoredPreviousSibling).nodeId\n : null,\n attribute: mutation.attributeName\n ? {\n attributeName: mutation.attributeName,\n value: (mutation.target as Element).getAttribute(mutation.attributeName),\n }\n : null,\n };\n\n this.callback(\n {\n mutation: mutationRecord,\n documentTime: this.getDocumentTime(),\n },\n this,\n );\n\n this.removeKnownNodesInMutation(mutation);\n }\n }\n\n private addKnownNodesInMutation(mutation: MutationRecord): void {\n const targetNode = mutation.target as Element | Text;\n const virtualDOMElement = this.realElementToVirtualElement.get(targetNode);\n if (!virtualDOMElement) {\n throw new Error(\n \"Unknown node in addKnownNodesInMutation:\" + targetNode + \",\" + mutation.type,\n );\n }\n if (mutation.type === \"childList\") {\n let previousSibling = mutation.previousSibling;\n let index = 0;\n while (previousSibling && this.isIgnoredElement(previousSibling as Element | Text)) {\n previousSibling = previousSibling.previousSibling;\n }\n if (previousSibling) {\n const previousSiblingElement = this.realElementToVirtualElement.get(\n previousSibling as Element | Text,\n );\n if (!previousSiblingElement) {\n throw new Error(\"Unknown previous sibling\");\n }\n index = virtualDOMElement.childNodes.indexOf(previousSiblingElement);\n if (index === -1) {\n throw new Error(\"Previous sibling is not currently a child of the parent element\");\n }\n index += 1;\n }\n mutation.addedNodes.forEach((node: Node) => {\n const asElementOrText = node as Element | Text;\n const childVirtualDOMElement = this.createVirtualDOMElementWithChildren(\n asElementOrText,\n virtualDOMElement,\n );\n if (childVirtualDOMElement) {\n if (virtualDOMElement.childNodes.indexOf(childVirtualDOMElement) === -1) {\n virtualDOMElement.childNodes.splice(index, 0, childVirtualDOMElement);\n index++;\n }\n }\n });\n } else if (mutation.type === \"attributes\") {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const attributeName = mutation.attributeName!;\n if (this.isIgnoredAttribute(targetNode, attributeName)) {\n return;\n }\n const attributeValue = (targetNode as Element).getAttribute(attributeName);\n if (attributeValue === null) {\n delete virtualDOMElement.attributes[attributeName];\n } else {\n virtualDOMElement.attributes[attributeName] = attributeValue;\n }\n } else if (mutation.type === \"characterData\") {\n virtualDOMElement.textContent = targetNode.textContent ? targetNode.textContent : undefined;\n }\n }\n\n private removeKnownNodesInMutation(mutation: MutationRecord): void {\n const targetNode = mutation.target as Element | Text;\n const virtualDOMElement = this.realElementToVirtualElement.get(targetNode);\n if (!virtualDOMElement) {\n throw new Error(\"Unknown node in mutation list:\" + targetNode + \", \" + mutation.type);\n }\n if (mutation.type === \"childList\") {\n for (const node of mutation.removedNodes) {\n const asElementOrText = node as Element | Text;\n if (this.isIgnoredElement(asElementOrText)) {\n continue;\n }\n const childDOMElement = this.realElementToVirtualElement.get(asElementOrText);\n if (!childDOMElement) {\n console.warn(this.htmlPath, \"Unknown node in removeKnownNodesInMutation\");\n continue;\n } else {\n this.removeVirtualDOMElement(childDOMElement);\n const index = virtualDOMElement.childNodes.indexOf(childDOMElement);\n virtualDOMElement.childNodes.splice(index, 1);\n }\n }\n return;\n }\n }\n\n private removeVirtualDOMElement(virtualDOMElement: LiveVirtualDOMElement): void {\n this.nodeIdToNode.delete(virtualDOMElement.nodeId);\n this.nodeToNodeId.delete(virtualDOMElement);\n this.realElementToVirtualElement.delete(virtualDOMElement.realElement);\n for (const child of virtualDOMElement.childNodes) {\n this.removeVirtualDOMElement(child);\n }\n }\n\n private createVirtualDOMElementWithChildren(\n node: Element | Text,\n parent: LiveVirtualDOMElement | null,\n ): LiveVirtualDOMElement | null {\n const virtualElement = this.createVirtualDOMElement(node, parent);\n if (!virtualElement) {\n return null;\n }\n if ((node as Element).childNodes) {\n for (let i = 0; i < (node as Element).childNodes.length; i++) {\n const child = (node as Element).childNodes[i];\n const childVirtualElement = this.createVirtualDOMElementWithChildren(\n child as Element | Text,\n virtualElement,\n );\n if (childVirtualElement) {\n virtualElement.childNodes.push(childVirtualElement);\n }\n }\n }\n\n return virtualElement;\n }\n\n private createVirtualDOMElement(\n node: Element | Text,\n parent: LiveVirtualDOMElement | null,\n ): LiveVirtualDOMElement | null {\n if (this.isIgnoredElement(node)) {\n return null;\n }\n const existingValue = this.realElementToVirtualElement.get(node);\n if (existingValue !== undefined) {\n throw new Error(\"Node already has a virtual element: \" + node.nodeName);\n }\n if (!node) {\n throw new Error(\"Cannot assign node id to null\");\n }\n\n const attributes: { [key: string]: string } = {};\n if ((node as any).attributes) {\n const asHTMLElement = node as HTMLElement;\n for (const key of asHTMLElement.getAttributeNames()) {\n const value = asHTMLElement.getAttribute(key);\n if (value === null) {\n throw new Error(\"Null attribute value for key: \" + key);\n }\n if (!this.isIgnoredAttribute(node, key)) {\n attributes[key] = value;\n }\n }\n }\n\n const nodeId = this.nextNodeId++;\n const virtualElement: LiveVirtualDOMElement = {\n nodeId,\n tag: node.nodeName,\n attributes,\n childNodes: [],\n realElement: node,\n parent,\n };\n if (node instanceof this.domRunner.getWindow().Text && node.textContent) {\n virtualElement.textContent = node.textContent;\n }\n this.nodeToNodeId.set(virtualElement, nodeId);\n this.nodeIdToNode.set(nodeId, virtualElement);\n this.realElementToVirtualElement.set(node, virtualElement);\n return virtualElement;\n }\n\n private getFirstNonIgnoredPreviousSibling(node: Element | Text): Element | Text | null {\n let currentNode = node;\n if (!this.isIgnoredElement(currentNode)) {\n return currentNode;\n }\n while (currentNode && currentNode.previousSibling) {\n currentNode = currentNode.previousSibling as Element | Text;\n if (!this.isIgnoredElement(currentNode)) {\n return currentNode;\n }\n }\n return null;\n }\n\n private getVirtualDOMElementForRealElementOrThrow(\n realElement: Element | Text,\n ): LiveVirtualDOMElement {\n const virtualElement = this.realElementToVirtualElement.get(realElement);\n if (!virtualElement) {\n throw new Error(`Virtual element not found for real element`);\n }\n return virtualElement;\n }\n\n private isIgnoredElement(node: Element | Text): boolean {\n if (this.ignoreTextNodes && node instanceof this.domRunner.getWindow().Text) {\n return true;\n } else if (node instanceof this.domRunner.getWindow().HTMLScriptElement) {\n return true;\n } else if (node instanceof this.domRunner.getWindow().Comment) {\n return true;\n }\n return false;\n }\n\n private isIgnoredAttribute(node: Element | Text, attributeName: string): boolean {\n return attributeName.startsWith(\"on\");\n }\n\n public dispatchRemoteEventFromConnectionId(connectionId: number, remoteEvent: RemoteEvent): void {\n const domNode = this.nodeIdToNode.get(remoteEvent.nodeId);\n if (!domNode) {\n console.error(\"Unknown node ID in remote event: \" + remoteEvent.nodeId);\n return;\n }\n\n if (domNode instanceof this.domRunner.getWindow().Text) {\n console.warn(\"Cannot dispatch remote event to text node\");\n return;\n }\n\n this.domRunner.dispatchRemoteEventFromConnectionId(\n connectionId,\n domNode.realElement as Element,\n remoteEvent,\n );\n }\n\n public dispose() {\n clearInterval(this.documentTimeIntervalTimer);\n this.domRunner.dispose();\n }\n\n private getDocumentTime() {\n return this.domRunner.getDocumentTime();\n }\n}\n", "import vm from \"vm\";\n\nimport { LogMessage, RemoteEvent } from \"@mml-io/observable-dom-common\";\nimport { AbortablePromise, DOMWindow, JSDOM, ResourceLoader, VirtualConsole } from \"jsdom\";\nimport * as nodeFetch from \"node-fetch\";\nimport nodeFetchFn from \"node-fetch\";\n\nimport { DOMRunnerFactory, DOMRunnerInterface, DOMRunnerMessage } from \"./ObservableDOM\";\n\nconst ErrDOMWindowNotInitialized = \"DOMWindow not initialized\";\n\n// TODO - remove this monkeypatching if it's possible to avoid the race conditions in naive MutationObserver usage\nconst monkeyPatchedMutationRecordCallbacks = new Set<() => void>();\nfunction installMutationObserverMonkeyPatch() {\n /*\n This monkey patch replaces the `createImpl` exported function implementation in the `MutationRecord` class in JSDOM\n to insert an iteration through callbacks that are therefore fired before a subsequent MutationRecord is created.\n This provides an opportunity to invoke the MutationObservers with a single MutationRecord rather than multiple.\n\n This is necessary as (at least intuitive) usage of the MutationObserver API does not enable creating accurate\n incremental diffs as the handling of all-but-the-last MutationRecord in a list requires collecting state from the\n DOM that has been since been mutated further. (e.g. if an attribute is changed twice in a row the first event cannot\n discover the intermediate value of the attribute as it can only query the latest DOM state). Whilst this simple case\n is solvable by walking backwards through the list of MutationRecords and using `oldValue` there are cases where adding\n child elements with the correct attributes is not possible when handling intermediate diffs.\n */\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const MutationRecordExports = require(\"jsdom/lib/jsdom/living/generated/MutationRecord\");\n const originalCreateImpl = MutationRecordExports.createImpl;\n // This overwrites the function property on the exports that mutation-observers.js uses to create MutationRecords.\n MutationRecordExports.createImpl = (...args: any[]) => {\n for (const callback of monkeyPatchedMutationRecordCallbacks) {\n callback();\n }\n return originalCreateImpl.call(null, ...args);\n };\n}\nlet monkeyPatchInstalled = false;\n\nexport const JSDOMRunnerFactory: DOMRunnerFactory = (\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (mutationList: DOMRunnerMessage) => void,\n): DOMRunnerInterface => {\n return new JSDOMRunner(htmlPath, htmlContents, params, callback);\n};\n\n// This is used to stop JSDOM trying to load resources\nclass RejectionResourceLoader extends ResourceLoader {\n public fetch(url: string): AbortablePromise<Buffer> | null {\n console.error(\"RejectionResourceLoader.fetch\", url);\n return null;\n }\n}\n\nexport class JSDOMRunner {\n private monkeyPatchMutationRecordCallback: () => void;\n\n public domWindow: DOMWindow | null = null;\n private jsdom: JSDOM;\n\n private callback: (message: DOMRunnerMessage) => void;\n private mutationObserver: MutationObserver | null = null;\n private htmlPath: string;\n\n private documentStartTime = Date.now();\n\n private isLoaded = false;\n private logBuffer: LogMessage[] = [];\n\n constructor(\n htmlPath: string,\n htmlContents: string,\n params: object,\n callback: (domRunnerMessage: DOMRunnerMessage) => void,\n ) {\n this.htmlPath = htmlPath;\n this.callback = callback;\n\n if (!monkeyPatchInstalled) {\n installMutationObserverMonkeyPatch();\n monkeyPatchInstalled = true;\n }\n\n this.monkeyPatchMutationRecordCallback = () => {\n /*\n This is called before every creation of a MutationRecord so that it can be used to process an existing record to\n avoid handling multiple MutationRecords at a time (see comment at the top of this file).\n */\n const records = this.mutationObserver?.takeRecords();\n if (records && records.length > 1) {\n throw new Error(\n \"The monkey patching should have prevented more than one record being handled at a time\",\n );\n } else if (records && records.length > 0) {\n this.callback({\n mutationList: records,\n });\n }\n };\n monkeyPatchedMutationRecordCallbacks.add(this.monkeyPatchMutationRecordCallback);\n\n this.jsdom = new JSDOM(htmlContents, {\n runScripts: \"dangerously\",\n resources: new RejectionResourceLoader(),\n url: this.htmlPath,\n virtualConsole: this.createVirtualConsole(),\n beforeParse: (window) => {\n this.domWindow = window;\n\n this.domWindow.fetch = nodeFetchFn as unknown as typeof fetch;\n this.domWindow.Headers = nodeFetch.Headers as unknown as typeof Headers;\n this.domWindow.Request = nodeFetch.Request as unknown as typeof Request;\n this.domWindow.Response = nodeFetch.Response as unknown as typeof Response;\n\n // This is a polyfill for https://developer.mozilla.org/en-US/docs/Web/API/Document/timeline\n const timeline = {};\n Object.defineProperty(timeline, \"currentTime\", {\n get: () => {\n return this.getDocumentTime();\n },\n });\n (window.document as any).timeline = timeline;\n\n // JSON stringify and parse to avoid potential reference leaks from the params object\n window.params = JSON.parse(JSON.stringify(params));\n\n this.mutationObserver = new window.MutationObserver((mutationList) => {\n this.callback({\n mutationList,\n });\n });\n\n window.addEventListener(\"load\", () => {\n this.mutationObserver?.observe(window.document, {\n attributes: true,\n childList: true,\n subtree: true,\n characterData: true,\n });\n\n this.isLoaded = true;\n\n this.callback({\n loaded: true,\n });\n\n this.flushLogBuffer();\n });\n },\n });\n }\n\n private flushLogBuffer() {\n for (const logMessage of this.logBuffer) {\n this.callback({\n logMessage,\n });\n }\n\n this.logBuffer = [];\n }\n\n private log(message: LogMessage) {\n if (!this.isLoaded) {\n this.logBuffer.push(message);\n return;\n }\n\n this.callback({\n logMessage: message,\n });\n }\n\n public getDocument(): Document {\n if (!this.domWindow) {\n throw new Error(ErrDOMWindowNotInitialized);\n }\n\n return this.domWindow.document;\n }\n\n public getWindow(): any {\n return this.domWindow;\n }\n\n public dispose() {\n const records = this.mutationObserver?.takeRecords();\n this.callback({\n mutationList: records,\n });\n monkeyPatchedMutationRecordCallbacks.delete(this.monkeyPatchMutationRecordCallback);\n this.mutationObserver?.disconnect();\n this.jsdom.window.close();\n }\n\n public getDocumentTime() {\n return Date.now() - this.documentStartTime;\n }\n\n public dispatchRemoteEventFromConnectionId(\n connectionId: number,\n domNode: Element,\n remoteEvent: RemoteEvent,\n ) {\n if (!this.domWindow) {\n throw new Error(ErrDOMWindowNotInitialized);\n }\n\n const bubbles = remoteEvent.bubbles || false;\n const remoteEventObject = new this.domWindow.CustomEvent(remoteEvent.name, {\n bubbles,\n detail: { ...remoteEvent.params, connectionId },\n });\n\n const eventTypeLowerCase = remoteEvent.name.toLowerCase();\n\n // TODO - check if there are other events that automatically wire up similarly to click->onclick and avoid those too\n if (eventTypeLowerCase !== \"click\") {\n const handlerAttributeName = \"on\" + eventTypeLowerCase;\n const handlerAttributeValue = domNode.getAttribute(handlerAttributeName);\n if (handlerAttributeValue) {\n // This event is defined as an HTML event attribute.\n const script = handlerAttributeValue;\n const vmContext = this.jsdom.getInternalVMContext();\n try {\n const invoke = vm.runInContext(`(function(event){ ${script} })`, vmContext);\n Reflect.apply(invoke, domNode, [remoteEventObject]);\n } catch (e) {\n console.error(\"Error running event handler:\", e);\n }\n }\n }\n\n // Dispatch the event via JavaScript.\n domNode.dispatchEvent(remoteEventObject);\n }\n\n private createVirtualConsole(): VirtualConsole {\n const virtualConsole = new VirtualConsole();\n virtualConsole.on(\"jsdomError\", (...args) => {\n this.log({\n level: \"system\",\n content: args,\n });\n });\n virtualConsole.on(\"error\", (...args) => {\n this.log({\n level: \"error\",\n content: args,\n });\n });\n virtualConsole.on(\"warn\", (...args) => {\n this.log({\n level: \"warn\",\n content: args,\n });\n });\n virtualConsole.on(\"log\", (...args) => {\n this.log({\n level: \"log\",\n content: args,\n });\n });\n virtualConsole.on(\"info\", (...args) => {\n this.log({\n level: \"info\",\n content: args,\n });\n });\n return virtualConsole;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["nodeFetchFn", "vm"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mml-io/observable-dom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"main": "./build/index.js",
|
|
5
5
|
"types": "./build/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"lint-fix": "eslint \"./src/**/*.{js,jsx,ts,tsx}\" --fix"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@mml-io/observable-dom-common": "^0.
|
|
19
|
+
"@mml-io/observable-dom-common": "^0.2.0",
|
|
20
20
|
"jsdom": "22.1.0",
|
|
21
21
|
"node-fetch": "2.6.11"
|
|
22
22
|
},
|
package/src/JSDOMRunner.ts
CHANGED
|
@@ -5,9 +5,9 @@ import { AbortablePromise, DOMWindow, JSDOM, ResourceLoader, VirtualConsole } fr
|
|
|
5
5
|
import * as nodeFetch from "node-fetch";
|
|
6
6
|
import nodeFetchFn from "node-fetch";
|
|
7
7
|
|
|
8
|
-
import { DOMRunnerFactory, DOMRunnerInterface, DOMRunnerMessage } from "./
|
|
8
|
+
import { DOMRunnerFactory, DOMRunnerInterface, DOMRunnerMessage } from "./ObservableDOM";
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const ErrDOMWindowNotInitialized = "DOMWindow not initialized";
|
|
11
11
|
|
|
12
12
|
// TODO - remove this monkeypatching if it's possible to avoid the race conditions in naive MutationObserver usage
|
|
13
13
|
const monkeyPatchedMutationRecordCallbacks = new Set<() => void>();
|
|
@@ -57,15 +57,12 @@ class RejectionResourceLoader extends ResourceLoader {
|
|
|
57
57
|
export class JSDOMRunner {
|
|
58
58
|
private monkeyPatchMutationRecordCallback: () => void;
|
|
59
59
|
|
|
60
|
-
private ipcWebsockets = new Set<WebSocket>();
|
|
61
|
-
|
|
62
60
|
public domWindow: DOMWindow | null = null;
|
|
63
|
-
private
|
|
61
|
+
private jsdom: JSDOM;
|
|
64
62
|
|
|
65
63
|
private callback: (message: DOMRunnerMessage) => void;
|
|
66
64
|
private mutationObserver: MutationObserver | null = null;
|
|
67
65
|
private htmlPath: string;
|
|
68
|
-
private ipcListeners = new Set<(event: any) => void>();
|
|
69
66
|
|
|
70
67
|
private documentStartTime = Date.now();
|
|
71
68
|
|
|
@@ -104,7 +101,7 @@ export class JSDOMRunner {
|
|
|
104
101
|
};
|
|
105
102
|
monkeyPatchedMutationRecordCallbacks.add(this.monkeyPatchMutationRecordCallback);
|
|
106
103
|
|
|
107
|
-
this.
|
|
104
|
+
this.jsdom = new JSDOM(htmlContents, {
|
|
108
105
|
runScripts: "dangerously",
|
|
109
106
|
resources: new RejectionResourceLoader(),
|
|
110
107
|
url: this.htmlPath,
|
|
@@ -129,24 +126,6 @@ export class JSDOMRunner {
|
|
|
129
126
|
// JSON stringify and parse to avoid potential reference leaks from the params object
|
|
130
127
|
window.params = JSON.parse(JSON.stringify(params));
|
|
131
128
|
|
|
132
|
-
const oldDocumentAddEventListener = window.document.addEventListener;
|
|
133
|
-
window.document.addEventListener = (...args: [string, EventListener, ...any[]]) => {
|
|
134
|
-
const [eventName, listener] = args;
|
|
135
|
-
if (eventName === "ipc") {
|
|
136
|
-
this.ipcListeners.add(listener);
|
|
137
|
-
}
|
|
138
|
-
return oldDocumentAddEventListener.call(window.document, ...args);
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const oldDocumentRemoveEventListener = window.document.addEventListener;
|
|
142
|
-
window.document.removeEventListener = (...args: [string, EventListener, ...any[]]) => {
|
|
143
|
-
const [eventName, listener] = args;
|
|
144
|
-
if (eventName === "ipc") {
|
|
145
|
-
this.ipcListeners.delete(listener);
|
|
146
|
-
}
|
|
147
|
-
return oldDocumentRemoveEventListener.call(window.document, ...args);
|
|
148
|
-
};
|
|
149
|
-
|
|
150
129
|
this.mutationObserver = new window.MutationObserver((mutationList) => {
|
|
151
130
|
this.callback({
|
|
152
131
|
mutationList,
|
|
@@ -196,7 +175,7 @@ export class JSDOMRunner {
|
|
|
196
175
|
|
|
197
176
|
public getDocument(): Document {
|
|
198
177
|
if (!this.domWindow) {
|
|
199
|
-
throw new Error(
|
|
178
|
+
throw new Error(ErrDOMWindowNotInitialized);
|
|
200
179
|
}
|
|
201
180
|
|
|
202
181
|
return this.domWindow.document;
|
|
@@ -206,30 +185,6 @@ export class JSDOMRunner {
|
|
|
206
185
|
return this.domWindow;
|
|
207
186
|
}
|
|
208
187
|
|
|
209
|
-
public addIPCWebsocket(webSocket: WebSocket) {
|
|
210
|
-
if (!this.domWindow) {
|
|
211
|
-
throw new Error(ErrDomWindowNotInitialized);
|
|
212
|
-
}
|
|
213
|
-
if (this.ipcListeners.size === 0) {
|
|
214
|
-
console.error("ipc requested, but no ipc listeners registered on document:", this.htmlPath);
|
|
215
|
-
webSocket.close();
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
this.ipcWebsockets.add(webSocket);
|
|
219
|
-
const event = new this.domWindow.CustomEvent("ipc", {
|
|
220
|
-
detail: {
|
|
221
|
-
webSocket,
|
|
222
|
-
},
|
|
223
|
-
});
|
|
224
|
-
for (const ipcListener of this.ipcListeners) {
|
|
225
|
-
ipcListener(event);
|
|
226
|
-
}
|
|
227
|
-
webSocket.addEventListener("close", () => {
|
|
228
|
-
this.ipcWebsockets.delete(webSocket);
|
|
229
|
-
});
|
|
230
|
-
return;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
188
|
public dispose() {
|
|
234
189
|
const records = this.mutationObserver?.takeRecords();
|
|
235
190
|
this.callback({
|
|
@@ -237,7 +192,7 @@ export class JSDOMRunner {
|
|
|
237
192
|
});
|
|
238
193
|
monkeyPatchedMutationRecordCallbacks.delete(this.monkeyPatchMutationRecordCallback);
|
|
239
194
|
this.mutationObserver?.disconnect();
|
|
240
|
-
this.
|
|
195
|
+
this.jsdom.window.close();
|
|
241
196
|
}
|
|
242
197
|
|
|
243
198
|
public getDocumentTime() {
|
|
@@ -250,7 +205,7 @@ export class JSDOMRunner {
|
|
|
250
205
|
remoteEvent: RemoteEvent,
|
|
251
206
|
) {
|
|
252
207
|
if (!this.domWindow) {
|
|
253
|
-
throw new Error(
|
|
208
|
+
throw new Error(ErrDOMWindowNotInitialized);
|
|
254
209
|
}
|
|
255
210
|
|
|
256
211
|
const bubbles = remoteEvent.bubbles || false;
|
|
@@ -268,7 +223,7 @@ export class JSDOMRunner {
|
|
|
268
223
|
if (handlerAttributeValue) {
|
|
269
224
|
// This event is defined as an HTML event attribute.
|
|
270
225
|
const script = handlerAttributeValue;
|
|
271
|
-
const vmContext = this.
|
|
226
|
+
const vmContext = this.jsdom.getInternalVMContext();
|
|
272
227
|
try {
|
|
273
228
|
const invoke = vm.runInContext(`(function(event){ ${script} })`, vmContext);
|
|
274
229
|
Reflect.apply(invoke, domNode, [remoteEventObject]);
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
LogMessage,
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
ObservableDOMInterface,
|
|
4
|
+
ObservableDOMMessage,
|
|
5
5
|
ObservableDOMParameters,
|
|
6
6
|
RemoteEvent,
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
StaticVirtualDOMElement,
|
|
8
|
+
StaticVirtualDOMMutationIdsRecord,
|
|
9
9
|
} from "@mml-io/observable-dom-common";
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { virtualDOMElementToStatic } from "./utils";
|
|
12
12
|
|
|
13
13
|
export type DOMRunnerMessage = {
|
|
14
14
|
loaded?: boolean;
|
|
@@ -24,7 +24,6 @@ export type DOMRunnerInterface = {
|
|
|
24
24
|
HTMLScriptElement: typeof HTMLScriptElement;
|
|
25
25
|
Comment: typeof Comment;
|
|
26
26
|
}; // TODO - Define this without using JSDOM types
|
|
27
|
-
addIPCWebsocket(webSocket: WebSocket): void;
|
|
28
27
|
dispatchRemoteEventFromConnectionId(
|
|
29
28
|
connectionId: number,
|
|
30
29
|
realElement: Element,
|
|
@@ -41,18 +40,18 @@ export type DOMRunnerFactory = (
|
|
|
41
40
|
callback: (domRunnerMessage: DOMRunnerMessage) => void,
|
|
42
41
|
) => DOMRunnerInterface;
|
|
43
42
|
|
|
44
|
-
export type
|
|
43
|
+
export type LiveVirtualDOMElement = Omit<StaticVirtualDOMElement, "childNodes"> & {
|
|
45
44
|
realElement: Element | Text;
|
|
46
|
-
childNodes: Array<
|
|
47
|
-
parent:
|
|
45
|
+
childNodes: Array<LiveVirtualDOMElement>;
|
|
46
|
+
parent: LiveVirtualDOMElement | null;
|
|
48
47
|
};
|
|
49
48
|
|
|
50
|
-
export class
|
|
51
|
-
private nodeToNodeId = new Map<
|
|
52
|
-
private nodeIdToNode = new Map<number,
|
|
53
|
-
private realElementToVirtualElement = new Map<Element | Text,
|
|
49
|
+
export class ObservableDOM implements ObservableDOMInterface {
|
|
50
|
+
private nodeToNodeId = new Map<LiveVirtualDOMElement, number>();
|
|
51
|
+
private nodeIdToNode = new Map<number, LiveVirtualDOMElement>();
|
|
52
|
+
private realElementToVirtualElement = new Map<Element | Text, LiveVirtualDOMElement>();
|
|
54
53
|
private ignoreTextNodes = true;
|
|
55
|
-
private callback: (message:
|
|
54
|
+
private callback: (message: ObservableDOMMessage, observableDOM: ObservableDOMInterface) => void;
|
|
56
55
|
private nextNodeId = 1;
|
|
57
56
|
private htmlPath: string;
|
|
58
57
|
private domRunner: DOMRunnerInterface;
|
|
@@ -61,7 +60,7 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
61
60
|
|
|
62
61
|
constructor(
|
|
63
62
|
observableDOMParameters: ObservableDOMParameters,
|
|
64
|
-
callback: (message:
|
|
63
|
+
callback: (message: ObservableDOMMessage, observableDOM: ObservableDOMInterface) => void,
|
|
65
64
|
runnerFactory: DOMRunnerFactory,
|
|
66
65
|
) {
|
|
67
66
|
this.htmlPath = observableDOMParameters.htmlPath;
|
|
@@ -69,9 +68,12 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
69
68
|
this.callback = callback;
|
|
70
69
|
|
|
71
70
|
this.documentTimeIntervalTimer = setInterval(() => {
|
|
72
|
-
this.callback(
|
|
73
|
-
|
|
74
|
-
|
|
71
|
+
this.callback(
|
|
72
|
+
{
|
|
73
|
+
documentTime: this.getDocumentTime(),
|
|
74
|
+
},
|
|
75
|
+
this,
|
|
76
|
+
);
|
|
75
77
|
}, observableDOMParameters.pingIntervalMilliseconds || 5000);
|
|
76
78
|
|
|
77
79
|
this.domRunner = runnerFactory(
|
|
@@ -80,37 +82,39 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
80
82
|
observableDOMParameters.params,
|
|
81
83
|
(domRunnerMessage: DOMRunnerMessage) => {
|
|
82
84
|
if (domRunnerMessage.loaded) {
|
|
83
|
-
this.
|
|
85
|
+
this.createVirtualDOMElementWithChildren(
|
|
84
86
|
this.domRunner.getDocument() as unknown as Element,
|
|
85
87
|
null,
|
|
86
88
|
);
|
|
87
89
|
|
|
88
|
-
const snapshot =
|
|
89
|
-
this.
|
|
90
|
+
const snapshot = virtualDOMElementToStatic(
|
|
91
|
+
this.getVirtualDOMElementForRealElementOrThrow(
|
|
90
92
|
this.domRunner.getDocument() as unknown as Element,
|
|
91
93
|
),
|
|
92
94
|
);
|
|
93
95
|
|
|
94
|
-
this.callback(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
this.callback(
|
|
97
|
+
{
|
|
98
|
+
snapshot,
|
|
99
|
+
documentTime: this.getDocumentTime(),
|
|
100
|
+
},
|
|
101
|
+
this,
|
|
102
|
+
);
|
|
98
103
|
} else if (domRunnerMessage.mutationList) {
|
|
99
104
|
this.processModificationList(domRunnerMessage.mutationList);
|
|
100
105
|
} else if (domRunnerMessage.logMessage) {
|
|
101
|
-
this.callback(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
106
|
+
this.callback(
|
|
107
|
+
{
|
|
108
|
+
logMessage: domRunnerMessage.logMessage,
|
|
109
|
+
documentTime: this.getDocumentTime(),
|
|
110
|
+
},
|
|
111
|
+
this,
|
|
112
|
+
);
|
|
105
113
|
}
|
|
106
114
|
},
|
|
107
115
|
);
|
|
108
116
|
}
|
|
109
117
|
|
|
110
|
-
public addIPCWebsocket(webSocket: WebSocket) {
|
|
111
|
-
return this.domRunner.addIPCWebsocket(webSocket);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
118
|
public addConnectedUserId(connectionId: number): void {
|
|
115
119
|
this.domRunner.getWindow().dispatchEvent(
|
|
116
120
|
new (this.domRunner.getWindow().CustomEvent)("connected", {
|
|
@@ -129,8 +133,8 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
129
133
|
|
|
130
134
|
private processModificationList(mutationList: Array<MutationRecord>): void {
|
|
131
135
|
const documentEl = this.domRunner.getDocument() as unknown as Element;
|
|
132
|
-
const
|
|
133
|
-
if (!
|
|
136
|
+
const documentVirtualDOMElement = this.realElementToVirtualElement.get(documentEl);
|
|
137
|
+
if (!documentVirtualDOMElement) {
|
|
134
138
|
throw new Error(`document not created in processModificationList`);
|
|
135
139
|
}
|
|
136
140
|
|
|
@@ -164,18 +168,18 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
164
168
|
const firstNonIgnoredPreviousSibling = mutation.previousSibling
|
|
165
169
|
? this.getFirstNonIgnoredPreviousSibling(mutation.previousSibling as Element | Text)
|
|
166
170
|
: null;
|
|
167
|
-
const targetElement = this.
|
|
171
|
+
const targetElement = this.getVirtualDOMElementForRealElementOrThrow(
|
|
168
172
|
mutation.target as Element | Text,
|
|
169
173
|
);
|
|
170
|
-
const addedNodes: Array<
|
|
174
|
+
const addedNodes: Array<StaticVirtualDOMElement> = [];
|
|
171
175
|
for (const node of mutation.addedNodes) {
|
|
172
176
|
if (this.isIgnoredElement(node as Element | Text)) {
|
|
173
177
|
continue;
|
|
174
178
|
}
|
|
175
|
-
const
|
|
179
|
+
const virtualDOMElement = this.getVirtualDOMElementForRealElementOrThrow(
|
|
176
180
|
node as Element | Text,
|
|
177
181
|
);
|
|
178
|
-
addedNodes.push(
|
|
182
|
+
addedNodes.push(virtualDOMElementToStatic(virtualDOMElement));
|
|
179
183
|
}
|
|
180
184
|
|
|
181
185
|
const removedNodeIds: Array<number> = [];
|
|
@@ -183,20 +187,20 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
183
187
|
if (this.isIgnoredElement(node as Element | Text)) {
|
|
184
188
|
continue;
|
|
185
189
|
}
|
|
186
|
-
const
|
|
190
|
+
const virtualDOMElement = this.getVirtualDOMElementForRealElementOrThrow(
|
|
187
191
|
node as Element | Text,
|
|
188
192
|
);
|
|
189
|
-
removedNodeIds.push(
|
|
193
|
+
removedNodeIds.push(virtualDOMElement.nodeId);
|
|
190
194
|
}
|
|
191
195
|
|
|
192
|
-
const mutationRecord:
|
|
196
|
+
const mutationRecord: StaticVirtualDOMMutationIdsRecord = {
|
|
193
197
|
type: mutation.type,
|
|
194
198
|
targetId: targetElement.nodeId,
|
|
195
199
|
addedNodes,
|
|
196
200
|
removedNodeIds,
|
|
197
201
|
previousSiblingId:
|
|
198
202
|
firstNonIgnoredPreviousSibling !== null
|
|
199
|
-
? this.
|
|
203
|
+
? this.getVirtualDOMElementForRealElementOrThrow(firstNonIgnoredPreviousSibling).nodeId
|
|
200
204
|
: null,
|
|
201
205
|
attribute: mutation.attributeName
|
|
202
206
|
? {
|
|
@@ -206,10 +210,13 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
206
210
|
: null,
|
|
207
211
|
};
|
|
208
212
|
|
|
209
|
-
this.callback(
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
+
this.callback(
|
|
214
|
+
{
|
|
215
|
+
mutation: mutationRecord,
|
|
216
|
+
documentTime: this.getDocumentTime(),
|
|
217
|
+
},
|
|
218
|
+
this,
|
|
219
|
+
);
|
|
213
220
|
|
|
214
221
|
this.removeKnownNodesInMutation(mutation);
|
|
215
222
|
}
|
|
@@ -217,8 +224,8 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
217
224
|
|
|
218
225
|
private addKnownNodesInMutation(mutation: MutationRecord): void {
|
|
219
226
|
const targetNode = mutation.target as Element | Text;
|
|
220
|
-
const
|
|
221
|
-
if (!
|
|
227
|
+
const virtualDOMElement = this.realElementToVirtualElement.get(targetNode);
|
|
228
|
+
if (!virtualDOMElement) {
|
|
222
229
|
throw new Error(
|
|
223
230
|
"Unknown node in addKnownNodesInMutation:" + targetNode + "," + mutation.type,
|
|
224
231
|
);
|
|
@@ -236,7 +243,7 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
236
243
|
if (!previousSiblingElement) {
|
|
237
244
|
throw new Error("Unknown previous sibling");
|
|
238
245
|
}
|
|
239
|
-
index =
|
|
246
|
+
index = virtualDOMElement.childNodes.indexOf(previousSiblingElement);
|
|
240
247
|
if (index === -1) {
|
|
241
248
|
throw new Error("Previous sibling is not currently a child of the parent element");
|
|
242
249
|
}
|
|
@@ -244,13 +251,13 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
244
251
|
}
|
|
245
252
|
mutation.addedNodes.forEach((node: Node) => {
|
|
246
253
|
const asElementOrText = node as Element | Text;
|
|
247
|
-
const
|
|
254
|
+
const childVirtualDOMElement = this.createVirtualDOMElementWithChildren(
|
|
248
255
|
asElementOrText,
|
|
249
|
-
|
|
256
|
+
virtualDOMElement,
|
|
250
257
|
);
|
|
251
|
-
if (
|
|
252
|
-
if (
|
|
253
|
-
|
|
258
|
+
if (childVirtualDOMElement) {
|
|
259
|
+
if (virtualDOMElement.childNodes.indexOf(childVirtualDOMElement) === -1) {
|
|
260
|
+
virtualDOMElement.childNodes.splice(index, 0, childVirtualDOMElement);
|
|
254
261
|
index++;
|
|
255
262
|
}
|
|
256
263
|
}
|
|
@@ -263,19 +270,19 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
263
270
|
}
|
|
264
271
|
const attributeValue = (targetNode as Element).getAttribute(attributeName);
|
|
265
272
|
if (attributeValue === null) {
|
|
266
|
-
delete
|
|
273
|
+
delete virtualDOMElement.attributes[attributeName];
|
|
267
274
|
} else {
|
|
268
|
-
|
|
275
|
+
virtualDOMElement.attributes[attributeName] = attributeValue;
|
|
269
276
|
}
|
|
270
277
|
} else if (mutation.type === "characterData") {
|
|
271
|
-
|
|
278
|
+
virtualDOMElement.textContent = targetNode.textContent ? targetNode.textContent : undefined;
|
|
272
279
|
}
|
|
273
280
|
}
|
|
274
281
|
|
|
275
282
|
private removeKnownNodesInMutation(mutation: MutationRecord): void {
|
|
276
283
|
const targetNode = mutation.target as Element | Text;
|
|
277
|
-
const
|
|
278
|
-
if (!
|
|
284
|
+
const virtualDOMElement = this.realElementToVirtualElement.get(targetNode);
|
|
285
|
+
if (!virtualDOMElement) {
|
|
279
286
|
throw new Error("Unknown node in mutation list:" + targetNode + ", " + mutation.type);
|
|
280
287
|
}
|
|
281
288
|
if (mutation.type === "childList") {
|
|
@@ -284,41 +291,41 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
284
291
|
if (this.isIgnoredElement(asElementOrText)) {
|
|
285
292
|
continue;
|
|
286
293
|
}
|
|
287
|
-
const
|
|
288
|
-
if (!
|
|
294
|
+
const childDOMElement = this.realElementToVirtualElement.get(asElementOrText);
|
|
295
|
+
if (!childDOMElement) {
|
|
289
296
|
console.warn(this.htmlPath, "Unknown node in removeKnownNodesInMutation");
|
|
290
297
|
continue;
|
|
291
298
|
} else {
|
|
292
|
-
this.
|
|
293
|
-
const index =
|
|
294
|
-
|
|
299
|
+
this.removeVirtualDOMElement(childDOMElement);
|
|
300
|
+
const index = virtualDOMElement.childNodes.indexOf(childDOMElement);
|
|
301
|
+
virtualDOMElement.childNodes.splice(index, 1);
|
|
295
302
|
}
|
|
296
303
|
}
|
|
297
304
|
return;
|
|
298
305
|
}
|
|
299
306
|
}
|
|
300
307
|
|
|
301
|
-
private
|
|
302
|
-
this.nodeIdToNode.delete(
|
|
303
|
-
this.nodeToNodeId.delete(
|
|
304
|
-
this.realElementToVirtualElement.delete(
|
|
305
|
-
for (const child of
|
|
306
|
-
this.
|
|
308
|
+
private removeVirtualDOMElement(virtualDOMElement: LiveVirtualDOMElement): void {
|
|
309
|
+
this.nodeIdToNode.delete(virtualDOMElement.nodeId);
|
|
310
|
+
this.nodeToNodeId.delete(virtualDOMElement);
|
|
311
|
+
this.realElementToVirtualElement.delete(virtualDOMElement.realElement);
|
|
312
|
+
for (const child of virtualDOMElement.childNodes) {
|
|
313
|
+
this.removeVirtualDOMElement(child);
|
|
307
314
|
}
|
|
308
315
|
}
|
|
309
316
|
|
|
310
|
-
private
|
|
317
|
+
private createVirtualDOMElementWithChildren(
|
|
311
318
|
node: Element | Text,
|
|
312
|
-
parent:
|
|
313
|
-
):
|
|
314
|
-
const virtualElement = this.
|
|
319
|
+
parent: LiveVirtualDOMElement | null,
|
|
320
|
+
): LiveVirtualDOMElement | null {
|
|
321
|
+
const virtualElement = this.createVirtualDOMElement(node, parent);
|
|
315
322
|
if (!virtualElement) {
|
|
316
323
|
return null;
|
|
317
324
|
}
|
|
318
325
|
if ((node as Element).childNodes) {
|
|
319
326
|
for (let i = 0; i < (node as Element).childNodes.length; i++) {
|
|
320
327
|
const child = (node as Element).childNodes[i];
|
|
321
|
-
const childVirtualElement = this.
|
|
328
|
+
const childVirtualElement = this.createVirtualDOMElementWithChildren(
|
|
322
329
|
child as Element | Text,
|
|
323
330
|
virtualElement,
|
|
324
331
|
);
|
|
@@ -331,10 +338,10 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
331
338
|
return virtualElement;
|
|
332
339
|
}
|
|
333
340
|
|
|
334
|
-
private
|
|
341
|
+
private createVirtualDOMElement(
|
|
335
342
|
node: Element | Text,
|
|
336
|
-
parent:
|
|
337
|
-
):
|
|
343
|
+
parent: LiveVirtualDOMElement | null,
|
|
344
|
+
): LiveVirtualDOMElement | null {
|
|
338
345
|
if (this.isIgnoredElement(node)) {
|
|
339
346
|
return null;
|
|
340
347
|
}
|
|
@@ -361,7 +368,7 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
361
368
|
}
|
|
362
369
|
|
|
363
370
|
const nodeId = this.nextNodeId++;
|
|
364
|
-
const virtualElement:
|
|
371
|
+
const virtualElement: LiveVirtualDOMElement = {
|
|
365
372
|
nodeId,
|
|
366
373
|
tag: node.nodeName,
|
|
367
374
|
attributes,
|
|
@@ -392,9 +399,9 @@ export class ObservableDom implements ObservableDomInterface {
|
|
|
392
399
|
return null;
|
|
393
400
|
}
|
|
394
401
|
|
|
395
|
-
private
|
|
402
|
+
private getVirtualDOMElementForRealElementOrThrow(
|
|
396
403
|
realElement: Element | Text,
|
|
397
|
-
):
|
|
404
|
+
): LiveVirtualDOMElement {
|
|
398
405
|
const virtualElement = this.realElementToVirtualElement.get(realElement);
|
|
399
406
|
if (!virtualElement) {
|
|
400
407
|
throw new Error(`Virtual element not found for real element`);
|
package/src/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "./
|
|
1
|
+
export * from "./ObservableDOM";
|
|
2
2
|
export * from "./JSDOMRunner";
|
package/src/utils.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StaticVirtualDOMElement } from "@mml-io/observable-dom-common";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { LiveVirtualDOMElement } from "./ObservableDOM";
|
|
4
4
|
|
|
5
|
-
export function
|
|
5
|
+
export function virtualDOMElementToStatic(el: LiveVirtualDOMElement): StaticVirtualDOMElement {
|
|
6
6
|
return {
|
|
7
7
|
nodeId: el.nodeId,
|
|
8
8
|
tag: el.tag,
|
|
9
9
|
attributes: el.attributes,
|
|
10
|
-
childNodes: el.childNodes.map((child) =>
|
|
10
|
+
childNodes: el.childNodes.map((child) => virtualDOMElementToStatic(child)),
|
|
11
11
|
textContent: el.textContent,
|
|
12
12
|
};
|
|
13
13
|
}
|