remote-components 0.1.0 → 0.1.2

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.
Files changed (62) hide show
  1. package/dist/{component-loader-76eb1b8b.d.ts → component-loader-21865da3.d.ts} +140 -16
  2. package/dist/host-config-58cdccea.d.ts +87 -0
  3. package/dist/html/host.cjs +294 -162
  4. package/dist/html/host.cjs.map +1 -1
  5. package/dist/html/host.js +294 -162
  6. package/dist/html/host.js.map +1 -1
  7. package/dist/internal/next/host/app-router-client.cjs +9 -7
  8. package/dist/internal/next/host/app-router-client.cjs.map +1 -1
  9. package/dist/internal/next/host/app-router-client.d.ts +32 -19
  10. package/dist/internal/next/host/app-router-client.js +9 -7
  11. package/dist/internal/next/host/app-router-client.js.map +1 -1
  12. package/dist/internal/next/remote/render-server.cjs.map +1 -1
  13. package/dist/internal/next/remote/render-server.d.ts +13 -14
  14. package/dist/internal/next/remote/render-server.js.map +1 -1
  15. package/dist/internal/shared/client/proxy-through-host.cjs +15 -1
  16. package/dist/internal/shared/client/proxy-through-host.cjs.map +1 -1
  17. package/dist/internal/shared/client/proxy-through-host.d.ts +5 -0
  18. package/dist/internal/shared/client/proxy-through-host.js +15 -1
  19. package/dist/internal/shared/client/proxy-through-host.js.map +1 -1
  20. package/dist/internal/shared/client/remote-component.cjs.map +1 -1
  21. package/dist/internal/shared/client/remote-component.d.ts +2 -2
  22. package/dist/internal/shared/client/remote-component.js.map +1 -1
  23. package/dist/internal/shared/contract/host-state.cjs +38 -0
  24. package/dist/internal/shared/contract/host-state.cjs.map +1 -0
  25. package/dist/internal/shared/contract/host-state.d.ts +53 -0
  26. package/dist/internal/shared/contract/host-state.js +14 -0
  27. package/dist/internal/shared/contract/host-state.js.map +1 -0
  28. package/dist/internal/shared/contract/resolve-name-from-src.cjs +40 -0
  29. package/dist/internal/shared/contract/resolve-name-from-src.cjs.map +1 -0
  30. package/dist/internal/shared/contract/resolve-name-from-src.d.ts +13 -0
  31. package/dist/internal/shared/contract/resolve-name-from-src.js +16 -0
  32. package/dist/internal/shared/contract/resolve-name-from-src.js.map +1 -0
  33. package/dist/internal/shared/ssr/dom-flight.d.ts +1 -1
  34. package/dist/internal/shared/ssr/fetch-remote-component.cjs.map +1 -1
  35. package/dist/internal/shared/ssr/fetch-remote-component.d.ts +1 -1
  36. package/dist/internal/shared/ssr/fetch-remote-component.js.map +1 -1
  37. package/dist/internal/shared/ssr/fetch-with-hooks.d.ts +1 -1
  38. package/dist/next/host/app-router-server.cjs.map +1 -1
  39. package/dist/next/host/app-router-server.d.ts +11 -41
  40. package/dist/next/host/app-router-server.js.map +1 -1
  41. package/dist/next/host/client/index.cjs +203 -95
  42. package/dist/next/host/client/index.cjs.map +1 -1
  43. package/dist/next/host/client/index.d.ts +1 -1
  44. package/dist/next/host/client/index.js +203 -95
  45. package/dist/next/host/client/index.js.map +1 -1
  46. package/dist/next/host/pages-router-client.cjs.map +1 -1
  47. package/dist/next/host/pages-router-client.d.ts +13 -36
  48. package/dist/next/host/pages-router-client.js.map +1 -1
  49. package/dist/next/host/pages-router-server.cjs.map +1 -1
  50. package/dist/next/host/pages-router-server.d.ts +17 -42
  51. package/dist/next/host/pages-router-server.js.map +1 -1
  52. package/dist/next/index.cjs.map +1 -1
  53. package/dist/next/index.d.ts +13 -39
  54. package/dist/next/index.js.map +1 -1
  55. package/dist/next/remote/server.d.ts +4 -0
  56. package/dist/react/index.cjs +203 -95
  57. package/dist/react/index.cjs.map +1 -1
  58. package/dist/react/index.d.ts +12 -49
  59. package/dist/react/index.js +203 -95
  60. package/dist/react/index.js.map +1 -1
  61. package/dist/{types-cbe44b51.d.ts → types-2b26a246.d.ts} +23 -6
  62. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { R as ResolveClientUrl } from '../proxy-through-host-a676a522.js';
