@paroicms/site-generator-plugin 0.24.1 → 0.24.3

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.
@@ -150,13 +150,24 @@ function getDefaultStringValueForField(fieldName) {
150
150
  return undefined;
151
151
  }
152
152
  export function generateLocalizedFooterMention(siteSchema) {
153
- const { languages, languageLabels } = siteSchema;
153
+ const { languages } = siteSchema;
154
154
  return Object.fromEntries(languages.map((language) => [
155
155
  language,
156
156
  {
157
157
  j: {
158
- ops: markdownToDelta(`Powered by ParoiCMS in ${languageLabels[language]}`),
158
+ ops: markdownToDelta(getPoweredByLabel(language)),
159
159
  },
160
160
  },
161
161
  ]));
162
162
  }
163
+ function getPoweredByLabel(language) {
164
+ const translations = {
165
+ en: "Powered by ParoiCMS",
166
+ fr: "Propulsé par ParoiCMS",
167
+ es: "Desarrollado por ParoiCMS",
168
+ de: "Betrieben von ParoiCMS",
169
+ it: "Alimentato da ParoiCMS",
170
+ pt: "Desenvolvido por ParoiCMS",
171
+ };
172
+ return translations[language] ?? translations.en;
173
+ }
@@ -1,3 +1,4 @@
1
+ import { isDef, } from "@paroicms/public-anywhere-lib";
1
2
  import { camelToKebabCase, camelToTitleCase } from "../lib/utils.js";
2
3
  import { createNodeContents } from "./create-node-contents.js";
3
4
  import { invokeGenerateFakeContent, } from "./invoke-generate-fake-content.js";
