← back to Nodailyworries
build.mjs
283 lines
// Build the No Daily Worries insurance-guide site from content fragments.
// Static-first: wraps each content/*.html fragment in a shared template and
// writes the pages into public/. Re-run after editing any fragment.
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
const BRAND = 'No Daily Worries';
const DOMAIN = 'nodailyworries.com';
const ADSENSE_PUB = 'ca-pub-5278231299883833';
// AdSense Auto-ads loader — present so AdSense can review + serve once approved.
const ADSENSE = `<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=${ADSENSE_PUB}" crossorigin="anonymous"></script>`;
const GUIDES = [
{ slug: 'auto-insurance', file: 'auto', title: 'Auto Insurance: A Plain-English Buyer’s Guide',
blurb: 'Coverage types, how to pick limits, what drives your premium, and real ways to save.' },
{ slug: 'home-insurance', file: 'home', title: 'Homeowners Insurance: What’s Covered, What Isn’t, and How Much You Need',
blurb: 'The six coverages, replacement cost vs. cash value, the gaps to fill, and how to size it right.' },
{ slug: 'life-insurance', file: 'life', title: 'Life Insurance: How Much You Need and Which Type to Buy',
blurb: 'Term vs. whole, the DIME method, underwriting, and the mistakes that cost families most.' },
{ slug: 'renters-insurance', file: 'renters', title: 'Renters Insurance: Small Policy, Big Protection',
blurb: 'What it covers beyond theft, replacement cost vs. cash value, how much you need, and why it’s such good value.' },
{ slug: 'health-insurance', file: 'health', title: 'Health Insurance: The Four Numbers That Decode Any Plan',
blurb: 'Premium, deductible, coinsurance, out-of-pocket max — plus HMO vs. PPO and how to match a plan to how you use care.' },
{ slug: 'umbrella-insurance', file: 'umbrella', title: 'Umbrella Insurance: Cheap Coverage That Protects Everything You Own',
blurb: 'How extra liability sits on top of auto and home, who actually needs it, and why $1M costs so little.' },
];
const nav = `
<header class="site-head">
<div class="wrap">
<a class="brand" href="/">No Daily <span>Worries</span></a>
<nav>
<a href="/guides/auto-insurance.html">Auto</a>
<a href="/guides/home-insurance.html">Home</a>
<a href="/guides/life-insurance.html">Life</a>
<a href="/#guides">All guides</a>
<a href="/about.html">About</a>
<a href="/contact.html">Contact</a>
<a href="/#quote" class="cta">Get quote help</a>
</nav>
</div>
</header>`;
const footer = `
<footer class="site-foot">
<div class="wrap">
<p>© ${new Date().getFullYear()} ${BRAND} · ${DOMAIN}</p>
<p class="fine">Educational information only — not personalized insurance, financial, or legal advice.
Coverage varies by insurer and state; consult a licensed agent.
<a href="/about.html">About</a> · <a href="/contact.html">Contact</a> · <a href="/privacy.html">Privacy</a></p>
</div>
</footer>`;
const page = ({ title, desc, canonical, body, type = 'website', jsonld = '' }) => `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>${title} · ${BRAND}</title>
<meta name="description" content="${desc}">
<link rel="canonical" href="https://${DOMAIN}${canonical}">
<meta property="og:title" content="${title}">
<meta property="og:description" content="${desc}">
<meta property="og:type" content="${type}">
<meta property="og:site_name" content="${BRAND}">
${jsonld}
<link rel="stylesheet" href="/style.css">
${ADSENSE}
</head>
<body>
${nav}
${body}
${footer}
</body>
</html>`;
const leadForm = `
<section id="quote" class="quote">
<div class="wrap">
<h2>Not sure where to start?</h2>
<p>Tell us what you’re shopping for and we’ll point you to the right guide and next step. No spam, no obligation.</p>
<form class="lead" onsubmit="return submitLead(event)">
<select name="line" required aria-label="Type of insurance">
<option value="">I need help with…</option>
<option>Auto insurance</option>
<option>Homeowners insurance</option>
<option>Life insurance</option>
<option>Not sure yet</option>
</select>
<input type="email" name="email" placeholder="Your email" required aria-label="Email">
<input type="text" name="zip" placeholder="ZIP" pattern="[0-9]{5}" aria-label="ZIP code" maxlength="5">
<button type="submit">Send</button>
</form>
<p class="lead-note" id="leadNote" hidden></p>
</div>
</section>
<script>
async function submitLead(e){
e.preventDefault();
const f=e.target, note=document.getElementById('leadNote');
const body=Object.fromEntries(new FormData(f).entries());
try{
const r=await fetch('/api/lead',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});
note.hidden=false;
note.textContent = r.ok ? 'Thanks — check your inbox for a starting point.' : 'Something went wrong. Please try again.';
if(r.ok) f.reset();
}catch(_){ note.hidden=false; note.textContent='Network error — please try again.'; }
return false;
}
</script>`;
// ---- home page ----
const homeBody = `
<section class="hero">
<div class="wrap">
<h1>Insurance, without the daily worries.</h1>
<p class="sub">Clear, honest guides that help you buy the right coverage — and stop overpaying for it.
No jargon, no sales pitch, just what actually matters.</p>
<a href="#guides" class="cta big">Start with a guide</a>
</div>
</section>
<section id="guides" class="guides">
<div class="wrap">
<h2>Buyer’s guides</h2>
<div class="cards">
${GUIDES.map(g => `
<a class="card" href="/guides/${g.slug}.html">
<span class="kicker">${g.slug.split('-')[0].toUpperCase()}</span>
<h3>${g.title}</h3>
<p>${g.blurb}</p>
<span class="read">Read the guide →</span>
</a>`).join('')}
</div>
</div>
</section>
<section class="why">
<div class="wrap">
<h2>Why No Daily Worries</h2>
<p>Insurance is sold fast and bought confused. We slow it down: every guide is written in plain English,
explains the trade-offs instead of pushing a product, and focuses on the two things that decide whether a
policy protects you — <strong>the right coverage</strong> and <strong>the right price</strong>. Whether you’re
insuring a first car, a first home, or a growing family, start here and buy with confidence.</p>
</div>
</section>
${leadForm}`;
// ---- build ----
mkdirSync('public/guides', { recursive: true });
writeFileSync('public/index.html', page({
title: 'Insurance buyer’s guides that save you money',
desc: 'Plain-English guides to auto, home, and life insurance — pick the right coverage and stop overpaying.',
canonical: '/', body: homeBody,
}));
const UPDATED = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' });
const UPDATED_ISO = new Date().toISOString().slice(0, 10);
for (const g of GUIDES) {
const frag = readFileSync(`content/${g.file}.html`, 'utf8');
const body = `
<article class="post">
<div class="wrap">
<p class="crumb"><a href="/">Home</a> / ${g.slug.split('-')[0][0].toUpperCase()+g.slug.split('-')[0].slice(1)} insurance</p>
<h1>${g.title}</h1>
<p class="meta" style="color:#8a8a8a;font-size:13px;margin:-2px 0 18px">Written & reviewed in-house · Last updated ${UPDATED}</p>
${frag}
<div class="post-cta">
<h3>Ready to compare?</h3>
<p>Use our quick form and we’ll point you to the right next step for your situation.</p>
<a href="/#quote" class="cta">Get quote help →</a>
</div>
</div>
</article>`;
const jsonld = `<script type="application/ld+json">${JSON.stringify({
'@context': 'https://schema.org', '@type': 'Article', headline: g.title, description: g.blurb,
datePublished: UPDATED_ISO, dateModified: UPDATED_ISO,
author: { '@type': 'Organization', name: BRAND }, publisher: { '@type': 'Organization', name: BRAND },
mainEntityOfPage: `https://${DOMAIN}/guides/${g.slug}.html`,
})}</script>`;
writeFileSync(`public/guides/${g.slug}.html`, page({
title: g.title, desc: g.blurb, canonical: `/guides/${g.slug}.html`, body, type: 'article', jsonld,
}));
}
// ---- privacy (AdSense-compliant: discloses third-party ad cookies + opt-outs) ----
const privacyBody = `
<article class="post">
<div class="wrap">
<h1>Privacy Policy</h1>
<p class="crumb">${BRAND} · ${DOMAIN} · Last updated ${new Date().toLocaleDateString('en-US',{year:'numeric',month:'long',day:'numeric'})}</p>
<p>This policy explains what ${BRAND} collects, how we use it, and your choices.</p>
<h2>Information we collect</h2>
<p>Standard server logs (IP address, browser type, referring pages, timestamps) and information stored in
cookies. If you submit our contact form, we collect the email, ZIP, and interest you provide so we can respond.
We do not sell your personal information.</p>
<h2>Cookies and advertising</h2>
<p>We use cookies to operate the site and to display advertising. <strong>Third-party vendors, including
Google, use cookies to serve ads</strong> based on your prior visits to this and other websites. Google’s use
of advertising cookies enables it and its partners to serve ads to you based on your visit to ${DOMAIN} and/or
other sites on the Internet. You may opt out of personalized advertising by visiting
<a href="https://www.google.com/settings/ads" target="_blank" rel="noopener noreferrer">Google Ads Settings</a>,
or opt out of third-party vendors’ use of cookies at
<a href="https://www.aboutads.info/choices/" target="_blank" rel="noopener noreferrer">aboutads.info</a>
(or <a href="https://www.youronlinechoices.eu/" target="_blank" rel="noopener noreferrer">youronlinechoices.eu</a>
in the EEA/UK). Where required by law, we request consent before setting non-essential advertising cookies.
See <a href="https://policies.google.com/technologies/partner-sites" target="_blank" rel="noopener noreferrer">Google’s Privacy & Terms</a>.</p>
<h2>Analytics</h2>
<p>We may use analytics cookies to understand aggregate site usage.</p>
<h2>Your choices</h2>
<p>Most browsers let you block or delete cookies through their settings, and you can use the opt-out links
above to limit personalized advertising.</p>
<h2>Contact</h2>
<p>Questions or a data request? Email <a href="mailto:privacy@${DOMAIN}">privacy@${DOMAIN}</a>.</p>
</div>
</article>`;
writeFileSync('public/privacy.html', page({
title: 'Privacy Policy', desc: `How ${BRAND} handles data, cookies, and advertising.`,
canonical: '/privacy.html', body: privacyBody,
}));
// ---- About (AdSense trust requirement: who is responsible for the site) ----
const aboutBody = `
<article class="post">
<div class="wrap">
<h1>About ${BRAND}</h1>
<p class="crumb">Plain-English insurance buyer's guides · ${DOMAIN}</p>
<p>${BRAND} exists to make insurance understandable. Most people buy coverage they don't fully understand and
find out what it does — or doesn't do — only on their worst day. We write clear, practical buyer's guides that
explain what each type of insurance actually covers, how to size it, and where to save without leaving yourself exposed.</p>
<h2>What we do</h2>
<p>We publish original, independent guides on auto, home, renters, life, health, and umbrella insurance — written
to be read by a normal person, not an underwriter. Every guide is educational and is dated so you know how current it is.</p>
<h2>How we stay independent</h2>
<p>Our guides are not a sales pitch for any one insurer. We explain the trade-offs and encourage you to compare
quotes and confirm details with a licensed agent for your state. Where a page links to a tool or service, it never
changes the advice in the guide.</p>
<h2>Editorial standards</h2>
<p>Every guide is written and edited in-house against the same checklist: explain the coverage in plain language,
define the jargon on first use, give concrete numbers and typical price ranges where they help, and flag the
trade-offs honestly rather than steering you toward any product. Guides are dated, reviewed periodically, and
corrected openly when rules or figures change — if you spot something out of date, tell us and we'll update it.</p>
<h2>How we're funded</h2>
<p>${BRAND} is reader-focused and supported by third-party advertising (see our
<a href="/privacy.html">Privacy Policy</a> for how ad cookies work and how to opt out). Advertising keeps the
guides free; it never determines what we recommend. We do not sell your personal information.</p>
<h2>An important note</h2>
<p>${BRAND} provides general educational information, not personalized insurance, financial, or legal advice.
Coverage, requirements, and pricing vary by insurer and by state. Always read your policy and consult a licensed
professional about your specific situation.</p>
<h2>Get in touch</h2>
<p>Questions, corrections, or a topic you'd like us to cover? See our <a href="/contact.html">contact page</a>.</p>
</div>
</article>`;
writeFileSync('public/about.html', page({
title: `About ${BRAND}`, desc: `Who is behind ${BRAND} and why we publish plain-English insurance buyer's guides.`,
canonical: '/about.html', body: aboutBody,
}));
// ---- Contact (AdSense trust requirement: a way to reach the publisher) ----
const contactBody = `
<article class="post">
<div class="wrap">
<h1>Contact ${BRAND}</h1>
<p class="crumb">We read every message · ${DOMAIN}</p>
<p>Have a question about a guide, spotted something that's out of date, or want us to cover a topic? We'd like to hear from you.</p>
<h2>Email</h2>
<p>Reach us at <a href="mailto:info@${DOMAIN}">info@${DOMAIN}</a>. We aim to respond within a few business days.</p>
<h2>Corrections</h2>
<p>Insurance rules change and we want our guides to stay accurate. If you find an error or something that's no longer
current, email us and we'll review and update it, noting the change.</p>
<h2>Editorial requests</h2>
<p>Want a plain-English guide on a coverage we haven't written about yet? Tell us — reader questions drive what we cover next.</p>
<p class="fine">Please note: ${BRAND} publishes educational information and cannot give personalized insurance,
financial, or legal advice. For advice about your specific policy or situation, consult a licensed agent.</p>
</div>
</article>`;
writeFileSync('public/contact.html', page({
title: `Contact ${BRAND}`, desc: `How to reach ${BRAND} — questions, corrections, and topic requests.`,
canonical: '/contact.html', body: contactBody,
}));
console.log('built: index + ' + GUIDES.length + ' guides + privacy');