@cloudcannon/editable-regions 0.0.17 → 0.0.19

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/helpers/checks.ts +0 -22
  2. package/helpers/cloudcannon.mjs +7 -22
  3. package/helpers/hydrate-editable-regions.ts +2 -1
  4. package/integrations/astro/astro-integration.mjs +1 -4
  5. package/integrations/astro/index.mjs +15 -41
  6. package/integrations/astro/modules/assets.js +1 -4
  7. package/integrations/astro/modules/content.js +2 -11
  8. package/integrations/astro/react-renderer.mjs +98 -24
  9. package/integrations/astro/svelte-renderer.mjs +72 -15
  10. package/integrations/astro/vue-renderer.mjs +61 -0
  11. package/integrations/eleventy/browser/collect-config.mjs +249 -0
  12. package/integrations/eleventy/browser/index.mjs +9 -0
  13. package/integrations/eleventy/browser/inert.mjs +35 -0
  14. package/integrations/eleventy/browser/liquid-builtins.mjs +306 -0
  15. package/integrations/eleventy/browser/liquid-render.mjs +216 -0
  16. package/integrations/eleventy/browser/process-shim.mjs +32 -0
  17. package/integrations/eleventy/browser/stub-mode.mjs +61 -0
  18. package/integrations/eleventy/index.cjs +28 -0
  19. package/integrations/eleventy/index.mjs +634 -0
  20. package/integrations/liquid/README.md +677 -0
  21. package/integrations/liquid/errors.mjs +41 -0
  22. package/integrations/liquid/fs.mjs +36 -75
  23. package/integrations/liquid/globals.mjs +308 -0
  24. package/integrations/liquid/include-with-tag.mjs +84 -0
  25. package/integrations/liquid/index.mjs +166 -170
  26. package/integrations/liquid/logger.mjs +15 -78
  27. package/integrations/liquid/page-map.mjs +32 -0
  28. package/integrations/liquid/shortcodes.mjs +62 -99
  29. package/integrations/react.mjs +5 -9
  30. package/integrations/vue.mjs +28 -0
  31. package/nodes/editable-array-item.ts +6 -1
  32. package/nodes/editable-component.ts +2 -3
  33. package/nodes/editable-text.ts +6 -0
  34. package/nodes/editable.ts +6 -1
  35. package/package.json +120 -79
  36. package/types/astro.d.ts +4 -0
  37. package/types/eleventy.d.cts +20 -0
  38. package/types/eleventy.d.ts +88 -0
  39. package/types/liquid.d.ts +14 -12
  40. package/types/vue.d.ts +40 -0
  41. package/integrations/eleventy.mjs +0 -294
  42. package/integrations/liquid/11ty-filters.mjs +0 -69
