remote-components 0.0.23 → 0.0.25

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 (74) hide show
  1. package/dist/html/host.cjs +329 -92
  2. package/dist/html/host.cjs.map +1 -1
  3. package/dist/html/host.js +332 -92
  4. package/dist/html/host.js.map +1 -1
  5. package/dist/internal/next/host/app-router-client.cjs +180 -71
  6. package/dist/internal/next/host/app-router-client.cjs.map +1 -1
  7. package/dist/internal/next/host/app-router-client.js +188 -72
  8. package/dist/internal/next/host/app-router-client.js.map +1 -1
  9. package/dist/internal/next/host/app-router-compat.cjs +114 -0
  10. package/dist/internal/next/host/app-router-compat.cjs.map +1 -0
  11. package/dist/internal/next/host/app-router-compat.d.ts +31 -0
  12. package/dist/internal/next/host/app-router-compat.js +79 -0
  13. package/dist/internal/next/host/app-router-compat.js.map +1 -0
  14. package/dist/internal/next/remote/render-client.cjs +10 -2
  15. package/dist/internal/next/remote/render-client.cjs.map +1 -1
  16. package/dist/internal/next/remote/render-client.js +10 -2
  17. package/dist/internal/next/remote/render-client.js.map +1 -1
  18. package/dist/internal/shared/client/apply-origin.cjs +61 -0
  19. package/dist/internal/shared/client/apply-origin.cjs.map +1 -0
  20. package/dist/internal/shared/client/apply-origin.d.ts +3 -0
  21. package/dist/internal/shared/client/apply-origin.js +37 -0
  22. package/dist/internal/shared/client/apply-origin.js.map +1 -0
  23. package/dist/internal/shared/client/polyfill.cjs +149 -0
  24. package/dist/internal/shared/client/polyfill.cjs.map +1 -0
  25. package/dist/internal/shared/client/polyfill.d.ts +6 -0
  26. package/dist/internal/shared/client/polyfill.js +124 -0
  27. package/dist/internal/shared/client/polyfill.js.map +1 -0
  28. package/dist/internal/shared/client/remote-component.cjs +20 -13
  29. package/dist/internal/shared/client/remote-component.cjs.map +1 -1
  30. package/dist/internal/shared/client/remote-component.d.ts +4 -3
  31. package/dist/internal/shared/client/remote-component.js +20 -13
  32. package/dist/internal/shared/client/remote-component.js.map +1 -1
  33. package/dist/internal/shared/ssr/dom-flight.cjs +40 -0
  34. package/dist/internal/shared/ssr/dom-flight.cjs.map +1 -1
  35. package/dist/internal/shared/ssr/dom-flight.js +40 -0
  36. package/dist/internal/shared/ssr/dom-flight.js.map +1 -1
  37. package/dist/internal/shared/ssr/fetch-remote-component.cjs +1 -1
  38. package/dist/internal/shared/ssr/fetch-remote-component.cjs.map +1 -1
  39. package/dist/internal/shared/ssr/fetch-remote-component.d.ts +6 -0
  40. package/dist/internal/shared/ssr/fetch-remote-component.js +1 -1
  41. package/dist/internal/shared/ssr/fetch-remote-component.js.map +1 -1
  42. package/dist/internal/shared/utils.cjs +31 -0
  43. package/dist/internal/shared/utils.cjs.map +1 -0
  44. package/dist/internal/shared/utils.d.ts +3 -0
  45. package/dist/internal/shared/utils.js +7 -0
  46. package/dist/internal/shared/utils.js.map +1 -0
  47. package/dist/next/config.cjs +50 -28
  48. package/dist/next/config.cjs.map +1 -1
  49. package/dist/next/config.js +50 -28
  50. package/dist/next/config.js.map +1 -1
  51. package/dist/next/host/client/index.cjs +16 -1
  52. package/dist/next/host/client/index.cjs.map +1 -1
  53. package/dist/next/host/client/index.js +16 -1
  54. package/dist/next/host/client/index.js.map +1 -1
  55. package/dist/next/host/pages-router-server.cjs +27 -13
  56. package/dist/next/host/pages-router-server.cjs.map +1 -1
  57. package/dist/next/host/pages-router-server.js +27 -13
  58. package/dist/next/host/pages-router-server.js.map +1 -1
  59. package/dist/next/middleware.cjs +5 -2
  60. package/dist/next/middleware.cjs.map +1 -1
  61. package/dist/next/middleware.d.ts +1 -0
  62. package/dist/next/middleware.js +5 -2
  63. package/dist/next/middleware.js.map +1 -1
  64. package/dist/next/remote/pages-router.cjs +3 -1
  65. package/dist/next/remote/pages-router.cjs.map +1 -1
  66. package/dist/next/remote/pages-router.d.ts +1 -0
  67. package/dist/next/remote/pages-router.js +3 -1
  68. package/dist/next/remote/pages-router.js.map +1 -1
  69. package/dist/react/index.cjs +213 -161
  70. package/dist/react/index.cjs.map +1 -1
  71. package/dist/react/index.d.ts +2 -1
  72. package/dist/react/index.js +202 -150
  73. package/dist/react/index.js.map +1 -1
  74. package/package.json +1 -1
