remote-components 0.0.22 → 0.0.23
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 +71 -21
- package/dist/html/host.cjs.map +1 -1
- package/dist/html/host.js +71 -21
- package/dist/html/host.js.map +1 -1
- package/dist/internal/next/host/app-router-client.cjs +50 -9
- package/dist/internal/next/host/app-router-client.cjs.map +1 -1
- package/dist/internal/next/host/app-router-client.d.ts +2 -2
- package/dist/internal/next/host/app-router-client.js +51 -10
- package/dist/internal/next/host/app-router-client.js.map +1 -1
- package/dist/internal/shared/client/remote-component.d.ts +2 -0
- package/dist/internal/shared/ssr/dom-flight.cjs +9 -17
- package/dist/internal/shared/ssr/dom-flight.cjs.map +1 -1
- package/dist/internal/shared/ssr/dom-flight.js +9 -17
- package/dist/internal/shared/ssr/dom-flight.js.map +1 -1
- package/dist/internal/shared/ssr/fetch-remote-component.cjs +2 -1
- package/dist/internal/shared/ssr/fetch-remote-component.cjs.map +1 -1
- package/dist/internal/shared/ssr/fetch-remote-component.js +2 -1
- package/dist/internal/shared/ssr/fetch-remote-component.js.map +1 -1
- package/dist/next/host/app-router-server.cjs +4 -0
- package/dist/next/host/app-router-server.cjs.map +1 -1
- package/dist/next/host/app-router-server.d.ts +5 -1
- package/dist/next/host/app-router-server.js +4 -0
- package/dist/next/host/app-router-server.js.map +1 -1
- package/dist/next/host/client/index.d.ts +1 -1
- package/dist/next/host/pages-router-server.cjs +11 -4
- package/dist/next/host/pages-router-server.cjs.map +1 -1
- package/dist/next/host/pages-router-server.d.ts +3 -0
- package/dist/next/host/pages-router-server.js +11 -4
- package/dist/next/host/pages-router-server.js.map +1 -1
- package/dist/react/index.cjs +53 -18
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.ts +6 -2
- package/dist/react/index.js +53 -18
- package/dist/react/index.js.map +1 -1
- package/dist/{types-7425dfe1.d.ts → types-b8210fd3.d.ts} +2 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/shared/ssr/fetch-remote-component.ts"],"sourcesContent":["import { type DefaultTreeAdapterMap, Parser } from 'parse5';\nimport { visit } from '#internal/shared/ssr/dom-flight';\nimport type { RemoteComponentMetadata } from './types';\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\n/**\n * Resolves the base URL that should be used to fetch the remote component.\n * This function must support local development, Vercel environments, and SSG\n * and dynamic rendering.\n */\nexport function getBaseUrl(reqHeaders: Headers): string {\n const host = reqHeaders.get('host');\n if (host) {\n return host.startsWith('localhost') ? `http://${host}` : `https://${host}`;\n }\n const forwardedHost = reqHeaders.get('x-forwarded-host');\n if (forwardedHost) {\n return `https://${forwardedHost}`;\n }\n if (process.env.VERCEL_PROJECT_PRODUCTION_URL) {\n return `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`;\n }\n // TODO: Resolve the correct URL in local development when the proxy is not running.\n return `http://localhost:${process.env.MFE_LOCAL_PROXY_PORT || 3024}`;\n}\n\nexport async function fetchRemoteComponent(\n src: string | URL,\n headers: Headers = new Headers(),\n options: {\n name?: string;\n rsc?: boolean;\n } = {\n rsc: false,\n },\n) {\n const url = new URL(src, getBaseUrl(headers));\n\n const fetchInit = {\n method: 'GET',\n headers: {\n // pass all headers to the remote component\n ...Object.fromEntries(headers.entries()),\n Accept: 'text/html',\n },\n credentials: 'include',\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 \"${url.href}\": ${res.status}`,\n );\n }\n\n // create a parser for the HTML response\n const parser = Parser.getFragmentParser<DefaultTreeAdapterMap>();\n\n if (!res.body) {\n throw new Error(`No response body for remote component \"${url.href}\"`);\n }\n\n const decoder = new TextDecoder();\n // read the response body as a stream and parse it using the parse5 fragment parser\n for await (const chunk of res.body as unknown as AsyncIterable<Uint8Array>) {\n parser.tokenizer.write(decoder.decode(chunk), false);\n }\n const fragment = parser.getFragment();\n\n let metadata: RemoteComponentMetadata = {\n bundle: CURRENT_ZONE ?? '__vercel_remote_component',\n route: '/',\n runtime: 'webpack',\n id: '__vercel_remote_component',\n };\n let remoteShared: Record<string, string> = {};\n const scripts: { src: string }[] = [];\n const links: Record<string, string | boolean>[] = [];\n const hydrationData: string[] = [];\n let nextData:\n | {\n props: { pageProps: Record<string, unknown> };\n buildId?: string;\n }\n | undefined;\n let html = '';\n\n // convert the parsed HTML fragment into an RSC flight data\n // and extract the metadata, scripts, links and remote component RSC flight data\n const rsc = visit(fragment, {\n url,\n name:
|
|
1
|
+
{"version":3,"sources":["../../../../src/shared/ssr/fetch-remote-component.ts"],"sourcesContent":["import { type DefaultTreeAdapterMap, Parser } from 'parse5';\nimport { visit } from '#internal/shared/ssr/dom-flight';\nimport type { RemoteComponentMetadata } from './types';\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\n/**\n * Resolves the base URL that should be used to fetch the remote component.\n * This function must support local development, Vercel environments, and SSG\n * and dynamic rendering.\n */\nexport function getBaseUrl(reqHeaders: Headers): string {\n const host = reqHeaders.get('host');\n if (host) {\n return host.startsWith('localhost') ? `http://${host}` : `https://${host}`;\n }\n const forwardedHost = reqHeaders.get('x-forwarded-host');\n if (forwardedHost) {\n return `https://${forwardedHost}`;\n }\n if (process.env.VERCEL_PROJECT_PRODUCTION_URL) {\n return `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`;\n }\n // TODO: Resolve the correct URL in local development when the proxy is not running.\n return `http://localhost:${process.env.MFE_LOCAL_PROXY_PORT || 3024}`;\n}\n\nexport async function fetchRemoteComponent(\n src: string | URL,\n headers: Headers = new Headers(),\n options: {\n name?: string;\n rsc?: boolean;\n } = {\n rsc: false,\n },\n) {\n const url = new URL(src, getBaseUrl(headers));\n\n const fetchInit = {\n method: 'GET',\n headers: {\n // pass all headers to the remote component\n ...Object.fromEntries(headers.entries()),\n Accept: 'text/html',\n },\n credentials: 'include',\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 \"${url.href}\": ${res.status}`,\n );\n }\n\n // create a parser for the HTML response\n const parser = Parser.getFragmentParser<DefaultTreeAdapterMap>();\n\n if (!res.body) {\n throw new Error(`No response body for remote component \"${url.href}\"`);\n }\n\n const decoder = new TextDecoder();\n // read the response body as a stream and parse it using the parse5 fragment parser\n for await (const chunk of res.body as unknown as AsyncIterable<Uint8Array>) {\n parser.tokenizer.write(decoder.decode(chunk), false);\n }\n const fragment = parser.getFragment();\n\n let metadata: RemoteComponentMetadata = {\n bundle: CURRENT_ZONE ?? '__vercel_remote_component',\n route: '/',\n runtime: 'webpack',\n id: '__vercel_remote_component',\n };\n let remoteShared: Record<string, string> = {};\n const scripts: { src: string }[] = [];\n const links: Record<string, string | boolean>[] = [];\n const hydrationData: string[] = [];\n let nextData:\n | {\n props: { pageProps: Record<string, unknown> };\n buildId?: string;\n }\n | undefined;\n let html = '';\n\n const remoteName =\n options.name || url.hash ? url.hash.substring(1) : undefined;\n // convert the parsed HTML fragment into an RSC flight data\n // and extract the metadata, scripts, links and remote component RSC flight data\n const rsc = visit(fragment, {\n url,\n name: remoteName,\n onMetadata(_metadata) {\n metadata = _metadata;\n },\n onScript(attrs) {\n if (!scripts.find((it) => it.src === attrs.src)) {\n scripts.push({\n src: new URL(attrs.src as string, new URL(url).origin).href,\n });\n }\n },\n onLink(attrs) {\n const relativeAttrs = {\n ...attrs,\n href: new URL(attrs.href as string, new URL(url).origin).href,\n };\n if (\n !links.find(\n (it) => it.href === relativeAttrs.href && it.rel === attrs.rel,\n )\n ) {\n links.push(relativeAttrs);\n }\n },\n onRSC(chunk) {\n hydrationData.push(chunk);\n },\n onNextData(data) {\n nextData = data;\n\n // use the Next.js Pages Router props data to extract remote component metadata\n if (data.props.__REMOTE_COMPONENT__) {\n Object.assign(metadata, data.props.__REMOTE_COMPONENT__);\n // only a singleton remote component is supported per page when using the Next.js Pages Router\n metadata.id = '__next';\n metadata.route = data.page ?? '/';\n }\n },\n onHTML(_html) {\n if (!html.includes(_html)) {\n html += _html;\n }\n },\n onShared(_shared) {\n remoteShared = _shared;\n },\n });\n\n let component: React.ReactNode | undefined;\n\n // only create a React component if requested\n if (options.rsc) {\n // RSC flight data for the static HTML in a single RSC line\n const componentRSC = `0:${JSON.stringify(rsc)}\\n`;\n\n const { createFromReadableStream } = await import(\n 'next/dist/compiled/react-server-dom-webpack/client.edge'\n );\n\n // create a React tree from the RSC flight data\n component = await createFromReadableStream(\n new ReadableStream({\n type: 'bytes',\n start(controller) {\n const encoder = new TextEncoder();\n controller.enqueue(encoder.encode(componentRSC));\n controller.close();\n },\n }),\n {\n serverConsumerManifest: {\n moduleLoading: {\n prefix: url.origin,\n crossOrigin: true,\n },\n moduleMap: {},\n },\n },\n );\n }\n\n const name = metadata.id.replace(/_ssr$/, '');\n return {\n name,\n url,\n metadata,\n rsc,\n scripts,\n links,\n hydrationData,\n nextData,\n component,\n html,\n remoteShared,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -55,9 +55,10 @@ async function fetchRemoteComponent(src, headers = new Headers(), options = {
|
|
|
55
55
|
const hydrationData = [];
|
|
56
56
|
let nextData;
|
|
57
57
|
let html = "";
|
|
58
|
+
const remoteName = options.name || url.hash ? url.hash.substring(1) : void 0;
|
|
58
59
|
const rsc = visit(fragment, {
|
|
59
60
|
url,
|
|
60
|
-
name:
|
|
61
|
+
name: remoteName,
|
|
61
62
|
onMetadata(_metadata) {
|
|
62
63
|
metadata = _metadata;
|
|
63
64
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/shared/ssr/fetch-remote-component.ts"],"sourcesContent":["import { type DefaultTreeAdapterMap, Parser } from 'parse5';\nimport { visit } from '#internal/shared/ssr/dom-flight';\nimport type { RemoteComponentMetadata } from './types';\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\n/**\n * Resolves the base URL that should be used to fetch the remote component.\n * This function must support local development, Vercel environments, and SSG\n * and dynamic rendering.\n */\nexport function getBaseUrl(reqHeaders: Headers): string {\n const host = reqHeaders.get('host');\n if (host) {\n return host.startsWith('localhost') ? `http://${host}` : `https://${host}`;\n }\n const forwardedHost = reqHeaders.get('x-forwarded-host');\n if (forwardedHost) {\n return `https://${forwardedHost}`;\n }\n if (process.env.VERCEL_PROJECT_PRODUCTION_URL) {\n return `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`;\n }\n // TODO: Resolve the correct URL in local development when the proxy is not running.\n return `http://localhost:${process.env.MFE_LOCAL_PROXY_PORT || 3024}`;\n}\n\nexport async function fetchRemoteComponent(\n src: string | URL,\n headers: Headers = new Headers(),\n options: {\n name?: string;\n rsc?: boolean;\n } = {\n rsc: false,\n },\n) {\n const url = new URL(src, getBaseUrl(headers));\n\n const fetchInit = {\n method: 'GET',\n headers: {\n // pass all headers to the remote component\n ...Object.fromEntries(headers.entries()),\n Accept: 'text/html',\n },\n credentials: 'include',\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 \"${url.href}\": ${res.status}`,\n );\n }\n\n // create a parser for the HTML response\n const parser = Parser.getFragmentParser<DefaultTreeAdapterMap>();\n\n if (!res.body) {\n throw new Error(`No response body for remote component \"${url.href}\"`);\n }\n\n const decoder = new TextDecoder();\n // read the response body as a stream and parse it using the parse5 fragment parser\n for await (const chunk of res.body as unknown as AsyncIterable<Uint8Array>) {\n parser.tokenizer.write(decoder.decode(chunk), false);\n }\n const fragment = parser.getFragment();\n\n let metadata: RemoteComponentMetadata = {\n bundle: CURRENT_ZONE ?? '__vercel_remote_component',\n route: '/',\n runtime: 'webpack',\n id: '__vercel_remote_component',\n };\n let remoteShared: Record<string, string> = {};\n const scripts: { src: string }[] = [];\n const links: Record<string, string | boolean>[] = [];\n const hydrationData: string[] = [];\n let nextData:\n | {\n props: { pageProps: Record<string, unknown> };\n buildId?: string;\n }\n | undefined;\n let html = '';\n\n // convert the parsed HTML fragment into an RSC flight data\n // and extract the metadata, scripts, links and remote component RSC flight data\n const rsc = visit(fragment, {\n url,\n name:
|
|
1
|
+
{"version":3,"sources":["../../../../src/shared/ssr/fetch-remote-component.ts"],"sourcesContent":["import { type DefaultTreeAdapterMap, Parser } from 'parse5';\nimport { visit } from '#internal/shared/ssr/dom-flight';\nimport type { RemoteComponentMetadata } from './types';\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\n/**\n * Resolves the base URL that should be used to fetch the remote component.\n * This function must support local development, Vercel environments, and SSG\n * and dynamic rendering.\n */\nexport function getBaseUrl(reqHeaders: Headers): string {\n const host = reqHeaders.get('host');\n if (host) {\n return host.startsWith('localhost') ? `http://${host}` : `https://${host}`;\n }\n const forwardedHost = reqHeaders.get('x-forwarded-host');\n if (forwardedHost) {\n return `https://${forwardedHost}`;\n }\n if (process.env.VERCEL_PROJECT_PRODUCTION_URL) {\n return `https://${process.env.VERCEL_PROJECT_PRODUCTION_URL}`;\n }\n // TODO: Resolve the correct URL in local development when the proxy is not running.\n return `http://localhost:${process.env.MFE_LOCAL_PROXY_PORT || 3024}`;\n}\n\nexport async function fetchRemoteComponent(\n src: string | URL,\n headers: Headers = new Headers(),\n options: {\n name?: string;\n rsc?: boolean;\n } = {\n rsc: false,\n },\n) {\n const url = new URL(src, getBaseUrl(headers));\n\n const fetchInit = {\n method: 'GET',\n headers: {\n // pass all headers to the remote component\n ...Object.fromEntries(headers.entries()),\n Accept: 'text/html',\n },\n credentials: 'include',\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 \"${url.href}\": ${res.status}`,\n );\n }\n\n // create a parser for the HTML response\n const parser = Parser.getFragmentParser<DefaultTreeAdapterMap>();\n\n if (!res.body) {\n throw new Error(`No response body for remote component \"${url.href}\"`);\n }\n\n const decoder = new TextDecoder();\n // read the response body as a stream and parse it using the parse5 fragment parser\n for await (const chunk of res.body as unknown as AsyncIterable<Uint8Array>) {\n parser.tokenizer.write(decoder.decode(chunk), false);\n }\n const fragment = parser.getFragment();\n\n let metadata: RemoteComponentMetadata = {\n bundle: CURRENT_ZONE ?? '__vercel_remote_component',\n route: '/',\n runtime: 'webpack',\n id: '__vercel_remote_component',\n };\n let remoteShared: Record<string, string> = {};\n const scripts: { src: string }[] = [];\n const links: Record<string, string | boolean>[] = [];\n const hydrationData: string[] = [];\n let nextData:\n | {\n props: { pageProps: Record<string, unknown> };\n buildId?: string;\n }\n | undefined;\n let html = '';\n\n const remoteName =\n options.name || url.hash ? url.hash.substring(1) : undefined;\n // convert the parsed HTML fragment into an RSC flight data\n // and extract the metadata, scripts, links and remote component RSC flight data\n const rsc = visit(fragment, {\n url,\n name: remoteName,\n onMetadata(_metadata) {\n metadata = _metadata;\n },\n onScript(attrs) {\n if (!scripts.find((it) => it.src === attrs.src)) {\n scripts.push({\n src: new URL(attrs.src as string, new URL(url).origin).href,\n });\n }\n },\n onLink(attrs) {\n const relativeAttrs = {\n ...attrs,\n href: new URL(attrs.href as string, new URL(url).origin).href,\n };\n if (\n !links.find(\n (it) => it.href === relativeAttrs.href && it.rel === attrs.rel,\n )\n ) {\n links.push(relativeAttrs);\n }\n },\n onRSC(chunk) {\n hydrationData.push(chunk);\n },\n onNextData(data) {\n nextData = data;\n\n // use the Next.js Pages Router props data to extract remote component metadata\n if (data.props.__REMOTE_COMPONENT__) {\n Object.assign(metadata, data.props.__REMOTE_COMPONENT__);\n // only a singleton remote component is supported per page when using the Next.js Pages Router\n metadata.id = '__next';\n metadata.route = data.page ?? '/';\n }\n },\n onHTML(_html) {\n if (!html.includes(_html)) {\n html += _html;\n }\n },\n onShared(_shared) {\n remoteShared = _shared;\n },\n });\n\n let component: React.ReactNode | undefined;\n\n // only create a React component if requested\n if (options.rsc) {\n // RSC flight data for the static HTML in a single RSC line\n const componentRSC = `0:${JSON.stringify(rsc)}\\n`;\n\n const { createFromReadableStream } = await import(\n 'next/dist/compiled/react-server-dom-webpack/client.edge'\n );\n\n // create a React tree from the RSC flight data\n component = await createFromReadableStream(\n new ReadableStream({\n type: 'bytes',\n start(controller) {\n const encoder = new TextEncoder();\n controller.enqueue(encoder.encode(componentRSC));\n controller.close();\n },\n }),\n {\n serverConsumerManifest: {\n moduleLoading: {\n prefix: url.origin,\n crossOrigin: true,\n },\n moduleMap: {},\n },\n },\n );\n }\n\n const name = metadata.id.replace(/_ssr$/, '');\n return {\n name,\n url,\n metadata,\n rsc,\n scripts,\n links,\n hydrationData,\n nextData,\n component,\n html,\n remoteShared,\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -31,6 +31,8 @@ async function RemoteComponent({
|
|
|
31
31
|
src,
|
|
32
32
|
name: nameProp,
|
|
33
33
|
isolate,
|
|
34
|
+
mode,
|
|
35
|
+
reset,
|
|
34
36
|
children
|
|
35
37
|
}) {
|
|
36
38
|
const headerList = await (0, import_headers.headers)();
|
|
@@ -55,9 +57,11 @@ async function RemoteComponent({
|
|
|
55
57
|
data: hydrationData,
|
|
56
58
|
isolate,
|
|
57
59
|
links,
|
|
60
|
+
mode,
|
|
58
61
|
name,
|
|
59
62
|
nextData,
|
|
60
63
|
remoteShared,
|
|
64
|
+
reset,
|
|
61
65
|
route: metadata.route,
|
|
62
66
|
runtime: metadata.runtime,
|
|
63
67
|
scripts,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/host/app-router-server.tsx"],"sourcesContent":["import { headers } from 'next/headers';\nimport { Suspense, Children } from 'react';\nimport { RemoteComponentClient } from '#internal/next/host/app-router-client';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\nexport interface RemoteComponentProps {\n /** The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported. */\n src: string | URL;\n /** The name of the exposed remote component. Used to identify the remote component when multiple remote components are exposed on a page. */\n name?: string;\n /** Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation. */\n isolate?: boolean;\n /** Loading fallback content to display while the remote component is being fetched. */\n children?: React.ReactNode;\n}\n\n/**\n * RemoteComponent is a Next.js 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 src - The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param isolate - Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation.\n * @returns A React component that renders the remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to consume a remote component from a remote application:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next/host';\n *\n * export default function MyPage() {\n * return (\n * <>\n * <h1>Welcome to My Page</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 */\nexport async function RemoteComponent({\n src,\n name: nameProp,\n isolate,\n children,\n}: RemoteComponentProps): Promise<React.ReactElement> {\n // get the headers from the request\n const headerList = await headers();\n\n const {\n metadata,\n scripts,\n links,\n hydrationData,\n nextData,\n component,\n remoteShared,\n url,\n } = await fetchRemoteComponent(src, headerList, {\n name: nameProp,\n rsc: true,\n });\n\n // pass all remote component data to the SSR/client layer\n // render remote component static HTML as children\n // remote _ssr suffix from remote component id\n const name = metadata.id.replace(/_ssr$/, '');\n const remoteComponentClient = (\n <RemoteComponentClient\n bundle={metadata.bundle || (CURRENT_ZONE ?? name)}\n data={hydrationData}\n isolate={isolate}\n links={links}\n name={name}\n nextData={nextData}\n remoteShared={remoteShared}\n route={metadata.route}\n runtime={metadata.runtime}\n scripts={scripts}\n url={url.href}\n >\n {component}\n </RemoteComponentClient>\n );\n\n if (Children.count(children) > 0) {\n // if there are children, render them inside the remote component\n return <Suspense fallback={children}>{remoteComponentClient}</Suspense>;\n }\n\n return remoteComponentClient;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;
|
|
1
|
+
{"version":3,"sources":["../../../src/next/host/app-router-server.tsx"],"sourcesContent":["import { headers } from 'next/headers';\nimport { Suspense, Children } from 'react';\nimport { RemoteComponentClient } from '#internal/next/host/app-router-client';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\nexport interface RemoteComponentProps {\n /** The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported. */\n src: string | URL;\n /** The name of the exposed remote component. Used to identify the remote component when multiple remote components are exposed on a page. */\n name?: string;\n /** Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation. */\n isolate?: boolean;\n /** The mode of the Shadow DOM. Defaults to `open`. */\n mode?: 'open' | 'closed';\n /** Whether to include a CSS reset style in the shadow DOM. Defaults to `false`. */\n reset?: boolean;\n /** Loading fallback content to display while the remote component is being fetched. */\n children?: React.ReactNode;\n}\n\n/**\n * RemoteComponent is a Next.js 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 src - The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param isolate - Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation.\n * @returns A React component that renders the remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to consume a remote component from a remote application:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next/host';\n *\n * export default function MyPage() {\n * return (\n * <>\n * <h1>Welcome to My Page</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 */\nexport async function RemoteComponent({\n src,\n name: nameProp,\n isolate,\n mode,\n reset,\n children,\n}: RemoteComponentProps): Promise<React.ReactElement> {\n // get the headers from the request\n const headerList = await headers();\n\n const {\n metadata,\n scripts,\n links,\n hydrationData,\n nextData,\n component,\n remoteShared,\n url,\n } = await fetchRemoteComponent(src, headerList, {\n name: nameProp,\n rsc: true,\n });\n\n // pass all remote component data to the SSR/client layer\n // render remote component static HTML as children\n // remote _ssr suffix from remote component id\n const name = metadata.id.replace(/_ssr$/, '');\n const remoteComponentClient = (\n <RemoteComponentClient\n bundle={metadata.bundle || (CURRENT_ZONE ?? name)}\n data={hydrationData}\n isolate={isolate}\n links={links}\n mode={mode}\n name={name}\n nextData={nextData}\n remoteShared={remoteShared}\n reset={reset}\n route={metadata.route}\n runtime={metadata.runtime}\n scripts={scripts}\n url={url.href}\n >\n {component}\n </RemoteComponentClient>\n );\n\n if (Children.count(children) > 0) {\n // if there are children, render them inside the remote component\n return <Suspense fallback={children}>{remoteComponentClient}</Suspense>;\n }\n\n return remoteComponentClient;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA8EI;AA9EJ,qBAAwB;AACxB,mBAAmC;AACnC,+BAAsC;AACtC,oCAAqC;AAErC,MAAM,eAAe,QAAQ,IAAI;AA2CjC,eAAsB,gBAAgB;AAAA,EACpC;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsD;AAEpD,QAAM,aAAa,UAAM,wBAAQ;AAEjC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,UAAM,oDAAqB,KAAK,YAAY;AAAA,IAC9C,MAAM;AAAA,IACN,KAAK;AAAA,EACP,CAAC;AAKD,QAAM,OAAO,SAAS,GAAG,QAAQ,SAAS,EAAE;AAC5C,QAAM,wBACJ;AAAA,IAAC;AAAA;AAAA,MACC,QAAQ,SAAS,WAAW,gBAAgB;AAAA,MAC5C,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,SAAS;AAAA,MAChB,SAAS,SAAS;AAAA,MAClB;AAAA,MACA,KAAK,IAAI;AAAA,MAER;AAAA;AAAA,EACH;AAGF,MAAI,sBAAS,MAAM,QAAQ,IAAI,GAAG;AAEhC,WAAO,4CAAC,yBAAS,UAAU,UAAW,iCAAsB;AAAA,EAC9D;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -5,6 +5,10 @@ interface RemoteComponentProps {
|
|
|
5
5
|
name?: string;
|
|
6
6
|
/** Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation. */
|
|
7
7
|
isolate?: boolean;
|
|
8
|
+
/** The mode of the Shadow DOM. Defaults to `open`. */
|
|
9
|
+
mode?: 'open' | 'closed';
|
|
10
|
+
/** Whether to include a CSS reset style in the shadow DOM. Defaults to `false`. */
|
|
11
|
+
reset?: boolean;
|
|
8
12
|
/** Loading fallback content to display while the remote component is being fetched. */
|
|
9
13
|
children?: React.ReactNode;
|
|
10
14
|
}
|
|
@@ -34,6 +38,6 @@ interface RemoteComponentProps {
|
|
|
34
38
|
* }
|
|
35
39
|
* ```
|
|
36
40
|
*/
|
|
37
|
-
declare function RemoteComponent({ src, name: nameProp, isolate, children, }: RemoteComponentProps): Promise<React.ReactElement>;
|
|
41
|
+
declare function RemoteComponent({ src, name: nameProp, isolate, mode, reset, children, }: RemoteComponentProps): Promise<React.ReactElement>;
|
|
38
42
|
|
|
39
43
|
export { RemoteComponent, RemoteComponentProps };
|
|
@@ -8,6 +8,8 @@ async function RemoteComponent({
|
|
|
8
8
|
src,
|
|
9
9
|
name: nameProp,
|
|
10
10
|
isolate,
|
|
11
|
+
mode,
|
|
12
|
+
reset,
|
|
11
13
|
children
|
|
12
14
|
}) {
|
|
13
15
|
const headerList = await headers();
|
|
@@ -32,9 +34,11 @@ async function RemoteComponent({
|
|
|
32
34
|
data: hydrationData,
|
|
33
35
|
isolate,
|
|
34
36
|
links,
|
|
37
|
+
mode,
|
|
35
38
|
name,
|
|
36
39
|
nextData,
|
|
37
40
|
remoteShared,
|
|
41
|
+
reset,
|
|
38
42
|
route: metadata.route,
|
|
39
43
|
runtime: metadata.runtime,
|
|
40
44
|
scripts,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/host/app-router-server.tsx"],"sourcesContent":["import { headers } from 'next/headers';\nimport { Suspense, Children } from 'react';\nimport { RemoteComponentClient } from '#internal/next/host/app-router-client';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\nexport interface RemoteComponentProps {\n /** The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported. */\n src: string | URL;\n /** The name of the exposed remote component. Used to identify the remote component when multiple remote components are exposed on a page. */\n name?: string;\n /** Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation. */\n isolate?: boolean;\n /** Loading fallback content to display while the remote component is being fetched. */\n children?: React.ReactNode;\n}\n\n/**\n * RemoteComponent is a Next.js 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 src - The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param isolate - Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation.\n * @returns A React component that renders the remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to consume a remote component from a remote application:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next/host';\n *\n * export default function MyPage() {\n * return (\n * <>\n * <h1>Welcome to My Page</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 */\nexport async function RemoteComponent({\n src,\n name: nameProp,\n isolate,\n children,\n}: RemoteComponentProps): Promise<React.ReactElement> {\n // get the headers from the request\n const headerList = await headers();\n\n const {\n metadata,\n scripts,\n links,\n hydrationData,\n nextData,\n component,\n remoteShared,\n url,\n } = await fetchRemoteComponent(src, headerList, {\n name: nameProp,\n rsc: true,\n });\n\n // pass all remote component data to the SSR/client layer\n // render remote component static HTML as children\n // remote _ssr suffix from remote component id\n const name = metadata.id.replace(/_ssr$/, '');\n const remoteComponentClient = (\n <RemoteComponentClient\n bundle={metadata.bundle || (CURRENT_ZONE ?? name)}\n data={hydrationData}\n isolate={isolate}\n links={links}\n name={name}\n nextData={nextData}\n remoteShared={remoteShared}\n route={metadata.route}\n runtime={metadata.runtime}\n scripts={scripts}\n url={url.href}\n >\n {component}\n </RemoteComponentClient>\n );\n\n if (Children.count(children) > 0) {\n // if there are children, render them inside the remote component\n return <Suspense fallback={children}>{remoteComponentClient}</Suspense>;\n }\n\n return remoteComponentClient;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/next/host/app-router-server.tsx"],"sourcesContent":["import { headers } from 'next/headers';\nimport { Suspense, Children } from 'react';\nimport { RemoteComponentClient } from '#internal/next/host/app-router-client';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\nexport interface RemoteComponentProps {\n /** The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported. */\n src: string | URL;\n /** The name of the exposed remote component. Used to identify the remote component when multiple remote components are exposed on a page. */\n name?: string;\n /** Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation. */\n isolate?: boolean;\n /** The mode of the Shadow DOM. Defaults to `open`. */\n mode?: 'open' | 'closed';\n /** Whether to include a CSS reset style in the shadow DOM. Defaults to `false`. */\n reset?: boolean;\n /** Loading fallback content to display while the remote component is being fetched. */\n children?: React.ReactNode;\n}\n\n/**\n * RemoteComponent is a Next.js 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 src - The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param isolate - Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation.\n * @returns A React component that renders the remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to consume a remote component from a remote application:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next/host';\n *\n * export default function MyPage() {\n * return (\n * <>\n * <h1>Welcome to My Page</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 */\nexport async function RemoteComponent({\n src,\n name: nameProp,\n isolate,\n mode,\n reset,\n children,\n}: RemoteComponentProps): Promise<React.ReactElement> {\n // get the headers from the request\n const headerList = await headers();\n\n const {\n metadata,\n scripts,\n links,\n hydrationData,\n nextData,\n component,\n remoteShared,\n url,\n } = await fetchRemoteComponent(src, headerList, {\n name: nameProp,\n rsc: true,\n });\n\n // pass all remote component data to the SSR/client layer\n // render remote component static HTML as children\n // remote _ssr suffix from remote component id\n const name = metadata.id.replace(/_ssr$/, '');\n const remoteComponentClient = (\n <RemoteComponentClient\n bundle={metadata.bundle || (CURRENT_ZONE ?? name)}\n data={hydrationData}\n isolate={isolate}\n links={links}\n mode={mode}\n name={name}\n nextData={nextData}\n remoteShared={remoteShared}\n reset={reset}\n route={metadata.route}\n runtime={metadata.runtime}\n scripts={scripts}\n url={url.href}\n >\n {component}\n </RemoteComponentClient>\n );\n\n if (Children.count(children) > 0) {\n // if there are children, render them inside the remote component\n return <Suspense fallback={children}>{remoteComponentClient}</Suspense>;\n }\n\n return remoteComponentClient;\n}\n"],"mappings":"AA8EI;AA9EJ,SAAS,eAAe;AACxB,SAAS,UAAU,gBAAgB;AACnC,SAAS,6BAA6B;AACtC,SAAS,4BAA4B;AAErC,MAAM,eAAe,QAAQ,IAAI;AA2CjC,eAAsB,gBAAgB;AAAA,EACpC;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsD;AAEpD,QAAM,aAAa,MAAM,QAAQ;AAEjC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,MAAM,qBAAqB,KAAK,YAAY;AAAA,IAC9C,MAAM;AAAA,IACN,KAAK;AAAA,EACP,CAAC;AAKD,QAAM,OAAO,SAAS,GAAG,QAAQ,SAAS,EAAE;AAC5C,QAAM,wBACJ;AAAA,IAAC;AAAA;AAAA,MACC,QAAQ,SAAS,WAAW,gBAAgB;AAAA,MAC5C,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,SAAS;AAAA,MAChB,SAAS,SAAS;AAAA,MAClB;AAAA,MACA,KAAK,IAAI;AAAA,MAER;AAAA;AAAA,EACH;AAGF,MAAI,SAAS,MAAM,QAAQ,IAAI,GAAG;AAEhC,WAAO,oBAAC,YAAS,UAAU,UAAW,iCAAsB;AAAA,EAC9D;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -135,8 +135,11 @@ function RemoteComponent(props) {
|
|
|
135
135
|
import_react2.RemoteComponent,
|
|
136
136
|
{
|
|
137
137
|
isolate: props.isolate,
|
|
138
|
+
mode: props.mode,
|
|
139
|
+
name: props.name,
|
|
140
|
+
reset: props.reset,
|
|
138
141
|
shared: shared(props.bundle ?? "default"),
|
|
139
|
-
src: props.src,
|
|
142
|
+
src: props.url ?? props.src,
|
|
140
143
|
children: props.children
|
|
141
144
|
}
|
|
142
145
|
);
|
|
@@ -145,9 +148,11 @@ function RemoteComponent(props) {
|
|
|
145
148
|
import_react2.RemoteComponent,
|
|
146
149
|
{
|
|
147
150
|
isolate: props.isolate,
|
|
151
|
+
mode: props.mode,
|
|
148
152
|
name: props.name,
|
|
153
|
+
reset: props.reset,
|
|
149
154
|
shared: shared(props.bundle ?? "default"),
|
|
150
|
-
src: props.src,
|
|
155
|
+
src: props.url ?? props.src,
|
|
151
156
|
children: remoteComponent
|
|
152
157
|
}
|
|
153
158
|
);
|
|
@@ -163,7 +168,8 @@ async function getRemoteComponentProps(src, headers) {
|
|
|
163
168
|
name,
|
|
164
169
|
links,
|
|
165
170
|
component,
|
|
166
|
-
nextData
|
|
171
|
+
nextData,
|
|
172
|
+
url
|
|
167
173
|
} = await (0, import_fetch_remote_component.fetchRemoteComponent)(
|
|
168
174
|
src,
|
|
169
175
|
headers instanceof Headers ? headers : (
|
|
@@ -189,7 +195,8 @@ async function getRemoteComponentProps(src, headers) {
|
|
|
189
195
|
name,
|
|
190
196
|
route,
|
|
191
197
|
runtime,
|
|
192
|
-
buildId: nextData?.buildId ?? null
|
|
198
|
+
buildId: nextData?.buildId ?? null,
|
|
199
|
+
url: url.href
|
|
193
200
|
};
|
|
194
201
|
const key = getKey(props);
|
|
195
202
|
if (nextData?.buildId !== "development") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/host/pages-router-server.tsx"],"sourcesContent":["import type { IncomingHttpHeaders } from 'node:http';\nimport { useEffect } from 'react';\nimport { shared as _shared } from '@remote-components/shared/host/pages';\nimport * as Form from 'next/form';\nimport * as Image from 'next/image';\nimport * as Link from 'next/link';\nimport * as Script from 'next/script';\nimport { useRouter } from 'next/router';\nimport { RemoteComponent as RemoteComponentReact } from '#remote-components/react/index';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\n\nconst navigationImpl = {\n useRouter,\n useSearchParams: () => {\n const router = useRouter();\n return {\n get: (key: string) => router.query[key],\n has: (key: string) => key in router.query,\n };\n },\n};\n\nconst sharedCache = new Map<string, Record<string, () => Promise<unknown>>>();\nconst shared = (bundle: string) => {\n if (sharedCache.has(bundle)) {\n return sharedCache.get(bundle);\n }\n\n const remoteLoader = ({ src, width, quality }: Image.ImageLoaderProps) => {\n const self = globalThis as typeof globalThis & {\n __remote_bundle_url__?: Record<string, URL>;\n };\n const { assetPrefix } =\n /^(?<assetPrefix>.*?)\\/_next\\//.exec(src)?.groups ?? {};\n return `${self.__remote_bundle_url__?.[bundle]?.origin ?? ''}${assetPrefix}/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality || 75}`;\n };\n\n const imageImpl = (ImageComponent: typeof Image.default) => {\n return function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n };\n };\n\n return {\n // polyfill Next.js App Router client API (minimal)\n // some API methods are not available when using a Next.js Pages Router application as host\n 'next/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/components/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/app-dir/link': () =>\n Promise.resolve(Link.default) as Promise<unknown>,\n 'next/link': () => Promise.resolve(Link.default) as Promise<unknown>,\n 'next/dist/client/app-dir/form': () =>\n Promise.resolve(Form.default) as Promise<unknown>,\n 'next/form': () => Promise.resolve(Form.default) as Promise<unknown>,\n 'next/dist/client/script': () =>\n Promise.resolve(Script.default) as Promise<unknown>,\n 'next/script': () => Promise.resolve(Script.default) as Promise<unknown>,\n 'next/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n 'next/dist/client/image-component': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n Image: imageImpl(Image.default.default ?? Image.default),\n }) as Promise<unknown>,\n 'next/dist/api/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n ..._shared,\n };\n};\n\n// internal symbols to access global store\nconst REMOTE_COMPONENT_STORE = Symbol('REMOTE_COMPONENT_STORE');\nconst REMOTE_COMPONENT_KEY = '__REMOTE_COMPONENT_KEY__';\n\n// temporary global store for remote component HTML\n// the store is used to save the HTML of remote components for SSR without sending the content to the client\nconst self = globalThis as typeof globalThis & {\n [REMOTE_COMPONENT_STORE]?: Map<string, React.ReactNode>;\n};\n\nfunction getKey({\n bundle,\n route,\n name,\n}: {\n bundle?: string;\n route?: string;\n name?: string;\n}): string {\n return `${bundle ?? '__next'}:${route ?? '/'}:${name ?? '__vercel_remote_component'}__${crypto.randomUUID()}`;\n}\n\nfunction setComponent(key: string, component: React.ReactNode): void {\n if (!self[REMOTE_COMPONENT_STORE]) {\n self[REMOTE_COMPONENT_STORE] = new Map();\n }\n self[REMOTE_COMPONENT_STORE].set(key, component);\n}\n\nfunction getComponent(key: string): React.ReactNode | undefined {\n const component = self[REMOTE_COMPONENT_STORE]?.get(key);\n // remove the component from the store after retrieving it to prevent memory leaks\n // storing the HTML in the global store is only needed for SSR and it's temporary only used for a single render\n self[REMOTE_COMPONENT_STORE]?.delete(key);\n return component;\n}\n\nexport interface RemoteComponentProps {\n src: string;\n bundle?: string;\n route?: string;\n name?: string;\n isolate?: boolean;\n [REMOTE_COMPONENT_KEY]?: string;\n children?: React.ReactNode;\n}\n\n/**\n * This component handles the rendering of remote microfrontends.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n */\nexport function RemoteComponent(props: RemoteComponentProps) {\n const remoteComponent =\n typeof document !== 'undefined'\n ? null\n : // retrieve the HTML from the global store\n getComponent(\n props[REMOTE_COMPONENT_KEY] ?? '__vercel_remote_component',\n );\n\n useEffect(() => {\n const clientSelf = globalThis as typeof globalThis & {\n __remote_component_shared__?: Record<string, () => Promise<unknown>>;\n };\n // eslint-disable-next-line camelcase\n clientSelf.__remote_component_shared__ = shared(props.bundle ?? 'default');\n }, [props.bundle]);\n\n if (!props[REMOTE_COMPONENT_KEY]) {\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {props.children}\n </RemoteComponentReact>\n );\n }\n\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n name={props.name}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {remoteComponent}\n </RemoteComponentReact>\n );\n}\n\n/**\n * Fetches the remote component properties from the server. You need to pass these properties to the `<RemoteComponent>` component to render the fetched remote component.\n *\n * @param src - The source URL of the remote component. When using the Vercel Microfrontends solution, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param headers - The HTTP headers used for supporting the Vercel Microfrontends proxy.\n * @returns The properties of the remote component.\n *\n * @example\n *\n * ```tsx\n * import { getRemoteComponentProps } from 'remote-components/next/host/pages';\n * import type { GetServerSideProps } from 'next';\n *\n * export const getServerSideProps: GetServerSideProps<PageProps> = async function getServerSideProps({ req }) {\n * const myRemoteComponent = await getRemoteComponentProps(\n * '/nextjs-app-remote/components/header',\n * req.headers,\n * );\n * return {\n * props: {\n * remoteComponents: {\n * myRemoteComponent,\n * },\n * },\n * };\n * }\n * ```\n */\nexport async function getRemoteComponentProps(\n src: string,\n headers: IncomingHttpHeaders,\n): Promise<RemoteComponentProps> {\n if (typeof document !== 'undefined') {\n throw new Error(\n 'getRemoteComponentProps can only be used on the server side.',\n );\n }\n\n const {\n metadata: { bundle, route, runtime },\n name,\n links,\n component,\n nextData,\n } = await fetchRemoteComponent(\n src,\n headers instanceof Headers\n ? headers\n : // convert IncomingHttpHeaders to web standard Headers\n Object.entries(headers).reduce((acc, [key, value]) => {\n if (value) {\n if (Array.isArray(value)) {\n value.forEach((v) => acc.append(key, v));\n } else {\n acc.append(key, value);\n }\n }\n return acc;\n }, new Headers()),\n {\n rsc: true,\n },\n );\n\n const props = {\n src,\n bundle,\n name,\n route,\n runtime,\n buildId: nextData?.buildId ?? null,\n };\n\n const key = getKey(props);\n\n // do not render the HTML in development mode when remote is using Next.js Pages Router\n // this behavior is emulating the Next.js Pages Router FOUC as the styles are only applied on the client when running in development mode\n if (nextData?.buildId !== 'development') {\n // store the HTML in a global store\n setComponent(\n key,\n <>\n {links.map((link) => (\n <link\n key={`${link.as}_${link.href}`}\n {...link}\n precedence={undefined}\n />\n ))}\n {component}\n </>,\n );\n }\n\n return {\n ...props,\n // add remote component key to the props\n [REMOTE_COMPONENT_KEY]: key,\n };\n}\n"],"mappings":"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","names":["import_react","self","_shared","RemoteComponentReact","key"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/next/host/pages-router-server.tsx"],"sourcesContent":["import type { IncomingHttpHeaders } from 'node:http';\nimport { useEffect } from 'react';\nimport { shared as _shared } from '@remote-components/shared/host/pages';\nimport * as Form from 'next/form';\nimport * as Image from 'next/image';\nimport * as Link from 'next/link';\nimport * as Script from 'next/script';\nimport { useRouter } from 'next/router';\nimport { RemoteComponent as RemoteComponentReact } from '#remote-components/react/index';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\n\nconst navigationImpl = {\n useRouter,\n useSearchParams: () => {\n const router = useRouter();\n return {\n get: (key: string) => router.query[key],\n has: (key: string) => key in router.query,\n };\n },\n};\n\nconst sharedCache = new Map<string, Record<string, () => Promise<unknown>>>();\nconst shared = (bundle: string) => {\n if (sharedCache.has(bundle)) {\n return sharedCache.get(bundle);\n }\n\n const remoteLoader = ({ src, width, quality }: Image.ImageLoaderProps) => {\n const self = globalThis as typeof globalThis & {\n __remote_bundle_url__?: Record<string, URL>;\n };\n const { assetPrefix } =\n /^(?<assetPrefix>.*?)\\/_next\\//.exec(src)?.groups ?? {};\n return `${self.__remote_bundle_url__?.[bundle]?.origin ?? ''}${assetPrefix}/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality || 75}`;\n };\n\n const imageImpl = (ImageComponent: typeof Image.default) => {\n return function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n };\n };\n\n return {\n // polyfill Next.js App Router client API (minimal)\n // some API methods are not available when using a Next.js Pages Router application as host\n 'next/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/components/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/app-dir/link': () =>\n Promise.resolve(Link.default) as Promise<unknown>,\n 'next/link': () => Promise.resolve(Link.default) as Promise<unknown>,\n 'next/dist/client/app-dir/form': () =>\n Promise.resolve(Form.default) as Promise<unknown>,\n 'next/form': () => Promise.resolve(Form.default) as Promise<unknown>,\n 'next/dist/client/script': () =>\n Promise.resolve(Script.default) as Promise<unknown>,\n 'next/script': () => Promise.resolve(Script.default) as Promise<unknown>,\n 'next/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n 'next/dist/client/image-component': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n Image: imageImpl(Image.default.default ?? Image.default),\n }) as Promise<unknown>,\n 'next/dist/api/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n ..._shared,\n };\n};\n\n// internal symbols to access global store\nconst REMOTE_COMPONENT_STORE = Symbol('REMOTE_COMPONENT_STORE');\nconst REMOTE_COMPONENT_KEY = '__REMOTE_COMPONENT_KEY__';\n\n// temporary global store for remote component HTML\n// the store is used to save the HTML of remote components for SSR without sending the content to the client\nconst self = globalThis as typeof globalThis & {\n [REMOTE_COMPONENT_STORE]?: Map<string, React.ReactNode>;\n};\n\nfunction getKey({\n bundle,\n route,\n name,\n}: {\n bundle?: string;\n route?: string;\n name?: string;\n}): string {\n return `${bundle ?? '__next'}:${route ?? '/'}:${name ?? '__vercel_remote_component'}__${crypto.randomUUID()}`;\n}\n\nfunction setComponent(key: string, component: React.ReactNode): void {\n if (!self[REMOTE_COMPONENT_STORE]) {\n self[REMOTE_COMPONENT_STORE] = new Map();\n }\n self[REMOTE_COMPONENT_STORE].set(key, component);\n}\n\nfunction getComponent(key: string): React.ReactNode | undefined {\n const component = self[REMOTE_COMPONENT_STORE]?.get(key);\n // remove the component from the store after retrieving it to prevent memory leaks\n // storing the HTML in the global store is only needed for SSR and it's temporary only used for a single render\n self[REMOTE_COMPONENT_STORE]?.delete(key);\n return component;\n}\n\nexport interface RemoteComponentProps {\n src: string;\n url?: string | URL;\n bundle?: string;\n route?: string;\n name?: string;\n isolate?: boolean;\n mode?: 'open' | 'closed';\n reset?: boolean;\n [REMOTE_COMPONENT_KEY]?: string;\n children?: React.ReactNode;\n}\n\n/**\n * This component handles the rendering of remote microfrontends.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n */\nexport function RemoteComponent(props: RemoteComponentProps) {\n const remoteComponent =\n typeof document !== 'undefined'\n ? null\n : // retrieve the HTML from the global store\n getComponent(\n props[REMOTE_COMPONENT_KEY] ?? '__vercel_remote_component',\n );\n\n useEffect(() => {\n const clientSelf = globalThis as typeof globalThis & {\n __remote_component_shared__?: Record<string, () => Promise<unknown>>;\n };\n // eslint-disable-next-line camelcase\n clientSelf.__remote_component_shared__ = shared(props.bundle ?? 'default');\n }, [props.bundle]);\n\n if (!props[REMOTE_COMPONENT_KEY]) {\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.url ?? props.src}\n >\n {props.children}\n </RemoteComponentReact>\n );\n }\n\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.url ?? props.src}\n >\n {remoteComponent}\n </RemoteComponentReact>\n );\n}\n\n/**\n * Fetches the remote component properties from the server. You need to pass these properties to the `<RemoteComponent>` component to render the fetched remote component.\n *\n * @param src - The source URL of the remote component. When using the Vercel Microfrontends solution, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param headers - The HTTP headers used for supporting the Vercel Microfrontends proxy.\n * @returns The properties of the remote component.\n *\n * @example\n *\n * ```tsx\n * import { getRemoteComponentProps } from 'remote-components/next/host/pages';\n * import type { GetServerSideProps } from 'next';\n *\n * export const getServerSideProps: GetServerSideProps<PageProps> = async function getServerSideProps({ req }) {\n * const myRemoteComponent = await getRemoteComponentProps(\n * '/nextjs-app-remote/components/header',\n * req.headers,\n * );\n * return {\n * props: {\n * remoteComponents: {\n * myRemoteComponent,\n * },\n * },\n * };\n * }\n * ```\n */\nexport async function getRemoteComponentProps(\n src: string,\n headers: IncomingHttpHeaders,\n): Promise<RemoteComponentProps> {\n if (typeof document !== 'undefined') {\n throw new Error(\n 'getRemoteComponentProps can only be used on the server side.',\n );\n }\n\n const {\n metadata: { bundle, route, runtime },\n name,\n links,\n component,\n nextData,\n url,\n } = await fetchRemoteComponent(\n src,\n headers instanceof Headers\n ? headers\n : // convert IncomingHttpHeaders to web standard Headers\n Object.entries(headers).reduce((acc, [key, value]) => {\n if (value) {\n if (Array.isArray(value)) {\n value.forEach((v) => acc.append(key, v));\n } else {\n acc.append(key, value);\n }\n }\n return acc;\n }, new Headers()),\n {\n rsc: true,\n },\n );\n\n const props = {\n src,\n bundle,\n name,\n route,\n runtime,\n buildId: nextData?.buildId ?? null,\n url: url.href,\n };\n\n const key = getKey(props);\n\n // do not render the HTML in development mode when remote is using Next.js Pages Router\n // this behavior is emulating the Next.js Pages Router FOUC as the styles are only applied on the client when running in development mode\n if (nextData?.buildId !== 'development') {\n // store the HTML in a global store\n setComponent(\n key,\n <>\n {links.map((link) => (\n <link\n key={`${link.as}_${link.href}`}\n {...link}\n precedence={undefined}\n />\n ))}\n {component}\n </>,\n );\n }\n\n return {\n ...props,\n // add remote component key to the props\n [REMOTE_COMPONENT_KEY]: key,\n };\n}\n"],"mappings":"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","names":["import_react","self","_shared","RemoteComponentReact","key"]}
|
|
@@ -4,10 +4,13 @@ import { IncomingHttpHeaders } from 'node:http';
|
|
|
4
4
|
declare const REMOTE_COMPONENT_KEY = "__REMOTE_COMPONENT_KEY__";
|
|
5
5
|
interface RemoteComponentProps {
|
|
6
6
|
src: string;
|
|
7
|
+
url?: string | URL;
|
|
7
8
|
bundle?: string;
|
|
8
9
|
route?: string;
|
|
9
10
|
name?: string;
|
|
10
11
|
isolate?: boolean;
|
|
12
|
+
mode?: 'open' | 'closed';
|
|
13
|
+
reset?: boolean;
|
|
11
14
|
[REMOTE_COMPONENT_KEY]?: string;
|
|
12
15
|
children?: React.ReactNode;
|
|
13
16
|
}
|
|
@@ -101,8 +101,11 @@ function RemoteComponent(props) {
|
|
|
101
101
|
RemoteComponentReact,
|
|
102
102
|
{
|
|
103
103
|
isolate: props.isolate,
|
|
104
|
+
mode: props.mode,
|
|
105
|
+
name: props.name,
|
|
106
|
+
reset: props.reset,
|
|
104
107
|
shared: shared(props.bundle ?? "default"),
|
|
105
|
-
src: props.src,
|
|
108
|
+
src: props.url ?? props.src,
|
|
106
109
|
children: props.children
|
|
107
110
|
}
|
|
108
111
|
);
|
|
@@ -111,9 +114,11 @@ function RemoteComponent(props) {
|
|
|
111
114
|
RemoteComponentReact,
|
|
112
115
|
{
|
|
113
116
|
isolate: props.isolate,
|
|
117
|
+
mode: props.mode,
|
|
114
118
|
name: props.name,
|
|
119
|
+
reset: props.reset,
|
|
115
120
|
shared: shared(props.bundle ?? "default"),
|
|
116
|
-
src: props.src,
|
|
121
|
+
src: props.url ?? props.src,
|
|
117
122
|
children: remoteComponent
|
|
118
123
|
}
|
|
119
124
|
);
|
|
@@ -129,7 +134,8 @@ async function getRemoteComponentProps(src, headers) {
|
|
|
129
134
|
name,
|
|
130
135
|
links,
|
|
131
136
|
component,
|
|
132
|
-
nextData
|
|
137
|
+
nextData,
|
|
138
|
+
url
|
|
133
139
|
} = await fetchRemoteComponent(
|
|
134
140
|
src,
|
|
135
141
|
headers instanceof Headers ? headers : (
|
|
@@ -155,7 +161,8 @@ async function getRemoteComponentProps(src, headers) {
|
|
|
155
161
|
name,
|
|
156
162
|
route,
|
|
157
163
|
runtime,
|
|
158
|
-
buildId: nextData?.buildId ?? null
|
|
164
|
+
buildId: nextData?.buildId ?? null,
|
|
165
|
+
url: url.href
|
|
159
166
|
};
|
|
160
167
|
const key = getKey(props);
|
|
161
168
|
if (nextData?.buildId !== "development") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/host/pages-router-server.tsx"],"sourcesContent":["import type { IncomingHttpHeaders } from 'node:http';\nimport { useEffect } from 'react';\nimport { shared as _shared } from '@remote-components/shared/host/pages';\nimport * as Form from 'next/form';\nimport * as Image from 'next/image';\nimport * as Link from 'next/link';\nimport * as Script from 'next/script';\nimport { useRouter } from 'next/router';\nimport { RemoteComponent as RemoteComponentReact } from '#remote-components/react/index';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\n\nconst navigationImpl = {\n useRouter,\n useSearchParams: () => {\n const router = useRouter();\n return {\n get: (key: string) => router.query[key],\n has: (key: string) => key in router.query,\n };\n },\n};\n\nconst sharedCache = new Map<string, Record<string, () => Promise<unknown>>>();\nconst shared = (bundle: string) => {\n if (sharedCache.has(bundle)) {\n return sharedCache.get(bundle);\n }\n\n const remoteLoader = ({ src, width, quality }: Image.ImageLoaderProps) => {\n const self = globalThis as typeof globalThis & {\n __remote_bundle_url__?: Record<string, URL>;\n };\n const { assetPrefix } =\n /^(?<assetPrefix>.*?)\\/_next\\//.exec(src)?.groups ?? {};\n return `${self.__remote_bundle_url__?.[bundle]?.origin ?? ''}${assetPrefix}/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality || 75}`;\n };\n\n const imageImpl = (ImageComponent: typeof Image.default) => {\n return function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n };\n };\n\n return {\n // polyfill Next.js App Router client API (minimal)\n // some API methods are not available when using a Next.js Pages Router application as host\n 'next/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/components/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/app-dir/link': () =>\n Promise.resolve(Link.default) as Promise<unknown>,\n 'next/link': () => Promise.resolve(Link.default) as Promise<unknown>,\n 'next/dist/client/app-dir/form': () =>\n Promise.resolve(Form.default) as Promise<unknown>,\n 'next/form': () => Promise.resolve(Form.default) as Promise<unknown>,\n 'next/dist/client/script': () =>\n Promise.resolve(Script.default) as Promise<unknown>,\n 'next/script': () => Promise.resolve(Script.default) as Promise<unknown>,\n 'next/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n 'next/dist/client/image-component': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n Image: imageImpl(Image.default.default ?? Image.default),\n }) as Promise<unknown>,\n 'next/dist/api/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n ..._shared,\n };\n};\n\n// internal symbols to access global store\nconst REMOTE_COMPONENT_STORE = Symbol('REMOTE_COMPONENT_STORE');\nconst REMOTE_COMPONENT_KEY = '__REMOTE_COMPONENT_KEY__';\n\n// temporary global store for remote component HTML\n// the store is used to save the HTML of remote components for SSR without sending the content to the client\nconst self = globalThis as typeof globalThis & {\n [REMOTE_COMPONENT_STORE]?: Map<string, React.ReactNode>;\n};\n\nfunction getKey({\n bundle,\n route,\n name,\n}: {\n bundle?: string;\n route?: string;\n name?: string;\n}): string {\n return `${bundle ?? '__next'}:${route ?? '/'}:${name ?? '__vercel_remote_component'}__${crypto.randomUUID()}`;\n}\n\nfunction setComponent(key: string, component: React.ReactNode): void {\n if (!self[REMOTE_COMPONENT_STORE]) {\n self[REMOTE_COMPONENT_STORE] = new Map();\n }\n self[REMOTE_COMPONENT_STORE].set(key, component);\n}\n\nfunction getComponent(key: string): React.ReactNode | undefined {\n const component = self[REMOTE_COMPONENT_STORE]?.get(key);\n // remove the component from the store after retrieving it to prevent memory leaks\n // storing the HTML in the global store is only needed for SSR and it's temporary only used for a single render\n self[REMOTE_COMPONENT_STORE]?.delete(key);\n return component;\n}\n\nexport interface RemoteComponentProps {\n src: string;\n bundle?: string;\n route?: string;\n name?: string;\n isolate?: boolean;\n [REMOTE_COMPONENT_KEY]?: string;\n children?: React.ReactNode;\n}\n\n/**\n * This component handles the rendering of remote microfrontends.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n */\nexport function RemoteComponent(props: RemoteComponentProps) {\n const remoteComponent =\n typeof document !== 'undefined'\n ? null\n : // retrieve the HTML from the global store\n getComponent(\n props[REMOTE_COMPONENT_KEY] ?? '__vercel_remote_component',\n );\n\n useEffect(() => {\n const clientSelf = globalThis as typeof globalThis & {\n __remote_component_shared__?: Record<string, () => Promise<unknown>>;\n };\n // eslint-disable-next-line camelcase\n clientSelf.__remote_component_shared__ = shared(props.bundle ?? 'default');\n }, [props.bundle]);\n\n if (!props[REMOTE_COMPONENT_KEY]) {\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {props.children}\n </RemoteComponentReact>\n );\n }\n\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n name={props.name}\n shared={shared(props.bundle ?? 'default')}\n src={props.src}\n >\n {remoteComponent}\n </RemoteComponentReact>\n );\n}\n\n/**\n * Fetches the remote component properties from the server. You need to pass these properties to the `<RemoteComponent>` component to render the fetched remote component.\n *\n * @param src - The source URL of the remote component. When using the Vercel Microfrontends solution, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param headers - The HTTP headers used for supporting the Vercel Microfrontends proxy.\n * @returns The properties of the remote component.\n *\n * @example\n *\n * ```tsx\n * import { getRemoteComponentProps } from 'remote-components/next/host/pages';\n * import type { GetServerSideProps } from 'next';\n *\n * export const getServerSideProps: GetServerSideProps<PageProps> = async function getServerSideProps({ req }) {\n * const myRemoteComponent = await getRemoteComponentProps(\n * '/nextjs-app-remote/components/header',\n * req.headers,\n * );\n * return {\n * props: {\n * remoteComponents: {\n * myRemoteComponent,\n * },\n * },\n * };\n * }\n * ```\n */\nexport async function getRemoteComponentProps(\n src: string,\n headers: IncomingHttpHeaders,\n): Promise<RemoteComponentProps> {\n if (typeof document !== 'undefined') {\n throw new Error(\n 'getRemoteComponentProps can only be used on the server side.',\n );\n }\n\n const {\n metadata: { bundle, route, runtime },\n name,\n links,\n component,\n nextData,\n } = await fetchRemoteComponent(\n src,\n headers instanceof Headers\n ? headers\n : // convert IncomingHttpHeaders to web standard Headers\n Object.entries(headers).reduce((acc, [key, value]) => {\n if (value) {\n if (Array.isArray(value)) {\n value.forEach((v) => acc.append(key, v));\n } else {\n acc.append(key, value);\n }\n }\n return acc;\n }, new Headers()),\n {\n rsc: true,\n },\n );\n\n const props = {\n src,\n bundle,\n name,\n route,\n runtime,\n buildId: nextData?.buildId ?? null,\n };\n\n const key = getKey(props);\n\n // do not render the HTML in development mode when remote is using Next.js Pages Router\n // this behavior is emulating the Next.js Pages Router FOUC as the styles are only applied on the client when running in development mode\n if (nextData?.buildId !== 'development') {\n // store the HTML in a global store\n setComponent(\n key,\n <>\n {links.map((link) => (\n <link\n key={`${link.as}_${link.href}`}\n {...link}\n precedence={undefined}\n />\n ))}\n {component}\n </>,\n );\n }\n\n return {\n ...props,\n // add remote component key to the props\n [REMOTE_COMPONENT_KEY]: key,\n };\n}\n"],"mappings":"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","names":["self","key"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/next/host/pages-router-server.tsx"],"sourcesContent":["import type { IncomingHttpHeaders } from 'node:http';\nimport { useEffect } from 'react';\nimport { shared as _shared } from '@remote-components/shared/host/pages';\nimport * as Form from 'next/form';\nimport * as Image from 'next/image';\nimport * as Link from 'next/link';\nimport * as Script from 'next/script';\nimport { useRouter } from 'next/router';\nimport { RemoteComponent as RemoteComponentReact } from '#remote-components/react/index';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\n\nconst navigationImpl = {\n useRouter,\n useSearchParams: () => {\n const router = useRouter();\n return {\n get: (key: string) => router.query[key],\n has: (key: string) => key in router.query,\n };\n },\n};\n\nconst sharedCache = new Map<string, Record<string, () => Promise<unknown>>>();\nconst shared = (bundle: string) => {\n if (sharedCache.has(bundle)) {\n return sharedCache.get(bundle);\n }\n\n const remoteLoader = ({ src, width, quality }: Image.ImageLoaderProps) => {\n const self = globalThis as typeof globalThis & {\n __remote_bundle_url__?: Record<string, URL>;\n };\n const { assetPrefix } =\n /^(?<assetPrefix>.*?)\\/_next\\//.exec(src)?.groups ?? {};\n return `${self.__remote_bundle_url__?.[bundle]?.origin ?? ''}${assetPrefix}/_next/image?url=${encodeURIComponent(src)}&w=${width}&q=${quality || 75}`;\n };\n\n const imageImpl = (ImageComponent: typeof Image.default) => {\n return function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n };\n };\n\n return {\n // polyfill Next.js App Router client API (minimal)\n // some API methods are not available when using a Next.js Pages Router application as host\n 'next/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/components/navigation': () =>\n Promise.resolve(navigationImpl) as Promise<unknown>,\n 'next/dist/client/app-dir/link': () =>\n Promise.resolve(Link.default) as Promise<unknown>,\n 'next/link': () => Promise.resolve(Link.default) as Promise<unknown>,\n 'next/dist/client/app-dir/form': () =>\n Promise.resolve(Form.default) as Promise<unknown>,\n 'next/form': () => Promise.resolve(Form.default) as Promise<unknown>,\n 'next/dist/client/script': () =>\n Promise.resolve(Script.default) as Promise<unknown>,\n 'next/script': () => Promise.resolve(Script.default) as Promise<unknown>,\n 'next/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n 'next/dist/client/image-component': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n Image: imageImpl(Image.default.default ?? Image.default),\n }) as Promise<unknown>,\n 'next/dist/api/image': () =>\n Promise.resolve({\n // @ts-expect-error default.default\n // eslint-disable-next-line\n default: imageImpl(Image.default.default ?? Image.default),\n getImageProps: Image.getImageProps,\n }) as Promise<unknown>,\n ..._shared,\n };\n};\n\n// internal symbols to access global store\nconst REMOTE_COMPONENT_STORE = Symbol('REMOTE_COMPONENT_STORE');\nconst REMOTE_COMPONENT_KEY = '__REMOTE_COMPONENT_KEY__';\n\n// temporary global store for remote component HTML\n// the store is used to save the HTML of remote components for SSR without sending the content to the client\nconst self = globalThis as typeof globalThis & {\n [REMOTE_COMPONENT_STORE]?: Map<string, React.ReactNode>;\n};\n\nfunction getKey({\n bundle,\n route,\n name,\n}: {\n bundle?: string;\n route?: string;\n name?: string;\n}): string {\n return `${bundle ?? '__next'}:${route ?? '/'}:${name ?? '__vercel_remote_component'}__${crypto.randomUUID()}`;\n}\n\nfunction setComponent(key: string, component: React.ReactNode): void {\n if (!self[REMOTE_COMPONENT_STORE]) {\n self[REMOTE_COMPONENT_STORE] = new Map();\n }\n self[REMOTE_COMPONENT_STORE].set(key, component);\n}\n\nfunction getComponent(key: string): React.ReactNode | undefined {\n const component = self[REMOTE_COMPONENT_STORE]?.get(key);\n // remove the component from the store after retrieving it to prevent memory leaks\n // storing the HTML in the global store is only needed for SSR and it's temporary only used for a single render\n self[REMOTE_COMPONENT_STORE]?.delete(key);\n return component;\n}\n\nexport interface RemoteComponentProps {\n src: string;\n url?: string | URL;\n bundle?: string;\n route?: string;\n name?: string;\n isolate?: boolean;\n mode?: 'open' | 'closed';\n reset?: boolean;\n [REMOTE_COMPONENT_KEY]?: string;\n children?: React.ReactNode;\n}\n\n/**\n * This component handles the rendering of remote microfrontends.\n *\n * @param props - The properties for the remote component.\n * @returns A React component that renders the remote component.\n */\nexport function RemoteComponent(props: RemoteComponentProps) {\n const remoteComponent =\n typeof document !== 'undefined'\n ? null\n : // retrieve the HTML from the global store\n getComponent(\n props[REMOTE_COMPONENT_KEY] ?? '__vercel_remote_component',\n );\n\n useEffect(() => {\n const clientSelf = globalThis as typeof globalThis & {\n __remote_component_shared__?: Record<string, () => Promise<unknown>>;\n };\n // eslint-disable-next-line camelcase\n clientSelf.__remote_component_shared__ = shared(props.bundle ?? 'default');\n }, [props.bundle]);\n\n if (!props[REMOTE_COMPONENT_KEY]) {\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.url ?? props.src}\n >\n {props.children}\n </RemoteComponentReact>\n );\n }\n\n return (\n <RemoteComponentReact\n isolate={props.isolate}\n mode={props.mode}\n name={props.name}\n reset={props.reset}\n shared={shared(props.bundle ?? 'default')}\n src={props.url ?? props.src}\n >\n {remoteComponent}\n </RemoteComponentReact>\n );\n}\n\n/**\n * Fetches the remote component properties from the server. You need to pass these properties to the `<RemoteComponent>` component to render the fetched remote component.\n *\n * @param src - The source URL of the remote component. When using the Vercel Microfrontends solution, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param headers - The HTTP headers used for supporting the Vercel Microfrontends proxy.\n * @returns The properties of the remote component.\n *\n * @example\n *\n * ```tsx\n * import { getRemoteComponentProps } from 'remote-components/next/host/pages';\n * import type { GetServerSideProps } from 'next';\n *\n * export const getServerSideProps: GetServerSideProps<PageProps> = async function getServerSideProps({ req }) {\n * const myRemoteComponent = await getRemoteComponentProps(\n * '/nextjs-app-remote/components/header',\n * req.headers,\n * );\n * return {\n * props: {\n * remoteComponents: {\n * myRemoteComponent,\n * },\n * },\n * };\n * }\n * ```\n */\nexport async function getRemoteComponentProps(\n src: string,\n headers: IncomingHttpHeaders,\n): Promise<RemoteComponentProps> {\n if (typeof document !== 'undefined') {\n throw new Error(\n 'getRemoteComponentProps can only be used on the server side.',\n );\n }\n\n const {\n metadata: { bundle, route, runtime },\n name,\n links,\n component,\n nextData,\n url,\n } = await fetchRemoteComponent(\n src,\n headers instanceof Headers\n ? headers\n : // convert IncomingHttpHeaders to web standard Headers\n Object.entries(headers).reduce((acc, [key, value]) => {\n if (value) {\n if (Array.isArray(value)) {\n value.forEach((v) => acc.append(key, v));\n } else {\n acc.append(key, value);\n }\n }\n return acc;\n }, new Headers()),\n {\n rsc: true,\n },\n );\n\n const props = {\n src,\n bundle,\n name,\n route,\n runtime,\n buildId: nextData?.buildId ?? null,\n url: url.href,\n };\n\n const key = getKey(props);\n\n // do not render the HTML in development mode when remote is using Next.js Pages Router\n // this behavior is emulating the Next.js Pages Router FOUC as the styles are only applied on the client when running in development mode\n if (nextData?.buildId !== 'development') {\n // store the HTML in a global store\n setComponent(\n key,\n <>\n {links.map((link) => (\n <link\n key={`${link.as}_${link.href}`}\n {...link}\n precedence={undefined}\n />\n ))}\n {component}\n </>,\n );\n }\n\n return {\n ...props,\n // add remote component key to the props\n [REMOTE_COMPONENT_KEY]: key,\n };\n}\n"],"mappings":"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","names":["self","key"]}
|
package/dist/react/index.cjs
CHANGED
|
@@ -47,6 +47,8 @@ function getRemoteComponentHtml(html) {
|
|
|
47
47
|
function RemoteComponent({
|
|
48
48
|
src,
|
|
49
49
|
isolate,
|
|
50
|
+
mode = "open",
|
|
51
|
+
reset,
|
|
50
52
|
credentials = "same-origin",
|
|
51
53
|
name: nameProp = "__vercel_remote_component",
|
|
52
54
|
shared = {},
|
|
@@ -69,16 +71,21 @@ function RemoteComponent({
|
|
|
69
71
|
const [data, setData] = (0, import_react.useState)(null);
|
|
70
72
|
const [remoteComponent, setRemoteComponent] = (0, import_react.useState)(null);
|
|
71
73
|
const shadowRootContainerRef = (0, import_react.useRef)(null);
|
|
72
|
-
const [shadowRoot, setShadowRoot] = (0, import_react.useState)(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
74
|
+
const [shadowRoot, setShadowRoot] = (0, import_react.useState)(() => {
|
|
75
|
+
const self = globalThis;
|
|
76
|
+
const shadowRootKey = `__remote_components_shadowroot_${src ? new URL(src, typeof location !== "undefined" ? location.origin : "http://localhost").href.replace(/[^a-z0-9]/g, "_") : ""}_${(data?.name ?? name).replace(/[^a-z0-9]/g, "_")}`;
|
|
77
|
+
const ssrShadowRoot = typeof document !== "undefined" ? document.getElementById(
|
|
78
|
+
`shadowroot_${src ? new URL(src, typeof location !== "undefined" ? location.origin : "http://localhost").href.replace(/[^a-z0-9]/g, "_") : ""}_${(data?.name ?? name).replace(/[^a-z0-9]/g, "_")}`
|
|
79
|
+
)?.shadowRoot ?? self[shadowRootKey] ?? null : null;
|
|
80
|
+
self[shadowRootKey] = null;
|
|
81
|
+
return ssrShadowRoot;
|
|
82
|
+
});
|
|
77
83
|
const htmlRef = (0, import_react.useRef)(
|
|
78
84
|
typeof document !== "undefined" ? document.getElementById(
|
|
79
|
-
`shadowroot_${src ? new URL(src, typeof location !== "undefined" ? location.origin : "http://localhost").href.replace(/[^a-z0-9]/g, "_") : ""}_${name}`
|
|
80
|
-
)?.shadowRoot?.innerHTML ?? document.getElementById(`__REMOTE_COMPONENT${name}`)?.innerHTML : null
|
|
85
|
+
`shadowroot_${src ? new URL(src, typeof location !== "undefined" ? location.origin : "http://localhost").href.replace(/[^a-z0-9]/g, "_") : ""}_${(data?.name ?? name).replace(/[^a-z0-9]/g, "_")}`
|
|
86
|
+
)?.shadowRoot?.innerHTML ?? document.getElementById(`__REMOTE_COMPONENT${name}`)?.innerHTML ?? document.querySelector(`div[data-bundle][data-route][id^="${name}"]`)?.innerHTML ?? document.querySelector("div[data-bundle][data-route]")?.innerHTML : null
|
|
81
87
|
);
|
|
88
|
+
const ssrHtmlRef = (0, import_react.useRef)(htmlRef.current);
|
|
82
89
|
const endTemplateRef = (0, import_react.useRef)(null);
|
|
83
90
|
const childrenRef = (0, import_react.useRef)(
|
|
84
91
|
typeof document !== "undefined" ? (() => {
|
|
@@ -102,11 +109,15 @@ function RemoteComponent({
|
|
|
102
109
|
}
|
|
103
110
|
if (isolate !== false && typeof document !== "undefined" && !shadowRoot) {
|
|
104
111
|
let shadowRootElement = null;
|
|
105
|
-
const element = document.getElementById(
|
|
112
|
+
const element = document.getElementById(
|
|
113
|
+
`shadowroot_${src ? new URL(src, typeof location !== "undefined" ? location.origin : "http://localhost").href.replace(/[^a-z0-9]/g, "_") : ""}_${(data?.name ?? name).replace(/[^a-z0-9]/g, "_")}`
|
|
114
|
+
);
|
|
106
115
|
shadowRootElement = element?.shadowRoot ?? null;
|
|
107
116
|
if (!shadowRootElement && element) {
|
|
108
|
-
|
|
109
|
-
|
|
117
|
+
try {
|
|
118
|
+
shadowRootElement = element.attachShadow({ mode });
|
|
119
|
+
} catch {
|
|
120
|
+
}
|
|
110
121
|
}
|
|
111
122
|
if (shadowRootElement) {
|
|
112
123
|
shadowRootElement.querySelectorAll("*:not(link)").forEach((node) => {
|
|
@@ -115,7 +126,7 @@ function RemoteComponent({
|
|
|
115
126
|
setShadowRoot(shadowRootElement);
|
|
116
127
|
}
|
|
117
128
|
}
|
|
118
|
-
}, [name, isolate, shadowRoot, remoteComponent]);
|
|
129
|
+
}, [name, isolate, shadowRoot, remoteComponent, mode]);
|
|
119
130
|
const url = (0, import_react.useMemo)(() => {
|
|
120
131
|
if (typeof src !== "string")
|
|
121
132
|
return new URL(
|
|
@@ -319,15 +330,16 @@ function RemoteComponent({
|
|
|
319
330
|
remoteComponent ?? children
|
|
320
331
|
] });
|
|
321
332
|
if (isolate !== false) {
|
|
322
|
-
const shadowRemoteComponentHtml = shadowRoot?.querySelector(
|
|
323
|
-
`#__REMOTE_COMPONENT${name}`
|
|
324
|
-
);
|
|
333
|
+
const shadowRemoteComponentHtml = shadowRoot?.querySelector(`#__REMOTE_COMPONENT${name}`) ?? shadowRoot?.querySelector("div[data-bundle][data-route]");
|
|
325
334
|
if (shadowRemoteComponentHtml) {
|
|
326
335
|
shadowRemoteComponentHtml.remove();
|
|
327
336
|
}
|
|
328
337
|
if (shadowRoot && remoteComponent && htmlRef.current) {
|
|
329
|
-
|
|
330
|
-
|
|
338
|
+
if (ssrHtmlRef.current) {
|
|
339
|
+
const content = shadowRoot.querySelectorAll(":not(link,style)");
|
|
340
|
+
content.forEach((node) => node.remove());
|
|
341
|
+
ssrHtmlRef.current = null;
|
|
342
|
+
}
|
|
331
343
|
htmlRef.current = null;
|
|
332
344
|
}
|
|
333
345
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
@@ -335,12 +347,35 @@ function RemoteComponent({
|
|
|
335
347
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
336
348
|
"div",
|
|
337
349
|
{
|
|
338
|
-
id: `shadowroot_${src ? new URL(src, typeof location !== "undefined" ? location.origin : "http://localhost").href.replace(/[^a-z0-9]/g, "_") : ""}_${data?.name ?? name}`,
|
|
350
|
+
id: `shadowroot_${src ? new URL(src, typeof location !== "undefined" ? location.origin : "http://localhost").href.replace(/[^a-z0-9]/g, "_") : ""}_${(data?.name ?? name).replace(/[^a-z0-9]/g, "_")}`,
|
|
339
351
|
ref: shadowRootContainerRef,
|
|
340
352
|
children: [
|
|
341
353
|
typeof document === "undefined" ? (
|
|
342
354
|
// eslint-disable-next-line react/no-unknown-property
|
|
343
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("template", { shadowrootmode:
|
|
355
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("template", { shadowrootmode: mode, children: [
|
|
356
|
+
typeof document === "undefined" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
357
|
+
"div",
|
|
358
|
+
{
|
|
359
|
+
dangerouslySetInnerHTML: {
|
|
360
|
+
__html: `<img
|
|
361
|
+
alt="" decoding="async" style="display:none"
|
|
362
|
+
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="
|
|
363
|
+
onload="(function(el){
|
|
364
|
+
const root = el.getRootNode();
|
|
365
|
+
globalThis.__remote_components_shadowroot_${src ? new URL(src, typeof location !== "undefined" ? location.origin : "http://localhost").href.replace(/[^a-z0-9]/g, "_") : ""}_${(data?.name ?? name).replace(/[^a-z0-9]/g, "_")} = root;
|
|
366
|
+
el.parentElement.remove();
|
|
367
|
+
})(this)"
|
|
368
|
+
/>`
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
) : null,
|
|
372
|
+
reset ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
373
|
+
"style",
|
|
374
|
+
{
|
|
375
|
+
"data-remote-components-reset": true,
|
|
376
|
+
children: `:host { all: initial; }`
|
|
377
|
+
}
|
|
378
|
+
) : null,
|
|
344
379
|
linksToRender,
|
|
345
380
|
children
|
|
346
381
|
] })
|