From 58f4415bf031e7f8c2156e0ad013d9323bf75e45 Mon Sep 17 00:00:00 2001 From: arsam Date: Fri, 18 Sep 2026 23:23:34 +0330 Subject: [PATCH] Import source: app/scripts/generate-semantic-pages.mjs Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality. --- app/scripts/generate-semantic-pages.mjs | 440 ++++++++++++++++++++++++ 1 file changed, 440 insertions(+) create mode 100644 app/scripts/generate-semantic-pages.mjs diff --git a/app/scripts/generate-semantic-pages.mjs b/app/scripts/generate-semantic-pages.mjs new file mode 100644 index 0000000..dc32a0a --- /dev/null +++ b/app/scripts/generate-semantic-pages.mjs @@ -0,0 +1,440 @@ +import { existsSync, 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 SITE_NAME = 'تکناو'; +const DEFAULT_IMAGE = `${SITE_URL}/images/og/default.jpg`; +const DEFAULT_KEYWORDS = 'تکناو, فناوری, هوش مصنوعی, علم داده, امنیت سایبری, نرم افزار, سخت افزار, استارتاپ, آینده فناوری, تحلیل تکنولوژی'; + +const template = readFileSync(resolve('dist/index.html'), 'utf8'); + +function esc(value) { + return String(value ?? '') + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(//g, '>'); +} + +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; +} + +const org = { + '@type': ['Organization', 'NewsMediaOrganization'], + '@id': `${SITE_URL}/#organization`, + name: 'تکناو', + alternateName: ['Teknav', 'تکنّاو'], + url: SITE_URL, + logo: { + '@type': 'ImageObject', + '@id': `${SITE_URL}/#logo`, + url: `${SITE_URL}/favicon.png`, + width: 512, + height: 512, + }, + image: { '@id': `${SITE_URL}/#logo` }, +}; + +const website = { + '@type': 'WebSite', + '@id': `${SITE_URL}/#website`, + url: SITE_URL, + name: 'تکناو', + alternateName: 'Teknav', + inLanguage: 'fa-IR', + publisher: { '@id': `${SITE_URL}/#organization` }, +}; + +const GLOSSARY_SAMEAS_MAP = { + ai: [ + 'https://www.wikidata.org/wiki/Q11660', + 'https://fa.wikipedia.org/wiki/%D9%87%D9%88%D8%B4_%D9%85%D8%B5%D9%86%D9%88%D8%B5%DB%8C' + ], + llm: [ + 'https://www.wikidata.org/wiki/Q115305900', + 'https://fa.wikipedia.org/wiki/%D9%85%D8%AF%D9%84_%D8%B2%D8%A8%D8%A7%D9%86%DB%8C_%D8%A8%D8%B2%D8%B1%DA%AF' + ], + rag: [ + 'https://www.wikidata.org/wiki/Q124378129', + 'https://en.wikipedia.org/wiki/Retrieval-augmented_generation' + ], + 'prompt-injection': [ + 'https://www.wikidata.org/wiki/Q117409023', + 'https://en.wikipedia.org/wiki/Prompt_injection' + ], + 'vector-database': [ + 'https://www.wikidata.org/wiki/Q119790697', + 'https://en.wikipedia.org/wiki/Vector_database' + ], + 'agentic-ai': [ + 'https://www.wikidata.org/wiki/Q127415174' + ] +}; + +function buildBreadcrumb(items) { + return { + '@type': 'BreadcrumbList', + itemListElement: items.map((item, idx) => ({ + '@type': 'ListItem', + position: idx + 1, + name: item.name, + ...(item.path ? { item: `${SITE_URL}${item.path}` } : {}), + })), + }; +} + +let count = 0; + +function writePage(routePath, title, description, keywords, jsonLdGraph) { + const canonical = `${SITE_URL}${routePath}`; + const jsonLd = JSON.stringify({ + '@context': 'https://schema.org', + '@graph': [ + org, + website, + ...jsonLdGraph, + ] + }); + + let html = template; + html = html.replace(//, ``); + html = html.replace(/[^<]*<\/title>/, `<title>${title}`); + html = html.replace(/(/, ``); + html = html.replace(/`); + html = replaceStaticFallback(html, renderStaticFallback({ + eyebrow: 'تکناو', + title, + description, + facts: [ + { label: 'زبان', value: 'فارسی' }, + { label: 'نشانی canonical', value: canonical }, + ], + links: [ + { href: '/', label: SITE_NAME, text: 'صفحه اصلی' }, + { href: '/articles', label: 'مقاله‌های فناوری', text: 'آرشیو تحلیل‌ها' }, + { href: '/authors', label: 'نویسندگان تکناو', text: 'پروفایل نویسندگان' }, + ], + })); + + // Normalize path format for directory creation + const relativeDir = routePath.startsWith('/') ? routePath.slice(1) : routePath; + const localDir = resolve(`dist/${relativeDir}`); + mkdirSync(localDir, { recursive: true }); + writeFileSync(`${localDir}/index.html`, html, 'utf8'); + count++; +} + +// 1. Generate Categories +for (const category of TeknavData.categories ?? []) { + const name = category.name; + const description = category.description || `مقاله‌ها و تحلیل‌های فارسی تکناو درباره ${name}.`; + const path = `/category/${category.slug}`; + const title = `${name} | مقاله‌ها و تحلیل‌های تکناو`; + const keywords = `${name}, مقاله ${name}, تحلیل ${name}, فناوری, تکناو`; + + const jsonLdGraph = [ + { + '@type': 'CollectionPage', + name, + description, + url: `${SITE_URL}${path}`, + inLanguage: 'fa-IR', + }, + buildBreadcrumb([ + { name: SITE_NAME, path: '/' }, + { name } + ]), + ]; + + writePage(path, title, description, keywords, jsonLdGraph); +} + +// 2. Generate Tags +for (const tag of TeknavData.tags ?? []) { + const name = tag.name; + const path = `/tag/${encodeURIComponent(name)}`; + const title = `برچسب ${name} | مقاله‌های تکنـاو`; + const description = `همه مقاله‌های تکنـاو درباره ${name}، همراه با تحلیل فارسی، داده و مسیرهای مرتبط برای مطالعه بیشتر.`; + const keywords = `${name}, تکنـاو, مقاله فناوری, تحلیل فناوری`; + + const jsonLdGraph = [ + { + '@type': 'CollectionPage', + name: `برچسب ${name}`, + url: `${SITE_URL}${path}`, + inLanguage: 'fa-IR', + }, + buildBreadcrumb([ + { name: SITE_NAME, path: '/' }, + { name: 'برچسب‌ها', path: '/articles' }, + { name } + ]), + ]; + + writePage(path, title, description, keywords, jsonLdGraph); +} + +// 3. Generate Glossary +for (const term of TeknavData.glossary ?? []) { + const slug = term.slug; + const path = `/glossary/${slug}`; + const title = `${term.term} چیست؟ | فرهنگ واژگان تکنّاو`; + const description = truncate(term.summary, 155); + const keywords = `${term.term}, ${term.english || ''}, فرهنگ واژگان فناوری, تکنّاو`; + const sameAs = GLOSSARY_SAMEAS_MAP[slug] || []; + + const jsonLdGraph = [ + { + '@type': 'DefinedTerm', + name: term.term, + alternateName: term.english, + description, + inDefinedTermSet: `${SITE_URL}/glossary`, + url: `${SITE_URL}${path}`, + sameAs, + }, + term.faqs?.length ? { + '@type': 'FAQPage', + mainEntity: term.faqs.map(([q, a]) => ({ + '@type': 'Question', + name: q, + acceptedAnswer: { '@type': 'Answer', text: a }, + })), + } : null, + buildBreadcrumb([ + { name: SITE_NAME, path: '/' }, + { name: 'فرهنگ واژگان', path: '/articles' }, + { name: term.term } + ]), + ].filter(Boolean); + + writePage(path, title, description, keywords, jsonLdGraph); +} + +// 4. Generate Topics +for (const topic of TeknavData.topicHubs ?? []) { + const slug = topic.slug; + const path = `/topics/${slug}`; + const name = topic.title; + const description = topic.description || `مقاله‌ها و تحلیل‌های تکناو درباره ${name}.`; + const title = topic.seoTitle || `${name} | خوشه موضوعی تکناو`; + const keywords = (topic.keywords ?? [name, 'تکناو', 'تحلیل فناوری']).join(', '); + + const jsonLdGraph = [ + { + '@type': 'CollectionPage', + name, + description, + url: `${SITE_URL}${path}`, + inLanguage: 'fa-IR', + isPartOf: { '@id': `${SITE_URL}/#website` }, + }, + topic.faqs?.length ? { + '@type': 'FAQPage', + mainEntity: topic.faqs.map(([q, a]) => ({ + '@type': 'Question', + name: q, + acceptedAnswer: { '@type': 'Answer', text: a }, + })), + } : null, + buildBreadcrumb([ + { name: SITE_NAME, path: '/' }, + { name: 'موضوعات', path: '/articles' }, + { name } + ]), + ].filter(Boolean); + + writePage(path, title, description, keywords, jsonLdGraph); +} + +// 5. Generate Series Pages +for (const series of TeknavData.articleSeries ?? []) { + const slug = series.slug; + const path = `/series/${slug}`; + const name = series.title; + const description = truncate(series.description || 'سری مقاله‌های تکناو برای دنبال کردن یک موضوع فناوری به‌ترتیب تحریریه.', 155); + const title = `${name} | سری مقاله‌های تکناو`; + const keywords = `${name}, سری مقاله, تکناو, تحلیل فناوری`; + + const jsonLdGraph = [ + { + '@type': 'CollectionPage', + name, + description, + url: `${SITE_URL}${path}`, + inLanguage: 'fa-IR', + }, + buildBreadcrumb([ + { name: SITE_NAME, path: '/' }, + { name: 'سری مقاله‌ها', path: '/articles' }, + { name } + ]), + ]; + + writePage(path, title, description, keywords, jsonLdGraph); +} + +// 6. Generate Static / Core Pages +const staticPages = [ + { + path: '/articles', + title: 'مقاله‌های فناوری | تکناو', + description: 'آرشیو مقاله‌های فارسی تکناو درباره هوش مصنوعی، علم داده، امنیت سایبری، نرم‌افزار، سخت‌افزار و آینده فناوری.', + keywords: DEFAULT_KEYWORDS, + jsonLdGraph: [{ '@type': 'CollectionPage', name: 'مقاله‌های فناوری تکناو', url: `${SITE_URL}/articles`, inLanguage: 'fa-IR' }] + }, + { + path: '/authors', + title: 'نویسندگان تکناو | تحلیلگران فناوری', + description: 'آشنایی با نویسندگان و تحلیلگران تکناو در حوزه هوش مصنوعی، امنیت سایبری، نرم‌افزار، سخت‌افزار و استارتاپ.', + keywords: 'نویسندگان تکناو, تحلیلگر فناوری, نویسنده هوش مصنوعی, نویسنده امنیت سایبری', + jsonLdGraph: [{ '@type': 'CollectionPage', name: 'نویسندگان تکناو', url: `${SITE_URL}/authors`, inLanguage: 'fa-IR' }] + }, + { + path: '/newsletter', + title: 'آرشیو خبرنامه تکنـاو | تحلیل هفتگی فناوری', + description: 'آرشیو خبرنامه‌های ارسال‌شده تکنـاو درباره هوش مصنوعی، امنیت سایبری، نرم‌افزار، سخت‌افزار، استارتاپ و داده.', + keywords: 'خبرنامه تکنـاو, آرشیو خبرنامه فناوری, تحلیل هفتگی فناوری, هوش مصنوعی, امنیت سایبری', + jsonLdGraph: [ + { '@type': 'CollectionPage', name: 'آرشیو خبرنامه تکنـاو', url: `${SITE_URL}/newsletter`, inLanguage: 'fa-IR' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'خبرنامه' }]) + ] + }, + { + path: '/jobs', + title: 'فرصت‌های شغلی فناوری | تکناو', + description: 'آگهی‌های شغلی منتخب فناوری و امکان ثبت موقعیت شغلی شرکت‌ها در تکناو.', + keywords: 'شغل فناوری, استخدام برنامه نویس, استخدام هوش مصنوعی, تکناو جابز', + jsonLdGraph: [ + { '@type': 'CollectionPage', name: 'فرصت‌های شغلی فناوری | تکناو', url: `${SITE_URL}/jobs`, inLanguage: 'fa-IR', description: 'آگهی‌های شغلی منتخب فناوری و امکان ثبت موقعیت شغلی شرکت‌ها در تکناو.' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'فرصت‌های شغلی فناوری' }]) + ] + }, + { + path: '/courses', + title: 'دوره‌های تخصصی فناوری | تکناو', + description: 'دوره‌های فارسی تکناو برای هوش مصنوعی، امنیت، نرم‌افزار و مهارت‌های فنی محصول‌محور.', + keywords: 'دوره فناوری, آموزش هوش مصنوعی, آموزش امنیت, دوره فارسی برنامه نویسی', + jsonLdGraph: [ + { '@type': 'CollectionPage', name: 'دوره‌های تخصصی فناوری | تکناو', url: `${SITE_URL}/courses`, inLanguage: 'fa-IR', description: 'دوره‌های فارسی تکناو برای هوش مصنوعی، امنیت، نرم‌افزار و مهارت‌های فنی محصول‌محور.' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'دوره‌های تخصصی فناوری' }]) + ] + }, + { + path: '/membership', + title: 'عضویت پریمیوم تکناو | محتوای اختصاصی فناوری', + description: 'عضویت پریمیوم تکناو برای تجربه بدون تبلیغ، محتوای اختصاصی و دسترسی بهتر به دوره‌ها و گزارش‌های تخصصی.', + keywords: 'عضویت تکناو, محتوای پریمیوم فناوری, تحلیل اختصاصی فناوری', + jsonLdGraph: [ + { '@type': 'CollectionPage', name: 'عضویت پریمیوم تکناو | محتوای اختصاصی فناوری', url: `${SITE_URL}/membership`, inLanguage: 'fa-IR', description: 'عضویت پریمیوم تکناو برای تجربه بدون تبلیغ، محتوای اختصاصی و دسترسی بهتر به دوره‌ها و گزارش‌های تخصصی.' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'عضویت پریمیوم تکناو' }]) + ] + }, + { + path: '/about', + title: 'درباره ما | تکناو', + description: 'درباره رسانه فارسی تکناو و ماموریت ما در تحلیل فناوری، هوش مصنوعی و امنیت سایبری.', + keywords: 'درباره تکناو, ماموریت تکناو, رسانه فناوری, تحلیل هوش مصنوعی', + jsonLdGraph: [ + { '@type': 'WebPage', '@id': `${SITE_URL}/about#webpage`, name: 'درباره ما | تکناو', description: 'درباره رسانه فارسی تکناو و ماموریت ما در تحلیل فناوری، هوش مصنوعی و امنیت سایبری.', url: `${SITE_URL}/about`, inLanguage: 'fa-IR' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'درباره ما' }]) + ] + }, + { + path: '/contact', + title: 'تماس با ما | تکناو', + description: 'ارتباط با تیم تحریریه و پشتیبانی تکناو برای نظرات، پیشنهادات و همکاری.', + keywords: 'تماس با تکناو, پشتیبانی تکناو, همکاری با تکناو, تبلیغات تکناو', + jsonLdGraph: [ + { '@type': 'WebPage', '@id': `${SITE_URL}/contact#webpage`, name: 'تماس با ما | تکناو', description: 'ارتباط با تیم تحریریه و پشتیبانی تکناو برای نظرات، پیشنهادات و همکاری.', url: `${SITE_URL}/contact`, inLanguage: 'fa-IR' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'تماس با ما' }]) + ] + }, + { + path: '/terms', + title: 'قوانین و مقررات | تکناو', + description: 'شرایط استفاده و قوانین و مقررات استفاده از محتوا و خدمات رسانه تکناو.', + keywords: 'قوانین تکناو, مقررات تکناو, شرایط استفاده تکناو', + jsonLdGraph: [ + { '@type': 'WebPage', '@id': `${SITE_URL}/terms#webpage`, name: 'قوانین و مقررات | تکناو', description: 'شرایط استفاده و قوانین و مقررات استفاده از محتوا و خدمات رسانه تکناو.', url: `${SITE_URL}/terms`, inLanguage: 'fa-IR' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'قوانین و مقررات' }]) + ] + }, + { + path: '/privacy', + title: 'حریم خصوصی | تکناو', + description: 'سیاست حریم خصوصی و نحوه حفاظت از اطلاعات کاربران در رسانه تکناو.', + keywords: 'حریم خصوصی تکناو, سیاست حریم خصوصی, امنیت اطلاعات کاربران', + jsonLdGraph: [ + { '@type': 'WebPage', '@id': `${SITE_URL}/privacy#webpage`, name: 'حریم خصوصی | تکناو', description: 'سیاست حریم خصوصی و نحوه حفاظت از اطلاعات کاربران در رسانه تکناو.', url: `${SITE_URL}/privacy`, inLanguage: 'fa-IR' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'حریم خصوصی' }]) + ] + }, + { + path: '/tools', + title: 'ابزارهای تخصصی فناوری | تکناو', + description: 'جعبه‌ابزار تخصصی تکناو برای توسعه‌دهندگان، مهندسان داده و تحلیلگران؛ شامل نرمال‌ساز متن و ماشین‌حساب RAG.', + keywords: 'ابزار توسعه دهنده, نرمال ساز متن, ماشین حساب RAG, جعبه ابزار تکناو', + jsonLdGraph: [ + { '@type': 'CollectionPage', name: 'ابزارهای تخصصی فناوری | تکناو', url: `${SITE_URL}/tools`, inLanguage: 'fa-IR', description: 'جعبه‌ابزار تخصصی تکناو برای توسعه‌دهندگان، مهندسان داده و تحلیلگران؛ شامل نرمال‌ساز متن و ماشین‌حساب RAG.' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'ابزارها' }]) + ] + }, + { + path: '/tools/nlp-normalizer', + title: 'نرمال‌ساز متن فارسی (NLP) | ابزارهای تکناو', + description: 'ابزار تخصصی پاکسازی، نیم‌فاصله‌گذاری و پیش‌پردازش متن فارسی برای تسک‌های پردازش زبان طبیعی و مدل‌های زبانی.', + keywords: 'نرمال ساز متن فارسی, نیم فاصله گذاری, پیش پردازش متن فارسی, NLP فارسی', + jsonLdGraph: [ + { '@type': 'WebPage', '@id': `${SITE_URL}/tools/nlp-normalizer#webpage`, name: 'نرمال‌ساز متن فارسی (NLP) | ابزارهای تکناو', description: 'ابزار تخصصی پاکسازی، نیم‌فاصله‌گذاری و پیش‌پردازش متن فارسی برای تسک‌های پردازش زبان طبیعی و مدل‌های زبانی.', url: `${SITE_URL}/tools/nlp-normalizer`, inLanguage: 'fa-IR' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'ابزارها', path: '/tools' }, { name: 'نرمال‌ساز متن فارسی' }]) + ] + }, + { + path: '/tools/rag-calculator', + title: 'ماشین‌حساب ظرفیت و هزینه RAG | ابزارهای تکناو', + description: 'ماشین‌حساب تخصصی محاسبه تعداد توکن، فضای پایگاه‌داده برداری، هزینه‌های استنتاج LLM و پهنای باند معماری RAG.', + keywords: 'هزینه RAG, ماشین حساب RAG, پهنای باند RAG, پایگاه داده برداری', + jsonLdGraph: [ + { '@type': 'WebPage', '@id': `${SITE_URL}/tools/rag-calculator#webpage`, name: 'ماشین‌حساب ظرفیت و هزینه RAG | ابزارهای تکناو', description: 'ماشین‌حساب تخصصی محاسبه تعداد توکن، فضای پایگاه‌داده برداری، هزینه‌های استنتاج LLM و پهنای باند معماری RAG.', url: `${SITE_URL}/tools/rag-calculator`, inLanguage: 'fa-IR' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'ابزارها', path: '/tools' }, { name: 'ماشین‌حساب RAG' }]) + ] + }, + { + path: '/tekrag-persian', + title: 'بنچمارک TekRag | ارزیابی سامانه‌های RAG فارسی', + description: 'درگاه رسمی بنچمارک TekRag؛ اولین فریم‌ورک استاندارد ارزیابی دقت، بازیابی و پاسخ‌دهی سیستم‌های RAG زبان فارسی.', + keywords: 'TekRag benchmark, بنچمارک RAG فارسی, ارزیابی سیستم RAG, هوش مصنوعی فارسی', + jsonLdGraph: [ + { '@type': 'WebPage', '@id': `${SITE_URL}/tekrag-persian#webpage`, name: 'بنچمارک TekRag | ارزیابی سامانه‌های RAG فارسی', description: 'درگاه رسمی بنچمارک TekRag؛ اولین فریم‌ورک استاندارد ارزیابی دقت، بازیابی و پاسخ‌دهی سیستم‌های RAG زبان فارسی.', url: `${SITE_URL}/tekrag-persian`, inLanguage: 'fa-IR' }, + buildBreadcrumb([{ name: SITE_NAME, path: '/' }, { name: 'بنچمارک TekRag' }]) + ] + } +]; + +for (const pg of staticPages) { + writePage(pg.path, pg.title, pg.description, pg.keywords, pg.jsonLdGraph); +} + +console.log(`[semantic-pages] generated ${count} static semantic HTML pages`);