UNPKG

@paroicms/site-generator-plugin

Version:

ParoiCMS Site Generator Plugin

194 lines (191 loc) 7.02 kB
import { mkdir, writeFile } from "node:fs/promises"; import { dirname, join } from "node:path"; import { getPredefinedFields } from "../lib/create-prompt.js"; import { camelToKebabCase } from "../lib/utils.js"; import { templateOfSiteFooter, templateOfSiteHeader } from "./common-template-creator.js"; import { templateOfDocumentType } from "./document-template-creator.js"; import { isMultilingual } from "./jt-site-schema-helpers.js"; import { getThemeCssContent } from "./theme-css.js"; export async function createTheme(ctx, siteDir, siteSchema) { const themeContext = createThemeCreatorContext(siteSchema); for (const nodeType of siteSchema.nodeTypes ?? []) { if (nodeType.kind === "site") continue; if (nodeType.kind === "part") continue; themeContext.addLiquidFile("root", `${camelToKebabCase(nodeType.typeName)}.liquid`, templateOfDocumentType(themeContext, nodeType)); } themeContext.addFile("theme.json", getThemeJsonContent()); themeContext.addFile(".theme-check.yml", getLiquidLinterCheckContent()); themeContext.addFile("assets/scss/theme.scss", getThemeCssContent()); themeContext.addFile("assets/css/theme.css", getThemeCssContent()); themeContext.addLiquidFile("layouts", "main-layout.liquid", templateOfLayout(themeContext)); themeContext.addLiquidFile("root", "404.liquid", templateOf404(themeContext)); if (isMultilingual(siteSchema)) { themeContext.addLiquidFile("root", "index.liquid", templateOfIndex()); } const themeDir = join(siteDir, "theme"); await mkdir(themeDir); const { files, issues } = themeContext.toFiles(); for (const file of files) { const filePath = join(themeDir, file.path); const dirPath = dirname(filePath); await ensureDirectory(dirPath, { recursive: true }); await writeFile(filePath, file.content, "utf-8"); } if (issues) { ctx.logger.warn(`Issues in "${siteDir}":`, issues); } } function createThemeCreatorContext(siteSchema) { const languages = siteSchema.languages ?? []; const liquidFiles = new Map(); const localeFiles = new Map(); const otherFiles = new Map(); const issues = []; return { siteSchema, predefinedFields: new Map(getPredefinedFields().map((f) => [f.fieldName, f])), setLocalizedLabel(label) { for (const language of languages) { const value = label[language]; if (!value) continue; let f = localeFiles.get(language); if (!f) { f = {}; localeFiles.set(language, f); } f[language] = value; } }, hasLiquidFile(directory, filename) { const path = directory === "root" ? `templates/${filename}` : `templates/${directory}/${filename}`; return liquidFiles.has(path); }, addLiquidFile(directory, filename, content, { skipIfExists = false } = {}) { const path = directory === "root" ? `templates/${filename}` : `templates/${directory}/${filename}`; if (liquidFiles.has(path)) { if (skipIfExists) return; issues.push(`Liquid file already exists, overwrite: "${path}"`); } liquidFiles.set(path, content); }, addFile(path, content) { if (otherFiles.has(path)) throw new Error(`File already exists: "${path}"`); otherFiles.set(path, content); }, toFiles() { const files = [ ...Array.from(liquidFiles.entries()).map(([path, content]) => ({ path, content })), ...Array.from(localeFiles.entries()).map(([language, content]) => ({ path: `locales/${language}.json`, content: JSON.stringify(content, null, 2), })), ...Array.from(otherFiles.entries()).map(([path, content]) => ({ path, content })), ]; files.sort((a, b) => a.path.localeCompare(b.path)); return { files, issues: issues.length > 0 ? issues : undefined }; }, }; } // function getDefaultLiquidContent() { // return `{% layout "layouts/main-layout.liquid" doc: doc site: site %} // {% block %} // <div class="Container"> // <div class="Page"> // {{ doc | info }} // </div> // </div> // {% endblock %}`; // } function templateOf404(ctx) { const { siteSchema } = ctx; const rawParam = isMultilingual(siteSchema) ? " raw: true" : ""; return `{% layout "layouts/main-layout.liquid" doc: doc site: site${rawParam} %} {% block %} <div class="Container"> <div class="Page"> <h1>404</h1> <p>This page doesn't exist. Please <a href='/'>return to home page</a>.</p> </div> </div> {% endblock %}`; } function templateOfIndex() { return `{% layout "layouts/main-layout.liquid" doc: doc site: site raw: true %} {% block %} <div class="Container"> <div class="Page"> {% for translation in doc.translations %} <a href="{{ translation.url }}">{{ translation.languageLabel }}</a> {% endfor %} </div> </div> {% endblock %}`; } function templateOfLayout(ctx) { const { siteSchema } = ctx; const multilingual = isMultilingual(siteSchema); ctx.addLiquidFile("partials", "site-header.liquid", templateOfSiteHeader(ctx)); ctx.addLiquidFile("partials", "site-footer.liquid", templateOfSiteFooter(ctx)); const renderHeader = `{% render "partials/site-header" doc: doc site: site %}`; const headerTemplate = multilingual ? `{% unless raw %} ${renderHeader} {% endunless %}` : renderHeader; const renderFooter = `{% render "partials/site-footer" doc: doc site: site %}`; const footerTemplate = multilingual ? `{% unless raw %} ${renderFooter} {% endunless %}` : renderFooter; return `<!doctype html> <html lang="{{ doc.language }}"> <head> <meta name="viewport" content="width=device-width,initial-scale=1"> {{ doc | headTags }} <title> {% if doc.type != "home" and doc.title %} {{ doc.title }} -{% endif %} {{ site.field.title }}</title> <link rel="stylesheet" href="{{ site.assetsUrl }}/css/theme.css"> </head> <body> ${headerTemplate} {% block -%} {%- endblock %} ${footerTemplate} </body> </html>`; } function getThemeJsonContent() { return JSON.stringify({ fTextImages: ["700x", "x400x", "700x350"], pixelRatio: 1.5, }, null, 2); } function getLiquidLinterCheckContent() { return `UndefinedObject: enabled: false TranslationKeyExists: enabled: false MissingTemplate: enabled: false RemoteAsset: enabled: false UnknownFilter: enabled: false`; } async function ensureDirectory(dirPath, { recursive = false } = {}) { try { await mkdir(dirPath, { recursive }); } catch (e) { if (e.code !== "EEXIST") throw e; } }