@cloudcannon/editable-regions 0.0.16 → 0.0.18
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/cloudcannon.mjs +7 -22
- 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 +4 -5
- package/integrations/eleventy/browser/collect-config.mjs +188 -0
- package/integrations/eleventy/browser/index.mjs +9 -0
- package/integrations/eleventy/browser/liquid-builtins.mjs +306 -0
- package/integrations/eleventy/browser/liquid-render.mjs +216 -0
- package/integrations/eleventy/index.cjs +1 -0
- package/integrations/eleventy/index.mjs +582 -0
- package/integrations/liquid/README.md +588 -0
- package/integrations/liquid/errors.mjs +39 -0
- package/integrations/liquid/fs.mjs +25 -74
- package/integrations/liquid/globals.mjs +209 -0
- package/integrations/liquid/include-with-tag.mjs +84 -0
- package/integrations/liquid/index.mjs +163 -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/nodes/editable-component.ts +6 -6
- package/package.json +19 -8
- package/types/eleventy.d.cts +20 -0
- package/types/eleventy.d.ts +81 -0
- package/types/liquid.d.ts +15 -12
- package/integrations/eleventy.mjs +0 -294
- package/integrations/liquid/11ty-filters.mjs +0 -69
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) => {
|
|
@@ -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: [],
|
|
@@ -30,10 +30,10 @@ addFrameworkRenderer({
|
|
|
30
30
|
}
|
|
31
31
|
},
|
|
32
32
|
/**
|
|
33
|
-
* Renders
|
|
34
|
-
* @param {any} Component
|
|
35
|
-
* @param {any} props
|
|
36
|
-
* @returns {Promise<{ html: string }>}
|
|
33
|
+
* Renders to static markup, falling back to a client-side render queue.
|
|
34
|
+
* @param {any} Component
|
|
35
|
+
* @param {any} props
|
|
36
|
+
* @returns {Promise<{ html: string }>}
|
|
37
37
|
*/
|
|
38
38
|
renderToStaticMarkup: async (Component, props) => {
|
|
39
39
|
try {
|
|
@@ -45,7 +45,6 @@ addFrameworkRenderer({
|
|
|
45
45
|
const root = createRoot(node);
|
|
46
46
|
flushSync(() => root.render(reactNode));
|
|
47
47
|
});
|
|
48
|
-
// Queue for client-side rendering if SSR fails
|
|
49
48
|
return {
|
|
50
49
|
html: `<div data-editable-region-csr-id=${id}></div>`,
|
|
51
50
|
};
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-mirrors an Eleventy config's helpers into the live-editing engine. The
|
|
3
|
+
* bundle imports the user's *real* config (so closures and imports survive,
|
|
4
|
+
* unlike `fn.toString()`) and replays it here against a recording stand-in for
|
|
5
|
+
* `eleventyConfig`, capturing every `addFilter`/`addShortcode`/etc. call.
|
|
6
|
+
*
|
|
7
|
+
* Node/build-time APIs the config imports are stubbed at bundle time (see
|
|
8
|
+
* `../index.mjs`), so importing them is harmless; only a helper that invokes
|
|
9
|
+
* one at render time fails.
|
|
10
|
+
*
|
|
11
|
+
* @typedef {"filters" | "shortcodes" | "pairedShortcodes" | "tags"} HelperKind
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
registerCustomTag,
|
|
16
|
+
registerFilter,
|
|
17
|
+
registerPairedShortcode,
|
|
18
|
+
registerShortcode,
|
|
19
|
+
} from "../../liquid/index.mjs";
|
|
20
|
+
import { warnOnce } from "../../liquid/logger.mjs";
|
|
21
|
+
import {
|
|
22
|
+
builtinFilterNames,
|
|
23
|
+
builtinShortcodeNames,
|
|
24
|
+
} from "./liquid-builtins.mjs";
|
|
25
|
+
|
|
26
|
+
/** @type {Record<HelperKind, (name: string, fn: any) => void>} */
|
|
27
|
+
const KIND_REGISTRARS = {
|
|
28
|
+
filters: registerFilter,
|
|
29
|
+
shortcodes: registerShortcode,
|
|
30
|
+
pairedShortcodes: registerPairedShortcode,
|
|
31
|
+
tags: registerCustomTag,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Maps each 11ty registration method to its `[kind, layer]`. Universal and
|
|
36
|
+
* Liquid-specific siblings feed the same kind; the Liquid layer wins on a
|
|
37
|
+
* collision, mirroring 11ty's `{ ...universal, ...liquid }` precedence.
|
|
38
|
+
* Variants for other engines (JS/Handlebars/Nunjucks) aren't mirrored.
|
|
39
|
+
*
|
|
40
|
+
* @type {Record<string, [HelperKind, "universal" | "liquid"]>}
|
|
41
|
+
*/
|
|
42
|
+
const METHOD_TARGETS = {
|
|
43
|
+
addFilter: ["filters", "universal"],
|
|
44
|
+
addAsyncFilter: ["filters", "universal"],
|
|
45
|
+
addLiquidFilter: ["filters", "liquid"],
|
|
46
|
+
addShortcode: ["shortcodes", "universal"],
|
|
47
|
+
addAsyncShortcode: ["shortcodes", "universal"],
|
|
48
|
+
addLiquidShortcode: ["shortcodes", "liquid"],
|
|
49
|
+
addPairedShortcode: ["pairedShortcodes", "universal"],
|
|
50
|
+
addPairedAsyncShortcode: ["pairedShortcodes", "universal"],
|
|
51
|
+
addPairedLiquidShortcode: ["pairedShortcodes", "liquid"],
|
|
52
|
+
addLiquidTag: ["tags", "liquid"],
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Eleventy accepts a plugin as either a config function directly or as a
|
|
57
|
+
* `{ configFunction, ... }` object. Returns the underlying function, or `null`
|
|
58
|
+
* if it's neither (so the caller can skip it).
|
|
59
|
+
*
|
|
60
|
+
* @param {any} plugin
|
|
61
|
+
* @returns {((config: any, opts: any) => void) | null}
|
|
62
|
+
*/
|
|
63
|
+
function resolvePluginFunction(plugin) {
|
|
64
|
+
if (typeof plugin === "function") return plugin;
|
|
65
|
+
if (typeof plugin?.configFunction === "function")
|
|
66
|
+
return plugin.configFunction;
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** @returns {Record<HelperKind, Map<string, any>>} */
|
|
71
|
+
function createEmptyLayer() {
|
|
72
|
+
return {
|
|
73
|
+
filters: new Map(),
|
|
74
|
+
shortcodes: new Map(),
|
|
75
|
+
pairedShortcodes: new Map(),
|
|
76
|
+
tags: new Map(),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Replays `configFn` against a recording stand-in and registers every
|
|
82
|
+
* collected helper that isn't skipped.
|
|
83
|
+
*
|
|
84
|
+
* @param {unknown} config - The config's default export (a function), or a
|
|
85
|
+
* module namespace whose `.default` is that function (ESM/CJS interop).
|
|
86
|
+
* @param {{ skip?: Partial<Record<HelperKind, string[]>> }} [options] - Per-kind
|
|
87
|
+
* override names to skip; builtin browser-port names are skipped automatically.
|
|
88
|
+
*/
|
|
89
|
+
export function collectAndRegisterEleventyHelpers(config, options = {}) {
|
|
90
|
+
const configFn =
|
|
91
|
+
typeof config === "function"
|
|
92
|
+
? config
|
|
93
|
+
: /** @type {any} */ (config)?.default;
|
|
94
|
+
|
|
95
|
+
if (typeof configFn !== "function") {
|
|
96
|
+
warnOnce(
|
|
97
|
+
"eleventy-config-shape",
|
|
98
|
+
"Could not auto-mirror Eleventy config helpers: the config's default " +
|
|
99
|
+
"export isn't a function. Filters/shortcodes defined in the config " +
|
|
100
|
+
"won't be available in live editing.",
|
|
101
|
+
);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Skip builtin browser-port names (derived from `liquid-builtins.mjs`) so a
|
|
106
|
+
// same-named config helper can't clobber our port, plus caller overrides.
|
|
107
|
+
/** @type {Record<HelperKind, Set<string>>} */
|
|
108
|
+
const skip = {
|
|
109
|
+
filters: new Set([...builtinFilterNames, ...(options.skip?.filters ?? [])]),
|
|
110
|
+
shortcodes: new Set([
|
|
111
|
+
...builtinShortcodeNames,
|
|
112
|
+
...(options.skip?.shortcodes ?? []),
|
|
113
|
+
]),
|
|
114
|
+
pairedShortcodes: new Set(options.skip?.pairedShortcodes ?? []),
|
|
115
|
+
tags: new Set(options.skip?.tags ?? []),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const layers = { universal: createEmptyLayer(), liquid: createEmptyLayer() };
|
|
119
|
+
|
|
120
|
+
/** @type {Record<string, any>} */
|
|
121
|
+
const recorder = {};
|
|
122
|
+
for (const [method, [kind, layer]] of Object.entries(METHOD_TARGETS)) {
|
|
123
|
+
recorder[method] = (
|
|
124
|
+
/** @type {string} */ name,
|
|
125
|
+
/** @type {any} */ helperFn,
|
|
126
|
+
) => {
|
|
127
|
+
if (typeof name === "string" && typeof helperFn === "function") {
|
|
128
|
+
layers[layer][kind].set(name, helperFn);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Unrecorded methods are no-ops so running the real config (which calls
|
|
134
|
+
// `addPassthroughCopy`, `on`, sets `dir`, ...) doesn't throw.
|
|
135
|
+
const configRecorder = new Proxy(recorder, {
|
|
136
|
+
get(target, prop, receiver) {
|
|
137
|
+
if (prop in target) return Reflect.get(target, prop, receiver);
|
|
138
|
+
return () => {};
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
recorder.addPlugin = (/** @type {any} */ plugin, /** @type {any} */ opts) => {
|
|
143
|
+
const pluginFn = resolvePluginFunction(plugin);
|
|
144
|
+
if (!pluginFn) return;
|
|
145
|
+
|
|
146
|
+
// A plugin is itself a config function, so replay it against the same
|
|
147
|
+
// recorder to capture the helpers it registers.
|
|
148
|
+
try {
|
|
149
|
+
pluginFn(configRecorder, opts);
|
|
150
|
+
} catch {
|
|
151
|
+
// Node-only plugins are stubbed at bundle time and throw when called.
|
|
152
|
+
// Helpers registered before the throw are kept; the config continues.
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
configFn(configRecorder);
|
|
158
|
+
} catch (err) {
|
|
159
|
+
warnOnce(
|
|
160
|
+
"eleventy-config-replay",
|
|
161
|
+
"Replaying the Eleventy config to mirror its helpers threw: " +
|
|
162
|
+
`${err instanceof Error ? err.message : err}. Some filters/` +
|
|
163
|
+
"shortcodes may be unavailable in live editing — define a browser " +
|
|
164
|
+
"override via `pluginOptions.liquid.<kind>` for any that are needed.",
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
for (const kind of /** @type {HelperKind[]} */ (
|
|
169
|
+
Object.keys(KIND_REGISTRARS)
|
|
170
|
+
)) {
|
|
171
|
+
const register = KIND_REGISTRARS[kind];
|
|
172
|
+
// Liquid layer spread last so it wins on a name collision.
|
|
173
|
+
const merged = new Map([...layers.universal[kind], ...layers.liquid[kind]]);
|
|
174
|
+
|
|
175
|
+
for (const [name, helperFn] of merged) {
|
|
176
|
+
if (skip[kind].has(name)) continue;
|
|
177
|
+
try {
|
|
178
|
+
register(name, helperFn);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
warnOnce(
|
|
181
|
+
`eleventy-mirror:${kind}:${name}`,
|
|
182
|
+
`Failed to mirror Eleventy ${kind} "${name}" into live editing: ` +
|
|
183
|
+
`${err instanceof Error ? err.message : err}.`,
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser entry for the 11ty ports (`@cloudcannon/editable-regions/eleventy/browser`).
|
|
3
|
+
* The generated bundle imports both helpers from here. Kept as a thin barrel
|
|
4
|
+
* so `collect-config.mjs` can import the builtin name lists from
|
|
5
|
+
* `liquid-builtins.mjs` without the two forming an import cycle.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export { collectAndRegisterEleventyHelpers } from "./collect-config.mjs";
|
|
9
|
+
export { registerEleventyBuiltins } from "./liquid-builtins.mjs";
|