3
3
  export { p as proxyClientRequestsThroughHost } from '../proxy-through-host-a676a522.js';
4
- import { L as LoadRemoteComponentProps, O as OnRequestHook, a as OnResponseHook } from '../component-loader-76eb1b8b.js';
5
- export { H as HookOptions } from '../component-loader-76eb1b8b.js';
4
+ import { H as HostConfig, C as ClientHostConfig, a as HostLifecycleCallbacks, L as LoadRemoteComponentProps } from '../component-loader-21865da3.js';
5
+ export { b as HookOptions, O as OnRequestHook, c as OnResponseHook } from '../component-loader-21865da3.js';
6
6
  import 'react';
7
7
 
8
8
  /**
@@ -32,55 +32,18 @@ declare module 'react/jsx-runtime' {
32
32
  }
33
33
  }
34
34
  }
35
- interface RemoteComponentProps {
36
- /** The source URL of the remote component. */
37
- src?: string | URL;
38
- /** Whether to isolate the remote component using a Shadow DOM wrapper. */
39
- isolate?: boolean;
40
- /** The shadow DOM mode to use when isolating the remote component. */
41
- mode?: 'open' | 'closed';
42
- /** Whether to include a CSS reset style in the shadow DOM. Defaults to `false`. */
43
- reset?: boolean;
44
- /** The credentials to use for the fetch request. Defaults to `same-origin`. */
45
- credentials?: RequestCredentials;
46
- name?: string;
35
+ /**
36
+ * Props for the React remote component host.
37
+ *
38
+ * Extends {@link HostConfig}, {@link ClientHostConfig}, and
39
+ * {@link HostLifecycleCallbacks}. Adds `shared` for module sharing and
40
+ * `children` for loading fallback content.
41
+ */
42
+ interface RemoteComponentProps extends HostConfig, ClientHostConfig, HostLifecycleCallbacks {
47
43
  /** Shared modules to include in the remote component's context. */
48
44
  shared?: LoadRemoteComponentProps['shared'];
49
- /** The children to use as a loading fallback until the remote component is loaded. */
45
+ /** Loading fallback content displayed while the remote component is being fetched. */
50
46
  children?: React.ReactNode;
51
- /** Called right before a new remote component load starts. */
52
- onBeforeLoad?: (src: string | URL) => void;
53
- /** Called when the remote component has been successfully loaded and mounted. */
54
- onLoad?: (src: string | URL) => void;
55
- /** Called when an error occurs while loading or mounting the remote component. */
56
- onError?: (error: unknown) => void;
57
- /** Called when a different remote component is loaded into the same wrapper. */
58
- onChange?: (info: {
59
- previousSrc: string | URL | null;
60
- nextSrc: string | URL | null;
61
- previousName: string | undefined;
62
- nextName: string | undefined;
63
- }) => void;
64
- /**
65
- * Called when a fetch request is made to retrieve the remote component payload.
66
- * Can be used to intercept requests, modify headers, or provide a custom response.
67
- */
68
- onRequest?: OnRequestHook;
69
- /**
70
- * Called after a fetch completes to retrieve the remote component payload.
71
- * Can be used to inspect the response (e.g., check for redirects) or transform it.
72
- */
73
- onResponse?: OnResponseHook;
74
- /**
75
- * Called before each client-side asset (script, stylesheet, chunk, module, image) is fetched.
76
- * Return a new URL string to redirect the request (e.g., through a proxy),
77
- * or `undefined` to use the original URL.
78
- *
79
- * Pass `proxyClientRequestsThroughHost` to proxy cross-origin assets through
80
- * the host on Vercel preview deployments. Requires `withRemoteComponentsHost`
81
- * middleware on the host to handle the proxied requests.
82
- */
83
- resolveClientUrl?: ResolveClientUrl;
84
47
  }
