@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.
- package/helpers/checks.ts +0 -22
- package/helpers/cloudcannon.mjs +7 -22
- package/helpers/hydrate-editable-regions.ts +2 -1
- package/integrations/astro/astro-integration.mjs +1 -4
- package/integrations/astro/index.mjs +15 -41
- package/integrations/astro/modules/assets.js +1 -4
- package/integrations/astro/modules/content.js +2 -11
- package/integrations/astro/react-renderer.mjs +98 -24
- package/integrations/astro/svelte-renderer.mjs +72 -15
- package/integrations/astro/vue-renderer.mjs +61 -0
- package/integrations/eleventy/browser/collect-config.mjs +249 -0
- package/integrations/eleventy/browser/index.mjs +9 -0
- package/integrations/eleventy/browser/inert.mjs +35 -0
- package/integrations/eleventy/browser/liquid-builtins.mjs +306 -0
- package/integrations/eleventy/browser/liquid-render.mjs +216 -0
- package/integrations/eleventy/browser/process-shim.mjs +32 -0
- package/integrations/eleventy/browser/stub-mode.mjs +61 -0
- package/integrations/eleventy/index.cjs +28 -0
- package/integrations/eleventy/index.mjs +634 -0
- package/integrations/liquid/README.md +677 -0
- package/integrations/liquid/errors.mjs +41 -0
- package/integrations/liquid/fs.mjs +36 -75
- package/integrations/liquid/globals.mjs +308 -0
- package/integrations/liquid/include-with-tag.mjs +84 -0
- package/integrations/liquid/index.mjs +166 -170
- package/integrations/liquid/logger.mjs +15 -78
- package/integrations/liquid/page-map.mjs +32 -0
- package/integrations/liquid/shortcodes.mjs +62 -99
- package/integrations/react.mjs +5 -9
- package/integrations/vue.mjs +28 -0
- package/nodes/editable-array-item.ts +6 -1
- package/nodes/editable-component.ts +2 -3
- package/nodes/editable-text.ts +6 -0
- package/nodes/editable.ts +6 -1
- package/package.json +120 -79
- package/types/astro.d.ts +4 -0
- package/types/eleventy.d.cts +20 -0
- package/types/eleventy.d.ts +88 -0
- package/types/liquid.d.ts +14 -12
- package/types/vue.d.ts +40 -0
- package/integrations/eleventy.mjs +0 -294
- 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;
|
package/helpers/cloudcannon.mjs
CHANGED
|
@@ -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
|
-
*
|
|
51
|
-
* @param {
|
|
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
|
-
*
|
|
62
|
-
* @param {
|
|
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
|
-
*
|
|
87
|
-
* @param {unknown} value
|
|
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
|
|
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
|
-
*
|
|
27
|
-
* @param {string}
|
|
28
|
-
* @param {Record<string,
|
|
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
|
|
61
|
-
*
|
|
51
|
+
* Registers an Astro component, wrapping it to render via Astro SSR with
|
|
52
|
+
* React hydration support.
|
|
62
53
|
*
|
|
63
|
-
* @param {string} key
|
|
64
|
-
* @param {unknown} component
|
|
65
|
-
* @returns {void}
|
|
54
|
+
* @param {string} key
|
|
55
|
+
* @param {unknown} component
|
|
66
56
|
*/
|
|
67
57
|
export const registerAstroComponent = (key, component) => {
|
|
68
58
|
/**
|
|
69
|
-
*
|
|
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
|
-
|
|
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
|
-
* @
|
|
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
|
-
|
|
20
|
-
|
|
37
|
+
if (
|
|
38
|
+
typeof Component === "function" &&
|
|
39
|
+
Component.$$typeof === Symbol.for("react.forward_ref")
|
|
40
|
+
)
|
|
41
|
+
return false;
|
|
21
42
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
43
|
+
if (
|
|
44
|
+
Component.prototype != null &&
|
|
45
|
+
typeof Component.prototype.render === "function"
|
|
46
|
+
) {
|
|
25
47
|
return (
|
|
26
|
-
|
|
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
|
|
34
|
-
* @param {any} Component
|
|
35
|
-
* @param {any} props
|
|
36
|
-
* @
|
|
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
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
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,
|
|
110
|
+
const reactNode = React.createElement(Component, newProps);
|
|
45
111
|
const root = createRoot(node);
|
|
46
112
|
flushSync(() => root.render(reactNode));
|
|
47
113
|
});
|
|
48
|
-
|
|
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 {
|
|
44
|
-
* @
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
+
});
|