@paroicms/site-generator-plugin 0.27.8 → 0.28.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/fake-content-generator/content-helpers.js +13 -0
- package/backend/dist/generator/fake-content-generator/create-database-with-fake-content.js +10 -23
- package/backend/dist/generator/fake-content-generator/create-node-contents.js +57 -98
- package/backend/dist/generator/fake-content-generator/fill-lnodes.js +177 -52
- package/backend/dist/generator/fake-content-generator/fill-site-fields.js +22 -54
- package/backend/dist/generator/fake-content-generator/fill-taxonomy-fields.js +15 -24
- package/backend/dist/generator/fake-content-generator/generate-fake-content.js +4 -2
- package/backend/dist/generator/fake-content-generator/invoke-generate-fake-content.js +11 -3
- package/backend/dist/generator/lib/create-prompt.js +2 -2
- package/backend/dist/generator/lib/parse-llm-response.js +65 -10
- package/backend/dist/generator/llm-queries/invoke-new-site-analysis.js +15 -1
- package/backend/dist/generator/site-generator/schema-with-authors.js +69 -0
- package/backend/dist/generator/site-generator/site-generator.js +18 -3
- package/backend/dist/generator/site-generator/theme-creator.js +4 -2
- package/backend/dist/generator/site-schema-generator/create-site-schema.js +4 -2
- package/backend/dist/lib/create-raw-context.js +1 -1
- package/backend/dist/lib/site-remover.js +3 -1
- package/backend/prompts/generate-fake-content-multiple-terms.md +21 -0
- package/frontend/dist/frontend.mjs +15 -15
- package/package.json +8 -9
- package/backend/prompts/0-context.md +0 -11
|
@@ -15,3 +15,16 @@ export function dedupMessages(messages) {
|
|
|
15
15
|
return counter && counter > 1 ? `${m} (×${counter})` : m;
|
|
16
16
|
});
|
|
17
17
|
}
|
|
18
|
+
export function getTaxonomyTypeNames(siteSchema) {
|
|
19
|
+
const taxonomyTypeNames = new Set();
|
|
20
|
+
for (const nodeType of Object.values(siteSchema.nodeTypes)) {
|
|
21
|
+
if (!nodeType.fields)
|
|
22
|
+
continue;
|
|
23
|
+
for (const field of nodeType.fields) {
|
|
24
|
+
if (field.dataType === "labeling") {
|
|
25
|
+
taxonomyTypeNames.add(field.taxonomy);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return taxonomyTypeNames;
|
|
30
|
+
}
|
|
@@ -1,27 +1,16 @@
|
|
|
1
1
|
import { getPartTypeByName, getRegularDocumentTypeByName, getRoutingDocumentTypeByName, } from "@paroicms/internal-anywhere-lib";
|
|
2
|
-
import { createSimpleTranslator, } from "@paroicms/public-server-lib";
|
|
3
2
|
import { updateGeneratedSiteStepSetAsCompleted, } from "../../db/db-write.queries.js";
|
|
4
3
|
import { createTaskCollector } from "../lib/tasks.js";
|
|
5
4
|
import { createGeneratedContentReport } from "./content-report.js";
|
|
6
5
|
import { addParts, addRegularDocuments, updateRoutingDocument } from "./fill-lnodes.js";
|
|
7
6
|
import { updateSiteFields } from "./fill-site-fields.js";
|
|
8
7
|
import { updateLNodesWithTaxonomies } from "./fill-taxonomy-fields.js";
|
|
9
|
-
export async function fillSiteWithFakeContent(ctx, stepHandle, {
|
|
10
|
-
const {
|
|
11
|
-
const { fqdn } = regSite;
|
|
8
|
+
export async function fillSiteWithFakeContent(ctx, stepHandle, { homeRoutingCluster, localizedValues, }) {
|
|
9
|
+
const { siteSchema } = ctx;
|
|
12
10
|
const report = createGeneratedContentReport();
|
|
13
|
-
|
|
14
|
-
const schemaI18n = createSimpleTranslator({
|
|
15
|
-
labels: siteSchema.l10n,
|
|
16
|
-
logger: ctx.logger,
|
|
17
|
-
});
|
|
18
|
-
await updateSiteFields(ctx, report, { fqdn, siteSchema, siteTitle: localizedValues.siteTitle });
|
|
11
|
+
await updateSiteFields(ctx, report, { siteTitle: localizedValues.siteTitle });
|
|
19
12
|
const tasks = createTaskCollector(ctx);
|
|
20
13
|
fillRoutingDocumentAndAddChildren(ctx, tasks, report, {
|
|
21
|
-
fqdn,
|
|
22
|
-
siteSchema,
|
|
23
|
-
schemaI18n,
|
|
24
|
-
}, {
|
|
25
14
|
clusterNode: homeRoutingCluster,
|
|
26
15
|
nodeType: siteSchema.nodeTypes.home,
|
|
27
16
|
});
|
|
@@ -29,9 +18,7 @@ export async function fillSiteWithFakeContent(ctx, stepHandle, { regSite, locali
|
|
|
29
18
|
const { doneCount, errorMessages } = await promise;
|
|
30
19
|
const results = report.getResults();
|
|
31
20
|
await updateLNodesWithTaxonomies(ctx, {
|
|
32
|
-
siteSchema,
|
|
33
21
|
idPicker: results.idPicker,
|
|
34
|
-
fqdn,
|
|
35
22
|
});
|
|
36
23
|
if (errorMessages.length > 0) {
|
|
37
24
|
ctx.logger.warn(`Failed to generate documents:\n - ${errorMessages.join("\n - ")}`);
|
|
@@ -45,14 +32,14 @@ export async function fillSiteWithFakeContent(ctx, stepHandle, { regSite, locali
|
|
|
45
32
|
contentErrors: errorMessages.length > 0 ? errorMessages.join("\n - ") : null,
|
|
46
33
|
});
|
|
47
34
|
}
|
|
48
|
-
function fillRoutingDocumentAndAddChildren(ctx, tasks, report,
|
|
35
|
+
function fillRoutingDocumentAndAddChildren(ctx, tasks, report, nodeOptions) {
|
|
49
36
|
const { clusterNode, nodeType } = nodeOptions;
|
|
50
|
-
const { siteSchema } =
|
|
51
|
-
tasks.add(() => updateRoutingDocument(ctx, report,
|
|
37
|
+
const { siteSchema } = ctx;
|
|
38
|
+
tasks.add(() => updateRoutingDocument(ctx, report, nodeOptions));
|
|
52
39
|
for (const listType of nodeType.lists ?? []) {
|
|
53
40
|
for (const typeName of listType.parts) {
|
|
54
41
|
const partType = getPartTypeByName(siteSchema, typeName);
|
|
55
|
-
tasks.add(() => addParts(ctx, report,
|
|
42
|
+
tasks.add(() => addParts(ctx, report, {
|
|
56
43
|
parentNodeId: clusterNode.nodeId,
|
|
57
44
|
nodeType: partType,
|
|
58
45
|
documentType: nodeType,
|
|
@@ -63,15 +50,15 @@ function fillRoutingDocumentAndAddChildren(ctx, tasks, report, siteOptions, node
|
|
|
63
50
|
const childType = getRoutingDocumentTypeByName(siteSchema, typeName);
|
|
64
51
|
const childIds = clusterNode.children?.[typeName];
|
|
65
52
|
if (!childIds)
|
|
66
|
-
|
|
67
|
-
fillRoutingDocumentAndAddChildren(ctx, tasks, report,
|
|
53
|
+
continue;
|
|
54
|
+
fillRoutingDocumentAndAddChildren(ctx, tasks, report, {
|
|
68
55
|
clusterNode: childIds,
|
|
69
56
|
nodeType: childType,
|
|
70
57
|
});
|
|
71
58
|
}
|
|
72
59
|
for (const typeName of nodeType.regularChildren ?? []) {
|
|
73
60
|
const childType = getRegularDocumentTypeByName(siteSchema, typeName);
|
|
74
|
-
tasks.add(() => addRegularDocuments(ctx, report,
|
|
61
|
+
tasks.add(() => addRegularDocuments(ctx, report, {
|
|
75
62
|
parentNodeId: clusterNode.nodeId,
|
|
76
63
|
nodeType: childType,
|
|
77
64
|
documentType: childType,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { convertMarkdownToTiptap } from "@paroicms/markdown-to-tiptap-json";
|
|
2
1
|
import { getRandomImagePath } from "../lib/images-lib.js";
|
|
3
2
|
export function createNodeContents(options) {
|
|
4
3
|
const { ctx, nodeType, count, generatedContents, outputTags, language } = options;
|
|
@@ -21,30 +20,57 @@ export function createNodeContents(options) {
|
|
|
21
20
|
function createNodeContent(options) {
|
|
22
21
|
const { ctx, nodeType, language, generatedContent, outputTags } = options;
|
|
23
22
|
const fieldTypes = nodeType.fields ?? [];
|
|
23
|
+
const medias = [];
|
|
24
24
|
if (nodeType.kind === "document") {
|
|
25
25
|
const { title, ...generatedFields } = generatedContent;
|
|
26
|
-
const fields =
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
const fields = toUpdateFieldValues(ctx, generatedFields, outputTags, fieldTypes, language);
|
|
27
|
+
if (nodeType.withFeaturedImage) {
|
|
28
|
+
medias.push({
|
|
29
|
+
kind: "featuredImage",
|
|
30
|
+
filePath: getRandomImagePath(),
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
collectMediaFieldEntries(fieldTypes, medias);
|
|
34
|
+
return { title, fields, medias: medias.length > 0 ? medias : undefined };
|
|
35
|
+
}
|
|
36
|
+
const fields = toUpdateFieldValues(ctx, generatedContent, outputTags, fieldTypes, language);
|
|
37
|
+
collectMediaFieldEntries(fieldTypes, medias);
|
|
38
|
+
return { fields, medias: medias.length > 0 ? medias : undefined };
|
|
39
|
+
}
|
|
40
|
+
function collectMediaFieldEntries(fieldTypes, medias) {
|
|
41
|
+
for (const fieldType of fieldTypes) {
|
|
42
|
+
if (fieldType.storedAs !== "mediaHandle")
|
|
43
|
+
continue;
|
|
44
|
+
if (fieldType.withGallery || fieldType.dataType === "gallery") {
|
|
45
|
+
// Gallery field: generate 3-5 random images
|
|
46
|
+
const count = 3 + Math.floor(Math.random() * 3);
|
|
47
|
+
const filePaths = [];
|
|
48
|
+
for (let i = 0; i < count; i++) {
|
|
49
|
+
filePaths.push(getRandomImagePath());
|
|
30
50
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
51
|
+
medias.push({
|
|
52
|
+
kind: "mediaGallery",
|
|
53
|
+
fieldName: fieldType.name,
|
|
54
|
+
filePaths,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// Single media field
|
|
59
|
+
medias.push({
|
|
60
|
+
kind: "mediaField",
|
|
61
|
+
fieldName: fieldType.name,
|
|
62
|
+
filePath: getRandomImagePath(),
|
|
63
|
+
});
|
|
64
|
+
}
|
|
37
65
|
}
|
|
38
|
-
const fields = toRiFieldSetContent(ctx, generatedContent, outputTags, fieldTypes, language);
|
|
39
|
-
return { fields };
|
|
40
66
|
}
|
|
41
|
-
function
|
|
67
|
+
function toUpdateFieldValues(ctx, content, outputTags, fieldTypes, language) {
|
|
42
68
|
const result = {};
|
|
43
69
|
for (const fieldType of fieldTypes) {
|
|
44
70
|
const isMarkdown = outputTags.find((tag) => tag.key === fieldType.name)?.format === "markdown";
|
|
45
71
|
const localized = fieldType.localized;
|
|
46
72
|
const value = content[fieldType.name];
|
|
47
|
-
const
|
|
73
|
+
const fieldValue = toUpdateFieldValue({
|
|
48
74
|
ctx,
|
|
49
75
|
fieldType,
|
|
50
76
|
generatedValue: value,
|
|
@@ -52,98 +78,37 @@ function toRiFieldSetContent(ctx, content, outputTags, fieldTypes, language) {
|
|
|
52
78
|
language,
|
|
53
79
|
isMarkdown,
|
|
54
80
|
});
|
|
55
|
-
if (
|
|
56
|
-
result[fieldType.name] =
|
|
81
|
+
if (fieldValue !== undefined) {
|
|
82
|
+
result[fieldType.name] = fieldValue;
|
|
57
83
|
}
|
|
58
84
|
}
|
|
59
85
|
return result;
|
|
60
86
|
}
|
|
61
|
-
function
|
|
62
|
-
const {
|
|
87
|
+
function toUpdateFieldValue(options) {
|
|
88
|
+
const { fieldType, generatedValue, localized, language, isMarkdown } = options;
|
|
89
|
+
// Return the markdown string directly for markdown fields
|
|
63
90
|
if (generatedValue !== undefined && localized) {
|
|
64
91
|
if (fieldType.dataType === "string") {
|
|
65
|
-
return
|
|
66
|
-
|
|
67
|
-
localized: true,
|
|
68
|
-
value: generatedValue,
|
|
69
|
-
};
|
|
92
|
+
// For localized strings, return the value for the language
|
|
93
|
+
return generatedValue[language];
|
|
70
94
|
}
|
|
71
|
-
if (fieldType.dataType === "json" &&
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
isMarkdown) {
|
|
75
|
-
return toTiptapRiJsonContent(ctx, generatedValue);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
if (fieldType.storedAs === "mediaHandle") {
|
|
79
|
-
if (fieldType.dataType === "media") {
|
|
80
|
-
const file = getRandomImagePath();
|
|
81
|
-
return localized
|
|
82
|
-
? {
|
|
83
|
-
dataType: "media",
|
|
84
|
-
localized: true,
|
|
85
|
-
value: { [language]: { file } },
|
|
86
|
-
}
|
|
87
|
-
: {
|
|
88
|
-
dataType: "media",
|
|
89
|
-
localized: false,
|
|
90
|
-
value: { file },
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
if (fieldType.dataType === "gallery") {
|
|
94
|
-
const files = [
|
|
95
|
-
getRandomImagePath(),
|
|
96
|
-
getRandomImagePath(),
|
|
97
|
-
getRandomImagePath(),
|
|
98
|
-
getRandomImagePath(),
|
|
99
|
-
getRandomImagePath(),
|
|
100
|
-
getRandomImagePath(),
|
|
101
|
-
];
|
|
102
|
-
return localized
|
|
103
|
-
? {
|
|
104
|
-
dataType: "gallery",
|
|
105
|
-
localized: true,
|
|
106
|
-
value: { [language]: { files } },
|
|
107
|
-
}
|
|
108
|
-
: {
|
|
109
|
-
dataType: "gallery",
|
|
110
|
-
localized: false,
|
|
111
|
-
value: { files },
|
|
112
|
-
};
|
|
95
|
+
if (fieldType.dataType === "json" && fieldType.renderAs === "html" && isMarkdown) {
|
|
96
|
+
// Return the markdown string for the language
|
|
97
|
+
return generatedValue[language];
|
|
113
98
|
}
|
|
114
99
|
}
|
|
100
|
+
// Skip media fields - they are handled separately via setMedia
|
|
101
|
+
if (fieldType.storedAs === "mediaHandle")
|
|
102
|
+
return undefined;
|
|
115
103
|
if (fieldType.dataType === "string") {
|
|
116
104
|
const value = getDefaultStringValueForField(fieldType.name);
|
|
117
105
|
if (value !== undefined) {
|
|
118
|
-
return
|
|
119
|
-
? {
|
|
120
|
-
dataType: "string",
|
|
121
|
-
localized: true,
|
|
122
|
-
value: { [language]: value },
|
|
123
|
-
}
|
|
124
|
-
: {
|
|
125
|
-
dataType: "string",
|
|
126
|
-
localized: false,
|
|
127
|
-
value,
|
|
128
|
-
};
|
|
106
|
+
return value;
|
|
129
107
|
}
|
|
130
108
|
}
|
|
131
109
|
if (fieldType.storedAs === "labeling")
|
|
132
110
|
return; // TODO: Generate values for labeling fields
|
|
133
111
|
}
|
|
134
|
-
function toTiptapRiJsonContent(ctx, content) {
|
|
135
|
-
return {
|
|
136
|
-
dataType: "json",
|
|
137
|
-
localized: true,
|
|
138
|
-
value: Object.fromEntries(Object.entries(content).map(([language, text]) => {
|
|
139
|
-
const { result, issues } = convertMarkdownToTiptap(text);
|
|
140
|
-
if (issues && issues.length > 0) {
|
|
141
|
-
ctx.logger.warn(`Markdown conversion issues for language ${language}:`, issues);
|
|
142
|
-
}
|
|
143
|
-
return [language, { j: result }];
|
|
144
|
-
})),
|
|
145
|
-
};
|
|
146
|
-
}
|
|
147
112
|
function getDefaultStringValueForField(fieldName) {
|
|
148
113
|
if (fieldName === "phone" || fieldName === "phone2")
|
|
149
114
|
return "0123456789";
|
|
@@ -153,15 +118,9 @@ function getDefaultStringValueForField(fieldName) {
|
|
|
153
118
|
return new Date().toISOString();
|
|
154
119
|
return;
|
|
155
120
|
}
|
|
156
|
-
export function generateLocalizedFooterMention(
|
|
121
|
+
export function generateLocalizedFooterMention(siteSchema) {
|
|
157
122
|
const { languages } = siteSchema;
|
|
158
|
-
return Object.fromEntries(languages.map((language) =>
|
|
159
|
-
const { result, issues } = convertMarkdownToTiptap(getPoweredByLabel(language));
|
|
160
|
-
if (issues && issues.length > 0) {
|
|
161
|
-
ctx.logger.warn(`Markdown conversion issues for footer mention (${language}):`, issues);
|
|
162
|
-
}
|
|
163
|
-
return [language, { j: result }];
|
|
164
|
-
}));
|
|
123
|
+
return Object.fromEntries(languages.map((language) => [language, getPoweredByLabel(language)]));
|
|
165
124
|
}
|
|
166
125
|
function getPoweredByLabel(language) {
|
|
167
126
|
const translations = {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { encodeLNodeId, } from "@paroicms/public-anywhere-lib";
|
|
2
|
+
import { getHandleOfFeaturedImage, getHandleOfFieldOnNode } from "@paroicms/public-server-lib";
|
|
3
|
+
import { dedupMessages, getTaxonomyTypeNames } from "./content-helpers.js";
|
|
2
4
|
import { generateFieldSetContent, generateMultipleFieldSetContents, } from "./generate-fake-content.js";
|
|
3
|
-
export async function updateRoutingDocument(ctx, report,
|
|
5
|
+
export async function updateRoutingDocument(ctx, report, nodeOptions) {
|
|
4
6
|
ctx.logger.debug(`[TASK] Updating routing document "${nodeOptions.nodeType.typeName}"…`);
|
|
5
7
|
const { clusterNode, nodeType } = nodeOptions;
|
|
6
|
-
const {
|
|
8
|
+
const { siteSchema, schemaI18n, siteConnector } = ctx;
|
|
7
9
|
const content = await generateFieldSetContent(ctx, {
|
|
8
10
|
nodeType,
|
|
9
11
|
documentType: nodeType,
|
|
@@ -12,11 +14,10 @@ export async function updateRoutingDocument(ctx, report, siteOptions, nodeOption
|
|
|
12
14
|
withTitle: false,
|
|
13
15
|
llmTaskName: nodeType.kebabName,
|
|
14
16
|
}, report);
|
|
15
|
-
|
|
16
|
-
nodeId: clusterNode.nodeId,
|
|
17
|
-
content: toRiDocumentContent(content, nodeType),
|
|
18
|
-
});
|
|
17
|
+
// Use updateFields for each language
|
|
19
18
|
for (const language of siteSchema.languages) {
|
|
19
|
+
const lNodeId = encodeLNodeId({ nodeId: clusterNode.nodeId, language });
|
|
20
|
+
await siteConnector.updateFields(lNodeId, content.fields);
|
|
20
21
|
report.addId({
|
|
21
22
|
typeName: nodeType.typeName,
|
|
22
23
|
id: {
|
|
@@ -27,37 +28,127 @@ export async function updateRoutingDocument(ctx, report, siteOptions, nodeOption
|
|
|
27
28
|
});
|
|
28
29
|
}
|
|
29
30
|
}
|
|
30
|
-
export async function addRegularDocuments(ctx, report,
|
|
31
|
+
export async function addRegularDocuments(ctx, report, nodeOptions) {
|
|
32
|
+
// Special case: author terms are created without LLM
|
|
33
|
+
if (nodeOptions.nodeType.typeName === "author") {
|
|
34
|
+
await addSingleAuthor(ctx, report, nodeOptions);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
31
37
|
ctx.logger.debug(`[TASK] Adding regular documents "${nodeOptions.nodeType.typeName}"…`);
|
|
32
38
|
const { parentNodeId, nodeType, documentType } = nodeOptions;
|
|
33
|
-
const {
|
|
39
|
+
const { siteSchema, schemaI18n, siteConnector } = ctx;
|
|
40
|
+
const taxonomyTypeNames = getTaxonomyTypeNames(siteSchema);
|
|
41
|
+
const isTaxonomyTerm = taxonomyTypeNames.has(nodeType.typeName);
|
|
34
42
|
const tolerateErrors = {
|
|
35
43
|
errorMessages: [],
|
|
36
44
|
};
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const maxAttempts = 3;
|
|
46
|
+
let list = [];
|
|
47
|
+
for (let attempt = 1; attempt <= maxAttempts; ++attempt) {
|
|
48
|
+
list = await generateMultipleFieldSetContents(ctx, {
|
|
49
|
+
siteSchema,
|
|
50
|
+
nodeType,
|
|
51
|
+
documentType,
|
|
52
|
+
schemaI18n,
|
|
53
|
+
count: getDefaultNodeContentCount(nodeType, siteSchema),
|
|
54
|
+
withTitle: true,
|
|
55
|
+
tolerateErrors,
|
|
56
|
+
llmTaskName: nodeType.kebabName,
|
|
57
|
+
isTaxonomyTerm,
|
|
58
|
+
}, report);
|
|
59
|
+
if (list.length > 0)
|
|
60
|
+
break;
|
|
61
|
+
if (attempt < maxAttempts) {
|
|
62
|
+
ctx.logger.debug(`[RETRY] Empty result for ${nodeType.typeName}, attempt ${attempt}/${maxAttempts}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
47
65
|
const errorMessages = dedupMessages(tolerateErrors.errorMessages);
|
|
48
66
|
if (errorMessages.length > 0) {
|
|
49
67
|
ctx.logger.warn(`Error generating content for ${nodeType.typeName}:\n - ${errorMessages.join("\n - ")}`);
|
|
50
68
|
}
|
|
51
|
-
const
|
|
69
|
+
for (const content of list) {
|
|
70
|
+
const [firstLanguage, ...otherLanguages] = siteSchema.languages;
|
|
71
|
+
const parentLNodeId = encodeLNodeId({ nodeId: parentNodeId, language: firstLanguage });
|
|
72
|
+
const title = content.title?.[firstLanguage];
|
|
73
|
+
const info = await siteConnector.createDocument({
|
|
74
|
+
parentLNodeId,
|
|
75
|
+
typeName: nodeType.typeName,
|
|
76
|
+
title,
|
|
77
|
+
values: content.fields,
|
|
78
|
+
});
|
|
79
|
+
const nodeId = info.nodeId;
|
|
80
|
+
await uploadMediaEntries(ctx, nodeId, content.medias);
|
|
81
|
+
report.addId({
|
|
82
|
+
typeName: nodeType.typeName,
|
|
83
|
+
id: { nodeId, language: firstLanguage },
|
|
84
|
+
parentNodeId,
|
|
85
|
+
});
|
|
86
|
+
for (const language of otherLanguages) {
|
|
87
|
+
if (!content.title?.[language])
|
|
88
|
+
continue;
|
|
89
|
+
await siteConnector.createDocumentTranslation({
|
|
90
|
+
nodeId,
|
|
91
|
+
language,
|
|
92
|
+
title: content.title?.[language],
|
|
93
|
+
values: content.fields,
|
|
94
|
+
});
|
|
95
|
+
report.addId({
|
|
96
|
+
typeName: nodeType.typeName,
|
|
97
|
+
id: { nodeId, language },
|
|
98
|
+
parentNodeId,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const HERO_NAMES = [
|
|
104
|
+
"Mario",
|
|
105
|
+
"Luigi",
|
|
106
|
+
"Link",
|
|
107
|
+
"Zelda",
|
|
108
|
+
"Kirby",
|
|
109
|
+
"Pikachu",
|
|
110
|
+
"Yoshi",
|
|
111
|
+
"Sonic",
|
|
112
|
+
"Toad",
|
|
113
|
+
"Peach",
|
|
114
|
+
];
|
|
115
|
+
async function addSingleAuthor(ctx, report, nodeOptions) {
|
|
116
|
+
ctx.logger.debug("[TASK] Adding single author…");
|
|
117
|
+
const { parentNodeId, nodeType } = nodeOptions;
|
|
118
|
+
const { siteSchema, siteConnector } = ctx;
|
|
119
|
+
const title = HERO_NAMES[Math.floor(Math.random() * HERO_NAMES.length)];
|
|
120
|
+
const [firstLanguage, ...otherLanguages] = siteSchema.languages;
|
|
121
|
+
const parentLNodeId = encodeLNodeId({ nodeId: parentNodeId, language: firstLanguage });
|
|
122
|
+
const info = await siteConnector.createDocument({
|
|
123
|
+
parentLNodeId,
|
|
124
|
+
typeName: nodeType.typeName,
|
|
125
|
+
title,
|
|
126
|
+
values: {},
|
|
127
|
+
});
|
|
128
|
+
const nodeId = info.nodeId;
|
|
129
|
+
report.addId({
|
|
130
|
+
typeName: nodeType.typeName,
|
|
131
|
+
id: { nodeId, language: firstLanguage },
|
|
52
132
|
parentNodeId,
|
|
53
|
-
contents: list.map((content) => toRiDocumentContent(content, nodeType)),
|
|
54
133
|
});
|
|
55
|
-
|
|
134
|
+
for (const language of otherLanguages) {
|
|
135
|
+
await siteConnector.createDocumentTranslation({
|
|
136
|
+
nodeId,
|
|
137
|
+
language,
|
|
138
|
+
title,
|
|
139
|
+
values: {},
|
|
140
|
+
});
|
|
141
|
+
report.addId({
|
|
142
|
+
typeName: nodeType.typeName,
|
|
143
|
+
id: { nodeId, language },
|
|
144
|
+
parentNodeId,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
56
147
|
}
|
|
57
|
-
export async function addParts(ctx, report,
|
|
148
|
+
export async function addParts(ctx, report, nodeOptions) {
|
|
58
149
|
ctx.logger.debug(`[TASK] Adding parts "${nodeOptions.nodeType.typeName}"…`);
|
|
59
150
|
const { parentNodeId, nodeType, documentType } = nodeOptions;
|
|
60
|
-
const {
|
|
151
|
+
const { siteSchema, schemaI18n, siteConnector } = ctx;
|
|
61
152
|
const tolerateErrors = {
|
|
62
153
|
errorMessages: [],
|
|
63
154
|
};
|
|
@@ -66,7 +157,7 @@ export async function addParts(ctx, report, siteOptions, nodeOptions) {
|
|
|
66
157
|
nodeType,
|
|
67
158
|
documentType,
|
|
68
159
|
schemaI18n,
|
|
69
|
-
count: getDefaultNodeContentCount(nodeType),
|
|
160
|
+
count: getDefaultNodeContentCount(nodeType, siteSchema),
|
|
70
161
|
withTitle: true,
|
|
71
162
|
tolerateErrors,
|
|
72
163
|
llmTaskName: nodeType.kebabName,
|
|
@@ -75,41 +166,75 @@ export async function addParts(ctx, report, siteOptions, nodeOptions) {
|
|
|
75
166
|
if (errorMessages.length > 0) {
|
|
76
167
|
ctx.logger.warn(`Error generating content for ${nodeType.typeName}:\n - ${errorMessages.join("\n - ")}`);
|
|
77
168
|
}
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
function toRiPartContent(content, nodeType) {
|
|
95
|
-
const { fields } = content;
|
|
96
|
-
return {
|
|
97
|
-
kind: "part",
|
|
98
|
-
typeName: nodeType.typeName,
|
|
99
|
-
fields,
|
|
100
|
-
};
|
|
169
|
+
for (const content of list) {
|
|
170
|
+
const [firstLanguage, ..._otherLanguages] = siteSchema.languages;
|
|
171
|
+
const parentLNodeId = encodeLNodeId({ nodeId: parentNodeId, language: firstLanguage });
|
|
172
|
+
const info = await siteConnector.createPart({
|
|
173
|
+
parentLNodeId,
|
|
174
|
+
typeName: nodeType.typeName,
|
|
175
|
+
values: content.fields,
|
|
176
|
+
});
|
|
177
|
+
const nodeId = info.nodeId;
|
|
178
|
+
await uploadMediaEntries(ctx, nodeId, content.medias);
|
|
179
|
+
report.addId({
|
|
180
|
+
typeName: nodeType.typeName,
|
|
181
|
+
id: { nodeId, language: firstLanguage },
|
|
182
|
+
parentNodeId,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
101
185
|
}
|
|
102
|
-
function getDefaultNodeContentCount(nodeType) {
|
|
186
|
+
function getDefaultNodeContentCount(nodeType, siteSchema) {
|
|
103
187
|
if (nodeType.kind === "site")
|
|
104
188
|
throw new Error("Cannot generate content for site node type");
|
|
105
189
|
if (nodeType.kind === "document") {
|
|
106
190
|
if (nodeType.documentKind === "routing")
|
|
107
191
|
return 1;
|
|
192
|
+
const taxonomyTypeNames = getTaxonomyTypeNames(siteSchema);
|
|
193
|
+
if (taxonomyTypeNames.has(nodeType.typeName))
|
|
194
|
+
return 4;
|
|
108
195
|
if (nodeType.route === ":yyyy/:mm/:dd/:relativeId-:slug")
|
|
109
|
-
return 14;
|
|
110
|
-
return 8;
|
|
196
|
+
return 14;
|
|
197
|
+
return 8;
|
|
111
198
|
}
|
|
112
199
|
if (nodeType.kind === "part")
|
|
113
|
-
return 8;
|
|
200
|
+
return 8;
|
|
114
201
|
throw new Error(`Unknown node type kind: ${nodeType.kind}`);
|
|
115
202
|
}
|
|
203
|
+
async function uploadMediaEntries(ctx, nodeId, medias) {
|
|
204
|
+
if (!medias || medias.length === 0)
|
|
205
|
+
return;
|
|
206
|
+
const { siteConnector, homeRoutingCluster } = ctx;
|
|
207
|
+
const siteNodeId = homeRoutingCluster.siteNodeId;
|
|
208
|
+
for (const entry of medias) {
|
|
209
|
+
switch (entry.kind) {
|
|
210
|
+
case "featuredImage": {
|
|
211
|
+
await siteConnector.setMedia({
|
|
212
|
+
handle: getHandleOfFeaturedImage(nodeId),
|
|
213
|
+
filePath: entry.filePath,
|
|
214
|
+
replace: true,
|
|
215
|
+
});
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
case "mediaField": {
|
|
219
|
+
await siteConnector.setMedia({
|
|
220
|
+
handle: getHandleOfFieldOnNode({ siteNodeId, nodeId, fieldName: entry.fieldName }),
|
|
221
|
+
filePath: entry.filePath,
|
|
222
|
+
replace: true,
|
|
223
|
+
});
|
|
224
|
+
break;
|
|
225
|
+
}
|
|
226
|
+
case "mediaGallery": {
|
|
227
|
+
const handle = getHandleOfFieldOnNode({ siteNodeId, nodeId, fieldName: entry.fieldName });
|
|
228
|
+
// Gallery: first image replaces, subsequent ones add
|
|
229
|
+
for (let i = 0; i < entry.filePaths.length; i++) {
|
|
230
|
+
await siteConnector.setMedia({
|
|
231
|
+
handle,
|
|
232
|
+
filePath: entry.filePaths[i],
|
|
233
|
+
replace: i === 0,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -1,63 +1,31 @@
|
|
|
1
|
+
import { getHandleOfSiteField } from "@paroicms/public-server-lib";
|
|
1
2
|
import { getRandomImagePath } from "../lib/images-lib.js";
|
|
2
3
|
import { generateLocalizedFooterMention } from "./create-node-contents.js";
|
|
3
4
|
export async function updateSiteFields(ctx, _report, options) {
|
|
4
|
-
const {
|
|
5
|
+
const { logger, siteSchema, siteConnector } = ctx;
|
|
5
6
|
logger.debug("Updating site fields…");
|
|
6
|
-
const {
|
|
7
|
+
const { siteTitle } = options;
|
|
7
8
|
const siteType = siteSchema.nodeTypes._site;
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
[
|
|
19
|
-
"favicon",
|
|
20
|
-
{
|
|
21
|
-
dataType: "media",
|
|
22
|
-
localized: false,
|
|
23
|
-
value: {
|
|
24
|
-
file: logoImageFile,
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
],
|
|
28
|
-
[
|
|
29
|
-
"ogImage",
|
|
30
|
-
{
|
|
31
|
-
dataType: "media",
|
|
32
|
-
localized: false,
|
|
33
|
-
value: {
|
|
34
|
-
file: logoImageFile,
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
];
|
|
39
|
-
if (siteType.fields.some((f) => f.name === "logo")) {
|
|
40
|
-
fields.push([
|
|
41
|
-
"logo",
|
|
42
|
-
{
|
|
43
|
-
dataType: "media",
|
|
44
|
-
localized: false,
|
|
45
|
-
value: {
|
|
46
|
-
file: logoImageFile,
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
]);
|
|
9
|
+
const footerMention = generateLocalizedFooterMention(siteSchema);
|
|
10
|
+
// Update site fields for each language
|
|
11
|
+
for (const language of siteSchema.languages) {
|
|
12
|
+
const values = {
|
|
13
|
+
title: siteTitle[language],
|
|
14
|
+
};
|
|
15
|
+
if (siteType.fields.some((f) => f.name === "footerMention")) {
|
|
16
|
+
values.footerMention = footerMention[language];
|
|
17
|
+
}
|
|
18
|
+
await siteConnector.updateSiteFields(language, values);
|
|
50
19
|
}
|
|
51
|
-
|
|
52
|
-
fields.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
20
|
+
for (const fieldName of ["favicon", "ogImage", "logo"]) {
|
|
21
|
+
const fieldType = siteType.fields.find((f) => f.name === fieldName);
|
|
22
|
+
if (!fieldType || fieldType.storedAs !== "mediaHandle")
|
|
23
|
+
continue;
|
|
24
|
+
await siteConnector.setMedia({
|
|
25
|
+
handle: getHandleOfSiteField(fieldName),
|
|
26
|
+
filePath: getRandomImagePath(),
|
|
27
|
+
replace: true,
|
|
28
|
+
});
|
|
60
29
|
}
|
|
61
|
-
await service.connector.updateSiteFields(fqdn, Object.fromEntries(fields));
|
|
62
30
|
logger.debug("… Site fields updated");
|
|
63
31
|
}
|