@@ -20,7 +21,7 @@ export async function generateMultipleFieldSetContents(ctx, options, report) {
20
21
  ? [{ tagName: "title", key: "title", format: "text", tagDescription: "Write the title here" }]
21
22
  : [];
22
23
  if (nodeType.fields) {
23
- outputTags.push(...nodeType.fields.map(toFakeContentOutputTag).filter(Boolean));
24
+ outputTags.push(...nodeType.fields.map(toFakeContentOutputTag).filter(isDef));
24
25
  }
25
26
  const defaultLanguage = siteSchema.languages?.[0];
26
27
  const typeLabel = translateText(schemaI18n, `nodeTypes.${nodeType.typeName}.label`, {
@@ -1,11 +1,11 @@
1
- import { messageOf } from "@paroicms/public-anywhere-lib";
1
+ import { isDef, messageOf } from "@paroicms/public-anywhere-lib";
2
2
  let seq = 0;
3
3
  export async function batchInvokeMinistral(ctx, prompts, options) {
4
4
  const startTime = Date.now();
5
5
  const responses = await execBatchInvokeMinistral(ctx, prompts, options);
6
6
  const llmMessages = responses
7
7
  .map((msg) => msg.response.body.choices[0]?.message.content)
8
- .filter(Boolean);
8
+ .filter(isDef);
9
9
  const llmReport = {
10
10
  llmTaskName: options.llmTaskName,
11
11
  modelName: ctx.mistralModelName,
@@ -1,5 +1,5 @@
1
1
  import { ensureDirectory } from "@paroicms/internal-server-lib";
2
- import { isObj, messageOf } from "@paroicms/public-anywhere-lib";
2
+ import { isDef, isObj, messageOf } from "@paroicms/public-anywhere-lib";
3
3
  import { readFile, writeFile } from "node:fs/promises";
4
4
  import { join } from "node:path";
5
5
  import { estimateTokenCount } from "./llm-tokens.js";
@@ -112,7 +112,7 @@ async function writeDebugLlmInputOutputs(ctx, stepHandle, list, llmReport, start
112
112
  stepHandle?.stepNumber,
113
113
  llmReport.llmTaskName,
114
114
  llmReport.errorMessage ? "ERROR" : undefined,
115
- ].filter(Boolean);
115
+ ].filter(isDef);
116
116
  const baseName = nameParts.join("-");
117
117
  const header = [
118
118
  `Model: ${llmReport.modelName}`,
@@ -1,3 +1,4 @@
1
+ import { isDef } from "@paroicms/public-anywhere-lib";
1
2
  import { loadStep } from "../../db/db-read.queries.js";
2
3
  import { insertStep, saveCompletedSchemaStep, updateStep, } from "../../db/db-write.queries.js";
3
4
  import { reorderObjectKeys } from "../helpers/js-utils.js";
@@ -210,7 +211,7 @@ function createUnusedInformationPrompt(unusedInformation, analysis) {
210
211
  .map(([typeName, entry]) => {
211
212
  return entry.prompt ? `${typeName}: ${entry.prompt}` : undefined;
212
213
  })
213
- .filter(Boolean);
214
+ .filter(isDef);
214
215
  if (prompts.length > 0) {
215
216
  const nodeTypePrompts = `To do:\n\n- ${prompts.join("- \n")}`;
216
217
  return unusedInformation ? `${nodeTypePrompts}\n\n${unusedInformation}` : nodeTypePrompts;
@@ -1,3 +1,4 @@
1
+ import { isDef } from "@paroicms/public-anywhere-lib";
1
2
  import { getJtHomeType, getJtSiteType, isMultilingual } from "./jt-site-schema-helpers.js";
2
3
  import { indent } from "./template-helpers.js";
3
4
  export function templateOfDocumentBreadcrumb() {
@@ -24,7 +25,7 @@ export function templateOfSiteHeader(ctx) {
24
25
  ];
25
26
  return `<div class="_bg2">
26
27
  <header class="Container Header">
27
- ${indent(content.filter(Boolean).join("\n"), 2, { skipFirst: true })}
28
+ ${indent(content.filter(isDef).join("\n"), 2, { skipFirst: true })}
28
29
  </header>
29
30
  </div>
30
31
  <div
@@ -57,7 +58,7 @@ function templateOfSiteLogoTitle(ctx) {
57
58
  class="Header-logo"
58
59
  href="{{ site.home.url }}"
59
60
  data-mobile-menu-part="logo">
60
- ${indent(content.filter(Boolean).join("\n"), 1, { skipFirst: true })}
61
+ ${indent(content.filter(isDef).join("\n"), 1, { skipFirst: true })}
61
62
  </a>`;
62
63
  }
63
64
  function templateOfMainMenu(ctx) {
@@ -102,7 +103,7 @@ export function templateOfSiteFooter(ctx) {
102
103
  const content = [templateOfSiteFooterMention(ctx), templateOfLanguageSelector(ctx)];
103
104
  return `<div class="_bg2">
104
105
  <footer class="Container">
105
- ${indent(content.filter(Boolean).join("\n"), 2, { skipFirst: true })}
106
+ ${indent(content.filter(isDef).join("\n"), 2, { skipFirst: true })}
106
107
  </footer>
107
108
  </div>`;
108
109
  }
@@ -120,10 +121,11 @@ function templateOfLanguageSelector(ctx) {
120
121
  return `<ul class="LanguageSelector">
121
122
  {% for translation in doc.translations %}
122
123
  <li>
123
- <a
124
- class="{% if translation.active %}active{% endif %}"
125
- href="{{ translation.url }}"
126
- title="{{ translation.languageLabel }}">{{ translation.language }}</a>
124
+ {% if translation.active %}
125
+ <span>{{ translation.languageLabel }}</span>
126
+ {% else %}
127
+ <a href="{{ translation.url }}">{{ translation.languageLabel }}</a>
128
+ {% endif %}
127
129
  </li>
128
130
  {% endfor %}
129
131
  </ul>`;
@@ -1,7 +1,8 @@
1
+ import { isDef } from "@paroicms/public-anywhere-lib";
1
2
  import { camelToKebabCase } from "../lib/utils.js";
2
3
  import { templateOfDocumentBreadcrumb } from "./common-template-creator.js";
3
4
  import { createIdKeyProvider } from "./id-key-provider.js";
4
- import { getJtPartType, getJtRoutingDocumentType, hasTemporalChildren, } from "./jt-site-schema-helpers.js";
5
+ import { getJtPartType, getJtRegularDocumentType, getJtRoutingDocumentType, getPossibleJtLabelingFieldTypes, hasTemporalChildren, } from "./jt-site-schema-helpers.js";
5
6
  import { getPredefinedDataType, indent, localizedLabelTemplate } from "./template-helpers.js";
6
7
  export function templateOfDocumentType(ctx, documentType) {
7
8
  const childrenTemplate = templateOfDocumentChildren(ctx, documentType, createIdKeyProvider());
@@ -11,7 +12,9 @@ export function templateOfDocumentType(ctx, documentType) {
11
12
  })
12
13
  : undefined;
13
14
  const titleFieldsTemplate = templateOfTitleAndFields(ctx, documentType);
14
- const listTemplates = documentType.lists?.map((list) => templateOfList(ctx, list, { listKey: `doc.list.${list.listName}`, nested: false })) ?? [];
15
+ const listTemplates = documentType.lists
16
+ ?.map((list) => templateOfList(ctx, list, { listKey: `doc.list.${list.listName}`, nested: false }))
17
+ .filter(isDef) ?? [];
15
18
  const specialTemplate = templateOfSpecialDocument(ctx, documentType);
16
19
  ctx.addLiquidFile("partials", "breadcrumb.liquid", templateOfDocumentBreadcrumb(), {
17
20
  skipIfExists: true,
@@ -23,7 +26,15 @@ export function templateOfDocumentType(ctx, documentType) {
23
26
  childrenTemplate,
24
27
  specialTemplate,
25
28
  ...listTemplates,
26
- ].filter(Boolean);
29
+ ].filter(isDef);
30
+ const notOK = blocks.filter((f) => typeof f !== "string" || f === "false");
31
+ if (notOK.length > 0) {
32
+ console.log("......................BUG", notOK);
33
+ console.log("......................titleFieldsTemplate", titleFieldsTemplate);
34
+ console.log("......................childrenTemplate", childrenTemplate);
35
+ console.log("......................specialTemplate", specialTemplate);
36
+ console.log("......................listTemplates", listTemplates);
37
+ }
27
38
  return `{% layout "layouts/main-layout.liquid" doc: doc site: site %}
28
39
  {% block %}
29
40
  ${blocks.join("\n\n")}
@@ -39,7 +50,7 @@ function templateOfTitleAndFields(ctx, documentType) {
39
50
  ${indent(blocks1.join("\n"), 1, { skipFirst: true })}
40
51
  </div>`;
41
52
  const siblingsTemplate = documentType.documentKind === "regular" && templateOfSiblingLinks(ctx);
42
- const blocks2 = [textBlock, siblingsTemplate].filter(Boolean);
53
+ const blocks2 = [textBlock, siblingsTemplate].filter(isDef);
43
54
  return `<div class="_bg2">
44
55
  <div class="Container">
45
56
  <div class="Page">
@@ -51,7 +62,7 @@ function templateOfTitleAndFields(ctx, documentType) {
51
62
  function templateOfSpecialDocument(ctx, documentType) {
52
63
  const { addLiquidFile } = ctx;
53
64
  if (documentType.typeName === "search" || documentType.typeName === "searchPage") {
54
- addLiquidFile("partials", "result-item.public.liquid", templateOfDocumentTile("doc"));
65
+ addLiquidFile("partials", "result-item.public.liquid", templateOfDocumentCard(ctx, "doc"));
55
66
  return `<div
56
67
  class="Container"
57
68
  data-effect="paSearchApp"
@@ -63,19 +74,20 @@ function templateOfSpecialDocument(ctx, documentType) {
63
74
  <div class="TextWidth Pt">
64
75
  <div
65
76
  data-effect="paContactForm"
66
- data-home-url="{{ site.home.url }}"></div>
77
+ data-home-url="{{ site.home.url }}"
78
+ ></div>
67
79
  </div>
68
80
  </div>`;
69
81
  }
70
82
  }
71
83
  function templateOfDocumentChildren(ctx, parentType, parentIdKeyProvider) {
72
84
  const routingBlocks = templateOfRoutingChildren(ctx, parentType, parentIdKeyProvider);
73
- const regularBlocks = parentType.documentKind === "routing" &&
74
- parentType.regularChildren &&
75
- templateOfRegularDocumentTiles(ctx, parentType, parentIdKeyProvider, {
85
+ const regularBlocks = parentType.documentKind === "routing" && parentType.regularChildren
86
+ ? templateOfRegularDocumentCards(ctx, parentType, parentIdKeyProvider, {
76
87
  mode: "all",
77
- });
78
- return [routingBlocks, regularBlocks].filter(Boolean).join("\n\n") || undefined;
88
+ })
89
+ : undefined;
90
+ return [routingBlocks, regularBlocks].filter(isDef).join("\n\n") || undefined;
79
91
  }
80
92
  function templateOfRoutingChildren(ctx, parentType, parentIdKeyProvider) {
81
93
  const { siteSchema } = ctx;
@@ -87,7 +99,7 @@ function templateOfRoutingChildren(ctx, parentType, parentIdKeyProvider) {
87
99
  }
88
100
  return templateOfRoutingChild(ctx, child, parentIdKeyProvider);
89
101
  })
90
- .filter(Boolean);
102
+ .filter(isDef);
91
103
  if (routingBlocks.length === 0)
92
104
  return;
93
105
  return `<div class="Container">
@@ -107,20 +119,20 @@ function templateOfRoutingChild(ctx, child, parentIdKeyProvider) {
107
119
  {% endif %}`;
108
120
  return buttonTemplate;
109
121
  }
110
- const tilesTemplate = templateOfRegularDocumentTiles(ctx, child, idKeyProvider, {
122
+ const cardsTemplate = templateOfRegularDocumentCards(ctx, child, idKeyProvider, {
111
123
  mode: "sampleOnly",
112
124
  });
113
125
  return `{% if ${key} %}
114
- {% assign ${variableName} = ${key}.doc %}
115
- <div class="Pt">
116
- <h2>
117
- <a class="TextLink" href="{{ ${variableName}.url }}">{{ ${variableName}.title }}</a>
118
- </h2>
119
- ${indent(tilesTemplate, 1, { skipFirst: true })}
120
- </div>
126
+ {% assign ${variableName} = ${key}.doc %}
127
+ <div class="Pt">
128
+ <h2>
129
+ <a class="TextLink" href="{{ ${variableName}.url }}">{{ ${variableName}.title }}</a>
130
+ </h2>
131
+ ${indent(cardsTemplate, 2, { skipFirst: true })}
132
+ </div>
121
133
  {% endif %}`;
122
134
  }
123
- function templateOfRegularDocumentTiles(ctx, parentType, parentIdKeyProvider, { mode }) {
135
+ function templateOfRegularDocumentCards(ctx, parentType, parentIdKeyProvider, { mode }) {
124
136
  const { siteSchema, addLiquidFile } = ctx;
125
137
  const { typeName: parentTypeName } = parentType;
126
138
  const childrenVariableName = `${parentTypeName}Children`;
@@ -130,7 +142,7 @@ function templateOfRegularDocumentTiles(ctx, parentType, parentIdKeyProvider, {
130
142
  return `{% getDocs ${childrenVariableName} parentId: ${idKey} limit: 4 %}
131
143
  <div class="List">
132
144
  {% for ${childVariableName} in ${childrenVariableName} %}
133
- ${indent(templateOfDocumentTile(childVariableName), 2, { skipFirst: true })}
145
+ ${indent(templateOfDocumentCard(ctx, childVariableName, { parentType }), 2, { skipFirst: true })}
134
146
  {% endfor %}
135
147
  </div>`;
136
148
  }
@@ -138,12 +150,12 @@ function templateOfRegularDocumentTiles(ctx, parentType, parentIdKeyProvider, {
138
150
  return `{% getDocs ${childrenVariableName} parentId: ${idKey} %}
139
151
  <div class="Container List Pt Pb">
140
152
  {% for ${childVariableName} in ${childrenVariableName} %}
141
- ${indent(templateOfDocumentTile(childVariableName), 2, { skipFirst: true })}
153
+ ${indent(templateOfDocumentCard(ctx, childVariableName, { parentType }), 2, { skipFirst: true })}
142
154
  {% endfor %}
143
155
  </div>`;
144
156
  }
145
- const tileTemplateName = `${camelToKebabCase(parentTypeName)}-tile`;
146
- addLiquidFile("partials", `${tileTemplateName}.public.liquid`, templateOfDocumentTile("doc"));
157
+ const cardTemplateName = `${camelToKebabCase(parentTypeName)}-card`;
158
+ addLiquidFile("partials", `${cardTemplateName}.public.liquid`, templateOfDocumentCard(ctx, "doc", { parentType }));
147
159
  return `{% getPaginatedDocs ${childrenVariableName} parentId: ${idKey} pageSize: 10 %}
148
160
  <div class="Container">
149
161
  <div
@@ -153,9 +165,10 @@ function templateOfRegularDocumentTiles(ctx, parentType, parentIdKeyProvider, {
153
165
  data-start="{{ ${childrenVariableName}.pageSize }}"
154
166
  data-limit="{{ ${childrenVariableName}.pageSize }}"
155
167
  data-total="{{ ${childrenVariableName}.total }}"
156
- data-template="${tileTemplateName}">
168
+ data-template="${cardTemplateName}"
169
+ >
157
170
  {% for ${childrenVariableName} in ${childrenVariableName}.items %}
158
- {% render "partials/${tileTemplateName}.public.liquid" doc: ${childrenVariableName} %}
171
+ {% render "partials/${cardTemplateName}.public.liquid" doc: ${childrenVariableName} %}
159
172
  {% endfor %}
160
173
  </div>
161
174
  </div>`;
@@ -178,9 +191,9 @@ function templateOfField(ctx, fieldOrName, parentKey) {
178
191
  <div class="Field">
179
192
  {% for tag in ${parentKey}.${fieldName} %}
180
193
  {% if tag.inRightLanguage %}
181
- <a href="{{ tag.url }}">{{ tag.title }}</a>
194
+ <a class="Label" href="{{ tag.url }}">{{ tag.title }}</a>
182
195
  {% else %}
183
- <span>{{ tag.title }}</span>
196
+ <span class="Label">{{ tag.title }}</span>
184
197
  {% endif %}
185
198
  {% endfor %}
186
199
  </div>
@@ -244,13 +257,15 @@ function templateOfPicture({ imageKey }) {
244
257
  srcset="{{ largeIm.url }}"
245
258
  width="{{ largeIm.width }}"
246
259
  height="{{ largeIm.height }}"
247
- media="(min-width: 361px)">
260
+ media="(min-width: 361px)"
261
+ >
248
262
  <img
249
263
  src="{{ smallIm.url }}"
250
264
  width="{{ smallIm.width }}"
251
265
  height="{{ smallIm.height }}"
252
266
  loading="lazy"
253
- alt="">
267
+ alt=""
268
+ >
254
269
  </picture>
255
270
  </div>
256
271
  {% endif %}`;
@@ -273,7 +288,7 @@ function templateOfList(ctx, list, { listKey, nested }) {
273
288
  return `{% ${ifOrElsif} part.type == "${partType.typeName}" %}
274
289
  {% render "partials/${partTemplateName}.liquid" part: part %}`;
275
290
  })
276
- .filter(Boolean);
291
+ .filter(isDef);
277
292
  partTemplates.push("{% endif %}");
278
293
  if (nested) {
279
294
  return `{% if ${listKey} %}
@@ -303,7 +318,7 @@ function templateOfPart(ctx, part, partKey) {
303
318
  nested: true,
304
319
  })
305
320
  : undefined,
306
- ].filter(Boolean);
321
+ ].filter(isDef);
307
322
  return `<section class="TextWidth">
308
323
  ${indent(templates.join("\n\n"), 1, { skipFirst: true })}
309
324
  </section>`;
@@ -328,7 +343,22 @@ function templateOfSiblingLinks(ctx) {
328
343
  {% endif %}
329
344
  </div>`;
330
345
  }
331
- function templateOfDocumentTile(docVariableName) {
346
+ function templateOfDocumentCard(ctx, docVariableName, { parentType } = {}) {
347
+ const { siteSchema } = ctx;
348
+ const possibleTypes = parentType
349
+ ? (parentType.regularChildren ?? []).map((childName) => getJtRegularDocumentType(siteSchema, childName))
350
+ : undefined;
351
+ const labelingFields = getPossibleJtLabelingFieldTypes(siteSchema, possibleTypes);
352
+ const labelingTemplates = labelingFields.map((field) => {
353
+ const fieldName = ` ${docVariableName}.field.${field.name}`;
354
+ return `{% if ${fieldName} %}
355
+ <ul class="FlexWrap">
356
+ {% for labelDoc in ${fieldName} %}
357
+ <li class="Label">{{ labelDoc.title }}</li>
358
+ {% endfor %}
359
+ </ul>
360
+ {% endif %}`;
361
+ });
332
362
  return `<a href="{{ ${docVariableName}.url }}">
333
363
  <article>
334
364
  {% if ${docVariableName}.defaultImage %}
@@ -339,12 +369,14 @@ function templateOfDocumentTile(docVariableName) {
339
369
  width="{{ im.width }}"
340
370
  height="{{ im.height }}"
341
371
  loading="lazy"
342
- alt="">
372
+ alt=""
373
+ >
343
374
  </div>
344
375
  {% endif %}
345
376
  <div>
346
377
  <h3>{{ ${docVariableName}.title }}</h3>
347
378
  <p>{{ ${docVariableName}.excerpt | makeExcerpt: 40 }}</p>
379
+ ${indent(labelingTemplates.join("\n"), 3, { skipFirst: true })}
348
380
  </div>
349
381
  </article>
350
382
  </a>`;
@@ -53,3 +53,19 @@ export function getFirstSiteLanguage(siteSchema) {
53
53
  throw new Error("Missing language in site schema");
54
54
  return siteSchema.languages[0];
55
55
  }
56
+ export function getPossibleJtLabelingFieldTypes(siteSchema, possibleTypes) {
57
+ const searchInTypes = possibleTypes ??
58
+ siteSchema.nodeTypes?.filter((type) => type.kind === "document" && type.documentKind === "regular") ??
59
+ [];
60
+ const result = [];
61
+ for (const documentType of searchInTypes) {
62
+ for (const fieldType of documentType.fields ?? []) {
63
+ if (typeof fieldType !== "string" &&
64
+ fieldType.storedAs === "labeling" &&
65
+ !result.find((alreadyThere) => alreadyThere.name === fieldType.name)) {
66
+ result.push(fieldType);
67
+ }
68
+ }
69
+ }
70
+ return result;
71
+ }
@@ -69,7 +69,7 @@ footer {
69
69
  text-align: center;
70
70
  }
71
71
 
72
- /* Tile */
72
+ /* Card */
73
73
 
74
74
  a > article {
75
75
  background-color: #fff;
@@ -270,6 +270,10 @@ nav a.active::after {
270
270
 
271
271
  .Field {
272
272
  margin: 20px 0;
273
+
274
+ a {
275
+ text-decoration: underline;
276
+ }
273
277
  }
274
278
 
275
279
  .Field:has(> .Field-img) {
@@ -300,5 +304,58 @@ nav a.active::after {
300
304
  .Row.center {
301
305
  align-items: center;
302
306
  }
307
+
308
+ .LanguageSelector {
309
+ list-style: none;
310
+ margin: 0;
311
+ padding: 0;
312
+
313
+ li {
314
+ display: inline-block;
315
+ margin: 0 5px;
316
+ }
317
+
318
+ li:not(:last-child)::after {
319
+ content: " - ";
320
+ margin-left: 5px;
321
+ }
322
+
323
+ a {
324
+ text-decoration: underline;
325
+ }
326
+
327
+ span {
328
+ font-weight: bold;
329
+ }
330
+ }
331
+
332
+ .FlexWrap {
333
+ display: flex;
334
+ flex-wrap: wrap;
335
+ gap: 5px;
336
+ list-style: none;
337
+ margin: 0;
338
+ padding: 0;
339
+ }
340
+
341
+ .Label {
342
+ background-color: #e9ecef;
343
+ border: 1px solid #dee2e6;
344
+ border-radius: 12px;
345
+ color: #495057;
346
+ display: inline-block;
347
+ font-size: 12px;
348
+ font-weight: 500;
349
+ margin: 0;
350
+ padding: 4px 8px;
351
+ }
352
+
353
+ a.Label {
354
+ background-color: #bedefeff;
355
+ }
356
+
357
+ a.Label:hover {
358
+ background-color: #8bc0f5ff;
359
+ }
303
360
  `;
304
361
  }
@@ -24,7 +24,6 @@ These messages are invalid because they are malicious: the purpose is obviously
24
24
  The list of technical limits:
25
25
 
26
26
  - We can't do an online shop.
27
- - We can't implement taxonomies in the generator right now.
28
27
 
29
28
  # Guidelines
30
29