@web-widget/react 3.0.0-beta.2 → 3.0.0-beta.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.
@@ -0,0 +1,195 @@
1
+ // src/server/context.ts
2
+ import { createContext } from "react";
3
+ var ReactServerRenderModeContext = createContext(null);
4
+
5
+ // src/widget/fallback.ts
6
+ import { isValidElement } from "react";
7
+ function resolveFallback(fallback) {
8
+ var _a;
9
+ if (fallback !== null && typeof fallback === "object" && !isValidElement(fallback) && !Array.isArray(fallback) && ("pending" in fallback || "error" in fallback)) {
10
+ const value = fallback;
11
+ return {
12
+ pendingFallback: value.pending,
13
+ errorFallback: (_a = value.error) != null ? _a : value.pending
14
+ };
15
+ }
16
+ return {
17
+ pendingFallback: fallback,
18
+ errorFallback: fallback
19
+ };
20
+ }
21
+
22
+ // src/widget/factory.ts
23
+ import {
24
+ Suspense,
25
+ createElement as createElement2,
26
+ memo,
27
+ useContext
28
+ } from "react";
29
+
30
+ // src/widget/task.ts
31
+ import {
32
+ WebWidgetRenderer
33
+ } from "@web-widget/web-widget";
34
+ async function renderWidgetResult(renderer, children, renderChildren) {
35
+ try {
36
+ if (!renderChildren) {
37
+ const html2 = children ? "" : await renderer.renderInnerHTMLToString();
38
+ return { status: "success", html: html2 };
39
+ }
40
+ if (!children) {
41
+ const html2 = await renderer.renderInnerHTMLToString();
42
+ return { status: "success", html: html2 };
43
+ }
44
+ const lightChildrenHTML = await renderChildren(children);
45
+ const html = await renderer.renderInnerHTMLToString({
46
+ children: lightChildrenHTML
47
+ });
48
+ return { status: "success", html };
49
+ } catch (error) {
50
+ return {
51
+ status: "error",
52
+ error: error instanceof Error ? error : new Error(String(error))
53
+ };
54
+ }
55
+ }
56
+ function createWidgetRenderTask({
57
+ children,
58
+ loader,
59
+ renderChildren,
60
+ ...options
61
+ }) {
62
+ if (!loader) {
63
+ throw new TypeError("Missing loader.");
64
+ }
65
+ const renderer = new WebWidgetRenderer(loader, options);
66
+ const { attributes, localName, opaqueInnerHTML } = renderer;
67
+ const result = opaqueInnerHTML !== void 0 ? Promise.resolve({ status: "success", html: opaqueInnerHTML }) : renderWidgetResult(renderer, children, renderChildren);
68
+ return {
69
+ attributes,
70
+ children,
71
+ hasChildrenRenderer: renderChildren !== void 0,
72
+ localName,
73
+ opaqueInnerHTML,
74
+ pendingBoundary: renderer.pendingBoundary,
75
+ result
76
+ };
77
+ }
78
+
79
+ // src/widget/views.ts
80
+ import { createElement, use } from "react";
81
+ function renderDirectChildren(task) {
82
+ if (task.hasChildrenRenderer || !task.children) return void 0;
83
+ return createElement(
84
+ task.localName,
85
+ { ...task.attributes, suppressHydrationWarning: true },
86
+ task.children
87
+ );
88
+ }
89
+ function renderPending(task, pendingFallback) {
90
+ return createElement(
91
+ task.localName,
92
+ { ...task.attributes, suppressHydrationWarning: true },
93
+ createElement(
94
+ task.pendingBoundary.localName,
95
+ {
96
+ "aria-busy": task.pendingBoundary.ariaBusy,
97
+ slot: task.pendingBoundary.slot,
98
+ style: { display: task.pendingBoundary.display }
99
+ },
100
+ pendingFallback
101
+ )
102
+ );
103
+ }
104
+ function renderResult(task, result, errorFallback) {
105
+ if (result.status === "error") {
106
+ console.error("[ReactWidget] Rendering error:", result.error);
107
+ return errorFallback;
108
+ }
109
+ return createElement(task.localName, {
110
+ ...task.attributes,
111
+ dangerouslySetInnerHTML: { __html: result.html },
112
+ suppressHydrationWarning: true
113
+ });
114
+ }
115
+ function StreamingWidgetView({
116
+ task,
117
+ clientOnly,
118
+ errorFallback,
119
+ pendingFallback
120
+ }) {
121
+ const directChildren = renderDirectChildren(task);
122
+ if (directChildren) return directChildren;
123
+ if (task.opaqueInnerHTML !== void 0) {
124
+ return renderResult(
125
+ task,
126
+ { status: "success", html: task.opaqueInnerHTML },
127
+ errorFallback
128
+ );
129
+ }
130
+ if (clientOnly && typeof window === "undefined" && pendingFallback != null) {
131
+ return renderPending(task, pendingFallback);
132
+ }
133
+ return renderResult(task, use(task.result), errorFallback);
134
+ }
135
+ async function BufferedWidgetView({
136
+ task,
137
+ clientOnly,
138
+ errorFallback,
139
+ pendingFallback
140
+ }) {
141
+ const directChildren = renderDirectChildren(task);
142
+ if (directChildren) return directChildren;
143
+ if (clientOnly && pendingFallback != null) {
144
+ return renderPending(task, pendingFallback);
145
+ }
146
+ return renderResult(task, await task.result, errorFallback);
147
+ }
148
+
149
+ // src/widget/factory.ts
150
+ function createWidgetAdapter(renderChildren) {
151
+ return function widget(loader, options = {}) {
152
+ return memo(function ReactWidget({
153
+ children,
154
+ slot,
155
+ widget: widget2 = {},
156
+ ...data
157
+ }) {
158
+ const { fallback, id, loading, clientOnly } = widget2;
159
+ const serverRenderMode = useContext(ReactServerRenderModeContext);
160
+ const { pendingFallback, errorFallback } = resolveFallback(fallback);
161
+ const buffered = serverRenderMode === "buffered";
162
+ const task = createWidgetRenderTask({
163
+ ...options,
164
+ clientOnly: widget2.clientOnly,
165
+ children,
166
+ data,
167
+ id,
168
+ loader,
169
+ loading: loading != null ? loading : options.loading,
170
+ renderChildren,
171
+ serverOnly: widget2.serverOnly,
172
+ slot
173
+ });
174
+ const view = createElement2(
175
+ buffered ? BufferedWidgetView : StreamingWidgetView,
176
+ {
177
+ clientOnly,
178
+ errorFallback,
179
+ pendingFallback,
180
+ task
181
+ }
182
+ );
183
+ return buffered || task.opaqueInnerHTML !== void 0 ? view : createElement2(Suspense, {
184
+ fallback: pendingFallback,
185
+ children: view
186
+ });
187
+ });
188
+ };
189
+ }
190
+
191
+ export {
192
+ ReactServerRenderModeContext,
193
+ resolveFallback,
194
+ createWidgetAdapter
195
+ };
@@ -1,9 +1,13 @@
1
1
  import * as _web_widget_schema from '@web-widget/schema';
