@paroicms/site-generator-plugin
Version:
ParoiCMS Site Generator Plugin
248 lines (247 loc) • 9.3 kB
JavaScript
import { generateSlug } from "@paroicms/public-anywhere-lib";
export function createSiteSchemaFromAnalysis(analysis) {
const { siteProperties } = analysis;
const siteSchema = {
ParoiCMSSiteSchemaFormatVersion: "9",
languages: [siteProperties.language],
plugins: [
"@paroicms/contact-form-plugin",
"@paroicms/content-loading-plugin",
"@paroicms/internal-link-plugin",
"@paroicms/public-menu-plugin",
"@paroicms/quill-editor-plugin",
"@paroicms/video-plugin",
],
nodeTypes: [
{
kind: "site",
},
...toNodeTypes(analysis),
],
};
return siteSchema;
}
function toNodeTypes(analysis) {
const nodeTypes = new Map(Object.entries(analysis.dictionary)
.map(([typeName, saType]) => [typeName, toNodeType(typeName, saType)])
.filter(([_, item]) => !!item));
const ctx = {
language: analysis.siteProperties.language,
analysis,
nodeTypes,
treeNodeDict: new Map(),
};
walkTree(analysis.tree.home, undefined, ctx);
const result = []; // keep the original order
for (const typeName of Object.keys(analysis.dictionary)) {
const found = nodeTypes.get(typeName);
if (!found)
continue; // it's a partList
result.push(found);
}
return result;
}
function walkTree(saNode, parentNode, ctx) {
const { typeName } = saNode;
const nodeType = ctx.nodeTypes.get(typeName);
if (!nodeType)
throw new Error(`Missing definition of node type "${typeName}"`);
const saType = ctx.analysis.dictionary[typeName];
if (!saType)
throw new Error(`Missing definition of node type "${typeName}"`);
if (ctx.treeNodeDict.has(typeName)) {
if (saNode.children)
throw new Error(`Duplicate definition of node type "${typeName}"`);
if (!parentNode)
throw new Error("Home node cannot be a reference");
if (nodeType.kind === "document") {
if (nodeType.documentKind === "routing") {
throw new Error(`A routing document cannot be a reference: "${typeName}"`);
}
if (nodeType.documentKind === "regular") {
if (saNode.kind !== "regularDocument") {
throw new Error(`Inconsistant kind for document "${typeName}"`);
}
appendAsChildRegularDocument({
nodeType,
typeName,
parentNode,
children: undefined,
}, ctx);
}
else {
throw new Error(`Unexpected document kind: "${nodeType.documentKind}"`);
}
}
else if (nodeType.kind === "part") {
if (saNode.kind !== "part") {
throw new Error(`Inconsistant kind for part "${typeName}"`);
}
appendAsChildPart({
nodeType,
typeName,
parentNode,
children: undefined,
listName: saNode.listName,
}, ctx);
}
else {
throw new Error(`Unexpected kind: "${nodeType.kind}"`);
}
return;
}
ctx.treeNodeDict.set(typeName, saNode);
if (saNode.kind === "routingDocument") {
if (nodeType.kind !== "document" || nodeType.documentKind !== "routing") {
throw new Error(`In the tree, "${typeName}" is used as a routing document but it is a "${nodeType.kind}"`);
}
if (parentNode) {
appendAsRoutingChild({
nodeType,
typeName,
parentNode,
});
}
}
else {
if (!parentNode)
throw new Error("Home node should be a routing document");
if (saNode.kind === "regularDocument") {
if (nodeType.kind !== "document" || nodeType.documentKind !== "regular") {
throw new Error(`In the tree, "${typeName}" is used as a regular document but it is a "${nodeType.kind}"`);
}
appendAsChildRegularDocument({
nodeType,
typeName,
parentNode,
children: saNode.children,
}, ctx);
}
else if (saNode.kind === "part") {
if (!parentNode)
throw new Error("Home node cannot be a list");
if (nodeType.kind !== "part") {
throw new Error(`In the tree, "${typeName}" is used as a part but it is a "${nodeType.kind}"`);
}
appendAsChildPart({
nodeType,
typeName,
parentNode,
children: saNode.children,
listName: saNode.listName,
}, ctx);
}
else {
throw new Error(`Unexpected node kind: "${saNode.kind}"`);
}
}
saNode.children?.forEach((child) => walkTree(child, nodeType, ctx));
}
function appendAsRoutingChild(options) {
const { nodeType, typeName, parentNode } = options;
if (nodeType.kind !== "document") {
throw new Error(`In the tree, "${typeName}" is used as a routing document but it is a "${nodeType.kind}"`);
}
if (nodeType.documentKind !== "routing") {
throw new Error(`In the tree, "${typeName}" is used as a routing document but it is a "${nodeType.documentKind}"`);
}
if (parentNode.kind !== "document") {
throw new Error(`In the tree, "${typeName}" is used as a routing document but its parent is a "${parentNode.kind}"`);
}
if (parentNode.documentKind !== "routing") {
throw new Error(`Due to a technical limitation, routing document "${typeName}" can't be used as child of "${parentNode.documentKind}"`);
}
parentNode.routingChildren ??= [];
parentNode.routingChildren.push(typeName);
}
function appendAsChildRegularDocument(options, ctx) {
const { nodeType, typeName, parentNode, children } = options;
const saType = ctx.analysis.dictionary[typeName];
if (nodeType.kind !== "document") {
throw new Error(`In the tree, "${typeName}" is used as a regular document but it is a "${nodeType.kind}"`);
}
if (nodeType.documentKind !== "regular") {
throw new Error(`In the tree, "${typeName}" is used as a regular document but it is a "${nodeType.documentKind}"`);
}
if (parentNode.kind !== "document") {
throw new Error(`In the tree, "${typeName}" is used as a regular document but its parent is a "${parentNode.kind}"`);
}
if (!parentNode.regularChildren) {
parentNode.regularChildren = [];
parentNode.regularChildrenSorting = saType.temporal ? "publishDate desc" : "title asc";
}
parentNode.regularChildren.push(typeName);
children?.forEach((child) => walkTree(child, nodeType, ctx));
}
function appendAsChildPart(options, ctx) {
const { nodeType, typeName, parentNode, children, listName } = options;
const saType = ctx.analysis.dictionary[typeName];
if (nodeType.kind !== "part") {
throw new Error(`In the tree, "${typeName}" is used as a part but it is a "${nodeType.kind}"`);
}
if (parentNode.kind === "document") {
parentNode.lists ??= [];
parentNode.lists.push({
listName,
parts: [typeName],
sorting: saType.temporal ? "publishDate desc" : "manual",
});
}
else if (parentNode.kind === "part") {
if (parentNode.list) {
parentNode.list.parts.push(typeName);
}
else {
parentNode.list = {
parts: [typeName],
sorting: saType.temporal ? "publishDate desc" : "manual",
};
}
}
else {
throw new Error(`Unexpected kind: "${parentNode.kind}"`);
}
children?.forEach((child) => walkTree(child, nodeType, ctx));
}
export function toNodeType(typeName, saType) {
if (saType.kind === "part")
return toPartNodeType(typeName);
if (saType.kind === "regularDocument")
return toRegularDocumentNodeType(typeName, saType);
if (saType.kind === "routingDocument")
return toRoutingDocumentNodeType(typeName, saType);
if (saType.kind === "partList")
return;
throw new Error(`Unexpected kind: "${saType.kind}" for node type "${typeName}"`);
}
function toPartNodeType(typeName) {
return {
typeName,
kind: "part",
};
}
function toRoutingDocumentNodeType(typeName, saType) {
return {
typeName,
kind: "document",
documentKind: "routing",
ogType: saType.ogType,
route: generateSlug(saType.label),
withFeaturedImage: typeName !== "home" && !!saType.entryPage,
adminUi: saType.taxonomy
? {
menuPlacement: "popup",
}
: undefined,
};
}
function toRegularDocumentNodeType(typeName, saType) {
return {
typeName,
kind: "document",
documentKind: "regular",
ogType: saType.ogType,
route: saType.temporal ? ":yyyy/:mm/:dd/:relativeId-:slug" : ":relativeId-:slug",
withFeaturedImage: true,
};
}