remote-components 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/html/host.cjs +13 -1
- package/dist/html/host.cjs.map +1 -1
- package/dist/html/host.js +13 -1
- package/dist/html/host.js.map +1 -1
- package/dist/react/index.cjs +116 -103
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +124 -104
- package/dist/react/index.js.map +1 -1
- package/dist/shared/client/remote-component.cjs +26 -2
- package/dist/shared/client/remote-component.cjs.map +1 -1
- package/dist/shared/client/remote-component.js +26 -2
- package/dist/shared/client/remote-component.js.map +1 -1
- package/package.json +1 -1
package/dist/react/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/index.tsx"],"sourcesContent":["import { useState, useEffect, useLayoutEffect, useRef, useMemo } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { RemoteComponentProps as RemoteComponentPropsType } from '../shared/client/types';\nimport {\n loadRemoteComponent,\n DEFAULT_ROUTE,\n RUNTIME_WEBPACK,\n REMOTE_COMPONENT_REGEX,\n type LoadRemoteComponentProps,\n} from '../shared/client/remote-component';\n\n// patch react/jsx-runtime to support the shadowrootmode attribute on template elements\ndeclare module 'react/jsx-runtime' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n template: {\n shadowrootmode?: 'open' | 'closed';\n id?: string;\n ref?: React.Ref<HTMLTemplateElement>;\n dangerouslySetInnerHTML?: {\n __html: string;\n };\n children?: React.ReactNode;\n };\n }\n }\n}\n\nexport interface RemoteComponentProps {\n /** The source URL of the remote component. */\n src?: string;\n /** Whether to isolate the remote component using a Shadow DOM wrapper. */\n isolate?: boolean;\n /** The credentials to use for the fetch request. Defaults to `same-origin`. */\n credentials?: RequestCredentials;\n name?: string;\n /** Shared modules to include in the remote component's context. */\n shared?: LoadRemoteComponentProps['shared'];\n /** The children to use as a loading fallback until the remote component is loaded. */\n children?: React.ReactNode;\n}\n\nfunction getRemoteComponentHtml(html: string) {\n if (typeof document === 'undefined') return html;\n\n const temp = document.createElement('div');\n temp.innerHTML = html;\n\n return temp.querySelector('div[id^=\"__REMOTE_COMPONENT\"]')?.innerHTML ?? '';\n}\n\n/**\n * RemoteComponent is a React component that fetches and renders a remote component.\n * It supports SSR and can isolate the remote component in a shadow DOM.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your React application to consume a remote component from a remote application:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/react';\n *\n * export default function App() {\n * return (\n * <>\n * <h1>Welcome to My App</h1>\n * <p>This page consumes a remote component from another application.</p>\n * <RemoteComponent src=\"/nextjs-app-remote/components/header\" />\n * </>\n * );\n * }\n * ```\n *\n * To share modules, you can provide a shared module map with references to the shared modules:\n *\n * ```tsx\n * <RemoteComponent\n * src=\"/nextjs-app-remote/components/header\"\n * shared={{\n * '@/components/provider': () => import('@/components/host-provider')\n * }}\n * />\n * ```\n */\nexport function RemoteComponent({\n src,\n isolate,\n credentials = 'same-origin',\n name = '__vercel_remote_component',\n shared = {},\n children,\n}: RemoteComponentProps) {\n const [data, setData] = useState<Omit<\n RemoteComponentPropsType,\n 'children'\n > | null>(null);\n const [remoteComponent, setRemoteComponent] = useState<\n React.ReactNode | Error\n >(null);\n const shadowRootContainerRef = useRef<HTMLDivElement | null>(null);\n const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(\n typeof document !== 'undefined'\n ? (document.getElementById(`shadowroot_${name}`)?.shadowRoot ?? null)\n : null,\n );\n const htmlRef = useRef<string | null>(\n typeof document !== 'undefined'\n ? (document.getElementById(`shadowroot_${name}`)?.shadowRoot?.innerHTML ??\n document.getElementById(`__REMOTE_COMPONENT${name}`)?.innerHTML)\n : null,\n );\n const endTemplateRef = useRef<HTMLTemplateElement | null>(null);\n\n useLayoutEffect(() => {\n if (isolate !== false && typeof document !== 'undefined' && !shadowRoot) {\n let shadowRootElement: ShadowRoot | null = null;\n const element = document.getElementById(`shadowroot_${name}`);\n shadowRootElement = element?.shadowRoot ?? null;\n\n if (!shadowRootElement && element) {\n // create a shadow root if it doesn't exist\n // this is a fallback for browsers that don't support declarative shadow DOM\n element.attachShadow({ mode: 'open' });\n shadowRootElement = element.shadowRoot;\n }\n\n if (shadowRootElement) {\n // remove all nodes from the shadow root except links\n shadowRootElement.querySelectorAll('*:not(link)').forEach((node) => {\n node.remove();\n });\n setShadowRoot(shadowRootElement);\n }\n }\n }, [name, isolate, shadowRoot]);\n\n const url = useMemo(() => {\n if (typeof src !== 'string')\n return new URL(\n typeof document !== 'undefined' ? location.href : 'http://localhost',\n );\n try {\n return typeof document !== 'undefined'\n ? new URL(src, location.href)\n : new URL(src);\n } catch {\n return new URL(src, 'http://localhost');\n }\n }, [src]);\n\n useEffect(() => {\n let mounted = true;\n\n (async () => {\n let html = getRemoteComponentHtml(\n htmlRef.current ??\n (endTemplateRef.current?.previousElementSibling?.tagName === 'div'\n ? endTemplateRef.current.previousElementSibling.innerHTML\n : ''),\n );\n\n if (!html && src) {\n // fetch the remote component\n const fetchInit = {\n method: 'GET',\n headers: {\n Accept: 'text/html',\n // pass the public address of the remote component to the server used for module map mutation\n 'Vercel-Remote-Component-Url': url.href,\n },\n credentials,\n } as RequestInit;\n\n const res = await fetch(url, fetchInit);\n\n if (!res.ok) {\n throw new Error(\n `Failed to fetch remote component \"${name}\": ${res.status}`,\n );\n }\n\n // get the full HTML content as a string\n html = await res.text();\n }\n\n // create a virtual element which will be used to parse the HTML and extract the component and RSC flight data\n const doc = document.createElement('div');\n doc.innerHTML = html;\n\n // reference to the remote component content\n const component =\n doc.querySelector(`div[data-bundle][data-route][id^=\"${name}\"]`) ??\n // fallback to the first element with the data-bundle and data-route attributes when not using a named remote component\n doc.querySelector('div[data-bundle][data-route]') ??\n // fallback to Next.js Pages Router\n doc.querySelector('div#__next');\n const nextData = JSON.parse(\n (\n doc.querySelector('#__NEXT_DATA__') ??\n doc.querySelector('#__REMOTE_NEXT_DATA__')\n )?.textContent ?? 'null',\n ) as {\n props: {\n pageProps: Record<string, unknown>;\n __REMOTE_COMPONENT__?: {\n bundle: string;\n runtime: 'turbopack' | 'webpack';\n };\n };\n page: string;\n buildId: string;\n } | null;\n\n const remoteName =\n component?.getAttribute('id')?.replace(/_ssr$/, '') ||\n (nextData ? '__next' : name);\n // reference to the RSC flight data\n const rsc = doc.querySelector(`#${remoteName}_rsc`);\n\n // reference to the bundle containing the client components\n const bundle =\n component?.getAttribute('data-bundle') ||\n nextData?.props.__REMOTE_COMPONENT__?.bundle ||\n 'default';\n\n const metadata = {\n name: remoteName,\n bundle,\n route:\n component?.getAttribute('data-route') ??\n nextData?.page ??\n DEFAULT_ROUTE,\n runtime: (component?.getAttribute('data-runtime') ??\n (nextData?.props.__REMOTE_COMPONENT__?.runtime ||\n RUNTIME_WEBPACK)) as RemoteComponentPropsType['runtime'],\n };\n\n const remoteSharedEl = doc.querySelector(`#${remoteName}_shared`);\n const remoteShared = (JSON.parse(remoteSharedEl?.textContent ?? '{}') ??\n {}) as Record<string, string>;\n remoteSharedEl?.parentElement?.removeChild(remoteSharedEl);\n\n if (!component || !(rsc || nextData)) {\n throw new Error(`Failed to find component with id \"${remoteName}\"`);\n }\n\n // reference to all link elements in the remote component\n const links = Array.from(\n doc.querySelectorAll<HTMLLinkElement>('link[href]'),\n ).map((link) => ({\n rel: link.rel,\n href: new URL(link.getAttribute('href') ?? link.href, url).href,\n as: link.getAttribute('as') || undefined,\n }));\n\n const scripts = doc.querySelectorAll<HTMLScriptElement>(\n 'script[src],script[data-src]',\n );\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (mounted) {\n if (rsc) {\n document.body.appendChild(rsc);\n }\n\n const newData = {\n ...metadata,\n links,\n remoteShared,\n url: url.href,\n data: rsc ? (rsc.textContent ?? '').split('\\n').filter(Boolean) : [],\n };\n setData(newData);\n\n loadRemoteComponent({\n url: new URL(url, location.origin),\n name,\n bundle,\n route: metadata.route,\n runtime: metadata.runtime,\n data: newData.data,\n nextData,\n scripts: Array.from(scripts).map((script) => {\n const scriptSrc =\n script.getAttribute('data-src') ||\n script.getAttribute('src') ||\n script.src;\n const { prefix, id: path } = REMOTE_COMPONENT_REGEX.exec(scriptSrc)\n ?.groups ?? {\n prefix: undefined,\n id: scriptSrc,\n };\n return {\n src: new URL(\n `${prefix ?? ''}${path}`.replace(\n /(?<char>[^:])(?<double>\\/\\/)/g,\n '$1/',\n ),\n url,\n ).href,\n };\n }),\n shared,\n remoteShared,\n container: shadowRoot,\n })\n .then((result) => {\n if (mounted) {\n if (result.error) {\n setRemoteComponent(result.error);\n } else {\n setRemoteComponent(result.component);\n }\n }\n })\n .catch((error: Error) => {\n if (mounted) {\n setRemoteComponent(error);\n }\n });\n }\n })().catch((error: Error) => {\n if (mounted) {\n setRemoteComponent(error);\n }\n });\n\n return () => {\n mounted = false;\n };\n }, [url, src, isolate, credentials, name, shared, shadowRoot]);\n\n if (remoteComponent instanceof Error) {\n throw remoteComponent;\n }\n\n const metadataJson = (\n <script data-remote-component type=\"application/json\">\n {JSON.stringify({\n name: data?.name || name,\n bundle: data?.bundle || 'default',\n route: data?.route || DEFAULT_ROUTE,\n runtime: data?.runtime || RUNTIME_WEBPACK,\n })}\n </script>\n );\n const linksToRender: React.ReactNode[] | null =\n data?.links?.map((link) => (\n <link\n as={link.as as string}\n href={new URL(link.href as string, url).href}\n key={`${link.href as string}_${link.rel}`}\n rel={link.rel as string}\n />\n )) || null;\n const componentToRender = (\n <>\n {linksToRender}\n {remoteComponent ?? children}\n </>\n );\n\n if (isolate !== false) {\n const shadowRemoteComponentHtml = shadowRoot?.querySelector(\n `#__REMOTE_COMPONENT${name}`,\n );\n if (shadowRemoteComponentHtml) {\n shadowRemoteComponentHtml.remove();\n }\n\n if (shadowRoot && remoteComponent && htmlRef.current) {\n const content = shadowRoot.querySelectorAll(':not(link,style)');\n content.forEach((node) => node.remove());\n htmlRef.current = null;\n }\n\n return (\n <>\n {metadataJson}\n <div\n id={`shadowroot_${data?.name ?? name}`}\n ref={shadowRootContainerRef}\n >\n {typeof document === 'undefined' ? (\n // eslint-disable-next-line react/no-unknown-property\n <template shadowrootmode=\"open\">\n {linksToRender}\n {children}\n </template>\n ) : null}\n {shadowRoot && remoteComponent\n ? createPortal(\n <>\n {linksToRender}\n {remoteComponent}\n </>,\n shadowRoot,\n )\n : null}\n </div>\n </>\n );\n }\n\n return (\n <>\n {metadataJson}\n {componentToRender}\n <template id={`${name}_end`} ref={endTemplateRef} />\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/index.tsx"],"sourcesContent":["import {\n useState,\n useEffect,\n useLayoutEffect,\n useRef,\n useMemo,\n startTransition,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport type { RemoteComponentProps as RemoteComponentPropsType } from '../shared/client/types';\nimport {\n loadRemoteComponent,\n DEFAULT_ROUTE,\n RUNTIME_WEBPACK,\n REMOTE_COMPONENT_REGEX,\n type LoadRemoteComponentProps,\n} from '../shared/client/remote-component';\n\n// patch react/jsx-runtime to support the shadowrootmode attribute on template elements\ndeclare module 'react/jsx-runtime' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n template: {\n shadowrootmode?: 'open' | 'closed';\n id?: string;\n ref?: React.Ref<HTMLTemplateElement>;\n dangerouslySetInnerHTML?: {\n __html: string;\n };\n children?: React.ReactNode;\n };\n }\n }\n}\n\nexport interface RemoteComponentProps {\n /** The source URL of the remote component. */\n src?: string;\n /** Whether to isolate the remote component using a Shadow DOM wrapper. */\n isolate?: boolean;\n /** The credentials to use for the fetch request. Defaults to `same-origin`. */\n credentials?: RequestCredentials;\n name?: string;\n /** Shared modules to include in the remote component's context. */\n shared?: LoadRemoteComponentProps['shared'];\n /** The children to use as a loading fallback until the remote component is loaded. */\n children?: React.ReactNode;\n}\n\nfunction getRemoteComponentHtml(html: string) {\n if (typeof document === 'undefined') return html;\n\n const temp = document.createElement('div');\n temp.innerHTML = html;\n\n // used by the Next.js Pages Router remote as a wrapper\n const ssrRemoteComponentContainer = temp.querySelector(\n 'div[id^=\"__REMOTE_COMPONENT\"]',\n );\n if (ssrRemoteComponentContainer) {\n return ssrRemoteComponentContainer.innerHTML;\n }\n\n // remote component content\n const remoteComponentContainer = temp.querySelector(\n `div[data-bundle][data-route][data-runtime][id^=\"__vercel_remote_component\"],div[data-bundle][data-route],div#__next`,\n );\n if (remoteComponentContainer) {\n return `${Array.from(temp.querySelectorAll('link,script'))\n .map((link) => link.outerHTML)\n .join('')}${remoteComponentContainer.outerHTML}`;\n }\n\n return '';\n}\n\n/**\n * RemoteComponent is a React component that fetches and renders a remote component.\n * It supports SSR and can isolate the remote component in a shadow DOM.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your React application to consume a remote component from a remote application:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/react';\n *\n * export default function App() {\n * return (\n * <>\n * <h1>Welcome to My App</h1>\n * <p>This page consumes a remote component from another application.</p>\n * <RemoteComponent src=\"/nextjs-app-remote/components/header\" />\n * </>\n * );\n * }\n * ```\n *\n * To share modules, you can provide a shared module map with references to the shared modules:\n *\n * ```tsx\n * <RemoteComponent\n * src=\"/nextjs-app-remote/components/header\"\n * shared={{\n * '@/components/provider': () => import('@/components/host-provider')\n * }}\n * />\n * ```\n */\nexport function RemoteComponent({\n src,\n isolate,\n credentials = 'same-origin',\n name = '__vercel_remote_component',\n shared = {},\n children,\n}: RemoteComponentProps) {\n const [data, setData] = useState<Omit<\n RemoteComponentPropsType,\n 'children'\n > | null>(null);\n const [remoteComponent, setRemoteComponent] = useState<\n React.ReactNode | Error\n >(null);\n const shadowRootContainerRef = useRef<HTMLDivElement | null>(null);\n const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(\n typeof document !== 'undefined'\n ? (document.getElementById(`shadowroot_${name}`)?.shadowRoot ?? null)\n : null,\n );\n const htmlRef = useRef<string | null>(\n typeof document !== 'undefined'\n ? (document.getElementById(`shadowroot_${name}`)?.shadowRoot?.innerHTML ??\n document.getElementById(`__REMOTE_COMPONENT${name}`)?.innerHTML)\n : null,\n );\n const endTemplateRef = useRef<HTMLTemplateElement | null>(null);\n\n useLayoutEffect(() => {\n if (isolate !== false && typeof document !== 'undefined' && !shadowRoot) {\n let shadowRootElement: ShadowRoot | null = null;\n const element = document.getElementById(`shadowroot_${name}`);\n shadowRootElement = element?.shadowRoot ?? null;\n\n if (!shadowRootElement && element) {\n // create a shadow root if it doesn't exist\n // this is a fallback for browsers that don't support declarative shadow DOM\n element.attachShadow({ mode: 'open' });\n shadowRootElement = element.shadowRoot;\n }\n\n if (shadowRootElement) {\n // remove all nodes from the shadow root except links\n shadowRootElement.querySelectorAll('*:not(link)').forEach((node) => {\n node.remove();\n });\n setShadowRoot(shadowRootElement);\n }\n }\n }, [name, isolate, shadowRoot]);\n\n const url = useMemo(() => {\n if (typeof src !== 'string')\n return new URL(\n typeof document !== 'undefined' ? location.href : 'http://localhost',\n );\n try {\n return typeof document !== 'undefined'\n ? new URL(src, location.href)\n : new URL(src);\n } catch {\n return new URL(src, 'http://localhost');\n }\n }, [src]);\n\n useEffect(() => {\n let mounted = true;\n\n startTransition(async () => {\n try {\n let html = getRemoteComponentHtml(\n htmlRef.current ??\n (endTemplateRef.current?.previousElementSibling?.tagName === 'div'\n ? endTemplateRef.current.previousElementSibling.innerHTML\n : ''),\n );\n\n if (!html && src) {\n // fetch the remote component\n const fetchInit = {\n method: 'GET',\n headers: {\n Accept: 'text/html',\n // pass the public address of the remote component to the server used for module map mutation\n 'Vercel-Remote-Component-Url': url.href,\n },\n credentials,\n } as RequestInit;\n\n const res = await fetch(url, fetchInit);\n\n if (!res.ok) {\n throw new Error(\n `Failed to fetch remote component \"${name}\": ${res.status}`,\n );\n }\n\n // get the full HTML content as a string\n const remoteHtml = await res.text();\n htmlRef.current = remoteHtml;\n html = getRemoteComponentHtml(remoteHtml);\n }\n\n // create a virtual element which will be used to parse the HTML and extract the component and RSC flight data\n const doc = document.createElement('div');\n doc.innerHTML = html;\n\n // reference to the remote component content\n const component =\n doc.querySelector(`div[data-bundle][data-route][id^=\"${name}\"]`) ??\n // fallback to the first element with the data-bundle and data-route attributes when not using a named remote component\n doc.querySelector('div[data-bundle][data-route]') ??\n // fallback to Next.js Pages Router\n doc.querySelector('div#__next');\n const nextData = JSON.parse(\n (\n doc.querySelector('#__NEXT_DATA__') ??\n doc.querySelector('#__REMOTE_NEXT_DATA__')\n )?.textContent ?? 'null',\n ) as {\n props: {\n pageProps: Record<string, unknown>;\n __REMOTE_COMPONENT__?: {\n bundle: string;\n runtime: 'turbopack' | 'webpack';\n };\n };\n page: string;\n buildId: string;\n } | null;\n\n const remoteName =\n component?.getAttribute('id')?.replace(/_ssr$/, '') ||\n (nextData ? '__next' : name);\n // reference to the RSC flight data\n const rsc = doc.querySelector(`#${remoteName}_rsc`);\n\n // reference to the bundle containing the client components\n const bundle =\n component?.getAttribute('data-bundle') ||\n nextData?.props.__REMOTE_COMPONENT__?.bundle ||\n 'default';\n\n const metadata = {\n name: remoteName,\n bundle,\n route:\n component?.getAttribute('data-route') ??\n nextData?.page ??\n DEFAULT_ROUTE,\n runtime: (component?.getAttribute('data-runtime') ??\n (nextData?.props.__REMOTE_COMPONENT__?.runtime ||\n RUNTIME_WEBPACK)) as RemoteComponentPropsType['runtime'],\n };\n\n const remoteSharedEl = doc.querySelector(`#${remoteName}_shared`);\n const remoteShared = (JSON.parse(remoteSharedEl?.textContent ?? '{}') ??\n {}) as Record<string, string>;\n remoteSharedEl?.parentElement?.removeChild(remoteSharedEl);\n\n if (!component || !(rsc || nextData)) {\n throw new Error(`Failed to find component with id \"${remoteName}\"`);\n }\n\n // reference to all link elements in the remote component\n const links = Array.from(\n doc.querySelectorAll<HTMLLinkElement>('link[href]'),\n ).map((link) => ({\n rel: link.rel,\n href: new URL(link.getAttribute('href') ?? link.href, url).href,\n as: link.getAttribute('as') || undefined,\n }));\n\n const scripts = doc.querySelectorAll<HTMLScriptElement>(\n 'script[src],script[data-src]',\n );\n\n if (mounted) {\n if (rsc) {\n document.body.appendChild(rsc);\n }\n\n const newData = {\n ...metadata,\n links,\n remoteShared,\n url: url.href,\n data: rsc\n ? (rsc.textContent ?? '').split('\\n').filter(Boolean)\n : [],\n };\n\n const result = await loadRemoteComponent({\n url: new URL(url, location.origin),\n name: remoteName,\n bundle,\n route: metadata.route,\n runtime: metadata.runtime,\n data: newData.data,\n nextData,\n scripts: Array.from(scripts).map((script) => {\n const scriptSrc =\n script.getAttribute('data-src') ||\n script.getAttribute('src') ||\n script.src;\n const { prefix, id: path } = REMOTE_COMPONENT_REGEX.exec(\n scriptSrc,\n )?.groups ?? {\n prefix: undefined,\n id: scriptSrc,\n };\n return {\n src: new URL(\n `${prefix ?? ''}${path}`.replace(\n /(?<char>[^:])(?<double>\\/\\/)/g,\n '$1/',\n ),\n url,\n ).href,\n };\n }),\n shared,\n remoteShared,\n container: shadowRoot,\n });\n\n // we need to re-check mounted state after await loadRemoteComponent\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (mounted) {\n setData(newData);\n if (result.error) {\n setRemoteComponent(result.error);\n } else {\n setRemoteComponent(result.component);\n }\n }\n }\n } catch (error) {\n if (mounted) {\n setRemoteComponent(error as Error);\n }\n }\n });\n\n return () => {\n mounted = false;\n };\n }, [url, src, isolate, credentials, name, shared, shadowRoot]);\n\n if (remoteComponent instanceof Error) {\n throw remoteComponent;\n }\n\n const metadataJson = (\n <script data-remote-component type=\"application/json\">\n {JSON.stringify({\n name: data?.name || name,\n bundle: data?.bundle || 'default',\n route: data?.route || DEFAULT_ROUTE,\n runtime: data?.runtime || RUNTIME_WEBPACK,\n })}\n </script>\n );\n const linksToRender: React.ReactNode[] | null =\n data?.links?.map((link) => (\n <link\n as={link.as as string}\n href={new URL(link.href as string, url).href}\n key={`${link.href as string}_${link.rel}`}\n rel={link.rel as string}\n />\n )) || null;\n const componentToRender = (\n <>\n {linksToRender}\n {remoteComponent ?? children}\n </>\n );\n\n if (isolate !== false) {\n const shadowRemoteComponentHtml = shadowRoot?.querySelector(\n `#__REMOTE_COMPONENT${name}`,\n );\n if (shadowRemoteComponentHtml) {\n shadowRemoteComponentHtml.remove();\n }\n\n if (shadowRoot && remoteComponent && htmlRef.current) {\n const content = shadowRoot.querySelectorAll(':not(link,style)');\n content.forEach((node) => node.remove());\n htmlRef.current = null;\n }\n\n return (\n <>\n {metadataJson}\n <div\n id={`shadowroot_${data?.name ?? name}`}\n ref={shadowRootContainerRef}\n >\n {typeof document === 'undefined' ? (\n // eslint-disable-next-line react/no-unknown-property\n <template shadowrootmode=\"open\">\n {linksToRender}\n {children}\n </template>\n ) : null}\n {shadowRoot && remoteComponent\n ? createPortal(\n <>\n {linksToRender}\n {remoteComponent}\n </>,\n shadowRoot,\n )\n : null}\n </div>\n </>\n );\n }\n\n return (\n <>\n {metadataJson}\n {componentToRender}\n <template id={`${name}_end`} ref={endTemplateRef} />\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
package/dist/react/index.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
useState,
|
|
4
|
+
useEffect,
|
|
5
|
+
useLayoutEffect,
|
|
6
|
+
useRef,
|
|
7
|
+
useMemo,
|
|
8
|
+
startTransition
|
|
9
|
+
} from "react";
|
|
3
10
|
import { createPortal } from "react-dom";
|
|
4
11
|
import {
|
|
5
12
|
loadRemoteComponent,
|
|
@@ -12,7 +19,19 @@ function getRemoteComponentHtml(html) {
|
|
|
12
19
|
return html;
|
|
13
20
|
const temp = document.createElement("div");
|
|
14
21
|
temp.innerHTML = html;
|
|
15
|
-
|
|
22
|
+
const ssrRemoteComponentContainer = temp.querySelector(
|
|
23
|
+
'div[id^="__REMOTE_COMPONENT"]'
|
|
24
|
+
);
|
|
25
|
+
if (ssrRemoteComponentContainer) {
|
|
26
|
+
return ssrRemoteComponentContainer.innerHTML;
|
|
27
|
+
}
|
|
28
|
+
const remoteComponentContainer = temp.querySelector(
|
|
29
|
+
`div[data-bundle][data-route][data-runtime][id^="__vercel_remote_component"],div[data-bundle][data-route],div#__next`
|
|
30
|
+
);
|
|
31
|
+
if (remoteComponentContainer) {
|
|
32
|
+
return `${Array.from(temp.querySelectorAll("link,script")).map((link) => link.outerHTML).join("")}${remoteComponentContainer.outerHTML}`;
|
|
33
|
+
}
|
|
34
|
+
return "";
|
|
16
35
|
}
|
|
17
36
|
function RemoteComponent({
|
|
18
37
|
src,
|
|
@@ -62,117 +81,118 @@ function RemoteComponent({
|
|
|
62
81
|
}, [src]);
|
|
63
82
|
useEffect(() => {
|
|
64
83
|
let mounted = true;
|
|
65
|
-
(async () => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const component = doc.querySelector(`div[data-bundle][data-route][id^="${name}"]`) ?? // fallback to the first element with the data-bundle and data-route attributes when not using a named remote component
|
|
90
|
-
doc.querySelector("div[data-bundle][data-route]") ?? // fallback to Next.js Pages Router
|
|
91
|
-
doc.querySelector("div#__next");
|
|
92
|
-
const nextData = JSON.parse(
|
|
93
|
-
(doc.querySelector("#__NEXT_DATA__") ?? doc.querySelector("#__REMOTE_NEXT_DATA__"))?.textContent ?? "null"
|
|
94
|
-
);
|
|
95
|
-
const remoteName = component?.getAttribute("id")?.replace(/_ssr$/, "") || (nextData ? "__next" : name);
|
|
96
|
-
const rsc = doc.querySelector(`#${remoteName}_rsc`);
|
|
97
|
-
const bundle = component?.getAttribute("data-bundle") || nextData?.props.__REMOTE_COMPONENT__?.bundle || "default";
|
|
98
|
-
const metadata = {
|
|
99
|
-
name: remoteName,
|
|
100
|
-
bundle,
|
|
101
|
-
route: component?.getAttribute("data-route") ?? nextData?.page ?? DEFAULT_ROUTE,
|
|
102
|
-
runtime: component?.getAttribute("data-runtime") ?? (nextData?.props.__REMOTE_COMPONENT__?.runtime || RUNTIME_WEBPACK)
|
|
103
|
-
};
|
|
104
|
-
const remoteSharedEl = doc.querySelector(`#${remoteName}_shared`);
|
|
105
|
-
const remoteShared = JSON.parse(remoteSharedEl?.textContent ?? "{}") ?? {};
|
|
106
|
-
remoteSharedEl?.parentElement?.removeChild(remoteSharedEl);
|
|
107
|
-
if (!component || !(rsc || nextData)) {
|
|
108
|
-
throw new Error(`Failed to find component with id "${remoteName}"`);
|
|
109
|
-
}
|
|
110
|
-
const links = Array.from(
|
|
111
|
-
doc.querySelectorAll("link[href]")
|
|
112
|
-
).map((link) => ({
|
|
113
|
-
rel: link.rel,
|
|
114
|
-
href: new URL(link.getAttribute("href") ?? link.href, url).href,
|
|
115
|
-
as: link.getAttribute("as") || void 0
|
|
116
|
-
}));
|
|
117
|
-
const scripts = doc.querySelectorAll(
|
|
118
|
-
"script[src],script[data-src]"
|
|
119
|
-
);
|
|
120
|
-
if (mounted) {
|
|
121
|
-
if (rsc) {
|
|
122
|
-
document.body.appendChild(rsc);
|
|
84
|
+
startTransition(async () => {
|
|
85
|
+
try {
|
|
86
|
+
let html = getRemoteComponentHtml(
|
|
87
|
+
htmlRef.current ?? (endTemplateRef.current?.previousElementSibling?.tagName === "div" ? endTemplateRef.current.previousElementSibling.innerHTML : "")
|
|
88
|
+
);
|
|
89
|
+
if (!html && src) {
|
|
90
|
+
const fetchInit = {
|
|
91
|
+
method: "GET",
|
|
92
|
+
headers: {
|
|
93
|
+
Accept: "text/html",
|
|
94
|
+
// pass the public address of the remote component to the server used for module map mutation
|
|
95
|
+
"Vercel-Remote-Component-Url": url.href
|
|
96
|
+
},
|
|
97
|
+
credentials
|
|
98
|
+
};
|
|
99
|
+
const res = await fetch(url, fetchInit);
|
|
100
|
+
if (!res.ok) {
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Failed to fetch remote component "${name}": ${res.status}`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
const remoteHtml = await res.text();
|
|
106
|
+
htmlRef.current = remoteHtml;
|
|
107
|
+
html = getRemoteComponentHtml(remoteHtml);
|
|
123
108
|
}
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
109
|
+
const doc = document.createElement("div");
|
|
110
|
+
doc.innerHTML = html;
|
|
111
|
+
const component = doc.querySelector(`div[data-bundle][data-route][id^="${name}"]`) ?? // fallback to the first element with the data-bundle and data-route attributes when not using a named remote component
|
|
112
|
+
doc.querySelector("div[data-bundle][data-route]") ?? // fallback to Next.js Pages Router
|
|
113
|
+
doc.querySelector("div#__next");
|
|
114
|
+
const nextData = JSON.parse(
|
|
115
|
+
(doc.querySelector("#__NEXT_DATA__") ?? doc.querySelector("#__REMOTE_NEXT_DATA__"))?.textContent ?? "null"
|
|
116
|
+
);
|
|
117
|
+
const remoteName = component?.getAttribute("id")?.replace(/_ssr$/, "") || (nextData ? "__next" : name);
|
|
118
|
+
const rsc = doc.querySelector(`#${remoteName}_rsc`);
|
|
119
|
+
const bundle = component?.getAttribute("data-bundle") || nextData?.props.__REMOTE_COMPONENT__?.bundle || "default";
|
|
120
|
+
const metadata = {
|
|
121
|
+
name: remoteName,
|
|
135
122
|
bundle,
|
|
136
|
-
route:
|
|
137
|
-
runtime:
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
123
|
+
route: component?.getAttribute("data-route") ?? nextData?.page ?? DEFAULT_ROUTE,
|
|
124
|
+
runtime: component?.getAttribute("data-runtime") ?? (nextData?.props.__REMOTE_COMPONENT__?.runtime || RUNTIME_WEBPACK)
|
|
125
|
+
};
|
|
126
|
+
const remoteSharedEl = doc.querySelector(`#${remoteName}_shared`);
|
|
127
|
+
const remoteShared = JSON.parse(remoteSharedEl?.textContent ?? "{}") ?? {};
|
|
128
|
+
remoteSharedEl?.parentElement?.removeChild(remoteSharedEl);
|
|
129
|
+
if (!component || !(rsc || nextData)) {
|
|
130
|
+
throw new Error(`Failed to find component with id "${remoteName}"`);
|
|
131
|
+
}
|
|
132
|
+
const links = Array.from(
|
|
133
|
+
doc.querySelectorAll("link[href]")
|
|
134
|
+
).map((link) => ({
|
|
135
|
+
rel: link.rel,
|
|
136
|
+
href: new URL(link.getAttribute("href") ?? link.href, url).href,
|
|
137
|
+
as: link.getAttribute("as") || void 0
|
|
138
|
+
}));
|
|
139
|
+
const scripts = doc.querySelectorAll(
|
|
140
|
+
"script[src],script[data-src]"
|
|
141
|
+
);
|
|
142
|
+
if (mounted) {
|
|
143
|
+
if (rsc) {
|
|
144
|
+
document.body.appendChild(rsc);
|
|
145
|
+
}
|
|
146
|
+
const newData = {
|
|
147
|
+
...metadata,
|
|
148
|
+
links,
|
|
149
|
+
remoteShared,
|
|
150
|
+
url: url.href,
|
|
151
|
+
data: rsc ? (rsc.textContent ?? "").split("\n").filter(Boolean) : []
|
|
152
|
+
};
|
|
153
|
+
const result = await loadRemoteComponent({
|
|
154
|
+
url: new URL(url, location.origin),
|
|
155
|
+
name: remoteName,
|
|
156
|
+
bundle,
|
|
157
|
+
route: metadata.route,
|
|
158
|
+
runtime: metadata.runtime,
|
|
159
|
+
data: newData.data,
|
|
160
|
+
nextData,
|
|
161
|
+
scripts: Array.from(scripts).map((script) => {
|
|
162
|
+
const scriptSrc = script.getAttribute("data-src") || script.getAttribute("src") || script.src;
|
|
163
|
+
const { prefix, id: path } = REMOTE_COMPONENT_REGEX.exec(
|
|
164
|
+
scriptSrc
|
|
165
|
+
)?.groups ?? {
|
|
166
|
+
prefix: void 0,
|
|
167
|
+
id: scriptSrc
|
|
168
|
+
};
|
|
169
|
+
return {
|
|
170
|
+
src: new URL(
|
|
171
|
+
`${prefix ?? ""}${path}`.replace(
|
|
172
|
+
/(?<char>[^:])(?<double>\/\/)/g,
|
|
173
|
+
"$1/"
|
|
174
|
+
),
|
|
175
|
+
url
|
|
176
|
+
).href
|
|
177
|
+
};
|
|
178
|
+
}),
|
|
179
|
+
shared,
|
|
180
|
+
remoteShared,
|
|
181
|
+
container: shadowRoot
|
|
182
|
+
});
|
|
160
183
|
if (mounted) {
|
|
184
|
+
setData(newData);
|
|
161
185
|
if (result.error) {
|
|
162
186
|
setRemoteComponent(result.error);
|
|
163
187
|
} else {
|
|
164
188
|
setRemoteComponent(result.component);
|
|
165
189
|
}
|
|
166
190
|
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
})().catch((error) => {
|
|
174
|
-
if (mounted) {
|
|
175
|
-
setRemoteComponent(error);
|
|
191
|
+
}
|
|
192
|
+
} catch (error) {
|
|
193
|
+
if (mounted) {
|
|
194
|
+
setRemoteComponent(error);
|
|
195
|
+
}
|
|
176
196
|
}
|
|
177
197
|
});
|
|
178
198
|
return () => {
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/index.tsx"],"sourcesContent":["import { useState, useEffect, useLayoutEffect, useRef, useMemo } from 'react';\nimport { createPortal } from 'react-dom';\nimport type { RemoteComponentProps as RemoteComponentPropsType } from '../shared/client/types';\nimport {\n loadRemoteComponent,\n DEFAULT_ROUTE,\n RUNTIME_WEBPACK,\n REMOTE_COMPONENT_REGEX,\n type LoadRemoteComponentProps,\n} from '../shared/client/remote-component';\n\n// patch react/jsx-runtime to support the shadowrootmode attribute on template elements\ndeclare module 'react/jsx-runtime' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n template: {\n shadowrootmode?: 'open' | 'closed';\n id?: string;\n ref?: React.Ref<HTMLTemplateElement>;\n dangerouslySetInnerHTML?: {\n __html: string;\n };\n children?: React.ReactNode;\n };\n }\n }\n}\n\nexport interface RemoteComponentProps {\n /** The source URL of the remote component. */\n src?: string;\n /** Whether to isolate the remote component using a Shadow DOM wrapper. */\n isolate?: boolean;\n /** The credentials to use for the fetch request. Defaults to `same-origin`. */\n credentials?: RequestCredentials;\n name?: string;\n /** Shared modules to include in the remote component's context. */\n shared?: LoadRemoteComponentProps['shared'];\n /** The children to use as a loading fallback until the remote component is loaded. */\n children?: React.ReactNode;\n}\n\nfunction getRemoteComponentHtml(html: string) {\n if (typeof document === 'undefined') return html;\n\n const temp = document.createElement('div');\n temp.innerHTML = html;\n\n return temp.querySelector('div[id^=\"__REMOTE_COMPONENT\"]')?.innerHTML ?? '';\n}\n\n/**\n * RemoteComponent is a React component that fetches and renders a remote component.\n * It supports SSR and can isolate the remote component in a shadow DOM.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your React application to consume a remote component from a remote application:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/react';\n *\n * export default function App() {\n * return (\n * <>\n * <h1>Welcome to My App</h1>\n * <p>This page consumes a remote component from another application.</p>\n * <RemoteComponent src=\"/nextjs-app-remote/components/header\" />\n * </>\n * );\n * }\n * ```\n *\n * To share modules, you can provide a shared module map with references to the shared modules:\n *\n * ```tsx\n * <RemoteComponent\n * src=\"/nextjs-app-remote/components/header\"\n * shared={{\n * '@/components/provider': () => import('@/components/host-provider')\n * }}\n * />\n * ```\n */\nexport function RemoteComponent({\n src,\n isolate,\n credentials = 'same-origin',\n name = '__vercel_remote_component',\n shared = {},\n children,\n}: RemoteComponentProps) {\n const [data, setData] = useState<Omit<\n RemoteComponentPropsType,\n 'children'\n > | null>(null);\n const [remoteComponent, setRemoteComponent] = useState<\n React.ReactNode | Error\n >(null);\n const shadowRootContainerRef = useRef<HTMLDivElement | null>(null);\n const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(\n typeof document !== 'undefined'\n ? (document.getElementById(`shadowroot_${name}`)?.shadowRoot ?? null)\n : null,\n );\n const htmlRef = useRef<string | null>(\n typeof document !== 'undefined'\n ? (document.getElementById(`shadowroot_${name}`)?.shadowRoot?.innerHTML ??\n document.getElementById(`__REMOTE_COMPONENT${name}`)?.innerHTML)\n : null,\n );\n const endTemplateRef = useRef<HTMLTemplateElement | null>(null);\n\n useLayoutEffect(() => {\n if (isolate !== false && typeof document !== 'undefined' && !shadowRoot) {\n let shadowRootElement: ShadowRoot | null = null;\n const element = document.getElementById(`shadowroot_${name}`);\n shadowRootElement = element?.shadowRoot ?? null;\n\n if (!shadowRootElement && element) {\n // create a shadow root if it doesn't exist\n // this is a fallback for browsers that don't support declarative shadow DOM\n element.attachShadow({ mode: 'open' });\n shadowRootElement = element.shadowRoot;\n }\n\n if (shadowRootElement) {\n // remove all nodes from the shadow root except links\n shadowRootElement.querySelectorAll('*:not(link)').forEach((node) => {\n node.remove();\n });\n setShadowRoot(shadowRootElement);\n }\n }\n }, [name, isolate, shadowRoot]);\n\n const url = useMemo(() => {\n if (typeof src !== 'string')\n return new URL(\n typeof document !== 'undefined' ? location.href : 'http://localhost',\n );\n try {\n return typeof document !== 'undefined'\n ? new URL(src, location.href)\n : new URL(src);\n } catch {\n return new URL(src, 'http://localhost');\n }\n }, [src]);\n\n useEffect(() => {\n let mounted = true;\n\n (async () => {\n let html = getRemoteComponentHtml(\n htmlRef.current ??\n (endTemplateRef.current?.previousElementSibling?.tagName === 'div'\n ? endTemplateRef.current.previousElementSibling.innerHTML\n : ''),\n );\n\n if (!html && src) {\n // fetch the remote component\n const fetchInit = {\n method: 'GET',\n headers: {\n Accept: 'text/html',\n // pass the public address of the remote component to the server used for module map mutation\n 'Vercel-Remote-Component-Url': url.href,\n },\n credentials,\n } as RequestInit;\n\n const res = await fetch(url, fetchInit);\n\n if (!res.ok) {\n throw new Error(\n `Failed to fetch remote component \"${name}\": ${res.status}`,\n );\n }\n\n // get the full HTML content as a string\n html = await res.text();\n }\n\n // create a virtual element which will be used to parse the HTML and extract the component and RSC flight data\n const doc = document.createElement('div');\n doc.innerHTML = html;\n\n // reference to the remote component content\n const component =\n doc.querySelector(`div[data-bundle][data-route][id^=\"${name}\"]`) ??\n // fallback to the first element with the data-bundle and data-route attributes when not using a named remote component\n doc.querySelector('div[data-bundle][data-route]') ??\n // fallback to Next.js Pages Router\n doc.querySelector('div#__next');\n const nextData = JSON.parse(\n (\n doc.querySelector('#__NEXT_DATA__') ??\n doc.querySelector('#__REMOTE_NEXT_DATA__')\n )?.textContent ?? 'null',\n ) as {\n props: {\n pageProps: Record<string, unknown>;\n __REMOTE_COMPONENT__?: {\n bundle: string;\n runtime: 'turbopack' | 'webpack';\n };\n };\n page: string;\n buildId: string;\n } | null;\n\n const remoteName =\n component?.getAttribute('id')?.replace(/_ssr$/, '') ||\n (nextData ? '__next' : name);\n // reference to the RSC flight data\n const rsc = doc.querySelector(`#${remoteName}_rsc`);\n\n // reference to the bundle containing the client components\n const bundle =\n component?.getAttribute('data-bundle') ||\n nextData?.props.__REMOTE_COMPONENT__?.bundle ||\n 'default';\n\n const metadata = {\n name: remoteName,\n bundle,\n route:\n component?.getAttribute('data-route') ??\n nextData?.page ??\n DEFAULT_ROUTE,\n runtime: (component?.getAttribute('data-runtime') ??\n (nextData?.props.__REMOTE_COMPONENT__?.runtime ||\n RUNTIME_WEBPACK)) as RemoteComponentPropsType['runtime'],\n };\n\n const remoteSharedEl = doc.querySelector(`#${remoteName}_shared`);\n const remoteShared = (JSON.parse(remoteSharedEl?.textContent ?? '{}') ??\n {}) as Record<string, string>;\n remoteSharedEl?.parentElement?.removeChild(remoteSharedEl);\n\n if (!component || !(rsc || nextData)) {\n throw new Error(`Failed to find component with id \"${remoteName}\"`);\n }\n\n // reference to all link elements in the remote component\n const links = Array.from(\n doc.querySelectorAll<HTMLLinkElement>('link[href]'),\n ).map((link) => ({\n rel: link.rel,\n href: new URL(link.getAttribute('href') ?? link.href, url).href,\n as: link.getAttribute('as') || undefined,\n }));\n\n const scripts = doc.querySelectorAll<HTMLScriptElement>(\n 'script[src],script[data-src]',\n );\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (mounted) {\n if (rsc) {\n document.body.appendChild(rsc);\n }\n\n const newData = {\n ...metadata,\n links,\n remoteShared,\n url: url.href,\n data: rsc ? (rsc.textContent ?? '').split('\\n').filter(Boolean) : [],\n };\n setData(newData);\n\n loadRemoteComponent({\n url: new URL(url, location.origin),\n name,\n bundle,\n route: metadata.route,\n runtime: metadata.runtime,\n data: newData.data,\n nextData,\n scripts: Array.from(scripts).map((script) => {\n const scriptSrc =\n script.getAttribute('data-src') ||\n script.getAttribute('src') ||\n script.src;\n const { prefix, id: path } = REMOTE_COMPONENT_REGEX.exec(scriptSrc)\n ?.groups ?? {\n prefix: undefined,\n id: scriptSrc,\n };\n return {\n src: new URL(\n `${prefix ?? ''}${path}`.replace(\n /(?<char>[^:])(?<double>\\/\\/)/g,\n '$1/',\n ),\n url,\n ).href,\n };\n }),\n shared,\n remoteShared,\n container: shadowRoot,\n })\n .then((result) => {\n if (mounted) {\n if (result.error) {\n setRemoteComponent(result.error);\n } else {\n setRemoteComponent(result.component);\n }\n }\n })\n .catch((error: Error) => {\n if (mounted) {\n setRemoteComponent(error);\n }\n });\n }\n })().catch((error: Error) => {\n if (mounted) {\n setRemoteComponent(error);\n }\n });\n\n return () => {\n mounted = false;\n };\n }, [url, src, isolate, credentials, name, shared, shadowRoot]);\n\n if (remoteComponent instanceof Error) {\n throw remoteComponent;\n }\n\n const metadataJson = (\n <script data-remote-component type=\"application/json\">\n {JSON.stringify({\n name: data?.name || name,\n bundle: data?.bundle || 'default',\n route: data?.route || DEFAULT_ROUTE,\n runtime: data?.runtime || RUNTIME_WEBPACK,\n })}\n </script>\n );\n const linksToRender: React.ReactNode[] | null =\n data?.links?.map((link) => (\n <link\n as={link.as as string}\n href={new URL(link.href as string, url).href}\n key={`${link.href as string}_${link.rel}`}\n rel={link.rel as string}\n />\n )) || null;\n const componentToRender = (\n <>\n {linksToRender}\n {remoteComponent ?? children}\n </>\n );\n\n if (isolate !== false) {\n const shadowRemoteComponentHtml = shadowRoot?.querySelector(\n `#__REMOTE_COMPONENT${name}`,\n );\n if (shadowRemoteComponentHtml) {\n shadowRemoteComponentHtml.remove();\n }\n\n if (shadowRoot && remoteComponent && htmlRef.current) {\n const content = shadowRoot.querySelectorAll(':not(link,style)');\n content.forEach((node) => node.remove());\n htmlRef.current = null;\n }\n\n return (\n <>\n {metadataJson}\n <div\n id={`shadowroot_${data?.name ?? name}`}\n ref={shadowRootContainerRef}\n >\n {typeof document === 'undefined' ? (\n // eslint-disable-next-line react/no-unknown-property\n <template shadowrootmode=\"open\">\n {linksToRender}\n {children}\n </template>\n ) : null}\n {shadowRoot && remoteComponent\n ? createPortal(\n <>\n {linksToRender}\n {remoteComponent}\n </>,\n shadowRoot,\n )\n : null}\n </div>\n </>\n );\n }\n\n return (\n <>\n {metadataJson}\n {componentToRender}\n <template id={`${name}_end`} ref={endTemplateRef} />\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/index.tsx"],"sourcesContent":["import {\n useState,\n useEffect,\n useLayoutEffect,\n useRef,\n useMemo,\n startTransition,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport type { RemoteComponentProps as RemoteComponentPropsType } from '../shared/client/types';\nimport {\n loadRemoteComponent,\n DEFAULT_ROUTE,\n RUNTIME_WEBPACK,\n REMOTE_COMPONENT_REGEX,\n type LoadRemoteComponentProps,\n} from '../shared/client/remote-component';\n\n// patch react/jsx-runtime to support the shadowrootmode attribute on template elements\ndeclare module 'react/jsx-runtime' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n template: {\n shadowrootmode?: 'open' | 'closed';\n id?: string;\n ref?: React.Ref<HTMLTemplateElement>;\n dangerouslySetInnerHTML?: {\n __html: string;\n };\n children?: React.ReactNode;\n };\n }\n }\n}\n\nexport interface RemoteComponentProps {\n /** The source URL of the remote component. */\n src?: string;\n /** Whether to isolate the remote component using a Shadow DOM wrapper. */\n isolate?: boolean;\n /** The credentials to use for the fetch request. Defaults to `same-origin`. */\n credentials?: RequestCredentials;\n name?: string;\n /** Shared modules to include in the remote component's context. */\n shared?: LoadRemoteComponentProps['shared'];\n /** The children to use as a loading fallback until the remote component is loaded. */\n children?: React.ReactNode;\n}\n\nfunction getRemoteComponentHtml(html: string) {\n if (typeof document === 'undefined') return html;\n\n const temp = document.createElement('div');\n temp.innerHTML = html;\n\n // used by the Next.js Pages Router remote as a wrapper\n const ssrRemoteComponentContainer = temp.querySelector(\n 'div[id^=\"__REMOTE_COMPONENT\"]',\n );\n if (ssrRemoteComponentContainer) {\n return ssrRemoteComponentContainer.innerHTML;\n }\n\n // remote component content\n const remoteComponentContainer = temp.querySelector(\n `div[data-bundle][data-route][data-runtime][id^=\"__vercel_remote_component\"],div[data-bundle][data-route],div#__next`,\n );\n if (remoteComponentContainer) {\n return `${Array.from(temp.querySelectorAll('link,script'))\n .map((link) => link.outerHTML)\n .join('')}${remoteComponentContainer.outerHTML}`;\n }\n\n return '';\n}\n\n/**\n * RemoteComponent is a React component that fetches and renders a remote component.\n * It supports SSR and can isolate the remote component in a shadow DOM.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your React application to consume a remote component from a remote application:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/react';\n *\n * export default function App() {\n * return (\n * <>\n * <h1>Welcome to My App</h1>\n * <p>This page consumes a remote component from another application.</p>\n * <RemoteComponent src=\"/nextjs-app-remote/components/header\" />\n * </>\n * );\n * }\n * ```\n *\n * To share modules, you can provide a shared module map with references to the shared modules:\n *\n * ```tsx\n * <RemoteComponent\n * src=\"/nextjs-app-remote/components/header\"\n * shared={{\n * '@/components/provider': () => import('@/components/host-provider')\n * }}\n * />\n * ```\n */\nexport function RemoteComponent({\n src,\n isolate,\n credentials = 'same-origin',\n name = '__vercel_remote_component',\n shared = {},\n children,\n}: RemoteComponentProps) {\n const [data, setData] = useState<Omit<\n RemoteComponentPropsType,\n 'children'\n > | null>(null);\n const [remoteComponent, setRemoteComponent] = useState<\n React.ReactNode | Error\n >(null);\n const shadowRootContainerRef = useRef<HTMLDivElement | null>(null);\n const [shadowRoot, setShadowRoot] = useState<ShadowRoot | null>(\n typeof document !== 'undefined'\n ? (document.getElementById(`shadowroot_${name}`)?.shadowRoot ?? null)\n : null,\n );\n const htmlRef = useRef<string | null>(\n typeof document !== 'undefined'\n ? (document.getElementById(`shadowroot_${name}`)?.shadowRoot?.innerHTML ??\n document.getElementById(`__REMOTE_COMPONENT${name}`)?.innerHTML)\n : null,\n );\n const endTemplateRef = useRef<HTMLTemplateElement | null>(null);\n\n useLayoutEffect(() => {\n if (isolate !== false && typeof document !== 'undefined' && !shadowRoot) {\n let shadowRootElement: ShadowRoot | null = null;\n const element = document.getElementById(`shadowroot_${name}`);\n shadowRootElement = element?.shadowRoot ?? null;\n\n if (!shadowRootElement && element) {\n // create a shadow root if it doesn't exist\n // this is a fallback for browsers that don't support declarative shadow DOM\n element.attachShadow({ mode: 'open' });\n shadowRootElement = element.shadowRoot;\n }\n\n if (shadowRootElement) {\n // remove all nodes from the shadow root except links\n shadowRootElement.querySelectorAll('*:not(link)').forEach((node) => {\n node.remove();\n });\n setShadowRoot(shadowRootElement);\n }\n }\n }, [name, isolate, shadowRoot]);\n\n const url = useMemo(() => {\n if (typeof src !== 'string')\n return new URL(\n typeof document !== 'undefined' ? location.href : 'http://localhost',\n );\n try {\n return typeof document !== 'undefined'\n ? new URL(src, location.href)\n : new URL(src);\n } catch {\n return new URL(src, 'http://localhost');\n }\n }, [src]);\n\n useEffect(() => {\n let mounted = true;\n\n startTransition(async () => {\n try {\n let html = getRemoteComponentHtml(\n htmlRef.current ??\n (endTemplateRef.current?.previousElementSibling?.tagName === 'div'\n ? endTemplateRef.current.previousElementSibling.innerHTML\n : ''),\n );\n\n if (!html && src) {\n // fetch the remote component\n const fetchInit = {\n method: 'GET',\n headers: {\n Accept: 'text/html',\n // pass the public address of the remote component to the server used for module map mutation\n 'Vercel-Remote-Component-Url': url.href,\n },\n credentials,\n } as RequestInit;\n\n const res = await fetch(url, fetchInit);\n\n if (!res.ok) {\n throw new Error(\n `Failed to fetch remote component \"${name}\": ${res.status}`,\n );\n }\n\n // get the full HTML content as a string\n const remoteHtml = await res.text();\n htmlRef.current = remoteHtml;\n html = getRemoteComponentHtml(remoteHtml);\n }\n\n // create a virtual element which will be used to parse the HTML and extract the component and RSC flight data\n const doc = document.createElement('div');\n doc.innerHTML = html;\n\n // reference to the remote component content\n const component =\n doc.querySelector(`div[data-bundle][data-route][id^=\"${name}\"]`) ??\n // fallback to the first element with the data-bundle and data-route attributes when not using a named remote component\n doc.querySelector('div[data-bundle][data-route]') ??\n // fallback to Next.js Pages Router\n doc.querySelector('div#__next');\n const nextData = JSON.parse(\n (\n doc.querySelector('#__NEXT_DATA__') ??\n doc.querySelector('#__REMOTE_NEXT_DATA__')\n )?.textContent ?? 'null',\n ) as {\n props: {\n pageProps: Record<string, unknown>;\n __REMOTE_COMPONENT__?: {\n bundle: string;\n runtime: 'turbopack' | 'webpack';\n };\n };\n page: string;\n buildId: string;\n } | null;\n\n const remoteName =\n component?.getAttribute('id')?.replace(/_ssr$/, '') ||\n (nextData ? '__next' : name);\n // reference to the RSC flight data\n const rsc = doc.querySelector(`#${remoteName}_rsc`);\n\n // reference to the bundle containing the client components\n const bundle =\n component?.getAttribute('data-bundle') ||\n nextData?.props.__REMOTE_COMPONENT__?.bundle ||\n 'default';\n\n const metadata = {\n name: remoteName,\n bundle,\n route:\n component?.getAttribute('data-route') ??\n nextData?.page ??\n DEFAULT_ROUTE,\n runtime: (component?.getAttribute('data-runtime') ??\n (nextData?.props.__REMOTE_COMPONENT__?.runtime ||\n RUNTIME_WEBPACK)) as RemoteComponentPropsType['runtime'],\n };\n\n const remoteSharedEl = doc.querySelector(`#${remoteName}_shared`);\n const remoteShared = (JSON.parse(remoteSharedEl?.textContent ?? '{}') ??\n {}) as Record<string, string>;\n remoteSharedEl?.parentElement?.removeChild(remoteSharedEl);\n\n if (!component || !(rsc || nextData)) {\n throw new Error(`Failed to find component with id \"${remoteName}\"`);\n }\n\n // reference to all link elements in the remote component\n const links = Array.from(\n doc.querySelectorAll<HTMLLinkElement>('link[href]'),\n ).map((link) => ({\n rel: link.rel,\n href: new URL(link.getAttribute('href') ?? link.href, url).href,\n as: link.getAttribute('as') || undefined,\n }));\n\n const scripts = doc.querySelectorAll<HTMLScriptElement>(\n 'script[src],script[data-src]',\n );\n\n if (mounted) {\n if (rsc) {\n document.body.appendChild(rsc);\n }\n\n const newData = {\n ...metadata,\n links,\n remoteShared,\n url: url.href,\n data: rsc\n ? (rsc.textContent ?? '').split('\\n').filter(Boolean)\n : [],\n };\n\n const result = await loadRemoteComponent({\n url: new URL(url, location.origin),\n name: remoteName,\n bundle,\n route: metadata.route,\n runtime: metadata.runtime,\n data: newData.data,\n nextData,\n scripts: Array.from(scripts).map((script) => {\n const scriptSrc =\n script.getAttribute('data-src') ||\n script.getAttribute('src') ||\n script.src;\n const { prefix, id: path } = REMOTE_COMPONENT_REGEX.exec(\n scriptSrc,\n )?.groups ?? {\n prefix: undefined,\n id: scriptSrc,\n };\n return {\n src: new URL(\n `${prefix ?? ''}${path}`.replace(\n /(?<char>[^:])(?<double>\\/\\/)/g,\n '$1/',\n ),\n url,\n ).href,\n };\n }),\n shared,\n remoteShared,\n container: shadowRoot,\n });\n\n // we need to re-check mounted state after await loadRemoteComponent\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (mounted) {\n setData(newData);\n if (result.error) {\n setRemoteComponent(result.error);\n } else {\n setRemoteComponent(result.component);\n }\n }\n }\n } catch (error) {\n if (mounted) {\n setRemoteComponent(error as Error);\n }\n }\n });\n\n return () => {\n mounted = false;\n };\n }, [url, src, isolate, credentials, name, shared, shadowRoot]);\n\n if (remoteComponent instanceof Error) {\n throw remoteComponent;\n }\n\n const metadataJson = (\n <script data-remote-component type=\"application/json\">\n {JSON.stringify({\n name: data?.name || name,\n bundle: data?.bundle || 'default',\n route: data?.route || DEFAULT_ROUTE,\n runtime: data?.runtime || RUNTIME_WEBPACK,\n })}\n </script>\n );\n const linksToRender: React.ReactNode[] | null =\n data?.links?.map((link) => (\n <link\n as={link.as as string}\n href={new URL(link.href as string, url).href}\n key={`${link.href as string}_${link.rel}`}\n rel={link.rel as string}\n />\n )) || null;\n const componentToRender = (\n <>\n {linksToRender}\n {remoteComponent ?? children}\n </>\n );\n\n if (isolate !== false) {\n const shadowRemoteComponentHtml = shadowRoot?.querySelector(\n `#__REMOTE_COMPONENT${name}`,\n );\n if (shadowRemoteComponentHtml) {\n shadowRemoteComponentHtml.remove();\n }\n\n if (shadowRoot && remoteComponent && htmlRef.current) {\n const content = shadowRoot.querySelectorAll(':not(link,style)');\n content.forEach((node) => node.remove());\n htmlRef.current = null;\n }\n\n return (\n <>\n {metadataJson}\n <div\n id={`shadowroot_${data?.name ?? name}`}\n ref={shadowRootContainerRef}\n >\n {typeof document === 'undefined' ? (\n // eslint-disable-next-line react/no-unknown-property\n <template shadowrootmode=\"open\">\n {linksToRender}\n {children}\n </template>\n ) : null}\n {shadowRoot && remoteComponent\n ? createPortal(\n <>\n {linksToRender}\n {remoteComponent}\n </>,\n shadowRoot,\n )\n : null}\n </div>\n </>\n );\n }\n\n return (\n <>\n {metadataJson}\n {componentToRender}\n <template id={`${name}_end`} ref={endTemplateRef} />\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -179,7 +179,19 @@ async function setupWebpackRuntime(runtime, scripts = [], url = new URL(location
|
|
|
179
179
|
}
|
|
180
180
|
self.__remote_bundle_url__[bundle ?? "default"] = url;
|
|
181
181
|
self.__webpack_get_script_filename__ = () => null;
|
|
182
|
-
await initializeSharedModules(
|
|
182
|
+
await initializeSharedModules(
|
|
183
|
+
bundle ?? "default",
|
|
184
|
+
// include all core modules as shared
|
|
185
|
+
{
|
|
186
|
+
react: async () => (await import("react")).default,
|
|
187
|
+
"react-dom": async () => (await import("react-dom")).default,
|
|
188
|
+
"react/jsx-dev-runtime": async () => (await import("react/jsx-dev-runtime")).default,
|
|
189
|
+
"react/jsx-runtime": async () => (await import("react/jsx-runtime")).default,
|
|
190
|
+
"react-dom/client": async () => (await import("react-dom/client")).default,
|
|
191
|
+
...shared
|
|
192
|
+
},
|
|
193
|
+
remoteShared
|
|
194
|
+
);
|
|
183
195
|
if (typeof self.__webpack_require__ !== "function" || self.__webpack_require_type__ !== "turbopack") {
|
|
184
196
|
if (!self.__original_webpack_require__ && !self.__original_webpack_chunk_load__) {
|
|
185
197
|
self.__original_webpack_chunk_load__ = self.__webpack_chunk_load__;
|
|
@@ -643,7 +655,19 @@ async function loadRemoteComponent({
|
|
|
643
655
|
}
|
|
644
656
|
}
|
|
645
657
|
async function loadRSCComponent(name, data) {
|
|
646
|
-
|
|
658
|
+
let createFromReadableStream;
|
|
659
|
+
try {
|
|
660
|
+
const { createFromReadableStream: _createFromReadableStream } = await import("next/dist/compiled/react-server-dom-webpack/client.browser");
|
|
661
|
+
createFromReadableStream = _createFromReadableStream;
|
|
662
|
+
} catch {
|
|
663
|
+
const {
|
|
664
|
+
default: { createFromReadableStream: _createFromReadableStream }
|
|
665
|
+
} = await import("react-server-dom-webpack/client.browser");
|
|
666
|
+
createFromReadableStream = _createFromReadableStream;
|
|
667
|
+
}
|
|
668
|
+
if (typeof createFromReadableStream !== "function") {
|
|
669
|
+
throw new Error("Failed to import react-server-dom-webpack");
|
|
670
|
+
}
|
|
647
671
|
const stream = createRSCStream(name, data);
|
|
648
672
|
const component = createFromReadableStream(stream);
|
|
649
673
|
return { component };
|