Files
teknav-bak/app/scripts/generate-company-pages.mjs
T
arsam 0bec9a7fa4 Import source: app/scripts/generate-company-pages.mjs
Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
2026-09-18 23:23:24 +03:30

111 lines
4.9 KiB
JavaScript

#!/usr/bin/env node
// Generates static company directory pages at dist/directory/{slug}/index.html.
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { TeknavData } from '../teknav-data.js';
import { renderStaticFallback, replaceStaticFallback } from './static-fallback.mjs';
const SITE_URL = (process.env.TEKNAV_SITE_URL || process.env.VITE_SITE_URL || 'https://www.teknav.ir').replace(/\/$/, '');
const companies = TeknavData.companies ?? [];
if (!companies.length) {
console.log('[company-pages] no companies defined — skipping');
process.exit(0);
}
const template = readFileSync(resolve('dist/index.html'), 'utf8');
function esc(value) {
return String(value ?? '')
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
function clean(value) {
return String(value ?? '').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
}
function truncate(value, max = 155) {
const text = clean(value);
return text.length > max ? `${text.slice(0, max - 1).trim()}…` : text;
}
function buildPage(company) {
const canonical = `${SITE_URL}/directory/${company.slug}`;
const title = esc(`${company.name} | دایرکتوری شرکت‌های فناوری | تکناو`);
const description = esc(truncate(company.metaDescription || company.description, 160));
const keywords = esc([company.name, 'شرکت فناوری', ...(company.techStack || [])].slice(0, 10).join(', '));
const jsonLd = JSON.stringify({
'@context': 'https://schema.org',
'@graph': [
{
'@type': ['Organization', 'NewsMediaOrganization'],
'@id': `${SITE_URL}/#organization`,
name: 'تکناو',
url: SITE_URL,
logo: { '@type': 'ImageObject', url: `${SITE_URL}/favicon.png` },
},
{
'@type': 'Organization',
name: company.name,
description: clean(company.description),
url: company.websiteUrl || canonical,
sameAs: company.websiteUrl ? [company.websiteUrl] : [],
},
{
'@type': 'BreadcrumbList',
itemListElement: [
{ '@type': 'ListItem', position: 1, name: 'تکناو', item: SITE_URL },
{ '@type': 'ListItem', position: 2, name: 'دایرکتوری', item: `${SITE_URL}/directory` },
{ '@type': 'ListItem', position: 3, name: company.name },
],
},
],
});
let html = template;
html = html.replace(/<html lang="[^"]*" dir="[^"]*">/, '<html lang="fa" dir="rtl">');
html = html.replace(/<title>[^<]*<\/title>/, `<title>${title}</title>`);
html = html.replace(/(<meta name="description" content=")[^"]*(")/, `$1${description}$2`);
html = html.replace(/(<meta name="keywords" content=")[^"]*(")/, `$1${keywords}$2`);
html = html.replace(/(<meta property="og:locale" content=")[^"]*(")/, '$1fa_IR$2');
html = html.replace(/(<meta property="og:title" content=")[^"]*(")/, `$1${title}$2`);
html = html.replace(/(<meta property="og:description" content=")[^"]*(")/,`$1${description}$2`);
html = html.replace(/(<meta property="og:url" content=")[^"]*(")/, `$1${esc(canonical)}$2`);
html = html.replace(/(<link rel="canonical" href=")[^"]*(")/, `$1${esc(canonical)}$2`);
html = html.replace(/<link rel="alternate" hreflang="[^"]+" href="[^"]+" \/>/, `<link rel="alternate" hreflang="fa" href="${esc(canonical)}" />`);
html = html.replace(/<script type="application\/ld\+json">[\s\S]*?<\/script>/,
`<script type="application/ld+json">${jsonLd}</script>`);
html = replaceStaticFallback(html, renderStaticFallback({
eyebrow: 'دایرکتوری شرکت‌های فناوری',
title: clean(company.name),
description: clean(company.description || company.metaDescription),
facts: [
company.category ? { label: 'دسته', value: company.category } : null,
company.location ? { label: 'موقعیت', value: company.location } : null,
company.techStack?.length ? { label: 'فناوری‌ها', value: company.techStack.join('، ') } : null,
company.websiteUrl ? { label: 'وب‌سایت', value: company.websiteUrl } : null,
].filter(Boolean),
links: [
company.websiteUrl ? { href: company.websiteUrl, label: `وب‌سایت ${company.name}`, text: 'لینک رسمی شرکت' } : null,
{ href: '/directory', label: 'دایرکتوری شرکت‌ها', text: 'فهرست شرکت‌های فناوری' },
{ href: '/articles', label: 'مقاله‌های فناوری', text: 'تحلیل‌های تکناو' },
].filter(Boolean),
}));
return html;
}
let count = 0;
for (const company of companies) {
const dir = resolve(`dist/directory/${company.slug}`);
mkdirSync(dir, { recursive: true });
writeFileSync(`${dir}/index.html`, buildPage(company), 'utf8');
count++;
}
console.log(`[company-pages] generated ${count} company pages in dist/directory/`);