From a5a0273a11f98903b2e62b3baa92ace72adfe4b6 Mon Sep 17 00:00:00 2001 From: Kuitos Date: Fri, 26 Aug 2022 14:23:03 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix=20the=20counter=20issue=20wh?= =?UTF-8?q?ile=20multiple=20apps=20mounting=20concurrently=20(#2250)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sandbox/patchers/dynamicAppend/common.ts | 26 +++++++++++++++++++ .../patchers/dynamicAppend/forLooseSandbox.ts | 23 ++++++++-------- .../dynamicAppend/forStrictSandbox.ts | 17 +++++------- 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/src/sandbox/patchers/dynamicAppend/common.ts b/src/sandbox/patchers/dynamicAppend/common.ts index 5806133..e6c5bb4 100644 --- a/src/sandbox/patchers/dynamicAppend/common.ts +++ b/src/sandbox/patchers/dynamicAppend/common.ts @@ -67,6 +67,32 @@ export function isStyledComponentsLike(element: HTMLStyleElement) { ); } +const appsCounterMap = new Map(); +export function calcAppCount( + appName: string, + calcType: 'increase' | 'decrease', + status: 'bootstrapping' | 'mounting', +): void { + const appCount = appsCounterMap.get(appName) || { bootstrappingPatchCount: 0, mountingPatchCount: 0 }; + switch (calcType) { + case 'increase': + appCount[`${status}PatchCount`] += 1; + break; + case 'decrease': + // bootstrap patch just called once but its freer will be called multiple times + if (appCount[`${status}PatchCount`] > 0) { + appCount[`${status}PatchCount`] -= 1; + } + break; + } + appsCounterMap.set(appName, appCount); +} +export function isAllAppsUnmounted(): boolean { + return Array.from(appsCounterMap.entries()).every( + ([, { bootstrappingPatchCount: bpc, mountingPatchCount: mpc }]) => bpc === 0 && mpc === 0, + ); +} + function patchCustomEvent( e: CustomEvent, elementGetter: () => HTMLScriptElement | HTMLLinkElement | null, diff --git a/src/sandbox/patchers/dynamicAppend/forLooseSandbox.ts b/src/sandbox/patchers/dynamicAppend/forLooseSandbox.ts index 537181b..a1f36c8 100644 --- a/src/sandbox/patchers/dynamicAppend/forLooseSandbox.ts +++ b/src/sandbox/patchers/dynamicAppend/forLooseSandbox.ts @@ -5,10 +5,13 @@ import { checkActivityFunctions } from 'single-spa'; import type { Freer } from '../../../interfaces'; -import { patchHTMLDynamicAppendPrototypeFunctions, rebuildCSSRules, recordStyledComponentsCSSRules } from './common'; - -let bootstrappingPatchCount = 0; -let mountingPatchCount = 0; +import { + calcAppCount, + isAllAppsUnmounted, + patchHTMLDynamicAppendPrototypeFunctions, + rebuildCSSRules, + recordStyledComponentsCSSRules, +} from './common'; /** * Just hijack dynamic head append, that could avoid accidentally hijacking the insertion of elements except in head. @@ -52,17 +55,15 @@ export function patchLooseSandbox( }), ); - if (!mounting) bootstrappingPatchCount++; - if (mounting) mountingPatchCount++; + if (!mounting) calcAppCount(appName, 'increase', 'bootstrapping'); + if (mounting) calcAppCount(appName, 'increase', 'mounting'); return function free() { - // bootstrap patch just called once but its freer will be called multiple times - if (!mounting && bootstrappingPatchCount !== 0) bootstrappingPatchCount--; - if (mounting) mountingPatchCount--; + if (!mounting) calcAppCount(appName, 'decrease', 'bootstrapping'); + if (mounting) calcAppCount(appName, 'decrease', 'mounting'); - const allMicroAppUnmounted = mountingPatchCount === 0 && bootstrappingPatchCount === 0; // release the overwrite prototype after all the micro apps unmounted - if (allMicroAppUnmounted) unpatchDynamicAppendPrototypeFunctions(); + if (isAllAppsUnmounted()) unpatchDynamicAppendPrototypeFunctions(); recordStyledComponentsCSSRules(dynamicStyleSheetElements); diff --git a/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts b/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts index 7f00314..553d292 100644 --- a/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts +++ b/src/sandbox/patchers/dynamicAppend/forStrictSandbox.ts @@ -8,7 +8,9 @@ import { nativeGlobal } from '../../../utils'; import { getCurrentRunningApp } from '../../common'; import type { ContainerConfig } from './common'; import { + calcAppCount, getAppWrapperHeadElement, + isAllAppsUnmounted, isHijackingTag, patchHTMLDynamicAppendPrototypeFunctions, rawHeadAppendChild, @@ -74,9 +76,6 @@ function patchDocumentCreateElement() { }; } -let bootstrappingPatchCount = 0; -let mountingPatchCount = 0; - export function patchStrictSandbox( appName: string, appWrapperGetter: () => HTMLElement | ShadowRoot, @@ -108,17 +107,15 @@ export function patchStrictSandbox( (element) => elementAttachContainerConfigMap.get(element)!, ); - if (!mounting) bootstrappingPatchCount++; - if (mounting) mountingPatchCount++; + if (!mounting) calcAppCount(appName, 'increase', 'bootstrapping'); + if (mounting) calcAppCount(appName, 'increase', 'mounting'); return function free() { - // bootstrap patch just called once but its freer will be called multiple times - if (!mounting && bootstrappingPatchCount !== 0) bootstrappingPatchCount--; - if (mounting) mountingPatchCount--; + if (!mounting) calcAppCount(appName, 'decrease', 'bootstrapping'); + if (mounting) calcAppCount(appName, 'decrease', 'mounting'); - const allMicroAppUnmounted = mountingPatchCount === 0 && bootstrappingPatchCount === 0; // release the overwritten prototype after all the micro apps unmounted - if (allMicroAppUnmounted) { + if (isAllAppsUnmounted()) { unpatchDynamicAppendPrototypeFunctions(); unpatchDocumentCreate(); }