const fs = require("fs"); const path = require("path"); const config = require("./site-config.js"); const articles = JSON.parse(fs.readFileSync("./data/articles.json", "utf-8")); const pagesData = JSON.parse(fs.readFileSync("./data/pages-data.json", "utf-8")); const homeTemplate = fs.readFileSync("./templates/index-template.html", "utf-8"); const articleTemplate = fs.readFileSync("./templates/article-template.html", "utf-8"); const aboutTemplate = fs.readFileSync("./about/about.html", "utf-8"); const privacyTemplate = fs.readFileSync("./privacy-policy/privacy.html", "utf-8"); const distDir = "./dist"; const articleOut = path.join(distDir, "articles"); /* ========================================================= FRESH DEPLOYMENT: CLEAR OUTPUT ========================================================= */ if (fs.existsSync(distDir)) { fs.rmSync(distDir, { recursive: true, force: true }); console.log("๐งน /dist cleared for fresh deployment."); } fs.mkdirSync(articleOut, { recursive: true }); /* ========================================================= HELPERS ========================================================= */ function formatSections(sections) { return (sections || []) .map(s => `
${s.text || s.body}
`) .join(""); } function scoreArticle(article) { let score = 0; const rawDate = article.datePublished || article.created_at; const parsedDate = new Date(rawDate); const safeTime = !isNaN(parsedDate.getTime()) ? parsedDate.getTime() : Date.now(); const daysOld = (Date.now() - safeTime) / (1000 * 60 * 60 * 24); score += Math.max(0, 10 - daysOld); if (article.image) score += 3; if (article.trendingBoost) score += article.trendingBoost; return score; } function getRelatedArticles(current, all) { return all .filter(a => a.slug !== current.slug) .map(a => { let relScore = 0; if (a.category === current.category) relScore += 10; return { ...a, relScore }; }) .sort((a, b) => { const timeA = new Date(a.datePublished || a.created_at).getTime(); const timeB = new Date(b.datePublished || b.created_at).getTime(); return b.relScore - a.relScore || timeB - timeA; }) .slice(0, 4) .map(r => ` `).join(""); } /* ========================================================= RANK ARTICLES ========================================================= */ const ranked = articles .map(article => ({ ...article, score: scoreArticle(article) })) .sort((a, b) => b.score - a.score); /* ========================================================= BUILD ARTICLES ========================================================= */ ranked.forEach((article) => { const canonical = `${config.baseUrl}/articles/${article.slug}.html`; const relatedHTML = getRelatedArticles(article, ranked); const parsedDate = new Date(article.datePublished || article.created_at); const safeDate = isNaN(parsedDate.getTime()) ? new Date() : parsedDate; // Hardcode the call-to-action text arrays uniformly across all pages const structuredIngredients = [ "Visit site to see list of ingredients" ]; const structuredInstructions = [ { "@type": "HowToStep", "text": "Visit site for instructions" } ]; // Dynamic Recipe Configuration Settings const recipeYield = article.recipeYield || "4 servings"; const cookTime = article.cookTime || "PT15M"; const totalTime = article.totalTime || "PT20M"; // Dynamic Aggregate Ratings Fallbacks const ratingValue = article.ratingValue || "4.9"; const reviewCount = article.reviewCount || "124"; let html = articleTemplate .replace(/__TITLE__/g, article.title) .replace(/__DESCRIPTION__/g, article.description || article.title) .replace(/__IMAGE_URL__/g, article.image || "") .replace(/__CONTENT__/g, article.content) .replace(/__INTRO_PARAGRAPH__/g, article.intro || "") .replace(/__DATE_PUBLISHED__/g, article.datePublished || safeDate.toISOString()) .replace(/__DATE_MODIFIED__/g, article.dateModified || article.datePublished || safeDate.toISOString()) .replace(/__DISPLAY_DATE__/g, safeDate.toDateString()) .replace(/__CANONICAL_URL__/g, canonical) .replace(/__RELATED_ARTICLES_DYNAMIC__/g, relatedHTML) .replace(/__RECIPE_YIELD__/g, recipeYield) .replace(/__COOK_TIME__/g, cookTime) .replace(/__TOTAL_TIME__/g, totalTime) .replace(/__RATING_VALUE__/g, ratingValue) .replace(/__REVIEW_COUNT__/g, reviewCount) .replace(/__INGREDIENTS_JSON_ARRAY__/g, JSON.stringify(structuredIngredients)) .replace(/__INSTRUCTIONS_JSON_ARRAY__/g, JSON.stringify(structuredInstructions)); fs.writeFileSync(path.join(articleOut, `${article.slug}.html`), html); }); /* ========================================================= HOMEPAGE ========================================================= */ const latest = ranked[0]; const grid = ranked.slice(1).map(article => ` `).join(""); const homepage = homeTemplate .replace(/{{LATEST_URL}}/g, `articles/${latest.slug}.html`) .replace(/{{LATEST_IMAGE}}/g, latest.image) .replace(/{{LATEST_TITLE}}/g, latest.title) .replace(/{{LATEST_DESCRIPTION}}/g, latest.description || latest.title) .replace(/{{ALL_ARTICLES_GRID}}/g, grid); fs.writeFileSync(path.join(distDir, "index.html"), homepage); /* ========================================================= STATIC PAGES ========================================================= */ const finalAbout = aboutTemplate .replace(/__TITLE__/g, pagesData.pages.about.title) .replace(/__DESCRIPTION__/g, pagesData.pages.about.description) .replace(/__CANONICAL_URL__/g, pagesData.pages.about.canonical) .replace(/__CONTENT__/g, formatSections(pagesData.pages.about.content_sections)); fs.writeFileSync(path.join(distDir, "about.html"), finalAbout); const finalPrivacy = privacyTemplate .replace(/__TITLE__/g, pagesData.pages.privacy.title) .replace(/__DESCRIPTION__/g, pagesData.pages.privacy.description) .replace(/__CANONICAL_URL__/g, pagesData.pages.privacy.canonical) .replace(/__DATE_MODIFIED__/g, pagesData.pages.privacy.dateModified || new Date().toDateString()) .replace(/__CONTENT__/g, formatSections(pagesData.pages.privacy.sections)); fs.writeFileSync(path.join(distDir, "privacy.html"), finalPrivacy); if (fs.existsSync("./contact-us.html")) { fs.copyFileSync("./contact-us.html", path.join(distDir, "contact-us.html")); } /* ========================================================= STATIC ROOT DEPLOYMENT & ASSET COPIES ========================================================= */ // Deploy Ultimate Cajun Potato Soup if (fs.existsSync("./ultimate-cajun-potato-soup.html")) { fs.copyFileSync("./ultimate-cajun-potato-soup.html", path.join(distDir, "ultimate-cajun-potato-soup.html")); } // Deploy Cristiano Ronaldo vs Messi GOAT Debate if (fs.existsSync("./cristiano-ronaldo-vs-messi-goat-debate.html")) { fs.copyFileSync("./cristiano-ronaldo-vs-messi-goat-debate.html", path.join(distDir, "cristiano-ronaldo-vs-messi-goat-debate.html")); } // Deploy Mbappe Next Generation Messi New Era if (fs.existsSync("./mbappe-next-generation-messi-new-era.html")) { fs.copyFileSync("./mbappe-next-generation-messi-new-era.html", path.join(distDir, "mbappe-next-generation-messi-new-era.html")); } // Deploy IMG_3389.png alongside static files in the root /dist if (fs.existsSync("./IMG_3389.png")) { fs.copyFileSync("./IMG_3389.png", path.join(distDir, "IMG_3389.png")); console.log("๐ผ๏ธ Deploying 'IMG_3389.png' to /dist."); } /* ========================================================= RSS + SITEMAP GENERATION ========================================================= */ const rssItems = ranked.slice(0, 50).map(article => { const d = new Date(article.datePublished || article.created_at); const safe = isNaN(d.getTime()) ? new Date() : d; const itemUrl = `${config.baseUrl}/articles/${article.slug}.html`; return `