85
48
  /**
86
49
  * RemoteComponent is a React component that fetches and renders a remote component.
@@ -120,4 +83,4 @@ interface RemoteComponentProps {
120
83
  */
121
84
  declare function RemoteComponent({ src, isolate, mode, reset, credentials, name: nameProp, shared, children, onBeforeLoad, onLoad, onError, onChange, onRequest, onResponse, resolveClientUrl: _resolveClientUrl, }: RemoteComponentProps): react_jsx_runtime.JSX.Element;
122
85
 
123
- export { OnRequestHook, OnResponseHook, RemoteComponent, RemoteComponentProps, RemoteComponentsProvider, ResolveClientUrl };
86
+ export { RemoteComponent, RemoteComponentProps, RemoteComponentsProvider, ResolveClientUrl };
@@ -183,6 +183,122 @@ Docs: ${CORS_DOCS_URL}`
183
183
  );
184
184
  }
185
185
 
186
+ // src/shared/utils/index.ts
187
+ function escapeString(str) {
188
+ return str.replace(/[^a-z0-9]/g, "_");
189
+ }
190
+ var attrToProp = {
191
+ fetchpriority: "fetchPriority",
192
+ crossorigin: "crossOrigin",
193
+ imagesrcset: "imageSrcSet",
194
+ imagesizes: "imageSizes",
195
+ srcset: "srcSet"
196
+ };
197
+
198
+ // src/shared/client/const.ts
199
+ var DEFAULT_ROUTE = "/";
200
+ var RUNTIME_WEBPACK = "webpack";
201
+ var RUNTIME_TURBOPACK = "turbopack";
202
+ var RUNTIME_SCRIPT = "script";
203
+ var REMOTE_COMPONENT_REGEX = /(?<prefix>.*?)\[(?<bundle>[^\]]+)\](?:%20| )(?<id>.+)/;
204
+ function getBundleKey(bundle) {
205
+ return escapeString(bundle);
206
+ }
207
+
208
+ // src/shared/client/parse-remote-html.ts
209
+ function validateSingleComponent(doc, name, url) {
210
+ if (doc.querySelectorAll("div[data-bundle][data-route]").length > 1 && !doc.querySelector(`div[data-bundle][data-route][id^="${name}"]`) || doc.querySelectorAll("remote-component:not([src])").length > 1 && !doc.querySelector(`remote-component[name="${name}"]`)) {
211
+ throw multipleRemoteComponentsError(url);
212
+ }
213
+ }
214
+ function findComponentElement(doc, name) {
215
+ return doc.querySelector(`div[data-bundle][data-route][id^="${name}"]`) ?? doc.querySelector("div[data-bundle][data-route]") ?? doc.querySelector("div#__next") ?? doc.querySelector(`remote-component[name="${name}"]:not([src])`) ?? doc.querySelector("remote-component:not([src])");
216
+ }
217
+ function parseNextData(doc) {
218
+ return JSON.parse(
219
+ (doc.querySelector("#__NEXT_DATA__") ?? doc.querySelector("#__REMOTE_NEXT_DATA__"))?.textContent ?? "null"
220
+ );
221
+ }
222
+ function resolveComponentName(component, nextData, fallbackName) {
223
+ const isRemoteComponent = component?.tagName.toLowerCase() === "remote-component";
224
+ const name = component?.getAttribute("id")?.replace(/_ssr$/, "") || isRemoteComponent && component?.getAttribute("name") || (nextData ? "__next" : fallbackName);
225
+ return { name, isRemoteComponent };
226
+ }
227
+ function extractComponentMetadata(component, nextData, name, url) {
228
+ return {
229
+ name,
230
+ bundle: component?.getAttribute("data-bundle") || nextData?.props.__REMOTE_COMPONENT__?.bundle || "default",
231
+ route: component?.getAttribute("data-route") ?? nextData?.page ?? (url.pathname || DEFAULT_ROUTE),
232
+ runtime: component?.getAttribute("data-runtime") ?? (nextData?.props.__REMOTE_COMPONENT__?.runtime || RUNTIME_SCRIPT)
233
+ };
234
+ }
235
+ function extractRemoteShared(doc, name, nextData) {
236
+ const remoteSharedEl = doc.querySelector(
237
+ `#${name}_shared[data-remote-components-shared]`
238
+ );
239
+ const remoteShared = nextData?.props.__REMOTE_COMPONENT__?.shared ?? (JSON.parse(remoteSharedEl?.textContent ?? "{}") ?? {});
240
+ remoteSharedEl?.remove();
241
+ return remoteShared;
242
+ }
243
+ function validateComponentFound(component, rsc, nextData, isRemoteComponent, url, name) {
244
+ if (!component || !(rsc || nextData || isRemoteComponent)) {
245
+ throw new RemoteComponentsError(
246
+ `Remote Component not found on ${url}.${name !== "__vercel_remote_component" ? ` The name for the <RemoteComponent> is "${name}". Check <RemoteComponent> usage.` : ""} Did you forget to wrap the content in <RemoteComponent>?`
247
+ );
248
+ }
249
+ }
250
+ function extractLinks(doc, component) {
251
+ return Array.from(doc.querySelectorAll("link[href]")).filter(
252
+ (link) => !component.contains(link)
253
+ );
254
+ }
255
+ function extractScripts(doc, component, isRemoteComponent) {
256
+ return Array.from(
257
+ (isRemoteComponent ? component : doc).querySelectorAll(
258
+ "script[src],script[data-src]"
259
+ )
260
+ );
261
+ }
262
+ function parseRemoteComponentDocument(doc, name, url) {
263
+ validateSingleComponent(doc, name, url.href);
264
+ const component = findComponentElement(doc, name);
265
+ const nextData = parseNextData(doc);
266
+ const { name: resolvedName, isRemoteComponent } = resolveComponentName(
267
+ component,
268
+ nextData,
269
+ name
270
+ );
271
+ const rsc = doc.querySelector(`#${resolvedName}_rsc`);
272
+ const metadata = extractComponentMetadata(
273
+ component,
274
+ nextData,
275
+ resolvedName,
276
+ url
277
+ );
278
+ const remoteShared = extractRemoteShared(doc, resolvedName, nextData);
279
+ validateComponentFound(
280
+ component,
281
+ rsc,
282
+ nextData,
283
+ isRemoteComponent,
284
+ url.href,
285
+ resolvedName
286
+ );
287
+ const links = extractLinks(doc, component);
288
+ const scripts = extractScripts(doc, component, isRemoteComponent);
289
+ return {
290
+ component,
291
+ name: resolvedName,
292
+ isRemoteComponent,
293
+ metadata,
294
+ nextData,
295
+ rsc,
296
+ remoteShared,
297
+ links,
298
+ scripts
299
+ };
300
+ }
301
+
186
302
  // src/shared/utils/logger.ts