2
+ export { WidgetContainerOptions } from '@web-widget/schema';
3
+ import { ReactWidgetFactory } from './react.adapter.server.js';
4
+ export { ReactWidgetComponent, ReactWidgetContainerProps, ReactWidgetProps, WebWidgetProps, resolveFallback } from './react.adapter.server.js';
2
5
  import { FunctionComponent } from 'react';
3
- export * from '@web-widget/helpers';
4
- export { D as DefineWebWidgetOptions, R as ReactWidgetComponent, a as ReactWidgetProps, W as WebWidgetProps, b as WidgetContainerConfig, c as WidgetFallback, d as container, r as resolveFallback } from './components-BdC3VZlV.js';
6
+ import 'react-dom/static';
5
7
  import '@web-widget/web-widget';
8
+ import 'react-dom/server';
6
9
 
10
+ declare const widget: ReactWidgetFactory;
7
11
  declare const render: _web_widget_schema.ClientRender<FunctionComponent<{}>, unknown, _web_widget_schema.ClientRenderOptions, void | _web_widget_schema.ClientRenderResult<unknown>>;
8
12
 
9
- export { render };
13
+ export { ReactWidgetFactory, render, widget };
@@ -1,32 +1,62 @@
1
1
  import {
2
- container,
2
+ createWidgetAdapter,
3
3
  resolveFallback
4
- } from "./chunk-VQFEYZQS.js";
4
+ } from "./chunk-CSPKK7RR.js";
5
5
 
6
6
  // src/adapter.client.ts
7
7
  import { defineClientRender } from "@web-widget/helpers";
8
8
  import { createElement, StrictMode } from "react";
9
9
  import { createRoot, hydrateRoot } from "react-dom/client";
