@paroicms/site-generator-plugin 0.24.3 → 0.25.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/backend/dist/generator/site-generator/common-template-creator.js +6 -9
- package/backend/dist/generator/site-generator/document-card-template-creator.js +40 -0
- package/backend/dist/generator/site-generator/document-template-creator.js +39 -92
- package/backend/dist/generator/site-generator/id-key-provider.js +1 -1
- package/backend/dist/generator/site-generator/labeled-list-template-creator.js +56 -0
- package/backend/dist/generator/site-generator/theme-creator-context.js +109 -0
- package/backend/dist/generator/site-generator/theme-creator.js +3 -67
- package/backend/dist/index.js +4 -4
- package/frontend/dist/frontend.mjs +59 -55
- package/package.json +10 -10
|
@@ -43,7 +43,7 @@ function templateOfSiteLogoTitle(ctx) {
|
|
|
43
43
|
const content = [
|
|
44
44
|
siteType.fields?.includes("logo")
|
|
45
45
|
? `{% if site.field.logo %}
|
|
46
|
-
{%
|
|
46
|
+
{% set logo = image(site.field.logo, resize: "50x50") %}
|
|
47
47
|
<img
|
|
48
48
|
src="{{ logo.url }}"
|
|
49
49
|
width="{{ logo.width }}"
|
|
@@ -67,8 +67,8 @@ function templateOfMainMenu(ctx) {
|
|
|
67
67
|
const menuItems = homeType.routingChildren?.filter((typeName) => typeName !== "search" && typeName !== "searchPage");
|
|
68
68
|
if (!menuItems || menuItems.length === 0)
|
|
69
69
|
return;
|
|
70
|
-
const itemTemplates = menuItems.map((typeName) => `{%
|
|
71
|
-
|
|
70
|
+
const itemTemplates = menuItems.map((typeName) => `{% set ${typeName} = doc(site.home.${typeName}) %}
|
|
71
|
+
{% if ${typeName} %}
|
|
72
72
|
<a
|
|
73
73
|
data-menu-item-id="{{ ${typeName}.id }}"
|
|
74
74
|
href="{{ ${typeName}.url }}">
|
|
@@ -89,12 +89,9 @@ function templateOfSearchOpener(ctx) {
|
|
|
89
89
|
if (!typeName)
|
|
90
90
|
return;
|
|
91
91
|
return `<div class="Row center">
|
|
92
|
-
{%
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
data-effect="paSearchOpener"
|
|
96
|
-
data-search-url="{{ ${typeName}.url }}"
|
|
97
|
-
data-icon-color="#fff"></span>
|
|
92
|
+
{% set ${typeName} = doc(site.home.${typeName}) %}
|
|
93
|
+
{% if ${typeName} %}
|
|
94
|
+
{% out searchOpener(url: ${typeName}.url, iconColor: "#fff") %}
|
|
98
95
|
{% endif %}
|
|
99
96
|
<div data-mobile-menu="button"></div>
|
|
100
97
|
</div>`;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { getJtRegularDocumentType, getPossibleJtLabelingFieldTypes, } from "./jt-site-schema-helpers.js";
|
|
2
|
+
import { indent } from "./template-helpers.js";
|
|
3
|
+
export function templateOfDocumentCard(ctx, docVariableName, { parentType } = {}) {
|
|
4
|
+
const { siteSchema } = ctx;
|
|
5
|
+
const possibleTypes = parentType
|
|
6
|
+
? (parentType.regularChildren ?? []).map((childName) => getJtRegularDocumentType(siteSchema, childName))
|
|
7
|
+
: undefined;
|
|
8
|
+
const labelingFields = getPossibleJtLabelingFieldTypes(siteSchema, possibleTypes);
|
|
9
|
+
const labelingTemplates = labelingFields.map((field) => {
|
|
10
|
+
const fieldName = ` ${docVariableName}.field.${field.name}`;
|
|
11
|
+
return `{% if ${fieldName} %}
|
|
12
|
+
<ul class="FlexWrap">
|
|
13
|
+
{% for labelDoc in ${fieldName} %}
|
|
14
|
+
<li class="Label">{{ labelDoc.title }}</li>
|
|
15
|
+
{% endfor %}
|
|
16
|
+
</ul>
|
|
17
|
+
{% endif %}`;
|
|
18
|
+
});
|
|
19
|
+
return `<a href="{{ ${docVariableName}.url }}">
|
|
20
|
+
<article>
|
|
21
|
+
{% if ${docVariableName}.defaultImage %}
|
|
22
|
+
<div>
|
|
23
|
+
{% set im = image(${docVariableName}.defaultImage, resize: "120x120") %}
|
|
24
|
+
<img
|
|
25
|
+
src="{{ im.url }}"
|
|
26
|
+
width="{{ im.width }}"
|
|
27
|
+
height="{{ im.height }}"
|
|
28
|
+
loading="lazy"
|
|
29
|
+
alt=""
|
|
30
|
+
>
|
|
31
|
+
</div>
|
|
32
|
+
{% endif %}
|
|
33
|
+
<div>
|
|
34
|
+
<h3>{{ ${docVariableName}.title }}</h3>
|
|
35
|
+
<p>{{ ${docVariableName}.excerpt | makeExcerpt: 40 }}</p>
|
|
36
|
+
${indent(labelingTemplates.join("\n"), 3, { skipFirst: true })}
|
|
37
|
+
</div>
|
|
38
|
+
</article>
|
|
39
|
+
</a>`;
|
|
40
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { isDef } from "@paroicms/public-anywhere-lib";
|
|
2
2
|
import { camelToKebabCase } from "../lib/utils.js";
|
|
3
3
|
import { templateOfDocumentBreadcrumb } from "./common-template-creator.js";
|
|
4
|
+
import { templateOfDocumentCard } from "./document-card-template-creator.js";
|
|
4
5
|
import { createIdKeyProvider } from "./id-key-provider.js";
|
|
5
|
-
import { getJtPartType,
|
|
6
|
+
import { getJtPartType, getJtRoutingDocumentType, hasTemporalChildren, } from "./jt-site-schema-helpers.js";
|
|
7
|
+
import { templatesOfLabeledList } from "./labeled-list-template-creator.js";
|
|
6
8
|
import { getPredefinedDataType, indent, localizedLabelTemplate } from "./template-helpers.js";
|
|
7
9
|
export function templateOfDocumentType(ctx, documentType) {
|
|
8
10
|
const childrenTemplate = templateOfDocumentChildren(ctx, documentType, createIdKeyProvider());
|
|
@@ -16,26 +18,20 @@ export function templateOfDocumentType(ctx, documentType) {
|
|
|
16
18
|
?.map((list) => templateOfList(ctx, list, { listKey: `doc.list.${list.listName}`, nested: false }))
|
|
17
19
|
.filter(isDef) ?? [];
|
|
18
20
|
const specialTemplate = templateOfSpecialDocument(ctx, documentType);
|
|
21
|
+
const labeledListTemplates = templatesOfLabeledList(ctx, documentType);
|
|
19
22
|
ctx.addLiquidFile("partials", "breadcrumb.liquid", templateOfDocumentBreadcrumb(), {
|
|
20
23
|
skipIfExists: true,
|
|
21
24
|
});
|
|
22
25
|
const blocks = [
|
|
23
|
-
`{% render "partials/breadcrumb" doc: doc
|
|
26
|
+
`{% render "partials/breadcrumb" doc: doc %}`,
|
|
24
27
|
featuredImageTemplate,
|
|
25
28
|
titleFieldsTemplate,
|
|
26
|
-
childrenTemplate,
|
|
27
29
|
specialTemplate,
|
|
28
30
|
...listTemplates,
|
|
31
|
+
...labeledListTemplates,
|
|
32
|
+
childrenTemplate,
|
|
29
33
|
].filter(isDef);
|
|
30
|
-
|
|
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
|
-
}
|
|
38
|
-
return `{% layout "layouts/main-layout.liquid" doc: doc site: site %}
|
|
34
|
+
return `{% layout "layouts/main-layout.liquid" %}
|
|
39
35
|
{% block %}
|
|
40
36
|
${blocks.join("\n\n")}
|
|
41
37
|
{% endblock %}`;
|
|
@@ -49,7 +45,7 @@ function templateOfTitleAndFields(ctx, documentType) {
|
|
|
49
45
|
const textBlock = `<div class="TextWidth">
|
|
50
46
|
${indent(blocks1.join("\n"), 1, { skipFirst: true })}
|
|
51
47
|
</div>`;
|
|
52
|
-
const siblingsTemplate = documentType.documentKind === "regular"
|
|
48
|
+
const siblingsTemplate = documentType.documentKind === "regular" ? templateOfSiblingLinks(ctx) : undefined;
|
|
53
49
|
const blocks2 = [textBlock, siblingsTemplate].filter(isDef);
|
|
54
50
|
return `<div class="_bg2">
|
|
55
51
|
<div class="Container">
|
|
@@ -63,19 +59,18 @@ function templateOfSpecialDocument(ctx, documentType) {
|
|
|
63
59
|
const { addLiquidFile } = ctx;
|
|
64
60
|
if (documentType.typeName === "search" || documentType.typeName === "searchPage") {
|
|
65
61
|
addLiquidFile("partials", "result-item.public.liquid", templateOfDocumentCard(ctx, "doc"));
|
|
66
|
-
return `<div
|
|
67
|
-
class="
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
return `<div class="Container">
|
|
63
|
+
<div class="Page">
|
|
64
|
+
{% out searchApp(class: "Container", template: "partials/result-item.public", by: 10) %}
|
|
65
|
+
</div>
|
|
66
|
+
</div>`;
|
|
71
67
|
}
|
|
72
68
|
if (documentType.typeName === "contact" || documentType.typeName === "contactPage") {
|
|
73
69
|
return `<div class="Container">
|
|
74
|
-
<div class="
|
|
75
|
-
<div
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
></div>
|
|
70
|
+
<div class="Page">
|
|
71
|
+
<div class="TextWidth Pt">
|
|
72
|
+
{% out contactForm %}
|
|
73
|
+
</div>
|
|
79
74
|
</div>
|
|
80
75
|
</div>`;
|
|
81
76
|
}
|
|
@@ -113,8 +108,8 @@ function templateOfRoutingChild(ctx, child, parentIdKeyProvider) {
|
|
|
113
108
|
const idKeyProvider = parentIdKeyProvider.createForRoutingChild(typeName);
|
|
114
109
|
const key = idKeyProvider.key;
|
|
115
110
|
if (!hasTemporalChildren(siteSchema, child) || regularChildrenSorting !== "publishDate desc") {
|
|
116
|
-
const buttonTemplate = `{%
|
|
117
|
-
|
|
111
|
+
const buttonTemplate = `{% set ${variableName} = doc(${key}) %}
|
|
112
|
+
{% if ${variableName} %}
|
|
118
113
|
<a class="Button" href="{{ ${variableName}.url }}">{{ ${variableName}.title }}</a>
|
|
119
114
|
{% endif %}`;
|
|
120
115
|
return buttonTemplate;
|
|
@@ -122,8 +117,8 @@ function templateOfRoutingChild(ctx, child, parentIdKeyProvider) {
|
|
|
122
117
|
const cardsTemplate = templateOfRegularDocumentCards(ctx, child, idKeyProvider, {
|
|
123
118
|
mode: "sampleOnly",
|
|
124
119
|
});
|
|
125
|
-
return `{%
|
|
126
|
-
|
|
120
|
+
return `{% set ${variableName} = doc(${key}) %}
|
|
121
|
+
{% if ${variableName} %}
|
|
127
122
|
<div class="Pt">
|
|
128
123
|
<h2>
|
|
129
124
|
<a class="TextLink" href="{{ ${variableName}.url }}">{{ ${variableName}.title }}</a>
|
|
@@ -137,9 +132,9 @@ function templateOfRegularDocumentCards(ctx, parentType, parentIdKeyProvider, {
|
|
|
137
132
|
const { typeName: parentTypeName } = parentType;
|
|
138
133
|
const childrenVariableName = `${parentTypeName}Children`;
|
|
139
134
|
const childVariableName = `${parentTypeName}Child`;
|
|
140
|
-
const
|
|
135
|
+
const key = parentIdKeyProvider.key;
|
|
141
136
|
if (mode === "sampleOnly") {
|
|
142
|
-
return `{%
|
|
137
|
+
return `{% set ${childrenVariableName} = docs(${key}.children, limit: 4) %}
|
|
143
138
|
<div class="List">
|
|
144
139
|
{% for ${childVariableName} in ${childrenVariableName} %}
|
|
145
140
|
${indent(templateOfDocumentCard(ctx, childVariableName, { parentType }), 2, { skipFirst: true })}
|
|
@@ -147,30 +142,18 @@ function templateOfRegularDocumentCards(ctx, parentType, parentIdKeyProvider, {
|
|
|
147
142
|
</div>`;
|
|
148
143
|
}
|
|
149
144
|
if (!hasTemporalChildren(siteSchema, parentType)) {
|
|
150
|
-
return `{%
|
|
145
|
+
return `{% set ${childrenVariableName} = docs(${key}.children) %}
|
|
151
146
|
<div class="Container List Pt Pb">
|
|
152
147
|
{% for ${childVariableName} in ${childrenVariableName} %}
|
|
153
148
|
${indent(templateOfDocumentCard(ctx, childVariableName, { parentType }), 2, { skipFirst: true })}
|
|
154
149
|
{% endfor %}
|
|
155
150
|
</div>`;
|
|
156
151
|
}
|
|
157
|
-
const cardTemplateName = `${camelToKebabCase(parentTypeName)}-card`;
|
|
158
|
-
addLiquidFile("partials", `${cardTemplateName}.
|
|
159
|
-
return `{%
|
|
152
|
+
const cardTemplateName = `${camelToKebabCase(parentTypeName)}-card.public`;
|
|
153
|
+
addLiquidFile("partials", `${cardTemplateName}.liquid`, templateOfDocumentCard(ctx, "doc", { parentType }), { skipIfExists: true });
|
|
154
|
+
return `{% set ${childrenVariableName} = paginatedDocs(${key}.children, by: 10) %}
|
|
160
155
|
<div class="Container">
|
|
161
|
-
|
|
162
|
-
class="Page List"
|
|
163
|
-
data-effect="paInfiniteLoading"
|
|
164
|
-
data-parent-id="{{ ${idKey} }}"
|
|
165
|
-
data-start="{{ ${childrenVariableName}.pageSize }}"
|
|
166
|
-
data-limit="{{ ${childrenVariableName}.pageSize }}"
|
|
167
|
-
data-total="{{ ${childrenVariableName}.total }}"
|
|
168
|
-
data-template="${cardTemplateName}"
|
|
169
|
-
>
|
|
170
|
-
{% for ${childrenVariableName} in ${childrenVariableName}.items %}
|
|
171
|
-
{% render "partials/${cardTemplateName}.public.liquid" doc: ${childrenVariableName} %}
|
|
172
|
-
{% endfor %}
|
|
173
|
-
</div>
|
|
156
|
+
{% out infiniteLoading(class: "Page List", paginatedDocs: ${childrenVariableName}, template: "partials/${cardTemplateName}") %}
|
|
174
157
|
</div>`;
|
|
175
158
|
}
|
|
176
159
|
function templateOfFields(ctx, fields, { parentKey }) {
|
|
@@ -214,7 +197,7 @@ function templateOfField(ctx, fieldOrName, parentKey) {
|
|
|
214
197
|
if (dataType === "gallery") {
|
|
215
198
|
return `<div class="Field">
|
|
216
199
|
{% for media in ${parentKey}.${fieldName} %}
|
|
217
|
-
{%
|
|
200
|
+
{% set im = image(media, resize: "150x150") %}
|
|
218
201
|
<img
|
|
219
202
|
class="Field-img"
|
|
220
203
|
src="{{ im.url }}"
|
|
@@ -229,7 +212,7 @@ function templateOfField(ctx, fieldOrName, parentKey) {
|
|
|
229
212
|
}
|
|
230
213
|
if (dataType === "media") {
|
|
231
214
|
const mediaKey = `${parentKey}.${fieldName}`;
|
|
232
|
-
return `{%
|
|
215
|
+
return `{% set im = image(${mediaKey}, resize: "x250x") %}
|
|
233
216
|
<div class="Field">
|
|
234
217
|
<img
|
|
235
218
|
class="Field-img"
|
|
@@ -249,8 +232,8 @@ function templateOfField(ctx, fieldOrName, parentKey) {
|
|
|
249
232
|
}
|
|
250
233
|
function templateOfPicture({ imageKey }) {
|
|
251
234
|
return `{% if ${imageKey} %}
|
|
252
|
-
{%
|
|
253
|
-
{%
|
|
235
|
+
{% set smallIm = image(${imageKey}, resize: "360x48") %}
|
|
236
|
+
{% set largeIm = image(${imageKey}, resize: "1200x160") %}
|
|
254
237
|
<div class="Container">
|
|
255
238
|
<picture class="Hero">
|
|
256
239
|
<source
|
|
@@ -333,51 +316,15 @@ function templateOfSiblingLinks(ctx) {
|
|
|
333
316
|
fr: "Suivant",
|
|
334
317
|
});
|
|
335
318
|
return `<div class="Row spaceBetween">
|
|
336
|
-
{%
|
|
337
|
-
|
|
319
|
+
{% set previous = doc(doc.siblings.previous) %}
|
|
320
|
+
{% if previous %}
|
|
321
|
+
<a href="{{ previous.url }}" title="{{ previous.title }}">← ${previousLabelTemplate}</a>
|
|
338
322
|
{% else %}
|
|
339
323
|
<span></span>
|
|
340
324
|
{% endif %}
|
|
341
|
-
{%
|
|
342
|
-
|
|
325
|
+
{% set next = doc(doc.siblings.next) %}
|
|
326
|
+
{% if next %}
|
|
327
|
+
<a href="{{ next.url }}" title="{{ next.title }}">${nextLabelTemplate} →</a>
|
|
343
328
|
{% endif %}
|
|
344
329
|
</div>`;
|
|
345
330
|
}
|
|
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
|
-
});
|
|
362
|
-
return `<a href="{{ ${docVariableName}.url }}">
|
|
363
|
-
<article>
|
|
364
|
-
{% if ${docVariableName}.defaultImage %}
|
|
365
|
-
<div>
|
|
366
|
-
{% useImage im image: ${docVariableName}.defaultImage resize: "120x120" %}
|
|
367
|
-
<img
|
|
368
|
-
src="{{ im.url }}"
|
|
369
|
-
width="{{ im.width }}"
|
|
370
|
-
height="{{ im.height }}"
|
|
371
|
-
loading="lazy"
|
|
372
|
-
alt=""
|
|
373
|
-
>
|
|
374
|
-
</div>
|
|
375
|
-
{% endif %}
|
|
376
|
-
<div>
|
|
377
|
-
<h3>{{ ${docVariableName}.title }}</h3>
|
|
378
|
-
<p>{{ ${docVariableName}.excerpt | makeExcerpt: 40 }}</p>
|
|
379
|
-
${indent(labelingTemplates.join("\n"), 3, { skipFirst: true })}
|
|
380
|
-
</div>
|
|
381
|
-
</article>
|
|
382
|
-
</a>`;
|
|
383
|
-
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export function createIdKeyProvider() {
|
|
2
2
|
return {
|
|
3
|
-
key: "doc",
|
|
3
|
+
key: "doc.routing",
|
|
4
4
|
idKey: "doc.id",
|
|
5
5
|
getChildRoutingKey: (typeName) => `doc.routing.${typeName}`,
|
|
6
6
|
createForRoutingChild: (routingTypeName) => createChildIdKeyProvider(`doc.routing.${routingTypeName}`),
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { isDef } from "@paroicms/public-anywhere-lib";
|
|
2
|
+
import { camelToKebabCase } from "../lib/utils.js";
|
|
3
|
+
import { templateOfDocumentCard } from "./document-card-template-creator.js";
|
|
4
|
+
export function templatesOfLabeledList(ctx, documentType) {
|
|
5
|
+
const { addLiquidFile } = ctx;
|
|
6
|
+
const labeledByUs = ctx.labeledDocuments.filter((doc) => {
|
|
7
|
+
const parentTypes = ctx.getParentTypes(documentType);
|
|
8
|
+
return parentTypes.some((parentType) => parentType.typeName === doc.taxonomyType.typeName);
|
|
9
|
+
});
|
|
10
|
+
return labeledByUs
|
|
11
|
+
.map((labeledDocument) => {
|
|
12
|
+
const parentRoutingKey = getRoutingKeyInCluster(ctx, labeledDocument.parentDocumentType, {
|
|
13
|
+
sameClusterAs: labeledDocument.taxonomyType,
|
|
14
|
+
});
|
|
15
|
+
if (!parentRoutingKey)
|
|
16
|
+
return;
|
|
17
|
+
const labeledListKey = `labeled${upperCaseFirstLetter(labeledDocument.parentDocumentType.typeName)}`;
|
|
18
|
+
const cardTemplateName = `${camelToKebabCase(labeledDocument.documentType.typeName)}-card.public`;
|
|
19
|
+
addLiquidFile("partials", `${cardTemplateName}.liquid`, templateOfDocumentCard(ctx, "doc", { parentType: labeledDocument.parentDocumentType }), { skipIfExists: true });
|
|
20
|
+
return `{% set ${labeledListKey} = paginatedDocs(${parentRoutingKey}.children, by: 10, term: doc, labeledWith: "${labeledDocument.field.name}") %}
|
|
21
|
+
<div class="Container">
|
|
22
|
+
{% out infiniteLoading(class: "Page List", paginatedDocs: ${labeledListKey}, template: "partials/${cardTemplateName}") %}
|
|
23
|
+
</div>`;
|
|
24
|
+
})
|
|
25
|
+
.filter(isDef);
|
|
26
|
+
}
|
|
27
|
+
function upperCaseFirstLetter(str) {
|
|
28
|
+
return `${str.charAt(0).toUpperCase()}${str.slice(1)}`;
|
|
29
|
+
}
|
|
30
|
+
function getRoutingKeyInCluster(ctx, documentType, options) {
|
|
31
|
+
const { siteSchema } = ctx;
|
|
32
|
+
const { nodeTypes } = siteSchema;
|
|
33
|
+
if (!nodeTypes)
|
|
34
|
+
return;
|
|
35
|
+
const getNodeType = (typeName) => nodeTypes.find((nt) => nt.typeName === typeName);
|
|
36
|
+
function findPath(target, current, parentPath) {
|
|
37
|
+
const currentPath = [...parentPath, current];
|
|
38
|
+
if (current === target)
|
|
39
|
+
return currentPath;
|
|
40
|
+
const node = getNodeType(current);
|
|
41
|
+
if (!node || node.kind !== "document" || !node.routingChildren)
|
|
42
|
+
return;
|
|
43
|
+
for (const childName of node.routingChildren) {
|
|
44
|
+
const found = findPath(target, childName, currentPath);
|
|
45
|
+
if (found)
|
|
46
|
+
return found;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const clusterRootTypeName = "home"; // limitation here: only works for the "home" cluster
|
|
50
|
+
const targetPath = findPath(documentType.typeName, clusterRootTypeName, []);
|
|
51
|
+
const sameClusterPath = findPath(options.sameClusterAs.typeName, clusterRootTypeName, []);
|
|
52
|
+
if (!targetPath || !sameClusterPath)
|
|
53
|
+
return;
|
|
54
|
+
targetPath.shift(); // remove leading 'home'
|
|
55
|
+
return `doc.cluster.routing.${targetPath.join(".")}`;
|
|
56
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { getPredefinedFields } from "../lib/create-prompt.js";
|
|
2
|
+
export function createThemeCreatorContext(siteSchema) {
|
|
3
|
+
const languages = siteSchema.languages ?? [];
|
|
4
|
+
const liquidFiles = new Map();
|
|
5
|
+
const localeFiles = new Map();
|
|
6
|
+
const otherFiles = new Map();
|
|
7
|
+
const issues = [];
|
|
8
|
+
const getParentTypes = makeGetParentTypes(siteSchema);
|
|
9
|
+
return {
|
|
10
|
+
siteSchema,
|
|
11
|
+
labeledDocuments: getLabeledDocuments(siteSchema, getParentTypes),
|
|
12
|
+
getParentTypes,
|
|
13
|
+
predefinedFields: new Map(getPredefinedFields().map((f) => [f.fieldName, f])),
|
|
14
|
+
setLocalizedLabel(label) {
|
|
15
|
+
for (const language of languages) {
|
|
16
|
+
const value = label[language];
|
|
17
|
+
if (!value)
|
|
18
|
+
continue;
|
|
19
|
+
let f = localeFiles.get(language);
|
|
20
|
+
if (!f) {
|
|
21
|
+
f = {};
|
|
22
|
+
localeFiles.set(language, f);
|
|
23
|
+
}
|
|
24
|
+
f[language] = value;
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
hasLiquidFile(directory, filename) {
|
|
28
|
+
const path = directory === "root" ? `templates/${filename}` : `templates/${directory}/${filename}`;
|
|
29
|
+
return liquidFiles.has(path);
|
|
30
|
+
},
|
|
31
|
+
addLiquidFile(directory, filename, content, { skipIfExists = false } = {}) {
|
|
32
|
+
const path = directory === "root" ? `templates/${filename}` : `templates/${directory}/${filename}`;
|
|
33
|
+
if (liquidFiles.has(path)) {
|
|
34
|
+
if (skipIfExists)
|
|
35
|
+
return;
|
|
36
|
+
issues.push(`Liquid file already exists, overwrite: "${path}"`);
|
|
37
|
+
}
|
|
38
|
+
liquidFiles.set(path, content);
|
|
39
|
+
},
|
|
40
|
+
addFile(path, content) {
|
|
41
|
+
if (otherFiles.has(path))
|
|
42
|
+
throw new Error(`File already exists: "${path}"`);
|
|
43
|
+
otherFiles.set(path, content);
|
|
44
|
+
},
|
|
45
|
+
toFiles() {
|
|
46
|
+
const files = [
|
|
47
|
+
...Array.from(liquidFiles.entries()).map(([path, content]) => ({ path, content })),
|
|
48
|
+
...Array.from(localeFiles.entries()).map(([language, content]) => ({
|
|
49
|
+
path: `locales/${language}.json`,
|
|
50
|
+
content: JSON.stringify(content, null, 2),
|
|
51
|
+
})),
|
|
52
|
+
...Array.from(otherFiles.entries()).map(([path, content]) => ({ path, content })),
|
|
53
|
+
];
|
|
54
|
+
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
55
|
+
return { files, issues: issues.length > 0 ? issues : undefined };
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function makeGetParentTypes(siteSchema) {
|
|
60
|
+
const parentsByChild = new Map();
|
|
61
|
+
const nodeTypes = siteSchema.nodeTypes ?? [];
|
|
62
|
+
for (const type of nodeTypes) {
|
|
63
|
+
if (type.kind !== "document")
|
|
64
|
+
continue;
|
|
65
|
+
if (type.documentKind === "routing") {
|
|
66
|
+
for (const childName of type.routingChildren ?? []) {
|
|
67
|
+
const list = parentsByChild.get(childName) ?? [];
|
|
68
|
+
list.push(type);
|
|
69
|
+
parentsByChild.set(childName, list);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
for (const childName of type.regularChildren ?? []) {
|
|
73
|
+
const list = parentsByChild.get(childName) ?? [];
|
|
74
|
+
list.push(type);
|
|
75
|
+
parentsByChild.set(childName, list);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return (documentType) => parentsByChild.get(documentType.typeName) ?? [];
|
|
79
|
+
}
|
|
80
|
+
function getLabeledDocuments(siteSchema, getParentTypes) {
|
|
81
|
+
const result = [];
|
|
82
|
+
for (const documentType of siteSchema.nodeTypes ?? []) {
|
|
83
|
+
if (documentType.kind !== "document" || documentType.documentKind !== "regular")
|
|
84
|
+
continue;
|
|
85
|
+
for (const field of documentType.fields ?? []) {
|
|
86
|
+
if (typeof field === "string" || field.storedAs !== "labeling")
|
|
87
|
+
continue;
|
|
88
|
+
const taxonomyType = siteSchema.nodeTypes?.find((nt) => nt.typeName === field.taxonomy);
|
|
89
|
+
if (!taxonomyType ||
|
|
90
|
+
taxonomyType.kind !== "document" ||
|
|
91
|
+
taxonomyType.documentKind !== "routing") {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
for (const parentDocumentType of getParentTypes(documentType)) {
|
|
95
|
+
if (parentDocumentType.kind !== "document" ||
|
|
96
|
+
parentDocumentType.documentKind !== "routing") {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
result.push({
|
|
100
|
+
documentType,
|
|
101
|
+
parentDocumentType,
|
|
102
|
+
field,
|
|
103
|
+
taxonomyType,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { isObj } from "@paroicms/public-anywhere-lib";
|
|
2
2
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
|
-
import { getPredefinedFields } from "../lib/create-prompt.js";
|
|
5
4
|
import { camelToKebabCase } from "../lib/utils.js";
|
|
6
5
|
import { templateOfSiteFooter, templateOfSiteHeader } from "./common-template-creator.js";
|
|
7
6
|
import { templateOfDocumentType } from "./document-template-creator.js";
|
|
8
7
|
import { isMultilingual } from "./jt-site-schema-helpers.js";
|
|
8
|
+
import { createThemeCreatorContext } from "./theme-creator-context.js";
|
|
9
9
|
import { getThemeCssContent } from "./theme-css.js";
|
|
10
10
|
export async function createTheme(ctx, siteDir, siteSchema) {
|
|
11
11
|
const themeContext = createThemeCreatorContext(siteSchema);
|
|
@@ -38,70 +38,6 @@ export async function createTheme(ctx, siteDir, siteSchema) {
|
|
|
38
38
|
ctx.logger.warn(`Issues in "${siteDir}":`, issues);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
function createThemeCreatorContext(siteSchema) {
|
|
42
|
-
const languages = siteSchema.languages ?? [];
|
|
43
|
-
const liquidFiles = new Map();
|
|
44
|
-
const localeFiles = new Map();
|
|
45
|
-
const otherFiles = new Map();
|
|
46
|
-
const issues = [];
|
|
47
|
-
return {
|
|
48
|
-
siteSchema,
|
|
49
|
-
predefinedFields: new Map(getPredefinedFields().map((f) => [f.fieldName, f])),
|
|
50
|
-
setLocalizedLabel(label) {
|
|
51
|
-
for (const language of languages) {
|
|
52
|
-
const value = label[language];
|
|
53
|
-
if (!value)
|
|
54
|
-
continue;
|
|
55
|
-
let f = localeFiles.get(language);
|
|
56
|
-
if (!f) {
|
|
57
|
-
f = {};
|
|
58
|
-
localeFiles.set(language, f);
|
|
59
|
-
}
|
|
60
|
-
f[language] = value;
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
hasLiquidFile(directory, filename) {
|
|
64
|
-
const path = directory === "root" ? `templates/${filename}` : `templates/${directory}/${filename}`;
|
|
65
|
-
return liquidFiles.has(path);
|
|
66
|
-
},
|
|
67
|
-
addLiquidFile(directory, filename, content, { skipIfExists = false } = {}) {
|
|
68
|
-
const path = directory === "root" ? `templates/${filename}` : `templates/${directory}/${filename}`;
|
|
69
|
-
if (liquidFiles.has(path)) {
|
|
70
|
-
if (skipIfExists)
|
|
71
|
-
return;
|
|
72
|
-
issues.push(`Liquid file already exists, overwrite: "${path}"`);
|
|
73
|
-
}
|
|
74
|
-
liquidFiles.set(path, content);
|
|
75
|
-
},
|
|
76
|
-
addFile(path, content) {
|
|
77
|
-
if (otherFiles.has(path))
|
|
78
|
-
throw new Error(`File already exists: "${path}"`);
|
|
79
|
-
otherFiles.set(path, content);
|
|
80
|
-
},
|
|
81
|
-
toFiles() {
|
|
82
|
-
const files = [
|
|
83
|
-
...Array.from(liquidFiles.entries()).map(([path, content]) => ({ path, content })),
|
|
84
|
-
...Array.from(localeFiles.entries()).map(([language, content]) => ({
|
|
85
|
-
path: `locales/${language}.json`,
|
|
86
|
-
content: JSON.stringify(content, null, 2),
|
|
87
|
-
})),
|
|
88
|
-
...Array.from(otherFiles.entries()).map(([path, content]) => ({ path, content })),
|
|
89
|
-
];
|
|
90
|
-
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
91
|
-
return { files, issues: issues.length > 0 ? issues : undefined };
|
|
92
|
-
},
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
// function getDefaultLiquidContent() {
|
|
96
|
-
// return `{% layout "layouts/main-layout.liquid" doc: doc site: site %}
|
|
97
|
-
// {% block %}
|
|
98
|
-
// <div class="Container">
|
|
99
|
-
// <div class="Page">
|
|
100
|
-
// {{ doc | info }}
|
|
101
|
-
// </div>
|
|
102
|
-
// </div>
|
|
103
|
-
// {% endblock %}`;
|
|
104
|
-
// }
|
|
105
41
|
function templateOf404(ctx) {
|
|
106
42
|
const { siteSchema } = ctx;
|
|
107
43
|
const rawParam = isMultilingual(siteSchema) ? " raw: true" : "";
|
|
@@ -132,13 +68,13 @@ function templateOfLayout(ctx) {
|
|
|
132
68
|
const multilingual = isMultilingual(siteSchema);
|
|
133
69
|
ctx.addLiquidFile("partials", "site-header.liquid", templateOfSiteHeader(ctx));
|
|
134
70
|
ctx.addLiquidFile("partials", "site-footer.liquid", templateOfSiteFooter(ctx));
|
|
135
|
-
const renderHeader = `{% render "partials/site-header" doc: doc
|
|
71
|
+
const renderHeader = `{% render "partials/site-header" doc: doc %}`;
|
|
136
72
|
const headerTemplate = multilingual
|
|
137
73
|
? `{% unless raw %}
|
|
138
74
|
${renderHeader}
|
|
139
75
|
{% endunless %}`
|
|
140
76
|
: renderHeader;
|
|
141
|
-
const renderFooter = `{% render "partials/site-footer" doc: doc
|
|
77
|
+
const renderFooter = `{% render "partials/site-footer" doc: doc %}`;
|
|
142
78
|
const footerTemplate = multilingual
|
|
143
79
|
? `{% unless raw %}
|
|
144
80
|
${renderFooter}
|
package/backend/dist/index.js
CHANGED
|
@@ -43,12 +43,12 @@ const plugin = {
|
|
|
43
43
|
startSiteRemover(rawContext);
|
|
44
44
|
});
|
|
45
45
|
service.setPublicAssetsDirectory(join(packageDir, "frontend", "dist"));
|
|
46
|
-
service.
|
|
47
|
-
service.setRenderState("
|
|
46
|
+
service.registerOutLiquidTagFunction("siteGeneratorApp", (service) => {
|
|
47
|
+
service.setRenderState("siteGeneratorApp", true);
|
|
48
48
|
return `<div id="site-generator-app"></div>`;
|
|
49
|
-
});
|
|
49
|
+
}, { raw: true });
|
|
50
50
|
service.registerHeadTags(({ state }) => {
|
|
51
|
-
if (!state.get("
|
|
51
|
+
if (!state.get("siteGeneratorApp"))
|
|
52
52
|
return;
|
|
53
53
|
return [
|
|
54
54
|
makeStylesheetLinkAsyncTag(`${service.pluginAssetsUrl}/frontend.css`),
|