remote-components 0.0.1 → 0.0.3
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 +56 -22
- package/dist/html/host.cjs.map +1 -1
- package/dist/html/host.js +56 -22
- package/dist/html/host.js.map +1 -1
- package/dist/next/config.cjs +75 -32
- package/dist/next/config.cjs.map +1 -1
- package/dist/next/config.d.ts +35 -2
- package/dist/next/config.js +75 -35
- package/dist/next/config.js.map +1 -1
- package/dist/next/host/app-client.cjs +78 -22
- package/dist/next/host/app-client.cjs.map +1 -1
- package/dist/next/host/app-client.d.ts +13 -1
- package/dist/next/host/app-client.js +77 -21
- package/dist/next/host/app-client.js.map +1 -1
- package/dist/next/host/app-server.cjs +6 -1
- package/dist/next/host/app-server.cjs.map +1 -1
- package/dist/next/host/app-server.d.ts +28 -1
- package/dist/next/host/app-server.js +6 -1
- package/dist/next/host/app-server.js.map +1 -1
- package/dist/next/host/pages-server.cjs.map +1 -1
- package/dist/next/host/pages-server.d.ts +34 -0
- package/dist/next/host/pages-server.js.map +1 -1
- package/dist/next/middleware.cjs +71 -0
- package/dist/next/middleware.cjs.map +1 -0
- package/dist/next/middleware.d.ts +28 -0
- package/dist/next/middleware.js +45 -0
- package/dist/next/middleware.js.map +1 -0
- package/dist/next/remote/pages.cjs.map +1 -1
- package/dist/next/remote/pages.d.ts +36 -4
- package/dist/next/remote/pages.js.map +1 -1
- package/dist/next/remote/render-server.cjs +4 -4
- package/dist/next/remote/render-server.cjs.map +1 -1
- package/dist/next/remote/render-server.d.ts +26 -4
- package/dist/next/remote/render-server.js +4 -4
- package/dist/next/remote/render-server.js.map +1 -1
- package/dist/next/remote/server.d.ts +0 -2
- package/dist/shared/client/remote-component.cjs +50 -18
- package/dist/shared/client/remote-component.cjs.map +1 -1
- package/dist/shared/client/remote-component.d.ts +7 -2
- package/dist/shared/client/remote-component.js +50 -18
- package/dist/shared/client/remote-component.js.map +1 -1
- package/dist/shared/webpack/next-client-pages-loader.cjs +6 -2
- package/dist/shared/webpack/next-client-pages-loader.cjs.map +1 -1
- package/dist/shared/webpack/next-client-pages-loader.js +6 -2
- package/dist/shared/webpack/next-client-pages-loader.js.map +1 -1
- package/package.json +9 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/host/pages-server.tsx"],"sourcesContent":["import type { IncomingHttpHeaders } from 'node:http';\nimport { useEffect } from 'react';\nimport { shared } from '@remote-component/shared/host';\nimport { fetchRemoteComponent } from '../../shared/ssr/fetch-remote-component';\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\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, string>;\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, html: string): void {\n if (!self[REMOTE_COMPONENT_STORE]) {\n self[REMOTE_COMPONENT_STORE] = new Map();\n }\n self[REMOTE_COMPONENT_STORE].set(key, html);\n}\n\nfunction getComponent(key: string): string | 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 [REMOTE_COMPONENT_KEY]?: string;\n children?: React.ReactNode;\n}\n\nexport function RemoteComponent(props: RemoteComponentProps): JSX.Element {\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;\n import('remote-components/html');\n }, []);\n\n if (!props[REMOTE_COMPONENT_KEY]) {\n return (\n <remote-component src={props.src}>{props.children}</remote-component>\n );\n }\n\n return (\n <remote-component src={props.src}>\n <div\n dangerouslySetInnerHTML={{ __html: remoteComponent ?? '' }}\n id=\"__REMOTE_COMPONENT__\"\n suppressHydrationWarning\n />\n </remote-component>\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 html,\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\n const props = {\n src,\n bundle,\n name,\n route,\n runtime,\n };\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 return props;\n }\n\n const key = getKey(props);\n // store the HTML in a global store\n setComponent(key, html);\n\n return {\n ...props,\n // add remote component key to the props\n [REMOTE_COMPONENT_KEY]: key,\n };\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../../src/next/host/pages-server.tsx"],"sourcesContent":["import type { IncomingHttpHeaders } from 'node:http';\nimport { useEffect } from 'react';\nimport { shared } from '@remote-component/shared/host';\nimport { fetchRemoteComponent } from '../../shared/ssr/fetch-remote-component';\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\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, string>;\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, html: string): void {\n if (!self[REMOTE_COMPONENT_STORE]) {\n self[REMOTE_COMPONENT_STORE] = new Map();\n }\n self[REMOTE_COMPONENT_STORE].set(key, html);\n}\n\nfunction getComponent(key: string): string | 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 [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): JSX.Element {\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;\n import('remote-components/html');\n }, []);\n\n if (!props[REMOTE_COMPONENT_KEY]) {\n return (\n <remote-component src={props.src}>{props.children}</remote-component>\n );\n }\n\n return (\n <remote-component src={props.src}>\n <div\n dangerouslySetInnerHTML={{ __html: remoteComponent ?? '' }}\n id=\"__REMOTE_COMPONENT__\"\n suppressHydrationWarning\n />\n </remote-component>\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 html,\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\n const props = {\n src,\n bundle,\n name,\n route,\n runtime,\n };\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 return props;\n }\n\n const key = getKey(props);\n // store the HTML in a global store\n setComponent(key, html);\n\n return {\n ...props,\n // add remote component key to the props\n [REMOTE_COMPONENT_KEY]: key,\n };\n}\n"],"mappings":"AA0FM;AAzFN,SAAS,iBAAiB;AAC1B,SAAS,cAAc;AACvB,SAAS,4BAA4B;AAgBrC,MAAM,yBAAyB,OAAO,wBAAwB;AAC9D,MAAM,uBAAuB;AAI7B,MAAM,OAAO;AAIb,SAAS,OAAO;AAAA,EACd;AAAA,EACA;AAAA,EACA;AACF,GAIW;AACT,SAAO,GAAG,UAAU,YAAY,SAAS,OAAO,QAAQ,gCAAgC,OAAO,WAAW;AAC5G;AAEA,SAAS,aAAa,KAAa,MAAoB;AACrD,MAAI,CAAC,KAAK,sBAAsB,GAAG;AACjC,SAAK,sBAAsB,IAAI,oBAAI,IAAI;AAAA,EACzC;AACA,OAAK,sBAAsB,EAAE,IAAI,KAAK,IAAI;AAC5C;AAEA,SAAS,aAAa,KAAiC;AACrD,QAAM,YAAY,KAAK,sBAAsB,GAAG,IAAI,GAAG;AAGvD,OAAK,sBAAsB,GAAG,OAAO,GAAG;AACxC,SAAO;AACT;AAiBO,SAAS,gBAAgB,OAA0C;AACxE,QAAM,kBACJ,OAAO,aAAa,cAChB;AAAA;AAAA,IAEA;AAAA,MACE,MAAM,oBAAoB,KAAK;AAAA,IACjC;AAAA;AAEN,YAAU,MAAM;AACd,UAAM,aAAa;AAInB,eAAW,8BAA8B;AACzC,WAAO,wBAAwB;AAAA,EACjC,GAAG,CAAC,CAAC;AAEL,MAAI,CAAC,MAAM,oBAAoB,GAAG;AAChC,WACE,oBAAC,sBAAiB,KAAK,MAAM,KAAM,gBAAM,UAAS;AAAA,EAEtD;AAEA,SACE,oBAAC,sBAAiB,KAAK,MAAM,KAC3B;AAAA,IAAC;AAAA;AAAA,MACC,yBAAyB,EAAE,QAAQ,mBAAmB,GAAG;AAAA,MACzD,IAAG;AAAA,MACH,0BAAwB;AAAA;AAAA,EAC1B,GACF;AAEJ;AA8BA,eAAsB,wBACpB,KACA,SAC+B;AAC/B,MAAI,OAAO,aAAa,aAAa;AACnC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM;AAAA,IACJ,UAAU,EAAE,QAAQ,OAAO,QAAQ;AAAA,IACnC;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,MAAM;AAAA,IACR;AAAA,IACA,mBAAmB,UACf;AAAA;AAAA,MAEA,OAAO,QAAQ,OAAO,EAAE,OAAO,CAAC,KAAK,CAACA,MAAK,KAAK,MAAM;AACpD,YAAI,OAAO;AACT,cAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,kBAAM,QAAQ,CAAC,MAAM,IAAI,OAAOA,MAAK,CAAC,CAAC;AAAA,UACzC,OAAO;AACL,gBAAI,OAAOA,MAAK,KAAK;AAAA,UACvB;AAAA,QACF;AACA,eAAO;AAAA,MACT,GAAG,IAAI,QAAQ,CAAC;AAAA;AAAA,EACtB;AAEA,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAIA,MAAI,UAAU,YAAY,eAAe;AACvC,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,OAAO,KAAK;AAExB,eAAa,KAAK,IAAI;AAEtB,SAAO;AAAA,IACL,GAAG;AAAA;AAAA,IAEH,CAAC,oBAAoB,GAAG;AAAA,EAC1B;AACF;","names":["key"]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/next/middleware/index.ts
|
|
21
|
+
var middleware_exports = {};
|
|
22
|
+
__export(middleware_exports, {
|
|
23
|
+
config: () => config,
|
|
24
|
+
withRemoteComponents: () => withRemoteComponents
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(middleware_exports);
|
|
27
|
+
var import_server = require("next/server");
|
|
28
|
+
function corsFromOptions(options) {
|
|
29
|
+
const ALLOWED_ORIGINS = (process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS || (Array.isArray(options?.origin) ? options.origin.join(",") : options?.origin) || "*").split(",").map((origin) => origin.trim());
|
|
30
|
+
const CORS_HEADERS = {
|
|
31
|
+
"Access-Control-Allow-Methods": process.env.REMOTE_COMPONENTS_ALLOW_METHODS || (Array.isArray(options?.method) ? options.method.map((m) => m.trim()).join(",") : options?.method) || "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS",
|
|
32
|
+
"Access-Control-Allow-Headers": `${process.env.REMOTE_COMPONENTS_ALLOW_HEADERS || (Array.isArray(options?.headers) ? options.headers.map((h) => h.trim()).join(",") : options?.headers) || "Content-Type,Authorization"},vercel-remote-component-url`,
|
|
33
|
+
...process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || options?.credentials ? {
|
|
34
|
+
"Access-Control-Allow-Credentials": process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || "true"
|
|
35
|
+
} : {}
|
|
36
|
+
};
|
|
37
|
+
return { ALLOWED_ORIGINS, CORS_HEADERS };
|
|
38
|
+
}
|
|
39
|
+
function withRemoteComponents(middleware, options) {
|
|
40
|
+
return async (request) => {
|
|
41
|
+
const { ALLOWED_ORIGINS, CORS_HEADERS } = corsFromOptions(options?.cors);
|
|
42
|
+
const origin = request.headers.get("origin") ?? "";
|
|
43
|
+
const isAllowed = ALLOWED_ORIGINS.includes("*") || ALLOWED_ORIGINS.includes(origin);
|
|
44
|
+
if (request.method === "OPTIONS") {
|
|
45
|
+
return new Response(null, {
|
|
46
|
+
status: 200,
|
|
47
|
+
headers: isAllowed ? {
|
|
48
|
+
"Access-Control-Allow-Origin": origin,
|
|
49
|
+
...CORS_HEADERS
|
|
50
|
+
} : {}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const response = typeof middleware === "function" ? await middleware(request) : import_server.NextResponse.next();
|
|
54
|
+
if (isAllowed) {
|
|
55
|
+
response.headers.set("Access-Control-Allow-Origin", origin);
|
|
56
|
+
}
|
|
57
|
+
Object.entries(CORS_HEADERS).forEach(
|
|
58
|
+
([k, v]) => response.headers.set(k, v)
|
|
59
|
+
);
|
|
60
|
+
return response;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
var config = {
|
|
64
|
+
matcher: "/:path*"
|
|
65
|
+
};
|
|
66
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
67
|
+
0 && (module.exports = {
|
|
68
|
+
config,
|
|
69
|
+
withRemoteComponents
|
|
70
|
+
});
|
|
71
|
+
//# sourceMappingURL=middleware.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/next/middleware/index.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\n\nexport interface RemoteComponentMiddlewareOptions {\n cors?: {\n origin?: string | string[];\n method?: string | string[];\n headers?: string | string[];\n credentials?: boolean;\n };\n}\n\nfunction corsFromOptions(options?: RemoteComponentMiddlewareOptions['cors']) {\n const ALLOWED_ORIGINS = (\n process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS ||\n (Array.isArray(options?.origin)\n ? options.origin.join(',')\n : options?.origin) ||\n '*'\n )\n .split(',')\n .map((origin) => origin.trim());\n\n const CORS_HEADERS = {\n 'Access-Control-Allow-Methods':\n process.env.REMOTE_COMPONENTS_ALLOW_METHODS ||\n (Array.isArray(options?.method)\n ? options.method.map((m) => m.trim()).join(',')\n : options?.method) ||\n 'GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS',\n 'Access-Control-Allow-Headers': `${\n process.env.REMOTE_COMPONENTS_ALLOW_HEADERS ||\n (Array.isArray(options?.headers)\n ? options.headers.map((h) => h.trim()).join(',')\n : options?.headers) ||\n 'Content-Type,Authorization'\n },vercel-remote-component-url`,\n ...(process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || options?.credentials\n ? {\n 'Access-Control-Allow-Credentials':\n process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || 'true',\n }\n : {}),\n };\n\n return { ALLOWED_ORIGINS, CORS_HEADERS };\n}\n\n/**\n * This middleware is used to handle CORS and other remote component related tasks.\n * It can be used to wrap a Next.js middleware function to add CORS headers and handle preflight requests.\n *\n * @param middleware - The Next.js middleware function to wrap.\n * @param options - Optional configuration for handling remote components.\n * @returns A Next.js middleware function that handles CORS and preflight requests\n */\nexport function withRemoteComponents(\n middleware?: (request: NextRequest) => NextResponse | Promise<NextResponse>,\n options?: RemoteComponentMiddlewareOptions,\n) {\n return async (request: NextRequest) => {\n const { ALLOWED_ORIGINS, CORS_HEADERS } = corsFromOptions(options?.cors);\n\n const origin = request.headers.get('origin') ?? '';\n const isAllowed =\n ALLOWED_ORIGINS.includes('*') || ALLOWED_ORIGINS.includes(origin);\n\n // Handle preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n status: 200,\n headers: isAllowed\n ? {\n 'Access-Control-Allow-Origin': origin,\n ...CORS_HEADERS,\n }\n : {},\n });\n }\n\n // For all other requests, continue and attach CORS\n const response =\n typeof middleware === 'function'\n ? await middleware(request)\n : NextResponse.next();\n\n if (isAllowed) {\n response.headers.set('Access-Control-Allow-Origin', origin);\n }\n\n Object.entries(CORS_HEADERS).forEach(([k, v]) =>\n response.headers.set(k, v),\n );\n\n return response;\n };\n}\n\n/**\n * This configuration is used to specify the paths that the middleware should match.\n * It matches all paths by default.\n */\nexport const config = {\n matcher: '/:path*',\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA+C;AAW/C,SAAS,gBAAgB,SAAoD;AAC3E,QAAM,mBACJ,QAAQ,IAAI,sCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,KAAK,GAAG,IACvB,SAAS,WACb,KAEC,MAAM,GAAG,EACT,IAAI,CAAC,WAAW,OAAO,KAAK,CAAC;AAEhC,QAAM,eAAe;AAAA,IACnB,gCACE,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC5C,SAAS,WACb;AAAA,IACF,gCAAgC,GAC9B,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,OAAO,IAC3B,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC7C,SAAS,YACb;AAAA,IAEF,GAAI,QAAQ,IAAI,uCAAuC,SAAS,cAC5D;AAAA,MACE,oCACE,QAAQ,IAAI,uCAAuC;AAAA,IACvD,IACA,CAAC;AAAA,EACP;AAEA,SAAO,EAAE,iBAAiB,aAAa;AACzC;AAUO,SAAS,qBACd,YACA,SACA;AACA,SAAO,OAAO,YAAyB;AACrC,UAAM,EAAE,iBAAiB,aAAa,IAAI,gBAAgB,SAAS,IAAI;AAEvE,UAAM,SAAS,QAAQ,QAAQ,IAAI,QAAQ,KAAK;AAChD,UAAM,YACJ,gBAAgB,SAAS,GAAG,KAAK,gBAAgB,SAAS,MAAM;AAGlE,QAAI,QAAQ,WAAW,WAAW;AAChC,aAAO,IAAI,SAAS,MAAM;AAAA,QACxB,QAAQ;AAAA,QACR,SAAS,YACL;AAAA,UACE,+BAA+B;AAAA,UAC/B,GAAG;AAAA,QACL,IACA,CAAC;AAAA,MACP,CAAC;AAAA,IACH;AAGA,UAAM,WACJ,OAAO,eAAe,aAClB,MAAM,WAAW,OAAO,IACxB,2BAAa,KAAK;AAExB,QAAI,WAAW;AACb,eAAS,QAAQ,IAAI,+BAA+B,MAAM;AAAA,IAC5D;AAEA,WAAO,QAAQ,YAAY,EAAE;AAAA,MAAQ,CAAC,CAAC,GAAG,CAAC,MACzC,SAAS,QAAQ,IAAI,GAAG,CAAC;AAAA,IAC3B;AAEA,WAAO;AAAA,EACT;AACF;AAMO,IAAM,SAAS;AAAA,EACpB,SAAS;AACX;","names":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
2
|
+
|
|
3
|
+
interface RemoteComponentMiddlewareOptions {
|
|
4
|
+
cors?: {
|
|
5
|
+
origin?: string | string[];
|
|
6
|
+
method?: string | string[];
|
|
7
|
+
headers?: string | string[];
|
|
8
|
+
credentials?: boolean;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* This middleware is used to handle CORS and other remote component related tasks.
|
|
13
|
+
* It can be used to wrap a Next.js middleware function to add CORS headers and handle preflight requests.
|
|
14
|
+
*
|
|
15
|
+
* @param middleware - The Next.js middleware function to wrap.
|
|
16
|
+
* @param options - Optional configuration for handling remote components.
|
|
17
|
+
* @returns A Next.js middleware function that handles CORS and preflight requests
|
|
18
|
+
*/
|
|
19
|
+
declare function withRemoteComponents(middleware?: (request: NextRequest) => NextResponse | Promise<NextResponse>, options?: RemoteComponentMiddlewareOptions): (request: NextRequest) => Promise<Response>;
|
|
20
|
+
/**
|
|
21
|
+
* This configuration is used to specify the paths that the middleware should match.
|
|
22
|
+
* It matches all paths by default.
|
|
23
|
+
*/
|
|
24
|
+
declare const config: {
|
|
25
|
+
matcher: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export { RemoteComponentMiddlewareOptions, config, withRemoteComponents };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/next/middleware/index.ts
|
|
2
|
+
import { NextResponse } from "next/server";
|
|
3
|
+
function corsFromOptions(options) {
|
|
4
|
+
const ALLOWED_ORIGINS = (process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS || (Array.isArray(options?.origin) ? options.origin.join(",") : options?.origin) || "*").split(",").map((origin) => origin.trim());
|
|
5
|
+
const CORS_HEADERS = {
|
|
6
|
+
"Access-Control-Allow-Methods": process.env.REMOTE_COMPONENTS_ALLOW_METHODS || (Array.isArray(options?.method) ? options.method.map((m) => m.trim()).join(",") : options?.method) || "GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS",
|
|
7
|
+
"Access-Control-Allow-Headers": `${process.env.REMOTE_COMPONENTS_ALLOW_HEADERS || (Array.isArray(options?.headers) ? options.headers.map((h) => h.trim()).join(",") : options?.headers) || "Content-Type,Authorization"},vercel-remote-component-url`,
|
|
8
|
+
...process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || options?.credentials ? {
|
|
9
|
+
"Access-Control-Allow-Credentials": process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || "true"
|
|
10
|
+
} : {}
|
|
11
|
+
};
|
|
12
|
+
return { ALLOWED_ORIGINS, CORS_HEADERS };
|
|
13
|
+
}
|
|
14
|
+
function withRemoteComponents(middleware, options) {
|
|
15
|
+
return async (request) => {
|
|
16
|
+
const { ALLOWED_ORIGINS, CORS_HEADERS } = corsFromOptions(options?.cors);
|
|
17
|
+
const origin = request.headers.get("origin") ?? "";
|
|
18
|
+
const isAllowed = ALLOWED_ORIGINS.includes("*") || ALLOWED_ORIGINS.includes(origin);
|
|
19
|
+
if (request.method === "OPTIONS") {
|
|
20
|
+
return new Response(null, {
|
|
21
|
+
status: 200,
|
|
22
|
+
headers: isAllowed ? {
|
|
23
|
+
"Access-Control-Allow-Origin": origin,
|
|
24
|
+
...CORS_HEADERS
|
|
25
|
+
} : {}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const response = typeof middleware === "function" ? await middleware(request) : NextResponse.next();
|
|
29
|
+
if (isAllowed) {
|
|
30
|
+
response.headers.set("Access-Control-Allow-Origin", origin);
|
|
31
|
+
}
|
|
32
|
+
Object.entries(CORS_HEADERS).forEach(
|
|
33
|
+
([k, v]) => response.headers.set(k, v)
|
|
34
|
+
);
|
|
35
|
+
return response;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
var config = {
|
|
39
|
+
matcher: "/:path*"
|
|
40
|
+
};
|
|
41
|
+
export {
|
|
42
|
+
config,
|
|
43
|
+
withRemoteComponents
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/next/middleware/index.ts"],"sourcesContent":["import { type NextRequest, NextResponse } from 'next/server';\n\nexport interface RemoteComponentMiddlewareOptions {\n cors?: {\n origin?: string | string[];\n method?: string | string[];\n headers?: string | string[];\n credentials?: boolean;\n };\n}\n\nfunction corsFromOptions(options?: RemoteComponentMiddlewareOptions['cors']) {\n const ALLOWED_ORIGINS = (\n process.env.REMOTE_COMPONENTS_ALLOWED_ORIGINS ||\n (Array.isArray(options?.origin)\n ? options.origin.join(',')\n : options?.origin) ||\n '*'\n )\n .split(',')\n .map((origin) => origin.trim());\n\n const CORS_HEADERS = {\n 'Access-Control-Allow-Methods':\n process.env.REMOTE_COMPONENTS_ALLOW_METHODS ||\n (Array.isArray(options?.method)\n ? options.method.map((m) => m.trim()).join(',')\n : options?.method) ||\n 'GET,HEAD,POST,PUT,PATCH,DELETE,OPTIONS',\n 'Access-Control-Allow-Headers': `${\n process.env.REMOTE_COMPONENTS_ALLOW_HEADERS ||\n (Array.isArray(options?.headers)\n ? options.headers.map((h) => h.trim()).join(',')\n : options?.headers) ||\n 'Content-Type,Authorization'\n },vercel-remote-component-url`,\n ...(process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || options?.credentials\n ? {\n 'Access-Control-Allow-Credentials':\n process.env.REMOTE_COMPONENTS_ALLOW_CREDENTIALS || 'true',\n }\n : {}),\n };\n\n return { ALLOWED_ORIGINS, CORS_HEADERS };\n}\n\n/**\n * This middleware is used to handle CORS and other remote component related tasks.\n * It can be used to wrap a Next.js middleware function to add CORS headers and handle preflight requests.\n *\n * @param middleware - The Next.js middleware function to wrap.\n * @param options - Optional configuration for handling remote components.\n * @returns A Next.js middleware function that handles CORS and preflight requests\n */\nexport function withRemoteComponents(\n middleware?: (request: NextRequest) => NextResponse | Promise<NextResponse>,\n options?: RemoteComponentMiddlewareOptions,\n) {\n return async (request: NextRequest) => {\n const { ALLOWED_ORIGINS, CORS_HEADERS } = corsFromOptions(options?.cors);\n\n const origin = request.headers.get('origin') ?? '';\n const isAllowed =\n ALLOWED_ORIGINS.includes('*') || ALLOWED_ORIGINS.includes(origin);\n\n // Handle preflight\n if (request.method === 'OPTIONS') {\n return new Response(null, {\n status: 200,\n headers: isAllowed\n ? {\n 'Access-Control-Allow-Origin': origin,\n ...CORS_HEADERS,\n }\n : {},\n });\n }\n\n // For all other requests, continue and attach CORS\n const response =\n typeof middleware === 'function'\n ? await middleware(request)\n : NextResponse.next();\n\n if (isAllowed) {\n response.headers.set('Access-Control-Allow-Origin', origin);\n }\n\n Object.entries(CORS_HEADERS).forEach(([k, v]) =>\n response.headers.set(k, v),\n );\n\n return response;\n };\n}\n\n/**\n * This configuration is used to specify the paths that the middleware should match.\n * It matches all paths by default.\n */\nexport const config = {\n matcher: '/:path*',\n};\n"],"mappings":";AAAA,SAA2B,oBAAoB;AAW/C,SAAS,gBAAgB,SAAoD;AAC3E,QAAM,mBACJ,QAAQ,IAAI,sCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,KAAK,GAAG,IACvB,SAAS,WACb,KAEC,MAAM,GAAG,EACT,IAAI,CAAC,WAAW,OAAO,KAAK,CAAC;AAEhC,QAAM,eAAe;AAAA,IACnB,gCACE,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,MAAM,IAC1B,QAAQ,OAAO,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC5C,SAAS,WACb;AAAA,IACF,gCAAgC,GAC9B,QAAQ,IAAI,oCACX,MAAM,QAAQ,SAAS,OAAO,IAC3B,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAC7C,SAAS,YACb;AAAA,IAEF,GAAI,QAAQ,IAAI,uCAAuC,SAAS,cAC5D;AAAA,MACE,oCACE,QAAQ,IAAI,uCAAuC;AAAA,IACvD,IACA,CAAC;AAAA,EACP;AAEA,SAAO,EAAE,iBAAiB,aAAa;AACzC;AAUO,SAAS,qBACd,YACA,SACA;AACA,SAAO,OAAO,YAAyB;AACrC,UAAM,EAAE,iBAAiB,aAAa,IAAI,gBAAgB,SAAS,IAAI;AAEvE,UAAM,SAAS,QAAQ,QAAQ,IAAI,QAAQ,KAAK;AAChD,UAAM,YACJ,gBAAgB,SAAS,GAAG,KAAK,gBAAgB,SAAS,MAAM;AAGlE,QAAI,QAAQ,WAAW,WAAW;AAChC,aAAO,IAAI,SAAS,MAAM;AAAA,QACxB,QAAQ;AAAA,QACR,SAAS,YACL;AAAA,UACE,+BAA+B;AAAA,UAC/B,GAAG;AAAA,QACL,IACA,CAAC;AAAA,MACP,CAAC;AAAA,IACH;AAGA,UAAM,WACJ,OAAO,eAAe,aAClB,MAAM,WAAW,OAAO,IACxB,aAAa,KAAK;AAExB,QAAI,WAAW;AACb,eAAS,QAAQ,IAAI,+BAA+B,MAAM;AAAA,IAC5D;AAEA,WAAO,QAAQ,YAAY,EAAE;AAAA,MAAQ,CAAC,CAAC,GAAG,CAAC,MACzC,SAAS,QAAQ,IAAI,GAAG,CAAC;AAAA,IAC3B;AAEA,WAAO;AAAA,EACT;AACF;AAMO,IAAM,SAAS;AAAA,EACpB,SAAS;AACX;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/remote/pages.ts"],"sourcesContent":["const CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\n
|
|
1
|
+
{"version":3,"sources":["../../../src/next/remote/pages.ts"],"sourcesContent":["const CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\nexport interface RemoteComponentMetadata {\n __REMOTE_COMPONENT__: {\n bundle: string | undefined;\n runtime: 'turbopack' | 'webpack';\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: CURRENT_ZONE,\n runtime: process.env.TURBOPACK ? 'turbopack' : 'webpack',\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAAM,eAAe,QAAQ,IAAI;AAyC1B,SAAS,6BAAsD;AACpE,SAAO;AAAA,IACL,sBAAsB;AAAA,MACpB,QAAQ;AAAA,MACR,SAAS,QAAQ,IAAI,YAAY,cAAc;AAAA,IACjD;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1,9 +1,41 @@
|
|
|
1
|
-
|
|
1
|
+
interface RemoteComponentMetadata {
|
|
2
2
|
__REMOTE_COMPONENT__: {
|
|
3
3
|
bundle: string | undefined;
|
|
4
|
-
runtime:
|
|
4
|
+
runtime: 'turbopack' | 'webpack';
|
|
5
5
|
};
|
|
6
|
-
}
|
|
7
|
-
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Returns the metadata for the remote component.
|
|
9
|
+
* This metadata is used to identify the remote component and its bundle.
|
|
10
|
+
*
|
|
11
|
+
* Extend your Next.js Pages Router page props with this metadata to ensure proper remote component loading.
|
|
12
|
+
*
|
|
13
|
+
* @returns The metadata for the remote component.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
*
|
|
17
|
+
* Create a custom App component in your Next.js application to include the remote component metadata:
|
|
18
|
+
*
|
|
19
|
+
* ```
|
|
20
|
+
* import {
|
|
21
|
+
* getRemoteComponentMetadata,
|
|
22
|
+
* type RemoteComponentMetadata,
|
|
23
|
+
* } from 'remote-components/next/pages';
|
|
24
|
+
* import App from 'next/app';
|
|
25
|
+
* import type { AppContext, AppInitialProps, AppProps } from 'next/app';
|
|
26
|
+
*
|
|
27
|
+
* export default function MyApp({ Component, pageProps }: AppProps) {
|
|
28
|
+
* return <Component {...pageProps} />;
|
|
29
|
+
* }
|
|
30
|
+
*
|
|
31
|
+
* MyApp.getInitialProps = async (
|
|
32
|
+
* context: AppContext,
|
|
33
|
+
* ): Promise<RemoteComponentMetadata & AppInitialProps> => {
|
|
34
|
+
* const ctx = await App.getInitialProps(context);
|
|
35
|
+
* return { ...ctx, ...getRemoteComponentMetadata() };
|
|
36
|
+
* };
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
declare function getRemoteComponentMetadata(): RemoteComponentMetadata;
|
|
8
40
|
|
|
9
41
|
export { RemoteComponentMetadata, getRemoteComponentMetadata };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/remote/pages.ts"],"sourcesContent":["const CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\n
|
|
1
|
+
{"version":3,"sources":["../../../src/next/remote/pages.ts"],"sourcesContent":["const CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\nexport interface RemoteComponentMetadata {\n __REMOTE_COMPONENT__: {\n bundle: string | undefined;\n runtime: 'turbopack' | 'webpack';\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: CURRENT_ZONE,\n runtime: process.env.TURBOPACK ? 'turbopack' : 'webpack',\n },\n };\n}\n"],"mappings":"AAAA,MAAM,eAAe,QAAQ,IAAI;AAyC1B,SAAS,6BAAsD;AACpE,SAAO;AAAA,IACL,sBAAsB;AAAA,MACpB,QAAQ;AAAA,MACR,SAAS,QAAQ,IAAI,YAAY,cAAc;AAAA,IACjD;AAAA,EACF;AACF;","names":[]}
|
|
@@ -37,7 +37,7 @@ var import_work_async_storage = require("next/dist/server/app-render/work-async-
|
|
|
37
37
|
const SERVER_ACTION_MANIFESTS_SINGLETON = Symbol.for(
|
|
38
38
|
"next.server.action-manifests"
|
|
39
39
|
);
|
|
40
|
-
const
|
|
40
|
+
const PROJECT_ID = process.env.REMOTE_COMPONENTS_PROJECT_ID;
|
|
41
41
|
function RemoteComponentData({ name, data }) {
|
|
42
42
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("script", { id: `${name}_rsc`, children: data.map(
|
|
43
43
|
(chunk, i) => (
|
|
@@ -69,7 +69,7 @@ async function RemoteComponent({
|
|
|
69
69
|
const ssrModuleMapping = { ...manifest?.ssrModuleMapping };
|
|
70
70
|
clientModules = Object.fromEntries(
|
|
71
71
|
Object.entries(clientModules).map(([key, value]) => {
|
|
72
|
-
const remoteId = `[${
|
|
72
|
+
const remoteId = `[${PROJECT_ID}] ${value.id}`;
|
|
73
73
|
ssrModuleMapping[remoteId] = ssrModuleMapping[value.id];
|
|
74
74
|
return [
|
|
75
75
|
key,
|
|
@@ -78,7 +78,7 @@ async function RemoteComponent({
|
|
|
78
78
|
id: remoteId,
|
|
79
79
|
// prepend the current zone to the chunks to handle remote component chunk loading in Webpack
|
|
80
80
|
// this is required to avoid loading the wrong chunk in the host application
|
|
81
|
-
chunks: value.chunks.map((chunk) => `[${
|
|
81
|
+
chunks: value.chunks.map((chunk) => `[${PROJECT_ID}] ${chunk}`)
|
|
82
82
|
// async: true,
|
|
83
83
|
}
|
|
84
84
|
];
|
|
@@ -106,7 +106,7 @@ async function RemoteComponent({
|
|
|
106
106
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
107
107
|
"div",
|
|
108
108
|
{
|
|
109
|
-
"data-bundle":
|
|
109
|
+
"data-bundle": PROJECT_ID,
|
|
110
110
|
"data-route": route,
|
|
111
111
|
"data-runtime": runtime,
|
|
112
112
|
id: `${remoteComponentName}_ssr`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/remote/render-server.tsx"],"sourcesContent":["import { headers } from 'next/headers';\nimport { workAsyncStorage } from 'next/dist/server/app-render/work-async-storage.external';\nimport type { Manifest } from './types';\n\n// internal Next.js symbol to access the manifest which is stored in the global scope\nconst SERVER_ACTION_MANIFESTS_SINGLETON = Symbol.for(\n 'next.server.action-manifests',\n);\n\nconst
|
|
1
|
+
{"version":3,"sources":["../../../src/next/remote/render-server.tsx"],"sourcesContent":["import { headers } from 'next/headers';\nimport { workAsyncStorage } from 'next/dist/server/app-render/work-async-storage.external';\nimport type { Manifest } from './types';\n\n// internal Next.js symbol to access the manifest which is stored in the global scope\nconst SERVER_ACTION_MANIFESTS_SINGLETON = Symbol.for(\n 'next.server.action-manifests',\n);\n\nconst PROJECT_ID = process.env.REMOTE_COMPONENTS_PROJECT_ID;\n\n// inject the RSC flight data into the HTML response using <script>\n// the RSC flight data is used to hydrate the remote component on the host\n// this approach is similar to an island architecture on the host\n// the remote component is static HTML until it is hydrated using this RSC flight data\nfunction RemoteComponentData({ name, data }: { name: string; data: string[] }) {\n return (\n <script id={`${name}_rsc`}>\n {data\n .map(\n (chunk, i) =>\n // make the data handling somewhat safe\n `${i === 0 ? `self[\"${name}\"]=self[\"${name}\"]||[];` : ''}self[\"${name}\"].push(${JSON.stringify(\n chunk,\n )});`,\n )\n .join('\\n')}\n </script>\n );\n}\n\n/**\n * RemoteComponent is a Next.js component that exposes a remote component\n * that can be used in a host application.\n *\n * @param name - The name of the remote component. Use a unique name to expose multiple remote components from the same page.\n * @param children - The content of the remote component. This is the content that will be rendered as the remote component.\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 expose the children as a remote component:\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 */\nexport async function RemoteComponent({\n name = '__vercel_remote_component',\n children,\n}: {\n name?: string;\n children: React.ReactNode;\n}): Promise<React.ReactNode> {\n const headersList = await headers();\n const url = headersList.get('Vercel-Remote-Component-Url');\n const isRemote = url !== null;\n\n if (!isRemote) {\n return children;\n }\n\n // this URL passed by the remote component consumer is only used to know the public address of the remote component\n // it is only used to determine if we need to mutate the client module map for now\n\n const { pathname } = new URL(url);\n // access the internal Next.js work store to get the active page and route\n const { page, route } = workAsyncStorage.getStore() ?? { page: pathname };\n\n // get reference to the manifests from the global scope\n const manifests = (\n globalThis as typeof globalThis & {\n [SERVER_ACTION_MANIFESTS_SINGLETON]: {\n clientReferenceManifestsPerPage?: Record<string, Manifest>;\n };\n }\n )[SERVER_ACTION_MANIFESTS_SINGLETON];\n const manifest = manifests.clientReferenceManifestsPerPage?.[route ?? page];\n\n const self = globalThis as typeof globalThis & {\n [SERVER_ACTION_MANIFESTS_SINGLETON]?: {\n clientReferenceManifestsPerPage?: Record<string, Manifest>;\n };\n __RSC_MANIFEST?: Record<string, unknown>;\n };\n\n // manually handle the internal Next.js manifest\n self.__RSC_MANIFEST = self.__RSC_MANIFEST || {};\n self.__RSC_MANIFEST[page] = self.__RSC_MANIFEST[page] || manifest;\n\n // get the client and SSR module mapping to be able to use client components in the remote component\n let clientModules = manifest?.clientModules ?? {};\n const ssrModuleMapping = { ...manifest?.ssrModuleMapping };\n\n // if the remote component is used in a hosting application, we need to mutate the module map to include the zone\n clientModules = Object.fromEntries(\n Object.entries(clientModules).map(([key, value]) => {\n // append a prefix to each entry in the module map to include the zone of the remote component\n const remoteId = `[${PROJECT_ID}] ${value.id}`;\n ssrModuleMapping[remoteId] = ssrModuleMapping[value.id];\n // override the original id with the new remote id\n return [\n key,\n {\n ...value,\n id: remoteId,\n // prepend the current zone to the chunks to handle remote component chunk loading in Webpack\n // this is required to avoid loading the wrong chunk in the host application\n chunks: value.chunks.map((chunk) => `[${PROJECT_ID}] ${chunk}`),\n // async: true,\n },\n ];\n }),\n );\n\n // dynamically import the runtime specific RSC rendering functions and client component\n const [{ renderToReadableStream }, { RemoteComponentSSR }] =\n await Promise.all(\n process.env.TURBOPACK\n ? [\n import('react-server-dom-turbopack/server.edge'),\n import('./render-client-turbopack'),\n ]\n : [\n import('react-server-dom-webpack/server.edge'),\n import('./render-client-webpack'),\n ],\n );\n // render the wrapped content of this component (children) into an RSC stream\n const stream = renderToReadableStream(children, clientModules);\n\n const data = [];\n const decoder = new TextDecoder();\n\n // convert the stream to an array for safe passing to the client\n for await (const chunk of stream as unknown as AsyncIterable<Uint8Array>) {\n data.push(decoder.decode(chunk));\n }\n\n const runtime = process.env.TURBOPACK ? 'turbopack' : 'webpack';\n const remoteComponentName = `${name}_${route?.replace(/\\//g, '_')}`;\n\n return (\n // wrap the remote component content into a div to know which part of the HTML belongs to the remote component\n <div\n data-bundle={PROJECT_ID}\n data-route={route}\n data-runtime={runtime}\n id={`${remoteComponentName}_ssr`}\n >\n <RemoteComponentSSR\n data={data}\n moduleLoading={manifest?.moduleLoading ?? {}}\n moduleMap={ssrModuleMapping}\n name={remoteComponentName}\n />\n {/* inject RSC flight data as <script> */}\n <RemoteComponentData data={data} name={remoteComponentName} />\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAiBI;AAjBJ,qBAAwB;AACxB,gCAAiC;AAIjC,MAAM,oCAAoC,OAAO;AAAA,EAC/C;AACF;AAEA,MAAM,aAAa,QAAQ,IAAI;AAM/B,SAAS,oBAAoB,EAAE,MAAM,KAAK,GAAqC;AAC7E,SACE,4CAAC,YAAO,IAAI,GAAG,YACZ,eACE;AAAA,IACC,CAAC,OAAO;AAAA;AAAA,MAEN,GAAG,MAAM,IAAI,SAAS,gBAAgB,gBAAgB,WAAW,eAAe,KAAK;AAAA,QACnF;AAAA,MACF;AAAA;AAAA,EACJ,EACC,KAAK,IAAI,GACd;AAEJ;AA2BA,eAAsB,gBAAgB;AAAA,EACpC,OAAO;AAAA,EACP;AACF,GAG6B;AAC3B,QAAM,cAAc,UAAM,wBAAQ;AAClC,QAAM,MAAM,YAAY,IAAI,6BAA6B;AACzD,QAAM,WAAW,QAAQ;AAEzB,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAKA,QAAM,EAAE,SAAS,IAAI,IAAI,IAAI,GAAG;AAEhC,QAAM,EAAE,MAAM,MAAM,IAAI,2CAAiB,SAAS,KAAK,EAAE,MAAM,SAAS;AAGxE,QAAM,YACJ,WAKA,iCAAiC;AACnC,QAAM,WAAW,UAAU,kCAAkC,SAAS,IAAI;AAE1E,QAAM,OAAO;AAQb,OAAK,iBAAiB,KAAK,kBAAkB,CAAC;AAC9C,OAAK,eAAe,IAAI,IAAI,KAAK,eAAe,IAAI,KAAK;AAGzD,MAAI,gBAAgB,UAAU,iBAAiB,CAAC;AAChD,QAAM,mBAAmB,EAAE,GAAG,UAAU,iBAAiB;AAGzD,kBAAgB,OAAO;AAAA,IACrB,OAAO,QAAQ,aAAa,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AAElD,YAAM,WAAW,IAAI,eAAe,MAAM;AAC1C,uBAAiB,QAAQ,IAAI,iBAAiB,MAAM,EAAE;AAEtD,aAAO;AAAA,QACL;AAAA,QACA;AAAA,UACE,GAAG;AAAA,UACH,IAAI;AAAA;AAAA;AAAA,UAGJ,QAAQ,MAAM,OAAO,IAAI,CAAC,UAAU,IAAI,eAAe,OAAO;AAAA;AAAA,QAEhE;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAGA,QAAM,CAAC,EAAE,uBAAuB,GAAG,EAAE,mBAAmB,CAAC,IACvD,MAAM,QAAQ;AAAA,IACZ,QAAQ,IAAI,YACR;AAAA,MACE,OAAO,wCAAwC;AAAA,MAC/C,OAAO,2BAA2B;AAAA,IACpC,IACA;AAAA,MACE,OAAO,sCAAsC;AAAA,MAC7C,OAAO,yBAAyB;AAAA,IAClC;AAAA,EACN;AAEF,QAAM,SAAS,uBAAuB,UAAU,aAAa;AAE7D,QAAM,OAAO,CAAC;AACd,QAAM,UAAU,IAAI,YAAY;AAGhC,mBAAiB,SAAS,QAAgD;AACxE,SAAK,KAAK,QAAQ,OAAO,KAAK,CAAC;AAAA,EACjC;AAEA,QAAM,UAAU,QAAQ,IAAI,YAAY,cAAc;AACtD,QAAM,sBAAsB,GAAG,QAAQ,OAAO,QAAQ,OAAO,GAAG;AAEhE;AAAA;AAAA,IAEE;AAAA,MAAC;AAAA;AAAA,QACC,eAAa;AAAA,QACb,cAAY;AAAA,QACZ,gBAAc;AAAA,QACd,IAAI,GAAG;AAAA,QAEP;AAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,eAAe,UAAU,iBAAiB,CAAC;AAAA,cAC3C,WAAW;AAAA,cACX,MAAM;AAAA;AAAA,UACR;AAAA,UAEA,4CAAC,uBAAoB,MAAY,MAAM,qBAAqB;AAAA;AAAA;AAAA,IAC9D;AAAA;AAEJ;","names":[]}
|
|
@@ -1,9 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* RemoteComponent is a Next.js component that exposes a remote component
|
|
3
|
+
* that can be used in a host application.
|
|
4
|
+
*
|
|
5
|
+
* @param name - The name of the remote component. Use a unique name to expose multiple remote components from the same page.
|
|
6
|
+
* @param children - The content of the remote component. This is the content that will be rendered as the remote component.
|
|
7
|
+
* @returns A React component that renders the remote component.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
*
|
|
11
|
+
* Use the `<RemoteComponent>` in your Next.js App Router application to expose the children as a remote component:
|
|
12
|
+
*
|
|
13
|
+
* ```tsx
|
|
14
|
+
* import { RemoteComponent } from 'remote-components/next';
|
|
15
|
+
*
|
|
16
|
+
* export default function MyPage() {
|
|
17
|
+
* return (
|
|
18
|
+
* <RemoteComponent>
|
|
19
|
+
* <h1>Hello from the remote component!</h1>
|
|
20
|
+
* <p>This is a remote component that can be used in a host application.</p>
|
|
21
|
+
* </RemoteComponent>
|
|
22
|
+
* );
|
|
23
|
+
* }
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
4
26
|
declare function RemoteComponent({ name, children, }: {
|
|
5
27
|
name?: string;
|
|
6
28
|
children: React.ReactNode;
|
|
7
|
-
}): Promise<
|
|
29
|
+
}): Promise<React.ReactNode>;
|
|
8
30
|
|
|
9
31
|
export { RemoteComponent };
|
|
@@ -4,7 +4,7 @@ import { workAsyncStorage } from "next/dist/server/app-render/work-async-storage
|
|
|
4
4
|
const SERVER_ACTION_MANIFESTS_SINGLETON = Symbol.for(
|
|
5
5
|
"next.server.action-manifests"
|
|
6
6
|
);
|
|
7
|
-
const
|
|
7
|
+
const PROJECT_ID = process.env.REMOTE_COMPONENTS_PROJECT_ID;
|
|
8
8
|
function RemoteComponentData({ name, data }) {
|
|
9
9
|
return /* @__PURE__ */ jsx("script", { id: `${name}_rsc`, children: data.map(
|
|
10
10
|
(chunk, i) => (
|
|
@@ -36,7 +36,7 @@ async function RemoteComponent({
|
|
|
36
36
|
const ssrModuleMapping = { ...manifest?.ssrModuleMapping };
|
|
37
37
|
clientModules = Object.fromEntries(
|
|
38
38
|
Object.entries(clientModules).map(([key, value]) => {
|
|
39
|
-
const remoteId = `[${
|
|
39
|
+
const remoteId = `[${PROJECT_ID}] ${value.id}`;
|
|
40
40
|
ssrModuleMapping[remoteId] = ssrModuleMapping[value.id];
|
|
41
41
|
return [
|
|
42
42
|
key,
|
|
@@ -45,7 +45,7 @@ async function RemoteComponent({
|
|
|
45
45
|
id: remoteId,
|
|
46
46
|
// prepend the current zone to the chunks to handle remote component chunk loading in Webpack
|
|
47
47
|
// this is required to avoid loading the wrong chunk in the host application
|
|
48
|
-
chunks: value.chunks.map((chunk) => `[${
|
|
48
|
+
chunks: value.chunks.map((chunk) => `[${PROJECT_ID}] ${chunk}`)
|
|
49
49
|
// async: true,
|
|
50
50
|
}
|
|
51
51
|
];
|
|
@@ -73,7 +73,7 @@ async function RemoteComponent({
|
|
|
73
73
|
/* @__PURE__ */ jsxs(
|
|
74
74
|
"div",
|
|
75
75
|
{
|
|
76
|
-
"data-bundle":
|
|
76
|
+
"data-bundle": PROJECT_ID,
|
|
77
77
|
"data-route": route,
|
|
78
78
|
"data-runtime": runtime,
|
|
79
79
|
id: `${remoteComponentName}_ssr`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/remote/render-server.tsx"],"sourcesContent":["import { headers } from 'next/headers';\nimport { workAsyncStorage } from 'next/dist/server/app-render/work-async-storage.external';\nimport type { Manifest } from './types';\n\n// internal Next.js symbol to access the manifest which is stored in the global scope\nconst SERVER_ACTION_MANIFESTS_SINGLETON = Symbol.for(\n 'next.server.action-manifests',\n);\n\nconst
|
|
1
|
+
{"version":3,"sources":["../../../src/next/remote/render-server.tsx"],"sourcesContent":["import { headers } from 'next/headers';\nimport { workAsyncStorage } from 'next/dist/server/app-render/work-async-storage.external';\nimport type { Manifest } from './types';\n\n// internal Next.js symbol to access the manifest which is stored in the global scope\nconst SERVER_ACTION_MANIFESTS_SINGLETON = Symbol.for(\n 'next.server.action-manifests',\n);\n\nconst PROJECT_ID = process.env.REMOTE_COMPONENTS_PROJECT_ID;\n\n// inject the RSC flight data into the HTML response using <script>\n// the RSC flight data is used to hydrate the remote component on the host\n// this approach is similar to an island architecture on the host\n// the remote component is static HTML until it is hydrated using this RSC flight data\nfunction RemoteComponentData({ name, data }: { name: string; data: string[] }) {\n return (\n <script id={`${name}_rsc`}>\n {data\n .map(\n (chunk, i) =>\n // make the data handling somewhat safe\n `${i === 0 ? `self[\"${name}\"]=self[\"${name}\"]||[];` : ''}self[\"${name}\"].push(${JSON.stringify(\n chunk,\n )});`,\n )\n .join('\\n')}\n </script>\n );\n}\n\n/**\n * RemoteComponent is a Next.js component that exposes a remote component\n * that can be used in a host application.\n *\n * @param name - The name of the remote component. Use a unique name to expose multiple remote components from the same page.\n * @param children - The content of the remote component. This is the content that will be rendered as the remote component.\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 expose the children as a remote component:\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 */\nexport async function RemoteComponent({\n name = '__vercel_remote_component',\n children,\n}: {\n name?: string;\n children: React.ReactNode;\n}): Promise<React.ReactNode> {\n const headersList = await headers();\n const url = headersList.get('Vercel-Remote-Component-Url');\n const isRemote = url !== null;\n\n if (!isRemote) {\n return children;\n }\n\n // this URL passed by the remote component consumer is only used to know the public address of the remote component\n // it is only used to determine if we need to mutate the client module map for now\n\n const { pathname } = new URL(url);\n // access the internal Next.js work store to get the active page and route\n const { page, route } = workAsyncStorage.getStore() ?? { page: pathname };\n\n // get reference to the manifests from the global scope\n const manifests = (\n globalThis as typeof globalThis & {\n [SERVER_ACTION_MANIFESTS_SINGLETON]: {\n clientReferenceManifestsPerPage?: Record<string, Manifest>;\n };\n }\n )[SERVER_ACTION_MANIFESTS_SINGLETON];\n const manifest = manifests.clientReferenceManifestsPerPage?.[route ?? page];\n\n const self = globalThis as typeof globalThis & {\n [SERVER_ACTION_MANIFESTS_SINGLETON]?: {\n clientReferenceManifestsPerPage?: Record<string, Manifest>;\n };\n __RSC_MANIFEST?: Record<string, unknown>;\n };\n\n // manually handle the internal Next.js manifest\n self.__RSC_MANIFEST = self.__RSC_MANIFEST || {};\n self.__RSC_MANIFEST[page] = self.__RSC_MANIFEST[page] || manifest;\n\n // get the client and SSR module mapping to be able to use client components in the remote component\n let clientModules = manifest?.clientModules ?? {};\n const ssrModuleMapping = { ...manifest?.ssrModuleMapping };\n\n // if the remote component is used in a hosting application, we need to mutate the module map to include the zone\n clientModules = Object.fromEntries(\n Object.entries(clientModules).map(([key, value]) => {\n // append a prefix to each entry in the module map to include the zone of the remote component\n const remoteId = `[${PROJECT_ID}] ${value.id}`;\n ssrModuleMapping[remoteId] = ssrModuleMapping[value.id];\n // override the original id with the new remote id\n return [\n key,\n {\n ...value,\n id: remoteId,\n // prepend the current zone to the chunks to handle remote component chunk loading in Webpack\n // this is required to avoid loading the wrong chunk in the host application\n chunks: value.chunks.map((chunk) => `[${PROJECT_ID}] ${chunk}`),\n // async: true,\n },\n ];\n }),\n );\n\n // dynamically import the runtime specific RSC rendering functions and client component\n const [{ renderToReadableStream }, { RemoteComponentSSR }] =\n await Promise.all(\n process.env.TURBOPACK\n ? [\n import('react-server-dom-turbopack/server.edge'),\n import('./render-client-turbopack'),\n ]\n : [\n import('react-server-dom-webpack/server.edge'),\n import('./render-client-webpack'),\n ],\n );\n // render the wrapped content of this component (children) into an RSC stream\n const stream = renderToReadableStream(children, clientModules);\n\n const data = [];\n const decoder = new TextDecoder();\n\n // convert the stream to an array for safe passing to the client\n for await (const chunk of stream as unknown as AsyncIterable<Uint8Array>) {\n data.push(decoder.decode(chunk));\n }\n\n const runtime = process.env.TURBOPACK ? 'turbopack' : 'webpack';\n const remoteComponentName = `${name}_${route?.replace(/\\//g, '_')}`;\n\n return (\n // wrap the remote component content into a div to know which part of the HTML belongs to the remote component\n <div\n data-bundle={PROJECT_ID}\n data-route={route}\n data-runtime={runtime}\n id={`${remoteComponentName}_ssr`}\n >\n <RemoteComponentSSR\n data={data}\n moduleLoading={manifest?.moduleLoading ?? {}}\n moduleMap={ssrModuleMapping}\n name={remoteComponentName}\n />\n {/* inject RSC flight data as <script> */}\n <RemoteComponentData data={data} name={remoteComponentName} />\n </div>\n );\n}\n"],"mappings":"AAiBI,cAwIA,YAxIA;AAjBJ,SAAS,eAAe;AACxB,SAAS,wBAAwB;AAIjC,MAAM,oCAAoC,OAAO;AAAA,EAC/C;AACF;AAEA,MAAM,aAAa,QAAQ,IAAI;AAM/B,SAAS,oBAAoB,EAAE,MAAM,KAAK,GAAqC;AAC7E,SACE,oBAAC,YAAO,IAAI,GAAG,YACZ,eACE;AAAA,IACC,CAAC,OAAO;AAAA;AAAA,MAEN,GAAG,MAAM,IAAI,SAAS,gBAAgB,gBAAgB,WAAW,eAAe,KAAK;AAAA,QACnF;AAAA,MACF;AAAA;AAAA,EACJ,EACC,KAAK,IAAI,GACd;AAEJ;AA2BA,eAAsB,gBAAgB;AAAA,EACpC,OAAO;AAAA,EACP;AACF,GAG6B;AAC3B,QAAM,cAAc,MAAM,QAAQ;AAClC,QAAM,MAAM,YAAY,IAAI,6BAA6B;AACzD,QAAM,WAAW,QAAQ;AAEzB,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAKA,QAAM,EAAE,SAAS,IAAI,IAAI,IAAI,GAAG;AAEhC,QAAM,EAAE,MAAM,MAAM,IAAI,iBAAiB,SAAS,KAAK,EAAE,MAAM,SAAS;AAGxE,QAAM,YACJ,WAKA,iCAAiC;AACnC,QAAM,WAAW,UAAU,kCAAkC,SAAS,IAAI;AAE1E,QAAM,OAAO;AAQb,OAAK,iBAAiB,KAAK,kBAAkB,CAAC;AAC9C,OAAK,eAAe,IAAI,IAAI,KAAK,eAAe,IAAI,KAAK;AAGzD,MAAI,gBAAgB,UAAU,iBAAiB,CAAC;AAChD,QAAM,mBAAmB,EAAE,GAAG,UAAU,iBAAiB;AAGzD,kBAAgB,OAAO;AAAA,IACrB,OAAO,QAAQ,aAAa,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM;AAElD,YAAM,WAAW,IAAI,eAAe,MAAM;AAC1C,uBAAiB,QAAQ,IAAI,iBAAiB,MAAM,EAAE;AAEtD,aAAO;AAAA,QACL;AAAA,QACA;AAAA,UACE,GAAG;AAAA,UACH,IAAI;AAAA;AAAA;AAAA,UAGJ,QAAQ,MAAM,OAAO,IAAI,CAAC,UAAU,IAAI,eAAe,OAAO;AAAA;AAAA,QAEhE;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAGA,QAAM,CAAC,EAAE,uBAAuB,GAAG,EAAE,mBAAmB,CAAC,IACvD,MAAM,QAAQ;AAAA,IACZ,QAAQ,IAAI,YACR;AAAA,MACE,OAAO,wCAAwC;AAAA,MAC/C,OAAO,2BAA2B;AAAA,IACpC,IACA;AAAA,MACE,OAAO,sCAAsC;AAAA,MAC7C,OAAO,yBAAyB;AAAA,IAClC;AAAA,EACN;AAEF,QAAM,SAAS,uBAAuB,UAAU,aAAa;AAE7D,QAAM,OAAO,CAAC;AACd,QAAM,UAAU,IAAI,YAAY;AAGhC,mBAAiB,SAAS,QAAgD;AACxE,SAAK,KAAK,QAAQ,OAAO,KAAK,CAAC;AAAA,EACjC;AAEA,QAAM,UAAU,QAAQ,IAAI,YAAY,cAAc;AACtD,QAAM,sBAAsB,GAAG,QAAQ,OAAO,QAAQ,OAAO,GAAG;AAEhE;AAAA;AAAA,IAEE;AAAA,MAAC;AAAA;AAAA,QACC,eAAa;AAAA,QACb,cAAY;AAAA,QACZ,gBAAc;AAAA,QACd,IAAI,GAAG;AAAA,QAEP;AAAA;AAAA,YAAC;AAAA;AAAA,cACC;AAAA,cACA,eAAe,UAAU,iBAAiB,CAAC;AAAA,cAC3C,WAAW;AAAA,cACX,MAAM;AAAA;AAAA,UACR;AAAA,UAEA,oBAAC,uBAAoB,MAAY,MAAM,qBAAqB;AAAA;AAAA;AAAA,IAC9D;AAAA;AAEJ;","names":[]}
|
|
@@ -111,9 +111,13 @@ function nextClientPagesLoader(bundle, route, styleContainer = document.head) {
|
|
|
111
111
|
const __NEXT_P_ORIGINAL = self.__NEXT_P;
|
|
112
112
|
const selfOriginal = self;
|
|
113
113
|
delete selfOriginal.__NEXT_P;
|
|
114
|
-
self.__remote_webpack_require__?.[bundle]?.(
|
|
114
|
+
self.__remote_webpack_require__?.[bundle]?.(
|
|
115
|
+
self.__remote_webpack_require__[bundle].type !== "turbopack" ? componentLoaderChunk : `[${bundle}] ${componentLoaderChunk}`
|
|
116
|
+
);
|
|
115
117
|
if (typeof appLoaderChunk === "string" || typeof appLoaderChunk === "number" && appLoaderChunk !== -1) {
|
|
116
|
-
self.__remote_webpack_require__?.[bundle]?.(
|
|
118
|
+
self.__remote_webpack_require__?.[bundle]?.(
|
|
119
|
+
self.__remote_webpack_require__[bundle].type !== "turbopack" ? appLoaderChunk : `[${bundle}] ${appLoaderChunk}`
|
|
120
|
+
);
|
|
117
121
|
}
|
|
118
122
|
if (self.__NEXT_P) {
|
|
119
123
|
const [, componentLoader] = self.__NEXT_P[0] ?? [
|
|
@@ -201,9 +205,13 @@ function getBundleKey(bundle) {
|
|
|
201
205
|
}
|
|
202
206
|
|
|
203
207
|
// src/shared/client/webpack-adapter.ts
|
|
204
|
-
async function setupWebpackRuntime(runtime, scripts = [], bundle, shared = {}, remoteShared = {}) {
|
|
208
|
+
async function setupWebpackRuntime(runtime, scripts = [], url = new URL(location.href), bundle, shared = {}, remoteShared = {}) {
|
|
205
209
|
const self = globalThis;
|
|
206
210
|
self.__DISABLE_WEBPACK_EXEC__ = true;
|
|
211
|
+
if (!self.__remote_bundle_url__) {
|
|
212
|
+
self.__remote_bundle_url__ = {};
|
|
213
|
+
}
|
|
214
|
+
self.__remote_bundle_url__[bundle ?? "default"] = url;
|
|
207
215
|
await initializeSharedModules(bundle ?? "default", shared, remoteShared);
|
|
208
216
|
if (typeof self.__webpack_require__ !== "function" || self.__webpack_require_type__ !== "turbopack") {
|
|
209
217
|
if (!self.__original_webpack_require__ && !self.__original_webpack_chunk_load__) {
|
|
@@ -213,7 +221,7 @@ async function setupWebpackRuntime(runtime, scripts = [], bundle, shared = {}, r
|
|
|
213
221
|
self.__webpack_chunk_load__ = createChunkLoader(runtime);
|
|
214
222
|
self.__webpack_require__ = createModuleRequire(runtime);
|
|
215
223
|
self.__webpack_require_type__ = runtime;
|
|
216
|
-
if (self.__remote_webpack_require__) {
|
|
224
|
+
if (self.__remote_webpack_require__ && runtime === RUNTIME_TURBOPACK) {
|
|
217
225
|
const remoteBundle = bundle ?? "default";
|
|
218
226
|
self.__remote_webpack_require__[remoteBundle] = self.__webpack_require__;
|
|
219
227
|
self.__remote_webpack_require__[remoteBundle].type = "turbopack";
|
|
@@ -241,12 +249,13 @@ function createChunkLoader(runtime) {
|
|
|
241
249
|
bundle: scriptBundle ?? "",
|
|
242
250
|
id: chunkId
|
|
243
251
|
};
|
|
252
|
+
const self = globalThis;
|
|
244
253
|
const url = new URL(
|
|
245
254
|
path ? `${prefix ?? ""}${path}`.replace(
|
|
246
255
|
/(?<char>[^:])(?<double>\/\/)/g,
|
|
247
256
|
"$1/"
|
|
248
257
|
) : "/",
|
|
249
|
-
location.origin
|
|
258
|
+
self.__remote_bundle_url__?.[bundle ?? "default"] ?? new URL(location.origin)
|
|
250
259
|
).href;
|
|
251
260
|
if (url.endsWith(".css")) {
|
|
252
261
|
loadCSS(url);
|
|
@@ -328,8 +337,8 @@ async function handleTurbopackChunk(code, bundle, url) {
|
|
|
328
337
|
function createModuleRequire(runtime) {
|
|
329
338
|
return (id) => {
|
|
330
339
|
const self = globalThis;
|
|
340
|
+
const { bundle, id: moduleId } = id.match(REMOTE_COMPONENT_REGEX)?.groups ?? { bundle: "default", id };
|
|
331
341
|
try {
|
|
332
|
-
const { bundle, id: moduleId } = id.match(REMOTE_COMPONENT_REGEX)?.groups ?? { bundle: "", id };
|
|
333
342
|
if (runtime === RUNTIME_WEBPACK && bundle && moduleId) {
|
|
334
343
|
return self.__remote_webpack_require__?.[bundle]?.(moduleId);
|
|
335
344
|
}
|
|
@@ -342,7 +351,13 @@ function createModuleRequire(runtime) {
|
|
|
342
351
|
}
|
|
343
352
|
throw new Error(`Module ${id} not found`);
|
|
344
353
|
} catch {
|
|
345
|
-
|
|
354
|
+
try {
|
|
355
|
+
return self.__original_webpack_require__?.(id);
|
|
356
|
+
} catch {
|
|
357
|
+
throw new Error(
|
|
358
|
+
`Module ${id} not found in remote component bundle ${bundle}`
|
|
359
|
+
);
|
|
360
|
+
}
|
|
346
361
|
}
|
|
347
362
|
};
|
|
348
363
|
}
|
|
@@ -355,12 +370,20 @@ function initializeSharedModules(bundle, shared = {}, remoteShared = {}) {
|
|
|
355
370
|
return Promise.all(
|
|
356
371
|
Object.entries(remoteShared).map(async ([id, module2]) => {
|
|
357
372
|
if (self.__remote_shared_modules__?.[bundle]) {
|
|
358
|
-
self.__remote_shared_modules__[bundle][id.replace("[app-ssr]", "[app-client]")] = await (shared[module2] ?? (() =>
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
373
|
+
self.__remote_shared_modules__[bundle][id.replace("[app-ssr]", "[app-client]")] = await (shared[module2] ?? (() => Promise.resolve(
|
|
374
|
+
new Proxy(
|
|
375
|
+
{},
|
|
376
|
+
{
|
|
377
|
+
get(_, prop) {
|
|
378
|
+
if (prop !== "then") {
|
|
379
|
+
console.warn(
|
|
380
|
+
`Shared dependency "${module2}" not found for "${bundle}" when trying to import "${prop}".`
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
)
|
|
386
|
+
)))();
|
|
364
387
|
}
|
|
365
388
|
})
|
|
366
389
|
);
|
|
@@ -488,6 +511,7 @@ function createRSCStream(name, data) {
|
|
|
488
511
|
|
|
489
512
|
// src/shared/client/component-loader.ts
|
|
490
513
|
async function loadRemoteComponent({
|
|
514
|
+
url,
|
|
491
515
|
name,
|
|
492
516
|
bundle,
|
|
493
517
|
route = "/",
|
|
@@ -496,13 +520,21 @@ async function loadRemoteComponent({
|
|
|
496
520
|
nextData,
|
|
497
521
|
scripts = [],
|
|
498
522
|
shared = {},
|
|
499
|
-
remoteShared = {}
|
|
523
|
+
remoteShared = {},
|
|
524
|
+
container
|
|
500
525
|
}) {
|
|
501
526
|
try {
|
|
502
527
|
if (runtime === "webpack") {
|
|
503
528
|
await loadScripts(scripts);
|
|
504
529
|
}
|
|
505
|
-
await setupWebpackRuntime(
|
|
530
|
+
await setupWebpackRuntime(
|
|
531
|
+
runtime,
|
|
532
|
+
scripts,
|
|
533
|
+
url,
|
|
534
|
+
bundle,
|
|
535
|
+
shared,
|
|
536
|
+
remoteShared
|
|
537
|
+
);
|
|
506
538
|
if (bundle) {
|
|
507
539
|
const resolve = {
|
|
508
540
|
"/react/index.js": React,
|
|
@@ -515,7 +547,7 @@ async function loadRemoteComponent({
|
|
|
515
547
|
if (data.length > 0) {
|
|
516
548
|
return await loadRSCComponent(name, data);
|
|
517
549
|
} else if (nextData) {
|
|
518
|
-
return loadNextPagesComponent(bundle, route, nextData, name);
|
|
550
|
+
return loadNextPagesComponent(bundle, route, nextData, name, container);
|
|
519
551
|
}
|
|
520
552
|
throw new Error(`No valid data provided for remote component ${name}`);
|
|
521
553
|
} catch (error) {
|
|
@@ -531,8 +563,8 @@ async function loadRSCComponent(name, data) {
|
|
|
531
563
|
const component = createFromReadableStream(stream);
|
|
532
564
|
return { component };
|
|
533
565
|
}
|
|
534
|
-
function loadNextPagesComponent(bundle, route, nextData, name) {
|
|
535
|
-
const { Component, App } = nextClientPagesLoader(bundle, route);
|
|
566
|
+
function loadNextPagesComponent(bundle, route, nextData, name, container) {
|
|
567
|
+
const { Component, App } = nextClientPagesLoader(bundle, route, container);
|
|
536
568
|
if (!Component) {
|
|
537
569
|
throw new Error(
|
|
538
570
|
`Remote component ${name} is trying to load the component for route ${route} but it is not available.`
|