Skip to content

Commit

Permalink
#7670: Ensure stylesheets are removed in IsolatedComponent (#8213)
Browse files Browse the repository at this point in the history
---------
Co-authored-by: Todd Schiller <[email protected]>
Co-authored-by: Graham Langford <[email protected]>
  • Loading branch information
fregante authored Apr 10, 2024
1 parent 9a44d41 commit 6e2f02e
Showing 1 changed file with 21 additions and 8 deletions.
29 changes: 21 additions & 8 deletions src/components/IsolatedComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,22 @@ import React, { Suspense } from "react";
import { Stylesheets } from "@/components/Stylesheets";
import EmotionShadowRoot from "@/components/EmotionShadowRoot";
import isolatedComponentList from "./isolatedComponentList";
import { signalFromPromise } from "abort-utils";

const MODE = process.env.SHADOW_DOM as "open" | "closed";

type LazyFactory<T> = () => Promise<{
default: React.ComponentType<T>;
}>;

// Drop the stylesheet injected by `mini-css-extract-plugin` into the main document.
// Until this is resolved https://github.com/webpack-contrib/mini-css-extract-plugin/issues/1092#issuecomment-2037540032
async function discardNewStylesheets(signal: AbortSignal) {
/**
* Drop the stylesheet injected by `mini-css-extract-plugin` into the main document.
*
* @warning The `lazyFactory` function should never be called outside `discardStylesheetsWhilePending`
* because this helper must catch the stylesheets injected when the factory is first called.
*/
async function discardStylesheetsWhilePending(
lazyFactory: LazyFactory<unknown>,
) {
const baseUrl = chrome.runtime.getURL("");

const observer = new MutationObserver((mutations) => {
Expand All @@ -52,9 +57,16 @@ async function discardNewStylesheets(signal: AbortSignal) {
childList: true,
});

signal.addEventListener("abort", () => {
// Call and discard. React.lazy() will call it again and use the result or the error.
// This is fine because import() does not re-fetch/re-run the module.
try {
// The function must be first called *after* the observer is set up.
await lazyFactory();
} catch {
// React.lazy() will take care of it
} finally {
observer.disconnect();
});
}
}

type Props<T> = {
Expand Down Expand Up @@ -111,8 +123,9 @@ export default function IsolatedComponent<T>({
);
}

// `discard` must be called before `React.lazy`
void discardNewStylesheets(signalFromPromise(lazy()));
// `discard` must be called before `React.lazy`.
// `discardStylesheetsWhilePending` is needed until this is resolved https://github.com/webpack-contrib/mini-css-extract-plugin/issues/1092#issuecomment-2037540032
void discardStylesheetsWhilePending(lazy);
const LazyComponent = React.lazy(lazy);

const stylesheetUrl = noStyle ? null : chrome.runtime.getURL(`${name}.css`);
Expand Down

0 comments on commit 6e2f02e

Please sign in to comment.