187
303
  var PREFIX = "remote-components";
188
304
  var DEBUG = typeof window !== "undefined" && localStorage.getItem("RC_DEBUG") === "true";
@@ -698,28 +814,6 @@ async function loadScripts(scripts, resolveClientUrl) {
698
814
  );
699
815
  }
700
816
 
701
- // src/shared/utils/index.ts
702
- function escapeString(str) {
703
- return str.replace(/[^a-z0-9]/g, "_");
704
- }
705
- var attrToProp = {
706
- fetchpriority: "fetchPriority",
707
- crossorigin: "crossOrigin",
708
- imagesrcset: "imageSrcSet",
709
- imagesizes: "imageSizes",
710
- srcset: "srcSet"
711
- };
712
-
713
- // src/shared/client/const.ts
714
- var DEFAULT_ROUTE = "/";
715
- var RUNTIME_WEBPACK = "webpack";
716
- var RUNTIME_TURBOPACK = "turbopack";
717
- var RUNTIME_SCRIPT = "script";
718
- var REMOTE_COMPONENT_REGEX = /(?<prefix>.*?)\[(?<bundle>[^\]]+)\](?:%20| )(?<id>.+)/;
719
- function getBundleKey(bundle) {
720
- return escapeString(bundle);
721
- }
722
-
723
817
  // src/shared/client/turbopack-patterns.ts
