remote-components 0.3.4 → 0.3.5

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 (42) hide show
  1. package/dist/host/html.cjs +471 -413
  2. package/dist/host/html.cjs.map +1 -1
  3. package/dist/host/html.js +471 -413
  4. package/dist/host/html.js.map +1 -1
  5. package/dist/host/nextjs/app/client-only.cjs +245 -131
  6. package/dist/host/nextjs/app/client-only.cjs.map +1 -1
  7. package/dist/host/nextjs/app/client-only.js +245 -131
  8. package/dist/host/nextjs/app/client-only.js.map +1 -1
  9. package/dist/host/nextjs/app.cjs +34 -2
  10. package/dist/host/nextjs/app.cjs.map +1 -1
  11. package/dist/host/nextjs/app.js +35 -3
  12. package/dist/host/nextjs/app.js.map +1 -1
  13. package/dist/host/react.cjs +245 -131
  14. package/dist/host/react.cjs.map +1 -1
  15. package/dist/host/react.js +245 -131
  16. package/dist/host/react.js.map +1 -1
  17. package/dist/internal/host/nextjs/app-client.cjs +38 -24
  18. package/dist/internal/host/nextjs/app-client.cjs.map +1 -1
  19. package/dist/internal/host/nextjs/app-client.js +38 -24
  20. package/dist/internal/host/nextjs/app-client.js.map +1 -1
  21. package/dist/internal/host/nextjs/remote-component-links.cjs +24 -13
  22. package/dist/internal/host/nextjs/remote-component-links.cjs.map +1 -1
  23. package/dist/internal/host/nextjs/remote-component-links.d.ts +3 -0
  24. package/dist/internal/host/nextjs/remote-component-links.js +24 -13
  25. package/dist/internal/host/nextjs/remote-component-links.js.map +1 -1
  26. package/dist/internal/host/shared/lifecycle.cjs +69 -0
  27. package/dist/internal/host/shared/lifecycle.cjs.map +1 -0
  28. package/dist/internal/host/shared/lifecycle.d.ts +34 -0
  29. package/dist/internal/host/shared/lifecycle.js +44 -0
  30. package/dist/internal/host/shared/lifecycle.js.map +1 -0
  31. package/dist/internal/host/shared/pipeline.cjs +222 -0
  32. package/dist/internal/host/shared/pipeline.cjs.map +1 -0
  33. package/dist/internal/host/shared/pipeline.d.ts +153 -0
  34. package/dist/internal/host/shared/pipeline.js +200 -0
  35. package/dist/internal/host/shared/pipeline.js.map +1 -0
  36. package/dist/internal/runtime/turbopack/patterns.cjs +1 -1
  37. package/dist/internal/runtime/turbopack/patterns.cjs.map +1 -1
  38. package/dist/internal/runtime/turbopack/patterns.js +1 -1
  39. package/dist/internal/runtime/turbopack/patterns.js.map +1 -1
  40. package/dist/internal/runtime/turbopack/remote-scope-setup.cjs.map +1 -1
  41. package/dist/internal/runtime/turbopack/remote-scope-setup.js.map +1 -1
  42. package/package.json +2 -2
@@ -211,6 +211,24 @@ function getClientOrServerUrl(src, serverFallback) {
211
211
  return typeof src === "string" ? new URL(src, fallback) : src;
212
212
  }
213
213
 
214
+ // src/host/shared/lifecycle.ts
215
+ function makeReactEmitter(callbacks) {
216
+ return {
217
+ beforeLoad(src) {
218
+ callbacks.onBeforeLoad?.(src);
219
+ },
220
+ load(src) {
221
+ callbacks.onLoad?.(src);
222
+ },
223
+ error(error, _src) {
224
+ callbacks.onError?.(error);
225
+ },
226
+ change(info) {
227
+ callbacks.onChange?.(info);
228
+ }
229
+ };
230
+ }
231
+
214
232
  // src/utils/index.ts
