Source: react/hooks/paragon/useParagonThemeCore.js

import { useEffect, useState } from 'react';

import { logError, logInfo } from '../../../logging';
import { fallbackThemeUrl, removeExistingLinks } from './utils';

/**
 * Custom React hook that manages the loading and updating of the core Paragon theme CSS and the brand override
 * theme CSS. It ensures that the core theme CSS (both default and brand override) is added to the document
 * `<head>` as `<link>` elements.
 *
 * The function logs and handles fallback logic in case the core theme fails to load.
 *
 * @memberof module:React
 *
 * @param {Object} args - The arguments object containing theme and callback information.
 * @param {Object} args.themeCore - The core theme configuration.
 * @param {string} args.themeCore.urls.default - The URL to the default core theme CSS.
 * @param {string} [args.themeCore.urls.brandOverride] - The URL to the brand override theme CSS (optional).
 * @param {Function} args.onComplete - A callback function that is called once both the core Paragon (default)
 * theme and brand override theme (if provided) are complete.
 */
const useParagonThemeCore = ({
  themeCore,
  onComplete,
}) => {
  const [isParagonThemeCoreComplete, setIsParagonThemeCoreComplete] = useState(false);
  const [isBrandThemeCoreComplete, setIsBrandThemeCoreComplete] = useState(false);

  useEffect(() => {
    // Call `onComplete` once both the paragon and brand theme core are complete.
    if (isParagonThemeCoreComplete && isBrandThemeCoreComplete) {
      onComplete();
    }
  }, [isParagonThemeCoreComplete, isBrandThemeCoreComplete, onComplete]);

  useEffect(() => {
    // If there is no config for the core theme url, do nothing.
    if (!themeCore?.urls) {
      setIsParagonThemeCoreComplete(true);
      setIsBrandThemeCoreComplete(true);
      return;
    }

    const existingCoreThemeLink = document.head.querySelector(`link[href='${themeCore.urls.default}']`);
    const brandCoreLink = document.head.querySelector(`link[href='${themeCore.urls.brandOverride}']`);

    if (existingCoreThemeLink) {
      existingCoreThemeLink.rel = 'stylesheet';
      existingCoreThemeLink.removeAttribute('as');
      existingCoreThemeLink.dataset.paragonThemeCore = true;
      if (brandCoreLink) {
        brandCoreLink.rel = 'stylesheet';
        brandCoreLink.removeAttribute('as');
        brandCoreLink.dataset.brandThemeCore = true;
      }
      setIsParagonThemeCoreComplete(true);
      setIsBrandThemeCoreComplete(true);
      return;
    }

    const getParagonThemeCoreLink = () => document.head.querySelector('link[data-paragon-theme-core="true"]');
    const getExistingCoreThemeLinks = (isBrandOverride) => {
      const coreThemeLinkSelector = `link[data-${isBrandOverride ? 'brand' : 'paragon'}-theme-core="true"]`;
      return document.head.querySelectorAll(coreThemeLinkSelector);
    };

    const createCoreThemeLink = (
      url,
      {
        isFallbackThemeUrl = false,
        isBrandOverride = false,
      } = {},
    ) => {
      let coreThemeLink = document.createElement('link');
      coreThemeLink.href = url;
      coreThemeLink.rel = 'stylesheet';
      if (isBrandOverride) {
        coreThemeLink.dataset.brandThemeCore = true;
      } else {
        coreThemeLink.dataset.paragonThemeCore = true;
      }
      coreThemeLink.onload = () => {
        if (isBrandOverride) {
          setIsBrandThemeCoreComplete(true);
        } else {
          setIsParagonThemeCoreComplete(true);
        }
      };
      coreThemeLink.onerror = () => {
        if (isFallbackThemeUrl) {
          logError('Could not load core theme fallback URL. Aborting.');
          if (isBrandOverride) {
            setIsBrandThemeCoreComplete(true);
          } else {
            setIsParagonThemeCoreComplete(true);
          }
          const otherExistingLinks = getExistingCoreThemeLinks(isBrandOverride);
          removeExistingLinks(otherExistingLinks);
          return;
        }
        const paragonThemeAccessor = isBrandOverride ? 'brand' : 'paragon';
        const core = PARAGON_THEME?.[paragonThemeAccessor]?.themeUrls?.core ?? null;
        if (core) {
          const coreThemeFallbackUrl = fallbackThemeUrl(core.fileName);
          logInfo(`Could not load core theme CSS from ${url}. Falling back to locally installed core theme CSS: ${coreThemeFallbackUrl}`);
          coreThemeLink = createCoreThemeLink(coreThemeFallbackUrl, { isFallbackThemeUrl: true, isBrandOverride });
          const otherExistingLinks = getExistingCoreThemeLinks(isBrandOverride);
          removeExistingLinks(otherExistingLinks);
          const foundParagonThemeCoreLink = getParagonThemeCoreLink();
          if (foundParagonThemeCoreLink) {
            foundParagonThemeCoreLink.insertAdjacentElement(
              'afterend',
              coreThemeLink,
            );
          } else {
            document.head.insertAdjacentElement(
              'afterbegin',
              coreThemeLink,
            );
          }
        } else {
          logError(`Failed to load core theme CSS from ${url} or fallback URL. Aborting.`);
        }
      };
      return coreThemeLink;
    };

    const paragonCoreThemeLink = createCoreThemeLink(themeCore.urls.default);
    document.head.insertAdjacentElement(
      'afterbegin',
      paragonCoreThemeLink,
    );

    if (themeCore.urls.brandOverride) {
      const brandCoreThemeLink = createCoreThemeLink(themeCore.urls.brandOverride, { isBrandOverride: true });
      const foundParagonThemeCoreLink = getParagonThemeCoreLink();
      if (foundParagonThemeCoreLink) {
        foundParagonThemeCoreLink.insertAdjacentElement(
          'afterend',
          brandCoreThemeLink,
        );
      } else {
        document.head.insertAdjacentElement(
          'afterbegin',
          brandCoreThemeLink,
        );
      }
    } else {
      setIsBrandThemeCoreComplete(true);
    }
  }, [themeCore?.urls, onComplete]);
};

export default useParagonThemeCore;