forked from
danabra.mov/rscexplorer
A tool for people curious about the React Server Components protocol
1/**
2 * RSC Explorer Embed API
3 *
4 * Usage:
5 * ```html
6 * <div id="demo" style="height: 500px;"></div>
7 * <script type="module">
8 * import { mount } from 'https://rscexplorer.dev/embed.js';
9 *
10 * mount('#demo', {
11 * server: `
12 * export default function App() {
13 * return <h1>Hello RSC</h1>;
14 * }
15 * `,
16 * client: `
17 * 'use client'
18 * export function Button() {
19 * return <button>Click</button>;
20 * }
21 * `
22 * });
23 * </script>
24 * ```
25 */
26
27// Get the embed URL relative to this script's location
28const getEmbedUrl = () => {
29 return new URL('embed.html', import.meta.url).href;
30};
31
32/**
33 * Mount an RSC Explorer embed into a container element
34 * @param {string|HTMLElement} container - CSS selector or DOM element
35 * @param {Object} options - Configuration options
36 * @param {string} options.server - Server component code
37 * @param {string} options.client - Client component code
38 * @returns {Object} - Control object with methods to interact with the embed
39 */
40export function mount(container, { server, client }) {
41 const el =
42 typeof container === "string"
43 ? document.querySelector(container)
44 : container;
45
46 if (!el) {
47 throw new Error(`RSC Explorer: Container not found: ${container}`);
48 }
49
50 // Create iframe
51 const iframe = document.createElement("iframe");
52 iframe.src = getEmbedUrl();
53 iframe.style.cssText =
54 "width: 100%; height: 100%; border: 1px solid #e0e0e0; border-radius: 8px;";
55
56 // Wait for iframe to be ready, then send code
57 const handleMessage = (event) => {
58 if (event.source !== iframe.contentWindow) return;
59
60 if (event.data?.type === "rsc-embed:ready") {
61 iframe.contentWindow.postMessage(
62 {
63 type: "rsc-embed:init",
64 code: { server: server.trim(), client: client.trim() },
65 },
66 "*",
67 );
68 }
69 };
70
71 window.addEventListener("message", handleMessage);
72
73 // Clear container and add iframe
74 el.innerHTML = "";
75 el.appendChild(iframe);
76
77 // Return control object
78 return {
79 iframe,
80 destroy: () => {
81 window.removeEventListener("message", handleMessage);
82 el.innerHTML = "";
83 },
84 };
85}