@decocms/start 0.25.5 → 0.26.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/start",
3
- "version": "0.25.5",
3
+ "version": "0.26.0",
4
4
  "type": "module",
5
5
  "description": "Deco framework for TanStack Start - CMS bridge, admin protocol, hooks, schema generation",
6
6
  "main": "./src/index.ts",
@@ -9,7 +9,7 @@ export {
9
9
  } from "./invoke";
10
10
  export { LIVE_CONTROLS_SCRIPT } from "./liveControls";
11
11
  export { handleMeta, setMetaData } from "./meta";
12
- export { handleRender, setRenderShell } from "./render";
12
+ export { handleRender, setPreviewWrapper, setRenderShell } from "./render";
13
13
  export {
14
14
  composeMeta,
15
15
  getRegisteredLoaders,
@@ -4,8 +4,9 @@ import { getSection } from "../cms/registry";
4
4
  import { resolveValue, WELL_KNOWN_TYPES } from "../cms/resolve";
5
5
  import { buildHtmlShell } from "../sdk/htmlShell";
6
6
  import { LIVE_CONTROLS_SCRIPT } from "./liveControls";
7
+ import { getPreviewWrapper } from "./setup";
7
8
 
8
- export { setRenderShell } from "./setup";
9
+ export { setRenderShell, setPreviewWrapper } from "./setup";
9
10
 
10
11
  function wrapInHtmlShell(sectionHtml: string): string {
11
12
  return buildHtmlShell({ body: sectionHtml, script: LIVE_CONTROLS_SCRIPT });
@@ -28,7 +29,10 @@ async function renderOneSection(section: Record<string, unknown>): Promise<strin
28
29
  const { __resolveType: _, ...sectionProps } = section;
29
30
  const { renderToString } = await import("react-dom/server");
30
31
  const mod = await sectionLoader();
31
- return renderToString(createElement(mod.default, sectionProps));
32
+ const element = createElement(mod.default, sectionProps);
33
+ const Wrapper = getPreviewWrapper();
34
+ const wrapped = Wrapper ? createElement(Wrapper, null, element) : element;
35
+ return renderToString(wrapped);
32
36
  } catch (error) {
33
37
  return `<div style="padding:8px;color:red;font-size:12px;">Error rendering ${resolveType}: ${(error as Error).message}</div>`;
34
38
  }
@@ -161,7 +165,10 @@ export async function handleRender(request: Request): Promise<Response> {
161
165
  const { __resolveType: _, ...cleanProps } = resolvedProps;
162
166
  const { renderToString } = await import("react-dom/server");
163
167
  const mod = await sectionLoader();
164
- const sectionHtml = renderToString(createElement(mod.default, cleanProps));
168
+ const element = createElement(mod.default, cleanProps);
169
+ const Wrapper = getPreviewWrapper();
170
+ const wrapped = Wrapper ? createElement(Wrapper, null, element) : element;
171
+ const sectionHtml = renderToString(wrapped);
165
172
  return new Response(wrapInHtmlShell(sectionHtml), {
166
173
  status: 200,
167
174
  headers: { "Content-Type": "text/html; charset=utf-8" },
@@ -25,6 +25,38 @@ let themeName = "";
25
25
  let bodyClass = "";
26
26
  let htmlLang = "en";
27
27
 
28
+ /**
29
+ * Optional React component that wraps section renders in admin previews.
30
+ * Use this to provide context that sections depend on (Router, QueryClient, etc.)
31
+ * without which renderToString would crash.
32
+ *
33
+ * The wrapper receives `{ children }` and should render them inside the
34
+ * necessary providers.
35
+ */
36
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
37
+ let previewWrapperComponent: any = null;
38
+
39
+ /**
40
+ * Register a wrapper component for admin preview renders.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * // In site's setup.ts:
45
+ * import { setPreviewWrapper } from "@decocms/start/admin/setup";
46
+ * import { PreviewProviders } from "./components/PreviewProviders";
47
+ * setPreviewWrapper(PreviewProviders);
48
+ * ```
49
+ */
50
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
51
+ export function setPreviewWrapper(wrapper: any) {
52
+ previewWrapperComponent = wrapper;
53
+ }
54
+
55
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
56
+ export function getPreviewWrapper(): any {
57
+ return previewWrapperComponent;
58
+ }
59
+
28
60
  export function setRenderShell(opts: {
29
61
  css?: string;
30
62
  fonts?: string[];