{"version":3,"file":"AsyncComponent.cjs","names":["isServer","useRef","loadDependency","Render"],"sources":["../../src/AsyncComponent/AsyncComponent.tsx"],"sourcesContent":["import { useState, cloneElement, isValidElement, createElement, type ReactElement } from 'react';\nimport { isServer } from '@pastweb/tools';\nimport { registerAsyncTask } from '@pastweb/tools/ssrUtils';\nimport { Render } from '../Render';\nimport { useBeforeMount } from '../useBeforeMount';\nimport { useRef } from '../useRef';\nimport { loadDependency } from './utils';\nimport type { Component } from '../createEntry';\nimport type { AsyncComponentProps, Dependency, DependencyInfo } from './types';\n\n// WeakMap keyed by the loader function so we can store the loaded component\nconst preloadedOnServer = new WeakMap<() => Promise<any>, any>();\n\n/**\n * A React component that asynchronously loads and renders another component based on the provided `component` prop.\n * It also handles asynchronous dependencies before loading the component.\n *\n * @param props - The properties for the `AsyncComponent`.\n * @param props.component - A function returning a promise that resolves to the component to be loaded.\n * @param props.dependencies - An optional array of dependencies that need to be resolved before the component is loaded.\n * @param props.fallback - An optional fallback React element or component to display while the main component is loading.\n * @param restProps - Any additional props to pass to the loaded component.\n *\n * @returns A React element that either displays the fallback, the loaded component, or `null` if neither is available.\n *\n * @remarks\n * - The `component` prop should be a function that returns a promise resolving to a React component.\n * - The `dependencies` prop should be an array of dependencies, which will be resolved before loading the main component.\n * - The `fallback` prop will be shown while waiting for the main component and dependencies to load.\n * - Once all dependencies are resolved, the `component` will be loaded and rendered with any additional `restProps`.\n * - If the `component` or dependencies fail to load, errors will be logged to the console.\n * \n * @example\n * // Example usage:\n * <AsyncComponent\n *   component={() => import('./MyComponent')}\n *   dependencies={[import('./dependency1'), import('./dependency2')]}\n *   fallback={<LoadingSpinner />}\n *   someProp={value}\n * />\n * // The `AsyncComponent` will render `LoadingSpinner` until all dependencies are resolved\n * // and the component is loaded, then it will render the loaded component with `someProp`.\n */\nexport function AsyncComponent(props: AsyncComponentProps) {\n  return isServer ? serverSide(props) : clientSide(props);\n}\n\n/**\n * Handles the client-side rendering of the async component.\n * Loads dependencies and the main component asynchronously, displaying a fallback while loading.\n *\n * @param props - The properties for the `AsyncComponent`.\n * @returns A React element representing the loaded component, the fallback, or `null`.\n */\nfunction clientSide(props: AsyncComponentProps): ReactElement | null {\n  const { component, dependencies, fallback = null, ...restProps } = props;\n\n  const [_component, setComponent] = useState<ReactElement | null>(fallback);\n  const depCounter = useRef(0);\n\n  /**\n   * Loads the main component and updates the state with the rendered element.\n   * Logs an error to the console if the component fails to load.\n   */\n  async function loadComponent() {\n    try {\n      const { default: Comp } = await component();\n      setComponent(<Comp />);\n    } catch (e) {\n      console.error(e);\n    }\n  }\n\n  /**\n   * Increments the dependency counter and triggers component loading\n   * once all dependencies have been processed.\n   */\n  function depCountIncrement(): void {\n    depCounter.value += 1;\n    if (depCounter.value === (dependencies!.length)) {\n      loadComponent();\n    }\n  }\n\n  /**\n   * Loads all dependencies asynchronously. Each dependency is loaded and its counter incremented.\n   * Once all dependencies are processed, the main component is loaded.\n   * If no dependencies are provided, the main component is loaded immediately.\n   */\n  async function loadDependencies() {\n    if (dependencies) {\n      dependencies.forEach(async (dep: Dependency | DependencyInfo, i: number) => {\n        try {\n          await loadDependency(dep);\n        } catch (e) {\n          console.error(`the dependency with the index ${i} has an error:`, e);\n        } finally {\n          depCountIncrement();\n        }\n      });\n    } else {\n      loadComponent();\n    }\n  }\n\n  useBeforeMount(() => loadDependencies());\n\n  return (\n    _component ?\n      isValidElement(_component) ?\n        cloneElement(_component as ReactElement, restProps) :\n        createElement(_component as unknown as string | Component, restProps)\n      : null\n  );\n}\n\n/**\n * Handles the server-side rendering of the async component.\n * Awaits all dependencies and the main component before rendering synchronously.\n *\n * @param props - The properties for the `AsyncComponent`.\n * @returns A promise resolving to a React element representing the loaded component, the fallback, or `null`.\n */\nfunction serverSide(props: AsyncComponentProps): ReactElement | null {\n  const { component, dependencies = [], fallback = null, ...restProps } = props;\n\n  // If we already resolved this loader (after resolveAsyncTasks), render it\n  const Preloaded = preloadedOnServer.get(component);\n  if (Preloaded) {\n    return <Render content={Preloaded} props={restProps} />;\n  }\n\n  // During collection render: just register the work and return fallback immediately.\n  // The actual loading + nested discovery happens later in resolveAsyncTasks().\n  registerAsyncTask(async () => {\n    // 1. Load dependencies\n    if (dependencies && dependencies.length > 0) {\n      await Promise.all(\n        dependencies.map(async (dep, index) => {\n          try {\n            await loadDependency(dep);\n          } catch (err) {\n            console.error(`dependency ${index} failed:`, err);\n          }\n        })\n      );\n    }\n\n    // 2. Load the component\n    const module = await component();\n    const Comp = module.default as Component;\n\n    if (!Comp) {\n      console.error('AsyncComponent: No default export found');\n      return;\n    }\n\n    // 3. Store it so the final server render can use it directly\n    preloadedOnServer.set(component, Comp);\n  });\n\n  return null;\n}\n"],"mappings":";;;;;;;;;;;;AAWA,MAAM,oCAAoB,IAAI,QAAiC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgC/D,SAAgB,eAAe,OAA4B;CACzD,OAAOA,eAAAA,WAAW,WAAW,KAAK,IAAI,WAAW,KAAK;AACxD;;;;;;;;AASA,SAAS,WAAW,OAAiD;CACnE,MAAM,EAAE,WAAW,cAAc,WAAW,MAAM,GAAG,cAAc;CAEnE,MAAM,CAAC,YAAY,iBAAA,GAAA,MAAA,SAAA,CAA8C,QAAQ;CACzE,MAAM,aAAaC,sBAAAA,OAAO,CAAC;;;;;CAM3B,eAAe,gBAAgB;EAC7B,IAAI;GACF,MAAM,EAAE,SAAS,SAAS,MAAM,UAAU;GAC1C,aAAa,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD,CAAO,CAAA,CAAC;EACvB,SAAS,GAAG;GACV,QAAQ,MAAM,CAAC;EACjB;CACF;;;;;CAMA,SAAS,oBAA0B;EACjC,WAAW,SAAS;EACpB,IAAI,WAAW,UAAW,aAAc,QACtC,cAAc;CAElB;;;;;;CAOA,eAAe,mBAAmB;EAChC,IAAI,cACF,aAAa,QAAQ,OAAO,KAAkC,MAAc;GAC1E,IAAI;IACF,MAAMC,6BAAAA,eAAe,GAAG;GAC1B,SAAS,GAAG;IACV,QAAQ,MAAM,iCAAiC,EAAE,iBAAiB,CAAC;GACrE,UAAU;IACR,kBAAkB;GACpB;EACF,CAAC;OAED,cAAc;CAElB;CAEA,sCAAA,qBAAqB,iBAAiB,CAAC;CAEvC,OACE,cAAA,GAAA,MAAA,eAAA,CACiB,UAAU,KAAA,GAAA,MAAA,aAAA,CACV,YAA4B,SAAS,KAAA,GAAA,MAAA,cAAA,CACpC,YAA6C,SAAS,IACpE;AAER;;;;;;;;AASA,SAAS,WAAW,OAAiD;CACnE,MAAM,EAAE,WAAW,eAAe,CAAC,GAAG,WAAW,MAAM,GAAG,cAAc;CAGxE,MAAM,YAAY,kBAAkB,IAAI,SAAS;CACjD,IAAI,WACF,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAACC,eAAAA,QAAD;EAAQ,SAAS;EAAW,OAAO;CAAY,CAAA;CAKxD,CAAA,GAAA,wBAAA,kBAAA,CAAkB,YAAY;EAE5B,IAAI,gBAAgB,aAAa,SAAS,GACxC,MAAM,QAAQ,IACZ,aAAa,IAAI,OAAO,KAAK,UAAU;GACrC,IAAI;IACF,MAAMD,6BAAAA,eAAe,GAAG;GAC1B,SAAS,KAAK;IACZ,QAAQ,MAAM,cAAc,MAAM,WAAW,GAAG;GAClD;EACF,CAAC,CACH;EAKF,MAAM,QAAO,MADQ,UAAU,EAAA,CACX;EAEpB,IAAI,CAAC,MAAM;GACT,QAAQ,MAAM,yCAAyC;GACvD;EACF;EAGA,kBAAkB,IAAI,WAAW,IAAI;CACvC,CAAC;CAED,OAAO;AACT"}