71 lines
3.0 KiB
JavaScript
Executable File
71 lines
3.0 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
'use strict';
|
|
|
|
// 从官网 sitemap 生成 KB 类目地图 bridge/kb/11-site-map.md(旧 bridge site-map.md 的移植)。
|
|
// 只生成导航/类目页 Q/A,不包含商品详情页;生成后需重建 KB 索引。
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const ROOT = path.resolve(__dirname, '..');
|
|
const OUT = path.join(ROOT, 'bridge', 'kb', '11-site-map.md');
|
|
const SITEMAP = process.env.SITE_MAP_URL || 'https://www.yehwang.com/sitemap.xml';
|
|
const BASE = process.env.SITE_BASE_URL || 'https://www.yehwang.com';
|
|
const WHITELIST = new Set(['jewelry', 'clothes', 'accessories', 'packing-and-display', 'bestsellers', 'new', 'top-deal', 'campaigns', 'brands', 'wholesale-service', 'factory-direct-wholesale-en', 'dropshipping', 'restock-request']);
|
|
|
|
function titleCase(segment) {
|
|
return String(segment || '')
|
|
.replace(/-/g, ' ')
|
|
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
}
|
|
|
|
function buildEntries(urls) {
|
|
const out = [];
|
|
const seen = new Set();
|
|
for (const url of urls) {
|
|
if (!url.startsWith(BASE)) continue;
|
|
const pathPart = url.slice(BASE.length).split('?')[0];
|
|
if (!pathPart || pathPart === '/' || /\.html$/i.test(pathPart)) continue;
|
|
const segments = pathPart.split('/').filter(Boolean);
|
|
if (!segments.length || !WHITELIST.has(segments[0])) continue;
|
|
if (segments.length > 4) continue;
|
|
if (seen.has(pathPart)) continue;
|
|
seen.add(pathPart);
|
|
out.push({ path: pathPart, url: BASE + pathPart, name: titleCase(segments[segments.length - 1]) });
|
|
}
|
|
out.sort((a, b) => a.path.localeCompare(b.path));
|
|
return out;
|
|
}
|
|
|
|
function render(entries) {
|
|
const head = [
|
|
'---',
|
|
'title: Yehwang Website Category Map (Site Map)',
|
|
'category: site-map',
|
|
`source: ${SITEMAP}`,
|
|
'generated: true',
|
|
'source_url:',
|
|
` - ${SITEMAP}`,
|
|
`last_updated: ${new Date().toISOString().slice(0, 10)}`,
|
|
'---',
|
|
'',
|
|
'Every Yehwang category page has a direct URL. Use these links to send customers straight to the right page instead of a search query.',
|
|
'',
|
|
];
|
|
const body = entries.map((e) => `### Q: Where can I find ${e.name}?\nBrowse our ${e.name} category: ${e.url}\n`);
|
|
return head.join('\n') + '\n' + body.join('\n');
|
|
}
|
|
|
|
(async () => {
|
|
const res = await fetch(SITEMAP, {
|
|
headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0 Safari/537.36' },
|
|
signal: AbortSignal.timeout(90000),
|
|
});
|
|
if (!res.ok) throw new Error(`sitemap HTTP ${res.status}`);
|
|
const xml = await res.text();
|
|
const urls = [...xml.matchAll(/<loc>([^<]+)<\/loc>/g)].map((m) => m[1].trim());
|
|
const entries = buildEntries(urls);
|
|
if (!entries.length) throw new Error('generated 0 site-map entries');
|
|
fs.writeFileSync(OUT, render(entries), 'utf8');
|
|
console.log(JSON.stringify({ ts: new Date().toISOString(), site_map_kb: 'done', entries: entries.length, out: OUT }));
|
|
})().catch((err) => { console.error('[build-site-kb]', err.message); process.exit(1); });
|