#!/usr/bin/env node /** * Migriert Contentful-Export (contentful-export.json) nach rustycms content/de/. * Nur deutsche Locale (en kann später ergänzt werden). * * Aufruf: node scripts/contentful-to-rustycms.mjs [Pfad-zum-Export] [--html-only] [--only=quote|iframe|image|image_gallery] * Default Export-Pfad: ../www.windwiderstand.de/contentful-export.json * --html-only: nur HTML migrieren * --only=X: nur Typ X migrieren (mehrfach möglich), X = html|quote|iframe|image|image_gallery|youtube_video */ import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ROOT = path.resolve(__dirname, ".."); const CONTENT_DE = path.join(ROOT, "content", "de"); const argv = process.argv.slice(2); const ONLY = new Set(); if (argv.includes("--html-only")) ONLY.add("html"); argv.forEach((a) => { const m = a.match(/^--only=(.+)$/); if (m) ONLY.add(m[1]); }); const EXPORT_PATH = argv.filter((a) => a !== "--html-only" && !a.startsWith("--only="))[0] || path.join(ROOT, "..", "www.windwiderstand.de", "contentful-export.json"); // ─── Slug-Normalisierung ───────────────────────────────────────────────── function slugify(value) { if (value == null || value === "") return ""; let s = String(value) .replace(/^\//, "") .replace(/\//g, "-") .replace(/[^a-zA-Z0-9_-]+/g, "-") .replace(/-+/g, "-") .replace(/^-|-$/g, ""); return s || "untitled"; } function safeSlug(entry, type) { const f = entry?.fields || {}; if (type === "page" && f.slug != null) return f.slug === "/" ? "home" : slugify(f.slug); if (f.id != null) return slugify(f.id); if (type === "link" && f.slug != null) return slugify(f.slug); if (entry?.sys?.id) return slugify(entry.sys.id); return "untitled"; } // ─── Rekursive Sammlung: alle Entries und Assets ────────────────────────── function collectNodes(obj, entries, assets) { if (!obj || typeof obj !== "object") return; if (Array.isArray(obj)) { obj.forEach((item) => collectNodes(item, entries, assets)); return; } const sys = obj.sys; const fields = obj.fields; if (sys?.type === "Asset" && fields?.file) { const id = sys.id; if (!assets.has(id)) assets.set(id, { sys, fields }); return; } const contentTypeId = sys?.contentType?.sys?.id || sys?.contentType?.id; if (sys?.type === "Entry" && contentTypeId) { const id = sys.id; if (!entries.has(id)) { entries.set(id, { sys: { ...sys, contentType: { id: contentTypeId } }, fields: fields || {} }); if (fields && typeof fields === "object") { Object.values(fields).forEach((v) => collectNodes(v, entries, assets)); } } return; } Object.values(obj).forEach((v) => collectNodes(v, entries, assets)); } // ─── JSON5-ähnlich ausgeben (gültiges JSON, 2 Leerzeichen) ──────────────── function writeJson5(filePath, obj) { const dir = path.dirname(filePath); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); const str = JSON.stringify(obj, null, 2); fs.writeFileSync(filePath, str + "\n", "utf8"); } // ─── Main ─────────────────────────────────────────────────────────────── function main() { if (!fs.existsSync(EXPORT_PATH)) { console.error("Export-Datei nicht gefunden:", EXPORT_PATH); process.exit(1); } console.log("Lese Export:", EXPORT_PATH); const data = JSON.parse(fs.readFileSync(EXPORT_PATH, "utf8")); const byType = data.byType || {}; const entries = new Map(); const assets = new Map(); Object.values(byType).forEach((group) => { if (group?.items) group.items.forEach((item) => collectNodes(item, entries, assets)); }); console.log("Einträge:", entries.size, "Assets:", assets.size); const idToSlug = new Map(); const SUPPORTED_ROW_COMPONENTS = new Set([ "markdown", "html", "componentLinkList", "fullwidthBanner", "componentPostOverview", "componentSearchableText", "quoteComponent", "iframe", "image", "imageGallery", "youtubeVideo", ]); function getSlug(entryOrId, contentType) { const entry = typeof entryOrId === "string" ? entries.get(entryOrId) : entryOrId; if (!entry) return null; const id = entry.sys?.id; const type = contentType || entry.sys?.contentType?.id || entry.sys?.contentType?.sys?.id; if (idToSlug.has(id)) return idToSlug.get(id); // Assets: bereits in idToSlug aus Schritt 1 if (entry.sys?.type === "Asset") return idToSlug.get(id) || slugify(entry.fields?.title || id); const slug = safeSlug(entry, type); let finalSlug = slug; let n = 0; const used = new Set(idToSlug.values()); while (used.has(finalSlug)) { n++; finalSlug = slug + "-" + n; } idToSlug.set(id, finalSlug); return finalSlug; } function refToSlug(ref) { if (ref == null) return null; if (typeof ref === "string") return idToSlug.get(ref) || ref; if (ref.sys?.type === "Entry") return getSlug(ref, ref.sys?.contentType?.id); if (ref.sys?.type === "Asset") return getSlug(ref, "img"); return null; } const run = (key) => !ONLY.size || ONLY.has(key); // Bei --only=image/image_gallery: nur Asset-Slugs in idToSlug eintragen (für refToSlug), ohne img-Dateien zu schreiben if (ONLY.size && (ONLY.has("image") || ONLY.has("image_gallery"))) { assets.forEach((asset, id) => { const slug = slugify(asset.fields?.title || id); const uniq = Array.from(idToSlug.values()).includes(slug) ? slug + "-" + id.slice(0, 6) : slug; idToSlug.set(id, uniq); }); } if (run("img")) { // ─── 1) Assets → img ─────────────────────────────────────────────────── assets.forEach((asset, id) => { const slug = slugify(asset.fields?.title || id); const uniq = Array.from(idToSlug.values()).includes(slug) ? slug + "-" + id.slice(0, 6) : slug; idToSlug.set(id, uniq); const file = asset.fields?.file || {}; let url = file.url || ""; if (url && !url.startsWith("http")) url = "https:" + url; const out = { _slug: uniq, title: asset.fields?.title ?? "", description: asset.fields?.description ?? "", file: { url, fileName: file.fileName, contentType: file.contentType, details: file.details, }, }; writeJson5(path.join(CONTENT_DE, "img", uniq + ".json5"), out); }); console.log("img:", assets.size); } if (run("tag")) { // ─── 2) Tag ──────────────────────────────────────────────────────────── (byType.tag?.items || []).forEach((entry) => { const slug = getSlug(entry, "tag"); const f = entry.fields || {}; writeJson5(path.join(CONTENT_DE, "tag", slug + ".json5"), { _slug: slug, name: f.name ?? slug, }); }); console.log("tag:", (byType.tag?.items || []).length); } if (run("link")) { // ─── 3) Link ─────────────────────────────────────────────────────────── (byType.link?.items || []).forEach((entry) => { const slug = getSlug(entry, "link"); const f = entry.fields || {}; writeJson5(path.join(CONTENT_DE, "link", slug + ".json5"), { _slug: slug, name: f.linkName ?? slug, internal: slug, linkName: f.linkName ?? "", url: f.url ?? "", newTab: f.newTab ?? false, external: f.externalLink ?? false, description: f.description ?? "", alt: f.alt ?? "", showText: f.showText !== false, author: f.author ?? "–", date: f.date ?? "", source: f.source ?? "–", }); }); console.log("link:", (byType.link?.items || []).length); } if (run("markdown")) { // ─── 4) Markdown ──────────────────────────────────────────────────────── function extractMarkdown(entry) { const slug = getSlug(entry, "markdown"); const f = entry.fields || {}; const layout = f.layout?.fields || {}; writeJson5(path.join(CONTENT_DE, "markdown", slug + ".json5"), { _slug: slug, name: f.id ?? slug, content: f.content ?? "", layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0, }, alignment: f.alignment ?? "left", }); return slug; } function ensureMarkdown(entry) { if (!entry || entry.sys?.contentType?.id !== "markdown") return null; if (!idToSlug.has(entry.sys.id)) extractMarkdown(entry); return idToSlug.get(entry.sys.id); } (byType.markdown?.items || []).forEach(extractMarkdown); entries.forEach((entry) => { if (entry.sys?.contentType?.id === "markdown" && !idToSlug.has(entry.sys.id)) extractMarkdown(entry); }); console.log("markdown:", Array.from(entries.values()).filter((e) => e.sys?.contentType?.id === "markdown").length); } // ─── 4b) HTML (html) ──────────────────────────────────────────────────── if (run("html")) { function extractHtml(entry) { const slug = getSlug(entry, "html"); const f = entry.fields || {}; const layout = f.layout?.fields || f.layout || {}; writeJson5(path.join(CONTENT_DE, "html", slug + ".json5"), { _slug: slug, name: f.id ?? slug, html: f.html ?? "", layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0, }, }); return slug; } (byType.html?.items || []).forEach(extractHtml); entries.forEach((entry) => { if (entry.sys?.contentType?.id === "html" && !idToSlug.has(entry.sys.id)) extractHtml(entry); }); console.log("html:", Array.from(entries.values()).filter((e) => e.sys?.contentType?.id === "html").length); } // ─── 4c) Quote (quoteComponent) ───────────────────────────────────────── if (run("quote")) { (byType.quoteComponent?.items || []).forEach((entry) => { const slug = getSlug(entry, "quoteComponent"); const f = entry.fields || {}; const layout = f.layout?.fields || f.layout || {}; writeJson5(path.join(CONTENT_DE, "quote", slug + ".json5"), { _slug: slug, quote: f.quote ?? "", author: f.author ?? "", variant: f.variant ?? "left", layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0, }, }); }); entries.forEach((e) => { if (e.sys?.contentType?.id === "quoteComponent" && !idToSlug.has(e.sys.id)) { const slug = getSlug(e, "quoteComponent"); const f = e.fields || {}; const layout = f.layout?.fields || f.layout || {}; writeJson5(path.join(CONTENT_DE, "quote", slug + ".json5"), { _slug: slug, quote: f.quote ?? "", author: f.author ?? "", variant: f.variant ?? "left", layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0 }, }); } }); console.log("quote:", Array.from(entries.values()).filter((e) => e.sys?.contentType?.id === "quoteComponent").length); } // ─── 4d) Iframe ──────────────────────────────────────────────────────── if (run("iframe")) { (byType.iframe?.items || []).forEach((entry) => { const slug = getSlug(entry, "iframe"); const f = entry.fields || {}; const layout = f.layout?.fields || f.layout || {}; writeJson5(path.join(CONTENT_DE, "iframe", slug + ".json5"), { _slug: slug, name: f.name ?? slug, content: f.content ?? "", iframe: f.iframe ?? "", overlayImage: refToSlug(f.overlayImage) ?? undefined, layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0, }, }); }); entries.forEach((e) => { if (e.sys?.contentType?.id === "iframe" && !idToSlug.has(e.sys.id)) { const slug = getSlug(e, "iframe"); const f = e.fields || {}; const layout = f.layout?.fields || f.layout || {}; writeJson5(path.join(CONTENT_DE, "iframe", slug + ".json5"), { _slug: slug, name: f.name ?? slug, content: f.content ?? "", iframe: f.iframe ?? "", overlayImage: refToSlug(f.overlayImage) ?? undefined, layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0 }, }); } }); console.log("iframe:", Array.from(entries.values()).filter((e) => e.sys?.contentType?.id === "iframe").length); } // ─── 4e) Image ─────────────────────────────────────────────────────────── if (run("image")) { (byType.image?.items || []).forEach((entry) => { const slug = getSlug(entry, "image"); const f = entry.fields || {}; const layout = f.layout?.fields || f.layout || {}; writeJson5(path.join(CONTENT_DE, "image", slug + ".json5"), { _slug: slug, name: f.name ?? slug, image: refToSlug(f.image) ?? "", caption: f.caption ?? "", layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0, }, ...(f.maxWidth != null && { maxWidth: f.maxWidth }), ...(f.aspectRatio != null && { aspectRatio: f.aspectRatio }), }); }); entries.forEach((e) => { if (e.sys?.contentType?.id === "image" && !idToSlug.has(e.sys.id)) { const slug = getSlug(e, "image"); const f = e.fields || {}; const layout = f.layout?.fields || f.layout || {}; writeJson5(path.join(CONTENT_DE, "image", slug + ".json5"), { _slug: slug, name: f.name ?? slug, image: refToSlug(f.image) ?? "", caption: f.caption ?? "", layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0 }, ...(f.maxWidth != null && { maxWidth: f.maxWidth }), ...(f.aspectRatio != null && { aspectRatio: f.aspectRatio }), }); } }); console.log("image:", Array.from(entries.values()).filter((e) => e.sys?.contentType?.id === "image").length); } // ─── 4f) ImageGallery ─────────────────────────────────────────────────── if (run("image_gallery")) { (byType.imageGallery?.items || []).forEach((entry) => { const slug = getSlug(entry, "imageGallery"); const f = entry.fields || {}; const layout = f.layout?.fields || f.layout || {}; const imageSlugs = (f.images || []).map(refToSlug).filter(Boolean); writeJson5(path.join(CONTENT_DE, "image_gallery", slug + ".json5"), { _slug: slug, name: f.name ?? slug, images: imageSlugs, layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0, }, ...(f.description != null && f.description !== "" && { description: f.description }), }); }); entries.forEach((e) => { if ((e.sys?.contentType?.id === "imageGallery" || e.sys?.contentType?.id === "imgGallery") && !idToSlug.has(e.sys.id)) { const slug = getSlug(e, "imageGallery"); const f = e.fields || {}; const layout = f.layout?.fields || f.layout || {}; const imageSlugs = (f.images || []).map(refToSlug).filter(Boolean); writeJson5(path.join(CONTENT_DE, "image_gallery", slug + ".json5"), { _slug: slug, name: f.name ?? slug, images: imageSlugs, layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0 }, ...(f.description != null && f.description !== "" && { description: f.description }), }); } }); console.log("image_gallery:", Array.from(entries.values()).filter((e) => e.sys?.contentType?.id === "imageGallery" || e.sys?.contentType?.id === "imgGallery").length); } // ─── 4g) YoutubeVideo ─────────────────────────────────────────────────── if (run("youtube_video")) { (byType.youtubeVideo?.items || []).forEach((entry) => { const slug = getSlug(entry, "youtubeVideo"); const f = entry.fields || {}; const layout = f.layout?.fields || f.layout || {}; writeJson5(path.join(CONTENT_DE, "youtube_video", slug + ".json5"), { _slug: slug, id: f.id ?? slug, youtubeId: f.youtubeId ?? "", ...(f.params != null && f.params !== "" && { params: f.params }), ...(f.title != null && f.title !== "" && { title: f.title }), ...(f.description != null && f.description !== "" && { description: f.description }), layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0, }, }); }); entries.forEach((e) => { if (e.sys?.contentType?.id === "youtubeVideo" && !idToSlug.has(e.sys.id)) { const slug = getSlug(e, "youtubeVideo"); const f = e.fields || {}; const layout = f.layout?.fields || f.layout || {}; writeJson5(path.join(CONTENT_DE, "youtube_video", slug + ".json5"), { _slug: slug, id: f.id ?? slug, youtubeId: f.youtubeId ?? "", ...(f.params != null && f.params !== "" && { params: f.params }), ...(f.title != null && f.title !== "" && { title: f.title }), ...(f.description != null && f.description !== "" && { description: f.description }), layout: { mobile: layout.mobile ?? "12", tablet: layout.tablet, desktop: layout.desktop, spaceBottom: layout.spaceBottom ?? 0 }, }); } }); console.log("youtube_video:", Array.from(entries.values()).filter((e) => e.sys?.contentType?.id === "youtubeVideo").length); } if (ONLY.size) { console.log("Fertig. Nur migriert:", [...ONLY].sort().join(", ")); return; } // ─── 5) Link-List (componentLinkList) ─────────────────────────────────── function writeLinkList(entry) { const slug = getSlug(entry, "componentLinkList"); const f = entry.fields || {}; const linkSlugs = (f.links || []).map((l) => refToSlug(l)).filter(Boolean); writeJson5(path.join(CONTENT_DE, "link_list", slug + ".json5"), { _slug: slug, headline: f.headline ?? "", links: linkSlugs, }); } (byType.componentLinkList?.items || []).forEach(writeLinkList); entries.forEach((e) => { if (e.sys?.contentType?.id === "componentLinkList" && !idToSlug.has(e.sys.id)) writeLinkList(e); }); console.log("link_list:", Array.from(entries.values()).filter((e) => e.sys?.contentType?.id === "componentLinkList").length); // ─── 6) FullwidthBanner ──────────────────────────────────────────────── function extractFullwidthBanner(entry) { const slug = getSlug(entry, "fullwidthBanner"); const f = entry.fields || {}; const img = f.img; let image = []; if (img?.fields?.file?.url) { let u = img.fields.file.url; if (!u.startsWith("http")) u = "https:" + u; image = [u]; } writeJson5(path.join(CONTENT_DE, "fullwidth_banner", slug + ".json5"), { _slug: slug, name: f.id ?? slug, variant: f.variant ?? "light", headline: f.headline ?? "", subheadline: f.subheadline ?? "", text: f.text ?? "", image, }); return slug; } (byType.fullwidthBanner?.items || []).forEach(extractFullwidthBanner); entries.forEach((e) => { if (e.sys?.contentType?.id === "fullwidthBanner" && !idToSlug.has(e.sys.id)) extractFullwidthBanner(e); }); console.log("fullwidth_banner:", Array.from(entries.values()).filter((e) => e.sys?.contentType?.id === "fullwidthBanner").length); // ─── 7) PostOverview (componentPostOverview) ──────────────────────────── (byType.componentPostOverview?.items || []).forEach((entry) => { const slug = getSlug(entry, "componentPostOverview"); const f = entry.fields || {}; const tagSlugs = (f.filterByTag || []).map(refToSlug).filter(Boolean); writeJson5(path.join(CONTENT_DE, "post_overview", slug + ".json5"), { _slug: slug, id: f.id ?? slug, headline: f.headline ?? "", allPosts: f.allPosts ?? true, filterByTag: tagSlugs, }); }); entries.forEach((e) => { if (e.sys?.contentType?.id === "componentPostOverview" && !idToSlug.has(e.sys.id)) { const slug = getSlug(e, "componentPostOverview"); const f = e.fields || {}; const tagSlugs = (f.filterByTag || []).map(refToSlug).filter(Boolean); writeJson5(path.join(CONTENT_DE, "post_overview", slug + ".json5"), { _slug: slug, id: f.id ?? slug, headline: f.headline ?? "", allPosts: f.allPosts ?? true, filterByTag: tagSlugs, }); } }); console.log("post_overview:", Array.from(entries.values()).filter((e) => e.sys?.contentType?.id === "componentPostOverview").length); // ─── 8) TextFragment + SearchableText (optional, falls verwendet) ──────── (byType.textFragment?.items || []).forEach((entry) => { const slug = getSlug(entry, "textFragment"); const f = entry.fields || {}; const tagSlugs = (f.tags || []).map(refToSlug).filter(Boolean); writeJson5(path.join(CONTENT_DE, "text_fragment", slug + ".json5"), { _slug: slug, id: f.id ?? slug, title: f.title ?? "", text: f.text ?? "", tags: tagSlugs, }); }); (byType.componentSearchableText?.items || []).forEach((entry) => { const slug = getSlug(entry, "componentSearchableText"); const f = entry.fields || {}; const fragSlugs = (f.textFragments || []).map(refToSlug).filter(Boolean); const tagSlugs = (f.tagWhitelist || []).map(refToSlug).filter(Boolean); writeJson5(path.join(CONTENT_DE, "searchable_text", slug + ".json5"), { _slug: slug, id: f.id ?? slug, textFragments: fragSlugs, tagWhitelist: tagSlugs, title: f.title, description: f.description, }); }); // ─── 9) Post ─────────────────────────────────────────────────────────── (byType.post?.items || []).forEach((entry) => { const slug = getSlug(entry, "post"); const f = entry.fields || {}; const row1Content = (f.row1Content || []).map(refToSlug).filter(Boolean); const obj = { _slug: slug, slug: f.slug ?? "/" + slug, linkName: f.linkName ?? "", headline: f.headline ?? "", subheadline: f.subheadline ?? "", excerpt: f.excerpt ?? "", ...(entry.sys?.createdAt && { created: entry.sys.createdAt }), postImage: refToSlug(f.postImage) || null, postTag: (f.postTag || []).map(refToSlug).filter(Boolean), important: f.important ?? false, content: f.content ?? "", showCommentSection: f.showCommentSection !== false, row1JustifyContent: f.row1JustifyContent ?? "start", row1AlignItems: f.row1AlignItems ?? "start", seoTitle: f.seoTitle ?? "", seoDescription: f.seoDescription ?? "", seoMetaRobots: f.seoMetaRobots ?? "index, follow", }; if (obj.postImage == null) delete obj.postImage; if (row1Content.length) obj.row1Content = row1Content; writeJson5(path.join(CONTENT_DE, "post", slug + ".json5"), obj); }); console.log("post:", (byType.post?.items || []).length); // ─── 10) Page (alle Page-Einträge aus entries, damit auch nur in Nav referenzierte) ─── const pageEntries = Array.from(entries.values()).filter((e) => (e.sys?.contentType?.id || e.sys?.contentType?.sys?.id) === "page"); pageEntries.forEach((entry) => { const slug = getSlug(entry, "page"); const f = entry.fields || {}; const row1Content = (f.row1Content || []) .filter((ref) => ref?.sys?.contentType && SUPPORTED_ROW_COMPONENTS.has(ref.sys.contentType.sys?.id || ref.sys.contentType.id)) .map(refToSlug) .filter(Boolean); const name = slug === "home" || f.slug === "/" ? "home" : slug; const pageSlug = f.slug ?? (name === "home" ? "/" : "/" + name); writeJson5(path.join(CONTENT_DE, "page", slug + ".json5"), { _slug: slug, slug: pageSlug, name, linkName: f.linkName ?? "", headline: f.headline ?? "", subheadline: f.subheadline ?? "", seoTitle: f.seoTitle ?? "", seoDescription: f.seoDescription ?? "", seoMetaRobots: f.seoMetaRobots ?? "index, follow", row1JustifyContent: f.row1JustifyContent ?? "start", row1AlignItems: f.row1AlignItems ?? "start", row1Content, ...(refToSlug(f.topFullwidthBanner) ? { topFullwidthBanner: refToSlug(f.topFullwidthBanner) } : {}), }); }); console.log("page:", pageEntries.length); // ─── 11) Footer ──────────────────────────────────────────────────────── (byType.footer?.items || []).forEach((entry) => { const slug = getSlug(entry, "footer"); const f = entry.fields || {}; const row1Content = (f.row1Content || []).map(refToSlug).filter(Boolean); writeJson5(path.join(CONTENT_DE, "footer", slug + ".json5"), { _slug: slug, id: f.id ?? slug, row1JustifyContent: f.row1JustifyContent ?? "start", row1AlignItems: f.row1AlignItems ?? "start", row1Content, }); }); // ─── 12) Navigation ───────────────────────────────────────────────────── (byType.navigation?.items || []).forEach((entry) => { const slug = getSlug(entry, "navigation"); const f = entry.fields || {}; const links = (f.links || []).map((p) => refToSlug(p)).filter(Boolean); const internal = f.id ?? (slug === "navigation-header" ? "navigation-header" : slug); writeJson5(path.join(CONTENT_DE, "navigation", (f.id || slug) + ".json5"), { _slug: f.id || slug, name: f.id ?? slug, internal, links, }); }); // ─── 13) PageConfig ───────────────────────────────────────────────────── const pageConfigItems = byType.pageConfig?.items || []; const defaultFooterText = "© Bürgerinitiative Vachdorf. Alle Rechte vorbehalten."; pageConfigItems.forEach((entry) => { const slug = "default"; const f = entry.fields || {}; const logoRef = f.logo; const logoSlug = logoRef ? (logoRef.sys?.type === "Asset" ? idToSlug.get(logoRef.sys?.id) : null) : null; writeJson5(path.join(CONTENT_DE, "page_config", slug + ".json5"), { _slug: slug, logo: logoSlug ?? "logo", footerText1: defaultFooterText, seoTitle: f.seoTitle ?? "Bürgerinitiative Vachdorf / $1", seoDescription: f.seoDescription ?? "$1 - Bürgerinitiative Vachdorf", website: f.website ?? "https://www.windwiderstand.de", }); }); if (pageConfigItems.length === 0) { writeJson5(path.join(CONTENT_DE, "page_config", "default.json5"), { _slug: "default", logo: "logo", footerText1: defaultFooterText, seoTitle: "Bürgerinitiative Vachdorf / $1", seoDescription: "$1 - Bürgerinitiative Vachdorf", website: "https://www.windwiderstand.de", }); } // ─── 14) Campaign ─────────────────────────────────────────────────────── (byType.campaign?.items || []).forEach((entry) => { const slug = getSlug(entry, "campaign"); const f = entry.fields || {}; writeJson5(path.join(CONTENT_DE, "campaign", slug + ".json5"), { _slug: slug, campaignName: f.campaingName ?? f.campaignName ?? slug, urlPattern: f.urlPatter ?? f.urlPattern ?? "/", selector: f.selector ?? "body", insertHtml: f.insertHtml ?? "beforeend", timeUntil: f.timeUntil ?? "", html: f.html ?? "", javascript: f.javascript ?? "", css: f.css ?? "", }); }); // ─── 15) Campaigns ───────────────────────────────────────────────────── (byType.campaigns?.items || []).forEach((entry) => { const slug = getSlug(entry, "campaigns"); const f = entry.fields || {}; const campaignSlugs = (f.campaings || f.campaigns || []).map(refToSlug).filter(Boolean); writeJson5(path.join(CONTENT_DE, "campaigns", (f.id || slug) + ".json5"), { _slug: f.id || slug, id: f.id ?? slug, campaigns: campaignSlugs, enable: f.enable !== false, }); }); console.log("Fertig. Content in", CONTENT_DE); } main();