724
818
  var REMOTE_SHARED_MARKER_RE = /(?:self|[a-z])\.TURBOPACK_REMOTE_SHARED/;
725
819
  var REMOTE_SHARED_ASSIGNMENT_RE = /\.TURBOPACK_REMOTE_SHARED=await (?:__turbopack_context__|e)\.A\((?<sharedModuleId>[0-9]+)\)/;
@@ -1520,7 +1614,21 @@ function loadNextPagesComponent(bundle, route, nextData, name, container) {
1520
1614
 
1521
1615
  // src/shared/client/proxy-through-host.ts
1522
1616
  function withRemoteSrc(resolveClientUrl, remoteSrc) {
1523
- return (url) => resolveClientUrl(remoteSrc, url);
1617
+ const remoteOrigin = parseOrigin(remoteSrc);
1618
+ return (url) => {
1619
+ const urlOrigin = parseOrigin(url);
1620
+ if (remoteOrigin && urlOrigin && urlOrigin !== remoteOrigin) {
1621
+ return void 0;
1622
+ }
1623
+ return resolveClientUrl(remoteSrc, url);
1624
+ };
1625
+ }
1626
+ function parseOrigin(url) {
1627
+ try {
1628
+ return new URL(url).origin;
1629
+ } catch {
1630
+ return void 0;
1631
+ }
1524
1632
  }
1525
1633
  var proxyClientRequestsThroughHost = (remoteSrc, url) => {
1526
1634
  if (typeof location === "undefined") {
@@ -1732,6 +1840,32 @@ async function loadStaticRemoteComponent(scripts, url, resolveClientUrl) {
1732
1840
  );
1733
1841
  }
1734
1842
 
1843
+ // src/shared/contract/host-state.ts
1844
+ function createHostState() {
1845
+ return {
1846
+ stage: "idle",
1847
+ prevSrc: void 0,
1848
+ prevUrl: void 0,
1849
+ prevName: void 0,
1850
+ prevIsRemoteComponent: false,
1851
+ abortController: void 0
1852
+ };
1853
+ }
1854
+
1855
+ // src/shared/contract/resolve-name-from-src.ts
1856
+ function resolveNameFromSrc(src, defaultName) {
1857
+ if (!src) {
1858
+ return defaultName;
1859
+ }
1860
+ const hash = typeof src === "string" ? src : src.hash;
1861
+ const hashIndex = hash.indexOf("#");
1862
+ if (hashIndex < 0) {
1863
+ return defaultName;
1864
+ }
1865
+ const name = hash.slice(hashIndex + 1);
1866
+ return name || defaultName;
1867
+ }
1868
+
1735
1869
  // src/shared/ssr/fetch-headers.ts
1736
1870
  function remoteFetchHeaders() {
1737
1871
  return {
@@ -1859,7 +1993,7 @@ function useShadowRoot({
1859
1993
  return { shadowRoot, shadowRootContainerRef };
1860
1994
  }
1861
1995
 
1862
- // src/react/utils/parse-remote-html.ts
1996
+ // src/react/utils/extract-remote-html.ts
1863
1997
  var DUMMY_FALLBACK = "http://remote-components-dummy-fallback";
1864
1998
  function getRemoteComponentHtml(html) {
1865
1999
  if (typeof document === "undefined")
@@ -1903,20 +2037,10 @@ function RemoteComponent({
1903
2037
  resolveClientUrl: _resolveClientUrl
1904
2038
  }) {
1905
2039
  const instanceId = useId();
1906
- const name = useMemo2(() => {
1907
- if (typeof src === "string") {
1908
- const url2 = new URL(
1909
- src,
1910
- typeof document !== "undefined" ? location.href : DUMMY_FALLBACK
1911
- );
1912
- if (url2.hash) {
1913
- return url2.hash.slice(1);
1914
- }
1915
- } else if (typeof src === "object" && "hash" in src && src.hash) {
1916
- return src.hash.slice(1) || nameProp;
1917
- }
1918
- return nameProp;
1919
- }, [src, nameProp]);
2040
+ const name = useMemo2(
2041
+ () => resolveNameFromSrc(src, nameProp),
2042
+ [src, nameProp]
2043
+ );
1920
2044
  const [data, setData] = useState2(null);
1921
2045
  const url = useMemo2(() => getClientOrServerUrl(src, DUMMY_FALLBACK), [src]);
1922
2046
  const resolveClientUrl = useResolveClientUrl(_resolveClientUrl, url.href);
@@ -1949,13 +2073,10 @@ function RemoteComponent({
1949
2073
  return elements;
1950
2074
  })() : []
1951
2075
  );
1952
- const prevSrcRef = useRef2(null);
2076
+ const hostStateRef = useRef2(createHostState());
1953
2077
  const componentHydrationHtml = useRef2(null);
1954
- const prevIsRemoteComponentRef = useRef2(false);
1955
- const prevUrlRef = useRef2(null);
1956
2078
  const prevRemoteComponentContainerRef = useRef2(null);
1957
2079
  const unmountRef = useRef2(null);
1958
- const prevNameRef = useRef2(void 0);
1959
2080
  useLayoutEffect2(() => {
1960
2081
  const shadowRootKey = `__remote_components_shadowroot_${keySuffix}`;
1961
2082
  return () => {
@@ -1994,14 +2115,18 @@ function RemoteComponent({
1994
2115
  }
1995
2116
  }, [shadowRoot, remoteComponent, name]);
1996
2117
  useEffect(() => {
1997
- if (src && src !== prevSrcRef.current) {
1998
- const previousSrc = prevSrcRef.current;
1999
- const previousName = prevNameRef.current;
2000
- prevSrcRef.current = src;
2118
+ if (src && src !== hostStateRef.current.prevSrc) {
2119
+ const previousSrc = hostStateRef.current.prevSrc;
2120
+ const previousName = hostStateRef.current.prevName;
2121
+ hostStateRef.current.prevSrc = src;
2001
2122
  if (previousSrc !== null) {
2002
2123
  htmlRef.current = null;
2003
2124
  }
2125
+ hostStateRef.current.abortController?.abort();
2126
+ hostStateRef.current.abortController = new AbortController();
2127
+ const { signal } = hostStateRef.current.abortController;
2004
2128
  onBeforeLoad?.(src);
2129
+ hostStateRef.current.stage = "loading";
2005
2130
  startTransition(async () => {
2006
2131
  try {
2007
2132
  let html = getRemoteComponentHtml(
@@ -2015,59 +2140,41 @@ function RemoteComponent({
2015
2140
  resolveClientUrl?.(url.href) ?? url.href,
2016
2141
  location.href
2017
2142
  );
2018
- const abortController = new AbortController();
2019
2143
  const res = await fetchWithHooks(resolvedUrl, fetchInit, {
2020
2144
  onRequest,
2021
2145
  onResponse,
2022
- abortController
2146
+ abortController: hostStateRef.current.abortController
2023
2147
  });
2024
2148
  if (!res || !res.ok) {
2025
2149
  throw await errorFromFailedFetch(url.href, resolvedUrl, res);
2026
2150
  }
2027
2151
  const remoteHtml = await res.text();
2152
+ if (signal.aborted)
2153
+ return;
2028
2154
  htmlRef.current = remoteHtml;
2029
2155
  html = getRemoteComponentHtml(remoteHtml);
2030
2156
  }
2157
+ if (signal.aborted)
2158
+ return;
2031
2159
  const parser = new DOMParser();
2032
2160
  const doc = parser.parseFromString(html, "text/html");
2033
- if (doc.querySelectorAll("div[data-bundle][data-route]").length > 1 && !doc.querySelector(
2034
- `div[data-bundle][data-route][id^="${name}"]`
2035
- ) || doc.querySelectorAll("remote-component:not([src])").length > 1 && !doc.querySelector(`remote-component[name="${name}"]`)) {
2036
- throw multipleRemoteComponentsError(url.href);
2037
- }
2038
- 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
2039
- doc.querySelector("div[data-bundle][data-route]") ?? // fallback to Next.js Pages Router
2040
- doc.querySelector("div#__next") ?? // fallback to the remote-component web component
2041
- doc.querySelector(`remote-component[name="${name}"]:not([src])`) ?? doc.querySelector("remote-component:not([src])");
2042
- const nextData = JSON.parse(
2043
- (doc.querySelector("#__NEXT_DATA__") ?? doc.querySelector("#__REMOTE_NEXT_DATA__"))?.textContent ?? "null"
2044
- );
2045
- const remoteName = component?.getAttribute("id")?.replace(/_ssr$/, "") || (nextData ? "__next" : name);
2046
- const rsc = doc.querySelector(`#${remoteName}_rsc`);
2047
- const bundle = component?.getAttribute("data-bundle") || nextData?.props.__REMOTE_COMPONENT__?.bundle || "default";
2048
- const isRemoteComponent = component?.tagName.toLowerCase() === "remote-component";
2049
- const metadata = {
2161
+ const {
2162
+ component,
2050
2163
  name: remoteName,
2051
- bundle,
2052
- route: component?.getAttribute("data-route") ?? nextData?.page ?? (url.pathname || DEFAULT_ROUTE),
2053
- runtime: component?.getAttribute("data-runtime") ?? (nextData?.props.__REMOTE_COMPONENT__?.runtime || RUNTIME_SCRIPT)
2054
- };
2055
- const remoteSharedEl = doc.querySelector(
2056
- `#${remoteName}_shared[data-remote-components-shared]`
2057
- );
2058
- const remoteShared = nextData?.props.__REMOTE_COMPONENT__?.shared ?? (JSON.parse(remoteSharedEl?.textContent ?? "{}") ?? {});
2059
- remoteSharedEl?.remove();
2060
- if (!component || !(rsc || nextData || isRemoteComponent)) {
2061
- throw new RemoteComponentsError(
2062
- `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>?`
2063
- );
2064
- }
2065
- if (prevIsRemoteComponentRef.current) {
2164
+ isRemoteComponent,
2165
+ metadata,
2166
+ nextData,
2167
+ rsc,
2168
+ remoteShared,
2169
+ links: linkElements,
2170
+ scripts: scriptElements
2171
+ } = parseRemoteComponentDocument(doc, name, url);
2172
+ if (hostStateRef.current.prevIsRemoteComponent) {
2066
2173
  if (shadowRoot) {
2067
2174
  shadowRoot.innerHTML = "";
2068
2175
  }
2069
2176
  const self = globalThis;
2070
- const prevUrl = prevUrlRef.current;
2177
+ const prevUrl = hostStateRef.current.prevUrl;
2071
2178
  if (prevUrl && self.__remote_script_entrypoint_unmount__?.[prevUrl.href]) {
2072
2179
  const unmountPromises = Promise.all(
2073
2180
  Array.from(unmountRef.current ?? []).map(
@@ -2080,15 +2187,11 @@ function RemoteComponent({
2080
2187
  await unmountPromises;
2081
2188
  }
2082
2189
  }
2083
- prevIsRemoteComponentRef.current = isRemoteComponent;
2084
- prevUrlRef.current = url;
2085
- prevNameRef.current = remoteName;
2190
+ hostStateRef.current.prevIsRemoteComponent = isRemoteComponent;
2191
+ hostStateRef.current.prevUrl = url;
2192
+ hostStateRef.current.prevName = remoteName;
2086
2193
  applyOriginToNodes(doc, url, resolveClientUrl);
2087
- const links = Array.from(
2088
- doc.querySelectorAll("link[href]")
2089
- ).filter((link) => {
2090
- return !component.contains(link);
2091
- }).map((link) => ({
2194
+ const links = linkElements.map((link) => ({
2092
2195
  href: new URL(link.getAttribute("href") ?? link.href, url).href,
2093
2196
  ...link.getAttributeNames().reduce((acc, key) => {
2094
2197
  if (key !== "href") {
@@ -2097,7 +2200,7 @@ function RemoteComponent({
2097
2200
  return acc;
2098
2201
  }, {})
2099
2202
  }));
2100
- const scripts = (isRemoteComponent ? component : doc).querySelectorAll("script[src],script[data-src]");
2203
+ const scripts = scriptElements;
2101
2204
  const inlineScripts = (isRemoteComponent ? component : doc).querySelectorAll(
2102
2205
  "script:not([src]):not([data-src]):not([id*='_rsc']):not([id='__NEXT_DATA__']):not([id='__REMOTE_NEXT_DATA__'])"
2103
2206
  );
@@ -2188,7 +2291,7 @@ function RemoteComponent({
2188
2291
  );
2189
2292
  }
2190
2293
  if (isRemoteComponent) {
2191
- if (previousSrc !== null) {
2294
+ if (previousSrc !== void 0) {
2192
2295
  onChange?.({
2193
2296
  previousSrc,
2194
2297
  nextSrc: src,
@@ -2240,12 +2343,13 @@ function RemoteComponent({
2240
2343
  );
2241
2344
  onLoad?.(src);
2242
2345
  }
2346
+ hostStateRef.current.stage = "loaded";
2243
2347
  } else {
2244
2348
  const result = await loadRemoteComponent({
2245
2349
  url,
2246
2350
  name: remoteName,
2247
2351
  rscName,
2248
- bundle,
2352
+ bundle: metadata.bundle,
2249
2353
  route: metadata.route,
2250
2354
  runtime: metadata.runtime,
2251
2355
  data: newData.data,
@@ -2280,7 +2384,7 @@ function RemoteComponent({
2280
2384
  rsc.remove();
2281
2385
  }
2282
2386
  setData(newData);
2283
- if (previousSrc !== null) {
2387
+ if (previousSrc !== void 0) {
2284
2388
  onChange?.({
2285
2389
  previousSrc,
2286
2390
  nextSrc: src,
@@ -2289,17 +2393,21 @@ function RemoteComponent({
2289
2393
  });
2290
2394
  }
2291
2395
  if (result.error) {
2396
+ hostStateRef.current.stage = "error";
2292
2397
  setRemoteComponent(result.error);
2293
2398
  onError?.(result.error);
2294
2399
  } else {
2400
+ hostStateRef.current.stage = "loaded";
2295
2401
  setRemoteComponent(result.component);
2296
2402
  onLoad?.(src);
2297
2403
  }
2298
2404
  }
2299
2405
  } catch (error) {
2300
2406
  if (isAbortError(error)) {
2407
+ hostStateRef.current.stage = "idle";
2301
2408
  return;
2302
2409
  }
2410
+ hostStateRef.current.stage = "error";
2303
2411
  setRemoteComponent(error);
2304
2412
  onError?.(error);
2305
2413
  }
@@ -2330,7 +2438,7 @@ function RemoteComponent({
2330
2438
  name: data?.name || name,
2331
2439
  bundle: data?.bundle || "default",
2332
2440
  route: data?.route || DEFAULT_ROUTE,
2333
- runtime: prevIsRemoteComponentRef.current ? RUNTIME_SCRIPT : data?.runtime || RUNTIME_WEBPACK
2441
+ runtime: hostStateRef.current.prevIsRemoteComponent ? RUNTIME_SCRIPT : data?.runtime || RUNTIME_WEBPACK
2334
2442
  }) });
2335
2443
  const resetStyle = reset ? /* @__PURE__ */ jsx2("style", { "data-remote-components-reset": "react", children: `:host { all: initial; }` }) : null;
2336
2444
  const linksToRender = data?.links?.map((link) => /* @__PURE__ */ createElement2(
@@ -2349,7 +2457,7 @@ function RemoteComponent({
2349
2457
  if (componentHydrationHtml.current && shadowRoot && !shadowRoot.innerHTML) {
2350
2458
  shadowRoot.innerHTML = componentHydrationHtml.current;
2351
2459
  componentHydrationHtml.current = null;
2352
- if (prevIsRemoteComponentRef.current) {
2460
+ if (hostStateRef.current.prevIsRemoteComponent) {
2353
2461
  loadStaticRemoteComponent(
2354
2462
  Array.from(shadowRoot.querySelectorAll("script")),
2355
2463
  url,