215
233
  function escapeString(str) {
216
234
  return str.replace(/[^a-z0-9]/g, "_");
@@ -589,32 +607,6 @@ async function buildWebpackResolve(hostShared, remoteShared, bundle, reactModule
589
607
  return resolve;
590
608
  }
591
609
 
592
- // src/host/shared/state.ts
593
- function createHostState() {
594
- return {
595
- stage: "idle",
596
- prevSrc: void 0,
597
- prevUrl: void 0,
598
- prevName: void 0,
599
- prevIsRemoteComponent: false,
600
- abortController: void 0
601
- };
602
- }
603
-
604
- // src/host/utils/resolve-name-from-src.ts
605
- function resolveNameFromSrc(src, defaultName) {
606
- if (!src) {
607
- return defaultName;
608
- }
609
- const hash = typeof src === "string" ? src : src.hash;
610
- const hashIndex = hash.indexOf("#");
611
- if (hashIndex < 0) {
612
- return defaultName;
613
- }
614
- const name = hash.slice(hashIndex + 1);
615
- return name || defaultName;
616
- }
617
-
618
610
  // src/runtime/html/html-spec.ts
619
611
  var ORIGIN_REWRITE_TAGS = [
620
612
  "img",
@@ -817,48 +809,6 @@ function parseRemoteComponentDocument(doc, name, url) {
817
809
  };
818
810
  }
819
811
 
820
- // src/runtime/html/set-attributes-from-props.ts
821
- var DOMAttributeNames = {
822
- acceptCharset: "accept-charset",
823
- className: "class",
824
- htmlFor: "for",
825
- httpEquiv: "http-equiv",
826
- noModule: "noModule"
827
- };
828
- var ignoreProps = [
829
- "onLoad",
830
- "onReady",
831
- "dangerouslySetInnerHTML",
832
- "children",
833
- "onError",
834
- "strategy",
835
- "stylesheets"
836
- ];
837
- function isBooleanScriptAttribute(attr) {
838
- return ["async", "defer", "noModule"].includes(attr);
839
- }
840
- function setAttributesFromProps(el, props) {
841
- for (const [p, value] of Object.entries(props)) {
842
- if (!Object.hasOwn(props, p))
843
- continue;
844
- if (ignoreProps.includes(p))
845
- continue;
846
- if (value === void 0) {
847
- continue;
848
- }
849
- const attr = DOMAttributeNames[p] || p.toLowerCase();
850
- if (el.tagName === "SCRIPT" && isBooleanScriptAttribute(attr)) {
851
- el[attr] = Boolean(value);
852
- } else {
853
- el.setAttribute(attr, String(value));
854
- }
855
- if (value === false || el.tagName === "SCRIPT" && isBooleanScriptAttribute(attr) && (!value || value === "false")) {
856
- el.setAttribute(attr, "");
857
- el.removeAttribute(attr);
858
- }
859
- }
860
- }
861
-
862
812
  // src/runtime/loaders/component-loader.ts
863
813
  import * as React from "react";
864
814
  import * as JSXDevRuntime from "react/jsx-dev-runtime";
@@ -1139,7 +1089,7 @@ var REMOTE_SHARED_ASSIGNMENT_RE = new RegExp(
1139
1089
  `\\.TURBOPACK_REMOTE_SHARED\\s*=\\s*await (?:__turbopack_context__|[a-z])\\.A\\((?<sharedModuleId>${MODULE_ID_PATTERN})\\)`
1140
1090
  );
1141
1091
  var ASYNC_MODULE_LOADER_RE = new RegExp(
1142
- `(?:__turbopack_context__|e)\\.A\\((?<asyncSharedModuleId>${MODULE_ID_PATTERN})\\)`
1092
+ `(?:__turbopack_context__|[a-z])\\.A\\((?<asyncSharedModuleId>${MODULE_ID_PATTERN})\\)`
1143
1093
  );
1144
1094
  var ASYNC_MODULE_CALLBACK_RE = new RegExp(
1145
1095
  `(?:parentImport|[a-z])\\((?<sharedModuleId>${MODULE_ID_PATTERN})\\)`
@@ -2098,6 +2048,188 @@ async function loadStaticRemoteComponent(scripts, url, resolveClientUrl) {
2098
2048
  );
2099
2049
  }
2100
2050
 
2051
+ // src/host/shared/pipeline.ts
2052
+ function preparePipeline(input) {
2053
+ const parser = new DOMParser();
2054
+ const doc = parser.parseFromString(input.html, "text/html");
2055
+ const parsed = parseRemoteComponentDocument(doc, input.name, input.url);
2056
+ const remoteShared = input.remoteShared ?? parsed.remoteShared;
2057
+ if ("__remote_components_missing_shared__" in remoteShared) {
2058
+ throw new RemoteComponentsError(
2059
+ remoteShared.__remote_components_missing_shared__
2060
+ );
2061
+ }
2062
+ applyOriginToNodes(doc, input.url, input.resolveClientUrl);
2063
+ const scriptDescriptors = buildScriptDescriptors(parsed.scripts, input.url);
2064
+ return { doc, parsed, scriptDescriptors };
2065
+ }
2066
+ async function loadPrepared(input) {
2067
+ const { prepared, url, signal, resolveClientUrl, container, rscName } = input;
2068
+ const { doc, parsed, scriptDescriptors } = prepared;
2069
+ if (signal.aborted) {
2070
+ return { status: "aborted" };
2071
+ }
2072
+ const userShared = await input.shared;
2073
+ if (signal.aborted) {
2074
+ return { status: "aborted" };
2075
+ }
2076
+ if (parsed.isRemoteComponent) {
2077
+ return loadStaticPath({
2078
+ parsed,
2079
+ doc,
2080
+ url,
2081
+ resolveClientUrl
2082
+ });
2083
+ }
2084
+ return loadDynamicPath({
2085
+ parsed,
2086
+ doc,
2087
+ url,
2088
+ scriptDescriptors,
2089
+ shared: userShared,
2090
+ resolveClientUrl,
2091
+ container,
2092
+ rscName
2093
+ });
2094
+ }
2095
+ function buildScriptDescriptors(scripts, url) {
2096
+ return scripts.map((script) => {
2097
+ const scriptSrc = script.getAttribute("data-src") || script.getAttribute("src") || script.src;
2098
+ const { prefix, id: path } = REMOTE_COMPONENT_REGEX.exec(scriptSrc)?.groups ?? {
2099
+ prefix: void 0,
2100
+ id: scriptSrc
2101
+ };
2102
+ return {
2103
+ src: new URL(collapseDoubleSlashes(`${prefix ?? ""}${path}`), url).href
2104
+ };
2105
+ });
2106
+ }
2107
+ async function loadStaticPath(input) {
2108
+ const { parsed, doc, url, resolveClientUrl } = input;
2109
+ const scripts = Array.from(
2110
+ parsed.component.querySelectorAll("script")
2111
+ );
2112
+ const { mount, unmount } = await loadStaticRemoteComponent(
2113
+ scripts,
2114
+ url,
2115
+ resolveClientUrl
2116
+ );
2117
+ return {
2118
+ status: "static",
2119
+ mount,
2120
+ unmount,
2121
+ metadata: parsed.metadata,
2122
+ parsed,
2123
+ doc
2124
+ };
2125
+ }
2126
+ async function loadDynamicPath(input) {
2127
+ const {
2128
+ parsed,
2129
+ doc,
2130
+ url,
2131
+ scriptDescriptors,
2132
+ shared,
2133
+ resolveClientUrl,
2134
+ container
2135
+ } = input;
2136
+ const rscName = input.rscName ?? (parsed.rsc ? `__remote_component_rsc_${escapeString(url.href)}_${escapeString(parsed.name)}` : void 0);
2137
+ const rscData = parsed.rsc ? (parsed.rsc.textContent || "").split("\n").filter(Boolean) : [];
2138
+ const result = await loadRemoteComponent({
2139
+ url,
2140
+ name: parsed.name,
2141
+ rscName,
2142
+ bundle: parsed.metadata.bundle,
2143
+ route: parsed.metadata.route,
2144
+ runtime: parsed.metadata.runtime,
2145
+ data: rscData,
2146
+ nextData: parsed.nextData,
2147
+ scripts: scriptDescriptors,
2148
+ shared: buildHostShared(shared, resolveClientUrl),
2149
+ remoteShared: parsed.remoteShared,
2150
+ container,
2151
+ resolveClientUrl
2152
+ });
2153
+ if (result.error) {
2154
+ return { status: "error", error: result.error };
2155
+ }
2156
+ return {
2157
+ status: "loaded",
2158
+ component: result.component,
2159
+ metadata: parsed.metadata,
2160
+ parsed,
2161
+ doc
2162
+ };
2163
+ }
2164
+
2165
+ // src/host/shared/state.ts
2166
+ function createHostState() {
2167
+ return {
2168
+ stage: "idle",
2169
+ prevSrc: void 0,
2170
+ prevUrl: void 0,
2171
+ prevName: void 0,
2172
+ prevIsRemoteComponent: false,
2173
+ abortController: void 0
2174
+ };
2175
+ }
2176
+
2177
+ // src/host/utils/resolve-name-from-src.ts
2178
+ function resolveNameFromSrc(src, defaultName) {
2179
+ if (!src) {
2180
+ return defaultName;
2181
+ }
2182
+ const hash = typeof src === "string" ? src : src.hash;
2183
+ const hashIndex = hash.indexOf("#");
2184
+ if (hashIndex < 0) {
2185
+ return defaultName;
2186
+ }
2187
+ const name = hash.slice(hashIndex + 1);
2188
+ return name || defaultName;
2189
+ }
2190
+
2191
+ // src/runtime/html/set-attributes-from-props.ts
2192
+ var DOMAttributeNames = {
2193
+ acceptCharset: "accept-charset",
2194
+ className: "class",
2195
+ htmlFor: "for",
2196
+ httpEquiv: "http-equiv",
2197
+ noModule: "noModule"
2198
+ };
2199
+ var ignoreProps = [
2200
+ "onLoad",
2201
+ "onReady",
2202
+ "dangerouslySetInnerHTML",
2203
+ "children",
2204
+ "onError",
2205
+ "strategy",
2206
+ "stylesheets"
2207
+ ];
2208
+ function isBooleanScriptAttribute(attr) {
2209
+ return ["async", "defer", "noModule"].includes(attr);
2210
+ }
2211
+ function setAttributesFromProps(el, props) {
2212
+ for (const [p, value] of Object.entries(props)) {
2213
+ if (!Object.hasOwn(props, p))
2214
+ continue;
2215
+ if (ignoreProps.includes(p))
2216
+ continue;
2217
+ if (value === void 0) {
2218
+ continue;
2219
+ }
2220
+ const attr = DOMAttributeNames[p] || p.toLowerCase();
2221
+ if (el.tagName === "SCRIPT" && isBooleanScriptAttribute(attr)) {
2222
+ el[attr] = Boolean(value);
2223
+ } else {
2224
+ el.setAttribute(attr, String(value));
2225
+ }
2226
+ if (value === false || el.tagName === "SCRIPT" && isBooleanScriptAttribute(attr) && (!value || value === "false")) {
2227
+ el.setAttribute(attr, "");
2228
+ el.removeAttribute(attr);
2229
+ }
2230
+ }
2231
+ }
2232
+
2101
2233
  // src/host/react/hooks/use-resolve-client-url.ts
2102
2234
  import { useMemo } from "react";
2103
2235
  import { useRemoteComponentsContext } from "#internal/host/react/context";
@@ -2224,6 +2356,10 @@ function ConsumeRemoteComponent({
2224
2356
  const { credentials: contextCredentials, shared: contextShared } = useRemoteComponentsContext2();
2225
2357
  const credentials = credentialsProp ?? contextCredentials ?? "same-origin";
2226
2358
  const shared = sharedProp ?? contextShared ?? {};
2359
+ const emitter = useMemo2(
2360
+ () => makeReactEmitter({ onBeforeLoad, onLoad, onError, onChange }),
2361
+ [onBeforeLoad, onLoad, onError, onChange]
2362
+ );
2227
2363
  const name = useMemo2(
2228
2364
  () => resolveNameFromSrc(src, nameProp),
2229
2365
  [src, nameProp]
@@ -2313,7 +2449,7 @@ function ConsumeRemoteComponent({
2313
2449
  hostStateRef.current.abortController?.abort();
2314
2450
  hostStateRef.current.abortController = new AbortController();
2315
2451
  const { signal } = hostStateRef.current.abortController;
2316
- onBeforeLoad?.(src);
2452
+ emitter.beforeLoad(src);
2317
2453
  hostStateRef.current.stage = "loading";
2318
2454
  startTransition(async () => {
2319
2455
  try {
@@ -2344,19 +2480,26 @@ function ConsumeRemoteComponent({
2344
2480
  }
2345
2481
  if (signal.aborted)
2346
2482
  return;
2347
- const parser = new DOMParser();
2348
- const doc = parser.parseFromString(html, "text/html");
2483
+ const userShared = await shared;
2484
+ if (signal.aborted)
2485
+ return;
2486
+ const prepared = preparePipeline({
2487
+ html,
2488
+ name,
2489
+ url,
2490
+ shared: userShared,
2491
+ resolveClientUrl
2492
+ });
2493
+ const { doc, parsed } = prepared;
2349
2494
  const {
2350
2495
  component,
2351
2496
  name: remoteName,
2352
2497
  isRemoteComponent,
2353
2498
  metadata,
2354
- nextData,
2355
2499
  rsc,
2356
2500
  remoteShared,
2357
- links: linkElements,
2358
- scripts: scriptElements
2359
- } = parseRemoteComponentDocument(doc, name, url);
2501
+ links: linkElements
2502
+ } = parsed;
2360
2503
  if (hostStateRef.current.prevIsRemoteComponent) {
2361
2504
  if (shadowRoot) {
2362
2505
  shadowRoot.innerHTML = "";
@@ -2377,7 +2520,6 @@ function ConsumeRemoteComponent({
2377
2520
  hostStateRef.current.prevIsRemoteComponent = isRemoteComponent;
2378
2521
  hostStateRef.current.prevUrl = url;
2379
2522
  hostStateRef.current.prevName = remoteName;
2380
- applyOriginToNodes(doc, url, resolveClientUrl);
2381
2523
  const links = linkElements.map((link) => ({
2382
2524
  href: new URL(link.getAttribute("href") ?? link.href, url).href,
2383
2525
  ...link.getAttributeNames().reduce((acc, key) => {
@@ -2387,7 +2529,6 @@ function ConsumeRemoteComponent({
2387
2529
  return acc;
2388
2530
  }, {})
2389
2531
  }));
2390
- const scripts = scriptElements;
2391
2532
  const inlineScripts = (isRemoteComponent ? component : doc).querySelectorAll(
2392
2533
  "script:not([src]):not([data-src]):not([id*='_rsc']):not([id='__NEXT_DATA__']):not([id='__REMOTE_NEXT_DATA__'])"
2393
2534
  );
@@ -2466,20 +2607,10 @@ function ConsumeRemoteComponent({
2466
2607
  componentHydrationHtml.current = `${Array.from(
2467
2608
  doc.querySelectorAll("link,style")
2468
2609
  ).map((link) => link.outerHTML).join("")}${reset ? `<style data-remote-components-reset="">:host { all: initial; }</style>` : ""}${component.innerHTML}`;
2469
- const userShared = await shared;
2470
- if ("__remote_components_missing_shared__" in userShared) {
2471
- userShared.__remote_components_missing_shared__().catch((e) => {
2472
- throw e;
2473
- });
2474
- } else if ("__remote_components_missing_shared__" in remoteShared) {
2475
- throw new RemoteComponentsError(
2476
- remoteShared.__remote_components_missing_shared__
2477
- );
2478
- }
2479
2610
  if (isRemoteComponent) {
2480
2611
  if (previousSrc !== void 0) {
2481
- onChange?.({
2482
- previousSrc,
2612
+ emitter.change({
2613
+ previousSrc: previousSrc ?? null,
2483
2614
  nextSrc: src,
2484
2615
  previousName,
2485
2616
  nextName: remoteName
@@ -2500,7 +2631,7 @@ function ConsumeRemoteComponent({
2500
2631
  await Promise.all(
2501
2632
  Array.from(mount).map((mountFn) => mountFn(shadowRoot))
2502
2633
  );
2503
- onLoad?.(src);
2634
+ emitter.load(src);
2504
2635
  } else if (isolate === false) {
2505
2636
  setRemoteComponent(
2506
2637
  // TODO: remove wrapper div by converting HTML to RSC or React tree
@@ -2524,59 +2655,42 @@ function ConsumeRemoteComponent({
2524
2655
  (mountFn) => mountFn(prevRemoteComponentContainerRef.current)
2525
2656
  )
2526
2657
  );
2527
- onLoad?.(src);
2658
+ emitter.load(src);
2528
2659
  }
2529
2660
  hostStateRef.current.stage = "loaded";
2530
2661
  } else {
2531
- const result = await loadRemoteComponent({
2662
+ const result = await loadPrepared({
2663
+ prepared,
2532
2664
  url,
2533
- name: remoteName,
2534
- rscName,
2535
- bundle: metadata.bundle,
2536
- route: metadata.route,
2537
- runtime: metadata.runtime,
2538
- data: newData.data,
2539
- nextData,
2540
- scripts: Array.from(scripts).map((script) => {
2541
- const scriptSrc = script.getAttribute("data-src") || script.getAttribute("src") || script.src;
2542
- const { prefix, id: path } = REMOTE_COMPONENT_REGEX.exec(
2543
- scriptSrc
2544
- )?.groups ?? {
2545
- prefix: void 0,
2546
- id: scriptSrc
2547
- };
2548
- return {
2549
- src: new URL(
2550
- collapseDoubleSlashes(`${prefix ?? ""}${path}`),
2551
- url
2552
- ).href
2553
- };
2554
- }),
2555
- shared: buildHostShared(userShared, resolveClientUrl),
2556
- remoteShared,
2665
+ signal,
2666
+ shared: userShared,
2667
+ resolveClientUrl,
2557
2668
  container: shadowRoot,
2558
- resolveClientUrl
2669
+ rscName
2559
2670
  });
2560
2671
  if (rsc) {
2561
2672
  rsc.remove();
2562
2673
  }
2563
2674
  setData(newData);
2564
2675
  if (previousSrc !== void 0) {
2565
- onChange?.({
2566
- previousSrc,
2676
+ emitter.change({
2677
+ previousSrc: previousSrc ?? null,
2567
2678
  nextSrc: src,
2568
2679
  previousName,
2569
2680
  nextName: remoteName
2570
2681
  });
2571
2682
  }
2572
- if (result.error) {
2683
+ if (result.status === "aborted") {
2684
+ return;
2685
+ }
2686
+ if (result.status === "error") {
2573
2687
  hostStateRef.current.stage = "error";
2574
2688
  setRemoteComponent(result.error);
2575
- onError?.(result.error);
2576
- } else {
2689
+ emitter.error(result.error);
2690
+ } else if (result.status === "loaded") {
2577
2691
  hostStateRef.current.stage = "loaded";
2578
2692
  setRemoteComponent(result.component);
2579
- onLoad?.(src);
2693
+ emitter.load(src);
2580
2694
  }
2581
2695
  }
2582
2696
  } catch (error) {
@@ -2586,7 +2700,7 @@ function ConsumeRemoteComponent({
2586
2700
  }
2587
2701
  hostStateRef.current.stage = "error";
2588
2702
  setRemoteComponent(error);
2589
- onError?.(error);
2703
+ emitter.error(error);
2590
2704
  }
2591
2705
  });
2592
2706
  }
@@ -2600,10 +2714,10 @@ function ConsumeRemoteComponent({
2600
2714
  shadowRoot,
2601
2715
  reset,
2602
2716
  id,
2603
- onBeforeLoad,
2604
- onLoad,
2605
- onError,
2606
- onChange,
2717
+ emitter.beforeLoad,
2718
+ emitter.load,
2719
+ emitter.error,
2720
+ emitter.change,
2607
2721
  onRequest,
2608
2722
  onResponse,
2609
2723
  resolveClientUrl
@@ -2645,7 +2759,7 @@ function ConsumeRemoteComponent({
2645
2759
  );
2646
2760
  }).then(() => {
2647
2761
  if (src) {
2648
- onLoad?.(src);
2762
+ emitter.load(src);
2649
2763
  }
2650
2764
  }).catch((e) => {
2651
2765
  const error = new RemoteComponentsError(
@@ -2655,7 +2769,7 @@ function ConsumeRemoteComponent({
2655
2769
  }
2656
2770
  );
2657
2771
  setRemoteComponent(error);
2658
- onError?.(error);
2772
+ emitter.error(error);
2659
2773
  });
2660
2774
  }
2661
2775
  }