@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
|
@@ -1,20 +1,30 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Liquid } from "liquidjs";
|
|
2
|
+
import { enhanceLiquidError } from "./errors.mjs";
|
|
3
3
|
import { inMemoryFs } from "./fs.mjs";
|
|
4
|
+
import {
|
|
5
|
+
buildCollectionsData,
|
|
6
|
+
buildPageData,
|
|
7
|
+
setEleventyData,
|
|
8
|
+
} from "./globals.mjs";
|
|
9
|
+
import { createIncludeWithTag } from "./include-with-tag.mjs";
|
|
4
10
|
import { group, groupEnd, log } from "./logger.mjs";
|
|
5
11
|
import { createPairedShortcodeTag, createShortcodeTag } from "./shortcodes.mjs";
|
|
6
12
|
|
|
7
|
-
// Re-
|
|
13
|
+
// Re-exported from their own modules (which avoid this file's browser-runtime
|
|
14
|
+
// side-effects) so the Node-side plugin can reach them via the package root.
|
|
15
|
+
export { createIncludeWithTag } from "./include-with-tag.mjs";
|
|
8
16
|
export { group, groupEnd, log, setVerbose } from "./logger.mjs";
|
|
17
|
+
export { registerPageMap } from "./page-map.mjs";
|
|
9
18
|
|
|
10
19
|
/** @type {import("liquidjs").Liquid | null} */
|
|
11
20
|
let sharedLiquidEngine = null;
|
|
12
21
|
|
|
13
22
|
/**
|
|
14
|
-
* Creates
|
|
23
|
+
* Creates the shared Liquid engine with the built-in `includeWith` tag. The
|
|
24
|
+
* host wires up its filters/shortcodes/ports afterwards (e.g.
|
|
25
|
+
* `registerEleventyBuiltins(engine)`).
|
|
15
26
|
*
|
|
16
|
-
* @param {
|
|
17
|
-
* @returns {void}
|
|
27
|
+
* @param {import("liquidjs").LiquidOptions} [options] - Spread into `new Liquid(...)`
|
|
18
28
|
*/
|
|
19
29
|
export function createSharedLiquidEngine(options) {
|
|
20
30
|
log("Creating shared Liquid engine");
|
|
@@ -26,141 +36,178 @@ export function createSharedLiquidEngine(options) {
|
|
|
26
36
|
},
|
|
27
37
|
...options,
|
|
28
38
|
});
|
|
29
|
-
log("Liquid engine instantiated");
|
|
30
|
-
|
|
31
|
-
// Register Eleventy's built-in filters
|
|
32
|
-
for (const [name, fn] of Object.entries(eleventyFilters)) {
|
|
33
|
-
sharedLiquidEngine.registerFilter(name, fn);
|
|
34
|
-
}
|
|
35
|
-
log(
|
|
36
|
-
"Registered",
|
|
37
|
-
Object.keys(eleventyFilters).length,
|
|
38
|
-
"built-in 11ty filters",
|
|
39
|
-
);
|
|
40
39
|
|
|
41
40
|
log(
|
|
42
|
-
"Available files in window.
|
|
43
|
-
Object.keys(window.
|
|
41
|
+
"Available files in window.cc_liquid_files:",
|
|
42
|
+
Object.keys(window.cc_liquid_files || {}),
|
|
44
43
|
);
|
|
45
44
|
|
|
46
45
|
sharedLiquidEngine.registerTag(
|
|
47
|
-
"
|
|
48
|
-
|
|
46
|
+
"includeWith",
|
|
47
|
+
createIncludeWithTag(sharedLiquidEngine),
|
|
49
48
|
);
|
|
50
|
-
|
|
49
|
+
|
|
50
|
+
return sharedLiquidEngine;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
|
-
*
|
|
55
|
-
*
|
|
54
|
+
* Pins a Liquid component under `key`, taking precedence over the
|
|
55
|
+
* include-resolution proxy. Used for `pluginOptions.liquid.components`.
|
|
56
56
|
*
|
|
57
|
-
* @param {string} key
|
|
58
|
-
* @param {string} contents
|
|
59
|
-
* @returns {void}
|
|
57
|
+
* @param {string} key
|
|
58
|
+
* @param {string} contents
|
|
60
59
|
*/
|
|
61
60
|
export function registerLiquidComponent(key, contents) {
|
|
62
61
|
log("Registering component:", key);
|
|
63
|
-
log("Component contents preview:", contents?.substring?.(0, 200) || contents);
|
|
64
62
|
|
|
65
63
|
if (!sharedLiquidEngine) {
|
|
66
64
|
throw new Error(
|
|
67
65
|
`sharedLiquidEngine not defined when registering component ${key}`,
|
|
68
66
|
);
|
|
69
67
|
}
|
|
70
|
-
const liquidEngine = sharedLiquidEngine;
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Wrapper function that renders the Liquid component to an HTMLElement.
|
|
74
|
-
*
|
|
75
|
-
* @param {Object} props - Props to pass to the Liquid template
|
|
76
|
-
* @returns {Promise<HTMLElement>} The rendered component as an HTMLElement
|
|
77
|
-
*/
|
|
78
|
-
const wrappedComponent = async (props) => {
|
|
79
|
-
group(`Rendering component: ${key}`);
|
|
80
|
-
log("Props:", props);
|
|
81
|
-
log("Parsing and rendering template...");
|
|
82
|
-
const htmlString = await liquidEngine.parseAndRender(contents, props);
|
|
83
|
-
log(
|
|
84
|
-
"Rendered HTML preview:",
|
|
85
|
-
htmlString?.substring?.(0, 200) || htmlString,
|
|
86
|
-
);
|
|
87
|
-
const rootEl = document.createElement("div");
|
|
88
|
-
rootEl.innerHTML = htmlString;
|
|
89
|
-
groupEnd();
|
|
90
|
-
return rootEl;
|
|
91
|
-
};
|
|
92
68
|
|
|
93
69
|
window.cc_components = window.cc_components || {};
|
|
94
|
-
window.cc_components[key] =
|
|
95
|
-
log(`Component registered, ${key}`);
|
|
70
|
+
window.cc_components[key] = createComponentRenderer(key, contents);
|
|
96
71
|
}
|
|
97
72
|
|
|
98
73
|
/**
|
|
99
|
-
*
|
|
74
|
+
* Wraps `window.cc_components` in a Proxy that resolves any component name on
|
|
75
|
+
* demand via `{% include %}` — the primary resolution path. Names registered
|
|
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.
|
|
100
81
|
*
|
|
101
|
-
* @param {string} name - The filter name
|
|
102
|
-
* @param {any} fn - The filter function
|
|
103
82
|
* @returns {void}
|
|
104
83
|
*/
|
|
105
|
-
export function
|
|
84
|
+
export function initComponentProxy() {
|
|
85
|
+
if (!sharedLiquidEngine) {
|
|
86
|
+
throw new Error(
|
|
87
|
+
"sharedLiquidEngine not defined when initializing component proxy",
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const target = window.cc_components || {};
|
|
92
|
+
|
|
93
|
+
window.cc_components = new Proxy(target, {
|
|
94
|
+
get(registered, key, receiver) {
|
|
95
|
+
if (Reflect.has(registered, key)) {
|
|
96
|
+
return Reflect.get(registered, key, receiver);
|
|
97
|
+
}
|
|
98
|
+
if (typeof key === "string") {
|
|
99
|
+
return createComponentRenderer(key, `{% include "${key}" %}`);
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Sets the `eleventy` global on the shared engine. Built at build time by the
|
|
108
|
+
* bundle generator; this is a thin setter.
|
|
109
|
+
*
|
|
110
|
+
* @param {{version: string, generator: string, env: {runMode: string, source: string}, directories: Record<string, string>}} data
|
|
111
|
+
*/
|
|
112
|
+
export function registerEleventyData(data) {
|
|
113
|
+
if (!sharedLiquidEngine) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
"sharedLiquidEngine not defined when registering eleventy data",
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
/** @type {any} */ (sharedLiquidEngine).options.globals.eleventy = data;
|
|
119
|
+
// Also surface to `globals` so the page proxy can derive `outputPath`.
|
|
120
|
+
setEleventyData(data);
|
|
121
|
+
log("Registered eleventy data, version:", data?.version);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Merges user-supplied globals (`pluginOptions.globals`) onto the engine. The
|
|
126
|
+
* built-in globals (`page`, `collections`, `eleventy`, `pkg`) are applied
|
|
127
|
+
* separately and take precedence per render.
|
|
128
|
+
*
|
|
129
|
+
* @param {Record<string, unknown>} globals
|
|
130
|
+
*/
|
|
131
|
+
export function registerGlobals(globals) {
|
|
132
|
+
if (!sharedLiquidEngine) {
|
|
133
|
+
throw new Error("sharedLiquidEngine not defined when registering globals");
|
|
134
|
+
}
|
|
135
|
+
Object.assign(
|
|
136
|
+
/** @type {any} */ (sharedLiquidEngine).options.globals,
|
|
137
|
+
globals ?? {},
|
|
138
|
+
);
|
|
139
|
+
log("Registered", Object.keys(globals ?? {}).length, "custom globals");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Sets the `pkg` global (11ty exposes `package.json` this way by default).
|
|
144
|
+
*
|
|
145
|
+
* @param {Record<string, any>} pkg
|
|
146
|
+
*/
|
|
147
|
+
export function registerPkg(pkg) {
|
|
148
|
+
if (!sharedLiquidEngine) {
|
|
149
|
+
throw new Error("sharedLiquidEngine not defined when registering pkg");
|
|
150
|
+
}
|
|
151
|
+
/** @type {any} */ (sharedLiquidEngine).options.globals.pkg = pkg ?? {};
|
|
152
|
+
log("Registered pkg, fields:", Object.keys(pkg ?? {}).length);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Registers a Liquid filter. Called by both the auto-mirror pass and
|
|
157
|
+
* user-supplied overrides (`pluginOptions.liquid.filters`).
|
|
158
|
+
*
|
|
159
|
+
* @param {string} name
|
|
160
|
+
* @param {any} fn
|
|
161
|
+
*/
|
|
162
|
+
export function registerFilter(name, fn) {
|
|
106
163
|
log("Registering filter:", name);
|
|
107
164
|
if (!sharedLiquidEngine) {
|
|
108
165
|
throw new Error(
|
|
109
|
-
`sharedLiquidEngine not defined when registering
|
|
166
|
+
`sharedLiquidEngine not defined when registering filter ${name}`,
|
|
110
167
|
);
|
|
111
168
|
}
|
|
112
169
|
sharedLiquidEngine.registerFilter(name, fn);
|
|
113
170
|
}
|
|
114
171
|
|
|
115
172
|
/**
|
|
116
|
-
* Registers a
|
|
173
|
+
* Registers a Liquid shortcode. Usage: {% shortcodeName arg1, arg2 %}
|
|
117
174
|
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* @param {string} name - The shortcode name (used as the tag name)
|
|
121
|
-
* @param {any} fn - The shortcode function (arg1, arg2, ...) => string
|
|
122
|
-
* @returns {void}
|
|
175
|
+
* @param {string} name
|
|
176
|
+
* @param {any} fn - (arg1, arg2, ...) => string
|
|
123
177
|
*/
|
|
124
|
-
export function
|
|
178
|
+
export function registerShortcode(name, fn) {
|
|
125
179
|
log("Registering shortcode:", name);
|
|
126
180
|
if (!sharedLiquidEngine) {
|
|
127
181
|
throw new Error(
|
|
128
|
-
`sharedLiquidEngine not defined when registering
|
|
182
|
+
`sharedLiquidEngine not defined when registering shortcode ${name}`,
|
|
129
183
|
);
|
|
130
184
|
}
|
|
131
|
-
sharedLiquidEngine.registerTag(name, createShortcodeTag(
|
|
185
|
+
sharedLiquidEngine.registerTag(name, createShortcodeTag(name, fn));
|
|
132
186
|
}
|
|
133
187
|
|
|
134
188
|
/**
|
|
135
|
-
* Registers a
|
|
136
|
-
*
|
|
137
|
-
* Usage in templates: {% shortcodeName arg %}content{% endshortcodeName %}
|
|
189
|
+
* Registers a Liquid paired shortcode.
|
|
190
|
+
* Usage: {% shortcodeName arg %}content{% endshortcodeName %}
|
|
138
191
|
*
|
|
139
|
-
* @param {string} name
|
|
140
|
-
* @param {any} fn -
|
|
141
|
-
* @returns {void}
|
|
192
|
+
* @param {string} name
|
|
193
|
+
* @param {any} fn - (content, arg1, ...) => string
|
|
142
194
|
*/
|
|
143
|
-
export function
|
|
195
|
+
export function registerPairedShortcode(name, fn) {
|
|
144
196
|
log("Registering paired shortcode:", name);
|
|
145
197
|
if (!sharedLiquidEngine) {
|
|
146
198
|
throw new Error(
|
|
147
|
-
`sharedLiquidEngine not defined when registering
|
|
199
|
+
`sharedLiquidEngine not defined when registering paired shortcode ${name}`,
|
|
148
200
|
);
|
|
149
201
|
}
|
|
150
202
|
sharedLiquidEngine.registerTag(name, createPairedShortcodeTag(name, fn));
|
|
151
203
|
}
|
|
152
204
|
|
|
153
205
|
/**
|
|
154
|
-
* Registers a custom tag with full LiquidJS parser access
|
|
155
|
-
*
|
|
156
|
-
* Custom tags are more powerful than shortcodes - they receive full access to
|
|
157
|
-
* the LiquidJS parser and can implement complex parsing/rendering logic.
|
|
158
|
-
*
|
|
159
|
-
* Usage in templates: {% tagName args %}
|
|
206
|
+
* Registers a custom tag with full LiquidJS parser access (the factory
|
|
207
|
+
* receives the engine). Usage: {% tagName args %}
|
|
160
208
|
*
|
|
161
|
-
* @param {string} name
|
|
162
|
-
* @param {any} factory -
|
|
163
|
-
* @returns {void}
|
|
209
|
+
* @param {string} name
|
|
210
|
+
* @param {any} factory - (liquidEngine) => { parse(), render() }
|
|
164
211
|
*/
|
|
165
212
|
export function registerCustomTag(name, factory) {
|
|
166
213
|
log("Registering custom tag:", name);
|
|
@@ -173,97 +220,46 @@ export function registerCustomTag(name, factory) {
|
|
|
173
220
|
}
|
|
174
221
|
|
|
175
222
|
/**
|
|
176
|
-
*
|
|
177
|
-
* Like Astro's {...props} spread for Liquid includes.
|
|
178
|
-
*
|
|
179
|
-
* Usage: {% bind_include "path/to/partial", objectToSpread %}
|
|
223
|
+
* Wraps `parseAndRender` with logging, error mapping, and HTMLElement output.
|
|
180
224
|
*
|
|
181
|
-
* @param {
|
|
182
|
-
* @
|
|
225
|
+
* @param {string} name
|
|
226
|
+
* @param {string} templateSource - A literal template, or `{% include "name" %}`
|
|
227
|
+
* @returns {(props: Record<string, any>) => Promise<HTMLElement>}
|
|
183
228
|
*/
|
|
184
|
-
|
|
185
|
-
return {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
*/
|
|
190
|
-
parse(tagToken) {
|
|
191
|
-
log("bind_include parsing tag with args:", tagToken.args);
|
|
192
|
-
const tokenizer = new Tokenizer(
|
|
193
|
-
tagToken.args,
|
|
194
|
-
this.liquid.options.operatorsTrie,
|
|
229
|
+
function createComponentRenderer(name, templateSource) {
|
|
230
|
+
return async (props) => {
|
|
231
|
+
if (!sharedLiquidEngine) {
|
|
232
|
+
throw new Error(
|
|
233
|
+
`sharedLiquidEngine not defined when rendering component ${name}`,
|
|
195
234
|
);
|
|
235
|
+
}
|
|
236
|
+
group(`Rendering component: ${name}`);
|
|
237
|
+
log("Props:", props);
|
|
196
238
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
this.objectToken = tokenizer.readValue();
|
|
209
|
-
if (!this.objectToken)
|
|
210
|
-
throw new Error("bind_include: missing object argument");
|
|
211
|
-
log("bind_include parsed object token:", this.objectToken);
|
|
212
|
-
},
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* Renders the included template with spread props.
|
|
216
|
-
* @param {any} context - The LiquidJS render context
|
|
217
|
-
*/
|
|
218
|
-
async render(context) {
|
|
219
|
-
group("bind_include rendering");
|
|
220
|
-
log("Evaluating path token...");
|
|
221
|
-
const path = await toPromise(evalToken(this.pathToken, context));
|
|
222
|
-
log("Path resolved to:", path);
|
|
223
|
-
|
|
224
|
-
log("Evaluating object token...");
|
|
225
|
-
const obj = await toPromise(evalToken(this.objectToken, context));
|
|
226
|
-
log("Object resolved to:", obj);
|
|
227
|
-
|
|
228
|
-
if (!path || typeof path !== "string") {
|
|
229
|
-
groupEnd();
|
|
230
|
-
throw new Error(`bind_include: invalid path "${path}"`);
|
|
231
|
-
}
|
|
232
|
-
if (!obj || typeof obj !== "object") {
|
|
233
|
-
log("Object is not valid, returning empty");
|
|
234
|
-
groupEnd();
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
log(
|
|
239
|
-
"Including:",
|
|
240
|
-
path,
|
|
241
|
-
"with",
|
|
242
|
-
Object.keys(obj).length,
|
|
243
|
-
"props:",
|
|
244
|
-
Object.keys(obj),
|
|
239
|
+
let htmlString;
|
|
240
|
+
try {
|
|
241
|
+
htmlString = await sharedLiquidEngine.parseAndRender(
|
|
242
|
+
templateSource,
|
|
243
|
+
// `page`/`collections` spread last so props can't shadow them
|
|
244
|
+
// (mirroring 11ty); they resolve at the top-level globals level.
|
|
245
|
+
{
|
|
246
|
+
...props,
|
|
247
|
+
page: buildPageData(),
|
|
248
|
+
collections: buildCollectionsData(),
|
|
249
|
+
},
|
|
245
250
|
);
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const error = /** @type {Error} */ (err);
|
|
260
|
-
log("Error during render:", error.message);
|
|
261
|
-
log("Full error:", error);
|
|
262
|
-
groupEnd();
|
|
263
|
-
throw error;
|
|
264
|
-
} finally {
|
|
265
|
-
context.pop();
|
|
266
|
-
}
|
|
267
|
-
},
|
|
251
|
+
} catch (err) {
|
|
252
|
+
log("Error during render:", err);
|
|
253
|
+
groupEnd();
|
|
254
|
+
throw enhanceLiquidError(err, name);
|
|
255
|
+
}
|
|
256
|
+
log(
|
|
257
|
+
"Rendered HTML preview:",
|
|
258
|
+
htmlString?.substring?.(0, 200) || htmlString,
|
|
259
|
+
);
|
|
260
|
+
const rootEl = document.createElement("div");
|
|
261
|
+
rootEl.innerHTML = htmlString;
|
|
262
|
+
groupEnd();
|
|
263
|
+
return rootEl;
|
|
268
264
|
};
|
|
269
265
|
}
|
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
* Simple logger for live editing integration.
|
|
3
|
-
* Enable verbose mode to see detailed logs in browser console.
|
|
4
|
-
*/
|
|
1
|
+
// Logging is gated on verbose mode, except `warn`/`warnOnce`.
|
|
5
2
|
|
|
6
3
|
let verboseEnabled = false;
|
|
7
4
|
|
|
8
|
-
/**
|
|
9
|
-
* Enables or disables verbose logging.
|
|
10
|
-
*
|
|
11
|
-
* @param {boolean} enabled - Whether to enable verbose logging
|
|
12
|
-
* @returns {void}
|
|
13
|
-
*/
|
|
5
|
+
/** @param {boolean} enabled */
|
|
14
6
|
export function setVerbose(enabled) {
|
|
15
7
|
verboseEnabled = enabled;
|
|
16
8
|
if (enabled) {
|
|
@@ -18,91 +10,36 @@ export function setVerbose(enabled) {
|
|
|
18
10
|
}
|
|
19
11
|
}
|
|
20
12
|
|
|
21
|
-
/**
|
|
22
|
-
* Returns whether verbose logging is enabled.
|
|
23
|
-
*
|
|
24
|
-
* @returns {boolean}
|
|
25
|
-
*/
|
|
26
|
-
export function isVerbose() {
|
|
27
|
-
return verboseEnabled;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Log only when verbose mode is enabled.
|
|
32
|
-
* Use for diagnostic information during development.
|
|
33
|
-
*
|
|
34
|
-
* @param {...any} args - Arguments to log
|
|
35
|
-
* @returns {void}
|
|
36
|
-
*/
|
|
37
|
-
export function log(...args) {
|
|
13
|
+
export function log(/** @type {any[]} */ ...args) {
|
|
38
14
|
if (verboseEnabled) {
|
|
39
15
|
console.log(...args);
|
|
40
16
|
}
|
|
41
17
|
}
|
|
42
18
|
|
|
43
|
-
/**
|
|
44
|
-
* Always log warnings.
|
|
45
|
-
*
|
|
46
|
-
* @param {...any} args - Arguments to log
|
|
47
|
-
* @returns {void}
|
|
48
|
-
*/
|
|
49
|
-
export function warn(...args) {
|
|
19
|
+
export function warn(/** @type {any[]} */ ...args) {
|
|
50
20
|
console.warn(...args);
|
|
51
21
|
}
|
|
52
22
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
*/
|
|
59
|
-
|
|
60
|
-
|
|
23
|
+
const warnedKeys = new Set();
|
|
24
|
+
|
|
25
|
+
/** Warns once per key for the lifetime of the page. */
|
|
26
|
+
export function warnOnce(
|
|
27
|
+
/** @type {string} */ key,
|
|
28
|
+
/** @type {any[]} */ ...args
|
|
29
|
+
) {
|
|
30
|
+
if (warnedKeys.has(key)) return;
|
|
31
|
+
warnedKeys.add(key);
|
|
32
|
+
warn(...args);
|
|
61
33
|
}
|
|
62
34
|
|
|
63
|
-
/**
|
|
64
|
-
* Group logs (only in verbose mode).
|
|
65
|
-
*
|
|
66
|
-
* @param {string} label - Group label
|
|
67
|
-
* @returns {void}
|
|
68
|
-
*/
|
|
69
|
-
export function group(label) {
|
|
35
|
+
export function group(/** @type {string} */ label) {
|
|
70
36
|
if (verboseEnabled) {
|
|
71
37
|
console.group(label);
|
|
72
38
|
}
|
|
73
39
|
}
|
|
74
40
|
|
|
75
|
-
/**
|
|
76
|
-
* End a console group (only in verbose mode).
|
|
77
|
-
*
|
|
78
|
-
* @returns {void}
|
|
79
|
-
*/
|
|
80
41
|
export function groupEnd() {
|
|
81
42
|
if (verboseEnabled) {
|
|
82
43
|
console.groupEnd();
|
|
83
44
|
}
|
|
84
45
|
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Start timing an operation (only in verbose mode).
|
|
88
|
-
*
|
|
89
|
-
* @param {string} label - Timer label
|
|
90
|
-
* @returns {void}
|
|
91
|
-
*/
|
|
92
|
-
export function time(label) {
|
|
93
|
-
if (verboseEnabled) {
|
|
94
|
-
console.time(label);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* End timing an operation (only in verbose mode).
|
|
100
|
-
*
|
|
101
|
-
* @param {string} label - Timer label
|
|
102
|
-
* @returns {void}
|
|
103
|
-
*/
|
|
104
|
-
export function timeEnd(label) {
|
|
105
|
-
if (verboseEnabled) {
|
|
106
|
-
console.timeEnd(label);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build-time snapshot of every page Eleventy produced, keyed by normalized
|
|
3
|
+
* input path. Holds permalinks computed by JS config or `eleventyComputed`,
|
|
4
|
+
* which the live front-matter read can't see. Lives in its own module so
|
|
5
|
+
* consumers can import it without the side-effects of `helpers/cloudcannon.mjs`.
|
|
6
|
+
*
|
|
7
|
+
* @typedef {{ url?: string, outputPath?: string }} PageMapEntry
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/** @type {Record<string, PageMapEntry>} */
|
|
11
|
+
let pageMap = {};
|
|
12
|
+
|
|
13
|
+
/** @param {Record<string, PageMapEntry> | null | undefined} map */
|
|
14
|
+
export function registerPageMap(map) {
|
|
15
|
+
pageMap = map ?? {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function getPageMap() {
|
|
19
|
+
return pageMap;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Normalises an Eleventy input path (`./src/foo.md`, `/src/foo.md`) to the
|
|
24
|
+
* no-leading-slash form used as the map's keys, so paths from different
|
|
25
|
+
* sources (11ty `results`, CC `currentFile().path`) compare equal.
|
|
26
|
+
*
|
|
27
|
+
* @param {string | null | undefined} p
|
|
28
|
+
*/
|
|
29
|
+
export function normalizeInputPath(p) {
|
|
30
|
+
if (typeof p !== "string" || !p) return "";
|
|
31
|
+
return p.replace(/^\.\//, "").replace(/^\/+/, "");
|
|
32
|
+
}
|