remote-components 0.0.36 → 0.0.37
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-8951c052.d.ts → component-loader-2ca91ad8.d.ts} +4 -1
- package/dist/internal/next/host/app-router-client.cjs +51 -38
- package/dist/internal/next/host/app-router-client.cjs.map +1 -1
- package/dist/internal/next/host/app-router-client.d.ts +2 -2
- package/dist/internal/next/host/app-router-client.js +51 -38
- package/dist/internal/next/host/app-router-client.js.map +1 -1
- package/dist/internal/shared/client/remote-component.d.ts +2 -2
- package/dist/internal/shared/ssr/fetch-remote-component.cjs +14 -14
- 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 +14 -14
- package/dist/internal/shared/ssr/fetch-remote-component.js.map +1 -1
- package/dist/internal/shared/ssr/get-client-or-server-url.cjs +35 -0
- package/dist/internal/shared/ssr/get-client-or-server-url.cjs.map +1 -0
- package/dist/internal/shared/ssr/get-client-or-server-url.d.ts +3 -0
- package/dist/internal/shared/ssr/get-client-or-server-url.js +11 -0
- package/dist/internal/shared/ssr/get-client-or-server-url.js.map +1 -0
- package/dist/next/host/app-router-server.cjs +3 -2
- package/dist/next/host/app-router-server.cjs.map +1 -1
- package/dist/next/host/app-router-server.js +3 -2
- package/dist/next/host/app-router-server.js.map +1 -1
- package/dist/next/host/client/index.cjs +31 -27
- 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 +31 -27
- package/dist/next/host/client/index.js.map +1 -1
- package/dist/next/host/pages-router-server.cjs +4 -8
- package/dist/next/host/pages-router-server.cjs.map +1 -1
- package/dist/next/host/pages-router-server.d.ts +0 -1
- package/dist/next/host/pages-router-server.js +4 -8
- package/dist/next/host/pages-router-server.js.map +1 -1
- package/dist/react/index.cjs +23 -27
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.js +23 -27
- package/dist/react/index.js.map +1 -1
- package/dist/{types-4e7dea94.d.ts → types-235b68a5.d.ts} +4 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/next/host/app-router-server.tsx"],"sourcesContent":["import { Children, Suspense } from 'react';\nimport { RemoteComponentClient } from '#internal/next/host/app-router-client';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\nexport interface RemoteComponentProps {\n /** The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported. */\n src: string | URL;\n /** The name of the exposed remote component. Used to identify the remote component when multiple remote components are exposed on a page. */\n name?: string;\n /** Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation. */\n isolate?: boolean;\n /** The mode of the Shadow DOM. Defaults to `open`. */\n mode?: 'open' | 'closed';\n /** Whether to include a CSS reset style in the shadow DOM. Defaults to `false`. */\n reset?: boolean;\n /** Headers to use when fetching the remote component, such as authentication headers. */\n additionalHeaders?: Headers | Record<string, string>;\n /** Loading fallback content to display while the remote component is being fetched. */\n children?: React.ReactNode;\n}\n\n/**\n * RemoteComponent is a Next.js component that fetches and renders a remote component.\n * It supports SSR and can isolate the remote component in a shadow DOM.\n *\n * @param src - The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param name - The name of the exposed remote component. Used to identify the remote component when multiple remote components are exposed on a page.\n * @param isolate - Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation.\n * @param mode - The mode of the Shadow DOM. Defaults to `open`.\n * @param reset - Whether to include a CSS reset style in the shadow DOM. Defaults to `false`.\n * @param additionalHeaders - Headers to use when fetching the remote component, such as authentication headers.\n * @param children - Loading fallback content to display while the remote component is being fetched.\n * @returns A React component that renders the remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to consume a remote component from a remote application:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next/host';\n *\n * export default function MyPage() {\n * return (\n * <>\n * <h1>Welcome to My Page</h1>\n * <p>This page consumes a remote component from another application.</p>\n * <RemoteComponent src=\"/nextjs-app-remote/components/header\" />\n * </>\n * );\n * }\n * ```\n */\nexport async function RemoteComponent({\n src,\n name: nameProp,\n isolate,\n mode,\n reset,\n additionalHeaders,\n children,\n}: RemoteComponentProps): Promise<React.ReactElement> {\n const headers = new Headers();\n for (const [key, value] of additionalHeaders instanceof Headers\n ? additionalHeaders.entries()\n : Object.entries(additionalHeaders ?? {})) {\n headers.set(key, value);\n }\n\n const {\n metadata,\n scripts,\n links,\n hydrationData,\n nextData,\n component,\n remoteShared,\n
|
|
1
|
+
{"version":3,"sources":["../../../src/next/host/app-router-server.tsx"],"sourcesContent":["import { Children, Suspense } from 'react';\nimport { RemoteComponentClient } from '#internal/next/host/app-router-client';\nimport { fetchRemoteComponent } from '#internal/shared/ssr/fetch-remote-component';\n\nconst CURRENT_ZONE = process.env.NEXT_PUBLIC_MFE_CURRENT_APPLICATION;\n\nexport interface RemoteComponentProps {\n /** The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported. */\n src: string | URL;\n /** The name of the exposed remote component. Used to identify the remote component when multiple remote components are exposed on a page. */\n name?: string;\n /** Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation. */\n isolate?: boolean;\n /** The mode of the Shadow DOM. Defaults to `open`. */\n mode?: 'open' | 'closed';\n /** Whether to include a CSS reset style in the shadow DOM. Defaults to `false`. */\n reset?: boolean;\n /** Headers to use when fetching the remote component, such as authentication headers. */\n additionalHeaders?: Headers | Record<string, string>;\n /** Loading fallback content to display while the remote component is being fetched. */\n children?: React.ReactNode;\n}\n\n/**\n * RemoteComponent is a Next.js component that fetches and renders a remote component.\n * It supports SSR and can isolate the remote component in a shadow DOM.\n *\n * @param src - The source URL of the remote component. When using Vercel Microfrontends, you can use relative paths, e.g. `/nextjs-app-remote/components/header`. Absolute URLs are also supported.\n * @param name - The name of the exposed remote component. Used to identify the remote component when multiple remote components are exposed on a page.\n * @param isolate - Whether to isolate the remote component using a Shadow DOM wrapper. Defaults to `true`. Use `false` explicitly to disable isolation.\n * @param mode - The mode of the Shadow DOM. Defaults to `open`.\n * @param reset - Whether to include a CSS reset style in the shadow DOM. Defaults to `false`.\n * @param additionalHeaders - Headers to use when fetching the remote component, such as authentication headers.\n * @param children - Loading fallback content to display while the remote component is being fetched.\n * @returns A React component that renders the remote component.\n *\n * @example\n *\n * Use the `<RemoteComponent>` in your Next.js App Router application to consume a remote component from a remote application:\n *\n * ```tsx\n * import { RemoteComponent } from 'remote-components/next/host';\n *\n * export default function MyPage() {\n * return (\n * <>\n * <h1>Welcome to My Page</h1>\n * <p>This page consumes a remote component from another application.</p>\n * <RemoteComponent src=\"/nextjs-app-remote/components/header\" />\n * </>\n * );\n * }\n * ```\n */\nexport async function RemoteComponent({\n src,\n name: nameProp,\n isolate,\n mode,\n reset,\n additionalHeaders,\n children,\n}: RemoteComponentProps): Promise<React.ReactElement> {\n const headers = new Headers();\n for (const [key, value] of additionalHeaders instanceof Headers\n ? additionalHeaders.entries()\n : Object.entries(additionalHeaders ?? {})) {\n headers.set(key, value);\n }\n\n const {\n metadata,\n scripts,\n links,\n hydrationData,\n nextData,\n component,\n remoteShared,\n serverUrl,\n } = await fetchRemoteComponent(src, headers, {\n name: nameProp,\n rsc: true,\n });\n\n // pass all remote component data to the SSR/client layer\n // render remote component static HTML as children\n // remote _ssr suffix from remote component id\n const name = metadata.id.replace(/_ssr$/, '');\n const remoteComponentClient = (\n <RemoteComponentClient\n bundle={metadata.bundle || (CURRENT_ZONE ?? name)}\n data={hydrationData}\n isolate={isolate}\n links={links}\n mode={mode}\n name={name}\n nextData={nextData}\n remoteShared={remoteShared}\n reset={reset}\n route={metadata.route}\n runtime={metadata.runtime}\n scripts={scripts}\n type={metadata.type}\n src={typeof src === 'string' ? src : src.href}\n serverUrl={serverUrl.href}\n >\n {component}\n </RemoteComponentClient>\n );\n\n if (Children.count(children) > 0) {\n // if there are children, render them inside the remote component\n return <Suspense fallback={children}>{remoteComponentClient}</Suspense>;\n }\n\n return remoteComponentClient;\n}\n"],"mappings":"AAyFI;AAzFJ,SAAS,UAAU,gBAAgB;AACnC,SAAS,6BAA6B;AACtC,SAAS,4BAA4B;AAErC,MAAM,eAAe,QAAQ,IAAI;AAkDjC,eAAsB,gBAAgB;AAAA,EACpC;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsD;AACpD,QAAM,UAAU,IAAI,QAAQ;AAC5B,aAAW,CAAC,KAAK,KAAK,KAAK,6BAA6B,UACpD,kBAAkB,QAAQ,IAC1B,OAAO,QAAQ,qBAAqB,CAAC,CAAC,GAAG;AAC3C,YAAQ,IAAI,KAAK,KAAK;AAAA,EACxB;AAEA,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,MAAM,qBAAqB,KAAK,SAAS;AAAA,IAC3C,MAAM;AAAA,IACN,KAAK;AAAA,EACP,CAAC;AAKD,QAAM,OAAO,SAAS,GAAG,QAAQ,SAAS,EAAE;AAC5C,QAAM,wBACJ;AAAA,IAAC;AAAA;AAAA,MACC,QAAQ,SAAS,WAAW,gBAAgB;AAAA,MAC5C,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,OAAO,SAAS;AAAA,MAChB,SAAS,SAAS;AAAA,MAClB;AAAA,MACA,MAAM,SAAS;AAAA,MACf,KAAK,OAAO,QAAQ,WAAW,MAAM,IAAI;AAAA,MACzC,WAAW,UAAU;AAAA,MAEpB;AAAA;AAAA,EACH;AAGF,MAAI,SAAS,MAAM,QAAQ,IAAI,GAAG;AAEhC,WAAO,oBAAC,YAAS,UAAU,UAAW,iCAAsB;AAAA,EAC9D;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -1341,6 +1341,15 @@ function remoteFetchHeaders(additionalHeaders) {
|
|
|
1341
1341
|
};
|
|
1342
1342
|
}
|
|
1343
1343
|
|
|
1344
|
+
// src/shared/ssr/get-client-or-server-url.ts
|
|
1345
|
+
function getClientOrServerUrl(src, serverFallback) {
|
|
1346
|
+
const fallback = typeof location !== "undefined" ? location.href : serverFallback;
|
|
1347
|
+
if (!src) {
|
|
1348
|
+
return new URL(fallback);
|
|
1349
|
+
}
|
|
1350
|
+
return typeof src === "string" ? new URL(src, fallback) : src;
|
|
1351
|
+
}
|
|
1352
|
+
|
|
1344
1353
|
// src/react/index.tsx
|
|
1345
1354
|
var import_jsx_runtime2 = (
|
|
1346
1355
|
// TODO: remove wrapper div by converting HTML to RSC or React tree
|
|
@@ -1366,6 +1375,7 @@ function getRemoteComponentHtml(html) {
|
|
|
1366
1375
|
}
|
|
1367
1376
|
return "";
|
|
1368
1377
|
}
|
|
1378
|
+
var DUMMY_FALLBACK = "http://remote-components-dummy-fallback";
|
|
1369
1379
|
function RemoteComponent({
|
|
1370
1380
|
src,
|
|
1371
1381
|
isolate,
|
|
@@ -1381,7 +1391,7 @@ function RemoteComponent({
|
|
|
1381
1391
|
if (typeof src === "string") {
|
|
1382
1392
|
const url2 = new URL(
|
|
1383
1393
|
src,
|
|
1384
|
-
typeof document !== "undefined" ? location.href :
|
|
1394
|
+
typeof document !== "undefined" ? location.href : DUMMY_FALLBACK
|
|
1385
1395
|
);
|
|
1386
1396
|
if (url2.hash) {
|
|
1387
1397
|
return url2.hash.slice(1);
|
|
@@ -1392,20 +1402,23 @@ function RemoteComponent({
|
|
|
1392
1402
|
return nameProp;
|
|
1393
1403
|
}, [src, nameProp]);
|
|
1394
1404
|
const [data, setData] = (0, import_react.useState)(null);
|
|
1405
|
+
const url = (0, import_react.useMemo)(() => getClientOrServerUrl(src, DUMMY_FALLBACK), [src]);
|
|
1406
|
+
const id = url.origin === (typeof location !== "undefined" ? location.origin : DUMMY_FALLBACK) ? url.pathname : url.href;
|
|
1407
|
+
const keySuffix = `${escapeString(id)}_${escapeString(data?.name ?? name)}`;
|
|
1395
1408
|
const [remoteComponent, setRemoteComponent] = (0, import_react.useState)(null);
|
|
1396
1409
|
const shadowRootContainerRef = (0, import_react.useRef)(null);
|
|
1397
1410
|
const [shadowRoot, setShadowRoot] = (0, import_react.useState)(() => {
|
|
1398
1411
|
const self = globalThis;
|
|
1399
|
-
const shadowRootKey = `__remote_components_shadowroot_${
|
|
1412
|
+
const shadowRootKey = `__remote_components_shadowroot_${keySuffix}`;
|
|
1400
1413
|
const ssrShadowRoot = typeof document !== "undefined" ? document.querySelector(
|
|
1401
|
-
`[data-remote-component-id="shadowroot_${
|
|
1414
|
+
`[data-remote-component-id="shadowroot_${keySuffix}"]`
|
|
1402
1415
|
)?.shadowRoot ?? self[shadowRootKey] ?? null : null;
|
|
1403
1416
|
self[shadowRootKey] = null;
|
|
1404
1417
|
return ssrShadowRoot;
|
|
1405
1418
|
});
|
|
1406
1419
|
const htmlRef = (0, import_react.useRef)(
|
|
1407
1420
|
typeof document !== "undefined" ? document.querySelector(
|
|
1408
|
-
`[data-remote-component-id="shadowroot_${
|
|
1421
|
+
`[data-remote-component-id="shadowroot_${keySuffix}"]`
|
|
1409
1422
|
)?.shadowRoot?.innerHTML ?? document.getElementById(`__REMOTE_COMPONENT${name}`)?.innerHTML ?? document.querySelector(`div[data-bundle][data-route][id^="${name}"]`)?.innerHTML ?? document.querySelector("div[data-bundle][data-route]")?.innerHTML : null
|
|
1410
1423
|
);
|
|
1411
1424
|
const endTemplateRef = (0, import_react.useRef)(null);
|
|
@@ -1437,10 +1450,10 @@ function RemoteComponent({
|
|
|
1437
1450
|
}
|
|
1438
1451
|
if (isolate !== false && typeof document !== "undefined" && !shadowRoot) {
|
|
1439
1452
|
const self = globalThis;
|
|
1440
|
-
const shadowRootKey = `__remote_components_shadowroot_${
|
|
1453
|
+
const shadowRootKey = `__remote_components_shadowroot_${keySuffix}`;
|
|
1441
1454
|
let shadowRootElement = null;
|
|
1442
1455
|
const element = document.querySelector(
|
|
1443
|
-
`[data-remote-component-id="shadowroot_${
|
|
1456
|
+
`[data-remote-component-id="shadowroot_${keySuffix}"]`
|
|
1444
1457
|
);
|
|
1445
1458
|
shadowRootElement = self[shadowRootKey] ?? element?.shadowRoot ?? null;
|
|
1446
1459
|
if (!shadowRootElement && element) {
|
|
@@ -1457,7 +1470,7 @@ function RemoteComponent({
|
|
|
1457
1470
|
setShadowRoot(shadowRootElement);
|
|
1458
1471
|
}
|
|
1459
1472
|
}
|
|
1460
|
-
}, [
|
|
1473
|
+
}, [isolate, shadowRoot, remoteComponent, mode, keySuffix]);
|
|
1461
1474
|
(0, import_react.useLayoutEffect)(() => {
|
|
1462
1475
|
if (shadowRoot && remoteComponent) {
|
|
1463
1476
|
const resetStyles = shadowRoot.querySelectorAll(
|
|
@@ -1481,17 +1494,6 @@ function RemoteComponent({
|
|
|
1481
1494
|
}
|
|
1482
1495
|
}
|
|
1483
1496
|
}, [shadowRoot, remoteComponent, name]);
|
|
1484
|
-
const url = (0, import_react.useMemo)(() => {
|
|
1485
|
-
if (typeof src !== "string")
|
|
1486
|
-
return new URL(
|
|
1487
|
-
typeof document !== "undefined" ? location.href : "http://localhost"
|
|
1488
|
-
);
|
|
1489
|
-
try {
|
|
1490
|
-
return typeof document !== "undefined" ? new URL(src, location.href) : new URL(src);
|
|
1491
|
-
} catch {
|
|
1492
|
-
return new URL(src, "http://localhost");
|
|
1493
|
-
}
|
|
1494
|
-
}, [src]);
|
|
1495
1497
|
(0, import_react.useEffect)(() => {
|
|
1496
1498
|
if (src && src !== prevSrcRef.current) {
|
|
1497
1499
|
prevSrcRef.current = src;
|
|
@@ -1666,7 +1668,9 @@ function RemoteComponent({
|
|
|
1666
1668
|
}
|
|
1667
1669
|
let rscName;
|
|
1668
1670
|
if (rsc) {
|
|
1669
|
-
rscName = `__remote_component_rsc_${escapeString(
|
|
1671
|
+
rscName = `__remote_component_rsc_${escapeString(
|
|
1672
|
+
id
|
|
1673
|
+
)}_${escapeString(remoteName)}`;
|
|
1670
1674
|
rsc.textContent = rsc.textContent?.replace(
|
|
1671
1675
|
new RegExp(`self\\["${remoteName}"\\]`, "g"),
|
|
1672
1676
|
`self["${rscName}"]`
|
|
@@ -1677,14 +1681,13 @@ function RemoteComponent({
|
|
|
1677
1681
|
...metadata,
|
|
1678
1682
|
links,
|
|
1679
1683
|
remoteShared,
|
|
1680
|
-
|
|
1684
|
+
src: typeof src === "string" ? src : src.href,
|
|
1685
|
+
serverUrl: url.href,
|
|
1681
1686
|
data: rsc ? (rsc.textContent || "").split("\n").filter(Boolean) : []
|
|
1682
1687
|
};
|
|
1683
1688
|
componentHydrationHtml.current = `${Array.from(
|
|
1684
1689
|
doc.querySelectorAll("link,style")
|
|
1685
|
-
).map((link) => link.outerHTML).join(
|
|
1686
|
-
""
|
|
1687
|
-
)}${reset ? `<style data-remote-components-reset="">:host { all: initial; }</style>` : ""}${component.innerHTML}`;
|
|
1690
|
+
).map((link) => link.outerHTML).join("")}${reset ? `<style data-remote-components-reset="">:host { all: initial; }</style>` : ""}${component.innerHTML}`;
|
|
1688
1691
|
const userShared = await shared;
|
|
1689
1692
|
if ("__remote_components_missing_shared__" in userShared) {
|
|
1690
1693
|
userShared.__remote_components_missing_shared__().catch((e) => {
|
|
@@ -1735,7 +1738,7 @@ function RemoteComponent({
|
|
|
1735
1738
|
}
|
|
1736
1739
|
} else {
|
|
1737
1740
|
const result = await loadRemoteComponent({
|
|
1738
|
-
url
|
|
1741
|
+
url,
|
|
1739
1742
|
name: remoteName,
|
|
1740
1743
|
rscName,
|
|
1741
1744
|
bundle,
|
|
@@ -1792,7 +1795,8 @@ function RemoteComponent({
|
|
|
1792
1795
|
shared,
|
|
1793
1796
|
shadowRoot,
|
|
1794
1797
|
additionalHeaders,
|
|
1795
|
-
reset
|
|
1798
|
+
reset,
|
|
1799
|
+
id
|
|
1796
1800
|
]);
|
|
1797
1801
|
if (remoteComponent instanceof Error) {
|
|
1798
1802
|
throw remoteComponent;
|
|
@@ -1849,7 +1853,7 @@ function RemoteComponent({
|
|
|
1849
1853
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1850
1854
|
"div",
|
|
1851
1855
|
{
|
|
1852
|
-
"data-remote-component-id": `shadowroot_${
|
|
1856
|
+
"data-remote-component-id": `shadowroot_${keySuffix}`,
|
|
1853
1857
|
id: `shadowroot_${data?.name ?? name}`,
|
|
1854
1858
|
ref: shadowRootContainerRef,
|
|
1855
1859
|
children: [
|
|
@@ -1865,7 +1869,7 @@ function RemoteComponent({
|
|
|
1865
1869
|
src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=="
|
|
1866
1870
|
onload="(function(el){
|
|
1867
1871
|
const root = el.getRootNode();
|
|
1868
|
-
globalThis.__remote_components_shadowroot_${
|
|
1872
|
+
globalThis.__remote_components_shadowroot_${keySuffix} = root;
|
|
1869
1873
|
el.parentElement.remove();
|
|
1870
1874
|
})(this)"
|
|
1871
1875
|
/>`
|