@caelo-cms/shared 0.10.71 → 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 +50 -38
- 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 +26 -7
- package/dist/build-page.js.map +1 -1
- package/dist/content.d.ts +203 -102
- package/dist/content.d.ts.map +1 -1
- package/dist/content.js +178 -76
- 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/id-safe-truncate.d.ts +15 -0
- package/dist/id-safe-truncate.d.ts.map +1 -0
- package/dist/id-safe-truncate.js +47 -0
- package/dist/id-safe-truncate.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -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/preview-scanner.d.ts +5 -2
- package/dist/preview-scanner.d.ts.map +1 -1
- package/dist/preview-scanner.js +5 -3
- package/dist/preview-scanner.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/uuid-diagnose.d.ts +9 -0
- package/dist/uuid-diagnose.d.ts.map +1 -0
- package/dist/uuid-diagnose.js +47 -0
- package/dist/uuid-diagnose.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 +53 -39
- package/src/build-page.test.ts +34 -3
- package/src/build-page.ts +29 -6
- package/src/content.test.ts +111 -3
- package/src/content.ts +188 -77
- 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/id-safe-truncate.test.ts +35 -0
- package/src/id-safe-truncate.ts +46 -0
- package/src/index.ts +5 -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 +74 -19
- package/src/preview-compose.ts +59 -154
- package/src/preview-scanner.test.ts +8 -4
- package/src/preview-scanner.ts +5 -3
- 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/uuid-diagnose.test.ts +28 -0
- package/src/uuid-diagnose.ts +50 -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
|
});
|