xpe 1.0.12 → 1.0.14

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.
@@ -415,11 +415,11 @@ export function recoverShadowRootEventListeners(iframeWindow) {
415
415
  rawRemoveEventListener.call(nextElement, listener.type, listener.callback, listener.options);
416
416
  rawAddEventListener.call(nextElement, listener.type, listener.callback, listener.options);
417
417
  });
418
- recoverFrameworkEventInvokers(nextElement);
418
+ recoverFrameworkEventInvokers(nextElement, elementListenerList);
419
419
  nextElement = walker.nextNode();
420
420
  }
421
421
  }
422
- function recoverFrameworkEventInvokers(element) {
422
+ function recoverFrameworkEventInvokers(element, cachedListeners) {
423
423
  if (element.nodeType !== Node.ELEMENT_NODE)
424
424
  return;
425
425
  Object.getOwnPropertySymbols(element)
@@ -435,6 +435,11 @@ function recoverFrameworkEventInvokers(element) {
435
435
  const eventType = toDomEventType(key);
436
436
  if (!eventType)
437
437
  return;
438
+ // A tracked Vue invoker is already restored through its realm-safe
439
+ // callback. Adding the raw invoker as well executes one click twice.
440
+ if (cachedListeners?.some((listener) => listener.type === eventType && listener.handler === invoker)) {
441
+ return;
442
+ }
438
443
  rawRemoveEventListener.call(element, eventType, invoker);
439
444
  rawAddEventListener.call(element, eventType, invoker);
440
445
  });
@@ -669,6 +674,10 @@ function patchNodeEffect(iframeWindow) {
669
674
  const rawAppendChild = iframeWindow.Node.prototype.appendChild;
670
675
  const rawInsertBefore = iframeWindow.Node.prototype.insertBefore;
671
676
  const rawRemoveChild = iframeWindow.Node.prototype.removeChild;
677
+ const hostAppendChild = window.Node.prototype.appendChild;
678
+ const hostInsertBefore = window.Node.prototype.insertBefore;
679
+ const hostRemoveChild = window.Node.prototype.removeChild;
680
+ const nodeOperation = (node, iframeOperation, hostOperation) => (window.Node.prototype.isPrototypeOf(node) ? hostOperation : iframeOperation);
672
681
  iframeWindow.Node.prototype.getRootNode = function (options) {
673
682
  const rootNode = rawGetRootNode.call(this, options);
674
683
  if (rootNode === sandbox.shadowRoot)
@@ -677,13 +686,15 @@ function patchNodeEffect(iframeWindow) {
677
686
  return rootNode;
678
687
  };
679
688
  iframeWindow.Node.prototype.appendChild = function (node) {
680
- const res = rawAppendChild.call(this, node);
689
+ const operation = nodeOperation(node, rawAppendChild, hostAppendChild);
690
+ const res = operation.call(this, node);
681
691
  patchElementEffect(node, iframeWindow);
682
692
  recoverEventListeners(this, iframeWindow);
683
693
  return res;
684
694
  };
685
695
  iframeWindow.Node.prototype.insertBefore = function (node, child) {
686
- const res = rawInsertBefore.call(this, node, child);
696
+ const operation = nodeOperation(node, rawInsertBefore, hostInsertBefore);
697
+ const res = operation.call(this, node, child);
687
698
  patchElementEffect(node, iframeWindow);
688
699
  recoverEventListeners(this, iframeWindow);
689
700
  return res;
@@ -691,7 +702,8 @@ function patchNodeEffect(iframeWindow) {
691
702
  iframeWindow.Node.prototype.removeChild = function (node) {
692
703
  let res;
693
704
  try {
694
- res = rawRemoveChild.call(this, node);
705
+ const operation = nodeOperation(node, rawRemoveChild, hostRemoveChild);
706
+ res = operation.call(this, node);
695
707
  }
696
708
  catch (e) {
697
709
  console.warn(`Failed to removeChild: ${node.nodeName.toLowerCase()} is not a child of ${this.nodeName.toLowerCase()}, try again with parentNode attribute. `);
@@ -76,7 +76,14 @@ export function proxyGenerator(iframe, urlElement, mainHostPath, appHostPath) {
76
76
  return new Proxy(document[propKey], {
77
77
  apply(_createElement, _ctx, args) {
78
78
  const rawCreateMethod = propKey === "createElement" ? rawCreateElement : rawCreateTextNode;
79
- const element = rawCreateMethod.apply(iframeDocument, args);
79
+ // Vue compiles static template blocks through one shared
80
+ // HTMLTemplateElement. A template created in the execution iframe
81
+ // exposes a fragment from that realm, which native host DOM
82
+ // insertion rejects. Keep templates and their content fragment in
83
+ // the visible host realm.
84
+ const element = propKey === "createElement" && String(args[0]).toLowerCase() === "template"
85
+ ? window.document.createElement("template")
86
+ : rawCreateMethod.apply(iframeDocument, args);
80
87
  patchElementEffect(element, iframeWindow);
81
88
  return element;
82
89
  },
@@ -276,7 +283,9 @@ export function localGenerator(iframe, urlElement, mainHostPath, appHostPath) {
276
283
  const activeIframe = getActiveIframe(iframeRef);
277
284
  const activeWindow = getIframeWindow(activeIframe);
278
285
  const activeDocument = getIframeDocument(activeIframe);
279
- const element = getXpeRawDocumentCreateElement(activeWindow).apply(activeDocument, args);
286
+ const element = String(args[0]).toLowerCase() === "template"
287
+ ? window.document.createElement("template")
288
+ : getXpeRawDocumentCreateElement(activeWindow).apply(activeDocument, args);
280
289
  patchElementEffect(element, activeWindow);
281
290
  return element;
282
291
  };
@@ -41,7 +41,8 @@ export declare function getAbsolutePath(url: string, base: string, hash?: boolea
41
41
  export declare function getSyncUrl(id: string, prefix: {
42
42
  [key: string]: string;
43
43
  }): string;
44
- export declare const requestIdleCallback: ((callback: IdleRequestCallback, options?: IdleRequestOptions) => number) & typeof globalThis.requestIdleCallback;
44
+ /** Required startup work must progress even when a background tab gets no idle callback. */
45
+ export declare function requestIdleCallback(callback: IdleRequestCallback): number;
45
46
  export declare function getContainer(container: string | HTMLElement): HTMLElement;
46
47
  export declare function warn(msg: string, data?: any): void;
47
48
  export declare function error(msg: string, data?: any): void;
@@ -198,7 +198,32 @@ export function getSyncUrl(id, prefix) {
198
198
  return syncUrl;
199
199
  }
200
200
  // @ts-ignore
201
- export const requestIdleCallback = window.requestIdleCallback || ((cb) => setTimeout(cb, 1));
201
+ /** Required startup work must progress even when a background tab gets no idle callback. */
202
+ export function requestIdleCallback(callback) {
203
+ const target = window;
204
+ const native = target.requestIdleCallback;
205
+ if (!native) {
206
+ return target.setTimeout(() => callback({ didTimeout: true, timeRemaining: () => 0 }), 1);
207
+ }
208
+ let completed = false;
209
+ let idleId;
210
+ const fallback = target.setTimeout(() => {
211
+ if (completed)
212
+ return;
213
+ completed = true;
214
+ if (idleId !== undefined)
215
+ target.cancelIdleCallback?.(idleId);
216
+ callback({ didTimeout: true, timeRemaining: () => 0 });
217
+ }, 100);
218
+ idleId = native.call(target, (deadline) => {
219
+ if (completed)
220
+ return;
221
+ completed = true;
222
+ target.clearTimeout(fallback);
223
+ callback(deadline);
224
+ }, { timeout: 100 });
225
+ return idleId;
226
+ }
202
227
  export function getContainer(container) {
203
228
  return typeof container === "string" ? document.querySelector(container) : container;
204
229
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xpe",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "下一代微前端应用运行底座",
5
5
  "type": "module",
6
6
  "license": "MIT",