${escapeHtml(company.legalNameEn)}
${escapeHtml(company.legalNameCn)}
${escapeHtml(company.address)}
#!/usr/bin/env node import fs from 'node:fs'; import path from 'node:path'; const root = path.resolve(path.dirname(new URL(import.meta.url).pathname), '..'); const distDir = path.join(root, 'dist'); const wwwDir = path.join(distDir, 'www'); const h5Dir = path.join(distDir, 'h5'); const siteContent = readJson(path.join(root, 'content', 'site.json')); const legalContent = readJson(path.join(root, 'content', 'legal.json')); const H5_PRIVACY_URL = `${siteContent.company.h5Website}/privacy.html`; const H5_TERMS_URL = `${siteContent.company.h5Website}/terms.html`; const H5_DELETE_ACCOUNT_URL = `${siteContent.company.h5Website}/delete-account.html`; const H5_CHILD_SAFETY_URL = `${siteContent.company.h5Website}/child-safety.html`; const landingPages = fs .readdirSync(path.join(root, 'content', 'landing-pages')) .filter((file) => file.endsWith('.json')) .map((file) => readJson(path.join(root, 'content', 'landing-pages', file))) .sort((a, b) => a.slug.localeCompare(b.slug)); build(); function build() { fs.rmSync(distDir, { force: true, recursive: true }); ensureDir(path.join(wwwDir, 'assets')); ensureDir(path.join(h5Dir, 'assets')); copyFile(path.join(root, 'src', 'site.css'), path.join(wwwDir, 'assets', 'site.css')); copyFile(path.join(root, 'src', 'site.css'), path.join(h5Dir, 'assets', 'site.css')); writeHtml(path.join(wwwDir, 'index.html'), renderHomePage()); writeHtml( path.join(wwwDir, 'privacy', 'index.html'), renderRedirectPage({ title: 'Privacy Policy Redirect', target: H5_PRIVACY_URL, }), ); writeHtml( path.join(wwwDir, 'terms', 'index.html'), renderRedirectPage({ title: 'Terms of Service Redirect', target: H5_TERMS_URL, }), ); writeHtml( path.join(wwwDir, 'delete-account', 'index.html'), renderRedirectPage({ title: 'Delete Account Redirect', target: H5_DELETE_ACCOUNT_URL, }), ); writeHtml( path.join(wwwDir, 'child-safety', 'index.html'), renderRedirectPage({ title: 'Child Safety Standards Redirect', target: H5_CHILD_SAFETY_URL, }), ); writeText(path.join(wwwDir, 'robots.txt'), 'User-agent: *\nAllow: /\n'); writeText(path.join(wwwDir, 'sitemap.xml'), renderSiteMap()); writeHtml(path.join(h5Dir, 'index.html'), renderLandingIndexPage()); writeHtml( path.join(h5Dir, 'privacy.html'), renderPlainLegalPage('privacy', { canonical: H5_PRIVACY_URL, }), ); writeHtml( path.join(h5Dir, 'terms.html'), renderLegalPage('terms', { canonical: H5_TERMS_URL, homeHref: '/', privacyHref: '/privacy.html', termsHref: '/terms.html', primaryActionHref: 'https://www.haiyihy.com/', primaryActionLabel: 'Official Site', }), ); writeHtml( path.join(h5Dir, 'delete-account.html'), renderPlainLegalPage('deleteAccount', { canonical: H5_DELETE_ACCOUNT_URL, }), ); writeHtml( path.join(h5Dir, 'child-safety.html'), renderPlainLegalPage('childSafety', { canonical: H5_CHILD_SAFETY_URL, }), ); writeHtml(path.join(h5Dir, 'studio', 'index.html'), renderStudioPage()); writeText(path.join(h5Dir, 'data', 'pages.json'), `${JSON.stringify(landingPages, null, 2)}\n`); for (const page of landingPages) { writeHtml(path.join(h5Dir, page.slug, 'index.html'), renderLandingPage(page)); } console.log(`Built public site to ${distDir}`); } function renderHomePage() { const { brand, company, hero } = siteContent; return renderDocument({ title: `${brand.name} | Official Website`, description: brand.description, canonical: `${company.website}/`, hostRoot: '', theme: 'ember', bodyClass: 'corp-body', body: `
${escapeHtml(hero.summary)}
${escapeHtml(company.legalNameCn)}
${escapeHtml(company.address)}
${escapeHtml(company.supportEmail)}
${escapeHtml(company.website)}
${escapeHtml(doc.summary)}
Effective date: ${escapeHtml(legalContent.effectiveDate)}
Operator: ${escapeHtml(company.legalNameEn)} (${escapeHtml(company.legalNameCn)})
Address: ${escapeHtml(company.address)}
Contact: ${escapeHtml(company.privacyEmail)} / ${escapeHtml(company.phone)}
${kind === 'deleteAccount' ? `Start a deletion request by email
` : ''}${escapeHtml(doc.summary)}
${doc.sections.map((section) => renderPlainLegalSection(section)).join('\n')} `; } function renderLandingIndexPage() { const { brand, company } = siteContent; return renderDocument({ title: `${brand.name} H5 | Campaign Pages`, description: 'Index of JSON-driven Yumi landing pages.', canonical: `${company.h5Website}/`, hostRoot: '', theme: 'sapphire', body: ` ${renderHeader({ brandName: brand.name, subline: 'H5 landing pages', links: [ { href: '/', label: 'H5 Home' }, { href: '/studio/', label: 'Studio' }, { href: 'https://www.haiyihy.com/', label: 'Official Site' }, ], action: { href: H5_PRIVACY_URL, label: 'Privacy' }, })}Each page below is generated from a single JSON file, which keeps campaign updates lightweight and predictable.
${escapeHtml(page.summary)}
Open page `).join('')}${escapeHtml(page.summary)}
${escapeHtml(section.body)}
${renderBullets(section.bullets)}This page is a publishing reference for operators and developers. It documents the current slugs and shows the exact JSON structure that drives each landing page.
public-site/content/landing-pages, rebuild the site, and redeploy. This keeps operations fast while avoiding app-release coupling.
${escapeHtml(page.summary)}
${escapeHtml(JSON.stringify(page, null, 2))}
${escapeHtml(paragraph)}
`).join(''); } function renderBullets(bullets = []) { if (!bullets?.length) { return ''; } return `- ${escapeHtml(item)}
`).join(''); } function renderDeleteAccountMailto(email) { const subject = encodeURIComponent('Delete Yumi Account'); return `mailto:${email}?subject=${subject}`; } function renderSiteMap() { const pages = [ 'https://www.haiyihy.com/', 'https://h5.haiyihy.com/', H5_PRIVACY_URL, H5_TERMS_URL, H5_DELETE_ACCOUNT_URL, H5_CHILD_SAFETY_URL, 'https://h5.haiyihy.com/studio/', ...landingPages.map((page) => `https://h5.haiyihy.com/${page.slug}/`), ]; return `Redirecting to ${escapeHtml(target)}...
`; } function readJson(filePath) { return JSON.parse(fs.readFileSync(filePath, 'utf8')); } function ensureDir(dirPath) { fs.mkdirSync(dirPath, { recursive: true }); } function writeHtml(filePath, content) { ensureDir(path.dirname(filePath)); fs.writeFileSync(filePath, `${content}\n`, 'utf8'); } function writeText(filePath, content) { ensureDir(path.dirname(filePath)); fs.writeFileSync(filePath, content, 'utf8'); } function copyFile(source, target) { ensureDir(path.dirname(target)); fs.copyFileSync(source, target); } function escapeHtml(value = '') { return String(value) .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') .replaceAll("'", '''); } function escapeAttr(value = '') { return escapeHtml(value); }