From 8d6bc0f565ad1f000efdeadec97077a4297f3efd Mon Sep 17 00:00:00 2001 From: pierrejeambrun Date: Tue, 5 Aug 2025 17:22:43 +0200 Subject: [PATCH] AIP-68 Fix multiple react app plugins --- .../src/airflow/ui/src/pages/ReactPlugin.tsx | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/airflow-core/src/airflow/ui/src/pages/ReactPlugin.tsx b/airflow-core/src/airflow/ui/src/pages/ReactPlugin.tsx index 8a19320563476..1c1800044f768 100644 --- a/airflow-core/src/airflow/ui/src/pages/ReactPlugin.tsx +++ b/airflow-core/src/airflow/ui/src/pages/ReactPlugin.tsx @@ -24,6 +24,13 @@ import type { ReactAppResponse } from "openapi/requests/types.gen"; import { ErrorPage } from "./Error"; +type PluginComponentType = FC<{ + dagId?: string; + mapIndex?: string; + runId?: string; + taskId?: string; +}>; + export const ReactPlugin = ({ reactApp }: { readonly reactApp: ReactAppResponse }) => { const { dagId, mapIndex, runId, taskId } = useParams(); @@ -31,20 +38,23 @@ export const ReactPlugin = ({ reactApp }: { readonly reactApp: ReactAppResponse // We are assuming the plugin manager is trusted and the bundle_url is safe import(/* @vite-ignore */ reactApp.bundle_url) .then(() => { - const component = ( - globalThis as unknown as { - AirflowPlugin: FC<{ - dagId?: string; - mapIndex?: string; - runId?: string; - taskId?: string; - }>; - } - ).AirflowPlugin; + // Store components in globalThis[reactApp.name] to avoid conflicts with the shared globalThis.AirflowPlugin + // global variable. + let pluginComponent = (globalThis as Record)[reactApp.name] as + | PluginComponentType + | undefined; - return { - default: component, - }; + if (pluginComponent === undefined) { + pluginComponent = (globalThis as Record).AirflowPlugin as PluginComponentType; + + (globalThis as Record)[reactApp.name] = pluginComponent; + } + + if (typeof pluginComponent !== "function") { + throw new TypeError(`Expected function, got ${typeof pluginComponent} for plugin ${reactApp.name}`); + } + + return { default: pluginComponent }; }) .catch((error: unknown) => { console.error("Component Failed Loading:", error);