{"version":3,"sources":["../../src/use-script-tag/index.ts"],"sourcesContent":["import { useRef } from 'react'\nimport { useLatest } from '../use-latest'\nimport { useMount } from '../use-mount'\nimport { useStableFn } from '../use-stable-fn'\nimport { useUnmount } from '../use-unmount'\nimport { createPromiseWithResolvers, isClient, noop } from '../utils/basic'\n\nimport type { MutableRefObject } from 'react'\n\nexport type ReferrerPolicy =\n  | 'no-referrer'\n  | 'no-referrer-when-downgrade'\n  | 'origin'\n  | 'origin-when-cross-origin'\n  | 'same-origin'\n  | 'strict-origin'\n  | 'strict-origin-when-cross-origin'\n  | 'unsafe-url'\n\nexport interface UseScriptTagOptions {\n  /**\n   * Whether load script immediately at mount\n   *\n   * @defaultValue true\n   */\n  immediate?: boolean\n  /**\n   * Whether the script is loaded asynchronously\n   *\n   * @defaultValue true\n   */\n  async?: boolean\n  /**\n   * The type of the script\n   *\n   * @defaultValue 'text/javascript'\n   */\n  type?: string\n  /**\n   * Whether to control the loading of the script manually\n   *\n   * @defaultValue false\n   */\n  manual?: boolean\n  /**\n   * The value of the `crossorigin` attribute\n   *\n   * @defaultValue undefined\n   */\n  crossOrigin?: 'anonymous' | 'use-credentials'\n  /**\n   * The value of the `referrerpolicy` attribute\n   *\n   * @defaultValue undefined\n   */\n  referrerPolicy?: ReferrerPolicy\n  /**\n   * Whether the script is a module script\n   *\n   * @defaultValue undefined\n   */\n  noModule?: boolean\n  /**\n   * Whether the script is deferred\n   *\n   * @defaultValue false\n   */\n  defer?: boolean\n  /**\n   * Additional attributes to be set on the script element\n   *\n   * @defaultValue {}\n   */\n  attrs?: Record<string, string>\n}\n\nexport type UseScriptTagReturns = {\n  /**\n   * The script element Ref\n   */\n  scriptTag: MutableRefObject<HTMLScriptElement | null>\n  /**\n   * Load the script\n   *\n   * @param waitForScriptLoad Whether to wait for the script to be loaded, `true` by default\n   */\n  load: (waitForScriptLoad?: boolean) => Promise<HTMLScriptElement | boolean>\n  /**\n   * Unload the script\n   */\n  unload(): void\n}\n\n/**\n * A React Hook that helps to apply a script tag to the document with ease in React.\n */\nexport function useScriptTag(\n  src: string,\n  onLoaded: (el: HTMLScriptElement) => void = noop,\n  options: UseScriptTagOptions = {},\n): UseScriptTagReturns {\n  const {\n    immediate = true,\n    manual = false,\n    type = 'text/javascript',\n    async = true,\n    crossOrigin,\n    referrerPolicy,\n    noModule,\n    defer,\n    attrs = {},\n  } = options\n\n  const scriptTag = useRef<HTMLScriptElement | null>(null)\n  const promiseRef = useRef<Promise<HTMLScriptElement | boolean> | null>(null)\n\n  function loadScript(waitForScriptLoad = true): Promise<HTMLScriptElement | boolean> {\n    const { promise, reject, resolve } = createPromiseWithResolvers<HTMLScriptElement | boolean>()\n\n    if (!isClient) {\n      resolve(false)\n      return promise\n    }\n\n    function createScriptElement() {\n      const el = document.createElement('script')\n      el.type = type\n      el.async = async\n      el.src = src\n      if (defer) el.defer = defer\n      if (crossOrigin) el.crossOrigin = crossOrigin\n      if (noModule) el.noModule = noModule\n      if (referrerPolicy) el.referrerPolicy = referrerPolicy\n      for (const [name, value] of Object.entries(attrs)) {\n        el.setAttribute(name, value)\n      }\n      return el\n    }\n\n    function resolveWithElement(el: HTMLScriptElement) {\n      scriptTag.current = el\n      resolve(el)\n    }\n\n    let shouldAppend = false\n    let el = document.querySelector<HTMLScriptElement>(`script[src=\"${src}\"]`)\n\n    if (!el) {\n      el = createScriptElement()\n      shouldAppend = true\n    } else if (el.hasAttribute('data-loaded')) {\n      resolveWithElement(el)\n      return promise\n    }\n\n    el.addEventListener('error', (event) => reject(event))\n    el.addEventListener('abort', (event) => reject(event))\n    el.addEventListener('load', () => {\n      if (!el) return\n      el.setAttribute('data-loaded', 'true')\n      onLoaded(el)\n      resolveWithElement(el)\n    })\n\n    if (shouldAppend) el = document.head.appendChild(el)\n\n    !waitForScriptLoad && resolveWithElement(el)\n\n    return promise\n  }\n\n  const latest = useLatest({ loadScript })\n\n  const load = useStableFn((waitForScriptLoad = true): Promise<HTMLScriptElement | boolean> => {\n    if (!promiseRef.current) {\n      promiseRef.current = latest.current.loadScript(waitForScriptLoad)\n    }\n\n    return promiseRef.current\n  })\n\n  const unload = useStableFn(() => {\n    promiseRef.current = null\n    if (scriptTag.current) scriptTag.current = null\n    const el = document.querySelector<HTMLScriptElement>(`script[src=\"${src}\"]`)\n    el && document.head.removeChild(el)\n  })\n\n  useMount(() => immediate && !manual && load())\n\n  useUnmount(() => !manual && unload())\n\n  return {\n    scriptTag,\n    load,\n    unload,\n  }\n}\n"],"names":["useScriptTag","src","onLoaded","noop","options","immediate","manual","type","async","crossOrigin","referrerPolicy","noModule","defer","attrs","scriptTag","useRef","promiseRef","loadScript","waitForScriptLoad","createPromiseWithResolvers","promise","reject","resolve","isClient","createScriptElement","el","document","createElement","Object","entries","name","value","setAttribute","resolveWithElement","current","shouldAppend","querySelector","hasAttribute","addEventListener","event","head","appendChild","latest","useLatest","load","useStableFn","unload","removeChild","useMount","useUnmount"],"mappings":"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"}