@paroicms/site-generator-plugin 0.5.0 → 0.5.2

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.
@@ -33,6 +33,16 @@ function fillRoutingDocumentAndAddChildren(ctx, tasks, siteOptions, nodeOptions)
33
33
  const { routingIds, nodeType } = nodeOptions;
34
34
  const { siteSchema } = siteOptions;
35
35
  tasks.add(() => updateRoutingDocument(ctx, siteOptions, nodeOptions));
36
+ for (const listType of nodeType.lists ?? []) {
37
+ for (const typeName of listType.parts) {
38
+ const partType = getPartTypeByName(siteSchema, typeName);
39
+ tasks.add(() => addParts(ctx, siteOptions, {
40
+ parentNodeId: routingIds.nodeId,
41
+ nodeType: partType,
42
+ documentType: nodeType,
43
+ }));
44
+ }
45
+ }
36
46
  for (const typeName of nodeType.routingChildren ?? []) {
37
47
  const childType = getRoutingDocumentTypeByName(siteSchema, typeName);
38
48
  const childIds = routingIds.children?.[typeName];
@@ -48,17 +58,9 @@ function fillRoutingDocumentAndAddChildren(ctx, tasks, siteOptions, nodeOptions)
48
58
  tasks.add(() => addRegularDocuments(ctx, siteOptions, {
49
59
  parentNodeId: routingIds.nodeId,
50
60
  nodeType: childType,
61
+ documentType: childType,
51
62
  }));
52
63
  }
53
- for (const listType of nodeType.lists ?? []) {
54
- for (const typeName of listType.parts) {
55
- const childType = getPartTypeByName(siteSchema, typeName);
56
- tasks.add(() => addParts(ctx, siteOptions, {
57
- parentNodeId: routingIds.nodeId,
58
- nodeType: childType,
59
- }));
60
- }
61
- }
62
64
  }
