@caelo-cms/shared 0.10.72 → 0.11.0
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/dist/ai-tools.d.ts +125 -50
- package/dist/ai-tools.d.ts.map +1 -1
- package/dist/ai-tools.js +48 -36
- package/dist/ai-tools.js.map +1 -1
- package/dist/build-page.d.ts +110 -30
- package/dist/build-page.d.ts.map +1 -1
- package/dist/build-page.js +23 -4
- package/dist/build-page.js.map +1 -1
- package/dist/content.d.ts +201 -100
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +176 -74
- package/dist/content.js.map +1 -1
- package/dist/css-var-aliases.d.ts +20 -0
- package/dist/css-var-aliases.d.ts.map +1 -0
- package/dist/css-var-aliases.js +95 -0
- package/dist/css-var-aliases.js.map +1 -0
- package/dist/css-var-scan.d.ts +0 -16
- package/dist/css-var-scan.d.ts.map +1 -1
- package/dist/css-var-scan.js +24 -0
- package/dist/css-var-scan.js.map +1 -1
- package/dist/edit-markers.d.ts +41 -5
- package/dist/edit-markers.d.ts.map +1 -1
- package/dist/edit-markers.js +47 -50
- package/dist/edit-markers.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/interactions.js +2 -2
- package/dist/interactions.js.map +1 -1
- package/dist/link-canonicalise.d.ts.map +1 -1
- package/dist/link-canonicalise.js +7 -1
- package/dist/link-canonicalise.js.map +1 -1
- package/dist/link-refs.d.ts +1 -1
- package/dist/link-refs.js +3 -3
- package/dist/lists/address.d.ts +34 -0
- package/dist/lists/address.d.ts.map +1 -0
- package/dist/lists/address.js +43 -0
- package/dist/lists/address.js.map +1 -0
- package/dist/lists/default-types.d.ts +10 -0
- package/dist/lists/default-types.d.ts.map +1 -0
- package/dist/lists/default-types.js +91 -0
- package/dist/lists/default-types.js.map +1 -0
- package/dist/lists/fields.d.ts +152 -0
- package/dist/lists/fields.d.ts.map +1 -0
- package/dist/lists/fields.js +258 -0
- package/dist/lists/fields.js.map +1 -0
- package/dist/lists/index.d.ts +14 -0
- package/dist/lists/index.d.ts.map +1 -0
- package/dist/lists/index.js +15 -0
- package/dist/lists/index.js.map +1 -0
- package/dist/lists/item-attr.d.ts +26 -0
- package/dist/lists/item-attr.d.ts.map +1 -0
- package/dist/lists/item-attr.js +31 -0
- package/dist/lists/item-attr.js.map +1 -0
- package/dist/lists/items.d.ts +105 -0
- package/dist/lists/items.d.ts.map +1 -0
- package/dist/lists/items.js +304 -0
- package/dist/lists/items.js.map +1 -0
- package/dist/lists/list-type.d.ts +401 -0
- package/dist/lists/list-type.d.ts.map +1 -0
- package/dist/lists/list-type.js +275 -0
- package/dist/lists/list-type.js.map +1 -0
- package/dist/lists/model.d.ts +14 -0
- package/dist/lists/model.d.ts.map +1 -0
- package/dist/lists/model.js +15 -0
- package/dist/lists/model.js.map +1 -0
- package/dist/lists/page-type.d.ts +115 -0
- package/dist/lists/page-type.d.ts.map +1 -0
- package/dist/lists/page-type.js +66 -0
- package/dist/lists/page-type.js.map +1 -0
- package/dist/lists/refs.d.ts +13 -0
- package/dist/lists/refs.d.ts.map +1 -0
- package/dist/lists/refs.js +18 -0
- package/dist/lists/refs.js.map +1 -0
- package/dist/lists/render-data.d.ts +114 -0
- package/dist/lists/render-data.d.ts.map +1 -0
- package/dist/lists/render-data.js +3 -0
- package/dist/lists/render-data.js.map +1 -0
- package/dist/lists/render-deps.d.ts +68 -0
- package/dist/lists/render-deps.d.ts.map +1 -0
- package/dist/lists/render-deps.js +91 -0
- package/dist/lists/render-deps.js.map +1 -0
- package/dist/lists/resolve-values.d.ts +63 -0
- package/dist/lists/resolve-values.d.ts.map +1 -0
- package/dist/lists/resolve-values.js +181 -0
- package/dist/lists/resolve-values.js.map +1 -0
- package/dist/lists/resolve.d.ts +106 -0
- package/dist/lists/resolve.d.ts.map +1 -0
- package/dist/lists/resolve.js +878 -0
- package/dist/lists/resolve.js.map +1 -0
- package/dist/lists/scope.d.ts +81 -0
- package/dist/lists/scope.d.ts.map +1 -0
- package/dist/lists/scope.js +3 -0
- package/dist/lists/scope.js.map +1 -0
- package/dist/markup-structure.d.ts +8 -0
- package/dist/markup-structure.d.ts.map +1 -1
- package/dist/markup-structure.js +2 -0
- package/dist/markup-structure.js.map +1 -1
- package/dist/media.d.ts +1 -1
- package/dist/nested-module-render.d.ts +25 -20
- package/dist/nested-module-render.d.ts.map +1 -1
- package/dist/nested-module-render.js +122 -31
- package/dist/nested-module-render.js.map +1 -1
- package/dist/page-log.d.ts +1 -1
- package/dist/preview-compose.d.ts +24 -21
- package/dist/preview-compose.d.ts.map +1 -1
- package/dist/preview-compose.js +34 -118
- package/dist/preview-compose.js.map +1 -1
- package/dist/source-text-transport.d.ts +17 -0
- package/dist/source-text-transport.d.ts.map +1 -0
- package/dist/source-text-transport.js +71 -0
- package/dist/source-text-transport.js.map +1 -0
- package/dist/template-engine.d.ts +44 -31
- package/dist/template-engine.d.ts.map +1 -1
- package/dist/template-engine.js +75 -174
- package/dist/template-engine.js.map +1 -1
- package/dist/template-sections.d.ts +69 -0
- package/dist/template-sections.d.ts.map +1 -0
- package/dist/template-sections.js +423 -0
- package/dist/template-sections.js.map +1 -0
- package/dist/version.d.ts +2 -2
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +8 -1
- package/src/__tests__/redos-hardening.test.ts +50 -16
- package/src/ai-tools-add-module-modes.test.ts +50 -0
- package/src/ai-tools.test.ts +10 -0
- package/src/ai-tools.ts +51 -37
- package/src/build-page.test.ts +34 -3
- package/src/build-page.ts +26 -3
- package/src/content.test.ts +111 -3
- package/src/content.ts +186 -75
- package/src/css-var-aliases.test.ts +61 -0
- package/src/css-var-aliases.ts +109 -0
- package/src/css-var-scan.ts +9 -0
- package/src/edit-markers.test.ts +37 -21
- package/src/edit-markers.ts +52 -20
- package/src/index.ts +3 -1
- package/src/interactions.ts +2 -2
- package/src/link-canonicalise.test.ts +36 -6
- package/src/link-canonicalise.ts +6 -1
- package/src/link-refs.test.ts +66 -42
- package/src/link-refs.ts +3 -3
- package/src/lists/address.ts +75 -0
- package/src/lists/default-types.ts +102 -0
- package/src/lists/fields.ts +285 -0
- package/src/lists/index.ts +16 -0
- package/src/lists/item-attr.test.ts +36 -0
- package/src/lists/item-attr.ts +49 -0
- package/src/lists/items.ts +402 -0
- package/src/lists/list-fixtures.test.ts +302 -0
- package/src/lists/list-type.ts +306 -0
- package/src/lists/lists.test.ts +328 -0
- package/src/lists/model.ts +16 -0
- package/src/lists/page-type.ts +71 -0
- package/src/lists/refs.ts +21 -0
- package/src/lists/render-data.ts +125 -0
- package/src/lists/render-deps.test.ts +132 -0
- package/src/lists/render-deps.ts +144 -0
- package/src/lists/resolve-values.ts +224 -0
- package/src/lists/resolve.test.ts +295 -0
- package/src/lists/resolve.ts +1148 -0
- package/src/lists/scope.ts +69 -0
- package/src/lists-compose-parity.test.ts +262 -0
- package/src/markup-structure.ts +10 -0
- package/src/nested-module-render.test.ts +111 -77
- package/src/nested-module-render.ts +85 -48
- package/src/preview-compose.test.ts +55 -19
- package/src/preview-compose.ts +59 -154
- package/src/source-text-transport.test.ts +63 -0
- package/src/source-text-transport.ts +70 -0
- package/src/template-engine-lists.test.ts +553 -0
- package/src/template-engine.test.ts +62 -80
- package/src/template-engine.ts +102 -245
- package/src/template-sections.ts +500 -0
- package/src/version.ts +1 -1
- package/dist/structured-sets.d.ts +0 -70
- package/dist/structured-sets.d.ts.map +0 -1
- package/dist/structured-sets.js +0 -92
- package/dist/structured-sets.js.map +0 -1
- package/src/structured-sets.ts +0 -114
- package/src/template-engine-data-lists.test.ts +0 -90
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import { describe, expect, it } from "bun:test";
|
|
18
|
+
import { fixtureData } from "./lists/list-fixtures.test.js";
|
|
19
|
+
import { createListRenderContext } from "./lists/resolve.js";
|
|
18
20
|
import { renderTemplate, type TemplateField } from "./template-engine.js";
|
|
19
21
|
|
|
20
22
|
describe("renderTemplate — primitive substitution", () => {
|
|
@@ -89,96 +91,65 @@ describe("renderTemplate — primitive substitution", () => {
|
|
|
89
91
|
});
|
|
90
92
|
});
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
// A `list` field over the shipped `texts` / `links` types (epic #1107);
|
|
95
|
+
// the full list syntax lives in template-engine-lists.test.ts.
|
|
96
|
+
const lists = createListRenderContext(fixtureData()).forPage(null);
|
|
97
|
+
const uid = (n: number) => `00000000-0000-4000-9000-${String(n).padStart(12, "0")}`;
|
|
98
|
+
const texts = (...t: string[]) =>
|
|
99
|
+
t.map((text, i) => ({ id: uid(i + 1), node: "text", values: { text } }));
|
|
100
|
+
const unstamp = (html: string) => html.replace(/ data-caelo-list-item="[^"]*"/g, "");
|
|
94
101
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
html: "<ul>{{#items}}<li>{{.}}</li>{{/items}}</ul>",
|
|
98
|
-
fields: [textListField],
|
|
99
|
-
contentValues: { items: ["a", "b", "c"] },
|
|
100
|
-
});
|
|
101
|
-
expect(r.html).toBe("<ul><li>a</li><li>b</li><li>c</li></ul>");
|
|
102
|
-
expect(r.missingSlots).toEqual([]);
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it("iterates with {{item}} alias per element", () => {
|
|
106
|
-
const r = renderTemplate({
|
|
107
|
-
html: "<ul>{{#items}}<li>{{item}}</li>{{/items}}</ul>",
|
|
108
|
-
fields: [textListField],
|
|
109
|
-
contentValues: { items: ["a", "b", "c"] },
|
|
110
|
-
});
|
|
111
|
-
expect(r.html).toBe("<ul><li>a</li><li>b</li><li>c</li></ul>");
|
|
112
|
-
});
|
|
102
|
+
describe("renderTemplate — list section", () => {
|
|
103
|
+
const listField: TemplateField = { name: "items", kind: "list", listType: "texts" };
|
|
113
104
|
|
|
114
|
-
it("
|
|
105
|
+
it("iterates a local list per entry", () => {
|
|
115
106
|
const r = renderTemplate({
|
|
116
|
-
html: "<ul>{{#items}}<li>{{
|
|
117
|
-
fields: [
|
|
118
|
-
contentValues: { items:
|
|
107
|
+
html: "<ul>{{#items}}<li>{{text}}</li>{{/items}}</ul>",
|
|
108
|
+
fields: [listField],
|
|
109
|
+
contentValues: { items: texts("a", "b", "c") },
|
|
110
|
+
lists,
|
|
119
111
|
});
|
|
120
|
-
expect(r.html).toBe("<ul><li>
|
|
112
|
+
expect(unstamp(r.html)).toBe("<ul><li>a</li><li>b</li><li>c</li></ul>");
|
|
113
|
+
expect(r.missingSlots).toEqual([]);
|
|
121
114
|
});
|
|
122
115
|
|
|
123
116
|
it("empty array renders as empty section, no missingSlots", () => {
|
|
124
117
|
const r = renderTemplate({
|
|
125
|
-
html: "<ul>{{#items}}<li>{{
|
|
126
|
-
fields: [
|
|
118
|
+
html: "<ul>{{#items}}<li>{{text}}</li>{{/items}}</ul>",
|
|
119
|
+
fields: [listField],
|
|
127
120
|
contentValues: { items: [] },
|
|
121
|
+
lists,
|
|
128
122
|
});
|
|
129
123
|
expect(r.html).toBe("<ul></ul>");
|
|
130
124
|
expect(r.missingSlots).toEqual([]);
|
|
131
125
|
});
|
|
132
126
|
|
|
133
|
-
it("
|
|
127
|
+
it("a malformed entry becomes a loud raw section + tracks list-malformed", () => {
|
|
134
128
|
const r = renderTemplate({
|
|
135
|
-
html: "<ul>{{#items}}<li>{{
|
|
136
|
-
fields: [
|
|
137
|
-
contentValues: { items: ["a", { bogus: true }
|
|
129
|
+
html: "<ul>{{#items}}<li>{{text}}</li>{{/items}}</ul>",
|
|
130
|
+
fields: [listField],
|
|
131
|
+
contentValues: { items: [...texts("a"), { bogus: true }] },
|
|
132
|
+
lists,
|
|
138
133
|
});
|
|
139
|
-
expect(r.html).
|
|
140
|
-
expect(r.
|
|
141
|
-
expect(r.html).toContain("<!-- caelo:missing reason=text-list-malformed items[1] -->");
|
|
142
|
-
expect(r.missingSlots).toContain("text-list-malformed:items[1]");
|
|
134
|
+
expect(r.html).toBe("<ul>{{#items}}<li>{{text}}</li>{{/items}}</ul>");
|
|
135
|
+
expect(r.missingSlots).toEqual(["list-malformed:items[items[1]]"]);
|
|
143
136
|
});
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
describe("renderTemplate — link-list section (AC #1)", () => {
|
|
147
|
-
const linkListField: TemplateField = { name: "nav_items", kind: "link-list" };
|
|
148
137
|
|
|
149
|
-
it("AC #1
|
|
138
|
+
it("links: label + link per entry (AC #1, list kind)", () => {
|
|
150
139
|
const r = renderTemplate({
|
|
151
|
-
html: '<nav>{{#nav_items}}<a href="{{
|
|
152
|
-
fields: [
|
|
140
|
+
html: '<nav>{{#nav_items}}<a href="{{link}}">{{label}}</a>{{/nav_items}}</nav>',
|
|
141
|
+
fields: [{ name: "nav_items", kind: "list", listType: "links" }],
|
|
153
142
|
contentValues: {
|
|
154
143
|
nav_items: [
|
|
155
|
-
{ label: "Docs", href: "/docs" },
|
|
156
|
-
{ label: "Blog", href: "/blog" },
|
|
144
|
+
{ id: uid(1), node: "link", values: { label: "Docs", link: { href: "/docs" } } },
|
|
145
|
+
{ id: uid(2), node: "link", values: { label: "Blog", link: { href: "/blog" } } },
|
|
157
146
|
],
|
|
158
147
|
},
|
|
148
|
+
lists,
|
|
159
149
|
});
|
|
160
|
-
expect(r.html).toBe('<nav><a href="/docs">Docs</a><a href="/blog">Blog</a></nav>');
|
|
161
|
-
expect(r.html).not.toMatch(/\{\{[#/]/);
|
|
162
|
-
expect(r.html).not.toMatch(/\{\{label/);
|
|
163
|
-
expect(r.html).not.toMatch(/\{\{href/);
|
|
150
|
+
expect(unstamp(r.html)).toBe('<nav><a href="/docs">Docs</a><a href="/blog">Blog</a></nav>');
|
|
164
151
|
expect(r.missingSlots).toEqual([]);
|
|
165
152
|
});
|
|
166
|
-
|
|
167
|
-
it("per-element malformed (missing href) becomes loud comment + tracks link-list-malformed", () => {
|
|
168
|
-
const r = renderTemplate({
|
|
169
|
-
html: '<nav>{{#nav_items}}<a href="{{href}}">{{label}}</a>{{/nav_items}}</nav>',
|
|
170
|
-
fields: [linkListField],
|
|
171
|
-
contentValues: {
|
|
172
|
-
nav_items: [
|
|
173
|
-
{ label: "OK", href: "/a" },
|
|
174
|
-
{ label: "OK only" }, // missing href
|
|
175
|
-
],
|
|
176
|
-
},
|
|
177
|
-
});
|
|
178
|
-
expect(r.html).toContain('<a href="/a">OK</a>');
|
|
179
|
-
expect(r.html).toContain("<!-- caelo:missing reason=link-list-malformed nav_items[1] -->");
|
|
180
|
-
expect(r.missingSlots).toContain("link-list-malformed:nav_items[1]");
|
|
181
|
-
});
|
|
182
153
|
});
|
|
183
154
|
|
|
184
155
|
describe("renderTemplate — module-list section", () => {
|
|
@@ -305,23 +276,33 @@ describe("renderTemplate — empty single module field (#1041)", () => {
|
|
|
305
276
|
});
|
|
306
277
|
|
|
307
278
|
describe("renderTemplate — literal braces in content are data (#1053)", () => {
|
|
308
|
-
it("a
|
|
279
|
+
it("a list entry keeps a literal {{Page URL}}", () => {
|
|
309
280
|
const r = renderTemplate({
|
|
310
|
-
html: "<ul>{{#items}}<li>{{
|
|
311
|
-
fields: [{ name: "items", kind: "
|
|
312
|
-
contentValues: { items:
|
|
281
|
+
html: "<ul>{{#items}}<li>{{text}}</li>{{/items}}</ul>",
|
|
282
|
+
fields: [{ name: "items", kind: "list", listType: "texts" }],
|
|
283
|
+
contentValues: { items: texts("link.href = '{{Page URL}}';", "{{title}}") },
|
|
284
|
+
lists,
|
|
313
285
|
});
|
|
314
|
-
expect(r.html).toBe("<ul><li>link.href = '{{Page URL}}';</li><li>{{title}}</li></ul>");
|
|
286
|
+
expect(unstamp(r.html)).toBe("<ul><li>link.href = '{{Page URL}}';</li><li>{{title}}</li></ul>");
|
|
315
287
|
expect(r.missingSlots).toEqual([]);
|
|
316
288
|
});
|
|
317
289
|
|
|
318
|
-
it("a
|
|
290
|
+
it("a list entry's label is never re-substituted as a placeholder", () => {
|
|
319
291
|
const r = renderTemplate({
|
|
320
|
-
html: '<nav>{{#nav}}<a href="{{
|
|
321
|
-
fields: [{ name: "nav", kind: "
|
|
322
|
-
contentValues: {
|
|
292
|
+
html: '<nav>{{#nav}}<a href="{{link}}">{{label}}</a>{{/nav}}</nav>',
|
|
293
|
+
fields: [{ name: "nav", kind: "list", listType: "links" }],
|
|
294
|
+
contentValues: {
|
|
295
|
+
nav: [
|
|
296
|
+
{
|
|
297
|
+
id: uid(1),
|
|
298
|
+
node: "link",
|
|
299
|
+
values: { label: "{{link}} {{Click URL}}", link: { href: "/x" } },
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
},
|
|
303
|
+
lists,
|
|
323
304
|
});
|
|
324
|
-
expect(r.html).toBe('<nav><a href="/x">{{
|
|
305
|
+
expect(unstamp(r.html)).toBe('<nav><a href="/x">{{link}} {{Click URL}}</a></nav>');
|
|
325
306
|
});
|
|
326
307
|
|
|
327
308
|
it("rendered nested module HTML is pasted verbatim, not parsed again", () => {
|
|
@@ -393,11 +374,12 @@ describe("renderTemplate — whitespace tolerance in markers", () => {
|
|
|
393
374
|
|
|
394
375
|
it("section {{ #items }}…{{ /items }} (whitespace inside braces) iterates normally", () => {
|
|
395
376
|
const r = renderTemplate({
|
|
396
|
-
html: "<ul>{{ #items }}<li>{{
|
|
397
|
-
fields: [{ name: "items", kind: "
|
|
398
|
-
contentValues: { items:
|
|
377
|
+
html: "<ul>{{ #items }}<li>{{ text }}</li>{{ /items }}</ul>",
|
|
378
|
+
fields: [{ name: "items", kind: "list", listType: "texts" }],
|
|
379
|
+
contentValues: { items: texts("a", "b") },
|
|
380
|
+
lists,
|
|
399
381
|
});
|
|
400
|
-
expect(r.html).toBe("<ul><li>a</li><li>b</li></ul>");
|
|
382
|
+
expect(unstamp(r.html)).toBe("<ul><li>a</li><li>b</li></ul>");
|
|
401
383
|
});
|
|
402
384
|
|
|
403
385
|
it("partial {{ > hero }} (whitespace inside braces) resolves normally", () => {
|
|
@@ -456,10 +438,10 @@ describe("renderTemplate — failure-marker parity", () => {
|
|
|
456
438
|
it("kind-mismatch on {{>name}} against a list field", () => {
|
|
457
439
|
const r = renderTemplate({
|
|
458
440
|
html: "<aside>{{>items}}</aside>",
|
|
459
|
-
fields: [{ name: "items", kind: "
|
|
441
|
+
fields: [{ name: "items", kind: "list", listType: "texts" }],
|
|
460
442
|
contentValues: {},
|
|
461
443
|
});
|
|
462
|
-
const expected = "kind-mismatch:items expected=module actual=
|
|
444
|
+
const expected = "kind-mismatch:items expected=module actual=list";
|
|
463
445
|
expect(r.html).toBe(`<aside><!-- caelo:missing reason=${expected} --></aside>`);
|
|
464
446
|
expect(r.missingSlots).toContain(expected);
|
|
465
447
|
});
|