UNPKG

@paroicms/site-generator-plugin

Version:

ParoiCMS Site Generator Plugin

248 lines (247 loc) 9.3 kB
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, }; }