63
65
  async function updateSiteFields(ctx, options) {
64
66
  const { service, logger } = ctx;
@@ -127,6 +129,7 @@ async function updateRoutingDocument(ctx, siteOptions, nodeOptions) {
127
129
  const { fqdn, siteSchema, schemaI18n } = siteOptions;
128
130
  const content = await generateFieldSetContent(ctx, {
129
131
  nodeType,
132
+ documentType: nodeType,
130
133
  siteSchema,
131
134
  schemaI18n,
132
135
  withTitle: false,
@@ -139,7 +142,7 @@ async function updateRoutingDocument(ctx, siteOptions, nodeOptions) {
139
142
  }
140
143
  async function addRegularDocuments(ctx, siteOptions, nodeOptions) {
141
144
  ctx.logger.debug(`[TASK] Adding regular documents "${nodeOptions.nodeType.typeName}"…`);
142
- const { parentNodeId, nodeType } = nodeOptions;
145
+ const { parentNodeId, nodeType, documentType } = nodeOptions;
143
146
  const { fqdn, siteSchema, schemaI18n } = siteOptions;
144
147
  const tolerateErrors = {
145
148
  errorMessages: [],
@@ -147,6 +150,7 @@ async function addRegularDocuments(ctx, siteOptions, nodeOptions) {
147
150
  const list = await generateMultipleFieldSetContents(ctx, {
148
151
  siteSchema,
149
152
  nodeType,
153
+ documentType,
150
154
  schemaI18n,
151
155
  count: getDefaultNodeContentCount(nodeType),
152
156
  withTitle: true,
@@ -163,7 +167,7 @@ async function addRegularDocuments(ctx, siteOptions, nodeOptions) {
163
167
  }
164
168
  async function addParts(ctx, siteOptions, nodeOptions) {
165
169
  ctx.logger.debug(`[TASK] Adding parts "${nodeOptions.nodeType.typeName}"…`);
166
- const { parentNodeId, nodeType } = nodeOptions;
170
+ const { parentNodeId, nodeType, documentType } = nodeOptions;
167
171
  const { fqdn, siteSchema, schemaI18n } = siteOptions;
168
172
  const tolerateErrors = {
169
173
  errorMessages: [],
@@ -171,6 +175,7 @@ async function addParts(ctx, siteOptions, nodeOptions) {
171
175
  const list = await generateMultipleFieldSetContents(ctx, {
172
176
  siteSchema,
173
177
  nodeType,
178
+ documentType,
174
179
  schemaI18n,
175
180
  count: getDefaultNodeContentCount(nodeType),
176
181
  withTitle: true,
@@ -11,10 +11,12 @@ export async function generateFieldSetContent(ctx, options) {
11
11
  return list[0];
12
12
  }
13
13
  export async function generateMultipleFieldSetContents(ctx, options) {
14
- const { siteSchema, nodeType, schemaI18n, count, withTitle, tolerateErrors, debugName } = options;
14
+ const { siteSchema, nodeType, documentType, schemaI18n, count, withTitle, tolerateErrors, debugName, } = options;
15
15
  if (nodeType.kind === "site")
16
16
  throw new Error("Cannot generate content for site node type");
17
- const outputTags = withTitle
17
+ // for a document, the LLM is best at generating the title, so we ask for it and remove it later
18
+ const skipTitle = nodeType.kind === "document" && !withTitle;
19
+ const outputTags = withTitle || skipTitle
18
20
  ? [{ tagName: "title", key: "title", format: "text", tagDescription: "Write the title here" }]
19
21
  : [];
20
22
  if (nodeType.fields) {
@@ -29,20 +31,32 @@ export async function generateMultipleFieldSetContents(ctx, options) {
29
31
  defaultValue: "",
30
32
  defaultLanguage,
31
33
  });
34
+ const documentDescription = translateText(schemaI18n, `nodeTypes.${documentType.typeName}.description`, {
35
+ defaultValue: "",
36
+ defaultLanguage,
37
+ });
32
38
  const siteTheme = translateText(schemaI18n, "siteTheme", {
33
39
  defaultValue: "",
34
40
  defaultLanguage,
35
41
  });
36
42
  const language = defaultLanguage ?? "en";
37
- const generatedContents = outputTags.length > 0
43
+ let generatedContents = outputTags.length > 0
38
44
  ? await invokeGenerateFakeContent(ctx, {
39
45
  count,
46
+ typeKind: nodeType.kind,
40
47
  typeLabel,
41
48
  typeDescription,
49
+ documentDescription,
42
50
  siteTheme,
43
51
  language,
44
52
  }, outputTags, { tolerateErrors, debugName })
45
53
  : undefined;
54
+ if (skipTitle && generatedContents) {
55
+ generatedContents = generatedContents.map((content) => {
56
+ const { title, ...rest } = content;
57
+ return rest;
58
+ });
59
+ }
46
60
  return createNodeContents({
47
61
  nodeType,
48
62
  count,
@@ -81,7 +95,7 @@ function toFakeContentOutputTag(fieldNameOrType) {
81
95
  tagName: `${camelToKebabCase(key)}-md`,
82
96
  key,
83
97
  format: "markdown",
84
- tagDescription: "Write document content here (150-200 words) in markdown format",
98
+ tagDescription: "Write document content here (150-200 words) in markdown format. It MUST NOT include the title.",
85
99
  };
86
100
  }
87
101
  if (key === "introduction") {
@@ -4,9 +4,10 @@ import { readPromptFile } from "../lib/create-prompt.js";
4
4
  import { debugBatchLlmOutputs } from "../lib/debug-utils.js";
5
5
  import { parseLlmResponseAsList } from "../lib/parse-llm-response.js";
6
6
  const singlePromptTpl = PromptTemplate.fromTemplate(await readPromptFile("generate-fake-content-single.md"));
7
- const multiplePromptTpl = PromptTemplate.fromTemplate(await readPromptFile("generate-fake-content-multiple.md"));
7
+ const multipleDocumentsPromptTpl = PromptTemplate.fromTemplate(await readPromptFile("generate-fake-content-multiple-documents.md"));
8
+ const multiplePartsPromptTpl = PromptTemplate.fromTemplate(await readPromptFile("generate-fake-content-multiple-parts.md"));
8
9
  export async function invokeGenerateFakeContent(ctx, input, outputTags, options) {
9
- const { language } = input;
10
+ const { language, typeKind } = input;
10
11
  const single = input.count === 1;
11
12
  const debugName = `fake-content-${options.debugName}${single ? "" : `-${input.count}`}`;
12
13
  const tagAndDescriptions = outputTags
@@ -26,6 +27,9 @@ export async function invokeGenerateFakeContent(ctx, input, outputTags, options)
26
27
  siteTheme: input.siteTheme,
27
28
  language: languageLabelIn(language, "en"),
28
29
  };
30
+ if (typeKind === "part") {
31
+ llmInput.documentDescription = input.documentDescription;
32
+ }
29
33
  if (!single) {
30
34
  llmInput.count = `${end - startIndex}`;
31
35
  }
@@ -35,7 +39,11 @@ export async function invokeGenerateFakeContent(ctx, input, outputTags, options)
35
39
  const debug = await debugBatchLlmOutputs(ctx, debugName, ctx.cheapModelName, batchInputs);
36
40
  let contents = debug.storedContents;
37
41
  if (!contents) {
38
- const llmMessages = await (single ? singlePromptTpl : multiplePromptTpl)
42
+ const llmMessages = await (single
43
+ ? singlePromptTpl
44
+ : typeKind === "document"
45
+ ? multipleDocumentsPromptTpl
46
+ : multiplePartsPromptTpl)
39
47
  .pipe(ctx.cheapModel)
40
48
  .batch(batchInputs);
41
49
  contents = await debug.getMessageContents(llmMessages);
@@ -97,10 +97,20 @@ async function invokeUpdateSiteSchemaStep2(ctx, input) {
97
97
  ...input.generatedSchema,
98
98
  };
99
99
  if (parsed.siteSchema) {
100
- result.siteSchema = parsed.siteSchema;
100
+ result.siteSchema = fixSiteSchema(parsed.siteSchema);
101
101
  }
102
102
  if (parsed.l10n) {
103
103
  result.l10n = parsed.l10n;
104
104
  }
105
105
  return result;
106
106
  }
107
+ function fixSiteSchema(siteSchema) {
108
+ for (const nodeType of siteSchema.nodeTypes ?? []) {
109
+ for (const field of nodeType.fields ?? []) {
110
+ if (typeof field !== "string" && field.dataType === "quillDelta") {
111
+ field.plugin = "@paroicms/quill-editor-plugin";
112
+ }
113
+ }
114
+ }
115
+ return siteSchema;
116
+ }
@@ -1,4 +1,4 @@
1
- Generate the text content of **{count}** different web pages.
1
+ Generate the text content of **{count}** different **web pages**.
2
2
 
3
3
  All the produced texts must be in **{language}**.
4
4
 
@@ -14,9 +14,9 @@ Make sure web pages are distinct from each other by:
14
14
 
15
15
  Guidelines:
16
16
 
17
+ - NEVER repeat a title among several sub-contents.
17
18
  - Never invite the user to write a comment. There is no comment system.
18
- - Do not repeat the title in markdown content.
19
19
 
20
- For each web page, use the following exact format:
20
+ Each web page is composed by several sub-contents. Use the following exact format:
21
21
 
22
22
  {tagAndDescriptions}
@@ -0,0 +1,22 @@
1
+ Generate the text content of **{count}** different **sections** of a web page.
2
+
3
+ All the produced texts must be in **{language}**.
4
+
5
+ The sections in question are of type: **{typeLabel}** ({typeDescription}).
6
+
7
+ For the context, the website's theme is: "{siteTheme}", and the web page is about "{documentDescription}".
8
+
9
+ Make sure sections are distinct from each other by:
10
+
11
+ - Cover different aspects in line with the type of sections;
12
+ - Using varied writing styles;
13
+ - Having different lengths within the specified range.
14
+
15
+ Guidelines:
16
+
17
+ - NEVER repeat a title among several sub-contents.
18
+ - Never invite the user to write a comment. There is no comment system.
19
+
20
+ Each section is composed by several sub-contents. Use the following exact format:
21
+
22
+ {tagAndDescriptions}
@@ -1,4 +1,4 @@
1
- You will be given a description of a website's structure. Your task is to generate three things:
1
+ You will be given a description of a website's structure. Your task is to **analyze** the website description, then you will generate three things:
2
2
 
3
3
  1. A YAML for website properties.
4
4
  2. A Markdown bulleted list that represents the hierarchy of node types (routing documents, regular documents and parts) within the site.
@@ -59,6 +59,7 @@ Guidelines for creating the hierarchical bullet list:
59
59
  - Reusing the same node type several times (for recursivity etc.):
60
60
  - When the same node type is reused as a child in several places, then write its children only on the first occurence.
61
61
  - On this task, do not focus on fields. If the website description provide fields then you will report them in the unused information. In particular, a collection of medias (a carousel, a slider, an image gallery, a slideshow, etc.) is a type of field.
62
+ - Don't try to provide a comment mechanism when the user requests a blog. Blog comments do not belong to the hierarchy of node types.
62
63
 
63
64
  Here's an example of a correct output:
64
65
 
@@ -1,4 +1,4 @@
1
- You are tasked with modifying a JSON object of a _site schema_, based on a given TypeScript type definition and an update message, and its localized labels. Follow these steps carefully:
1
+ You are tasked to plan how modifying a JSON object of a _site schema_, based on a given TypeScript type definition and an update message, and its localized labels. Follow these steps carefully:
2
2
 
3
3
  # 1. Review the TypeScript type definition of the JSON structure:
4
4
 
@@ -20,7 +20,7 @@ Here is an example of an object for describing a custom **HTML** field type:
20
20
 
21
21
  <field_type_example>
22
22
  {{
23
- "name": "myNewField",
23
+ "name": "myCustomField",
24
24
  "localized": true,
25
25
  "storedAs": "text",
26
26
  "dataType": "quillDelta",
@@ -35,11 +35,11 @@ Then, in the locales, the translations of custom field types must be in a sub-ke
35
35
  {{
36
36
  "nodeTypes": {{
37
37
  ...
38
- "myNodeType": {{
38
+ "myNodeTypeName": {{
39
39
  ...
40
40
  "fields": {{
41
- "myNewField": {{
42
- "label": "My new field",
41
+ "myCustomField": {{
42
+ "label": "My custom field",
43
43
  "description": "This is a description that will be displayed in a tooltip of the back-office"
44
44
  }}
45
45
  }}
@@ -48,10 +48,29 @@ Then, in the locales, the translations of custom field types must be in a sub-ke
48
48
  }}
49
49
  </l10n_example>
50
50
 
51
+ Here is how to write field locales for a custom site field:
52
+
53
+ <l10n_example>
54
+ {{
55
+ "nodeTypes": {{
56
+ ...
57
+ "_site": {{
58
+ "fields": {{
59
+ "myCustomSiteField": {{
60
+ "label": "My custom site field",
61
+ "description": "…"
62
+ }}
63
+ }}
64
+ }}
65
+ }}
66
+ }}
67
+ </l10n_example>
68
+
51
69
  Important:
52
70
 
53
71
  - Never add locales for a predefined field.
54
72
  - Never add an unknown predefined field.
73
+ - The type name of the "site" node type is omitted from the JSON but its value is always `_site`.
55
74
 
56
75
  # 3. Examine the current JSON data, which conforms to the `JtSiteSchema` type:
57
76