@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.
- package/dist/chunk-CSPKK7RR.js +195 -0
- package/dist/react.adapter.client.d.ts +7 -3
- package/dist/react.adapter.client.js +41 -11
- package/dist/react.adapter.server.d.ts +31 -7
- package/dist/react.adapter.server.js +40 -21
- package/dist/react.transform.d.ts +7 -0
- package/dist/react.transform.js +9 -0
- package/globals.d.ts +2 -2
- package/package.json +54 -29
- package/dist/chunk-VQFEYZQS.js +0 -131
- package/dist/components-BdC3VZlV.d.ts +0 -104
|
@@ -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
|
-
|
|
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
|
-
|
|
2
|
+
createWidgetAdapter,
|
|
3
3
|
resolveFallback
|
|
4
|
-
} from "./chunk-
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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(
|
|
51
|
+
let vNode = isAsyncFunction ? await component(props) : createElement(component, props);
|
|
25
52
|
vNode = createElement(StrictMode, null, vNode);
|
|
26
53
|
if (recovering) {
|
|
27
|
-
root = hydrateRoot(
|
|
54
|
+
root = hydrateRoot(container, vNode, {
|
|
55
|
+
identifierPrefix: id,
|
|
56
|
+
onRecoverableError: (error) => reportRecoverableError(container, error)
|
|
57
|
+
});
|
|
28
58
|
} else {
|
|
29
|
-
root = createRoot(
|
|
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 {
|
|
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
|
-
|
|
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
|
|
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 &
|
|
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
|
-
|
|
2
|
+
ReactServerRenderModeContext,
|
|
3
|
+
createWidgetAdapter,
|
|
3
4
|
resolveFallback
|
|
4
|
-
} from "./chunk-
|
|
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/
|
|
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
|
|
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
|
-
|
|
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
|
|
40
|
-
const {
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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(
|
|
54
|
-
vNode = createElement(
|
|
55
|
-
|
|
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
|
};
|
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
|
|
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.
|
|
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": "
|
|
17
|
-
"@web-widget/schema": "
|
|
18
|
-
"@web-widget/web-widget": "
|
|
16
|
+
"@web-widget/helpers": "workspace:*",
|
|
17
|
+
"@web-widget/schema": "workspace:*",
|
|
18
|
+
"@web-widget/web-widget": "workspace:*"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
|
-
"@types/
|
|
22
|
-
"@types/react": "
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
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": "
|
|
85
|
-
}
|
|
86
|
-
|
|
107
|
+
"test": "vitest run"
|
|
108
|
+
},
|
|
109
|
+
"sideEffects": false,
|
|
110
|
+
"type": "module"
|
|
111
|
+
}
|
package/dist/chunk-VQFEYZQS.js
DELETED
|
@@ -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 };
|