package/helpers/checks.ts CHANGED
@@ -1,7 +1,3 @@
1
- import Editable from "../nodes/editable.js";
2
- import EditableArrayItem from "../nodes/editable-array-item.js";
3
- import EditableText from "../nodes/editable-text.js";
4
-
5
1
  const getEditableType = (el: HTMLElement): string | undefined => {
6
2
  if (el.tagName.startsWith("EDITABLE-")) {
7
3
  return el.tagName.slice(9).toLowerCase();
@@ -9,24 +5,6 @@ const getEditableType = (el: HTMLElement): string | undefined => {
9
5
  return el.dataset.editable;
10
6
  };
11
7
 
12
- export const hasEditable = <T extends object>(
13
- el: T,
14
- ): el is T & { editable: Editable } => {
15
- return "editable" in el && el.editable instanceof Editable;
16
- };
17
-
18
- export const hasEditableText = <T extends object>(
19
- el: T,
20
- ): el is T & { editable: EditableText } => {
21
- return "editable" in el && el.editable instanceof EditableText;
22
- };
23
-
24
- export const hasEditableArrayItem = <T extends object>(
25
- el: T,
26
- ): el is T & { editable: EditableArrayItem } => {
27
- return "editable" in el && el.editable instanceof EditableArrayItem;
28
- };
29
-
30
8
  export const isEditableWebcomponent = (el: unknown): boolean => {
31
9
  if (!(el instanceof HTMLElement)) {
32
10
  return false;
@@ -20,10 +20,7 @@ const extendedWindow = /** @type {any} */ (window);
20
20
  /** @type {CloudCannonVisualEditorAPIV1} */
21
21
  let _cloudcannon;
22
22
 
23
- /**
24
- * Promise that resolves when the CloudCannon API is loaded
25
- * @type {Promise<void>}
26
- */
23
+ /** @type {Promise<void>} */
27
24
  export const apiLoadedPromise = new Promise((resolve) => {
28
25
  if (extendedWindow.CloudCannonAPI) {
29
26
  _cloudcannon = /** @type {any} */ (
@@ -47,10 +44,8 @@ export const apiLoadedPromise = new Promise((resolve) => {
47
44
  });
48
45
 
49
46
  /**
50
- * Add a renderer for editable components
51
- * @param {string} key - The component key
52
- * @param {ComponentRenderer} renderer - The component renderer function
53
- * @returns {void}
47
+ * @param {string} key
48
+ * @param {ComponentRenderer} renderer
54
49
  */
55
50
  export const addEditableComponentRenderer = (key, renderer) => {
56
51
  extendedWindow.cc_components = extendedWindow.cc_components || {};
@@ -58,33 +53,23 @@ export const addEditableComponentRenderer = (key, renderer) => {
58
53
  };
59
54
 
60
55
  /**
61
- * Add a renderer for editable snippets
62
- * @param {string} key - The snippet key
63
- * @param {ComponentRenderer} renderer - The snippet renderer function
64
- * @returns {void}
56
+ * @param {string} key
57
+ * @param {ComponentRenderer} renderer
65
58
  */
66
59
  export const addEditableSnippetRenderer = (key, renderer) => {
67
60
  extendedWindow.cc_snippets = extendedWindow.cc_snippets || {};
68
61
  extendedWindow.cc_snippets[key] = renderer;
69
62
  };
70
63
 
71
- /**
72
- * Get all registered editable component renderers
73
- * @returns {Record<string, ComponentRenderer>}
74
- */
75
64
  export const getEditableComponentRenderers = () =>
76
65
  extendedWindow.cc_components ?? {};
77
66
 
78
- /**
79
- * Get all registered editable snippet renderers
80
- * @returns {Record<string, ComponentRenderer>}
81
- */
82
67
  export const getEditableSnippetRenderers = () =>
83
68
  extendedWindow.cc_snippets ?? {};
84
69
 
85
70
  /**
86
- * Realize API values by converting CloudCannon API objects to their data representations
87
- * @param {unknown} value - The value to realize
71
+ * Resolves CloudCannon API objects (collections, files, datasets) to plain data.
72
+ * @param {unknown} value
88
73
  * @returns {Promise<unknown>}
89
74
  */
90
75
  export const realizeAPIValue = async (value) => {
@@ -7,7 +7,8 @@ import {
7
7
  EditableSource,
8
8
  EditableText,
9
9
  } from "../nodes";
10
- import { hasEditable, isEditableWebcomponent } from "./checks";
10
+ import { hasEditable } from "../nodes/editable.js";
11
+ import { isEditableWebcomponent } from "./checks";
11
12
 
12
13
  const baseEditableMap: Record<string, typeof Editable | undefined> = {
13
14
  array: EditableArray,
@@ -4,10 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
  /** @type{string[]} */
5
5
  const SUPPORTED_VIRTUAL_MODULES = ["assets", "content"];
6
6
 
7
- /**
8
- * @param {*} original
9
- * @returns
10
- */
7
+ /** @param {*} original */
11
8
  function wrapTransform(original) {
12
9
  /**
13
10
  * @this {*}
@@ -4,30 +4,21 @@ import {
4
4
  } from "astro/runtime/server/index.js";
5
5
  import { addEditableComponentRenderer } from "../../helpers/cloudcannon.mjs";
6
6
 
7
- /**
8
- * Queue of React components waiting to be rendered
9
- * @type {((node: Element) => void)[]}
10
- */
7
+ /** @type {((node: Element) => void)[]} */
11
8
  const renderRoots = [];
12
9
 
13
10
  const renderers = [
14
11
  {
15
12
  name: "dynamic-tags",
16
13
  ssr: {
17
- /**
18
- * Checks if the component is a string (HTML tag name).
19
- * @param {any} Component - The component to check
20
- * @returns {boolean} True if component is a string tag name
21
- */
14
+ /** @param {any} Component */
22
15
  check: (Component) => {
23
16
  return typeof Component === "string";
24
17
  },
25
18
  /**
26
- * Renders a dynamic HTML tag with props and slots.
27
- * @param {string} Component - The HTML tag name
28
- * @param {Record<string, any>} props - Props to render as attributes
29
- * @param {Record<string, string>} slots - Slot content
30
- * @returns {Promise<string>} The rendered HTML string
19
+ * @param {string} Component - HTML tag name
20
+ * @param {Record<string, any>} props
21
+ * @param {Record<string, string>} slots
31
22
  */
32
23
  renderToStaticMarkup: async (Component, props, slots) => {
33
24
  const propsString = Object.entries(props)
@@ -57,25 +48,19 @@ export const queueForClientSideRender = (renderFunction) => {
57
48
  };
58
49
 
59
50
  /**
60
- * Registers an Astro component with the CloudCannon component system.
61
- * Creates a wrapper that handles Astro SSR rendering with React hydration support.
51
+ * Registers an Astro component, wrapping it to render via Astro SSR with
52
+ * React hydration support.
62
53
  *
63
- * @param {string} key - Unique identifier for the component
64
- * @param {unknown} component - The Astro component function to register
65
- * @returns {void}
54
+ * @param {string} key
55
+ * @param {unknown} component
66
56
  */
67
57
  export const registerAstroComponent = (key, component) => {
68
58
  /**
69
- * Wrapper function that renders the Astro component with SSR and client-side hydration.
70
- *
71
- * @param {any} props - Props to pass to the Astro component
72
- * @returns {Promise<HTMLElement>} The rendered component as an HTMLElement
59
+ * @param {any} props
60
+ * @returns {Promise<HTMLElement>}
73
61
  */
74
62
  const wrappedComponent = async (props) => {
75
- /**
76
- * Encryption key for Astro server islands
77
- * @type {CryptoKey | undefined}
78
- */
63
+ /** @type {CryptoKey | undefined} Encryption key for Astro server islands */
79
64
  let encryptionKey;
80
65
  try {
81
66
  encryptionKey = await window.crypto.subtle.generateKey(
@@ -130,9 +115,7 @@ export const registerAstroComponent = (key, component) => {
130
115
  slots: {},
131
116
  props,
132
117
  resolve: () => "editable-region-placeholder",
133
- /**
134
- * @param {*} args
135
- */
118
+ /** @param {*} args */
136
119
  createAstro(...args) {
137
120
  if (args.length < 2 || args.length > 3) {
138
121
  console.warn(
@@ -150,18 +133,12 @@ export const registerAstroComponent = (key, component) => {
150
133
  }
151
134
 
152
135
  const astroSlots = {
153
- /**
154
- * @param {string} name
155
- * @returns boolean
156
- */
136
+ /** @param {string} name */
157
137
  has: (name) => {
158
138
  if (!componentSlots) return false;
159
139
  return Boolean(componentSlots[name]);
160
140
  },
161
- /**
162
- * @param {string} name
163
- * @returns string
164
- */
141
+ /** @param {string} name */
165
142
  render: (name) => {
166
143
  return renderSlotToString(SSRResult, componentSlots[name]);
167
144
  },
@@ -174,7 +151,6 @@ export const registerAstroComponent = (key, component) => {
174
151
  };
175
152
  },
176
153
  };
177
- // Render the Astro component to HTML string
178
154
  const result = await renderToString(SSRResult, component, props, {});
179
155
  const doc = document.implementation.createHTMLDocument();
180
156
  doc.body.innerHTML = result;
@@ -184,7 +160,6 @@ export const registerAstroComponent = (key, component) => {
184
160
  renderRoots[csrId]?.(node);
185
161
  });
186
162
 
187
- // Clear the React roots queue
188
163
  renderRoots.length = 0;
189
164
 
190
165
  doc.querySelectorAll("link, [data-island-id]").forEach((node) => {
@@ -201,6 +176,5 @@ export const registerAstroComponent = (key, component) => {
201
176
  return doc.body;
202
177
  };
203
178
 
204
- // Register the wrapped component in the global registry
205
179
  addEditableComponentRenderer(key, wrappedComponent);
206
180
  };
@@ -4,10 +4,7 @@ import PictureInternal from "./picture.astro";
4
4
  export const Image = ImageInternal;
5
5
  export const Picture = PictureInternal;
6
6
 
7
- /**
8
- * @param {{src: any }} options
9
- * @returns
10
- */
7
+ /** @param {{src: any }} options */
11
8
  export const getImage = async (options) => {
12
9
  const resolvedSrc =
13
10
  typeof options.src === "object" && "then" in options.src
@@ -47,10 +47,8 @@ export const getCollection = async (collectionKey, filter) => {
47
47
  };
48
48
 
49
49
  /**
50
- *
51
50
  * @param {string | {collection: string, slug?: string, id?: string}} objOrString
52
51
  * @param {string} [maybeString]
53
- * @returns
54
52
  */
55
53
  export const getEntry = async (objOrString, maybeString) => {
56
54
  if (typeof objOrString === "object") {
@@ -85,10 +83,7 @@ export const getEntry = async (objOrString, maybeString) => {
85
83
  );
86
84
  };
87
85
 
88
- /**
89
- * @param {{collection: string, slug?: string, id?: string}[]} entries
90
- * @returns
91
- */
86
+ /** @param {{collection: string, slug?: string, id?: string}[]} entries */
92
87
  export const getEntries = (entries) => {
93
88
  return Promise.all(entries.map((entry) => getEntry(entry)));
94
89
  };
@@ -96,16 +91,12 @@ export const getEntries = (entries) => {
96
91
  /**
97
92
  * @param {string} collection
98
93
  * @param {string} slug
99
- * @returns
100
94
  */
101
95
  export const getEntryBySlug = (collection, slug) => {
102
96
  return getEntry({ collection, slug });
103
97
  };
104
98
 
105
- /**
106
- * @param {any} entry
107
- * @returns
108
- */
99
+ /** @param {any} entry */
109
100
  export const render = async (entry) => ({
110
101
  Content: () => entry?.body ?? "Content is not available when live editing",
111
102
  headings: [],
@@ -1,55 +1,129 @@
1
- import { createElement } from "react";
1
+ /** biome-ignore-all lint/suspicious/noPrototypeBuiltins: Matches the behaviour of @astrojs/react */
2
+ import ssr from "@astrojs/react/server.js";
3
+ import * as React from "react";
2
4
  import { flushSync } from "react-dom";
3
5
  import { createRoot } from "react-dom/client";
4
- import { renderToStaticMarkup } from "react-dom/server.browser";
5
-
6
6
  import { addFrameworkRenderer, queueForClientSideRender } from "./index.mjs";
7
7
 
8
+ /**
9
+ * @param {string} str
10
+ * @returns {string}
11
+ */
12
+ const slotName = (str) =>
13
+ str.trim().replace(/[-_]([a-z])/g, (_, w) => w.toUpperCase());
14
+ const reactTypeof = Symbol.for("react.element");
15
+ const reactTransitionalTypeof = Symbol.for("react.transitional.element");
16
+
8
17
  addFrameworkRenderer({
9
18
  name: "@astrojs/react",
10
19
  clientEntrypoint: "@astrojs/react/client.js",
11
20
  ssr: {
12
21
  /**
13
22
  * @param {any} Component
14
- * @returns {boolean}
23
+ * @param {any} props
24
+ * @param {Record<string, string>} children
25
+ * @returns {Promise<boolean>}
15
26
  */
16
- check: (Component) => {
27
+ check: async (Component, props, children) => {
28
+ if (typeof Component === "object") {
29
+ return !!Component.$$typeof
30
+ ?.toString()
31
+ .slice("Symbol(".length)
32
+ .startsWith("react");
33
+ }
17
34
  if (typeof Component !== "function") return false;
35
+ if (Component.name === "QwikComponent") return false;
18
36
 
19
- // React class components have render on the prototype
20
- if (typeof Component.prototype?.render === "function") return true;
37
+ if (
38
+ typeof Component === "function" &&
39
+ Component.$$typeof === Symbol.for("react.forward_ref")
40
+ )
41
+ return false;
21
42
 
22
- // React functional components return vnodes with $$typeof
23
- try {
24
- const vnode = Component({});
43
+ if (
44
+ Component.prototype != null &&
45
+ typeof Component.prototype.render === "function"
46
+ ) {
25
47
  return (
26
- vnode != null && typeof vnode === "object" && "$$typeof" in vnode
48
+ React.Component.isPrototypeOf(Component) ||
49
+ React.PureComponent.isPrototypeOf(Component)
27
50
  );
28
- } catch {
29
- return false;
30
51
  }
52
+
53
+ let isReactComponent = false;
54
+ /** @param {...any} args */
55
+ function Tester(...args) {
56
+ try {
57
+ const vnode = Component(...args);
58
+ if (
59
+ vnode &&
60
+ (vnode.$$typeof === reactTypeof ||
61
+ vnode.$$typeof === reactTransitionalTypeof)
62
+ ) {
63
+ isReactComponent = true;
64
+ }
65
+ } catch {}
66
+
67
+ return React.createElement("div");
68
+ }
69
+
70
+ await ssr.renderToStaticMarkup.call(this, Tester, props, children);
71
+
72
+ return isReactComponent;
31
73
  },
32
74
  /**
33
- * Renders a React component to static markup or queues for client-side rendering.
34
- * @param {any} Component - The React component function
35
- * @param {any} props - Props to pass to the component
36
- * @returns {Promise<{ html: string }>} Object containing the rendered HTML
75
+ * Renders to static markup, falling back to a client-side render queue.
76
+ * @param {any} Component
77
+ * @param {any} props
78
+ * @param {Record<string, string>} inputSlotted
79
+ * @param {any} metadata
80
+ * @returns {Promise<{ html: string }>}
37
81
  */
38
- renderToStaticMarkup: async (Component, props) => {
39
- try {
40
- const reactNode = Component(props);
41
- return { html: renderToStaticMarkup(reactNode) };
42
- } catch (_err) {
82
+ renderToStaticMarkup: async (Component, props, inputSlotted, metadata) => {
83
+ if (metadata?.hydrate) {
84
+ const { default: children, ...slotted } = inputSlotted;
85
+ /** @type{Record<string, React.ReactNode>} */
86
+ const slots = {};
87
+ for (const [key, value] of Object.entries(slotted)) {
88
+ const name = slotName(key);
89
+ slots[name] = React.createElement("astro-static-slot", {
90
+ suppressHydrationWarning: true,
91
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: Intentionally rendering static html
92
+ dangerouslySetInnerHTML: { __html: value },
93
+ });
94
+ }
95
+
96
+ const newProps = {
97
+ ...props,
98
+ ...slots,
99
+ };
100
+ const newChildren = children ?? props.children;
101
+ if (newChildren != null) {
102
+ newProps.children = React.createElement("astro-static-slot", {
103
+ suppressHydrationWarning: true,
104
+ // biome-ignore lint/security/noDangerouslySetInnerHtml: Intentionally rendering static html
105
+ dangerouslySetInnerHTML: { __html: newChildren },
106
+ });
107
+ }
108
+
43
109
  const id = queueForClientSideRender((node) => {
44
- const reactNode = createElement(Component, props, null);
110
+ const reactNode = React.createElement(Component, newProps);
45
111
  const root = createRoot(node);
46
112
  flushSync(() => root.render(reactNode));
47
113
  });
48
- // Queue for client-side rendering if SSR fails
114
+
49
115
  return {
50
116
  html: `<div data-editable-region-csr-id=${id}></div>`,
51
117
  };
52
118
  }
119
+
120
+ return ssr.renderToStaticMarkup.call(
121
+ this,
122
+ Component,
123
+ props,
124
+ inputSlotted,
125
+ { ...metadata, astroStaticSlot: true, hydrate: false },
126
+ );
53
127
  },
54
128
  },
55
129
  });
@@ -1,3 +1,4 @@
1
+ import { createRawSnippet } from "svelte";
1
2
  import { addFrameworkRenderer, queueForClientSideRender } from "./index.mjs";
2
3
 
3
4
  /** @type{((component: any, args: { target: HTMLElement, props: unknown }) => void) | undefined} */
@@ -39,27 +40,83 @@ addFrameworkRenderer({
39
40
  return false;
40
41
  },
41
42
  /**
43
+ * Renders to static markup, falling back to a client-side render queue.
42
44
  * @param {any} Component
43
- * @param {unknown} props
44
- * @returns {Promise<{html: string}>}
45
+ * @param {any} props
46
+ * @param {Record<string, string>} slots
47
+ * @param {any} metadata
48
+ * @returns {Promise<{ html: string }>}
45
49
  */
46
- renderToStaticMarkup: async (Component, props) => {
47
- const id = queueForClientSideRender((node) => {
48
- if (mount) {
49
- mount(Component, {
50
- target: /** @type{any}*/ (node),
51
- props,
52
- });
50
+ renderToStaticMarkup: async (Component, props, slots, metadata) => {
51
+ /** @type{Record<string, any>} */
52
+ const renderProps = {};
53
+ /** @type{Record<string, any> | undefined} */
54
+ let $$slots;
55
+ /** @type{import("svelte").Snippet | undefined} */
56
+ let children;
57
+ for (const [key, value] of Object.entries(slots)) {
58
+ $$slots ??= {};
59
+ if (key === "default") {
60
+ $$slots.default = true;
61
+ children = createRawSnippet(() => ({
62
+ render: () => value,
63
+ }));
53
64
  } else {
54
- new Component({ target: node, props });
65
+ $$slots[key] = createRawSnippet(() => ({
66
+ render: () => value,
67
+ }));
55
68
  }
69
+ const slotName = key === "default" ? "children" : key;
70
+ renderProps[slotName] = createRawSnippet(() => ({
71
+ render: () => value,
72
+ }));
73
+ }
56
74
 
57
- flushSync();
58
- });
59
-
60
- return {
61
- html: `<div data-editable-region-csr-id=${id}></div>`,
75
+ const newProps = {
76
+ ...props,
77
+ children,
78
+ $$slots,
79
+ ...renderProps,
62
80
  };
81
+
82
+ if (metadata?.hydrate) {
83
+ const id = queueForClientSideRender((node) => {
84
+ if (mount) {
85
+ mount(Component, {
86
+ target: /** @type{any}*/ (node),
87
+ props: newProps,
88
+ });
89
+ } else {
90
+ new Component({
91
+ target: node,
92
+ props: newProps,
93
+ });
94
+ }
95
+
96
+ flushSync();
97
+ });
98
+
99
+ return {
100
+ html: `<div data-editable-region-csr-id=${id}></div>`,
101
+ };
102
+ }
103
+
104
+ const doc = document.implementation.createHTMLDocument();
105
+ if (mount) {
106
+ mount(Component, {
107
+ target: /** @type{any}*/ (doc.body),
108
+ props: newProps,
109
+ });
110
+ } else {
111
+ new Component({
112
+ target: doc.body,
113
+ props: newProps,
114
+ });
115
+ }
116
+
117
+ flushSync();
118
+
119
+ return { html: doc.body.innerHTML };
63
120
  },
64
121
  },
65
122
  });
@@ -0,0 +1,61 @@
1
+ import { createApp, h } from "vue";
2
+ import { renderToString } from "vue/server-renderer";
3
+
4
+ import { addFrameworkRenderer, queueForClientSideRender } from "./index.mjs";
5
+
6
+ addFrameworkRenderer({
7
+ name: "@astrojs/vue",
8
+ clientEntrypoint: "@astrojs/vue/client.js",
9
+ ssr: {
10
+ /**
11
+ * Checks if the component is a Vue component (object with Vue-specific markers).
12
+ * @param {any} Component - The component to check
13
+ * @returns {boolean} True if component looks like a Vue component
14
+ */
15
+ check: (Component) => {
16
+ return (
17
+ typeof Component === "object" &&
18
+ Component !== null &&
19
+ ("render" in Component ||
20
+ "setup" in Component ||
21
+ "template" in Component ||
22
+ "ssrRender" in Component ||
23
+ "__ssrInlineRender" in Component ||
24
+ "__file" in Component)
25
+ );
26
+ },
27
+
28
+ /**
29
+ * Renders to static markup, falling back to a client-side render queue.
30
+ * @param {any} Component
31
+ * @param {any} inputProps
32
+ * @param {Record<string, string>} slotted
33
+ * @param {any} metadata
34
+ * @returns {Promise<{ html: string }>}
35
+ */
36
+ renderToStaticMarkup: async (Component, inputProps, slotted, metadata) => {
37
+ /** @type{Record<string, Function>} */
38
+ const slots = {};
39
+ const props = { ...inputProps };
40
+ delete props.slot;
41
+ for (const [key, value] of Object.entries(slotted)) {
42
+ slots[key] = () => h("astro-static-slot", { innerHTML: value });
43
+ }
44
+
45
+ if (metadata?.hydrate) {
46
+ const id = queueForClientSideRender((node) => {
47
+ const app = createApp({ render: () => h(Component, props, slots) });
48
+ app.mount(node);
49
+ });
50
+
51
+ return {
52
+ html: `<div data-editable-region-csr-id=${id}></div>`,
53
+ };
54
+ }
55
+
56
+ const app = createApp({ render: () => h(Component, props, slots) });
57
+ const html = await renderToString(app);
58
+ return { html };
59
+ },
60
+ },
61
+ });