10
- export * from "@web-widget/helpers";
10
+
11
+ // src/client/hydration-error.ts
12
+ var HYDRATION_ERROR_EVENT = "web-widget:hydration-error";
13
+ function hydrationHost(container) {
14
+ var _a, _b;
15
+ if (container instanceof Element) return container.closest("web-widget");
16
+ const root = container;
17
+ return (_b = (_a = root.host) == null ? void 0 : _a.closest("web-widget")) != null ? _b : null;
18
+ }
19
+ function reportRecoverableError(container, error) {
20
+ var _a;
21
+ const host = hydrationHost(container);
22
+ (host != null ? host : container).dispatchEvent(
23
+ new CustomEvent(HYDRATION_ERROR_EVENT, {
24
+ bubbles: true,
25
+ composed: true,
26
+ detail: {
27
+ moduleURL: (_a = host == null ? void 0 : host.getAttribute("import")) != null ? _a : "",
28
+ adapter: "react",
29
+ phase: "mismatch",
30
+ error
31
+ }
32
+ })
33
+ );
34
+ }
35
+
36
+ // src/adapter.client.ts
37
+ var widget = createWidgetAdapter();
11
38
  var render = defineClientRender(
12
- async (component, data, { recovering, container: container2 }) => {
13
- data = data != null ? data : {};
39
+ async (component, data, { id, recovering, container }) => {
14
40
  if (!component) {
15
41
  throw new TypeError(`Missing component.`);
16
42
  }
17
- if (!container2) {
43
+ if (!container) {
18
44
  throw new Error(`Missing container.`);
19
45
  }
46
+ const props = data != null ? data : {};
20
47
  let root;
21
48
  return {
22
49
  async mount() {
23
50
  const isAsyncFunction = Object.prototype.toString.call(component) === "[object AsyncFunction]";
24
- let vNode = isAsyncFunction ? await component(data) : createElement(component, data);
51
+ let vNode = isAsyncFunction ? await component(props) : createElement(component, props);
25
52
  vNode = createElement(StrictMode, null, vNode);
26
53
  if (recovering) {
27
- root = hydrateRoot(container2, vNode);
54
+ root = hydrateRoot(container, vNode, {
55
+ identifierPrefix: id,
56
+ onRecoverableError: (error) => reportRecoverableError(container, error)
57
+ });
28
58
  } else {
29
- root = createRoot(container2);
59
+ root = createRoot(container, { identifierPrefix: id });
30
60
  root.render(vNode);
31
61
  }
32
62
  },
@@ -38,7 +68,7 @@ var render = defineClientRender(
38
68
  }
39
69
  );
40
70
  export {
41
- container,
42
71
  render,
43
- resolveFallback
72
+ resolveFallback,
73
+ widget
44
74
  };
@@ -1,17 +1,39 @@
1
1
  import * as _web_widget_schema from '@web-widget/schema';
2
- import { FunctionComponent } from 'react';
2
+ import { WidgetHostProps, WidgetContainerProps, WidgetContainerOptions, ExtractWidgetProps, WidgetModuleLoader } from '@web-widget/schema';
3
+ export { WidgetContainerOptions } from '@web-widget/schema';
4
+ import { FunctionComponent, ReactNode } from 'react';
3
5
  import { PrerenderOptions } from 'react-dom/static';
4
- export * from '@web-widget/helpers';
5
- export { D as DefineWebWidgetOptions, R as ReactWidgetComponent, a as ReactWidgetProps, W as WebWidgetProps, b as WidgetContainerConfig, c as WidgetFallback, d as container, r as resolveFallback } from './components-BdC3VZlV.js';
6
+ import { WebWidgetRendererOptions } from '@web-widget/web-widget';
6
7
  import { RenderToReadableStreamOptions } from 'react-dom/server';
7
- import '@web-widget/web-widget';
8
8
 
9
9
  declare global {
10
10
  interface ReadableStream {
11
11
  [Symbol.asyncIterator](): AsyncIterator<ArrayBuffer | Uint8Array>;
12
12
  }
13
13
  }
14
- type RenderToStringOptions = PrerenderOptions;
14
+ type PrerenderToStringOptions = PrerenderOptions;
15
+
16
+ type ReactWidgetContainerProps = WidgetContainerProps<ReactNode>;
17
+ interface ReactWidgetProps extends WidgetHostProps {
18
+ children?: ReactNode;
19
+ /** Container configuration, isolated from widget's own props. */
20
+ widget?: ReactWidgetContainerProps;
21
+ }
22
+ interface ReactWidgetComponent<T> extends FunctionComponent<T & ReactWidgetProps> {
23
+ }
24
+ interface ReactWidgetFactory {
25
+ <M>(loader: () => Promise<M>, options?: WidgetContainerOptions): ReactWidgetComponent<ExtractWidgetProps<M>>;
26
+ <Props>(loader: WidgetModuleLoader, options?: WidgetContainerOptions): ReactWidgetComponent<Props>;
27
+ }
28
+ type WebWidgetProps = WebWidgetRendererOptions & {
29
+ loader: WidgetModuleLoader;
30
+ children?: ReactNode;
31
+ };
32
+
33
+ declare function resolveFallback(fallback: ReactWidgetContainerProps['fallback']): {
34
+ pendingFallback: ReactNode;
35
+ errorFallback: ReactNode;
36
+ };
15
37
 
16
38
  declare module '@web-widget/schema' {
17
39
  interface ServerRenderOptions {
@@ -19,12 +41,14 @@ declare module '@web-widget/schema' {
19
41
  }
20
42
  }
21
43
 
44
+ declare const widget: ReactWidgetFactory;
22
45
  type StreamOptions = {
46
+ /** @deprecated */
23
47
  awaitAllReady?: boolean;
24
- } & RenderToReadableStreamOptions & RenderToStringOptions;
48
+ } & RenderToReadableStreamOptions & PrerenderToStringOptions;
25
49
  interface ReactRenderOptions {
26
50
  react?: StreamOptions;
27
51
  }
28
52
  declare const render: _web_widget_schema.ServerRender<FunctionComponent<{}>, unknown, _web_widget_schema.ServerRenderOptions, _web_widget_schema.ServerRenderResult>;
29
53
 
30
- export { type ReactRenderOptions, render };
54
+ export { type ReactRenderOptions, type ReactWidgetComponent, type ReactWidgetContainerProps, type ReactWidgetFactory, type ReactWidgetProps, type WebWidgetProps, render, resolveFallback, widget };
@@ -1,18 +1,19 @@
1
1
  import {
2
- container,
2
+ ReactServerRenderModeContext,
3
+ createWidgetAdapter,
3
4
  resolveFallback
4
- } from "./chunk-VQFEYZQS.js";
5
+ } from "./chunk-CSPKK7RR.js";
5
6
 
6
7
  // src/adapter.server.ts
7
8
  import { defineServerRender } from "@web-widget/helpers";
8
9
  import { createElement, StrictMode } from "react";
9
10
 
10
- // src/edge.ts
11
+ // src/server/react-dom.ts
11
12
  import { renderToReadableStream as _renderToReadableStream } from "react-dom/server.edge";
12
13
  import { prerender as _prerender } from "react-dom/static.edge";
13
14
  var prerender = _prerender;
14
15
  var renderToReadableStream = _renderToReadableStream;
15
- async function renderToString(vNode, options) {
16
+ async function prerenderToString(vNode, options) {
16
17
  return readableStreamToString(
17
18
  (await prerender(vNode, options)).prelude
18
19
  );
@@ -28,31 +29,49 @@ async function readableStreamToString(readableStream) {
28
29
  }
29
30
 
30
31
  // src/adapter.server.ts
31
- export * from "@web-widget/helpers";
32
+ var widget = createWidgetAdapter(
33
+ (children) => prerenderToString(
34
+ createElement(
35
+ ReactServerRenderModeContext.Provider,
36
+ { value: "buffered" },
37
+ children
38
+ ),
39
+ {}
40
+ )
41
+ );
32
42
  var DEFAULT_TIMEOUT_MS = 1e3 * 10;
33
43
  var render = defineServerRender(
34
- async (component, data, { progressive, react }) => {
35
- data = data != null ? data : {};
44
+ async (component, data, { id, progressive, react }) => {
36
45
  if (!component) {
37
46
  throw new TypeError(`Missing component.`);
38
47
  }
39
- const reactRenderOptions = Object.create(react != null ? react : null);
40
- const { onError, awaitAllReady, signal } = reactRenderOptions;
41
- reactRenderOptions.signal = signal != null ? signal : AbortSignal.timeout(DEFAULT_TIMEOUT_MS);
48
+ const props = data != null ? data : {};
49
+ const { awaitAllReady, onError, signal, ...configuredOptions } = react != null ? react : {};
42
50
  const preShellErrors = [];
43
51
  let shellRendered = false;
44
- reactRenderOptions.onError = (e, i) => {
45
- if (shellRendered) {
46
- console.error(e);
47
- } else {
48
- preShellErrors.push(e);
52
+ const reactRenderOptions = {
53
+ ...configuredOptions,
54
+ // React uses this namespace for useId(). It must match hydrateRoot and
55
+ // be unique across independently rendered widget roots in one document.
56
+ identifierPrefix: id,
57
+ signal: signal != null ? signal : AbortSignal.timeout(DEFAULT_TIMEOUT_MS),
58
+ onError(e, i) {
59
+ if (shellRendered) {
60
+ console.error(e);
61
+ } else {
62
+ preShellErrors.push(e);
63
+ }
64
+ onError == null ? void 0 : onError(e instanceof Error ? e : new Error(String(e)), i);
49
65
  }
50
- onError == null ? void 0 : onError(e instanceof Error ? e : new Error(String(e)), i);
51
66
  };
52
67
  const isAsyncFunction = Object.prototype.toString.call(component) === "[object AsyncFunction]";
53
- let vNode = isAsyncFunction ? await component(data) : createElement(component, data);
54
- vNode = createElement(StrictMode, null, vNode);
55
- const renderMethod = progressive ? renderToReadableStream : renderToString;
68
+ let vNode = isAsyncFunction ? await component(props) : createElement(component, props);
69
+ vNode = createElement(
70
+ ReactServerRenderModeContext.Provider,
71
+ { value: progressive ? "progressive" : "buffered" },
72
+ createElement(StrictMode, null, vNode)
73
+ );
74
+ const renderMethod = progressive ? renderToReadableStream : prerenderToString;
56
75
  const result = await renderMethod(vNode, reactRenderOptions);
57
76
  shellRendered = true;
58
77
  for (const e of preShellErrors) {
@@ -65,7 +84,7 @@ var render = defineServerRender(
65
84
  }
66
85
  );
67
86
  export {
68
- container,
69
87
  render,
70
- resolveFallback
88
+ resolveFallback,
89
+ widget
71
90
  };
@@ -0,0 +1,7 @@
1
+ declare const _default: {
2
+ name: string;
3
+ extensions: string[];
4
+ adapter: string;
5
+ };
6
+
7
+ export { _default as default };
@@ -0,0 +1,9 @@
1
+ // src/transform.ts
2
+ var transform_default = {
3
+ name: "react",
4
+ extensions: [".tsx", ".jsx"],
5
+ adapter: "@web-widget/react/adapter"
6
+ };
7
+ export {
8
+ transform_default as default
9
+ };
package/globals.d.ts CHANGED
@@ -18,8 +18,8 @@ type WidgetContainerConfig = {
18
18
  * - `{ pending?, error? }` — specify independently; `error` defaults to `pending`.
19
19
  */
20
20
  fallback?: WebWidgetFallback;
21
- /** Client-side module loading strategy: `'lazy'` loads on first render, `'eager'` on module parse, `'idle'` on browser idle. */
22
- loading?: 'lazy' | 'eager' | 'idle';
21
+ /** Client-side module loading strategy. `'auto'` prioritizes visible widgets and eventually loads all widgets. */
22
+ loading?: 'auto' | 'lazy' | 'eager' | 'idle';
23
23
  /** Widget renders only on the server (SSR), producing static HTML with no client-side mount. Mutually exclusive with `clientOnly`. */
24
24
  serverOnly?: true;
25
25
  /** Widget renders only on the client, producing no server HTML (empty placeholder until client mount). Mutually exclusive with `serverOnly`. */
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@web-widget/react",
3
3
  "description": "React adapter",
4
- "version": "3.0.0-beta.2",
4
+ "version": "3.0.0-beta.5",
5
5
  "homepage": "https://github.com/web-widget/web-widget#readme",
6
6
  "repository": {
7
7
  "type": "git",
@@ -13,38 +13,45 @@
13
13
  },
14
14
  "license": "MIT",
15
15
  "dependencies": {
16
- "@web-widget/helpers": "3.0.0-beta.2",
17
- "@web-widget/schema": "3.0.0-beta.2",
18
- "@web-widget/web-widget": "3.0.0-beta.2"
16
+ "@web-widget/helpers": "workspace:*",
17
+ "@web-widget/schema": "workspace:*",
18
+ "@web-widget/web-widget": "workspace:*"
19
19
  },
20
20
  "devDependencies": {
21
- "@types/jest": "^29.5.12",
22
- "@types/react": "^19.2.14",
23
- "@types/react-dom": "^19.2.3",
24
- "jest": "^29.7.0",
25
- "react": "^19.2.6",
26
- "react-dom": "^19.2.6",
27
- "ts-jest": "29.2.1",
28
- "tsup": "^8.3.4",
29
- "typescript": "^5.9.3",
30
- "vite": "^8.0.12"
21
+ "@types/react": "catalog:",
22
+ "@types/react-dom": "catalog:",
23
+ "react": "catalog:",
24
+ "react-dom": "catalog:",
25
+ "tsup": "catalog:",
26
+ "typescript": "catalog:",
27
+ "vite": "catalog:",
28
+ "vitest": "catalog:"
31
29
  },
32
30
  "exports": {
33
31
  ".": {
34
32
  "types": "./dist/react.d.ts",
33
+ "development": "./src/index.ts",
35
34
  "default": "./dist/react.js"
36
35
  },
36
+ "./transform": {
37
+ "types": "./dist/react.transform.d.ts",
38
+ "development": "./src/transform.ts",
39
+ "default": "./dist/react.transform.js"
40
+ },
37
41
  "./adapter": {
38
42
  "worker": {
39
43
  "types": "./dist/react.adapter.server.d.ts",
44
+ "development": "./src/adapter.server.ts",
40
45
  "default": "./dist/react.adapter.server.js"
41
46
  },
42
47
  "browser": {
43
48
  "types": "./dist/react.adapter.client.d.ts",
49
+ "development": "./src/adapter.client.ts",
44
50
  "default": "./dist/react.adapter.client.js"
45
51
  },
46
52
  "default": {
47
53
  "types": "./dist/react.adapter.server.d.ts",
54
+ "development": "./src/adapter.server.ts",
48
55
  "default": "./dist/react.adapter.server.js"
49
56
  }
50
57
  },
@@ -52,15 +59,6 @@
52
59
  "types": "./globals.d.ts"
53
60
  }
54
61
  },
55
- "webWidgetAdapter": {
56
- "version": "1.0.0",
57
- "name": "react",
58
- "extensions": [
59
- ".tsx",
60
- ".jsx"
61
- ],
62
- "adapter": "./adapter"
63
- },
64
62
  "files": [
65
63
  "dist",
66
64
  "globals.d.ts"
@@ -70,10 +68,35 @@
70
68
  "react-dom": "^19.0.0"
71
69
  },
72
70
  "publishConfig": {
73
- "access": "public"
71
+ "access": "public",
72
+ "exports": {
73
+ ".": {
74
+ "types": "./dist/react.d.ts",
75
+ "default": "./dist/react.js"
76
+ },
77
+ "./transform": {
78
+ "types": "./dist/react.transform.d.ts",
79
+ "default": "./dist/react.transform.js"
80
+ },
81
+ "./adapter": {
82
+ "worker": {
83
+ "types": "./dist/react.adapter.server.d.ts",
84
+ "default": "./dist/react.adapter.server.js"
85
+ },
86
+ "browser": {
87
+ "types": "./dist/react.adapter.client.d.ts",
88
+ "default": "./dist/react.adapter.client.js"
89
+ },
90
+ "default": {
91
+ "types": "./dist/react.adapter.server.d.ts",
92
+ "default": "./dist/react.adapter.server.js"
93
+ }
94
+ },
95
+ "./globals": {
96
+ "types": "./globals.d.ts"
97
+ }
98
+ }
74
99
  },
75
- "sideEffects": false,
76
- "type": "module",
77
100
  "scripts": {
78
101
  "build": "pnpm exec tsup",
79
102
  "clean:build": "rm -rf dist",
@@ -81,6 +104,8 @@
81
104
  "dev": "pnpm exec tsup --watch",
82
105
  "lint": "eslint src",
83
106
  "prebuild": "pnpm run clean:build",
84
- "test": "jest"
85
- }
86
- }
107
+ "test": "vitest run"
108
+ },
109
+ "sideEffects": false,
110
+ "type": "module"
111
+ }
@@ -1,131 +0,0 @@
1
- // src/components.ts
2
- import { WebWidgetRenderer } from "@web-widget/web-widget";
3
- import {
4
- Component,
5
- Fragment,
6
- Suspense,
7
- createElement,
8
- isValidElement,
9
- use,
10
- memo
11
- } from "react";
12
- var renderWebWidget = function({
13
- children,
14
- loader,
15
- ...props
16
- }) {
17
- if (!loader) {
18
- throw new TypeError(`Missing loader.`);
19
- }
20
- if (children) {
21
- throw new TypeError(`Children not supported.`);
22
- }
23
- const widget = new WebWidgetRenderer(loader, {
24
- ...props,
25
- // TODO children
26
- children: ""
27
- });
28
- const localName = widget.localName;
29
- const attributes = widget.attributes;
30
- const innerHTML = widget.renderInnerHTMLToString().catch(
31
- (err) => err instanceof Error ? err : new Error(String(err))
32
- );
33
- return {
34
- localName,
35
- attributes,
36
- innerHTML
37
- };
38
- };
39
- function WebWidget({
40
- localName,
41
- attributes,
42
- innerHTML,
43
- errorFallback
44
- }) {
45
- const html = use(innerHTML);
46
- if (html instanceof Error) {
47
- console.error("[ReactWidget] Rendering error:", html);
48
- return createElement(Fragment, null, errorFallback);
49
- }
50
- return createElement(localName, {
51
- ...attributes,
52
- dangerouslySetInnerHTML: { __html: html },
53
- // NOTE: Since the innerHTML property of a widget
54
- // will inevitably differ between the server and the client,
55
- // this turns off hydration mismatch warnings.
56
- suppressHydrationWarning: true
57
- });
58
- }
59
- function resolveFallback(fallback) {
60
- var _a;
61
- if (fallback !== null && typeof fallback === "object" && !isValidElement(fallback) && !Array.isArray(fallback) && ("pending" in fallback || "error" in fallback)) {
62
- const obj = fallback;
63
- return {
64
- pendingFallback: obj.pending,
65
- errorFallback: (_a = obj.error) != null ? _a : obj.pending
66
- };
67
- }
68
- return {
69
- pendingFallback: fallback,
70
- errorFallback: fallback
71
- };
72
- }
73
- var WidgetErrorBoundary = class extends Component {
74
- constructor() {
75
- super(...arguments);
76
- this.state = { hasError: false };
77
- }
78
- static getDerivedStateFromError() {
79
- return { hasError: true };
80
- }
81
- componentDidCatch(error) {
82
- console.error("[ReactWidget] Rendering error:", error);
83
- }
84
- render() {
85
- var _a;
86
- if (this.state.hasError) {
87
- return (_a = this.props.fallback) != null ? _a : null;
88
- }
89
- return this.props.children;
90
- }
91
- };
92
- function container(loader, options = {}) {
93
- return memo(function ReactWidget({
94
- children,
95
- widget: {
96
- fallback,
97
- loading = ((_a) => (_a = options.loading) != null ? _a : "lazy")(),
98
- serverOnly,
99
- clientOnly
100
- } = {},
101
- ...data
102
- }) {
103
- var _a2;
104
- const renderStage = serverOnly ? "server" : clientOnly ? "client" : options.renderStage;
105
- const { pendingFallback, errorFallback } = resolveFallback(fallback);
106
- return createElement(
107
- WidgetErrorBoundary,
108
- { fallback: errorFallback },
109
- createElement(Suspense, {
110
- fallback: pendingFallback,
111
- children: createElement(WebWidget, {
112
- errorFallback,
113
- ...renderWebWidget({
114
- ...options,
115
- children,
116
- data,
117
- loader,
118
- loading,
119
- renderStage,
120
- renderTarget: (_a2 = options.renderTarget) != null ? _a2 : "light"
121
- })
122
- })
123
- })
124
- );
125
- });
126
- }
127
-
128
- export {
129
- resolveFallback,
130
- container
131
- };
@@ -1,104 +0,0 @@
1
- import { WebWidgetRendererOptions, Loader } from '@web-widget/web-widget';
2
- import { ExtractWidgetProps } from '@web-widget/schema';
3
- import { FunctionComponent, ReactNode } from 'react';
4
-
5
- interface ReactWidgetComponent<T> extends FunctionComponent<T & ReactWidgetProps> {
6
- }
7
- /**
8
- * Extract the props type `P` from a widget module's default export.
9
- *
10
- * Handles multiple component paradigms so that cross-framework widgets
11
- * (e.g. Vue imported into React) retain their props types:
12
- *
13
- * - React: `ComponentType<P>`, `FunctionComponent<P>`, etc.
14
- * - Vue: components expose `$props` via a constructor signature.
15
- * - Fallback: `unknown` when no pattern matches.
16
- */
17
- interface WebWidgetProps {
18
- base?: WebWidgetRendererOptions['base'];
19
- children?: ReactNode;
20
- data?: WebWidgetRendererOptions['data'];
21
- import?: WebWidgetRendererOptions['import'];
22
- inactive?: WebWidgetRendererOptions['inactive'];
23
- loader: Loader;
24
- meta?: WebWidgetRendererOptions['meta'];
25
- loading?: WebWidgetRendererOptions['loading'];
26
- name?: WebWidgetRendererOptions['name'];
27
- renderStage?: WebWidgetRendererOptions['renderStage'];
28
- renderTarget?: WebWidgetRendererOptions['renderTarget'];
29
- }
30
- type DefineWebWidgetOptions = Partial<Pick<WebWidgetRendererOptions, 'base' | 'import' | 'loading' | 'name' | 'renderStage' | 'renderTarget'>>;
31
- type WidgetFallback = ReactNode | {
32
- pending?: ReactNode;
33
- error?: ReactNode;
34
- };
35
- /**
36
- * Resolve a WidgetFallback into separate pending and error UI.
37
- * - `ReactNode`: used for both pending and error.
38
- * - `{ pending?, error? }`: error defaults to pending if omitted.
39
- */
40
- declare function resolveFallback(fallback: WidgetFallback | undefined): {
41
- pendingFallback: ReactNode;
42
- errorFallback: ReactNode;
43
- };
44
- type WidgetContainerConfig = {
45
- /**
46
- * Fallback UI for loading and error states.
47
- *
48
- * Only effective during server-side rendering: loading UI shows while the
49
- * widget module renders; error UI shows if rendering fails. Both are
50
- * serialized into the HTML stream — no client-side retry exists in the
51
- * islands architecture.
52
- *
53
- * - `ReactNode` — used for both pending (Suspense) and error (ErrorBoundary).
54
- * - `{ pending?, error? }` — specify independently; `error` defaults to `pending`.
55
- *
56
- * @example
57
- * // Simple: same UI for both states
58
- * <Widget widget={{ fallback: <Spinner /> }} />
59
- *
60
- * // Differentiated: separate pending and error UI
61
- * <Widget widget={{ fallback: { pending: <Spinner />, error: <ErrorUI /> } }} />
62
- */
63
- fallback?: WidgetFallback;
64
- /** Client-side module loading strategy: `'lazy'` loads on first render, `'eager'` on module parse, `'idle'` on browser idle. */
65
- loading?: WebWidgetRendererOptions['loading'];
66
- /** Widget renders only on the server (SSR), producing static HTML with no client-side mount. Mutually exclusive with `clientOnly`. */
67
- serverOnly?: true;
68
- /** Widget renders only on the client, producing no server HTML (empty placeholder until client mount). Mutually exclusive with `serverOnly`. */
69
- clientOnly?: true;
70
- };
71
- interface ReactWidgetProps {
72
- children?: ReactNode;
73
- /** Container configuration, isolated from widget's own props */
74
- widget?: WidgetContainerConfig;
75
- }
76
- /**
77
- * Container function (WebWidgetAdapter protocol).
78
- *
79
- * Wraps a widget module loader into a React component with full props
80
- * type inference from the source module's default export.
81
- *
82
- * For same-framework imports (React → React), props types are inferred
83
- * automatically. Cross-framework imports use explicit `container()` calls;
84
- * sources without a structural props contract can use `container<Props>()`.
85
- *
86
- * @example
87
- * ```tsx
88
- * import { container } from '@web-widget/react/adapter';
89
- *
90
- * // Same-framework: full type inference
91
- * const Counter = container(() => import('./Counter@widget.tsx'));
92
- * // ^? ReactWidgetComponent<{ count: number }>
93
- *
94
- * <Counter count={42} /> // type-checked: ✓
95
- *
96
- * // Cross-framework: explicit container() recommended
97
- * const VueCounter = container(() => import('./Counter@widget.vue'));
98
- * <VueCounter count={42} /> // type-checked: ✓
99
- * ```
100
- */
101
- declare function container<M>(loader: () => Promise<M>, options?: DefineWebWidgetOptions): ReactWidgetComponent<ExtractWidgetProps<M>>;
102
- declare function container<Props>(loader: Loader, options?: DefineWebWidgetOptions): ReactWidgetComponent<Props>;
103
-
104
- export { type DefineWebWidgetOptions as D, type ReactWidgetComponent as R, type WebWidgetProps as W, type ReactWidgetProps as a, type WidgetContainerConfig as b, type WidgetFallback as c, container as d, resolveFallback as r };