remote-components 0.0.32 → 0.0.33
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/{component-loader-e5513139.d.ts → component-loader-28ad0083.d.ts} +1 -0
- package/dist/html/host.cjs +105 -28
- package/dist/html/host.cjs.map +1 -1
- package/dist/html/host.js +105 -28
- package/dist/html/host.js.map +1 -1
- package/dist/internal/next/host/app-router-client.cjs +6 -2
- 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 +6 -2
- package/dist/internal/next/host/app-router-client.js.map +1 -1
- package/dist/internal/next/remote/render-client.cjs +13 -3
- package/dist/internal/next/remote/render-client.cjs.map +1 -1
- package/dist/internal/next/remote/render-client.js +13 -3
- package/dist/internal/next/remote/render-client.js.map +1 -1
- package/dist/internal/next/remote/render-server.cjs +22 -3
- package/dist/internal/next/remote/render-server.cjs.map +1 -1
- package/dist/internal/next/remote/render-server.js +23 -4
- package/dist/internal/next/remote/render-server.js.map +1 -1
- package/dist/internal/shared/client/remote-component.cjs +35 -17
- package/dist/internal/shared/client/remote-component.cjs.map +1 -1
- package/dist/internal/shared/client/remote-component.d.ts +9 -2
- package/dist/internal/shared/client/remote-component.js +34 -17
- package/dist/internal/shared/client/remote-component.js.map +1 -1
- package/dist/internal/shared/error.cjs +50 -0
- package/dist/internal/shared/error.cjs.map +1 -0
- package/dist/internal/shared/error.d.ts +10 -0
- package/dist/internal/shared/error.js +24 -0
- package/dist/internal/shared/error.js.map +1 -0
- package/dist/internal/shared/ssr/dom-flight.cjs +8 -1
- package/dist/internal/shared/ssr/dom-flight.cjs.map +1 -1
- package/dist/internal/shared/ssr/dom-flight.d.ts +2 -1
- package/dist/internal/shared/ssr/dom-flight.js +8 -1
- package/dist/internal/shared/ssr/dom-flight.js.map +1 -1
- package/dist/internal/shared/ssr/fetch-remote-component.cjs +48 -5
- package/dist/internal/shared/ssr/fetch-remote-component.cjs.map +1 -1
- package/dist/internal/shared/ssr/fetch-remote-component.d.ts +1 -1
- package/dist/internal/shared/ssr/fetch-remote-component.js +52 -5
- package/dist/internal/shared/ssr/fetch-remote-component.js.map +1 -1
- package/dist/internal/webpack/next-client-pages-loader.cjs +3 -2
- package/dist/internal/webpack/next-client-pages-loader.cjs.map +1 -1
- package/dist/internal/webpack/next-client-pages-loader.js +3 -2
- package/dist/internal/webpack/next-client-pages-loader.js.map +1 -1
- package/dist/next/config.cjs +26 -9
- package/dist/next/config.cjs.map +1 -1
- package/dist/next/config.js +26 -9
- package/dist/next/config.js.map +1 -1
- package/dist/next/host/client/index.cjs +86 -29
- package/dist/next/host/client/index.cjs.map +1 -1
- package/dist/next/host/client/index.d.ts +1 -1
- package/dist/next/host/client/index.js +86 -29
- package/dist/next/host/client/index.js.map +1 -1
- package/dist/next/host/pages-router-client.cjs +1 -1
- package/dist/next/host/pages-router-client.cjs.map +1 -1
- package/dist/next/host/pages-router-client.js +1 -1
- package/dist/next/host/pages-router-client.js.map +1 -1
- package/dist/next/host/pages-router-server.cjs +4 -3
- package/dist/next/host/pages-router-server.cjs.map +1 -1
- package/dist/next/host/pages-router-server.js +4 -3
- package/dist/next/host/pages-router-server.js.map +1 -1
- package/dist/next/index.cjs +4 -2
- package/dist/next/index.cjs.map +1 -1
- package/dist/next/index.js +4 -2
- package/dist/next/index.js.map +1 -1
- package/dist/next/remote/pages-router.cjs +1 -1
- package/dist/next/remote/pages-router.cjs.map +1 -1
- package/dist/next/remote/pages-router.js +1 -1
- package/dist/next/remote/pages-router.js.map +1 -1
- package/dist/react/index.cjs +42 -11
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.ts +2 -2
- package/dist/react/index.js +46 -11
- package/dist/react/index.js.map +1 -1
- package/dist/shared/host/app.cjs +36 -0
- package/dist/shared/host/app.cjs.map +1 -0
- package/dist/shared/host/app.d.ts +3 -0
- package/dist/shared/host/app.js +12 -0
- package/dist/shared/host/app.js.map +1 -0
- package/dist/shared/host/pages.cjs +36 -0
- package/dist/shared/host/pages.cjs.map +1 -0
- package/dist/shared/host/pages.d.ts +3 -0
- package/dist/shared/host/pages.js +12 -0
- package/dist/shared/host/pages.js.map +1 -0
- package/dist/shared/remote/app.cjs +32 -0
- package/dist/shared/remote/app.cjs.map +1 -0
- package/dist/shared/remote/app.d.ts +3 -0
- package/dist/shared/remote/app.js +8 -0
- package/dist/shared/remote/app.js.map +1 -0
- package/dist/shared/remote/pages.cjs +32 -0
- package/dist/shared/remote/pages.cjs.map +1 -0
- package/dist/shared/remote/pages.d.ts +3 -0
- package/dist/shared/remote/pages.js +8 -0
- package/dist/shared/remote/pages.js.map +1 -0
- package/dist/shared/remote/wrapper.cjs +48 -0
- package/dist/shared/remote/wrapper.cjs.map +1 -0
- package/dist/shared/remote/wrapper.d.ts +2 -0
- package/dist/shared/remote/wrapper.js +25 -0
- package/dist/shared/remote/wrapper.js.map +1 -0
- package/dist/{types-b8210fd3.d.ts → types-7c207455.d.ts} +1 -1
- package/dist/{types-280a3640.d.ts → types-e4a3fa37.d.ts} +1 -0
- package/package.json +37 -1
|
@@ -33,7 +33,7 @@ __export(pages_router_client_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(pages_router_client_exports);
|
|
34
34
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
35
35
|
var import_react = require("react");
|
|
36
|
-
var import_pages = require("
|
|
36
|
+
var import_pages = require("remote-components/shared/host/pages");
|
|
37
37
|
function RemoteComponent({ src, children }) {
|
|
38
38
|
(0, import_react.useEffect)(() => {
|
|
39
39
|
const self = globalThis;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/host/pages-router-client.tsx"],"sourcesContent":["import { useEffect } from 'react';\nimport { shared } from '
|
|
1
|
+
{"version":3,"sources":["../../../src/next/host/pages-router-client.tsx"],"sourcesContent":["import { useEffect } from 'react';\nimport { shared } from 'remote-components/shared/host/pages';\n\n// patch react/jsx-runtime to support the remote-component custom element\ndeclare module 'react/jsx-runtime' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n 'remote-component': {\n src?: string;\n children: React.ReactNode;\n };\n }\n }\n}\n\nexport interface RemoteComponentProps {\n src: string;\n children?: React.ReactNode;\n}\n\nexport function RemoteComponent({ src, children }: RemoteComponentProps) {\n useEffect(() => {\n const self = globalThis as typeof globalThis & {\n __remote_component_shared__?: Record<string, () => Promise<unknown>>;\n };\n // eslint-disable-next-line camelcase\n self.__remote_component_shared__ = shared;\n import('remote-components/html/host');\n }, []);\n\n return <remote-component src={src}>{children}</remote-component>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AA+BS;AA/BT,mBAA0B;AAC1B,mBAAuB;AAoBhB,SAAS,gBAAgB,EAAE,KAAK,SAAS,GAAyB;AACvE,8BAAU,MAAM;AACd,UAAM,OAAO;AAIb,SAAK,8BAA8B;AACnC,WAAO,6BAA6B;AAAA,EACtC,GAAG,CAAC,CAAC;AAEL,SAAO,4CAAC,sBAAiB,KAAW,UAAS;AAC/C;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
|
-
import { shared } from "
|
|
3
|
+
import { shared } from "remote-components/shared/host/pages";
|
|
4
4
|
function RemoteComponent({ src, children }) {
|
|
5
5
|
useEffect(() => {
|
|
6
6
|
const self = globalThis;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/host/pages-router-client.tsx"],"sourcesContent":["import { useEffect } from 'react';\nimport { shared } from '
|
|
1
|
+
{"version":3,"sources":["../../../src/next/host/pages-router-client.tsx"],"sourcesContent":["import { useEffect } from 'react';\nimport { shared } from 'remote-components/shared/host/pages';\n\n// patch react/jsx-runtime to support the remote-component custom element\ndeclare module 'react/jsx-runtime' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n export namespace JSX {\n interface IntrinsicElements {\n 'remote-component': {\n src?: string;\n children: React.ReactNode;\n };\n }\n }\n}\n\nexport interface RemoteComponentProps {\n src: string;\n children?: React.ReactNode;\n}\n\nexport function RemoteComponent({ src, children }: RemoteComponentProps) {\n useEffect(() => {\n const self = globalThis as typeof globalThis & {\n __remote_component_shared__?: Record<string, () => Promise<unknown>>;\n };\n // eslint-disable-next-line camelcase\n self.__remote_component_shared__ = shared;\n import('remote-components/html/host');\n }, []);\n\n return <remote-component src={src}>{children}</remote-component>;\n}\n"],"mappings":"AA+BS;AA/BT,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AAoBhB,SAAS,gBAAgB,EAAE,KAAK,SAAS,GAAyB;AACvE,YAAU,MAAM;AACd,UAAM,OAAO;AAIb,SAAK,8BAA8B;AACnC,WAAO,6BAA6B;AAAA,EACtC,GAAG,CAAC,CAAC;AAEL,SAAO,oBAAC,sBAAiB,KAAW,UAAS;AAC/C;","names":[]}
|
|
@@ -34,14 +34,15 @@ __export(pages_router_server_exports, {
|
|
|
34
34
|
module.exports = __toCommonJS(pages_router_server_exports);
|
|
35
35
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
36
36
|
var import_react = require("react");
|
|
37
|
-
var import_pages = require("@remote-components/shared/host/pages");
|
|
38
37
|
var Form = __toESM(require("next/form"), 1);
|
|
39
38
|
var Image = __toESM(require("next/image"), 1);
|
|
40
39
|
var Link = __toESM(require("next/link"), 1);
|
|
41
40
|
var Script = __toESM(require("next/script"), 1);
|
|
42
41
|
var Router = __toESM(require("next/router"), 1);
|
|
42
|
+
var import_pages = require("remote-components/shared/host/pages");
|
|
43
43
|
var import_react2 = require("#remote-components/react/index");
|
|
44
44
|
var import_fetch_remote_component = require("#internal/shared/ssr/fetch-remote-component");
|
|
45
|
+
var import_remote_component = require("#internal/shared/client/remote-component");
|
|
45
46
|
const navigationImpl = {
|
|
46
47
|
useRouter: () => Router.useRouter(),
|
|
47
48
|
useSearchParams: () => {
|
|
@@ -165,8 +166,8 @@ function RemoteComponent(props) {
|
|
|
165
166
|
}
|
|
166
167
|
async function getRemoteComponentProps(src, headers) {
|
|
167
168
|
if (typeof document !== "undefined") {
|
|
168
|
-
throw new
|
|
169
|
-
"getRemoteComponentProps can only be used on the server side."
|
|
169
|
+
throw new import_remote_component.RemoteComponentsError(
|
|
170
|
+
"`getRemoteComponentProps()` can only be used on the server side."
|
|
170
171
|
);
|
|
171
172
|
}
|
|
172
173
|
const {
|
|
@@ -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 * as Router 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: () => Router.useRouter(),\n useSearchParams: () => {\n const router = 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 function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n }\n RemoteImage.default = RemoteImage;\n RemoteImage.getImageProps = Image.getImageProps;\n return RemoteImage;\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/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 'next/router': () => Promise.resolve(Router) as Promise<unknown>,\n ..._shared,\n // always override next/image to use the remote loader\n 'next/image': () =>\n Promise.resolve(\n // @ts-expect-error default.default\n // eslint-disable-next-line\n imageImpl(Image.default.default ?? Image.default),\n ) as Promise<unknown>,\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 additionalHeaders?: Headers | Record<string, string>;\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 additionalHeaders={props.additionalHeaders}\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 additionalHeaders={props.additionalHeaders}\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 remoteShared,\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 <script\n data-remote-components-shared=\"\"\n id={`${name}_shared`}\n type=\"application/json\"\n >\n {JSON.stringify(remoteShared)}\n </script>\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 * as Form from 'next/form';\nimport * as Image from 'next/image';\nimport * as Link from 'next/link';\nimport * as Script from 'next/script';\nimport * as Router from 'next/router';\nimport { shared as _shared } from 'remote-components/shared/host/pages';\nimport { RemoteComponent as RemoteComponentReact } from '#remote-components/react/index';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\nimport { RemoteComponentsError } from '#internal/shared/client/remote-component';\n\nconst navigationImpl = {\n useRouter: () => Router.useRouter(),\n useSearchParams: () => {\n const router = 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 function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n }\n RemoteImage.default = RemoteImage;\n RemoteImage.getImageProps = Image.getImageProps;\n return RemoteImage;\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/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 'next/router': () => Promise.resolve(Router) as Promise<unknown>,\n ..._shared,\n // always override next/image to use the remote loader\n 'next/image': () =>\n Promise.resolve(\n // @ts-expect-error default.default\n // eslint-disable-next-line\n imageImpl(Image.default.default ?? Image.default),\n ) as Promise<unknown>,\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 additionalHeaders?: Headers | Record<string, string>;\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 additionalHeaders={props.additionalHeaders}\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 additionalHeaders={props.additionalHeaders}\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 RemoteComponentsError(\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 remoteShared,\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 <script\n data-remote-components-shared=\"\"\n id={`${name}_shared`}\n type=\"application/json\"\n >\n {JSON.stringify(remoteShared)}\n </script>\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,13 +1,14 @@
|
|
|
1
1
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
|
-
import { shared as _shared } from "@remote-components/shared/host/pages";
|
|
4
3
|
import * as Form from "next/form";
|
|
5
4
|
import * as Image from "next/image";
|
|
6
5
|
import * as Link from "next/link";
|
|
7
6
|
import * as Script from "next/script";
|
|
8
7
|
import * as Router from "next/router";
|
|
8
|
+
import { shared as _shared } from "remote-components/shared/host/pages";
|
|
9
9
|
import { RemoteComponent as RemoteComponentReact } from "#remote-components/react/index";
|
|
10
10
|
import { fetchRemoteComponent } from "#internal/shared/ssr/fetch-remote-component";
|
|
11
|
+
import { RemoteComponentsError } from "#internal/shared/client/remote-component";
|
|
11
12
|
const navigationImpl = {
|
|
12
13
|
useRouter: () => Router.useRouter(),
|
|
13
14
|
useSearchParams: () => {
|
|
@@ -131,8 +132,8 @@ function RemoteComponent(props) {
|
|
|
131
132
|
}
|
|
132
133
|
async function getRemoteComponentProps(src, headers) {
|
|
133
134
|
if (typeof document !== "undefined") {
|
|
134
|
-
throw new
|
|
135
|
-
"getRemoteComponentProps can only be used on the server side."
|
|
135
|
+
throw new RemoteComponentsError(
|
|
136
|
+
"`getRemoteComponentProps()` can only be used on the server side."
|
|
136
137
|
);
|
|
137
138
|
}
|
|
138
139
|
const {
|
|
@@ -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 * as Router 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: () => Router.useRouter(),\n useSearchParams: () => {\n const router = 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 function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n }\n RemoteImage.default = RemoteImage;\n RemoteImage.getImageProps = Image.getImageProps;\n return RemoteImage;\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/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 'next/router': () => Promise.resolve(Router) as Promise<unknown>,\n ..._shared,\n // always override next/image to use the remote loader\n 'next/image': () =>\n Promise.resolve(\n // @ts-expect-error default.default\n // eslint-disable-next-line\n imageImpl(Image.default.default ?? Image.default),\n ) as Promise<unknown>,\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 additionalHeaders?: Headers | Record<string, string>;\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 additionalHeaders={props.additionalHeaders}\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 additionalHeaders={props.additionalHeaders}\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 remoteShared,\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 <script\n data-remote-components-shared=\"\"\n id={`${name}_shared`}\n type=\"application/json\"\n >\n {JSON.stringify(remoteShared)}\n </script>\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 * as Form from 'next/form';\nimport * as Image from 'next/image';\nimport * as Link from 'next/link';\nimport * as Script from 'next/script';\nimport * as Router from 'next/router';\nimport { shared as _shared } from 'remote-components/shared/host/pages';\nimport { RemoteComponent as RemoteComponentReact } from '#remote-components/react/index';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\nimport { RemoteComponentsError } from '#internal/shared/client/remote-component';\n\nconst navigationImpl = {\n useRouter: () => Router.useRouter(),\n useSearchParams: () => {\n const router = 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 function RemoteImage(props: Image.ImageProps) {\n return <ImageComponent loader={remoteLoader} {...props} />;\n }\n RemoteImage.default = RemoteImage;\n RemoteImage.getImageProps = Image.getImageProps;\n return RemoteImage;\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/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 'next/router': () => Promise.resolve(Router) as Promise<unknown>,\n ..._shared,\n // always override next/image to use the remote loader\n 'next/image': () =>\n Promise.resolve(\n // @ts-expect-error default.default\n // eslint-disable-next-line\n imageImpl(Image.default.default ?? Image.default),\n ) as Promise<unknown>,\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 additionalHeaders?: Headers | Record<string, string>;\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 additionalHeaders={props.additionalHeaders}\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 additionalHeaders={props.additionalHeaders}\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 RemoteComponentsError(\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 remoteShared,\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 <script\n data-remote-components-shared=\"\"\n id={`${name}_shared`}\n type=\"application/json\"\n >\n {JSON.stringify(remoteShared)}\n </script>\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/next/index.cjs
CHANGED
|
@@ -31,6 +31,8 @@ __export(next_exports, {
|
|
|
31
31
|
RemoteComponent: () => RemoteComponent
|
|
32
32
|
});
|
|
33
33
|
module.exports = __toCommonJS(next_exports);
|
|
34
|
+
var import_wrapper = require("remote-components/shared/remote/wrapper");
|
|
35
|
+
var import_error = require("#internal/shared/error");
|
|
34
36
|
async function RemoteComponent(props) {
|
|
35
37
|
if ("src" in props) {
|
|
36
38
|
const { RemoteComponent: RemoteComponentAppServer } = await import("#remote-components/next/host/app-router-server");
|
|
@@ -40,8 +42,8 @@ async function RemoteComponent(props) {
|
|
|
40
42
|
const { RemoteComponent: RemoteComponentRender } = await import("#internal/next/remote/render-server");
|
|
41
43
|
return RemoteComponentRender(props);
|
|
42
44
|
}
|
|
43
|
-
throw new
|
|
44
|
-
'Invalid props passed to RemoteComponent
|
|
45
|
+
throw new import_error.RemoteComponentsError(
|
|
46
|
+
'Invalid props passed to <RemoteComponent>. Expected either "src" or "children".'
|
|
45
47
|
);
|
|
46
48
|
}
|
|
47
49
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/next/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/next/index.tsx"],"sourcesContent":["import type { RemoteComponent as RemoteComponentRenderType } from '#internal/next/remote/render-server';\nimport type { RemoteComponent as RemoteComponentAppServerType } from '#remote-components/next/host/app-router-server';\n\ntype RemoteComponentAppServerProps = Parameters<\n typeof RemoteComponentAppServerType\n>[0];\ntype RemoteComponentRenderProps = Parameters<\n typeof RemoteComponentRenderType\n>[0];\ntype RemoteComponentProps =\n | RemoteComponentAppServerProps\n | RemoteComponentRenderProps;\n\n/**\n * RemoteComponent is a Next.js component that exposes or consumes a remote component\n * that can be used in a host application.\n *\n * @param src - [host] The source URL of the remote component. Use this to consume a remote component from another application.\n * @param name - [host & remote] The name of the remote component. Use a unique name to expose multiple remote components from the same page.\n * @param isolate - [host] Whether to isolate the remote component's styles and scripts. Defaults to true.\n * @param mode - [host] The mode of the Shadow DOM. Defaults to `open`.\n * @param reset - [host] Whether to include a CSS reset style in the shadow DOM. Defaults to `false`.\n * @param additionalHeaders - [host] Additional headers to use when fetching the remote component.\n * @param children - [host & remote] Either the content of the remote component. Or when src is supplied, the fallback content to display while the remote component is being fetched.\n * @returns Either a React component that renders the remote component or a Next.js component that exposes the children as a remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to expose the children as a remote component without the `src` prop:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next';\n *\n * export default function MyPage() {\n * return (\n * <RemoteComponent>\n * <h1>Hello from the remote component!</h1>\n * <p>This is a remote component that can be used in a host application.</p>\n * </RemoteComponent>\n * );\n * }\n * ```\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to consume a remote component from another application with the `src` prop:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next';\n *\n * export default function MyPage() {\n * return (\n * <>\n * <RemoteComponent src=\"https://example.com/remote-component\" />\n * </>\n * );\n * }\n * ```\n *\n * The `children` of the `<RemoteComponent>` will be exposed as a remote component or it will be used as the loading fallback when consuming a remote component.\n */\nexport async function RemoteComponent(\n props: RemoteComponentProps,\n): Promise<React.ReactElement> {\n if ('src' in props) {\n const { RemoteComponent: RemoteComponentAppServer } = await import(\n '#remote-components/next/host/app-router-server'\n );\n return RemoteComponentAppServer(props);\n }\n\n if ('children' in props) {\n const { RemoteComponent: RemoteComponentRender } = await import(\n '#internal/next/remote/render-server'\n );\n return RemoteComponentRender(props);\n }\n\n throw new
|
|
1
|
+
{"version":3,"sources":["../../src/next/index.tsx"],"sourcesContent":["import 'remote-components/shared/remote/wrapper';\nimport type { RemoteComponent as RemoteComponentRenderType } from '#internal/next/remote/render-server';\nimport { RemoteComponentsError } from '#internal/shared/error';\nimport type { RemoteComponent as RemoteComponentAppServerType } from '#remote-components/next/host/app-router-server';\n\ntype RemoteComponentAppServerProps = Parameters<\n typeof RemoteComponentAppServerType\n>[0];\ntype RemoteComponentRenderProps = Parameters<\n typeof RemoteComponentRenderType\n>[0];\ntype RemoteComponentProps =\n | RemoteComponentAppServerProps\n | RemoteComponentRenderProps;\n\n/**\n * RemoteComponent is a Next.js component that exposes or consumes a remote component\n * that can be used in a host application.\n *\n * @param src - [host] The source URL of the remote component. Use this to consume a remote component from another application.\n * @param name - [host & remote] The name of the remote component. Use a unique name to expose multiple remote components from the same page.\n * @param isolate - [host] Whether to isolate the remote component's styles and scripts. Defaults to true.\n * @param mode - [host] The mode of the Shadow DOM. Defaults to `open`.\n * @param reset - [host] Whether to include a CSS reset style in the shadow DOM. Defaults to `false`.\n * @param additionalHeaders - [host] Additional headers to use when fetching the remote component.\n * @param children - [host & remote] Either the content of the remote component. Or when src is supplied, the fallback content to display while the remote component is being fetched.\n * @returns Either a React component that renders the remote component or a Next.js component that exposes the children as a remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to expose the children as a remote component without the `src` prop:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next';\n *\n * export default function MyPage() {\n * return (\n * <RemoteComponent>\n * <h1>Hello from the remote component!</h1>\n * <p>This is a remote component that can be used in a host application.</p>\n * </RemoteComponent>\n * );\n * }\n * ```\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to consume a remote component from another application with the `src` prop:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next';\n *\n * export default function MyPage() {\n * return (\n * <>\n * <RemoteComponent src=\"https://example.com/remote-component\" />\n * </>\n * );\n * }\n * ```\n *\n * The `children` of the `<RemoteComponent>` will be exposed as a remote component or it will be used as the loading fallback when consuming a remote component.\n */\nexport async function RemoteComponent(\n props: RemoteComponentProps,\n): Promise<React.ReactElement> {\n if ('src' in props) {\n const { RemoteComponent: RemoteComponentAppServer } = await import(\n '#remote-components/next/host/app-router-server'\n );\n return RemoteComponentAppServer(props);\n }\n\n if ('children' in props) {\n const { RemoteComponent: RemoteComponentRender } = await import(\n '#internal/next/remote/render-server'\n );\n return RemoteComponentRender(props);\n }\n\n throw new RemoteComponentsError(\n 'Invalid props passed to <RemoteComponent>. Expected either \"src\" or \"children\".',\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qBAAO;AAEP,mBAAsC;AA2DtC,eAAsB,gBACpB,OAC6B;AAC7B,MAAI,SAAS,OAAO;AAClB,UAAM,EAAE,iBAAiB,yBAAyB,IAAI,MAAM,OAC1D,gDACF;AACA,WAAO,yBAAyB,KAAK;AAAA,EACvC;AAEA,MAAI,cAAc,OAAO;AACvB,UAAM,EAAE,iBAAiB,sBAAsB,IAAI,MAAM,OACvD,qCACF;AACA,WAAO,sBAAsB,KAAK;AAAA,EACpC;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;","names":[]}
|
package/dist/next/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import "remote-components/shared/remote/wrapper";
|
|
2
|
+
import { RemoteComponentsError } from "#internal/shared/error";
|
|
1
3
|
async function RemoteComponent(props) {
|
|
2
4
|
if ("src" in props) {
|
|
3
5
|
const { RemoteComponent: RemoteComponentAppServer } = await import("#remote-components/next/host/app-router-server");
|
|
@@ -7,8 +9,8 @@ async function RemoteComponent(props) {
|
|
|
7
9
|
const { RemoteComponent: RemoteComponentRender } = await import("#internal/next/remote/render-server");
|
|
8
10
|
return RemoteComponentRender(props);
|
|
9
11
|
}
|
|
10
|
-
throw new
|
|
11
|
-
'Invalid props passed to RemoteComponent
|
|
12
|
+
throw new RemoteComponentsError(
|
|
13
|
+
'Invalid props passed to <RemoteComponent>. Expected either "src" or "children".'
|
|
12
14
|
);
|
|
13
15
|
}
|
|
14
16
|
export {
|
package/dist/next/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/next/index.tsx"],"sourcesContent":["import type { RemoteComponent as RemoteComponentRenderType } from '#internal/next/remote/render-server';\nimport type { RemoteComponent as RemoteComponentAppServerType } from '#remote-components/next/host/app-router-server';\n\ntype RemoteComponentAppServerProps = Parameters<\n typeof RemoteComponentAppServerType\n>[0];\ntype RemoteComponentRenderProps = Parameters<\n typeof RemoteComponentRenderType\n>[0];\ntype RemoteComponentProps =\n | RemoteComponentAppServerProps\n | RemoteComponentRenderProps;\n\n/**\n * RemoteComponent is a Next.js component that exposes or consumes a remote component\n * that can be used in a host application.\n *\n * @param src - [host] The source URL of the remote component. Use this to consume a remote component from another application.\n * @param name - [host & remote] The name of the remote component. Use a unique name to expose multiple remote components from the same page.\n * @param isolate - [host] Whether to isolate the remote component's styles and scripts. Defaults to true.\n * @param mode - [host] The mode of the Shadow DOM. Defaults to `open`.\n * @param reset - [host] Whether to include a CSS reset style in the shadow DOM. Defaults to `false`.\n * @param additionalHeaders - [host] Additional headers to use when fetching the remote component.\n * @param children - [host & remote] Either the content of the remote component. Or when src is supplied, the fallback content to display while the remote component is being fetched.\n * @returns Either a React component that renders the remote component or a Next.js component that exposes the children as a remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to expose the children as a remote component without the `src` prop:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next';\n *\n * export default function MyPage() {\n * return (\n * <RemoteComponent>\n * <h1>Hello from the remote component!</h1>\n * <p>This is a remote component that can be used in a host application.</p>\n * </RemoteComponent>\n * );\n * }\n * ```\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to consume a remote component from another application with the `src` prop:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next';\n *\n * export default function MyPage() {\n * return (\n * <>\n * <RemoteComponent src=\"https://example.com/remote-component\" />\n * </>\n * );\n * }\n * ```\n *\n * The `children` of the `<RemoteComponent>` will be exposed as a remote component or it will be used as the loading fallback when consuming a remote component.\n */\nexport async function RemoteComponent(\n props: RemoteComponentProps,\n): Promise<React.ReactElement> {\n if ('src' in props) {\n const { RemoteComponent: RemoteComponentAppServer } = await import(\n '#remote-components/next/host/app-router-server'\n );\n return RemoteComponentAppServer(props);\n }\n\n if ('children' in props) {\n const { RemoteComponent: RemoteComponentRender } = await import(\n '#internal/next/remote/render-server'\n );\n return RemoteComponentRender(props);\n }\n\n throw new
|
|
1
|
+
{"version":3,"sources":["../../src/next/index.tsx"],"sourcesContent":["import 'remote-components/shared/remote/wrapper';\nimport type { RemoteComponent as RemoteComponentRenderType } from '#internal/next/remote/render-server';\nimport { RemoteComponentsError } from '#internal/shared/error';\nimport type { RemoteComponent as RemoteComponentAppServerType } from '#remote-components/next/host/app-router-server';\n\ntype RemoteComponentAppServerProps = Parameters<\n typeof RemoteComponentAppServerType\n>[0];\ntype RemoteComponentRenderProps = Parameters<\n typeof RemoteComponentRenderType\n>[0];\ntype RemoteComponentProps =\n | RemoteComponentAppServerProps\n | RemoteComponentRenderProps;\n\n/**\n * RemoteComponent is a Next.js component that exposes or consumes a remote component\n * that can be used in a host application.\n *\n * @param src - [host] The source URL of the remote component. Use this to consume a remote component from another application.\n * @param name - [host & remote] The name of the remote component. Use a unique name to expose multiple remote components from the same page.\n * @param isolate - [host] Whether to isolate the remote component's styles and scripts. Defaults to true.\n * @param mode - [host] The mode of the Shadow DOM. Defaults to `open`.\n * @param reset - [host] Whether to include a CSS reset style in the shadow DOM. Defaults to `false`.\n * @param additionalHeaders - [host] Additional headers to use when fetching the remote component.\n * @param children - [host & remote] Either the content of the remote component. Or when src is supplied, the fallback content to display while the remote component is being fetched.\n * @returns Either a React component that renders the remote component or a Next.js component that exposes the children as a remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to expose the children as a remote component without the `src` prop:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next';\n *\n * export default function MyPage() {\n * return (\n * <RemoteComponent>\n * <h1>Hello from the remote component!</h1>\n * <p>This is a remote component that can be used in a host application.</p>\n * </RemoteComponent>\n * );\n * }\n * ```\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to consume a remote component from another application with the `src` prop:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next';\n *\n * export default function MyPage() {\n * return (\n * <>\n * <RemoteComponent src=\"https://example.com/remote-component\" />\n * </>\n * );\n * }\n * ```\n *\n * The `children` of the `<RemoteComponent>` will be exposed as a remote component or it will be used as the loading fallback when consuming a remote component.\n */\nexport async function RemoteComponent(\n props: RemoteComponentProps,\n): Promise<React.ReactElement> {\n if ('src' in props) {\n const { RemoteComponent: RemoteComponentAppServer } = await import(\n '#remote-components/next/host/app-router-server'\n );\n return RemoteComponentAppServer(props);\n }\n\n if ('children' in props) {\n const { RemoteComponent: RemoteComponentRender } = await import(\n '#internal/next/remote/render-server'\n );\n return RemoteComponentRender(props);\n }\n\n throw new RemoteComponentsError(\n 'Invalid props passed to <RemoteComponent>. Expected either \"src\" or \"children\".',\n );\n}\n"],"mappings":"AAAA,OAAO;AAEP,SAAS,6BAA6B;AA2DtC,eAAsB,gBACpB,OAC6B;AAC7B,MAAI,SAAS,OAAO;AAClB,UAAM,EAAE,iBAAiB,yBAAyB,IAAI,MAAM,OAC1D,gDACF;AACA,WAAO,yBAAyB,KAAK;AAAA,EACvC;AAEA,MAAI,cAAc,OAAO;AACvB,UAAM,EAAE,iBAAiB,sBAAsB,IAAI,MAAM,OACvD,qCACF;AACA,WAAO,sBAAsB,KAAK;AAAA,EACpC;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,EACF;AACF;","names":[]}
|
|
@@ -21,7 +21,7 @@ __export(pages_router_exports, {
|
|
|
21
21
|
getRemoteComponentMetadata: () => getRemoteComponentMetadata
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(pages_router_exports);
|
|
24
|
-
var import_pages = require("
|
|
24
|
+
var import_pages = require("remote-components/shared/remote/pages");
|
|
25
25
|
const PROJECT_ID = process.env.REMOTE_COMPONENTS_PROJECT_ID || process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION || process.env.VERCEL_PROJECT_ID;
|
|
26
26
|
function getRemoteComponentMetadata() {
|
|
27
27
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/remote/pages-router.ts"],"sourcesContent":["import { shared } from '
|
|
1
|
+
{"version":3,"sources":["../../../src/next/remote/pages-router.ts"],"sourcesContent":["import { shared } from 'remote-components/shared/remote/pages';\n\nconst PROJECT_ID =\n process.env.REMOTE_COMPONENTS_PROJECT_ID ||\n process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION ||\n process.env.VERCEL_PROJECT_ID;\n\nexport interface RemoteComponentMetadata {\n __REMOTE_COMPONENT__: {\n bundle: string | undefined;\n runtime: 'turbopack' | 'webpack';\n shared?: Record<string, string>;\n };\n}\n\n/**\n * Returns the metadata for the remote component.\n * This metadata is used to identify the remote component and its bundle.\n *\n * Extend your Next.js Pages Router page props with this metadata to ensure proper remote component loading.\n *\n * @returns The metadata for the remote component.\n *\n * @example\n *\n * Create a custom App component in your Next.js application to include the remote component metadata:\n *\n * ```\n * import {\n * getRemoteComponentMetadata,\n * type RemoteComponentMetadata,\n * } from 'remote-components/next/pages';\n * import App from 'next/app';\n * import type { AppContext, AppInitialProps, AppProps } from 'next/app';\n *\n * export default function MyApp({ Component, pageProps }: AppProps) {\n * return <Component {...pageProps} />;\n * }\n *\n * MyApp.getInitialProps = async (\n * context: AppContext,\n * ): Promise<RemoteComponentMetadata & AppInitialProps> => {\n * const ctx = await App.getInitialProps(context);\n * return { ...ctx, ...getRemoteComponentMetadata() };\n * };\n * ```\n */\nexport function getRemoteComponentMetadata(): RemoteComponentMetadata {\n return {\n __REMOTE_COMPONENT__: {\n bundle: PROJECT_ID,\n runtime: process.env.TURBOPACK ? 'turbopack' : 'webpack',\n shared,\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAuB;AAEvB,MAAM,aACJ,QAAQ,IAAI,gCACZ,QAAQ,IAAI,uCACZ,QAAQ,IAAI;AA0CP,SAAS,6BAAsD;AACpE,SAAO;AAAA,IACL,sBAAsB;AAAA,MACpB,QAAQ;AAAA,MACR,SAAS,QAAQ,IAAI,YAAY,cAAc;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { shared } from "
|
|
1
|
+
import { shared } from "remote-components/shared/remote/pages";
|
|
2
2
|
const PROJECT_ID = process.env.REMOTE_COMPONENTS_PROJECT_ID || process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION || process.env.VERCEL_PROJECT_ID;
|
|
3
3
|
function getRemoteComponentMetadata() {
|
|
4
4
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/remote/pages-router.ts"],"sourcesContent":["import { shared } from '
|
|
1
|
+
{"version":3,"sources":["../../../src/next/remote/pages-router.ts"],"sourcesContent":["import { shared } from 'remote-components/shared/remote/pages';\n\nconst PROJECT_ID =\n process.env.REMOTE_COMPONENTS_PROJECT_ID ||\n process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION ||\n process.env.VERCEL_PROJECT_ID;\n\nexport interface RemoteComponentMetadata {\n __REMOTE_COMPONENT__: {\n bundle: string | undefined;\n runtime: 'turbopack' | 'webpack';\n shared?: Record<string, string>;\n };\n}\n\n/**\n * Returns the metadata for the remote component.\n * This metadata is used to identify the remote component and its bundle.\n *\n * Extend your Next.js Pages Router page props with this metadata to ensure proper remote component loading.\n *\n * @returns The metadata for the remote component.\n *\n * @example\n *\n * Create a custom App component in your Next.js application to include the remote component metadata:\n *\n * ```\n * import {\n * getRemoteComponentMetadata,\n * type RemoteComponentMetadata,\n * } from 'remote-components/next/pages';\n * import App from 'next/app';\n * import type { AppContext, AppInitialProps, AppProps } from 'next/app';\n *\n * export default function MyApp({ Component, pageProps }: AppProps) {\n * return <Component {...pageProps} />;\n * }\n *\n * MyApp.getInitialProps = async (\n * context: AppContext,\n * ): Promise<RemoteComponentMetadata & AppInitialProps> => {\n * const ctx = await App.getInitialProps(context);\n * return { ...ctx, ...getRemoteComponentMetadata() };\n * };\n * ```\n */\nexport function getRemoteComponentMetadata(): RemoteComponentMetadata {\n return {\n __REMOTE_COMPONENT__: {\n bundle: PROJECT_ID,\n runtime: process.env.TURBOPACK ? 'turbopack' : 'webpack',\n shared,\n },\n };\n}\n"],"mappings":"AAAA,SAAS,cAAc;AAEvB,MAAM,aACJ,QAAQ,IAAI,gCACZ,QAAQ,IAAI,uCACZ,QAAQ,IAAI;AA0CP,SAAS,6BAAsD;AACpE,SAAO;AAAA,IACL,sBAAsB;AAAA,MACpB,QAAQ;AAAA,MACR,SAAS,QAAQ,IAAI,YAAY,cAAc;AAAA,MAC/C;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/dist/react/index.cjs
CHANGED
|
@@ -30,6 +30,7 @@ var import_polyfill = require("#internal/shared/client/polyfill");
|
|
|
30
30
|
var import_apply_origin = require("#internal/shared/client/apply-origin");
|
|
31
31
|
var import_utils = require("#internal/shared/utils");
|
|
32
32
|
var import_fetch_headers = require("#internal/shared/ssr/fetch-headers");
|
|
33
|
+
var import_error = require("#internal/shared/error");
|
|
33
34
|
function getRemoteComponentHtml(html) {
|
|
34
35
|
if (typeof document === "undefined")
|
|
35
36
|
return html;
|
|
@@ -172,7 +173,6 @@ function RemoteComponent({
|
|
|
172
173
|
}
|
|
173
174
|
}, [src]);
|
|
174
175
|
(0, import_react2.useEffect)(() => {
|
|
175
|
-
let mounted = true;
|
|
176
176
|
if (src && src !== prevSrcRef.current) {
|
|
177
177
|
prevSrcRef.current = src;
|
|
178
178
|
(0, import_react2.startTransition)(async () => {
|
|
@@ -188,9 +188,31 @@ function RemoteComponent({
|
|
|
188
188
|
};
|
|
189
189
|
const res = await fetch(url, fetchInit);
|
|
190
190
|
if (!res.ok) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
);
|
|
191
|
+
let error = (0, import_error.failedToFetchRemoteComponentError)(url.href, {
|
|
192
|
+
cause: new Error(`${res.status} ${res.statusText}`)
|
|
193
|
+
});
|
|
194
|
+
try {
|
|
195
|
+
const body = await res.text();
|
|
196
|
+
const parser2 = new DOMParser();
|
|
197
|
+
const doc2 = parser2.parseFromString(body, "text/html");
|
|
198
|
+
const errorTemplate = doc2.querySelector(
|
|
199
|
+
"template[data-next-error-message]"
|
|
200
|
+
);
|
|
201
|
+
const errorMessage = errorTemplate?.getAttribute(
|
|
202
|
+
"data-next-error-message"
|
|
203
|
+
);
|
|
204
|
+
const errorStack = errorTemplate?.getAttribute(
|
|
205
|
+
"data-next-error-stack"
|
|
206
|
+
);
|
|
207
|
+
if (errorMessage) {
|
|
208
|
+
error = new import_remote_component.RemoteComponentsError(errorMessage);
|
|
209
|
+
if (errorStack) {
|
|
210
|
+
error.stack = errorStack;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
} catch {
|
|
214
|
+
}
|
|
215
|
+
throw error;
|
|
194
216
|
}
|
|
195
217
|
const remoteHtml = await res.text();
|
|
196
218
|
htmlRef.current = remoteHtml;
|
|
@@ -198,6 +220,9 @@ function RemoteComponent({
|
|
|
198
220
|
}
|
|
199
221
|
const parser = new DOMParser();
|
|
200
222
|
const doc = parser.parseFromString(html, "text/html");
|
|
223
|
+
if (doc.querySelectorAll("div[data-bundle][data-route]").length > 1 && !doc.querySelector(`div[data-bundle][data-route][id^="${name}"]`)) {
|
|
224
|
+
throw (0, import_error.multipleRemoteComponentsError)(url.href);
|
|
225
|
+
}
|
|
201
226
|
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
|
|
202
227
|
doc.querySelector("div[data-bundle][data-route]") ?? // fallback to Next.js Pages Router
|
|
203
228
|
doc.querySelector("div#__next");
|
|
@@ -219,7 +244,9 @@ function RemoteComponent({
|
|
|
219
244
|
const remoteShared = nextData?.props.__REMOTE_COMPONENT__?.shared ?? (JSON.parse(remoteSharedEl?.textContent ?? "{}") ?? {});
|
|
220
245
|
remoteSharedEl?.remove();
|
|
221
246
|
if (!component || !(rsc || nextData)) {
|
|
222
|
-
throw new
|
|
247
|
+
throw new import_remote_component.RemoteComponentsError(
|
|
248
|
+
`Remote Component not found on ${url.href}.${remoteName !== "__vercel_remote_component" ? `The name for the <RemoteComponent> is "${remoteName}". Check <RemoteComponent> usage.` : ""} Did you forget to wrap the content in <RemoteComponent>?`
|
|
249
|
+
);
|
|
223
250
|
}
|
|
224
251
|
(0, import_apply_origin.applyOriginToNodes)(doc, url);
|
|
225
252
|
const links = Array.from(
|
|
@@ -311,6 +338,15 @@ function RemoteComponent({
|
|
|
311
338
|
};
|
|
312
339
|
componentHydrationHtml.current = Array.from(doc.querySelectorAll("link,style")).map((link) => link.outerHTML).join("") + component.innerHTML;
|
|
313
340
|
const userShared = await shared;
|
|
341
|
+
if ("__remote_components_missing_shared__" in userShared) {
|
|
342
|
+
userShared.__remote_components_missing_shared__().catch((e) => {
|
|
343
|
+
throw e;
|
|
344
|
+
});
|
|
345
|
+
} else if ("__remote_components_missing_shared__" in remoteShared) {
|
|
346
|
+
throw new import_remote_component.RemoteComponentsError(
|
|
347
|
+
remoteShared.__remote_components_missing_shared__
|
|
348
|
+
);
|
|
349
|
+
}
|
|
314
350
|
const result = await (0, import_remote_component.loadRemoteComponent)({
|
|
315
351
|
url: new URL(url, location.origin),
|
|
316
352
|
name: remoteName,
|
|
@@ -355,15 +391,10 @@ function RemoteComponent({
|
|
|
355
391
|
setRemoteComponent(result.component);
|
|
356
392
|
}
|
|
357
393
|
} catch (error) {
|
|
358
|
-
|
|
359
|
-
setRemoteComponent(error);
|
|
360
|
-
}
|
|
394
|
+
setRemoteComponent(error);
|
|
361
395
|
}
|
|
362
396
|
});
|
|
363
397
|
}
|
|
364
|
-
return () => {
|
|
365
|
-
mounted = false;
|
|
366
|
-
};
|
|
367
398
|
}, [
|
|
368
399
|
url,
|
|
369
400
|
src,
|