@cloudcannon/editable-regions 0.0.18 → 0.0.20-rc.1
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/hydrate-editable-regions.ts +2 -1
- package/integrations/astro/react-renderer.mjs +94 -19
- package/integrations/astro/svelte-renderer.mjs +72 -15
- package/integrations/astro/vue-renderer.mjs +61 -0
- package/integrations/eleventy/browser/collect-config.mjs +69 -8
- package/integrations/eleventy/browser/inert.mjs +35 -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 -1
- package/integrations/eleventy/index.mjs +82 -30
- package/integrations/hugo/browser/entry.js +13 -0
- package/integrations/hugo/browser/errors.ts +41 -0
- package/integrations/hugo/browser/index.ts +344 -0
- package/integrations/hugo/browser/logger.ts +42 -0
- package/integrations/hugo/browser/wasm_exec.js +575 -0
- package/integrations/hugo/hugo-module/layouts/partials/editable-regions/find-config-files.html +14 -0
- package/integrations/hugo/hugo-module/layouts/partials/editable-regions/find-dep-template-files.html +66 -0
- package/integrations/hugo/hugo-module/layouts/partials/editable-regions/find-files-with-extension.html +27 -0
- package/integrations/hugo/hugo-module/layouts/partials/editable-regions/find-template-files.html +51 -0
- package/integrations/hugo/hugo-module/layouts/partials/editable-regions/load-deps.html +5 -0
- package/integrations/hugo/hugo-module/layouts/partials/editable-regions/normalize-extensions.html +9 -0
- package/integrations/hugo/hugo-module/layouts/partials/editable-regions/resources.html +87 -0
- package/integrations/hugo/hugo-module/layouts/partials/editable-regions/wasm-url.html +28 -0
- package/integrations/hugo/hugo-module/layouts/partials/editable-regions.html +6 -0
- package/integrations/hugo/renderer/build.sh +24 -0
- package/integrations/hugo/renderer/go.mod +100 -0
- package/integrations/hugo/renderer/go.sum +241 -0
- package/integrations/hugo/renderer/main.go +412 -0
- package/integrations/liquid/README.md +92 -3
- package/integrations/liquid/errors.mjs +3 -1
- package/integrations/liquid/fs.mjs +11 -1
- package/integrations/liquid/globals.mjs +131 -26
- package/integrations/liquid/index.mjs +5 -2
- 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 +132 -90
- package/types/astro.d.ts +4 -0
- package/types/eleventy.d.ts +11 -4
- package/types/hugo.d.ts +69 -0
- package/types/liquid.d.ts +0 -1
- package/types/vue.d.ts +40 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Builders for the `page` and `collections` globals on the shared Liquid
|
|
2
|
-
// engine. Both return Promises that LiquidJS awaits at the globals level
|
|
3
|
-
//
|
|
2
|
+
// engine. Both return Promises that LiquidJS awaits at the globals level.
|
|
3
|
+
// `page` resolves to a plain object; `collections` resolves to an object whose
|
|
4
|
+
// keys are lazy getters, so a template only pays for the collections it reads.
|
|
4
5
|
|
|
5
6
|
import { apiLoadedPromise, CloudCannon } from "../../helpers/cloudcannon.mjs";
|
|
6
7
|
import { getPageMap, normalizeInputPath } from "./page-map.mjs";
|
|
@@ -135,7 +136,13 @@ async function materialiseFile(file) {
|
|
|
135
136
|
*/
|
|
136
137
|
export async function buildPageData() {
|
|
137
138
|
await apiLoadedPromise;
|
|
138
|
-
|
|
139
|
+
let file;
|
|
140
|
+
try {
|
|
141
|
+
file = CloudCannon?.currentFile?.();
|
|
142
|
+
} catch {
|
|
143
|
+
// No current file (page with no associated source).
|
|
144
|
+
return {};
|
|
145
|
+
}
|
|
139
146
|
if (!file) return {};
|
|
140
147
|
const inputPath = file.path;
|
|
141
148
|
const data = (await file.data.get()) ?? {};
|
|
@@ -150,27 +157,74 @@ export async function buildPageData() {
|
|
|
150
157
|
};
|
|
151
158
|
}
|
|
152
159
|
|
|
153
|
-
/**
|
|
160
|
+
/**
|
|
161
|
+
* Ceiling on concurrent `file.data.get()` calls. One call per file over a
|
|
162
|
+
* collection of thousands fails with `ERR_INSUFFICIENT_RESOURCES` — a net-stack
|
|
163
|
+
* error, so each resolves to a request somewhere behind the editor API.
|
|
164
|
+
*/
|
|
165
|
+
const MATERIALISE_CONCURRENCY = 24;
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* `Promise.all(items.map(fn))` with at most `limit` calls in flight. Results
|
|
169
|
+
* keep their input order.
|
|
170
|
+
*
|
|
171
|
+
* @template T, R
|
|
172
|
+
* @param {T[]} items
|
|
173
|
+
* @param {(item: T) => Promise<R>} fn
|
|
174
|
+
* @param {number} limit
|
|
175
|
+
* @returns {Promise<R[]>}
|
|
176
|
+
*/
|
|
177
|
+
async function mapWithConcurrency(items, fn, limit) {
|
|
178
|
+
/** @type {R[]} */
|
|
179
|
+
const results = new Array(items.length);
|
|
180
|
+
let cursor = 0;
|
|
181
|
+
|
|
182
|
+
const worker = async () => {
|
|
183
|
+
while (cursor < items.length) {
|
|
184
|
+
const index = cursor++;
|
|
185
|
+
results[index] = await fn(items[index]);
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
await Promise.all(
|
|
190
|
+
Array.from({ length: Math.min(limit, items.length) }, worker),
|
|
191
|
+
);
|
|
192
|
+
return results;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** One `CloudCannon.collections()` call, keyed by name. @type {Promise<Map<string, any>> | null} */
|
|
196
|
+
let collectionIndexCache = null;
|
|
197
|
+
|
|
198
|
+
/** Materialised items, per collection name. @type {Map<string, Promise<any[]>>} */
|
|
199
|
+
const collectionItemsCache = new Map();
|
|
200
|
+
|
|
201
|
+
/** @type {Promise<Record<string, any>> | null} */
|
|
154
202
|
let collectionsCache = null;
|
|
155
203
|
|
|
156
204
|
/** @type {Array<{ target: any, event: "change" | "delete", handler: () => void }>} */
|
|
157
205
|
let collectionsSubscriptions = [];
|
|
158
206
|
|
|
159
207
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
208
|
+
* Enumerates the site's collections — one API call, cached — and subscribes to
|
|
209
|
+
* `change`/`delete` on each so an edit drops the caches. Never calls
|
|
210
|
+
* `collection.items()`: knowing the *names* is what lets the getters be
|
|
211
|
+
* enumerable without fetching behind them.
|
|
163
212
|
*
|
|
164
|
-
* @returns {Promise<
|
|
213
|
+
* @returns {Promise<Map<string, any>>}
|
|
165
214
|
*/
|
|
166
|
-
|
|
167
|
-
if (!
|
|
168
|
-
|
|
215
|
+
function loadCollectionIndex() {
|
|
216
|
+
if (!collectionIndexCache) {
|
|
217
|
+
collectionIndexCache = (async () => {
|
|
169
218
|
await apiLoadedPromise;
|
|
170
219
|
const allCollections = await CloudCannon?.collections?.();
|
|
171
|
-
|
|
220
|
+
|
|
221
|
+
/** @type {Map<string, any>} */
|
|
222
|
+
const index = new Map();
|
|
223
|
+
if (!allCollections?.length) return index;
|
|
172
224
|
|
|
173
225
|
for (const collection of allCollections) {
|
|
226
|
+
index.set(collection.collectionKey, collection);
|
|
227
|
+
|
|
174
228
|
const handler = () => resetCollectionsCache();
|
|
175
229
|
collection.addEventListener("change", handler);
|
|
176
230
|
collection.addEventListener("delete", handler);
|
|
@@ -179,31 +233,82 @@ export function buildCollectionsData() {
|
|
|
179
233
|
{ target: collection, event: "delete", handler },
|
|
180
234
|
);
|
|
181
235
|
}
|
|
236
|
+
return index;
|
|
237
|
+
})();
|
|
238
|
+
}
|
|
239
|
+
return collectionIndexCache;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Materialises one collection's files, memoised per name — the only place that
|
|
244
|
+
* issues per-file requests. An unknown name is `[]`, matching 11ty.
|
|
245
|
+
*
|
|
246
|
+
* @param {string} key
|
|
247
|
+
* @returns {Promise<any[]>}
|
|
248
|
+
*/
|
|
249
|
+
function loadCollectionItems(key) {
|
|
250
|
+
let items = collectionItemsCache.get(key);
|
|
251
|
+
if (!items) {
|
|
252
|
+
items = (async () => {
|
|
253
|
+
const collection = (await loadCollectionIndex()).get(key);
|
|
254
|
+
if (!collection) return [];
|
|
182
255
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
return /** @type {[string, any[]]} */ ([key, items]);
|
|
194
|
-
}),
|
|
256
|
+
let files;
|
|
257
|
+
try {
|
|
258
|
+
files = await collection.items();
|
|
259
|
+
} catch {
|
|
260
|
+
return [];
|
|
261
|
+
}
|
|
262
|
+
return mapWithConcurrency(
|
|
263
|
+
files,
|
|
264
|
+
materialiseFile,
|
|
265
|
+
MATERIALISE_CONCURRENCY,
|
|
195
266
|
);
|
|
196
|
-
|
|
267
|
+
})();
|
|
268
|
+
collectionItemsCache.set(key, items);
|
|
269
|
+
}
|
|
270
|
+
return items;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Builds (or returns cached) the `collections` object. Every key is a lazy
|
|
275
|
+
* getter returning a `Promise` of its items, which LiquidJS awaits during
|
|
276
|
+
* expression evaluation — so a component that never mentions `collections`
|
|
277
|
+
* issues no per-file requests.
|
|
278
|
+
*
|
|
279
|
+
* Getters not a Proxy: LiquidJS probes `next` and `toLiquid` on every object
|
|
280
|
+
* it resolves, and a blanket-getter Proxy answers those with a Promise, which
|
|
281
|
+
* breaks the lookup entirely.
|
|
282
|
+
*
|
|
283
|
+
* @returns {Promise<Record<string, any>>}
|
|
284
|
+
*/
|
|
285
|
+
export function buildCollectionsData() {
|
|
286
|
+
if (!collectionsCache) {
|
|
287
|
+
collectionsCache = (async () => {
|
|
288
|
+
const index = await loadCollectionIndex();
|
|
289
|
+
|
|
290
|
+
/** @type {Record<string, any>} */
|
|
291
|
+
const collections = {};
|
|
292
|
+
for (const key of index.keys()) {
|
|
293
|
+
Object.defineProperty(collections, key, {
|
|
294
|
+
enumerable: true,
|
|
295
|
+
configurable: true,
|
|
296
|
+
get: () => loadCollectionItems(key),
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
return collections;
|
|
197
300
|
})();
|
|
198
301
|
}
|
|
199
302
|
return collectionsCache;
|
|
200
303
|
}
|
|
201
304
|
|
|
202
|
-
/** Clears
|
|
305
|
+
/** Clears every collections cache and tears down the invalidation listeners. */
|
|
203
306
|
export function resetCollectionsCache() {
|
|
204
307
|
for (const { target, event, handler } of collectionsSubscriptions) {
|
|
205
308
|
target.removeEventListener(event, handler);
|
|
206
309
|
}
|
|
207
310
|
collectionsSubscriptions = [];
|
|
311
|
+
collectionIndexCache = null;
|
|
312
|
+
collectionItemsCache.clear();
|
|
208
313
|
collectionsCache = null;
|
|
209
314
|
}
|
|
@@ -73,8 +73,11 @@ export function registerLiquidComponent(key, contents) {
|
|
|
73
73
|
/**
|
|
74
74
|
* Wraps `window.cc_components` in a Proxy that resolves any component name on
|
|
75
75
|
* demand via `{% include %}` — the primary resolution path. Names registered
|
|
76
|
-
* via `registerLiquidComponent` take precedence.
|
|
77
|
-
*
|
|
76
|
+
* via `registerLiquidComponent` take precedence.
|
|
77
|
+
*
|
|
78
|
+
* Call after `createSharedLiquidEngine()` and last of the `register*` calls:
|
|
79
|
+
* publishing `cc_components` is what tells the editor every helper is in
|
|
80
|
+
* place, and an empty one reads as a missing registration script.
|
|
78
81
|
*
|
|
79
82
|
* @returns {void}
|
|
80
83
|
*/
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createApp, h } from "vue";
|
|
2
|
+
import { addEditableComponentRenderer } from "../helpers/cloudcannon.mjs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Registers a Vue component with the CloudCannon component system.
|
|
6
|
+
* Creates a wrapper that renders the Vue component to an HTMLElement.
|
|
7
|
+
*
|
|
8
|
+
* @param {string} key - Unique identifier for the component
|
|
9
|
+
* @param {any} component - The Vue component to register
|
|
10
|
+
* @returns {void}
|
|
11
|
+
*/
|
|
12
|
+
export const registerVueComponent = (key, component) => {
|
|
13
|
+
/**
|
|
14
|
+
* Wrapper function that renders the Vue component to an HTMLElement.
|
|
15
|
+
*
|
|
16
|
+
* @param {any} props - Props to pass to the Vue component
|
|
17
|
+
* @returns {HTMLElement} The rendered component as an HTMLElement
|
|
18
|
+
*/
|
|
19
|
+
const wrappedComponent = (props) => {
|
|
20
|
+
const rootEl = document.createElement("div");
|
|
21
|
+
const app = createApp({ render: () => h(component, props) });
|
|
22
|
+
app.mount(rootEl);
|
|
23
|
+
|
|
24
|
+
return rootEl;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
addEditableComponentRenderer(key, wrappedComponent);
|
|
28
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import "../components/ui/editable-array-item-controls.js";
|
|
2
2
|
import type EditableArrayItemControls from "../components/ui/editable-array-item-controls.js";
|
|
3
3
|
import {
|
|
4
|
-
hasEditableArrayItem,
|
|
5
4
|
isEditableArray,
|
|
6
5
|
isEditableArrayItem,
|
|
7
6
|
isEditableElement,
|
|
@@ -10,6 +9,12 @@ import { CloudCannon, realizeAPIValue } from "../helpers/cloudcannon.mjs";
|
|
|
10
9
|
import type EditableArray from "./editable-array.js";
|
|
11
10
|
import EditableComponent from "./editable-component.js";
|
|
12
11
|
|
|
12
|
+
export const hasEditableArrayItem = <T extends object>(
|
|
13
|
+
el: T,
|
|
14
|
+
): el is T & { editable: EditableArrayItem } => {
|
|
15
|
+
return "editable" in el && el.editable instanceof EditableArrayItem;
|
|
16
|
+
};
|
|
17
|
+
|
|
13
18
|
export default class EditableArrayItem extends EditableComponent {
|
|
14
19
|
parent: EditableArray | null = null;
|
|
15
20
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
areEqualEditables,
|
|
3
|
-
hasEditable,
|
|
4
|
-
hasEditableText,
|
|
5
3
|
isEditableElement,
|
|
6
4
|
isEditableText,
|
|
7
5
|
} from "../helpers/checks.js";
|
|
8
|
-
import Editable from "./editable.js";
|
|
6
|
+
import Editable, { hasEditable } from "./editable.js";
|
|
7
|
+
import { hasEditableText } from "./editable-text.js";
|
|
9
8
|
import "../components/ui/editable-region-error-card.js";
|
|
10
9
|
import "../components/ui/editable-component-controls.js";
|
|
11
10
|
import type EditableComponentControls from "../components/ui/editable-component-controls.js";
|
package/nodes/editable-text.ts
CHANGED
|
@@ -3,6 +3,12 @@ import Editable from "./editable.js";
|
|
|
3
3
|
|
|
4
4
|
type EditableFocusEvent = CustomEvent<number>;
|
|
5
5
|
|
|
6
|
+
export const hasEditableText = <T extends object>(
|
|
7
|
+
el: T,
|
|
8
|
+
): el is T & { editable: EditableText } => {
|
|
9
|
+
return "editable" in el && el.editable instanceof EditableText;
|
|
10
|
+
};
|
|
11
|
+
|
|
6
12
|
export default class EditableText extends Editable {
|
|
7
13
|
editor?: any;
|
|
8
14
|
focused = false;
|
package/nodes/editable.ts
CHANGED
|
@@ -3,9 +3,14 @@ import type {
|
|
|
3
3
|
CloudCannonVisualEditorAPIV1Dataset,
|
|
4
4
|
CloudCannonVisualEditorAPIV1File,
|
|
5
5
|
} from "@cloudcannon/visual-editor-api";
|
|
6
|
-
import { hasEditable } from "../helpers/checks";
|
|
7
6
|
import { apiLoadedPromise, CloudCannon } from "../helpers/cloudcannon.mjs";
|
|
8
7
|
|
|
8
|
+
export const hasEditable = <T extends object>(
|
|
9
|
+
el: T,
|
|
10
|
+
): el is T & { editable: Editable } => {
|
|
11
|
+
return "editable" in el && el.editable instanceof Editable;
|
|
12
|
+
};
|
|
13
|
+
|
|
9
14
|
declare global {
|
|
10
15
|
interface HTMLElement {
|
|
11
16
|
__pendingEditableListeners?: EditableListener[];
|
package/package.json
CHANGED
|
@@ -1,92 +1,134 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
2
|
+
"name": "@cloudcannon/editable-regions",
|
|
3
|
+
"version": "0.0.20-rc.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Visual Editing for the CloudCannon CMS.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"cloudcannon"
|
|
8
|
+
],
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"author": "CloudCannon <support@cloudcannon.com>",
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=20.19.0"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/CloudCannon/editable-regions#readme",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/CloudCannon/editable-regions.git"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/CloudCannon/editable-regions/issues",
|
|
21
|
+
"email": "support@cloudcannon.com"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"typecheck": "tsc --noEmit",
|
|
25
|
+
"typecheck:watch": "tsc --noEmit --watch",
|
|
26
|
+
"lint-autofix": "biome check --fix",
|
|
27
|
+
"lint": "biome check",
|
|
28
|
+
"build:hugo": "bash integrations/hugo/renderer/build.sh",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"test:update-snapshots": "vitest run -u",
|
|
32
|
+
"test:build-fixtures": "npm run test:build-astro-fixture && npm run test:build-eleventy-fixture && npm run test:build-eleventy-plugin-config && npm run test:build-hugo-fixture && npm run test:build-hugo-custom-dirs && npm run test:build-hugo-config-options && npm run test:build-hugo-templates-overrides",
|
|
33
|
+
"test:build-hugo-custom-dirs": "npm --prefix test/unit/_fixtures/hugo-custom-dirs run build",
|
|
34
|
+
"test:build-hugo-config-options": "npm --prefix test/unit/_fixtures/hugo-config-options run build",
|
|
35
|
+
"test:build-hugo-templates-overrides": "npm --prefix test/unit/_fixtures/hugo-templates-overrides run build",
|
|
36
|
+
"test:build-hugo-fixtures": "npm run test:build-hugo-fixture && npm run test:build-hugo-custom-dirs && npm run test:build-hugo-config-options && npm run test:build-hugo-templates-overrides",
|
|
37
|
+
"test:build-astro-fixture": "npm --prefix test/unit/_fixtures/astro install --silent && npm --prefix test/unit/_fixtures/astro run build",
|
|
38
|
+
"test:build-eleventy-fixture": "npm --prefix test/unit/_fixtures/eleventy install --silent && npm --prefix test/unit/_fixtures/eleventy run build",
|
|
39
|
+
"test:build-eleventy-plugin-config": "npm --prefix test/unit/_fixtures/eleventy-plugin-config install --silent && npm --prefix test/unit/_fixtures/eleventy-plugin-config run build",
|
|
40
|
+
"test:build-hugo-fixture": "npm --prefix test/unit/_fixtures/hugo run build"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"components",
|
|
44
|
+
"helpers",
|
|
45
|
+
"integrations",
|
|
46
|
+
"nodes",
|
|
47
|
+
"styles",
|
|
48
|
+
"types",
|
|
49
|
+
"!integrations/hugo/renderer/hugo_renderer.wasm",
|
|
50
|
+
"!integrations/hugo/hugo-module/assets/_cloudcannon"
|
|
51
|
+
],
|
|
52
|
+
"exports": {
|
|
53
|
+
"./*": null,
|
|
54
|
+
"./astro": {
|
|
55
|
+
"types": "./types/astro.d.ts",
|
|
56
|
+
"default": "./integrations/astro/index.mjs"
|
|
57
|
+
},
|
|
58
|
+
"./astro-react-renderer": {
|
|
59
|
+
"types": "./types/astro.d.ts",
|
|
60
|
+
"default": "./integrations/astro/react-renderer.mjs"
|
|
61
|
+
},
|
|
62
|
+
"./astro-svelte-renderer": {
|
|
63
|
+
"types": "./types/astro.d.ts",
|
|
64
|
+
"default": "./integrations/astro/svelte-renderer.mjs"
|
|
65
|
+
},
|
|
66
|
+
"./astro-vue-renderer": {
|
|
67
|
+
"types": "./types/astro.d.ts",
|
|
68
|
+
"default": "./integrations/astro/vue-renderer.mjs"
|
|
69
|
+
},
|
|
70
|
+
"./astro-integration": {
|
|
71
|
+
"types": "./types/astro.d.ts",
|
|
72
|
+
"default": "./integrations/astro/astro-integration.mjs"
|
|
73
|
+
},
|
|
74
|
+
"./react": {
|
|
75
|
+
"types": "./types/react.d.ts",
|
|
76
|
+
"default": "./integrations/react.mjs"
|
|
77
|
+
},
|
|
78
|
+
"./svelte": {
|
|
79
|
+
"types": "./types/svelte.d.ts",
|
|
80
|
+
"default": "./integrations/svelte.mjs"
|
|
81
|
+
},
|
|
82
|
+
"./vue": {
|
|
83
|
+
"types": "./types/vue.d.ts",
|
|
84
|
+
"default": "./integrations/vue.mjs"
|
|
85
|
+
},
|
|
86
|
+
"./liquid": "./integrations/liquid/index.mjs",
|
|
87
|
+
"./eleventy": {
|
|
88
|
+
"require": {
|
|
89
|
+
"types": "./types/eleventy.d.cts",
|
|
90
|
+
"default": "./integrations/eleventy/index.cjs"
|
|
91
|
+
},
|
|
92
|
+
"import": {
|
|
93
|
+
"types": "./types/eleventy.d.ts",
|
|
94
|
+
"default": "./integrations/eleventy/index.mjs"
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"./eleventy/browser": "./integrations/eleventy/browser/index.mjs",
|
|
98
|
+
"./hugo/browser": {
|
|
99
|
+
"types": "./types/hugo.d.ts",
|
|
100
|
+
"default": "./integrations/hugo/browser/index.ts"
|
|
101
|
+
},
|
|
102
|
+
"./internal/components": "./components/index.js",
|
|
103
|
+
"./internal/styles": "./styles/index.js"
|
|
104
|
+
},
|
|
105
|
+
"devDependencies": {
|
|
106
|
+
"@astrojs/react": "6.0.2",
|
|
107
|
+
"@astrojs/svelte": "9.0.1",
|
|
108
|
+
"@astrojs/vue": "7.0.2",
|
|
109
|
+
"@biomejs/biome": "2.5.8",
|
|
110
|
+
"@cloudcannon/visual-editor-api": "0.0.20",
|
|
111
|
+
"@sindresorhus/slugify": "3.0.0",
|
|
112
|
+
"@sveltejs/vite-plugin-svelte": "7.3.0",
|
|
113
|
+
"@types/js-beautify": "1.14.3",
|
|
114
|
+
"@types/node": "26.2.0",
|
|
115
|
+
"@types/react": "19.2.18",
|
|
116
|
+
"@types/react-dom": "19.2.4",
|
|
117
|
+
"@vitejs/plugin-vue": "6.0.8",
|
|
118
|
+
"astro": "7.2.2",
|
|
119
|
+
"happy-dom": "20.11.2",
|
|
120
|
+
"js-beautify": "2.0.3",
|
|
121
|
+
"liquidjs": "10.29.0",
|
|
122
|
+
"react": "19.2.8",
|
|
123
|
+
"react-dom": "19.2.8",
|
|
124
|
+
"slugify": "1.6.9",
|
|
125
|
+
"svelte": "5.56.9",
|
|
126
|
+
"typescript": "6.0.3",
|
|
127
|
+
"vitest": "4.1.10",
|
|
128
|
+
"vue": "3.5.41",
|
|
129
|
+
"vue-tsc": "3.3.10"
|
|
130
|
+
},
|
|
131
|
+
"dependencies": {
|
|
132
|
+
"esbuild": "0.28.2"
|
|
133
|
+
}
|
|
92
134
|
}
|
package/types/astro.d.ts
CHANGED
|
@@ -21,3 +21,7 @@ declare module "@cloudcannon/editable-regions/astro-react-renderer" {
|
|
|
21
21
|
declare module "@cloudcannon/editable-regions/astro-svelte-renderer" {
|
|
22
22
|
// Side-effect only module that registers Svelte renderer
|
|
23
23
|
}
|
|
24
|
+
|
|
25
|
+
declare module "@cloudcannon/editable-regions/astro-vue-renderer" {
|
|
26
|
+
// Side-effect only module that registers Vue renderer
|
|
27
|
+
}
|
package/types/eleventy.d.ts
CHANGED
|
@@ -17,10 +17,17 @@ export interface LiquidOptions {
|
|
|
17
17
|
configPath?: string;
|
|
18
18
|
/**
|
|
19
19
|
* Extra bare module specifiers to stub out of the browser bundle, on top of
|
|
20
|
-
* the 11ty toolchain and Node built-ins (always stubbed).
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
20
|
+
* the 11ty toolchain and Node built-ins (always stubbed). Two uses:
|
|
21
|
+
*
|
|
22
|
+
* - a native/Node-only package (e.g. `sharp`) that would otherwise break
|
|
23
|
+
* bundling;
|
|
24
|
+
* - a Node-only package the config *calls* at config time, such as a
|
|
25
|
+
* plugin factory in `addPlugin(pluginFoo({ … }))`. The argument is
|
|
26
|
+
* evaluated before `addPlugin` is reached, so stubbing the module is the
|
|
27
|
+
* only way to stop it aborting the auto-mirror replay.
|
|
28
|
+
*
|
|
29
|
+
* A stubbed module called during the replay is skipped with a warning; the
|
|
30
|
+
* same call from a rendered helper still throws.
|
|
24
31
|
*/
|
|
25
32
|
browserStub?: string[];
|
|
26
33
|
/**
|
package/types/hugo.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CloudCannonVisualEditorAPIRouter,
|
|
3
|
+
CloudCannonVisualEditorAPIV0,
|
|
4
|
+
CloudCannonVisualEditorAPIV1,
|
|
5
|
+
} from "@cloudcannon/visual-editor-api";
|
|
6
|
+
|
|
7
|
+
declare module "@cloudcannon/editable-regions/hugo/browser" {
|
|
8
|
+
/**
|
|
9
|
+
* Boots Hugo live editing from the `window.cc_hugo*` globals emitted by
|
|
10
|
+
* the Hugo module's snapshot prelude. Installs the component proxy
|
|
11
|
+
* immediately; the WASM renderer loads once the CloudCannon Visual Editor
|
|
12
|
+
* API appears.
|
|
13
|
+
*/
|
|
14
|
+
export function initHugoLiveEditing(): void;
|
|
15
|
+
|
|
16
|
+
/** Starts (or returns the in-flight start of) the WASM renderer. */
|
|
17
|
+
export function ensureEngine(): Promise<void>;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Wraps `window.cc_components` in a Proxy manufacturing a renderer for
|
|
21
|
+
* any component name on demand; partial existence is decided by the Hugo
|
|
22
|
+
* renderer at render time. Called by `initHugoLiveEditing`.
|
|
23
|
+
*/
|
|
24
|
+
export function initComponentProxy(): void;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
declare global {
|
|
28
|
+
/** Snapshot metadata emitted onto `window.cc_hugo` by the module's prelude. */
|
|
29
|
+
interface HugoRuntimeMeta {
|
|
30
|
+
generator?: string;
|
|
31
|
+
wasmUrl?: string;
|
|
32
|
+
verbose?: boolean;
|
|
33
|
+
env?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Result from the editor-site mutation entry points. */
|
|
37
|
+
interface HugoEditorResult {
|
|
38
|
+
error?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Result from `renderHugoPartials`. */
|
|
42
|
+
interface HugoRenderResult {
|
|
43
|
+
html?: string;
|
|
44
|
+
error?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The Hugo runtime's `window` doubles as the CloudCannon Visual Editor
|
|
49
|
+
* window and carries the snapshot globals emitted by the module's prelude.
|
|
50
|
+
*/
|
|
51
|
+
interface Window {
|
|
52
|
+
/** CloudCannon's versioned API router (present inside the Visual Editor). */
|
|
53
|
+
CloudCannonAPI?: CloudCannonVisualEditorAPIRouter;
|
|
54
|
+
/** The installed v0/v1 CloudCannon API for this page. */
|
|
55
|
+
CloudCannon?: CloudCannonVisualEditorAPIV0 | CloudCannonVisualEditorAPIV1;
|
|
56
|
+
/** Emitter metadata: generator, wasmUrl, verbose, env. */
|
|
57
|
+
cc_hugo?: HugoRuntimeMeta;
|
|
58
|
+
/** Template/config snapshot keyed by physical path. */
|
|
59
|
+
cc_hugo_files?: Record<string, string>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// The Hugo WASM renderer exposes these functions on `globalThis` once it
|
|
63
|
+
// boots; the runtime calls them directly after the engine is ready.
|
|
64
|
+
function writeHugoFiles(json: string): HugoEditorResult | null;
|
|
65
|
+
function removeHugoFiles(json: string): HugoEditorResult | null;
|
|
66
|
+
function readHugoFiles(json: string): Record<string, string>;
|
|
67
|
+
function initHugoEditorSite(): HugoEditorResult | null;
|
|
68
|
+
function renderHugoPartials(json: string): HugoRenderResult | null;
|
|
69
|
+
}
|
package/types/liquid.d.ts
CHANGED
|
@@ -31,7 +31,6 @@ declare module "@cloudcannon/editable-regions/liquid" {
|
|
|
31
31
|
name: string,
|
|
32
32
|
factory: (liquidEngine: Liquid) => any,
|
|
33
33
|
): void;
|
|
34
|
-
export function registerProcessEnv(env: Record<string, string>): void;
|
|
35
34
|
export function registerEleventyData(data: {
|
|
36
35
|
version: string;
|
|
37
36
|
generator: string;
|