package/dist/html/host.js CHANGED
@@ -157,14 +157,25 @@ var init_next_client_pages_loader = __esm({
157
157
  }
158
158
  });
159
159
 
160
+ // src/shared/utils/index.ts
161
+ function escapeString(str) {
162
+ return str.replace(/[^a-z0-9]/g, "_");
163
+ }
164
+ var init_utils = __esm({
165
+ "src/shared/utils/index.ts"() {
166
+ "use strict";
167
+ }
168
+ });
169
+
160
170
  // src/shared/client/const.ts
161
171
  function getBundleKey(bundle) {
162
- return bundle.replace(/-/g, "_");
172
+ return escapeString(bundle);
163
173
  }
164
174
  var RUNTIME_WEBPACK, RUNTIME_TURBOPACK, REMOTE_COMPONENT_REGEX;
165
175
  var init_const = __esm({
166
176
  "src/shared/client/const.ts"() {
167
177
  "use strict";
178
+ init_utils();
168
179
  RUNTIME_WEBPACK = "webpack";
169
180
  RUNTIME_TURBOPACK = "turbopack";
170
181
  REMOTE_COMPONENT_REGEX = /(?<prefix>.*?)\[(?<bundle>[^\]]+)\](?:%20| )(?<id>.+)/;
@@ -373,7 +384,7 @@ function initializeSharedModules(bundle, shared = {}, remoteShared = {}) {
373
384
  Object.entries(remoteShared).map(async ([id, module]) => {
374
385
  if (self.__remote_shared_modules__?.[bundle]) {
375
386
  if (shared[module]) {
376
- self.__remote_shared_modules__[bundle][id.replace("[app-ssr]", "[app-client]")] = await shared[module]();
387
+ self.__remote_shared_modules__[bundle][id.replace("[app-ssr]", "[app-client]")] = await shared[module](bundle);
377
388
  } else {
378
389
  console.error(`Shared module "${module}" not found for "${bundle}".`);
379
390
  }
@@ -386,7 +397,7 @@ function getSharedModule(bundle, id) {
386
397
  for (const [key, value] of Object.entries(
387
398
  self.__remote_shared_modules__?.[bundle] ?? {}
388
399
  )) {
389
- if (typeof id === "string" && id.includes(key) || id === key) {
400
+ if (typeof value !== "undefined" && (typeof id === "string" && id.includes(key) || id === key)) {
390
401
  return value;
391
402
  }
392
403
  }
@@ -520,6 +531,24 @@ var init_webpack_adapter = __esm({
520
531
 
521
532
  // src/shared/client/polyfill.tsx
522
533
  import { jsx } from "react/jsx-runtime";
534
+ function applyBundleUrlToSrc(bundle, src) {
535
+ const self = globalThis;
536
+ if (self.__remote_bundle_url__?.[bundle]?.origin === location.origin) {
537
+ return src;
538
+ }
539
+ const { assetPrefix, path } = /^(?<assetPrefix>.*?)\/_next\/(?<path>.*)/.exec(src)?.groups ?? {};
540
+ if (!path) {
541
+ return new URL(src, self.__remote_bundle_url__?.[bundle]?.origin).href;
542
+ }
543
+ return `${self.__remote_bundle_url__?.[bundle]?.origin ?? ""}${assetPrefix}/_next/${path}`;
544
+ }
545
+ function applyBundleUrlToImagePropsSrc(bundle, src) {
546
+ if (typeof src === "string") {
547
+ return applyBundleUrlToSrc(bundle, src);
548
+ }
549
+ const propSrc = src;
550
+ return applyBundleUrlToSrc(bundle, propSrc.src);
551
+ }
523
552
  function sharedPolyfills(shared) {
524
553
  const self = globalThis;
525
554
  const polyfill = {
@@ -555,18 +584,17 @@ function sharedPolyfills(shared) {
555
584
  },
556
585
  __esModule: true
557
586
  })),
558
- "next/dist/client/image-component": self.__remote_component_host_shared_modules__?.["next/image"] ?? shared?.["next/image"] ?? (() => Promise.resolve({
559
- Image: (props) => (
560
- // eslint-disable-next-line @next/next/no-img-element, jsx-a11y/alt-text
561
- /* @__PURE__ */ jsx(
562
- "img",
563
- {
564
- ...props,
565
- src: props.src,
566
- suppressHydrationWarning: true
567
- }
568
- )
569
- ),
587
+ "next/dist/client/image-component": self.__remote_component_host_shared_modules__?.["next/image"] ?? shared?.["next/image"] ?? ((bundle) => Promise.resolve({
588
+ Image: imageImpl(bundle),
589
+ __esModule: true
590
+ })),
591
+ "next/image": self.__remote_component_host_shared_modules__?.["next/image"] ?? shared?.["next/image"] ?? ((bundle) => Promise.resolve({
592
+ default: imageImpl(bundle),
593
+ getImageProps: (_imgProps) => {
594
+ throw new Error(
595
+ "Next.js getImageProps() is not implemented in remote components"
596
+ );
597
+ },
570
598
  __esModule: true
571
599
  })),
572
600
  "next/dist/client/script": self.__remote_component_host_shared_modules__?.["next/script"] ?? shared?.["next/script"] ?? (() => Promise.resolve({
@@ -574,19 +602,57 @@ function sharedPolyfills(shared) {
574
602
  // do not throw an error for now
575
603
  default: () => null,
576
604
  __esModule: true
577
- }))
605
+ })),
606
+ "next/router": self.__remote_component_host_shared_modules__?.["next/router"] ?? shared?.["next/router"] ?? (() => (
607
+ // TODO: incomplete implementation
608
+ Promise.resolve({
609
+ useRouter() {
610
+ return {
611
+ push: (routerUrl) => {
612
+ history.pushState({}, "", routerUrl);
613
+ },
614
+ replace: (routerUrl) => {
615
+ history.replaceState({}, "", routerUrl);
616
+ },
617
+ back: () => {
618
+ history.back();
619
+ }
620
+ };
621
+ },
622
+ __esModule: true
623
+ })
624
+ ))
578
625
  };
579
626
  polyfill["next/navigation"] = polyfill["next/dist/client/components/navigation"];
580
627
  polyfill["next/link"] = polyfill["next/dist/client/app-dir/link"];
581
628
  polyfill["next/form"] = polyfill["next/dist/client/app-dir/form"];
582
- polyfill["next/image"] = polyfill["next/dist/client/image-component"];
583
629
  polyfill["next/dist/api/image"] = polyfill["next/dist/client/image-component"];
584
630
  polyfill["next/script"] = polyfill["next/dist/client/script"];
585
631
  return polyfill;
586
632
  }
633
+ var imageImpl;
587
634
  var init_polyfill = __esm({
588
635
  "src/shared/client/polyfill.tsx"() {
589
636
  "use strict";
637
+ imageImpl = (bundle) => function RemoteImage({ priority: _, ...props }) {
638
+ const newSrc = applyBundleUrlToImagePropsSrc(
639
+ bundle,
640
+ typeof props.src === "string" ? props.src : props.src.src
641
+ );
642
+ return (
643
+ // eslint-disable-next-line @next/next/no-img-element, jsx-a11y/alt-text
644
+ /* @__PURE__ */ jsx(
645
+ "img",
646
+ {
647
+ decoding: "async",
648
+ style: { color: "transparent" },
649
+ ...props,
650
+ src: newSrc,
651
+ suppressHydrationWarning: true
652
+ }
653
+ )
654
+ );
655
+ };
590
656
  }
591
657
  });
592
658
 
@@ -629,6 +695,7 @@ async function webpackRuntime(bundle, shared, remoteShared) {
629
695
  const newScript = document.createElement("script");
630
696
  newScript.onload = () => {
631
697
  resolve2();
698
+ newScript.remove();
632
699
  };
633
700
  newScript.onerror = () => {
634
701
  reject(
@@ -636,6 +703,7 @@ async function webpackRuntime(bundle, shared, remoteShared) {
636
703
  `Failed to load script ${script.src} for remote component`
637
704
  )
638
705
  );
706
+ newScript.remove();
639
707
  };
640
708
  const scriptSrc = script.getAttribute("src") || script.getAttribute("data-src");
641
709
  if (scriptSrc) {
@@ -674,7 +742,7 @@ async function webpackRuntime(bundle, shared, remoteShared) {
674
742
  await Promise.all(
675
743
  Object.entries(resolve).map(async ([key, value]) => {
676
744
  if (typeof value === "function") {
677
- resolve[key] = await value();
745
+ resolve[key] = await value(remoteBundle);
678
746
  }
679
747
  return Promise.resolve(value);
680
748
  })
@@ -753,7 +821,7 @@ var init_turbopack = __esm({
753
821
  });
754
822
 
755
823
  // src/html/host/index.tsx
756
- import { startTransition } from "react";
824
+ import { startTransition, useLayoutEffect } from "react";
757
825
  import { hydrateRoot } from "react-dom/client";
758
826
 
759
827
  // src/shared/client/component-loader.ts
@@ -786,26 +854,27 @@ function fixPayload(payload) {
786
854
  }
787
855
  }
788
856
  }
789
- function createRSCStream(name, data) {
857
+ function createRSCStream(rscName, data) {
790
858
  return new ReadableStream({
791
859
  type: "bytes",
792
860
  start(controller) {
793
861
  const encoder = new TextEncoder();
794
862
  const self = globalThis;
795
- if (!self[name] && data.length > 0) {
863
+ if (data.length > 0) {
796
864
  data.forEach((chunk) => {
797
865
  const lines = chunk.split("\n");
798
866
  for (const line of lines) {
799
867
  const match = /\.push\("(?<rsc>.*)"\);$/.exec(line);
800
868
  if (match?.groups?.rsc) {
801
- self[name] = self[name] ?? [];
802
- self[name].push(JSON.parse(`"${match.groups.rsc}"`));
869
+ self[rscName] = self[rscName] ?? [];
870
+ self[rscName].push(JSON.parse(`"${match.groups.rsc}"`));
803
871
  }
804
872
  }
805
873
  });
806
874
  }
807
- const allChunks = (self[name] ?? [`0:[null]
875
+ const allChunks = (self[rscName] ?? [`0:[null]
808
876
  `]).join("");
877
+ self[rscName] = null;
809
878
  allChunks.split("\n").forEach((chunk) => {
810
879
  if (chunk.length > 0) {
811
880
  const { before, id, prefix, payload } = /^(?<before>.*?)?(?<id>[0-9a-zA-Z]+):(?<prefix>[A-Z])?(?<payload>\[.*\])/.exec(
@@ -835,6 +904,44 @@ function createRSCStream(name, data) {
835
904
  init_webpack_adapter();
836
905
  init_const();
837
906
 
907
+ // src/html/host/index.tsx
908
+ init_utils();
909
+
910
+ // src/shared/client/apply-origin.ts
911
+ var tagNames = [
912
+ "img",
913
+ "source",
914
+ "video",
915
+ "audio",
916
+ "track",
917
+ "iframe",
918
+ "embed",
919
+ "script",
920
+ "link"
921
+ ];
922
+ function applyOriginToNodes(doc, url) {
923
+ const nodes = doc.querySelectorAll(
924
+ tagNames.map((type) => `${type}[src],${type}[srcset]`).join(",")
925
+ );
926
+ nodes.forEach((node) => {
927
+ if (node.hasAttribute("src") && /^[./]+\/?/.test(node.getAttribute("src") ?? "")) {
928
+ node.src = new URL(node.getAttribute("src") ?? "/", url).href;
929
+ }
930
+ if (node.hasAttribute("srcset")) {
931
+ const srcSet = node.getAttribute("srcset")?.split(",").map((entry) => {
932
+ const [urlPart, descriptor] = entry.trim().split(/\s+/);
933
+ if (!urlPart)
934
+ return entry;
935
+ const absoluteUrl = new URL(urlPart, url).href;
936
+ return descriptor ? `${absoluteUrl} ${descriptor}` : absoluteUrl;
937
+ }).join(", ");
938
+ if (srcSet) {
939
+ node.setAttribute("srcset", srcSet);
940
+ }
941
+ }
942
+ });
943
+ }
944
+
838
945
  // src/html/host/runtime/index.ts
839
946
  async function getRuntime(type, url, bundle, shared, remoteShared) {
840
947
  if (typeof globalThis.process === "undefined") {
@@ -857,44 +964,64 @@ import { jsx as jsx2 } from "react/jsx-runtime";
857
964
  if (typeof HTMLElement !== "undefined") {
858
965
  class RemoteComponent extends HTMLElement {
859
966
  constructor() {
860
- super();
967
+ super(...arguments);
861
968
  this.__next = null;
862
969
  this.fouc = null;
863
970
  this.isLoading = false;
864
971
  this.root = null;
865
- this.root = this.attachShadow({
866
- mode: this.getAttribute("mode") === "closed" ? "closed" : "open"
867
- });
868
- this.fallbackSlot = document.createElement("slot");
869
- this.root.appendChild(this.fallbackSlot);
870
- this.name = this.getAttribute("name") || "__vercel_remote_component";
871
- this.bundle = "default";
872
- if (this.hasAttribute("src") || this.querySelector("div#__REMOTE_COMPONENT__") || this.hasAttribute("data-ssr")) {
873
- this.load().catch((e) => {
874
- throw new Error(`Failed to load remote component: ${e}`);
875
- });
876
- }
877
972
  }
878
973
  static get observedAttributes() {
879
- return ["src", "name"];
974
+ return ["src", "name", "mode"];
880
975
  }
881
976
  // watch for src attribute changes
882
977
  // this is required to reload the remote component when the src attribute is added later
883
978
  // this is for rendering the custom element using React
884
979
  attributeChangedCallback(name, oldValue, newValue) {
885
980
  if ((name === "src" || name === "name") && oldValue !== newValue) {
981
+ if (this.getAttribute("src")) {
982
+ this.load().catch((e) => {
983
+ console.error(e);
984
+ throw new Error(
985
+ `Failed to load remote component: ${this.bundle ?? this.name ?? this.getAttribute("src")}`
986
+ );
987
+ });
988
+ }
989
+ } else if (name === "mode" && oldValue !== newValue && this.root) {
990
+ const newRoot = this.attachShadow({
991
+ mode: newValue === "closed" ? "closed" : "open"
992
+ });
993
+ Array.from(this.root.children).forEach((child) => {
994
+ newRoot.appendChild(child);
995
+ });
996
+ this.root = newRoot;
886
997
  this.load().catch((e) => {
887
- console.error(e);
888
- throw new Error(
889
- `Failed to load remote component: ${this.bundle ?? this.name ?? this.getAttribute("src")}`
890
- );
998
+ throw new Error(`Failed to load remote component: ${e}`);
891
999
  });
892
1000
  }
893
1001
  }
894
1002
  async load() {
1003
+ await new Promise((resolve) => {
1004
+ (typeof queueMicrotask === "function" ? queueMicrotask : requestAnimationFrame)(() => {
1005
+ resolve(void 0);
1006
+ });
1007
+ });
895
1008
  if (this.isLoading) {
896
1009
  return;
897
1010
  }
1011
+ if (!this.root) {
1012
+ this.root = this.attachShadow({
1013
+ mode: this.getAttribute("mode") === "closed" ? "closed" : "open"
1014
+ });
1015
+ this.fallbackSlot = document.createElement("slot");
1016
+ this.root.appendChild(this.fallbackSlot);
1017
+ }
1018
+ this.name = this.getAttribute("name") || "__vercel_remote_component";
1019
+ this.bundle = "default";
1020
+ if (this.hasAttribute("src") || this.querySelector("div#__REMOTE_COMPONENT__") || this.hasAttribute("data-ssr")) {
1021
+ this.load().catch((e) => {
1022
+ throw new Error(`Failed to load remote component: ${e}`);
1023
+ });
1024
+ }
898
1025
  this.isLoading = true;
899
1026
  const src = this.getAttribute("src");
900
1027
  const remoteComponentChild = this.querySelector("div#__REMOTE_COMPONENT__") || this.querySelector("div[data-bundle][data-route]");
@@ -925,8 +1052,8 @@ if (typeof HTMLElement !== "undefined") {
925
1052
  }
926
1053
  html = await res.text();
927
1054
  }
928
- const doc = document.createElement("div");
929
- doc.innerHTML = html;
1055
+ const parser = new DOMParser();
1056
+ const doc = parser.parseFromString(html, "text/html");
930
1057
  const component = doc.querySelector(`div[data-bundle][data-route][id^="${this.name}"]`) ?? // fallback to the first element with the data-bundle and data-route attributes when not using a named remote component
931
1058
  doc.querySelector("div[data-bundle][data-route]") ?? // fallback to Next.js Pages Router
932
1059
  doc.querySelector("div#__next");
@@ -936,11 +1063,18 @@ if (typeof HTMLElement !== "undefined") {
936
1063
  if (nextData && nextData.buildId === "development" && !this.reactRoot) {
937
1064
  this.fouc = document.createElement("style");
938
1065
  this.fouc.textContent = `:host { display: none; }`;
939
- this.root?.appendChild(this.fouc);
1066
+ this.root.appendChild(this.fouc);
940
1067
  }
941
1068
  this.name = component?.getAttribute("id")?.replace(/_ssr$/, "") || (nextData ? "__next" : this.name);
942
1069
  const rsc = doc.querySelector(`#${this.name}_rsc`);
943
1070
  this.bundle = component?.getAttribute("data-bundle") || nextData?.props.__REMOTE_COMPONENT__?.bundle || this.bundle;
1071
+ if (url) {
1072
+ const self2 = globalThis;
1073
+ if (!self2.__remote_bundle_url__) {
1074
+ self2.__remote_bundle_url__ = {};
1075
+ }
1076
+ self2.__remote_bundle_url__[this.bundle ?? "default"] = url;
1077
+ }
944
1078
  const metadata = document.createElement("script");
945
1079
  metadata.type = "application/json";
946
1080
  metadata.setAttribute("data-remote-component", "");
@@ -950,47 +1084,81 @@ if (typeof HTMLElement !== "undefined") {
950
1084
  route: component?.getAttribute("data-route") ?? nextData?.page ?? "/",
951
1085
  runtime: component?.getAttribute("data-runtime") ?? nextData?.props.__REMOTE_COMPONENT__?.runtime
952
1086
  });
953
- this.parentNode?.insertBefore(metadata, this);
954
- const remoteSharedEl = doc.querySelector(`#${this.name}_shared`);
955
- const remoteShared = JSON.parse(remoteSharedEl?.textContent ?? "{}") ?? {};
1087
+ if (this.previousElementSibling?.getAttribute("data-remote-component") !== null) {
1088
+ this.previousElementSibling?.remove();
1089
+ }
1090
+ this.parentElement?.insertBefore(metadata, this);
1091
+ const remoteSharedEl = doc.querySelector(
1092
+ `#${this.name}_shared[data-remote-components-shared]`
1093
+ );
1094
+ const remoteShared = nextData?.props.__REMOTE_COMPONENT__?.shared ?? (JSON.parse(remoteSharedEl?.textContent ?? "{}") ?? {});
956
1095
  remoteSharedEl?.parentElement?.removeChild(remoteSharedEl);
957
1096
  if (!component || !(rsc || nextData)) {
958
1097
  throw new Error(`Failed to find component with id "${this.name}"`);
959
1098
  }
960
1099
  const removable = Array.from(this.childNodes);
961
1100
  const links = doc.querySelectorAll("link[href]");
962
- await Promise.all(
963
- Array.from(links).map((link) => {
964
- return new Promise((resolve, reject) => {
965
- const newLink = document.createElement("link");
966
- if (link.rel === "stylesheet") {
967
- newLink.onload = () => {
1101
+ const remoteComponentSrc = this.getAttribute("src");
1102
+ const attachLinks = async () => {
1103
+ await Promise.all(
1104
+ Array.from(links).filter((link) => {
1105
+ return !component.contains(link);
1106
+ }).map((link) => {
1107
+ return new Promise((resolve, reject) => {
1108
+ const newLink = document.createElement("link");
1109
+ if (link.rel === "stylesheet") {
1110
+ newLink.onload = () => {
1111
+ resolve();
1112
+ };
1113
+ newLink.onerror = () => {
1114
+ reject(
1115
+ new Error(
1116
+ `Failed to load link ${link.href} for remote component`
1117
+ )
1118
+ );
1119
+ };
1120
+ } else {
968
1121
  resolve();
969
- };
970
- newLink.onerror = () => {
971
- reject(
972
- new Error(
973
- `Failed to load link ${link.href} for remote component`
974
- )
975
- );
976
- };
977
- } else {
978
- resolve();
979
- }
980
- for (const attr of link.attributes) {
981
- if (attr.name === "href") {
1122
+ }
1123
+ for (const attr of link.attributes) {
1124
+ if (attr.name === "href") {
1125
+ newLink.setAttribute(
1126
+ attr.name,
1127
+ new URL(attr.value, url ?? location.origin).href
1128
+ );
1129
+ } else {
1130
+ newLink.setAttribute(attr.name, attr.value);
1131
+ }
1132
+ }
1133
+ if (remoteComponentSrc) {
982
1134
  newLink.setAttribute(
983
- attr.name,
984
- new URL(attr.value, url ?? location.origin).href
1135
+ "data-remote-component-src",
1136
+ remoteComponentSrc
985
1137
  );
986
- } else {
987
- newLink.setAttribute(attr.name, attr.value);
988
1138
  }
1139
+ this.root?.appendChild(newLink);
1140
+ });
1141
+ })
1142
+ );
1143
+ const styles = doc.querySelectorAll("style");
1144
+ styles.forEach((style) => {
1145
+ if (style.parentElement?.tagName.toLowerCase() === "head") {
1146
+ const newStyle = document.createElement("style");
1147
+ newStyle.textContent = style.textContent;
1148
+ if (remoteComponentSrc) {
1149
+ newStyle.setAttribute(
1150
+ "data-remote-component-src",
1151
+ remoteComponentSrc
1152
+ );
989
1153
  }
990
- this.root?.appendChild(newLink);
991
- });
992
- })
993
- );
1154
+ this.root?.appendChild(newStyle);
1155
+ }
1156
+ });
1157
+ };
1158
+ if (!this.reactRoot) {
1159
+ await attachLinks();
1160
+ }
1161
+ applyOriginToNodes(doc, url ?? new URL(location.href));
994
1162
  if (!this.reactRoot) {
995
1163
  Array.from(component.children).forEach((el) => {
996
1164
  this.root?.appendChild(el);
@@ -999,12 +1167,32 @@ if (typeof HTMLElement !== "undefined") {
999
1167
  for (const el of removable) {
1000
1168
  el.parentElement?.removeChild(el);
1001
1169
  }
1002
- this.fallbackSlot.remove();
1003
- if (this.getAttribute("reset") !== null && !this.root?.querySelector("style[data-remote-components-reset]")) {
1004
- const allInitial = document.createElement("style");
1005
- allInitial.setAttribute("data-remote-components-reset", "");
1006
- allInitial.textContent = `:host { all: initial; }`;
1007
- this.root?.prepend(allInitial);
1170
+ this.fallbackSlot?.remove();
1171
+ const applyReset = () => {
1172
+ if (this.getAttribute("reset") !== null && !this.root?.querySelector("link[data-remote-components-reset]")) {
1173
+ const allInitial = document.createElement("link");
1174
+ allInitial.setAttribute("data-remote-components-reset", "");
1175
+ const css = `:host { all: initial; }`;
1176
+ const allInitialHref = URL.createObjectURL(
1177
+ new Blob([css], { type: "text/css" })
1178
+ );
1179
+ allInitial.href = allInitialHref;
1180
+ allInitial.rel = "stylesheet";
1181
+ allInitial.onload = () => {
1182
+ URL.revokeObjectURL(allInitialHref);
1183
+ allInitial.removeAttribute("onload");
1184
+ };
1185
+ allInitial.onerror = () => {
1186
+ URL.revokeObjectURL(allInitialHref);
1187
+ allInitial.removeAttribute("onload");
1188
+ };
1189
+ this.root?.prepend(allInitial);
1190
+ } else if (this.getAttribute("reset") === null && this.root?.querySelector("link[data-remote-components-reset]")) {
1191
+ this.root.querySelector("link[data-remote-components-reset]")?.remove();
1192
+ }
1193
+ };
1194
+ if (!this.reactRoot) {
1195
+ applyReset();
1008
1196
  }
1009
1197
  const {
1010
1198
  self,
@@ -1039,20 +1227,59 @@ if (typeof HTMLElement !== "undefined") {
1039
1227
  this.bundle ?? "default",
1040
1228
  this.name ?? "__vercel_remote_component"
1041
1229
  );
1230
+ const doCleanup = () => {
1231
+ if (this.root && remoteComponentSrc) {
1232
+ const selector = `[data-remote-component-src]:not([data-remote-component-src="${remoteComponentSrc}"])`;
1233
+ const prevCleanup = [
1234
+ ...this.root.querySelectorAll(selector),
1235
+ ...document.body.querySelectorAll(selector)
1236
+ ];
1237
+ if (prevCleanup.length > 0) {
1238
+ prevCleanup.forEach((prev) => {
1239
+ prev.remove();
1240
+ });
1241
+ }
1242
+ }
1243
+ };
1042
1244
  if (rsc) {
1043
1245
  rsc.parentElement?.removeChild(rsc);
1246
+ const rscName = `__remote_component_rsc_${escapeString(url.href)}_${escapeString(this.name)}`;
1044
1247
  const rscClone = document.createElement("script");
1045
- rscClone.id = `${this.name}_rsc`;
1046
- rscClone.textContent = rsc.textContent;
1248
+ rscClone.id = `${rscName}_rsc`;
1249
+ rscClone.textContent = rsc.textContent?.replace(
1250
+ new RegExp(`self\\["${this.name}"\\]`, "g"),
1251
+ `self["${rscName}"]`
1252
+ ) ?? "";
1047
1253
  document.body.appendChild(rscClone);
1048
1254
  let cache;
1049
1255
  const RemoteComponentFromReadableStream = ({
1050
- name
1256
+ name,
1257
+ initial
1051
1258
  }) => {
1052
- const stream = createRSCStream(name, self[name] ?? [`0:[null]
1053
- `]);
1259
+ const stream = createRSCStream(
1260
+ rscName,
1261
+ self[rscName] ?? [`0:[null]
1262
+ `]
1263
+ );
1054
1264
  const Component = cache ?? // cache the component to avoid reloading the RSC flight data
1055
1265
  (cache = createFromReadableStream(stream));
1266
+ useLayoutEffect(() => {
1267
+ if (self[name]) {
1268
+ delete self[name];
1269
+ }
1270
+ const rscScript = document.getElementById(`${name}_rsc`);
1271
+ if (rscScript) {
1272
+ rscScript.remove();
1273
+ }
1274
+ doCleanup();
1275
+ applyReset();
1276
+ if (!initial) {
1277
+ attachLinks().catch((e) => {
1278
+ console.error(e);
1279
+ });
1280
+ }
1281
+ this.isLoading = false;
1282
+ }, [initial, name]);
1056
1283
  return Component;
1057
1284
  };
1058
1285
  if (this.reactRoot) {
@@ -1062,11 +1289,11 @@ if (typeof HTMLElement !== "undefined") {
1062
1289
  /* @__PURE__ */ jsx2(
1063
1290
  RemoteComponentFromReadableStream,
1064
1291
  {
1292
+ initial: false,
1065
1293
  name: this.name ?? "__vercel_remote_component"
1066
1294
  }
1067
1295
  )
1068
1296
  );
1069
- this.isLoading = false;
1070
1297
  });
1071
1298
  return;
1072
1299
  }
@@ -1077,6 +1304,7 @@ if (typeof HTMLElement !== "undefined") {
1077
1304
  /* @__PURE__ */ jsx2(
1078
1305
  RemoteComponentFromReadableStream,
1079
1306
  {
1307
+ initial: true,
1080
1308
  name: this.name ?? "__vercel_remote_component"
1081
1309
  }
1082
1310
  )
@@ -1088,12 +1316,24 @@ if (typeof HTMLElement !== "undefined") {
1088
1316
  this.root
1089
1317
  );
1090
1318
  if (Component) {
1319
+ const RemoteComponentFromNext = ((NextApp, NextComponent, remoteComponent = this) => function RemoteComponentNext({ initial }) {
1320
+ useLayoutEffect(() => {
1321
+ doCleanup();
1322
+ if (!initial) {
1323
+ applyReset();
1324
+ attachLinks().catch((e) => {
1325
+ console.error(e);
1326
+ });
1327
+ }
1328
+ remoteComponent.isLoading = false;
1329
+ }, [initial]);
1330
+ return NextApp ? /* @__PURE__ */ jsx2(NextApp, { Component: NextComponent, ...nextData.props }) : /* @__PURE__ */ jsx2(NextComponent, { ...nextData.props });
1331
+ })(App, Component, this);
1091
1332
  if (this.reactRoot) {
1092
1333
  const root = this.reactRoot;
1093
1334
  startTransition(() => {
1094
- root.render(
1095
- App ? /* @__PURE__ */ jsx2(App, { Component, ...nextData.props }) : /* @__PURE__ */ jsx2(Component, { ...nextData.props })
1096
- );
1335
+ root.render(/* @__PURE__ */ jsx2(RemoteComponentFromNext, { initial: false }));
1336
+ doCleanup();
1097
1337
  this.isLoading = false;
1098
1338
  });
1099
1339
  return;
@@ -1102,11 +1342,11 @@ if (typeof HTMLElement !== "undefined") {
1102
1342
  // hydrateRoot expects a document or element, but it works for the shadow DOM too
1103
1343
  // @ts-expect-error support for shadow DOM
1104
1344
  this.root,
1105
- App ? /* @__PURE__ */ jsx2(App, { Component, ...nextData.props }) : /* @__PURE__ */ jsx2(Component, { ...nextData.props })
1345
+ /* @__PURE__ */ jsx2(RemoteComponentFromNext, { initial: true })
1106
1346
  );
1107
1347
  }
1108
1348
  if (this.fouc) {
1109
- this.root?.removeChild(this.fouc);
1349
+ this.root.removeChild(this.fouc);
1110
1350
  }
1111
1351
  }
1112
1352
  this.isLoading = false;